Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / xfa / src / fxjse / src / class.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../foxitlib.h"\r
8 #include "fxv8.h"\r
9 #include "context.h"\r
10 #include "class.h"\r
11 #include "value.h"\r
12 #include "scope_inline.h"\r
13 #include "util_inline.h"\r
14 static void FXJSE_V8ConstructorCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value>& info);\r
15 static void FXJSE_V8FunctionCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value>& info);\r
16 static void FXJSE_V8GetterCallback_Wrapper(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info);\r
17 static void FXJSE_V8SetterCallback_Wrapper(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info);\r
18 void FXJSE_DefineFunctions(FXJSE_HCONTEXT hContext, const FXJSE_FUNCTION* lpFunctions, int nNum)\r
19 {\r
20     CFXJSE_Context* lpContext = reinterpret_cast<CFXJSE_Context*>(hContext);\r
21     ASSERT(lpContext);\r
22     CFXJSE_ScopeUtil_IsolateHandleContext scope(lpContext);\r
23     v8::Isolate *pIsolate = lpContext->GetRuntime();\r
24     v8::Handle<v8::Object>  hGlobalObject = FXJSE_GetGlobalObjectFromContext(scope.GetLocalContext());\r
25     for(FX_INT32 i = 0; i < nNum; i++) {\r
26         hGlobalObject->ForceSet(v8::String::NewFromUtf8(pIsolate, lpFunctions[i].name),\r
27                                 v8::Function::New(pIsolate, FXJSE_V8FunctionCallback_Wrapper, v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION*>(lpFunctions + i))),\r
28                                 static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));\r
29     }\r
30 }\r
31 FXJSE_HCLASS FXJSE_DefineClass(FXJSE_HCONTEXT hContext, const FXJSE_CLASS* lpClass)\r
32 {\r
33     CFXJSE_Context* lpContext = reinterpret_cast<CFXJSE_Context*>(hContext);\r
34     ASSERT(lpContext);\r
35     return reinterpret_cast<FXJSE_HCLASS>(CFXJSE_Class::Create(lpContext, lpClass, FALSE));\r
36 }\r
37 FXJSE_HCLASS FXJSE_GetClass(FXJSE_HCONTEXT hContext, FX_BSTR szName)\r
38 {\r
39     return reinterpret_cast<FXJSE_HCLASS>(CFXJSE_Class::GetClassFromContext(reinterpret_cast<CFXJSE_Context*>(hContext), szName));\r
40 }\r
41 static void FXJSE_V8FunctionCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value>& info)\r
42 {\r
43     const FXJSE_FUNCTION* lpFunctionInfo = static_cast<FXJSE_FUNCTION*>(info.Data().As<v8::External>()->Value());\r
44     if (!lpFunctionInfo) {\r
45         return;\r
46     }\r
47     CFX_ByteStringC szFunctionName(lpFunctionInfo->name);\r
48     CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate());\r
49     lpThisValue->ForceSetValue(info.This());\r
50     CFXJSE_Value* lpRetValue = CFXJSE_Value::Create(info.GetIsolate());\r
51     CFXJSE_ArgumentsImpl impl = {&info, lpRetValue};\r
52     lpFunctionInfo->callbackProc(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), szFunctionName, reinterpret_cast<CFXJSE_Arguments&>(impl));\r
53     if(!lpRetValue->DirectGetValue().IsEmpty()) {\r
54         info.GetReturnValue().Set(lpRetValue->DirectGetValue());\r
55     }\r
56     delete lpRetValue;\r
57     lpRetValue = NULL;\r
58     delete lpThisValue;\r
59     lpThisValue = NULL;\r
60 }\r
61 static void FXJSE_V8ClassGlobalConstructorCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value>& info)\r
62 {\r
63     const FXJSE_CLASS* lpClassDefinition = static_cast<FXJSE_CLASS*>(info.Data().As<v8::External>()->Value());\r
64     if (!lpClassDefinition) {\r
65         return;\r
66     }\r
67     CFX_ByteStringC szFunctionName(lpClassDefinition->name);\r
68     CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate());\r
69     lpThisValue->ForceSetValue(info.This());\r
70     CFXJSE_Value* lpRetValue = CFXJSE_Value::Create(info.GetIsolate());\r
71     CFXJSE_ArgumentsImpl impl = {&info, lpRetValue};\r
72     lpClassDefinition->constructor(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), szFunctionName, reinterpret_cast<CFXJSE_Arguments&>(impl));\r
73     if(!lpRetValue->DirectGetValue().IsEmpty()) {\r
74         info.GetReturnValue().Set(lpRetValue->DirectGetValue());\r
75     }\r
76     delete lpRetValue;\r
77     lpRetValue = NULL;\r
78     delete lpThisValue;\r
79     lpThisValue = NULL;\r
80 }\r
81 static void FXJSE_V8GetterCallback_Wrapper(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)\r
82 {\r
83     const FXJSE_PROPERTY* lpPropertyInfo = static_cast<FXJSE_PROPERTY*>(info.Data().As<v8::External>()->Value());\r
84     if (!lpPropertyInfo) {\r
85         return;\r
86     }\r
87     CFX_ByteStringC szPropertyName(lpPropertyInfo->name);\r
88     CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate());\r
89     CFXJSE_Value* lpPropValue = CFXJSE_Value::Create(info.GetIsolate());\r
90     lpThisValue->ForceSetValue(info.This());\r
91     lpPropertyInfo->getProc(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), szPropertyName, reinterpret_cast<FXJSE_HVALUE>(lpPropValue));\r
92     info.GetReturnValue().Set(lpPropValue->DirectGetValue());\r
93     delete lpThisValue;\r
94     lpThisValue = NULL;\r
95     delete lpPropValue;\r
96     lpPropValue = NULL;\r
97 }\r
98 static void FXJSE_V8SetterCallback_Wrapper(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)\r
99 {\r
100     const FXJSE_PROPERTY* lpPropertyInfo = static_cast<FXJSE_PROPERTY*>(info.Data().As<v8::External>()->Value());\r
101     if (!lpPropertyInfo) {\r
102         return;\r
103     }\r
104     CFX_ByteStringC szPropertyName(lpPropertyInfo->name);\r
105     CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate());\r
106     CFXJSE_Value* lpPropValue = CFXJSE_Value::Create(info.GetIsolate());\r
107     lpThisValue->ForceSetValue(info.This());\r
108     lpPropValue->ForceSetValue(value);\r
109     lpPropertyInfo->setProc(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), szPropertyName, reinterpret_cast<FXJSE_HVALUE>(lpPropValue));\r
110     delete lpThisValue;\r
111     lpThisValue = NULL;\r
112     delete lpPropValue;\r
113     lpPropValue = NULL;\r
114 }\r
115 static void FXJSE_V8ConstructorCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value>& info)\r
116 {\r
117     const FXJSE_CLASS* lpClassDefinition = static_cast<FXJSE_CLASS*>(info.Data().As<v8::External>()->Value());\r
118     if (!lpClassDefinition) {\r
119         return;\r
120     }\r
121     FXSYS_assert(info.This()->InternalFieldCount());\r
122     info.This()->SetAlignedPointerInInternalField(0, NULL);\r
123     CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate());\r
124     lpThisValue->ForceSetValue(info.This());\r
125     if(lpClassDefinition->dynMethodCall || lpClassDefinition->dynPropGetter || lpClassDefinition->dynPropSetter || lpClassDefinition->dynPropTypeGetter) {\r
126         CFXJSE_Class::SetUpDynPropHandler(NULL, lpThisValue, lpClassDefinition);\r
127     }\r
128     delete lpThisValue;\r
129     lpThisValue = NULL;\r
130 }\r
131 FXJSE_HRUNTIME CFXJSE_Arguments::GetRuntime() const\r
132 {\r
133     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
134     return reinterpret_cast<FXJSE_HRUNTIME>(lpArguments->m_pRetValue->GetIsolate());\r
135 }\r
136 FX_INT32                CFXJSE_Arguments::GetLength() const\r
137 {\r
138     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
139     return lpArguments->m_pInfo->Length();\r
140 }\r
141 FXJSE_HVALUE    CFXJSE_Arguments::GetValue(FX_INT32 index) const\r
142 {\r
143     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
144     CFXJSE_Value* lpArgValue = CFXJSE_Value::Create(v8::Isolate::GetCurrent());\r
145     ASSERT(lpArgValue);\r
146     lpArgValue->ForceSetValue((*lpArguments->m_pInfo)[index]);\r
147     return reinterpret_cast<FXJSE_HVALUE>(lpArgValue);\r
148 }\r
149 FX_BOOL                 CFXJSE_Arguments::GetBoolean(FX_INT32 index) const\r
150 {\r
151     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
152     return (*lpArguments->m_pInfo)[index]->BooleanValue() ? TRUE : FALSE;\r
153 }\r
154 FX_INT32                CFXJSE_Arguments::GetInt32(FX_INT32 index) const\r
155 {\r
156     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
157     return static_cast<FX_INT32>((*lpArguments->m_pInfo)[index]->NumberValue());\r
158 }\r
159 FX_FLOAT                CFXJSE_Arguments::GetFloat(FX_INT32 index) const\r
160 {\r
161     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
162     return static_cast<FX_FLOAT>((*lpArguments->m_pInfo)[index]->NumberValue());\r
163 }\r
164 CFX_ByteString  CFXJSE_Arguments::GetUTF8String(FX_INT32 index) const\r
165 {\r
166     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
167     v8::Handle<v8::String> hString = (*lpArguments->m_pInfo)[index]->ToString();\r
168     v8::String::Utf8Value  szStringVal(hString);\r
169     return CFX_ByteString(*szStringVal);\r
170 }\r
171 FX_LPVOID               CFXJSE_Arguments::GetObject(FX_INT32 index, FXJSE_HCLASS hClass ) const\r
172 {\r
173     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
174     v8::Handle<v8::Value> hValue = (*lpArguments->m_pInfo)[index];\r
175     ASSERT(!hValue.IsEmpty());\r
176     if(!hValue->IsObject()) {\r
177         return NULL;\r
178     }\r
179     CFXJSE_Class* lpClass = reinterpret_cast<CFXJSE_Class*>(hClass);\r
180     return FXJSE_RetrieveObjectBinding(hValue.As<v8::Object>(), lpClass);\r
181 }\r
182 FXJSE_HVALUE    CFXJSE_Arguments::GetReturnValue()\r
183 {\r
184     const CFXJSE_ArgumentsImpl* lpArguments = reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this);\r
185     return reinterpret_cast<FXJSE_HVALUE>(lpArguments->m_pRetValue);\r
186 }\r
187 static void FXJSE_Context_GlobalObjToString(const v8::FunctionCallbackInfo<v8::Value>& info)\r
188 {\r
189     const FXJSE_CLASS* lpClass = static_cast<FXJSE_CLASS*>(info.Data().As<v8::External>()->Value());\r
190     if (!lpClass) {\r
191         return;\r
192     }\r
193     if(info.This() == info.Holder() && lpClass->name) {\r
194         CFX_ByteString szStringVal;\r
195         szStringVal.Format("[object %s]", lpClass->name);\r
196         info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), (FX_LPCSTR)szStringVal, v8::String::kNormalString, szStringVal.GetLength()));\r
197     } else {\r
198         info.GetReturnValue().Set(info.This()->ObjectProtoToString());\r
199     }\r
200 }\r
201 CFXJSE_Class* CFXJSE_Class::Create(CFXJSE_Context* lpContext, const FXJSE_CLASS* lpClassDefinition, FX_BOOL bIsJSGlobal )\r
202 {\r
203     if(!lpContext || !lpClassDefinition) {\r
204         return NULL;\r
205     }\r
206     CFXJSE_Class* pClass = GetClassFromContext(lpContext, lpClassDefinition->name);\r
207     if(pClass) {\r
208         return pClass;\r
209     }\r
210     v8::Isolate* pIsolate = lpContext->m_pIsolate;\r
211     pClass = FX_NEW CFXJSE_Class(lpContext);\r
212     ASSERT(pClass);\r
213     pClass->m_szClassName = lpClassDefinition->name;\r
214     CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);\r
215     v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(pIsolate, bIsJSGlobal ? 0 : FXJSE_V8ConstructorCallback_Wrapper, v8::External::New(pIsolate, const_cast<FXJSE_CLASS*>(lpClassDefinition)));\r
216     hFunctionTemplate->SetClassName(v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name));\r
217     hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(1);\r
218     v8::Local<v8::ObjectTemplate> hObjectTemplate = hFunctionTemplate->InstanceTemplate();\r
219     if (lpClassDefinition->dynPropDeleter || lpClassDefinition->dynPropTypeGetter) {\r
220         SetUpNamedPropHandler(pIsolate, hObjectTemplate, lpClassDefinition);\r
221     }\r
222     if(lpClassDefinition->propNum) {\r
223         for(FX_INT32 i = 0; i < lpClassDefinition->propNum; i++) {\r
224             hObjectTemplate->SetNativeDataProperty(v8::String::NewFromUtf8(pIsolate, lpClassDefinition->properties[i].name),\r
225                                                    lpClassDefinition->properties[i].getProc ? FXJSE_V8GetterCallback_Wrapper : NULL,\r
226                                                    lpClassDefinition->properties[i].setProc ? FXJSE_V8SetterCallback_Wrapper : NULL,\r
227                                                    v8::External::New(pIsolate,  const_cast<FXJSE_PROPERTY*>(lpClassDefinition->properties + i)),\r
228                                                    static_cast<v8::PropertyAttribute>(v8::DontDelete));\r
229         }\r
230     }\r
231     if(lpClassDefinition->methNum) {\r
232         for(FX_INT32 i = 0; i < lpClassDefinition->methNum; i++) {\r
233             hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name),\r
234                                  v8::FunctionTemplate::New(pIsolate, FXJSE_V8FunctionCallback_Wrapper,\r
235                                          v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION*>(lpClassDefinition->methods + i))),\r
236                                  static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));\r
237         }\r
238     }\r
239     if(lpClassDefinition->constructor) {\r
240         if(bIsJSGlobal) {\r
241             hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name),\r
242                                  v8::FunctionTemplate::New(pIsolate, FXJSE_V8ClassGlobalConstructorCallback_Wrapper,\r
243                                          v8::External::New(pIsolate, const_cast<FXJSE_CLASS*>(lpClassDefinition))),\r
244                                  static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));\r
245         } else {\r
246             v8::Local<v8::Context> hLocalContext = v8::Local<v8::Context>::New(pIsolate, lpContext->m_hContext);\r
247             FXJSE_GetGlobalObjectFromContext(hLocalContext)->Set(v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name),\r
248                     v8::Function::New(pIsolate, FXJSE_V8ClassGlobalConstructorCallback_Wrapper,\r
249                                       v8::External::New(pIsolate, const_cast<FXJSE_CLASS*>(lpClassDefinition))));\r
250         }\r
251     }\r
252     if(bIsJSGlobal) {\r
253         hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString"), v8::FunctionTemplate::New(pIsolate, FXJSE_Context_GlobalObjToString, v8::External::New(pIsolate, const_cast<FXJSE_CLASS*>(lpClassDefinition))));\r
254     }\r
255     pClass->m_hTemplate.Reset(lpContext->m_pIsolate, hFunctionTemplate);\r
256     lpContext->m_rgClasses.Add(pClass);\r
257     return pClass;\r
258 }\r
259 CFXJSE_Class* CFXJSE_Class::GetClassFromContext(CFXJSE_Context* pContext, FX_BSTR szName)\r
260 {\r
261     for(int count = pContext->m_rgClasses.GetSize(), i = 0; i < count; i++) {\r
262         CFXJSE_Class* pClass = pContext->m_rgClasses[i];\r
263         if(pClass->m_szClassName == szName) {\r
264             return pClass;\r
265         }\r
266     }\r
267     return NULL;\r
268 }\r