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