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/xfa/cfxjse_context.h"
8
9 #include <utility>
10
11 #include "fxjs/cfxjs_engine.h"
12 #include "fxjs/xfa/cfxjse_class.h"
13 #include "fxjs/xfa/cfxjse_isolatetracker.h"
14 #include "fxjs/xfa/cfxjse_runtimedata.h"
15 #include "fxjs/xfa/cfxjse_value.h"
16 #include "third_party/base/ptr_util.h"
17
18 namespace {
19
20 const char szCompatibleModeScript[] =
21 "(function(global, list) {\n"
22 " 'use strict';\n"
23 " var objname;\n"
24 " for (objname in list) {\n"
25 " var globalobj = global[objname];\n"
26 " if (globalobj) {\n"
27 " list[objname].forEach(function(name) {\n"
28 " if (!globalobj[name]) {\n"
29 " Object.defineProperty(globalobj, name, {\n"
30 " writable: true,\n"
31 " enumerable: false,\n"
32 " value: (function(obj) {\n"
33 " if (arguments.length === 0) {\n"
34 " throw new TypeError('missing argument 0 when calling "
35 " function ' + objname + '.' + name);\n"
36 " }\n"
37 " return globalobj.prototype[name].apply(obj, "
38 " Array.prototype.slice.call(arguments, 1));\n"
39 " })\n"
40 " });\n"
41 " }\n"
42 " });\n"
43 " }\n"
44 " }\n"
45 "}(this, {String: ['substr', 'toUpperCase']}));";
46
47 const char szConsoleScript[] =
48 "console.show = function() {};\n"
49 "\n"
50 "console.println = function(...args) {\n"
51 " this.log(...args);\n"
52 "};";
53
54 // Only address matters, values are for humans debuging here.
55 char g_FXJSEHostObjectTag[] = "FXJSE Host Object";
56 char g_FXJSEProxyObjectTag[] = "FXJSE Proxy Object";
57
CreateReturnValue(v8::Isolate * pIsolate,v8::TryCatch * trycatch)58 v8::Local<v8::Object> CreateReturnValue(v8::Isolate* pIsolate,
59 v8::TryCatch* trycatch) {
60 v8::Local<v8::Object> hReturnValue = v8::Object::New(pIsolate);
61 if (!trycatch->HasCaught())
62 return hReturnValue;
63
64 v8::Local<v8::Message> hMessage = trycatch->Message();
65 if (hMessage.IsEmpty())
66 return hReturnValue;
67
68 v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
69 v8::Local<v8::Value> hException = trycatch->Exception();
70 if (hException->IsObject()) {
71 v8::Local<v8::String> hNameStr =
72 v8::String::NewFromUtf8(pIsolate, "name", v8::NewStringType::kNormal)
73 .ToLocalChecked();
74 v8::Local<v8::Value> hValue =
75 hException.As<v8::Object>()->Get(context, hNameStr).ToLocalChecked();
76 if (hValue->IsString() || hValue->IsStringObject()) {
77 hReturnValue->Set(context, 0, hValue).FromJust();
78 } else {
79 v8::Local<v8::String> hErrorStr =
80 v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
81 .ToLocalChecked();
82 hReturnValue->Set(context, 0, hErrorStr).FromJust();
83 }
84
85 v8::Local<v8::String> hMessageStr =
86 v8::String::NewFromUtf8(pIsolate, "message", v8::NewStringType::kNormal)
87 .ToLocalChecked();
88 hValue =
89 hException.As<v8::Object>()->Get(context, hMessageStr).ToLocalChecked();
90 if (hValue->IsString() || hValue->IsStringObject())
91 hReturnValue->Set(context, 1, hValue).FromJust();
92 else
93 hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
94 } else {
95 v8::Local<v8::String> hErrorStr =
96 v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
97 .ToLocalChecked();
98 hReturnValue->Set(context, 0, hErrorStr).FromJust();
99 hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
100 }
101 hReturnValue->Set(context, 2, hException).FromJust();
102 int line = hMessage->GetLineNumber(context).FromMaybe(0);
103 hReturnValue->Set(context, 3, v8::Integer::New(pIsolate, line)).FromJust();
104 v8::Local<v8::String> source =
105 hMessage->GetSourceLine(context).FromMaybe(v8::Local<v8::String>());
106 hReturnValue->Set(context, 4, source).FromJust();
107 int column = hMessage->GetStartColumn(context).FromMaybe(0);
108 hReturnValue->Set(context, 5, v8::Integer::New(pIsolate, column)).FromJust();
109 column = hMessage->GetEndColumn(context).FromMaybe(0);
110 hReturnValue->Set(context, 6, v8::Integer::New(pIsolate, column)).FromJust();
111 return hReturnValue;
112 }
113
114 class CFXJSE_ScopeUtil_IsolateHandleContext {
115 public:
CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context * pContext)116 explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext)
117 : m_parent(pContext->GetIsolate()), m_cscope(pContext->GetContext()) {}
118 CFXJSE_ScopeUtil_IsolateHandleContext(
119 const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
120 CFXJSE_ScopeUtil_IsolateHandleContext& operator=(
121 const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
122
123 private:
124 void* operator new(size_t size) = delete;
125 void operator delete(void*, size_t) = delete;
126
127 CFXJSE_ScopeUtil_IsolateHandle m_parent;
128 v8::Context::Scope m_cscope;
129 };
130
FXJSE_UpdateProxyBinding(v8::Local<v8::Object> hObject)131 void FXJSE_UpdateProxyBinding(v8::Local<v8::Object> hObject) {
132 ASSERT(!hObject.IsEmpty());
133 ASSERT(hObject->InternalFieldCount() == 2);
134 hObject->SetAlignedPointerInInternalField(0, g_FXJSEProxyObjectTag);
135 hObject->SetAlignedPointerInInternalField(1, nullptr);
136 }
137
138 } // namespace
139
FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,CFXJSE_HostObject * lpNewBinding)140 void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
141 CFXJSE_HostObject* lpNewBinding) {
142 ASSERT(!hObject.IsEmpty());
143 ASSERT(hObject->InternalFieldCount() == 2);
144 hObject->SetAlignedPointerInInternalField(0, g_FXJSEHostObjectTag);
145 hObject->SetAlignedPointerInInternalField(1, lpNewBinding);
146 }
147
FXJSE_ClearObjectBinding(v8::Local<v8::Object> hObject)148 void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hObject) {
149 ASSERT(!hObject.IsEmpty());
150 ASSERT(hObject->InternalFieldCount() == 2);
151 hObject->SetAlignedPointerInInternalField(0, nullptr);
152 hObject->SetAlignedPointerInInternalField(1, nullptr);
153 }
154
FXJSE_RetrieveObjectBinding(v8::Local<v8::Object> hJSObject)155 CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(
156 v8::Local<v8::Object> hJSObject) {
157 ASSERT(!hJSObject.IsEmpty());
158 if (!hJSObject->IsObject())
159 return nullptr;
160
161 v8::Local<v8::Object> hObject = hJSObject;
162 if (hObject->InternalFieldCount() != 2 ||
163 hObject->GetAlignedPointerFromInternalField(0) == g_FXJSEProxyObjectTag) {
164 v8::Local<v8::Value> hProtoObject = hObject->GetPrototype();
165 if (hProtoObject.IsEmpty() || !hProtoObject->IsObject())
166 return nullptr;
167
168 hObject = hProtoObject.As<v8::Object>();
169 if (hObject->InternalFieldCount() != 2)
170 return nullptr;
171 }
172 if (hObject->GetAlignedPointerFromInternalField(0) != g_FXJSEHostObjectTag)
173 return nullptr;
174
175 return static_cast<CFXJSE_HostObject*>(
176 hObject->GetAlignedPointerFromInternalField(1));
177 }
178
179 // static
Create(v8::Isolate * pIsolate,const FXJSE_CLASS_DESCRIPTOR * pGlobalClass,CFXJSE_HostObject * pGlobalObject)180 std::unique_ptr<CFXJSE_Context> CFXJSE_Context::Create(
181 v8::Isolate* pIsolate,
182 const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
183 CFXJSE_HostObject* pGlobalObject) {
184 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
185 auto pContext = pdfium::MakeUnique<CFXJSE_Context>(pIsolate);
186
187 v8::Local<v8::ObjectTemplate> hObjectTemplate;
188 if (pGlobalClass) {
189 CFXJSE_Class* pGlobalClassObj =
190 CFXJSE_Class::Create(pContext.get(), pGlobalClass, true);
191 ASSERT(pGlobalClassObj);
192 v8::Local<v8::FunctionTemplate> hFunctionTemplate =
193 v8::Local<v8::FunctionTemplate>::New(pIsolate,
194 pGlobalClassObj->m_hTemplate);
195 hObjectTemplate = hFunctionTemplate->InstanceTemplate();
196 } else {
197 hObjectTemplate = v8::ObjectTemplate::New(pIsolate);
198 hObjectTemplate->SetInternalFieldCount(2);
199 }
200 hObjectTemplate->Set(
201 v8::Symbol::GetToStringTag(pIsolate),
202 v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
203 .ToLocalChecked());
204
205 v8::Local<v8::Context> hNewContext =
206 v8::Context::New(pIsolate, nullptr, hObjectTemplate);
207
208 v8::Local<v8::Object> pThisProxy = hNewContext->Global();
209 FXJSE_UpdateProxyBinding(pThisProxy);
210
211 v8::Local<v8::Object> pThis = pThisProxy->GetPrototype().As<v8::Object>();
212 FXJSE_UpdateObjectBinding(pThis, pGlobalObject);
213
214 v8::Local<v8::Context> hRootContext = v8::Local<v8::Context>::New(
215 pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext);
216 hNewContext->SetSecurityToken(hRootContext->GetSecurityToken());
217 pContext->m_hContext.Reset(pIsolate, hNewContext);
218 return pContext;
219 }
220
CFXJSE_Context(v8::Isolate * pIsolate)221 CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
222
~CFXJSE_Context()223 CFXJSE_Context::~CFXJSE_Context() {}
224
GetGlobalObject()225 std::unique_ptr<CFXJSE_Value> CFXJSE_Context::GetGlobalObject() {
226 auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
227 CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
228 v8::Local<v8::Context> hContext =
229 v8::Local<v8::Context>::New(GetIsolate(), m_hContext);
230 v8::Local<v8::Object> hGlobalObject =
231 hContext->Global()->GetPrototype().As<v8::Object>();
232 pValue->ForceSetValue(hGlobalObject);
233 return pValue;
234 }
235
GetContext()236 v8::Local<v8::Context> CFXJSE_Context::GetContext() {
237 return v8::Local<v8::Context>::New(GetIsolate(), m_hContext);
238 }
239
AddClass(std::unique_ptr<CFXJSE_Class> pClass)240 void CFXJSE_Context::AddClass(std::unique_ptr<CFXJSE_Class> pClass) {
241 m_rgClasses.push_back(std::move(pClass));
242 }
243
GetClassByName(ByteStringView szName) const244 CFXJSE_Class* CFXJSE_Context::GetClassByName(ByteStringView szName) const {
245 auto pClass =
246 std::find_if(m_rgClasses.begin(), m_rgClasses.end(),
247 [szName](const std::unique_ptr<CFXJSE_Class>& item) {
248 return szName == item->m_szClassName;
249 });
250 return pClass != m_rgClasses.end() ? pClass->get() : nullptr;
251 }
252
EnableCompatibleMode()253 void CFXJSE_Context::EnableCompatibleMode() {
254 ExecuteScript(szCompatibleModeScript, nullptr, nullptr);
255 ExecuteScript(szConsoleScript, nullptr, nullptr);
256 }
257
ExecuteScript(const char * szScript,CFXJSE_Value * lpRetValue,CFXJSE_Value * lpNewThisObject)258 bool CFXJSE_Context::ExecuteScript(const char* szScript,
259 CFXJSE_Value* lpRetValue,
260 CFXJSE_Value* lpNewThisObject) {
261 CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
262 v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
263 v8::TryCatch trycatch(GetIsolate());
264 v8::Local<v8::String> hScriptString =
265 v8::String::NewFromUtf8(GetIsolate(), szScript,
266 v8::NewStringType::kNormal)
267 .ToLocalChecked();
268 if (!lpNewThisObject) {
269 v8::Local<v8::Script> hScript;
270 if (v8::Script::Compile(hContext, hScriptString).ToLocal(&hScript)) {
271 ASSERT(!trycatch.HasCaught());
272 v8::Local<v8::Value> hValue;
273 if (hScript->Run(hContext).ToLocal(&hValue)) {
274 ASSERT(!trycatch.HasCaught());
275 if (lpRetValue)
276 lpRetValue->ForceSetValue(hValue);
277 return true;
278 }
279 }
280 if (lpRetValue)
281 lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
282 return false;
283 }
284
285 v8::Local<v8::Value> hNewThis = v8::Local<v8::Value>::New(
286 GetIsolate(), lpNewThisObject->DirectGetValue());
287 ASSERT(!hNewThis.IsEmpty());
288 v8::Local<v8::String> hEval =
289 v8::String::NewFromUtf8(GetIsolate(),
290 "(function () { return eval(arguments[0]); })",
291 v8::NewStringType::kNormal)
292 .ToLocalChecked();
293 v8::Local<v8::Script> hWrapper =
294 v8::Script::Compile(hContext, hEval).ToLocalChecked();
295 v8::Local<v8::Value> hWrapperValue;
296 if (hWrapper->Run(hContext).ToLocal(&hWrapperValue)) {
297 ASSERT(!trycatch.HasCaught());
298 v8::Local<v8::Function> hWrapperFn = hWrapperValue.As<v8::Function>();
299 v8::Local<v8::Value> rgArgs[] = {hScriptString};
300 v8::Local<v8::Value> hValue;
301 if (hWrapperFn->Call(hContext, hNewThis.As<v8::Object>(), 1, rgArgs)
302 .ToLocal(&hValue)) {
303 ASSERT(!trycatch.HasCaught());
304 if (lpRetValue)
305 lpRetValue->ForceSetValue(hValue);
306 return true;
307 }
308 }
309
310 #ifndef NDEBUG
311 v8::String::Utf8Value error(GetIsolate(), trycatch.Exception());
312 fprintf(stderr, "JS Error: %s\n", *error);
313
314 v8::Local<v8::Message> message = trycatch.Message();
315 if (!message.IsEmpty()) {
316 v8::Local<v8::Context> context(GetIsolate()->GetCurrentContext());
317 int linenum = message->GetLineNumber(context).FromJust();
318 v8::String::Utf8Value sourceline(
319 GetIsolate(), message->GetSourceLine(context).ToLocalChecked());
320 fprintf(stderr, "Line %d: %s\n", linenum, *sourceline);
321 }
322 #endif // NDEBUG
323
324 if (lpRetValue)
325 lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
326 return false;
327 }
328