Provide an array buffer allocator to V8.
[pdfium.git] / fpdfsdk / src / javascript / JS_Runtime.cpp
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 #include "../../include/javascript/JavaScript.h"
8 #include "../../include/javascript/IJavaScript.h"
9 #include "../../include/javascript/JS_EventHandler.h"
10 #include "../../include/javascript/JS_Runtime.h"
11 #include "../../include/javascript/JS_Context.h"
12 #include "../../include/javascript/JS_Define.h"
13 #include "../../include/javascript/JS_Object.h"
14 #include "../../include/javascript/JS_Value.h"
15 #include "../../include/javascript/Document.h"
16 #include "../../include/javascript/app.h"
17 #include "../../include/javascript/color.h"
18 #include "../../include/javascript/Consts.h"
19 #include "../../include/javascript/Document.h"
20 #include "../../include/javascript/event.h"
21 #include "../../include/javascript/Field.h"
22 #include "../../include/javascript/Icon.h"
23 #include "../../include/javascript/PublicMethods.h"
24 #include "../../include/javascript/report.h"
25 #include "../../include/javascript/util.h"
26 #include "../../include/javascript/JS_GlobalData.h"
27 #include "../../include/javascript/global.h"
28 #include "../../include/javascript/console.h"
29
30 CJS_RuntimeFactory::~CJS_RuntimeFactory()
31 {
32 }
33
34 IFXJS_Runtime*                                  CJS_RuntimeFactory::NewJSRuntime(CPDFDoc_Environment* pApp)
35 {
36         if (!m_bInit)
37         {
38                 JS_Initial();
39                 m_bInit = TRUE;
40         }
41         return new CJS_Runtime(pApp);
42 }
43 void                                                    CJS_RuntimeFactory::AddRef()
44 {
45         //to do.Should be implemented as atom manipulation.
46         m_nRef++;
47 }
48 void                                                    CJS_RuntimeFactory::Release()
49 {       
50         if(m_bInit)
51         {
52                 //to do.Should be implemented as atom manipulation.
53                 if (--m_nRef == 0)
54                 {
55                         JS_Release();
56                         ReleaseGlobalData();
57                         m_bInit = FALSE;
58                 }
59         }
60 }
61
62 void                                                    CJS_RuntimeFactory::DeleteJSRuntime(IFXJS_Runtime* pRuntime)
63 {
64         if(pRuntime)
65                 delete (CJS_Runtime*)pRuntime;
66 }
67
68 CJS_GlobalData* CJS_RuntimeFactory::NewGlobalData(CPDFDoc_Environment* pApp)
69 {
70         if (m_pGlobalData)
71         {
72                 m_nGlobalDataCount++;
73                 return m_pGlobalData;
74         }
75         else
76         {
77                 m_nGlobalDataCount = 1;
78                 m_pGlobalData = new CJS_GlobalData(pApp);
79                 return m_pGlobalData;
80         }
81 }
82
83 void CJS_RuntimeFactory::ReleaseGlobalData()
84 {
85         m_nGlobalDataCount--;
86         
87         if (m_nGlobalDataCount <= 0)
88         {
89                 delete m_pGlobalData;
90                 m_pGlobalData = NULL;
91         }
92 }
93
94 void* CJS_ArrayBufferAllocator::Allocate(size_t length) {
95     return calloc(1, length);
96 }
97
98 void* CJS_ArrayBufferAllocator::AllocateUninitialized(size_t length) {
99     return malloc(length);
100 }
101
102 void CJS_ArrayBufferAllocator::Free(void* data, size_t length) {
103     free(data);
104 }
105
106 /* ------------------------------ CJS_Runtime ------------------------------ */
107
108 CJS_Runtime::CJS_Runtime(CPDFDoc_Environment * pApp) : 
109         m_pApp(pApp),
110         m_pDocument(NULL),
111         m_bBlocking(FALSE),
112         m_bRegistered(FALSE),
113         m_pFieldEventPath(NULL)
114 {
115         m_pArrayBufferAllocator.reset(new CJS_ArrayBufferAllocator());
116
117         v8::Isolate::CreateParams params;
118         params.array_buffer_allocator = m_pArrayBufferAllocator.get();
119         m_isolate = v8::Isolate::New(params);
120
121         InitJSObjects();
122
123         CJS_Context * pContext = (CJS_Context*)NewContext();
124         JS_InitialRuntime(*this, this, pContext, m_context);
125         ReleaseContext(pContext);
126 }
127
128 CJS_Runtime::~CJS_Runtime()
129 {
130         for (int i=0, sz=m_ContextArray.GetSize(); i<sz; i++)
131                 delete m_ContextArray.GetAt(i);
132
133         m_ContextArray.RemoveAll();
134
135         JS_ReleaseRuntime(*this, m_context);
136
137         RemoveEventsInLoop(m_pFieldEventPath);
138
139         m_pApp = NULL;
140         m_pDocument = NULL;
141         m_pFieldEventPath = NULL;
142         m_context.Reset();
143
144         //m_isolate->Exit();
145         m_isolate->Dispose();
146 }
147
148 FX_BOOL CJS_Runtime::InitJSObjects()
149 {
150         v8::Isolate::Scope isolate_scope(GetIsolate());
151         v8::HandleScope handle_scope(GetIsolate());
152         v8::Handle<v8::Context> context = v8::Context::New(GetIsolate());
153         v8::Context::Scope context_scope(context);
154         //0 - 8
155         if (CJS_Border::Init(*this, JS_STATIC) < 0) return FALSE;
156         if (CJS_Display::Init(*this, JS_STATIC) < 0) return FALSE;
157         if (CJS_Font::Init(*this, JS_STATIC) < 0) return FALSE;
158         if (CJS_Highlight::Init(*this, JS_STATIC) < 0) return FALSE;
159         if (CJS_Position::Init(*this, JS_STATIC) < 0) return FALSE;
160         if (CJS_ScaleHow::Init(*this, JS_STATIC) < 0) return FALSE;
161         if (CJS_ScaleWhen::Init(*this, JS_STATIC) < 0) return FALSE;
162         if (CJS_Style::Init(*this, JS_STATIC) < 0) return FALSE;        
163         if (CJS_Zoomtype::Init(*this, JS_STATIC) < 0) return FALSE;     
164
165         //9 - 11
166         if (CJS_App::Init(*this, JS_STATIC) < 0) return FALSE;
167         if (CJS_Color::Init(*this, JS_STATIC) < 0) return FALSE;   
168         if (CJS_Console::Init(*this, JS_STATIC) < 0) return FALSE;
169
170         //12 - 14
171         if (CJS_Document::Init(*this, JS_DYNAMIC) < 0) return FALSE;  
172         if (CJS_Event::Init(*this, JS_STATIC) < 0) return FALSE;                
173         if (CJS_Field::Init(*this, JS_DYNAMIC) < 0) return FALSE;    
174
175         //15 - 17
176         if (CJS_Global::Init(*this, JS_STATIC) < 0) return FALSE;               
177         if (CJS_Icon::Init(*this, JS_DYNAMIC) < 0) return FALSE;
178         if (CJS_Util::Init(*this, JS_STATIC) < 0) return FALSE;
179
180         if (CJS_PublicMethods::Init(*this) < 0) return FALSE;
181         if (CJS_GlobalConsts::Init(*this) < 0) return FALSE;
182         if (CJS_GlobalArrays::Init(*this) < 0) return FALSE;
183
184         if (CJS_TimerObj::Init(*this, JS_DYNAMIC) < 0) return FALSE;
185         if (CJS_PrintParamsObj::Init(*this, JS_DYNAMIC) <0) return FALSE;
186
187         return TRUE;
188 }
189
190 IFXJS_Context* CJS_Runtime::NewContext()
191 {
192         CJS_Context * p = new CJS_Context(this);
193         m_ContextArray.Add(p);
194         return p;
195 }
196
197 void CJS_Runtime::ReleaseContext(IFXJS_Context * pContext)
198 {
199         CJS_Context* pJSContext = (CJS_Context*)pContext;
200
201         for (int i=0, sz=m_ContextArray.GetSize(); i<sz; i++)
202         {
203                 if (pJSContext == m_ContextArray.GetAt(i))
204                 {
205                         delete pJSContext;
206                         m_ContextArray.RemoveAt(i);
207                         break;
208                 }
209         }
210 }
211
212 IFXJS_Context*  CJS_Runtime::GetCurrentContext()
213 {
214         if(!m_ContextArray.GetSize())
215                 return NULL;
216         return m_ContextArray.GetAt(m_ContextArray.GetSize()-1);
217 }
218
219 void CJS_Runtime::SetReaderDocument(CPDFSDK_Document* pReaderDoc)
220 {
221         if (m_pDocument != pReaderDoc)
222         {
223                 v8::Isolate::Scope isolate_scope(m_isolate);
224                 v8::HandleScope handle_scope(m_isolate);
225                 v8::Local<v8::Context> context =v8::Local<v8::Context>::New(m_isolate, m_context);
226                 v8::Context::Scope context_scope(context);
227
228                 m_pDocument = pReaderDoc;
229
230                 if (pReaderDoc)
231                 {
232                         JSObject pThis = JS_GetThisObj(*this);
233                         if(!pThis.IsEmpty())
234                         {
235                                 if (JS_GetObjDefnID(pThis) == JS_GetObjDefnID(*this, L"Document"))
236                                 {
237                                         if (CJS_Document* pJSDocument = (CJS_Document*)JS_GetPrivate(pThis))
238                                         {
239                                                 if (Document * pDocument = (Document*)pJSDocument->GetEmbedObject())
240                                                         pDocument->AttachDoc(pReaderDoc);
241                                         }
242                                 }
243                         }
244                         JS_SetThisObj(*this, JS_GetObjDefnID(*this, L"Document"));
245                 }
246                 else
247                 {
248                         JS_SetThisObj(*this, JS_GetObjDefnID(*this, L"app"));
249                 }
250         }
251 }
252
253 FX_BOOL CJS_Runtime::AddEventToLoop(const CFX_WideString& sTargetName, JS_EVENT_T eEventType)
254 {
255         if (m_pFieldEventPath == NULL)
256         {
257                 m_pFieldEventPath = new CJS_FieldEvent;
258                 m_pFieldEventPath->sTargetName = sTargetName;
259                 m_pFieldEventPath->eEventType = eEventType;
260                 m_pFieldEventPath->pNext = NULL;
261
262                 return TRUE;
263         }
264
265         //to search
266         CJS_FieldEvent* p = m_pFieldEventPath;
267         CJS_FieldEvent* pLast = m_pFieldEventPath;
268         while (p)
269         {
270                 if (p->eEventType == eEventType && p->sTargetName == sTargetName)
271                         return FALSE;
272
273                 pLast = p;
274                 p = p->pNext;
275         }
276
277         //to add
278         CJS_FieldEvent* pNew = new CJS_FieldEvent;
279         pNew->sTargetName = sTargetName;
280         pNew->eEventType = eEventType;
281         pNew->pNext = NULL;
282
283         pLast->pNext = pNew;
284
285         return TRUE;
286 }
287
288 void CJS_Runtime::RemoveEventInLoop(const CFX_WideString& sTargetName, JS_EVENT_T eEventType)
289 {
290         FX_BOOL bFind = FALSE;
291
292         CJS_FieldEvent* p = m_pFieldEventPath;
293         CJS_FieldEvent* pLast = NULL;
294         while (p)
295         {
296                 if (p->eEventType == eEventType && p->sTargetName == sTargetName)
297                 {
298                         bFind = TRUE;
299                         break;
300                 }
301
302                 pLast = p;
303                 p = p->pNext;
304         }
305
306         if (bFind)
307         {
308                 RemoveEventsInLoop(p);
309
310                 if (p == m_pFieldEventPath)
311                         m_pFieldEventPath = NULL;
312
313                 if (pLast)
314                         pLast->pNext = NULL;
315         }
316 }
317
318 void CJS_Runtime::RemoveEventsInLoop(CJS_FieldEvent* pStart)
319 {
320         CJS_FieldEvent* p = pStart;
321
322         while (p)
323         {
324                 CJS_FieldEvent* pOld = p;
325                 p = pOld->pNext;
326
327                 delete pOld;
328         }
329 }
330
331 v8::Handle<v8::Context> CJS_Runtime::NewJSContext()
332 {
333         return v8::Local<v8::Context>::New(m_isolate, m_context);
334 }
335
336 CFX_WideString ChangeObjName(const CFX_WideString& str)
337 {
338         CFX_WideString sRet = str;
339         sRet.Replace(L"_", L".");
340         return sRet;
341 }