Home | History | Annotate | Download | only in javascript
      1 // Copyright 2014 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 "fpdfsdk/javascript/global.h"
      8 
      9 #include <vector>
     10 
     11 #include "core/fxcrt/fx_ext.h"
     12 #include "fpdfsdk/javascript/JS_Define.h"
     13 #include "fpdfsdk/javascript/JS_EventHandler.h"
     14 #include "fpdfsdk/javascript/JS_GlobalData.h"
     15 #include "fpdfsdk/javascript/JS_Object.h"
     16 #include "fpdfsdk/javascript/JS_Value.h"
     17 #include "fpdfsdk/javascript/cjs_event_context.h"
     18 #include "fpdfsdk/javascript/resource.h"
     19 
     20 JSConstSpec CJS_Global::ConstSpecs[] = {{0, JSConstSpec::Number, 0, 0}};
     21 
     22 JSPropertySpec CJS_Global::PropertySpecs[] = {{0, 0, 0}};
     23 
     24 JSMethodSpec CJS_Global::MethodSpecs[] = {
     25     {"setPersistent", setPersistent_static},
     26     {0, 0}};
     27 
     28 IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, JSGlobalAlternate, global);
     29 
     30 void CJS_Global::InitInstance(IJS_Runtime* pIRuntime) {
     31   CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
     32   JSGlobalAlternate* pGlobal =
     33       static_cast<JSGlobalAlternate*>(GetEmbedObject());
     34   pGlobal->Initial(pRuntime->GetFormFillEnv());
     35 }
     36 
     37 JSGlobalData::JSGlobalData()
     38     : nType(JS_GlobalDataType::NUMBER),
     39       dData(0),
     40       bData(false),
     41       sData(""),
     42       bPersistent(false),
     43       bDeleted(false) {}
     44 
     45 JSGlobalData::~JSGlobalData() {
     46   pData.Reset();
     47 }
     48 
     49 JSGlobalAlternate::JSGlobalAlternate(CJS_Object* pJSObject)
     50     : CJS_EmbedObj(pJSObject), m_pFormFillEnv(nullptr) {}
     51 
     52 JSGlobalAlternate::~JSGlobalAlternate() {
     53   DestroyGlobalPersisitentVariables();
     54   m_pGlobalData->Release();
     55 }
     56 
     57 void JSGlobalAlternate::Initial(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
     58   m_pFormFillEnv.Reset(pFormFillEnv);
     59   m_pGlobalData = CJS_GlobalData::GetRetainedInstance(pFormFillEnv);
     60   UpdateGlobalPersistentVariables();
     61 }
     62 
     63 bool JSGlobalAlternate::QueryProperty(const FX_WCHAR* propname) {
     64   return CFX_WideString(propname) != L"setPersistent";
     65 }
     66 
     67 bool JSGlobalAlternate::DelProperty(CJS_Runtime* pRuntime,
     68                                     const FX_WCHAR* propname,
     69                                     CFX_WideString& sError) {
     70   auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
     71   if (it == m_mapGlobal.end())
     72     return false;
     73 
     74   it->second->bDeleted = true;
     75   return true;
     76 }
     77 
     78 bool JSGlobalAlternate::DoProperty(CJS_Runtime* pRuntime,
     79                                    const FX_WCHAR* propname,
     80                                    CJS_PropValue& vp,
     81                                    CFX_WideString& sError) {
     82   if (vp.IsSetting()) {
     83     CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname);
     84     switch (vp.GetJSValue()->GetType()) {
     85       case CJS_Value::VT_number: {
     86         double dData;
     87         vp >> dData;
     88         return SetGlobalVariables(sPropName, JS_GlobalDataType::NUMBER, dData,
     89                                   false, "", v8::Local<v8::Object>(), false);
     90       }
     91       case CJS_Value::VT_boolean: {
     92         bool bData;
     93         vp >> bData;
     94         return SetGlobalVariables(sPropName, JS_GlobalDataType::BOOLEAN, 0,
     95                                   bData, "", v8::Local<v8::Object>(), false);
     96       }
     97       case CJS_Value::VT_string: {
     98         CFX_ByteString sData;
     99         vp >> sData;
    100         return SetGlobalVariables(sPropName, JS_GlobalDataType::STRING, 0,
    101                                   false, sData, v8::Local<v8::Object>(), false);
    102       }
    103       case CJS_Value::VT_object: {
    104         v8::Local<v8::Object> pData;
    105         vp >> pData;
    106         return SetGlobalVariables(sPropName, JS_GlobalDataType::OBJECT, 0,
    107                                   false, "", pData, false);
    108       }
    109       case CJS_Value::VT_null: {
    110         return SetGlobalVariables(sPropName, JS_GlobalDataType::NULLOBJ, 0,
    111                                   false, "", v8::Local<v8::Object>(), false);
    112       }
    113       case CJS_Value::VT_undefined: {
    114         DelProperty(pRuntime, propname, sError);
    115         return true;
    116       }
    117       default:
    118         break;
    119     }
    120   } else {
    121     auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
    122     if (it == m_mapGlobal.end()) {
    123       vp.GetJSValue()->SetNull(pRuntime);
    124       return true;
    125     }
    126     JSGlobalData* pData = it->second;
    127     if (pData->bDeleted) {
    128       vp.GetJSValue()->SetNull(pRuntime);
    129       return true;
    130     }
    131     switch (pData->nType) {
    132       case JS_GlobalDataType::NUMBER:
    133         vp << pData->dData;
    134         return true;
    135       case JS_GlobalDataType::BOOLEAN:
    136         vp << pData->bData;
    137         return true;
    138       case JS_GlobalDataType::STRING:
    139         vp << pData->sData;
    140         return true;
    141       case JS_GlobalDataType::OBJECT: {
    142         v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
    143             vp.GetJSRuntime()->GetIsolate(), pData->pData);
    144         vp << obj;
    145         return true;
    146       }
    147       case JS_GlobalDataType::NULLOBJ:
    148         vp.GetJSValue()->SetNull(pRuntime);
    149         return true;
    150       default:
    151         break;
    152     }
    153   }
    154   return false;
    155 }
    156 
    157 bool JSGlobalAlternate::setPersistent(CJS_Runtime* pRuntime,
    158                                       const std::vector<CJS_Value>& params,
    159                                       CJS_Value& vRet,
    160                                       CFX_WideString& sError) {
    161   if (params.size() != 2) {
    162     sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
    163     return false;
    164   }
    165 
    166   auto it = m_mapGlobal.find(params[0].ToCFXByteString(pRuntime));
    167   if (it != m_mapGlobal.end()) {
    168     JSGlobalData* pData = it->second;
    169     if (!pData->bDeleted) {
    170       pData->bPersistent = params[1].ToBool(pRuntime);
    171       return true;
    172     }
    173   }
    174 
    175   sError = JSGetStringFromID(IDS_STRING_JSNOGLOBAL);
    176   return false;
    177 }
    178 
    179 void JSGlobalAlternate::UpdateGlobalPersistentVariables() {
    180   CJS_Runtime* pRuntime =
    181       static_cast<CJS_Runtime*>(CFXJS_Engine::CurrentEngineFromIsolate(
    182           m_pJSObject->ToV8Object()->GetIsolate()));
    183 
    184   for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
    185     CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i);
    186     switch (pData->data.nType) {
    187       case JS_GlobalDataType::NUMBER:
    188         SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::NUMBER,
    189                            pData->data.dData, false, "",
    190                            v8::Local<v8::Object>(), pData->bPersistent == 1);
    191         pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
    192                                     pData->data.sKey.UTF8Decode(),
    193                                     pRuntime->NewNumber(pData->data.dData));
    194         break;
    195       case JS_GlobalDataType::BOOLEAN:
    196         SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::BOOLEAN, 0,
    197                            pData->data.bData == 1, "", v8::Local<v8::Object>(),
    198                            pData->bPersistent == 1);
    199         pRuntime->PutObjectProperty(
    200             m_pJSObject->ToV8Object(), pData->data.sKey.UTF8Decode(),
    201             pRuntime->NewBoolean(pData->data.bData == 1));
    202         break;
    203       case JS_GlobalDataType::STRING:
    204         SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::STRING, 0,
    205                            false, pData->data.sData, v8::Local<v8::Object>(),
    206                            pData->bPersistent == 1);
    207         pRuntime->PutObjectProperty(
    208             m_pJSObject->ToV8Object(), pData->data.sKey.UTF8Decode(),
    209             pRuntime->NewString(pData->data.sData.UTF8Decode().AsStringC()));
    210         break;
    211       case JS_GlobalDataType::OBJECT: {
    212         v8::Local<v8::Object> pObj = pRuntime->NewFxDynamicObj(-1);
    213         PutObjectProperty(pObj, &pData->data);
    214         SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::OBJECT, 0,
    215                            false, "", pObj, pData->bPersistent == 1);
    216         pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
    217                                     pData->data.sKey.UTF8Decode(), pObj);
    218       } break;
    219       case JS_GlobalDataType::NULLOBJ:
    220         SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::NULLOBJ, 0,
    221                            false, "", v8::Local<v8::Object>(),
    222                            pData->bPersistent == 1);
    223         pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
    224                                     pData->data.sKey.UTF8Decode(),
    225                                     pRuntime->NewNull());
    226         break;
    227     }
    228   }
    229 }
    230 
    231 void JSGlobalAlternate::CommitGlobalPersisitentVariables(
    232     CJS_Runtime* pRuntime) {
    233   for (auto it = m_mapGlobal.begin(); it != m_mapGlobal.end(); ++it) {
    234     CFX_ByteString name = it->first;
    235     JSGlobalData* pData = it->second;
    236     if (pData->bDeleted) {
    237       m_pGlobalData->DeleteGlobalVariable(name);
    238     } else {
    239       switch (pData->nType) {
    240         case JS_GlobalDataType::NUMBER:
    241           m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
    242           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
    243           break;
    244         case JS_GlobalDataType::BOOLEAN:
    245           m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
    246           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
    247           break;
    248         case JS_GlobalDataType::STRING:
    249           m_pGlobalData->SetGlobalVariableString(name, pData->sData);
    250           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
    251           break;
    252         case JS_GlobalDataType::OBJECT: {
    253           CJS_GlobalVariableArray array;
    254           v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
    255               GetJSObject()->GetIsolate(), pData->pData);
    256           ObjectToArray(pRuntime, obj, array);
    257           m_pGlobalData->SetGlobalVariableObject(name, array);
    258           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
    259         } break;
    260         case JS_GlobalDataType::NULLOBJ:
    261           m_pGlobalData->SetGlobalVariableNull(name);
    262           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
    263           break;
    264       }
    265     }
    266   }
    267 }
    268 
    269 void JSGlobalAlternate::ObjectToArray(CJS_Runtime* pRuntime,
    270                                       v8::Local<v8::Object> pObj,
    271                                       CJS_GlobalVariableArray& array) {
    272   std::vector<CFX_WideString> pKeyList = pRuntime->GetObjectPropertyNames(pObj);
    273   for (const auto& ws : pKeyList) {
    274     CFX_ByteString sKey = ws.UTF8Encode();
    275     v8::Local<v8::Value> v = pRuntime->GetObjectProperty(pObj, ws);
    276     switch (CJS_Value::GetValueType(v)) {
    277       case CJS_Value::VT_number: {
    278         CJS_KeyValue* pObjElement = new CJS_KeyValue;
    279         pObjElement->nType = JS_GlobalDataType::NUMBER;
    280         pObjElement->sKey = sKey;
    281         pObjElement->dData = pRuntime->ToDouble(v);
    282         array.Add(pObjElement);
    283       } break;
    284       case CJS_Value::VT_boolean: {
    285         CJS_KeyValue* pObjElement = new CJS_KeyValue;
    286         pObjElement->nType = JS_GlobalDataType::BOOLEAN;
    287         pObjElement->sKey = sKey;
    288         pObjElement->dData = pRuntime->ToBoolean(v);
    289         array.Add(pObjElement);
    290       } break;
    291       case CJS_Value::VT_string: {
    292         CFX_ByteString sValue =
    293             CJS_Value(pRuntime, v).ToCFXByteString(pRuntime);
    294         CJS_KeyValue* pObjElement = new CJS_KeyValue;
    295         pObjElement->nType = JS_GlobalDataType::STRING;
    296         pObjElement->sKey = sKey;
    297         pObjElement->sData = sValue;
    298         array.Add(pObjElement);
    299       } break;
    300       case CJS_Value::VT_object: {
    301         CJS_KeyValue* pObjElement = new CJS_KeyValue;
    302         pObjElement->nType = JS_GlobalDataType::OBJECT;
    303         pObjElement->sKey = sKey;
    304         ObjectToArray(pRuntime, pRuntime->ToObject(v), pObjElement->objData);
    305         array.Add(pObjElement);
    306       } break;
    307       case CJS_Value::VT_null: {
    308         CJS_KeyValue* pObjElement = new CJS_KeyValue;
    309         pObjElement->nType = JS_GlobalDataType::NULLOBJ;
    310         pObjElement->sKey = sKey;
    311         array.Add(pObjElement);
    312       } break;
    313       default:
    314         break;
    315     }
    316   }
    317 }
    318 
    319 void JSGlobalAlternate::PutObjectProperty(v8::Local<v8::Object> pObj,
    320                                           CJS_KeyValue* pData) {
    321   CJS_Runtime* pRuntime = CJS_Runtime::CurrentRuntimeFromIsolate(
    322       m_pJSObject->ToV8Object()->GetIsolate());
    323 
    324   for (int i = 0, sz = pData->objData.Count(); i < sz; i++) {
    325     CJS_KeyValue* pObjData = pData->objData.GetAt(i);
    326     switch (pObjData->nType) {
    327       case JS_GlobalDataType::NUMBER:
    328         pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
    329                                     pRuntime->NewNumber(pObjData->dData));
    330         break;
    331       case JS_GlobalDataType::BOOLEAN:
    332         pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
    333                                     pRuntime->NewBoolean(pObjData->bData == 1));
    334         break;
    335       case JS_GlobalDataType::STRING:
    336         pRuntime->PutObjectProperty(
    337             pObj, pObjData->sKey.UTF8Decode(),
    338             pRuntime->NewString(pObjData->sData.UTF8Decode().AsStringC()));
    339         break;
    340       case JS_GlobalDataType::OBJECT: {
    341         v8::Local<v8::Object> pNewObj = pRuntime->NewFxDynamicObj(-1);
    342         PutObjectProperty(pNewObj, pObjData);
    343         pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(), pNewObj);
    344       } break;
    345       case JS_GlobalDataType::NULLOBJ:
    346         pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
    347                                     pRuntime->NewNull());
    348         break;
    349     }
    350   }
    351 }
    352 
    353 void JSGlobalAlternate::DestroyGlobalPersisitentVariables() {
    354   for (const auto& pair : m_mapGlobal) {
    355     delete pair.second;
    356   }
    357   m_mapGlobal.clear();
    358 }
    359 
    360 bool JSGlobalAlternate::SetGlobalVariables(const CFX_ByteString& propname,
    361                                            JS_GlobalDataType nType,
    362                                            double dData,
    363                                            bool bData,
    364                                            const CFX_ByteString& sData,
    365                                            v8::Local<v8::Object> pData,
    366                                            bool bDefaultPersistent) {
    367   if (propname.IsEmpty())
    368     return false;
    369 
    370   auto it = m_mapGlobal.find(propname);
    371   if (it != m_mapGlobal.end()) {
    372     JSGlobalData* pTemp = it->second;
    373     if (pTemp->bDeleted || pTemp->nType != nType) {
    374       pTemp->dData = 0;
    375       pTemp->bData = 0;
    376       pTemp->sData = "";
    377       pTemp->nType = nType;
    378     }
    379 
    380     pTemp->bDeleted = false;
    381     switch (nType) {
    382       case JS_GlobalDataType::NUMBER: {
    383         pTemp->dData = dData;
    384       } break;
    385       case JS_GlobalDataType::BOOLEAN: {
    386         pTemp->bData = bData;
    387       } break;
    388       case JS_GlobalDataType::STRING: {
    389         pTemp->sData = sData;
    390       } break;
    391       case JS_GlobalDataType::OBJECT: {
    392         pTemp->pData.Reset(pData->GetIsolate(), pData);
    393       } break;
    394       case JS_GlobalDataType::NULLOBJ:
    395         break;
    396       default:
    397         return false;
    398     }
    399     return true;
    400   }
    401 
    402   JSGlobalData* pNewData = nullptr;
    403 
    404   switch (nType) {
    405     case JS_GlobalDataType::NUMBER: {
    406       pNewData = new JSGlobalData;
    407       pNewData->nType = JS_GlobalDataType::NUMBER;
    408       pNewData->dData = dData;
    409       pNewData->bPersistent = bDefaultPersistent;
    410     } break;
    411     case JS_GlobalDataType::BOOLEAN: {
    412       pNewData = new JSGlobalData;
    413       pNewData->nType = JS_GlobalDataType::BOOLEAN;
    414       pNewData->bData = bData;
    415       pNewData->bPersistent = bDefaultPersistent;
    416     } break;
    417     case JS_GlobalDataType::STRING: {
    418       pNewData = new JSGlobalData;
    419       pNewData->nType = JS_GlobalDataType::STRING;
    420       pNewData->sData = sData;
    421       pNewData->bPersistent = bDefaultPersistent;
    422     } break;
    423     case JS_GlobalDataType::OBJECT: {
    424       pNewData = new JSGlobalData;
    425       pNewData->nType = JS_GlobalDataType::OBJECT;
    426       pNewData->pData.Reset(pData->GetIsolate(), pData);
    427       pNewData->bPersistent = bDefaultPersistent;
    428     } break;
    429     case JS_GlobalDataType::NULLOBJ: {
    430       pNewData = new JSGlobalData;
    431       pNewData->nType = JS_GlobalDataType::NULLOBJ;
    432       pNewData->bPersistent = bDefaultPersistent;
    433     } break;
    434     default:
    435       return false;
    436   }
    437 
    438   m_mapGlobal[propname] = pNewData;
    439   return true;
    440 }
    441