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 "fxjs/cjs_runtime.h"
8
9 #include <algorithm>
10
11 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
12 #include "fxjs/JS_Define.h"
13 #include "fxjs/cjs_annot.h"
14 #include "fxjs/cjs_app.h"
15 #include "fxjs/cjs_border.h"
16 #include "fxjs/cjs_color.h"
17 #include "fxjs/cjs_console.h"
18 #include "fxjs/cjs_display.h"
19 #include "fxjs/cjs_document.h"
20 #include "fxjs/cjs_event.h"
21 #include "fxjs/cjs_event_context.h"
22 #include "fxjs/cjs_eventhandler.h"
23 #include "fxjs/cjs_field.h"
24 #include "fxjs/cjs_font.h"
25 #include "fxjs/cjs_global.h"
26 #include "fxjs/cjs_globalarrays.h"
27 #include "fxjs/cjs_globalconsts.h"
28 #include "fxjs/cjs_globaldata.h"
29 #include "fxjs/cjs_highlight.h"
30 #include "fxjs/cjs_icon.h"
31 #include "fxjs/cjs_object.h"
32 #include "fxjs/cjs_position.h"
33 #include "fxjs/cjs_printparamsobj.h"
34 #include "fxjs/cjs_publicmethods.h"
35 #include "fxjs/cjs_report.h"
36 #include "fxjs/cjs_scalehow.h"
37 #include "fxjs/cjs_scalewhen.h"
38 #include "fxjs/cjs_style.h"
39 #include "fxjs/cjs_timerobj.h"
40 #include "fxjs/cjs_util.h"
41 #include "fxjs/cjs_zoomtype.h"
42 #include "public/fpdf_formfill.h"
43 #include "third_party/base/stl_util.h"
44
45 #ifdef PDF_ENABLE_XFA
46 #include "fxjs/cfxjse_value.h"
47 #endif // PDF_ENABLE_XFA
48
49 // static
Initialize(unsigned int slot,void * isolate)50 void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {
51 FXJS_Initialize(slot, reinterpret_cast<v8::Isolate*>(isolate));
52 }
53
54 // static
Destroy()55 void IJS_Runtime::Destroy() {
56 FXJS_Release();
57 }
58
59 // static
Create(CPDFSDK_FormFillEnvironment * pFormFillEnv)60 IJS_Runtime* IJS_Runtime::Create(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
61 return new CJS_Runtime(pFormFillEnv);
62 }
63
64 // static
CurrentRuntimeFromIsolate(v8::Isolate * pIsolate)65 CJS_Runtime* CJS_Runtime::CurrentRuntimeFromIsolate(v8::Isolate* pIsolate) {
66 return static_cast<CJS_Runtime*>(
67 CFXJS_Engine::CurrentEngineFromIsolate(pIsolate));
68 }
69
CJS_Runtime(CPDFSDK_FormFillEnvironment * pFormFillEnv)70 CJS_Runtime::CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv)
71 : m_pFormFillEnv(pFormFillEnv),
72 m_bBlocking(false),
73 m_isolateManaged(false) {
74 v8::Isolate* pIsolate = nullptr;
75
76 IPDF_JSPLATFORM* pPlatform = m_pFormFillEnv->GetFormFillInfo()->m_pJsPlatform;
77 if (pPlatform->version <= 2) {
78 unsigned int embedderDataSlot = 0;
79 v8::Isolate* pExternalIsolate = nullptr;
80 if (pPlatform->version == 2) {
81 pExternalIsolate = reinterpret_cast<v8::Isolate*>(pPlatform->m_isolate);
82 embedderDataSlot = pPlatform->m_v8EmbedderSlot;
83 }
84 FXJS_Initialize(embedderDataSlot, pExternalIsolate);
85 }
86 m_isolateManaged = FXJS_GetIsolate(&pIsolate);
87 SetIsolate(pIsolate);
88
89 #ifdef PDF_ENABLE_XFA
90 v8::Isolate::Scope isolate_scope(pIsolate);
91 v8::HandleScope handle_scope(pIsolate);
92 #endif
93
94 if (m_isolateManaged || FXJS_GlobalIsolateRefCount() == 0)
95 DefineJSObjects();
96
97 IJS_EventContext* pContext = NewEventContext();
98 InitializeEngine();
99 ReleaseEventContext(pContext);
100 SetFormFillEnvToDocument();
101 }
102
~CJS_Runtime()103 CJS_Runtime::~CJS_Runtime() {
104 NotifyObservedPtrs();
105 ReleaseEngine();
106 if (m_isolateManaged) {
107 GetIsolate()->Dispose();
108 SetIsolate(nullptr);
109 }
110 }
111
DefineJSObjects()112 void CJS_Runtime::DefineJSObjects() {
113 v8::Isolate::Scope isolate_scope(GetIsolate());
114 v8::HandleScope handle_scope(GetIsolate());
115 v8::Local<v8::Context> context = v8::Context::New(GetIsolate());
116 v8::Context::Scope context_scope(context);
117
118 // The call order determines the "ObjDefID" assigned to each class.
119 // ObjDefIDs 0 - 2
120 CJS_Border::DefineJSObjects(this);
121 CJS_Display::DefineJSObjects(this);
122 CJS_Font::DefineJSObjects(this);
123
124 // ObjDefIDs 3 - 5
125 CJS_Highlight::DefineJSObjects(this);
126 CJS_Position::DefineJSObjects(this);
127 CJS_ScaleHow::DefineJSObjects(this);
128
129 // ObjDefIDs 6 - 8
130 CJS_ScaleWhen::DefineJSObjects(this);
131 CJS_Style::DefineJSObjects(this);
132 CJS_Zoomtype::DefineJSObjects(this);
133
134 // ObjDefIDs 9 - 11
135 CJS_App::DefineJSObjects(this);
136 CJS_Color::DefineJSObjects(this);
137 CJS_Console::DefineJSObjects(this);
138
139 // ObjDefIDs 12 - 14
140 CJS_Document::DefineJSObjects(this);
141 CJS_Event::DefineJSObjects(this);
142 CJS_Field::DefineJSObjects(this);
143
144 // ObjDefIDs 15 - 17
145 CJS_Global::DefineJSObjects(this);
146 CJS_Icon::DefineJSObjects(this);
147 CJS_Util::DefineJSObjects(this);
148
149 // ObjDefIDs 18 - 20 (these can't fail, return void).
150 CJS_PublicMethods::DefineJSObjects(this);
151 CJS_GlobalConsts::DefineJSObjects(this);
152 CJS_GlobalArrays::DefineJSObjects(this);
153
154 // ObjDefIDs 21 - 23.
155 CJS_TimerObj::DefineJSObjects(this);
156 CJS_PrintParamsObj::DefineJSObjects(this);
157 CJS_Annot::DefineJSObjects(this);
158 }
159
NewEventContext()160 IJS_EventContext* CJS_Runtime::NewEventContext() {
161 m_EventContextArray.push_back(pdfium::MakeUnique<CJS_EventContext>(this));
162 return m_EventContextArray.back().get();
163 }
164
ReleaseEventContext(IJS_EventContext * pContext)165 void CJS_Runtime::ReleaseEventContext(IJS_EventContext* pContext) {
166 auto it = std::find(m_EventContextArray.begin(), m_EventContextArray.end(),
167 pdfium::FakeUniquePtr<CJS_EventContext>(
168 static_cast<CJS_EventContext*>(pContext)));
169 if (it != m_EventContextArray.end())
170 m_EventContextArray.erase(it);
171 }
172
GetCurrentEventContext() const173 CJS_EventContext* CJS_Runtime::GetCurrentEventContext() const {
174 return m_EventContextArray.empty() ? nullptr
175 : m_EventContextArray.back().get();
176 }
177
SetFormFillEnvToDocument()178 void CJS_Runtime::SetFormFillEnvToDocument() {
179 v8::Isolate::Scope isolate_scope(GetIsolate());
180 v8::HandleScope handle_scope(GetIsolate());
181 v8::Local<v8::Context> context = NewLocalContext();
182 v8::Context::Scope context_scope(context);
183
184 v8::Local<v8::Object> pThis = GetThisObj();
185 if (pThis.IsEmpty())
186 return;
187
188 if (CFXJS_Engine::GetObjDefnID(pThis) != CJS_Document::GetObjDefnID())
189 return;
190
191 CJS_Document* pJSDocument =
192 static_cast<CJS_Document*>(GetObjectPrivate(pThis));
193 if (!pJSDocument)
194 return;
195
196 Document* pDocument = static_cast<Document*>(pJSDocument->GetEmbedObject());
197 if (!pDocument)
198 return;
199
200 pDocument->SetFormFillEnv(m_pFormFillEnv.Get());
201 }
202
GetFormFillEnv() const203 CPDFSDK_FormFillEnvironment* CJS_Runtime::GetFormFillEnv() const {
204 return m_pFormFillEnv.Get();
205 }
206
ExecuteScript(const WideString & script,WideString * info)207 int CJS_Runtime::ExecuteScript(const WideString& script, WideString* info) {
208 FXJSErr error = {};
209 int nRet = Execute(script, &error);
210 if (nRet < 0) {
211 *info = WideString::Format(L"[ Line: %05d { %ls } ] : %s", error.linnum - 1,
212 error.srcline, error.message);
213 }
214 return nRet;
215 }
216
AddEventToSet(const FieldEvent & event)217 bool CJS_Runtime::AddEventToSet(const FieldEvent& event) {
218 return m_FieldEventSet.insert(event).second;
219 }
220
RemoveEventFromSet(const FieldEvent & event)221 void CJS_Runtime::RemoveEventFromSet(const FieldEvent& event) {
222 m_FieldEventSet.erase(event);
223 }
224
225 #ifdef PDF_ENABLE_XFA
ChangeObjName(const WideString & str)226 WideString ChangeObjName(const WideString& str) {
227 WideString sRet = str;
228 sRet.Replace(L"_", L".");
229 return sRet;
230 }
231
GetValueByName(const ByteStringView & utf8Name,CFXJSE_Value * pValue)232 bool CJS_Runtime::GetValueByName(const ByteStringView& utf8Name,
233 CFXJSE_Value* pValue) {
234 v8::Isolate::Scope isolate_scope(GetIsolate());
235 v8::HandleScope handle_scope(GetIsolate());
236 v8::Local<v8::Context> context = NewLocalContext();
237 v8::Context::Scope context_scope(context);
238 v8::Local<v8::Value> propvalue = context->Global()->Get(
239 v8::String::NewFromUtf8(GetIsolate(), utf8Name.unterminated_c_str(),
240 v8::String::kNormalString, utf8Name.GetLength()));
241 if (propvalue.IsEmpty()) {
242 pValue->SetUndefined();
243 return false;
244 }
245 pValue->ForceSetValue(propvalue);
246 return true;
247 }
248
SetValueByName(const ByteStringView & utf8Name,CFXJSE_Value * pValue)249 bool CJS_Runtime::SetValueByName(const ByteStringView& utf8Name,
250 CFXJSE_Value* pValue) {
251 if (utf8Name.IsEmpty() || !pValue)
252 return false;
253
254 v8::Isolate* pIsolate = GetIsolate();
255 v8::Isolate::Scope isolate_scope(pIsolate);
256 v8::HandleScope handle_scope(pIsolate);
257 v8::Local<v8::Context> context = NewLocalContext();
258 v8::Context::Scope context_scope(context);
259 v8::Local<v8::Value> propvalue =
260 v8::Local<v8::Value>::New(GetIsolate(), pValue->DirectGetValue());
261 context->Global()->Set(
262 v8::String::NewFromUtf8(pIsolate, utf8Name.unterminated_c_str(),
263 v8::String::kNormalString, utf8Name.GetLength()),
264 propvalue);
265 return true;
266 }
267 #endif
268
MaybeCoerceToNumber(v8::Local<v8::Value> value)269 v8::Local<v8::Value> CJS_Runtime::MaybeCoerceToNumber(
270 v8::Local<v8::Value> value) {
271 bool bAllowNaN = false;
272 if (value->IsString()) {
273 ByteString bstr = ByteString::FromUnicode(ToWideString(value));
274 if (bstr.GetLength() == 0)
275 return value;
276 if (bstr == "NaN")
277 bAllowNaN = true;
278 }
279
280 v8::Isolate* pIsolate = GetIsolate();
281 v8::TryCatch try_catch(pIsolate);
282 v8::MaybeLocal<v8::Number> maybeNum =
283 value->ToNumber(pIsolate->GetCurrentContext());
284 if (maybeNum.IsEmpty())
285 return value;
286
287 v8::Local<v8::Number> num = maybeNum.ToLocalChecked();
288 if (std::isnan(num->Value()) && !bAllowNaN)
289 return value;
290
291 return num;
292 }
293