• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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 #include "fxjs/xfa/cfxjse_class.h"
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "fxjs/cjs_result.h"
13 #include "fxjs/js_resources.h"
14 #include "fxjs/xfa/cfxjse_arguments.h"
15 #include "fxjs/xfa/cfxjse_context.h"
16 #include "fxjs/xfa/cfxjse_isolatetracker.h"
17 #include "fxjs/xfa/cfxjse_value.h"
18 #include "third_party/base/ptr_util.h"
19 
20 using pdfium::fxjse::kClassTag;
21 using pdfium::fxjse::kFuncTag;
22 
23 namespace {
24 
AsFunctionDescriptor(void * ptr)25 FXJSE_FUNCTION_DESCRIPTOR* AsFunctionDescriptor(void* ptr) {
26   auto* result = static_cast<FXJSE_FUNCTION_DESCRIPTOR*>(ptr);
27   return result && result->tag == kFuncTag ? result : nullptr;
28 }
29 
AsClassDescriptor(void * ptr)30 FXJSE_CLASS_DESCRIPTOR* AsClassDescriptor(void* ptr) {
31   auto* result = static_cast<FXJSE_CLASS_DESCRIPTOR*>(ptr);
32   return result && result->tag == kClassTag ? result : nullptr;
33 }
34 
V8FunctionCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value> & info)35 void V8FunctionCallback_Wrapper(
36     const v8::FunctionCallbackInfo<v8::Value>& info) {
37   const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
38       AsFunctionDescriptor(info.Data().As<v8::External>()->Value());
39   if (!lpFunctionInfo)
40     return;
41 
42   ByteStringView szFunctionName(lpFunctionInfo->name);
43   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
44   lpThisValue->ForceSetValue(info.Holder());
45   auto lpRetValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
46   CFXJSE_Arguments impl(&info, lpRetValue.get());
47   lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl);
48   if (!lpRetValue->DirectGetValue().IsEmpty())
49     info.GetReturnValue().Set(lpRetValue->DirectGetValue());
50 }
51 
V8ConstructorCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value> & info)52 void V8ConstructorCallback_Wrapper(
53     const v8::FunctionCallbackInfo<v8::Value>& info) {
54   if (!info.IsConstructCall())
55     return;
56 
57   const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
58       AsClassDescriptor(info.Data().As<v8::External>()->Value());
59   if (!lpClassDefinition)
60     return;
61 
62   ASSERT(info.Holder()->InternalFieldCount() == 2);
63   info.Holder()->SetAlignedPointerInInternalField(0, nullptr);
64   info.Holder()->SetAlignedPointerInInternalField(1, nullptr);
65 }
66 
Context_GlobalObjToString(const v8::FunctionCallbackInfo<v8::Value> & info)67 void Context_GlobalObjToString(
68     const v8::FunctionCallbackInfo<v8::Value>& info) {
69   const FXJSE_CLASS_DESCRIPTOR* lpClass =
70       AsClassDescriptor(info.Data().As<v8::External>()->Value());
71   if (!lpClass)
72     return;
73 
74   if (info.This() == info.Holder() && lpClass->name) {
75     ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name);
76     info.GetReturnValue().Set(
77         v8::String::NewFromUtf8(info.GetIsolate(), szStringVal.c_str(),
78                                 v8::NewStringType::kNormal,
79                                 szStringVal.GetLength())
80             .ToLocalChecked());
81     return;
82   }
83   v8::Local<v8::String> local_str =
84       info.Holder()
85           ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext())
86           .FromMaybe(v8::Local<v8::String>());
87   info.GetReturnValue().Set(local_str);
88 }
89 
DynPropGetterAdapter_MethodCallback(const v8::FunctionCallbackInfo<v8::Value> & info)90 void DynPropGetterAdapter_MethodCallback(
91     const v8::FunctionCallbackInfo<v8::Value>& info) {
92   v8::Local<v8::Object> hCallBackInfo = info.Data().As<v8::Object>();
93   if (hCallBackInfo->InternalFieldCount() != 2)
94     return;
95 
96   auto* pClassDescriptor = static_cast<const FXJSE_CLASS_DESCRIPTOR*>(
97       hCallBackInfo->GetAlignedPointerFromInternalField(0));
98   if (pClassDescriptor != &GlobalClassDescriptor &&
99       pClassDescriptor != &NormalClassDescriptor &&
100       pClassDescriptor != &VariablesClassDescriptor &&
101       pClassDescriptor != &kFormCalcFM2JSDescriptor) {
102     return;
103   }
104 
105   v8::Local<v8::String> hPropName =
106       hCallBackInfo->GetInternalField(1).As<v8::String>();
107   if (hPropName.IsEmpty())
108     return;
109 
110   v8::String::Utf8Value szPropName(info.GetIsolate(), hPropName);
111   CJS_Result result =
112       pClassDescriptor->dynMethodCall(info, WideString::FromUTF8(*szPropName));
113 
114   if (result.HasError()) {
115     WideString err = JSFormatErrorString(pClassDescriptor->name, *szPropName,
116                                          result.Error());
117     v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(
118         info.GetIsolate(), err.ToDefANSI().c_str(), v8::NewStringType::kNormal);
119     info.GetIsolate()->ThrowException(str.ToLocalChecked());
120     return;
121   }
122 
123   if (result.HasReturn())
124     info.GetReturnValue().Set(result.Return());
125 }
126 
DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR * lpClass,CFXJSE_Value * pObject,ByteStringView szPropName,CFXJSE_Value * pValue)127 void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
128                           CFXJSE_Value* pObject,
129                           ByteStringView szPropName,
130                           CFXJSE_Value* pValue) {
131   ASSERT(lpClass);
132 
133   int32_t nPropType =
134       lpClass->dynPropTypeGetter == nullptr
135           ? FXJSE_ClassPropType_Property
136           : lpClass->dynPropTypeGetter(pObject, szPropName, false);
137   if (nPropType == FXJSE_ClassPropType_Property) {
138     if (lpClass->dynPropGetter)
139       lpClass->dynPropGetter(pObject, szPropName, pValue);
140   } else if (nPropType == FXJSE_ClassPropType_Method) {
141     if (lpClass->dynMethodCall && pValue) {
142       v8::Isolate* pIsolate = pValue->GetIsolate();
143       v8::HandleScope hscope(pIsolate);
144       v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
145           v8::ObjectTemplate::New(pIsolate);
146       hCallBackInfoTemplate->SetInternalFieldCount(2);
147       v8::Local<v8::Object> hCallBackInfo =
148           hCallBackInfoTemplate
149               ->NewInstance(pValue->GetIsolate()->GetCurrentContext())
150               .ToLocalChecked();
151       hCallBackInfo->SetAlignedPointerInInternalField(
152           0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
153       hCallBackInfo->SetInternalField(
154           1, v8::String::NewFromUtf8(
155                  pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()),
156                  v8::NewStringType::kNormal, szPropName.GetLength())
157                  .ToLocalChecked());
158       pValue->ForceSetValue(
159           v8::Function::New(pValue->GetIsolate()->GetCurrentContext(),
160                             DynPropGetterAdapter_MethodCallback, hCallBackInfo,
161                             0, v8::ConstructorBehavior::kThrow)
162               .ToLocalChecked());
163     }
164   }
165 }
166 
DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR * lpClass,CFXJSE_Value * pObject,ByteStringView szPropName,CFXJSE_Value * pValue)167 void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
168                           CFXJSE_Value* pObject,
169                           ByteStringView szPropName,
170                           CFXJSE_Value* pValue) {
171   ASSERT(lpClass);
172   int32_t nPropType =
173       lpClass->dynPropTypeGetter == nullptr
174           ? FXJSE_ClassPropType_Property
175           : lpClass->dynPropTypeGetter(pObject, szPropName, false);
176   if (nPropType != FXJSE_ClassPropType_Method) {
177     if (lpClass->dynPropSetter)
178       lpClass->dynPropSetter(pObject, szPropName, pValue);
179   }
180 }
181 
DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR * lpClass,CFXJSE_Value * pObject,ByteStringView szPropName)182 bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
183                          CFXJSE_Value* pObject,
184                          ByteStringView szPropName) {
185   ASSERT(lpClass);
186   int32_t nPropType =
187       lpClass->dynPropTypeGetter == nullptr
188           ? FXJSE_ClassPropType_Property
189           : lpClass->dynPropTypeGetter(pObject, szPropName, true);
190   return nPropType != FXJSE_ClassPropType_None;
191 }
192 
NamedPropertyQueryCallback(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Integer> & info)193 void NamedPropertyQueryCallback(
194     v8::Local<v8::Name> property,
195     const v8::PropertyCallbackInfo<v8::Integer>& info) {
196   v8::Local<v8::Object> thisObject = info.Holder();
197   const FXJSE_CLASS_DESCRIPTOR* lpClass =
198       AsClassDescriptor(info.Data().As<v8::External>()->Value());
199   if (!lpClass)
200     return;
201 
202   v8::HandleScope scope(info.GetIsolate());
203   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
204   ByteStringView szFxPropName(*szPropName, szPropName.length());
205   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
206   lpThisValue->ForceSetValue(thisObject);
207   if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) {
208     info.GetReturnValue().Set(v8::DontDelete);
209     return;
210   }
211   const int32_t iV8Absent = 64;
212   info.GetReturnValue().Set(iV8Absent);
213 }
214 
NamedPropertyGetterCallback(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Value> & info)215 void NamedPropertyGetterCallback(
216     v8::Local<v8::Name> property,
217     const v8::PropertyCallbackInfo<v8::Value>& info) {
218   v8::Local<v8::Object> thisObject = info.Holder();
219   const FXJSE_CLASS_DESCRIPTOR* lpClass =
220       AsClassDescriptor(info.Data().As<v8::External>()->Value());
221   if (!lpClass)
222     return;
223 
224   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
225   ByteStringView szFxPropName(*szPropName, szPropName.length());
226   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
227   lpThisValue->ForceSetValue(thisObject);
228   auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
229   DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
230                        lpNewValue.get());
231   info.GetReturnValue().Set(lpNewValue->DirectGetValue());
232 }
233 
NamedPropertySetterCallback(v8::Local<v8::Name> property,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<v8::Value> & info)234 void NamedPropertySetterCallback(
235     v8::Local<v8::Name> property,
236     v8::Local<v8::Value> value,
237     const v8::PropertyCallbackInfo<v8::Value>& info) {
238   v8::Local<v8::Object> thisObject = info.Holder();
239   const FXJSE_CLASS_DESCRIPTOR* lpClass =
240       AsClassDescriptor(info.Data().As<v8::External>()->Value());
241   if (!lpClass)
242     return;
243 
244   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
245   ByteStringView szFxPropName(*szPropName, szPropName.length());
246   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
247   lpThisValue->ForceSetValue(thisObject);
248   auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
249   lpNewValue->ForceSetValue(value);
250   DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
251                        lpNewValue.get());
252   info.GetReturnValue().Set(value);
253 }
254 
NamedPropertyEnumeratorCallback(const v8::PropertyCallbackInfo<v8::Array> & info)255 void NamedPropertyEnumeratorCallback(
256     const v8::PropertyCallbackInfo<v8::Array>& info) {
257   info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
258 }
259 
SetUpNamedPropHandler(v8::Isolate * pIsolate,v8::Local<v8::ObjectTemplate> * pObjectTemplate,const FXJSE_CLASS_DESCRIPTOR * lpClassDefinition)260 void SetUpNamedPropHandler(v8::Isolate* pIsolate,
261                            v8::Local<v8::ObjectTemplate>* pObjectTemplate,
262                            const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
263   v8::NamedPropertyHandlerConfiguration configuration(
264       lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : nullptr,
265       lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : nullptr,
266       lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback
267                                            : nullptr,
268       nullptr, NamedPropertyEnumeratorCallback,
269       v8::External::New(pIsolate,
270                         const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
271       v8::PropertyHandlerFlags::kNonMasking);
272   (*pObjectTemplate)->SetHandler(configuration);
273 }
274 
275 }  // namespace
276 
277 // static
Create(CFXJSE_Context * lpContext,const FXJSE_CLASS_DESCRIPTOR * lpClassDefinition,bool bIsJSGlobal)278 CFXJSE_Class* CFXJSE_Class::Create(
279     CFXJSE_Context* lpContext,
280     const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
281     bool bIsJSGlobal) {
282   if (!lpContext || !lpClassDefinition)
283     return nullptr;
284 
285   CFXJSE_Class* pExistingClass =
286       lpContext->GetClassByName(lpClassDefinition->name);
287   if (pExistingClass)
288     return pExistingClass;
289 
290   v8::Isolate* pIsolate = lpContext->GetIsolate();
291   auto pClass = pdfium::MakeUnique<CFXJSE_Class>(lpContext);
292   pClass->m_szClassName = lpClassDefinition->name;
293   pClass->m_lpClassDefinition = lpClassDefinition;
294   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
295   v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
296       pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
297       v8::External::New(
298           pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
299   hFunctionTemplate->SetClassName(
300       v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name,
301                               v8::NewStringType::kNormal)
302           .ToLocalChecked());
303   hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2);
304   v8::Local<v8::ObjectTemplate> hObjectTemplate =
305       hFunctionTemplate->InstanceTemplate();
306   SetUpNamedPropHandler(pIsolate, &hObjectTemplate, lpClassDefinition);
307 
308   if (lpClassDefinition->methNum) {
309     for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
310       v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
311           pIsolate, V8FunctionCallback_Wrapper,
312           v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
313                                           lpClassDefinition->methods + i)));
314       fun->RemovePrototype();
315       hObjectTemplate->Set(
316           v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name,
317                                   v8::NewStringType::kNormal)
318               .ToLocalChecked(),
319           fun,
320           static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
321     }
322   }
323 
324   if (bIsJSGlobal) {
325     v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
326         pIsolate, Context_GlobalObjToString,
327         v8::External::New(
328             pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
329     fun->RemovePrototype();
330     hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString",
331                                                  v8::NewStringType::kNormal)
332                              .ToLocalChecked(),
333                          fun);
334   }
335   pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate);
336   CFXJSE_Class* pResult = pClass.get();
337   lpContext->AddClass(std::move(pClass));
338   return pResult;
339 }
340 
CFXJSE_Class(CFXJSE_Context * lpContext)341 CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext) : m_pContext(lpContext) {}
342 
~CFXJSE_Class()343 CFXJSE_Class::~CFXJSE_Class() {}
344