// 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 "../../include/javascript/JavaScript.h" #include "../../include/javascript/IJavaScript.h" #include "../../include/javascript/JS_Define.h" #include "../../include/javascript/JS_Object.h" #include "../../include/javascript/JS_Value.h" #include "../../include/javascript/JS_GlobalData.h" #include "../../include/javascript/global.h" #include "../../include/javascript/JS_EventHandler.h" #include "../../include/javascript/JS_Context.h" /* ---------------------------- global ---------------------------- */ BEGIN_JS_STATIC_CONST(CJS_Global) END_JS_STATIC_CONST() BEGIN_JS_STATIC_PROP(CJS_Global) END_JS_STATIC_PROP() BEGIN_JS_STATIC_METHOD(CJS_Global) JS_STATIC_METHOD_ENTRY(setPersistent, 2) END_JS_STATIC_METHOD() IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, global_alternate, global); FX_BOOL CJS_Global::InitInstance(IFXJS_Context* cc) { CJS_Context* pContext = (CJS_Context*)cc; ASSERT(pContext != NULL); global_alternate* pGlobal = (global_alternate*)GetEmbedObject(); ASSERT(pGlobal != NULL); pGlobal->Initial(pContext->GetReaderApp()); return TRUE; }; global_alternate::global_alternate(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject), m_pApp(NULL) { } global_alternate::~global_alternate(void) { ASSERT(m_pApp != NULL); // CommitGlobalPersisitentVariables(); DestroyGlobalPersisitentVariables(); CJS_RuntimeFactory* pFactory = m_pApp->m_pJSRuntimeFactory; ASSERT(pFactory); pFactory->ReleaseGlobalData(); } void global_alternate::Initial(CPDFDoc_Environment* pApp) { m_pApp = pApp; CJS_RuntimeFactory* pFactory = pApp->m_pJSRuntimeFactory; ASSERT(pFactory); m_pGlobalData = pFactory->NewGlobalData(pApp); UpdateGlobalPersistentVariables(); } FX_BOOL global_alternate::QueryProperty(FX_LPCWSTR propname) { return CFX_WideString(propname) != L"setPersistent"; } FX_BOOL global_alternate::DelProperty(IFXJS_Context* cc, FX_LPCWSTR propname, JS_ErrorString& sError) { js_global_data* pData = NULL; CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname); if (m_mapGlobal.Lookup(sPropName, (FX_LPVOID&)pData)) { pData->bDeleted = TRUE; return TRUE; } return FALSE; } FX_BOOL global_alternate::DoProperty(IFXJS_Context* cc, FX_LPCWSTR propname, CJS_PropValue& vp, JS_ErrorString& sError) { if (vp.IsSetting()) { CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname); switch (vp.GetType()) { case VT_number: { double dData; vp >> dData; return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NUMBER, dData, false, "", v8::Handle<v8::Object>(), FALSE); } case VT_boolean: { bool bData; vp >> bData; return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_BOOLEAN, 0, (bool)vp, "", v8::Handle<v8::Object>(), FALSE); } case VT_string: { CFX_ByteString sData; vp >> sData; return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_STRING, 0, false, sData, v8::Handle<v8::Object>(), FALSE); } case VT_object: { JSObject pData = (JSObject)vp; return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_OBJECT, 0, false, "", pData, FALSE); // else // { // if (vp.IsArrayObject()) // { // CJS_Array array; // vp.ConvertToArray(array); // return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_OBJECT, 0, false, "", // (Dobject*)(Darray*)array, FALSE); // } // else // return FALSE; // } } case VT_null: { return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NULL, 0, false, "", v8::Handle<v8::Object>(), FALSE); } case VT_undefined: { DelProperty(cc, propname, sError); return TRUE; } default: return FALSE; } } else { js_global_data* pData = NULL; CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname); if (m_mapGlobal.Lookup(sPropName, (FX_LPVOID&)pData)) { if (pData) { if (!pData->bDeleted) { switch (pData->nType) { case JS_GLOBALDATA_TYPE_NUMBER: vp << pData->dData; break; case JS_GLOBALDATA_TYPE_BOOLEAN: vp << pData->bData; break; case JS_GLOBALDATA_TYPE_STRING: vp << pData->sData; break; case JS_GLOBALDATA_TYPE_OBJECT: { v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(vp.GetIsolate(),pData->pData); vp << obj; break; } case JS_GLOBALDATA_TYPE_NULL: vp.SetNull(); break; default: return FALSE; } return TRUE; } else { return TRUE; } } else { vp.SetNull(); return TRUE; } } else { vp.SetNull(); return TRUE; } } return FALSE; } FX_BOOL global_alternate::setPersistent(OBJ_METHOD_PARAMS) { if (params.size() != 2) { //sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR); return FALSE; } CFX_ByteString sName = params[0]; js_global_data* pData = NULL; if (m_mapGlobal.Lookup(sName, (FX_LPVOID&)pData)) { if (pData && !pData->bDeleted) { pData->bPersistent = (bool)params[1]; return TRUE; } } //sError = JSGetStringFromID(IDS_JSPARAM_INCORRECT); return FALSE; } void global_alternate::UpdateGlobalPersistentVariables() { ASSERT(m_pGlobalData != NULL); for (int i=0,sz=m_pGlobalData->GetSize(); i<sz; i++) { CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i); ASSERT(pData != NULL); switch (pData->data.nType) { case JS_GLOBALDATA_TYPE_NUMBER: this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NUMBER, pData->data.dData, false, "", v8::Handle<v8::Object>(), pData->bPersistent == 1); JS_PutObjectNumber(NULL,(JSFXObject)(*m_pJSObject), pData->data.sKey.UTF8Decode(), pData->data.dData); break; case JS_GLOBALDATA_TYPE_BOOLEAN: this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_BOOLEAN, 0, (bool)(pData->data.bData == 1), "", v8::Handle<v8::Object>(), pData->bPersistent == 1); JS_PutObjectBoolean(NULL,(JSFXObject)(*m_pJSObject), pData->data.sKey.UTF8Decode(), (bool)(pData->data.bData == 1)); break; case JS_GLOBALDATA_TYPE_STRING: this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_STRING, 0, false, pData->data.sData, v8::Handle<v8::Object>(), pData->bPersistent == 1); JS_PutObjectString(NULL,(JSFXObject)(*m_pJSObject), pData->data.sKey.UTF8Decode(), pData->data.sData.UTF8Decode()); break; case JS_GLOBALDATA_TYPE_OBJECT: { IJS_Runtime* pRuntime = JS_GetRuntime((JSFXObject)(*m_pJSObject)); v8::Handle<v8::Object> pObj = JS_NewFxDynamicObj(pRuntime, NULL, -1); PutObjectProperty(pObj, &pData->data); this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_OBJECT, 0, false, "", (JSObject)pObj, pData->bPersistent == 1); JS_PutObjectObject(NULL,(JSFXObject)(*m_pJSObject), pData->data.sKey.UTF8Decode(), (JSObject)pObj); } break; case JS_GLOBALDATA_TYPE_NULL: this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NULL, 0, false, "", v8::Handle<v8::Object>(), pData->bPersistent == 1); JS_PutObjectNull(NULL,(JSFXObject)(*m_pJSObject), pData->data.sKey.UTF8Decode()); break; } } } void global_alternate::CommitGlobalPersisitentVariables() { ASSERT(m_pGlobalData != NULL); FX_POSITION pos = m_mapGlobal.GetStartPosition(); while (pos) { CFX_ByteString name; js_global_data* pData = NULL; m_mapGlobal.GetNextAssoc(pos, name, (FX_LPVOID&)pData); if (pData) { if (pData->bDeleted) { m_pGlobalData->DeleteGlobalVariable(name); } else { switch (pData->nType) { case JS_GLOBALDATA_TYPE_NUMBER: m_pGlobalData->SetGlobalVariableNumber(name, pData->dData); m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); break; case JS_GLOBALDATA_TYPE_BOOLEAN: m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData); m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); break; case JS_GLOBALDATA_TYPE_STRING: m_pGlobalData->SetGlobalVariableString(name, pData->sData); m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); break; case JS_GLOBALDATA_TYPE_OBJECT: //if (pData->pData) { CJS_GlobalVariableArray array; v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(GetJSObject()->GetIsolate(),pData->pData); ObjectToArray(obj, array); m_pGlobalData->SetGlobalVariableObject(name, array); m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); } break; case JS_GLOBALDATA_TYPE_NULL: m_pGlobalData->SetGlobalVariableNull(name); m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); break; } } } } } void global_alternate::ObjectToArray(v8::Handle<v8::Object> pObj, CJS_GlobalVariableArray& array) { v8::Handle<v8::Array> pKeyList = JS_GetObjectElementNames(pObj); int nObjElements = pKeyList->Length(); v8::Local<v8::Context> context = pObj->CreationContext(); v8::Isolate* isolate = context->GetIsolate(); for (int i=0; i<nObjElements; i++) { CFX_WideString ws = JS_ToString(JS_GetArrayElemnet(pKeyList, i)); CFX_ByteString sKey = ws.UTF8Encode(); v8::Handle<v8::Value> v = JS_GetObjectElement(isolate, pObj, (const wchar_t*)(FX_LPCWSTR)ws); FXJSVALUETYPE vt = GET_VALUE_TYPE(v); switch (vt) { case VT_number: { CJS_KeyValue* pObjElement = new CJS_KeyValue; pObjElement->nType = JS_GLOBALDATA_TYPE_NUMBER; pObjElement->sKey = sKey; pObjElement->dData = JS_ToNumber(v); array.Add(pObjElement); } break; case VT_boolean: { CJS_KeyValue* pObjElement = new CJS_KeyValue; pObjElement->nType = JS_GLOBALDATA_TYPE_BOOLEAN; pObjElement->sKey = sKey; pObjElement->dData = JS_ToBoolean(v); array.Add(pObjElement); } break; case VT_string: { CFX_ByteString sValue = CJS_Value(isolate, v, VT_string); CJS_KeyValue* pObjElement = new CJS_KeyValue; pObjElement->nType = JS_GLOBALDATA_TYPE_STRING; pObjElement->sKey = sKey; pObjElement->sData = sValue; array.Add(pObjElement); } break; case VT_object: { CJS_KeyValue* pObjElement = new CJS_KeyValue; pObjElement->nType = JS_GLOBALDATA_TYPE_OBJECT; pObjElement->sKey = sKey; ObjectToArray(JS_ToObject(v), pObjElement->objData); array.Add(pObjElement); } break; case VT_null: { CJS_KeyValue* pObjElement = new CJS_KeyValue; pObjElement->nType = JS_GLOBALDATA_TYPE_NULL; pObjElement->sKey = sKey; array.Add(pObjElement); } break; default: break; } } } void global_alternate::PutObjectProperty(v8::Handle<v8::Object> pObj, CJS_KeyValue* pData) { ASSERT(pData != NULL); for (int i=0,sz=pData->objData.Count(); i<sz; i++) { CJS_KeyValue* pObjData = pData->objData.GetAt(i); ASSERT(pObjData != NULL); switch (pObjData->nType) { case JS_GLOBALDATA_TYPE_NUMBER: JS_PutObjectNumber(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode(), pObjData->dData); break; case JS_GLOBALDATA_TYPE_BOOLEAN: JS_PutObjectBoolean(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode(), (bool)(pObjData->bData == 1)); break; case JS_GLOBALDATA_TYPE_STRING: JS_PutObjectString(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode(), pObjData->sData.UTF8Decode()); break; case JS_GLOBALDATA_TYPE_OBJECT: { IJS_Runtime* pRuntime = JS_GetRuntime((JSFXObject)(*m_pJSObject)); v8::Handle<v8::Object> pNewObj = JS_NewFxDynamicObj(pRuntime, NULL, -1); PutObjectProperty(pNewObj, pObjData); JS_PutObjectObject(NULL, (JSObject)pObj, pObjData->sKey.UTF8Decode(), (JSObject)pNewObj); } break; case JS_GLOBALDATA_TYPE_NULL: JS_PutObjectNull(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode()); break; } } } void global_alternate::DestroyGlobalPersisitentVariables() { FX_POSITION pos = m_mapGlobal.GetStartPosition(); while (pos) { CFX_ByteString name; js_global_data* pData = NULL; m_mapGlobal.GetNextAssoc(pos, name, (FX_LPVOID&)pData); delete pData; } m_mapGlobal.RemoveAll(); } FX_BOOL global_alternate::SetGlobalVariables(FX_LPCSTR propname, int nType, double dData, bool bData, const CFX_ByteString& sData, JSObject pData, bool bDefaultPersistent) { if (propname == NULL) return FALSE; js_global_data* pTemp = NULL; m_mapGlobal.Lookup(propname, (FX_LPVOID&)pTemp); if (pTemp) { if (pTemp->bDeleted || pTemp->nType != nType) { pTemp->dData = 0; pTemp->bData = 0; pTemp->sData = ""; pTemp->nType = nType; } pTemp->bDeleted = FALSE; switch (nType) { case JS_GLOBALDATA_TYPE_NUMBER: { pTemp->dData = dData; } break; case JS_GLOBALDATA_TYPE_BOOLEAN: { pTemp->bData = bData; } break; case JS_GLOBALDATA_TYPE_STRING: { pTemp->sData = sData; } break; case JS_GLOBALDATA_TYPE_OBJECT: { pTemp->pData.Reset(JS_GetRuntime(pData), pData); } break; case JS_GLOBALDATA_TYPE_NULL: break; default: return FALSE; } return TRUE; } js_global_data* pNewData = NULL; switch (nType) { case JS_GLOBALDATA_TYPE_NUMBER: { pNewData = new js_global_data; pNewData->nType = JS_GLOBALDATA_TYPE_NUMBER; pNewData->dData = dData; pNewData->bPersistent = bDefaultPersistent; } break; case JS_GLOBALDATA_TYPE_BOOLEAN: { pNewData = new js_global_data; pNewData->nType = JS_GLOBALDATA_TYPE_BOOLEAN; pNewData->bData = bData; pNewData->bPersistent = bDefaultPersistent; } break; case JS_GLOBALDATA_TYPE_STRING: { pNewData = new js_global_data; pNewData->nType = JS_GLOBALDATA_TYPE_STRING; pNewData->sData = sData; pNewData->bPersistent = bDefaultPersistent; } break; case JS_GLOBALDATA_TYPE_OBJECT: { pNewData = new js_global_data; pNewData->nType = JS_GLOBALDATA_TYPE_OBJECT; pNewData->pData.Reset(JS_GetRuntime(pData), pData); pNewData->bPersistent = bDefaultPersistent; } break; case JS_GLOBALDATA_TYPE_NULL: { pNewData = new js_global_data; pNewData->nType = JS_GLOBALDATA_TYPE_NULL; pNewData->bPersistent = bDefaultPersistent; } break; default: return FALSE; } m_mapGlobal.SetAt(propname, (FX_LPVOID)pNewData); return TRUE; }