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 "JS_Runtime.h"
8
9 #include "Consts.h"
10 #include "Document.h"
11 #include "Field.h"
12 #include "Icon.h"
13 #include "JS_Context.h"
14 #include "JS_Define.h"
15 #include "JS_EventHandler.h"
16 #include "JS_GlobalData.h"
17 #include "JS_Object.h"
18 #include "JS_Value.h"
19 #include "PublicMethods.h"
20 #include "app.h"
21 #include "color.h"
22 #include "console.h"
23 #include "event.h"
24 #include "fpdfsdk/include/fsdk_mgr.h" // For CPDFDoc_Environment.
25 #include "fpdfsdk/include/javascript/IJavaScript.h"
26 #include "global.h"
27 #include "report.h"
28 #include "util.h"
29 #include "third_party/base/stl_util.h"
30
31 #ifdef PDF_ENABLE_XFA
32 #include "fpdfsdk/include/fpdfxfa/fpdfxfa_app.h"
33 #include "xfa/src/fxjse/src/value.h"
34 #endif // PDF_ENABLE_XFA
35
36 // static
Initialize(unsigned int slot,void * isolate)37 void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {
38 FXJS_Initialize(slot, reinterpret_cast<v8::Isolate*>(isolate));
39 }
40
41 // static
Create(CPDFDoc_Environment * pEnv)42 IJS_Runtime* IJS_Runtime::Create(CPDFDoc_Environment* pEnv) {
43 return new CJS_Runtime(pEnv);
44 }
45
46 // static
FromContext(const IJS_Context * cc)47 CJS_Runtime* CJS_Runtime::FromContext(const IJS_Context* cc) {
48 const CJS_Context* pContext = static_cast<const CJS_Context*>(cc);
49 return pContext->GetJSRuntime();
50 }
51
CJS_Runtime(CPDFDoc_Environment * pApp)52 CJS_Runtime::CJS_Runtime(CPDFDoc_Environment* pApp)
53 : m_pApp(pApp),
54 m_pDocument(NULL),
55 m_bBlocking(FALSE),
56 m_isolate(NULL),
57 m_isolateManaged(false) {
58 #ifndef PDF_ENABLE_XFA
59 IPDF_JSPLATFORM* pPlatform = m_pApp->GetFormFillInfo()->m_pJsPlatform;
60 if (pPlatform->version <= 2) {
61 unsigned int embedderDataSlot = 0;
62 v8::Isolate* pExternalIsolate = nullptr;
63 if (pPlatform->version == 2) {
64 pExternalIsolate = reinterpret_cast<v8::Isolate*>(pPlatform->m_isolate);
65 embedderDataSlot = pPlatform->m_v8EmbedderSlot;
66 #else
67 if (CPDFXFA_App::GetInstance()->GetJSERuntime()) {
68 // TODO(tsepez): CPDFXFA_App should also use the embedder provided isolate.
69 m_isolate = (v8::Isolate*)CPDFXFA_App::GetInstance()->GetJSERuntime();
70 } else {
71 IPDF_JSPLATFORM* pPlatform = m_pApp->GetFormFillInfo()->m_pJsPlatform;
72 if (pPlatform->version <= 2) {
73 unsigned int embedderDataSlot = 0;
74 v8::Isolate* pExternalIsolate = nullptr;
75 if (pPlatform->version == 2) {
76 pExternalIsolate = reinterpret_cast<v8::Isolate*>(pPlatform->m_isolate);
77 embedderDataSlot = pPlatform->m_v8EmbedderSlot;
78 }
79 FXJS_Initialize(embedderDataSlot, pExternalIsolate);
80 #endif
81 }
82 #ifndef PDF_ENABLE_XFA
83 FXJS_Initialize(embedderDataSlot, pExternalIsolate);
84 #else
85 m_isolateManaged = FXJS_GetIsolate(&m_isolate);
86 }
87
88 v8::Isolate* isolate = m_isolate;
89 v8::Isolate::Scope isolate_scope(isolate);
90 v8::Locker locker(isolate);
91 v8::HandleScope handle_scope(isolate);
92 if (CPDFXFA_App::GetInstance()->IsJavaScriptInitialized()) {
93 CJS_Context* pContext = (CJS_Context*)NewContext();
94 FXJS_InitializeRuntime(GetIsolate(), this, &m_context, &m_StaticObjects);
95 ReleaseContext(pContext);
96 return;
97 #endif
98 }
99 #ifndef PDF_ENABLE_XFA
100 m_isolateManaged = FXJS_GetIsolate(&m_isolate);
101 #else
102
103 #endif
104 if (m_isolateManaged || FXJS_GlobalIsolateRefCount() == 0)
105 DefineJSObjects();
106
107 #ifdef PDF_ENABLE_XFA
108 CPDFXFA_App::GetInstance()->SetJavaScriptInitialized(TRUE);
109
110 #endif
111 CJS_Context* pContext = (CJS_Context*)NewContext();
112 FXJS_InitializeRuntime(GetIsolate(), this, &m_context, &m_StaticObjects);
113 ReleaseContext(pContext);
114 }
115
116 CJS_Runtime::~CJS_Runtime() {
117 for (auto* obs : m_observers)
118 obs->OnDestroyed();
119
120 for (int i = 0, sz = m_ContextArray.GetSize(); i < sz; i++)
121 delete m_ContextArray.GetAt(i);
122
123 m_ContextArray.RemoveAll();
124 #ifndef PDF_ENABLE_XFA
125 FXJS_ReleaseRuntime(GetIsolate(), &m_context, &m_StaticObjects);
126 #endif
127
128 m_pApp = NULL;
129 m_pDocument = NULL;
130 m_context.Reset();
131
132 if (m_isolateManaged)
133 m_isolate->Dispose();
134 }
135
136 void CJS_Runtime::DefineJSObjects() {
137 v8::Isolate::Scope isolate_scope(GetIsolate());
138 #ifdef PDF_ENABLE_XFA
139 v8::Locker locker(GetIsolate());
140 #endif
141 v8::HandleScope handle_scope(GetIsolate());
142 v8::Local<v8::Context> context = v8::Context::New(GetIsolate());
143 v8::Context::Scope context_scope(context);
144
145 // The call order determines the "ObjDefID" assigned to each class.
146 // ObjDefIDs 0 - 2
147 CJS_Border::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
148 CJS_Display::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
149 CJS_Font::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
150
151 // ObjDefIDs 3 - 5
152 CJS_Highlight::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
153 CJS_Position::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
154 CJS_ScaleHow::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
155
156 // ObjDefIDs 6 - 8
157 CJS_ScaleWhen::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
158 CJS_Style::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
159 CJS_Zoomtype::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
160
161 // ObjDefIDs 9 - 11
162 CJS_App::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
163 CJS_Color::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
164 CJS_Console::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
165
166 // ObjDefIDs 12 - 14
167 CJS_Document::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_GLOBAL);
168 CJS_Event::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
169 CJS_Field::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_DYNAMIC);
170
171 // ObjDefIDs 15 - 17
172 CJS_Global::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
173 CJS_Icon::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_DYNAMIC);
174 CJS_Util::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_STATIC);
175
176 // ObjDefIDs 18 - 20 (these can't fail, return void).
177 CJS_PublicMethods::DefineJSObjects(GetIsolate());
178 CJS_GlobalConsts::DefineJSObjects(this);
179 CJS_GlobalArrays::DefineJSObjects(this);
180
181 // ObjDefIDs 21 - 22.
182 CJS_TimerObj::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_DYNAMIC);
183 CJS_PrintParamsObj::DefineJSObjects(GetIsolate(), FXJSOBJTYPE_DYNAMIC);
184 }
185
186 IJS_Context* CJS_Runtime::NewContext() {
187 CJS_Context* p = new CJS_Context(this);
188 m_ContextArray.Add(p);
189 return p;
190 }
191
192 void CJS_Runtime::ReleaseContext(IJS_Context* pContext) {
193 CJS_Context* pJSContext = (CJS_Context*)pContext;
194
195 for (int i = 0, sz = m_ContextArray.GetSize(); i < sz; i++) {
196 if (pJSContext == m_ContextArray.GetAt(i)) {
197 delete pJSContext;
198 m_ContextArray.RemoveAt(i);
199 break;
200 }
201 }
202 }
203
204 IJS_Context* CJS_Runtime::GetCurrentContext() {
205 if (!m_ContextArray.GetSize())
206 return NULL;
207 return m_ContextArray.GetAt(m_ContextArray.GetSize() - 1);
208 }
209
210 void CJS_Runtime::SetReaderDocument(CPDFSDK_Document* pReaderDoc) {
211 if (m_pDocument != pReaderDoc) {
212 v8::Isolate::Scope isolate_scope(m_isolate);
213 #ifdef PDF_ENABLE_XFA
214 v8::Locker locker(m_isolate);
215 #endif
216 v8::HandleScope handle_scope(m_isolate);
217 v8::Local<v8::Context> context =
218 v8::Local<v8::Context>::New(m_isolate, m_context);
219 v8::Context::Scope context_scope(context);
220
221 m_pDocument = pReaderDoc;
222 if (pReaderDoc) {
223 v8::Local<v8::Object> pThis = FXJS_GetThisObj(GetIsolate());
224 if (!pThis.IsEmpty()) {
225 if (FXJS_GetObjDefnID(pThis) == CJS_Document::g_nObjDefnID) {
226 if (CJS_Document* pJSDocument =
227 (CJS_Document*)FXJS_GetPrivate(GetIsolate(), pThis)) {
228 if (Document* pDocument = (Document*)pJSDocument->GetEmbedObject())
229 pDocument->AttachDoc(pReaderDoc);
230 }
231 }
232 }
233 }
234 }
235 }
236
237 int CJS_Runtime::Execute(IJS_Context* cc,
238 const wchar_t* script,
239 CFX_WideString* info) {
240 FXJSErr error = {};
241 int nRet = FXJS_Execute(m_isolate, cc, script, &error);
242 if (nRet < 0) {
243 info->Format(L"[ Line: %05d { %s } ] : %s", error.linnum - 1, error.srcline,
244 error.message);
245 }
246 return nRet;
247 }
248
249 bool CJS_Runtime::AddEventToSet(const FieldEvent& event) {
250 return m_FieldEventSet.insert(event).second;
251 }
252
253 void CJS_Runtime::RemoveEventFromSet(const FieldEvent& event) {
254 m_FieldEventSet.erase(event);
255 }
256
257 v8::Local<v8::Context> CJS_Runtime::NewJSContext() {
258 return v8::Local<v8::Context>::New(m_isolate, m_context);
259 }
260
261 #ifdef PDF_ENABLE_XFA
262 CFX_WideString ChangeObjName(const CFX_WideString& str) {
263 CFX_WideString sRet = str;
264 sRet.Replace(L"_", L".");
265 return sRet;
266 }
267 FX_BOOL CJS_Runtime::GetHValueByName(const CFX_ByteStringC& utf8Name,
268 FXJSE_HVALUE hValue) {
269 #ifdef PDF_ENABLE_XFA
270 const FX_CHAR* name = utf8Name.GetCStr();
271
272 v8::Locker lock(GetIsolate());
273 v8::Isolate::Scope isolate_scope(GetIsolate());
274 v8::HandleScope handle_scope(GetIsolate());
275 v8::Local<v8::Context> old_context = GetIsolate()->GetCurrentContext();
276 v8::Local<v8::Context> context =
277 v8::Local<v8::Context>::New(GetIsolate(), m_context);
278 v8::Context::Scope context_scope(context);
279
280 // Caution: We're about to hand to XFA an object that in order to invoke
281 // methods will require that the current v8::Context always has a pointer
282 // to a CJS_Runtime in its embedder data slot. Unfortunately, XFA creates
283 // its own v8::Context which has not initialized the embedder data slot.
284 // Do so now.
285 // TODO(tsepez): redesign PDF-side objects to not rely on v8::Context's
286 // embedder data slots, and/or to always use the right context.
287 FXJS_SetRuntimeForV8Context(old_context, this);
288
289 v8::Local<v8::Value> propvalue =
290 context->Global()->Get(v8::String::NewFromUtf8(
291 GetIsolate(), name, v8::String::kNormalString, utf8Name.GetLength()));
292
293 if (propvalue.IsEmpty()) {
294 FXJSE_Value_SetUndefined(hValue);
295 return FALSE;
296 }
297 ((CFXJSE_Value*)hValue)->ForceSetValue(propvalue);
298 #endif
299
300 return TRUE;
301 }
302 FX_BOOL CJS_Runtime::SetHValueByName(const CFX_ByteStringC& utf8Name,
303 FXJSE_HVALUE hValue) {
304 #ifdef PDF_ENABLE_XFA
305 if (utf8Name.IsEmpty() || hValue == NULL)
306 return FALSE;
307 const FX_CHAR* name = utf8Name.GetCStr();
308 v8::Isolate* pIsolate = GetIsolate();
309 v8::Locker lock(pIsolate);
310 v8::Isolate::Scope isolate_scope(pIsolate);
311 v8::HandleScope handle_scope(pIsolate);
312 v8::Local<v8::Context> context =
313 v8::Local<v8::Context>::New(pIsolate, m_context);
314 v8::Context::Scope context_scope(context);
315
316 // v8::Local<v8::Context> tmpCotext =
317 // v8::Local<v8::Context>::New(GetIsolate(), m_context);
318 v8::Local<v8::Value> propvalue = v8::Local<v8::Value>::New(
319 GetIsolate(), ((CFXJSE_Value*)hValue)->DirectGetValue());
320 context->Global()->Set(
321 v8::String::NewFromUtf8(pIsolate, name, v8::String::kNormalString,
322 utf8Name.GetLength()),
323 propvalue);
324 #endif
325 return TRUE;
326 }
327
328 #endif
329 void CJS_Runtime::AddObserver(Observer* observer) {
330 ASSERT(!pdfium::ContainsKey(m_observers, observer));
331 m_observers.insert(observer);
332 }
333
334 void CJS_Runtime::RemoveObserver(Observer* observer) {
335 ASSERT(pdfium::ContainsKey(m_observers, observer));
336 m_observers.erase(observer);
337 }
338