// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "xfa/fxjse/context.h" #include "xfa/fxjse/class.h" #include "xfa/fxjse/scope_inline.h" #include "xfa/fxjse/util_inline.h" #include "xfa/fxjse/value.h" FXJSE_HCONTEXT FXJSE_Context_Create(FXJSE_HRUNTIME hRuntime, const FXJSE_CLASS* lpGlobalClass, void* lpGlobalObject) { CFXJSE_Context* pContext = CFXJSE_Context::Create( reinterpret_cast(hRuntime), lpGlobalClass, lpGlobalObject); return reinterpret_cast(pContext); } void FXJSE_Context_Release(FXJSE_HCONTEXT hContext) { CFXJSE_Context* pContext = reinterpret_cast(hContext); if (pContext) { delete pContext; } } FXJSE_HVALUE FXJSE_Context_GetGlobalObject(FXJSE_HCONTEXT hContext) { CFXJSE_Context* pContext = reinterpret_cast(hContext); if (!pContext) { return NULL; } CFXJSE_Value* lpValue = CFXJSE_Value::Create(pContext->GetRuntime()); ASSERT(lpValue); pContext->GetGlobalObject(lpValue); return reinterpret_cast(lpValue); } FXJSE_HRUNTIME FXJSE_Context_GetRuntime(FXJSE_HCONTEXT hContext) { CFXJSE_Context* pContext = reinterpret_cast(hContext); return pContext ? reinterpret_cast(pContext->GetRuntime()) : NULL; } static const FX_CHAR* szCompatibleModeScripts[] = { "(function(global, list) {\n" " 'use strict';\n" " var objname;\n" " for (objname in list) {\n" " var globalobj = global[objname];\n" " if (globalobj) {\n" " list[objname].forEach(function(name) {\n" " if (!globalobj[name]) {\n" " Object.defineProperty(globalobj, name, {\n" " writable: true,\n" " enumerable: false,\n" " value: (function(obj) {\n" " if (arguments.length === 0) {\n" " throw new TypeError('missing argument 0 when calling " " function ' + objname + '.' + name);\n" " }\n" " return globalobj.prototype[name].apply(obj, " " Array.prototype.slice.call(arguments, 1));\n" " })\n" " });\n" " }\n" " });\n" " }\n" " }\n" "}(this, {String: ['substr', 'toUpperCase']}));"}; void FXJSE_Context_EnableCompatibleMode(FXJSE_HCONTEXT hContext, FX_DWORD dwCompatibleFlags) { for (uint32_t i = 0; i < (uint32_t)FXJSE_COMPATIBLEMODEFLAGCOUNT; i++) { if (dwCompatibleFlags & (1 << i)) { FXJSE_ExecuteScript(hContext, szCompatibleModeScripts[i], NULL, NULL); } } } FX_BOOL FXJSE_ExecuteScript(FXJSE_HCONTEXT hContext, const FX_CHAR* szScript, FXJSE_HVALUE hRetValue, FXJSE_HVALUE hNewThisObject) { CFXJSE_Context* pContext = reinterpret_cast(hContext); ASSERT(pContext); return pContext->ExecuteScript( szScript, reinterpret_cast(hRetValue), reinterpret_cast(hNewThisObject)); } v8::Local FXJSE_CreateReturnValue(v8::Isolate* pIsolate, v8::TryCatch& trycatch) { v8::Local hReturnValue = v8::Object::New(pIsolate); if (trycatch.HasCaught()) { v8::Local hException = trycatch.Exception(); v8::Local hMessage = trycatch.Message(); if (hException->IsObject()) { v8::Local hValue; hValue = hException.As()->Get( v8::String::NewFromUtf8(pIsolate, "name")); if (hValue->IsString() || hValue->IsStringObject()) { hReturnValue->Set(0, hValue); } else { hReturnValue->Set(0, v8::String::NewFromUtf8(pIsolate, "Error")); } hValue = hException.As()->Get( v8::String::NewFromUtf8(pIsolate, "message")); if (hValue->IsString() || hValue->IsStringObject()) { hReturnValue->Set(1, hValue); } else { hReturnValue->Set(1, hMessage->Get()); } } else { hReturnValue->Set(0, v8::String::NewFromUtf8(pIsolate, "Error")); hReturnValue->Set(1, hMessage->Get()); } hReturnValue->Set(2, hException); hReturnValue->Set(3, v8::Integer::New(pIsolate, hMessage->GetLineNumber())); hReturnValue->Set(4, hMessage->GetSourceLine()); v8::Maybe maybe_int = hMessage->GetStartColumn(pIsolate->GetCurrentContext()); hReturnValue->Set(5, v8::Integer::New(pIsolate, maybe_int.FromMaybe(0))); maybe_int = hMessage->GetEndColumn(pIsolate->GetCurrentContext()); hReturnValue->Set(6, v8::Integer::New(pIsolate, maybe_int.FromMaybe(0))); } return hReturnValue; } FX_BOOL FXJSE_ReturnValue_GetMessage(FXJSE_HVALUE hRetValue, CFX_ByteString& utf8Name, CFX_ByteString& utf8Message) { CFXJSE_Value* lpValue = reinterpret_cast(hRetValue); if (!lpValue) { return FALSE; } v8::Isolate* pIsolate = lpValue->GetIsolate(); CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); v8::Local hValue = v8::Local::New(pIsolate, lpValue->DirectGetValue()); if (!hValue->IsObject()) { return FALSE; } v8::String::Utf8Value hStringVal0( hValue.As()->Get(0)->ToString()); utf8Name = *hStringVal0; v8::String::Utf8Value hStringVal1( hValue.As()->Get(1)->ToString()); utf8Message = *hStringVal1; return TRUE; } FX_BOOL FXJSE_ReturnValue_GetLineInfo(FXJSE_HVALUE hRetValue, int32_t& nLine, int32_t& nCol) { CFXJSE_Value* lpValue = reinterpret_cast(hRetValue); if (!lpValue) { return FALSE; } v8::Isolate* pIsolate = lpValue->GetIsolate(); CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); v8::Local hValue = v8::Local::New(pIsolate, lpValue->DirectGetValue()); if (!hValue->IsObject()) { return FALSE; } v8::MaybeLocal maybe_int = hValue.As()->Get(3)->ToInt32(pIsolate->GetCurrentContext()); nLine = maybe_int.FromMaybe(v8::Local())->Value(); maybe_int = hValue.As()->Get(5)->ToInt32(pIsolate->GetCurrentContext()); nCol = maybe_int.FromMaybe(v8::Local())->Value(); return TRUE; } CFXJSE_Context* CFXJSE_Context::Create(v8::Isolate* pIsolate, const FXJSE_CLASS* lpGlobalClass, void* lpGlobalObject) { CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); CFXJSE_Context* pContext = new CFXJSE_Context(pIsolate); CFXJSE_Class* lpGlobalClassObj = NULL; v8::Local hObjectTemplate; if (lpGlobalClass) { lpGlobalClassObj = CFXJSE_Class::Create(pContext, lpGlobalClass, TRUE); ASSERT(lpGlobalClassObj); v8::Local hFunctionTemplate = v8::Local::New(pIsolate, lpGlobalClassObj->m_hTemplate); hObjectTemplate = hFunctionTemplate->InstanceTemplate(); } else { hObjectTemplate = v8::ObjectTemplate::New(pIsolate); hObjectTemplate->SetInternalFieldCount(1); } v8::Local hNewContext = v8::Context::New(pIsolate, NULL, hObjectTemplate); v8::Local hRootContext = v8::Local::New( pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext); hNewContext->SetSecurityToken(hRootContext->GetSecurityToken()); v8::Local hGlobalObject = FXJSE_GetGlobalObjectFromContext(hNewContext); FXJSE_UpdateObjectBinding(hGlobalObject, lpGlobalObject); pContext->m_hContext.Reset(pIsolate, hNewContext); return pContext; } CFXJSE_Context::~CFXJSE_Context() { for (int32_t i = 0, count = m_rgClasses.GetSize(); i < count; i++) { CFXJSE_Class* pClass = m_rgClasses[i]; if (pClass) { delete pClass; } } m_rgClasses.RemoveAll(); } void CFXJSE_Context::GetGlobalObject(CFXJSE_Value* pValue) { ASSERT(pValue); CFXJSE_ScopeUtil_IsolateHandleContext scope(this); v8::Local hContext = v8::Local::New(m_pIsolate, m_hContext); v8::Local hGlobalObject = hContext->Global(); pValue->ForceSetValue(hGlobalObject); } FX_BOOL CFXJSE_Context::ExecuteScript(const FX_CHAR* szScript, CFXJSE_Value* lpRetValue, CFXJSE_Value* lpNewThisObject) { CFXJSE_ScopeUtil_IsolateHandleContext scope(this); v8::TryCatch trycatch(m_pIsolate); v8::Local hScriptString = v8::String::NewFromUtf8(m_pIsolate, szScript); if (lpNewThisObject == NULL) { v8::Local hScript = v8::Script::Compile(hScriptString); if (!trycatch.HasCaught()) { v8::Local hValue = hScript->Run(); if (!trycatch.HasCaught()) { if (lpRetValue) { lpRetValue->m_hValue.Reset(m_pIsolate, hValue); } return TRUE; } } if (lpRetValue) { lpRetValue->m_hValue.Reset(m_pIsolate, FXJSE_CreateReturnValue(m_pIsolate, trycatch)); } return FALSE; } else { v8::Local hNewThis = v8::Local::New(m_pIsolate, lpNewThisObject->m_hValue); ASSERT(!hNewThis.IsEmpty()); v8::Local hWrapper = v8::Script::Compile(v8::String::NewFromUtf8( m_pIsolate, "(function () { return eval(arguments[0]); })")); v8::Local hWrapperValue = hWrapper->Run(); ASSERT(hWrapperValue->IsFunction()); v8::Local hWrapperFn = hWrapperValue.As(); if (!trycatch.HasCaught()) { v8::Local rgArgs[] = {hScriptString}; v8::Local hValue = hWrapperFn->Call(hNewThis.As(), 1, rgArgs); if (!trycatch.HasCaught()) { if (lpRetValue) { lpRetValue->m_hValue.Reset(m_pIsolate, hValue); } return TRUE; } } if (lpRetValue) { lpRetValue->m_hValue.Reset(m_pIsolate, FXJSE_CreateReturnValue(m_pIsolate, trycatch)); } return FALSE; } }