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/cfxjse_context.h" 8 9 #include "fxjs/cfxjse_class.h" 10 #include "fxjs/cfxjse_value.h" 11 12 namespace { 13 14 const FX_CHAR szCompatibleModeScript[] = 15 "(function(global, list) {\n" 16 " 'use strict';\n" 17 " var objname;\n" 18 " for (objname in list) {\n" 19 " var globalobj = global[objname];\n" 20 " if (globalobj) {\n" 21 " list[objname].forEach(function(name) {\n" 22 " if (!globalobj[name]) {\n" 23 " Object.defineProperty(globalobj, name, {\n" 24 " writable: true,\n" 25 " enumerable: false,\n" 26 " value: (function(obj) {\n" 27 " if (arguments.length === 0) {\n" 28 " throw new TypeError('missing argument 0 when calling " 29 " function ' + objname + '.' + name);\n" 30 " }\n" 31 " return globalobj.prototype[name].apply(obj, " 32 " Array.prototype.slice.call(arguments, 1));\n" 33 " })\n" 34 " });\n" 35 " }\n" 36 " });\n" 37 " }\n" 38 " }\n" 39 "}(this, {String: ['substr', 'toUpperCase']}));"; 40 41 } // namespace 42 43 // Note, not in the anonymous namespace due to the friend call 44 // in cfxjse_context.h 45 // TODO(dsinclair): Remove the friending, use public methods. 46 class CFXJSE_ScopeUtil_IsolateHandleContext { 47 public: 48 explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext) 49 : m_context(pContext), 50 m_parent(pContext->m_pIsolate), 51 m_cscope(v8::Local<v8::Context>::New(pContext->m_pIsolate, 52 pContext->m_hContext)) {} 53 v8::Isolate* GetIsolate() { return m_context->m_pIsolate; } 54 v8::Local<v8::Context> GetLocalContext() { 55 return v8::Local<v8::Context>::New(m_context->m_pIsolate, 56 m_context->m_hContext); 57 } 58 59 private: 60 CFXJSE_ScopeUtil_IsolateHandleContext( 61 const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete; 62 void operator=(const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete; 63 void* operator new(size_t size) = delete; 64 void operator delete(void*, size_t) = delete; 65 66 CFXJSE_Context* m_context; 67 CFXJSE_ScopeUtil_IsolateHandle m_parent; 68 v8::Context::Scope m_cscope; 69 }; 70 71 v8::Local<v8::Object> FXJSE_GetGlobalObjectFromContext( 72 const v8::Local<v8::Context>& hContext) { 73 return hContext->Global()->GetPrototype().As<v8::Object>(); 74 } 75 76 void FXJSE_UpdateObjectBinding(v8::Local<v8::Object>& hObject, 77 CFXJSE_HostObject* lpNewBinding) { 78 ASSERT(!hObject.IsEmpty()); 79 ASSERT(hObject->InternalFieldCount() > 0); 80 hObject->SetAlignedPointerInInternalField(0, 81 static_cast<void*>(lpNewBinding)); 82 } 83 84 CFXJSE_HostObject* FXJSE_RetrieveObjectBinding( 85 const v8::Local<v8::Object>& hJSObject, 86 CFXJSE_Class* lpClass) { 87 ASSERT(!hJSObject.IsEmpty()); 88 if (!hJSObject->IsObject()) 89 return nullptr; 90 91 v8::Local<v8::Object> hObject = hJSObject; 92 if (hObject->InternalFieldCount() == 0) { 93 v8::Local<v8::Value> hProtoObject = hObject->GetPrototype(); 94 if (hProtoObject.IsEmpty() || !hProtoObject->IsObject()) 95 return nullptr; 96 97 hObject = hProtoObject.As<v8::Object>(); 98 if (hObject->InternalFieldCount() == 0) 99 return nullptr; 100 } 101 if (lpClass) { 102 v8::Local<v8::FunctionTemplate> hClass = 103 v8::Local<v8::FunctionTemplate>::New( 104 lpClass->GetContext()->GetRuntime(), lpClass->GetTemplate()); 105 if (!hClass->HasInstance(hObject)) 106 return nullptr; 107 } 108 return static_cast<CFXJSE_HostObject*>( 109 hObject->GetAlignedPointerFromInternalField(0)); 110 } 111 112 v8::Local<v8::Object> FXJSE_CreateReturnValue(v8::Isolate* pIsolate, 113 v8::TryCatch& trycatch) { 114 v8::Local<v8::Object> hReturnValue = v8::Object::New(pIsolate); 115 if (trycatch.HasCaught()) { 116 v8::Local<v8::Value> hException = trycatch.Exception(); 117 v8::Local<v8::Message> hMessage = trycatch.Message(); 118 if (hException->IsObject()) { 119 v8::Local<v8::Value> hValue; 120 hValue = hException.As<v8::Object>()->Get( 121 v8::String::NewFromUtf8(pIsolate, "name")); 122 if (hValue->IsString() || hValue->IsStringObject()) 123 hReturnValue->Set(0, hValue); 124 else 125 hReturnValue->Set(0, v8::String::NewFromUtf8(pIsolate, "Error")); 126 127 hValue = hException.As<v8::Object>()->Get( 128 v8::String::NewFromUtf8(pIsolate, "message")); 129 if (hValue->IsString() || hValue->IsStringObject()) 130 hReturnValue->Set(1, hValue); 131 else 132 hReturnValue->Set(1, hMessage->Get()); 133 } else { 134 hReturnValue->Set(0, v8::String::NewFromUtf8(pIsolate, "Error")); 135 hReturnValue->Set(1, hMessage->Get()); 136 } 137 hReturnValue->Set(2, hException); 138 hReturnValue->Set(3, v8::Integer::New(pIsolate, hMessage->GetLineNumber())); 139 hReturnValue->Set(4, hMessage->GetSourceLine()); 140 v8::Maybe<int32_t> maybe_int = 141 hMessage->GetStartColumn(pIsolate->GetCurrentContext()); 142 hReturnValue->Set(5, v8::Integer::New(pIsolate, maybe_int.FromMaybe(0))); 143 maybe_int = hMessage->GetEndColumn(pIsolate->GetCurrentContext()); 144 hReturnValue->Set(6, v8::Integer::New(pIsolate, maybe_int.FromMaybe(0))); 145 } 146 return hReturnValue; 147 } 148 149 // static 150 CFXJSE_Context* CFXJSE_Context::Create( 151 v8::Isolate* pIsolate, 152 const FXJSE_CLASS_DESCRIPTOR* lpGlobalClass, 153 CFXJSE_HostObject* lpGlobalObject) { 154 CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); 155 CFXJSE_Context* pContext = new CFXJSE_Context(pIsolate); 156 CFXJSE_Class* lpGlobalClassObj = nullptr; 157 v8::Local<v8::ObjectTemplate> hObjectTemplate; 158 if (lpGlobalClass) { 159 lpGlobalClassObj = CFXJSE_Class::Create(pContext, lpGlobalClass, true); 160 ASSERT(lpGlobalClassObj); 161 v8::Local<v8::FunctionTemplate> hFunctionTemplate = 162 v8::Local<v8::FunctionTemplate>::New(pIsolate, 163 lpGlobalClassObj->m_hTemplate); 164 hObjectTemplate = hFunctionTemplate->InstanceTemplate(); 165 } else { 166 hObjectTemplate = v8::ObjectTemplate::New(pIsolate); 167 hObjectTemplate->SetInternalFieldCount(1); 168 } 169 hObjectTemplate->Set( 170 v8::Symbol::GetToStringTag(pIsolate), 171 v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal) 172 .ToLocalChecked()); 173 v8::Local<v8::Context> hNewContext = 174 v8::Context::New(pIsolate, nullptr, hObjectTemplate); 175 v8::Local<v8::Context> hRootContext = v8::Local<v8::Context>::New( 176 pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext); 177 hNewContext->SetSecurityToken(hRootContext->GetSecurityToken()); 178 v8::Local<v8::Object> hGlobalObject = 179 FXJSE_GetGlobalObjectFromContext(hNewContext); 180 FXJSE_UpdateObjectBinding(hGlobalObject, lpGlobalObject); 181 pContext->m_hContext.Reset(pIsolate, hNewContext); 182 return pContext; 183 } 184 185 CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {} 186 187 CFXJSE_Context::~CFXJSE_Context() {} 188 189 std::unique_ptr<CFXJSE_Value> CFXJSE_Context::GetGlobalObject() { 190 std::unique_ptr<CFXJSE_Value> pValue(new CFXJSE_Value(m_pIsolate)); 191 192 CFXJSE_ScopeUtil_IsolateHandleContext scope(this); 193 v8::Local<v8::Context> hContext = 194 v8::Local<v8::Context>::New(m_pIsolate, m_hContext); 195 v8::Local<v8::Object> hGlobalObject = hContext->Global(); 196 pValue->ForceSetValue(hGlobalObject); 197 198 return pValue; 199 } 200 201 void CFXJSE_Context::EnableCompatibleMode() { 202 ExecuteScript(szCompatibleModeScript, nullptr, nullptr); 203 } 204 205 bool CFXJSE_Context::ExecuteScript(const FX_CHAR* szScript, 206 CFXJSE_Value* lpRetValue, 207 CFXJSE_Value* lpNewThisObject) { 208 CFXJSE_ScopeUtil_IsolateHandleContext scope(this); 209 v8::TryCatch trycatch(m_pIsolate); 210 v8::Local<v8::String> hScriptString = 211 v8::String::NewFromUtf8(m_pIsolate, szScript); 212 if (!lpNewThisObject) { 213 v8::Local<v8::Script> hScript = v8::Script::Compile(hScriptString); 214 if (!trycatch.HasCaught()) { 215 v8::Local<v8::Value> hValue = hScript->Run(); 216 if (!trycatch.HasCaught()) { 217 if (lpRetValue) 218 lpRetValue->m_hValue.Reset(m_pIsolate, hValue); 219 return true; 220 } 221 } 222 if (lpRetValue) { 223 lpRetValue->m_hValue.Reset(m_pIsolate, 224 FXJSE_CreateReturnValue(m_pIsolate, trycatch)); 225 } 226 return false; 227 } 228 229 v8::Local<v8::Value> hNewThis = 230 v8::Local<v8::Value>::New(m_pIsolate, lpNewThisObject->m_hValue); 231 ASSERT(!hNewThis.IsEmpty()); 232 v8::Local<v8::Script> hWrapper = v8::Script::Compile(v8::String::NewFromUtf8( 233 m_pIsolate, "(function () { return eval(arguments[0]); })")); 234 v8::Local<v8::Value> hWrapperValue = hWrapper->Run(); 235 ASSERT(hWrapperValue->IsFunction()); 236 v8::Local<v8::Function> hWrapperFn = hWrapperValue.As<v8::Function>(); 237 if (!trycatch.HasCaught()) { 238 v8::Local<v8::Value> rgArgs[] = {hScriptString}; 239 v8::Local<v8::Value> hValue = 240 hWrapperFn->Call(hNewThis.As<v8::Object>(), 1, rgArgs); 241 if (!trycatch.HasCaught()) { 242 if (lpRetValue) 243 lpRetValue->m_hValue.Reset(m_pIsolate, hValue); 244 return true; 245 } 246 } 247 if (lpRetValue) { 248 lpRetValue->m_hValue.Reset(m_pIsolate, 249 FXJSE_CreateReturnValue(m_pIsolate, trycatch)); 250 } 251 return false; 252 } 253