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