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