Home | History | Annotate | Download | only in fxjs
      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