Home | History | Annotate | Download | only in fxjs
      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 "fxjs/cjs_runtime.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
     12 #include "fxjs/JS_Define.h"
     13 #include "fxjs/cjs_annot.h"
     14 #include "fxjs/cjs_app.h"
     15 #include "fxjs/cjs_border.h"
     16 #include "fxjs/cjs_color.h"
     17 #include "fxjs/cjs_console.h"
     18 #include "fxjs/cjs_display.h"
     19 #include "fxjs/cjs_document.h"
     20 #include "fxjs/cjs_event.h"
     21 #include "fxjs/cjs_event_context.h"
     22 #include "fxjs/cjs_eventhandler.h"
     23 #include "fxjs/cjs_field.h"
     24 #include "fxjs/cjs_font.h"
     25 #include "fxjs/cjs_global.h"
     26 #include "fxjs/cjs_globalarrays.h"
     27 #include "fxjs/cjs_globalconsts.h"
     28 #include "fxjs/cjs_globaldata.h"
     29 #include "fxjs/cjs_highlight.h"
     30 #include "fxjs/cjs_icon.h"
     31 #include "fxjs/cjs_object.h"
     32 #include "fxjs/cjs_position.h"
     33 #include "fxjs/cjs_printparamsobj.h"
     34 #include "fxjs/cjs_publicmethods.h"
     35 #include "fxjs/cjs_report.h"
     36 #include "fxjs/cjs_scalehow.h"
     37 #include "fxjs/cjs_scalewhen.h"
     38 #include "fxjs/cjs_style.h"
     39 #include "fxjs/cjs_timerobj.h"
     40 #include "fxjs/cjs_util.h"
     41 #include "fxjs/cjs_zoomtype.h"
     42 #include "public/fpdf_formfill.h"
     43 #include "third_party/base/stl_util.h"
     44 
     45 #ifdef PDF_ENABLE_XFA
     46 #include "fxjs/cfxjse_value.h"
     47 #endif  // PDF_ENABLE_XFA
     48 
     49 // static
     50 void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {
     51   FXJS_Initialize(slot, reinterpret_cast<v8::Isolate*>(isolate));
     52 }
     53 
     54 // static
     55 void IJS_Runtime::Destroy() {
     56   FXJS_Release();
     57 }
     58 
     59 // static
     60 IJS_Runtime* IJS_Runtime::Create(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
     61   return new CJS_Runtime(pFormFillEnv);
     62 }
     63 
     64 // static
     65 CJS_Runtime* CJS_Runtime::CurrentRuntimeFromIsolate(v8::Isolate* pIsolate) {
     66   return static_cast<CJS_Runtime*>(
     67       CFXJS_Engine::CurrentEngineFromIsolate(pIsolate));
     68 }
     69 
     70 CJS_Runtime::CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv)
     71     : m_pFormFillEnv(pFormFillEnv),
     72       m_bBlocking(false),
     73       m_isolateManaged(false) {
     74   v8::Isolate* pIsolate = nullptr;
     75 
     76   IPDF_JSPLATFORM* pPlatform = m_pFormFillEnv->GetFormFillInfo()->m_pJsPlatform;
     77   if (pPlatform->version <= 2) {
     78     unsigned int embedderDataSlot = 0;
     79     v8::Isolate* pExternalIsolate = nullptr;
     80     if (pPlatform->version == 2) {
     81       pExternalIsolate = reinterpret_cast<v8::Isolate*>(pPlatform->m_isolate);
     82       embedderDataSlot = pPlatform->m_v8EmbedderSlot;
     83     }
     84     FXJS_Initialize(embedderDataSlot, pExternalIsolate);
     85   }
     86   m_isolateManaged = FXJS_GetIsolate(&pIsolate);
     87   SetIsolate(pIsolate);
     88 
     89 #ifdef PDF_ENABLE_XFA
     90   v8::Isolate::Scope isolate_scope(pIsolate);
     91   v8::HandleScope handle_scope(pIsolate);
     92 #endif
     93 
     94   if (m_isolateManaged || FXJS_GlobalIsolateRefCount() == 0)
     95     DefineJSObjects();
     96 
     97   IJS_EventContext* pContext = NewEventContext();
     98   InitializeEngine();
     99   ReleaseEventContext(pContext);
    100   SetFormFillEnvToDocument();
    101 }
    102 
    103 CJS_Runtime::~CJS_Runtime() {
    104   NotifyObservedPtrs();
    105   ReleaseEngine();
    106   if (m_isolateManaged) {
    107     GetIsolate()->Dispose();
    108     SetIsolate(nullptr);
    109   }
    110 }
    111 
    112 void CJS_Runtime::DefineJSObjects() {
    113   v8::Isolate::Scope isolate_scope(GetIsolate());
    114   v8::HandleScope handle_scope(GetIsolate());
    115   v8::Local<v8::Context> context = v8::Context::New(GetIsolate());
    116   v8::Context::Scope context_scope(context);
    117 
    118   // The call order determines the "ObjDefID" assigned to each class.
    119   // ObjDefIDs 0 - 2
    120   CJS_Border::DefineJSObjects(this);
    121   CJS_Display::DefineJSObjects(this);
    122   CJS_Font::DefineJSObjects(this);
    123 
    124   // ObjDefIDs 3 - 5
    125   CJS_Highlight::DefineJSObjects(this);
    126   CJS_Position::DefineJSObjects(this);
    127   CJS_ScaleHow::DefineJSObjects(this);
    128 
    129   // ObjDefIDs 6 - 8
    130   CJS_ScaleWhen::DefineJSObjects(this);
    131   CJS_Style::DefineJSObjects(this);
    132   CJS_Zoomtype::DefineJSObjects(this);
    133 
    134   // ObjDefIDs 9 - 11
    135   CJS_App::DefineJSObjects(this);
    136   CJS_Color::DefineJSObjects(this);
    137   CJS_Console::DefineJSObjects(this);
    138 
    139   // ObjDefIDs 12 - 14
    140   CJS_Document::DefineJSObjects(this);
    141   CJS_Event::DefineJSObjects(this);
    142   CJS_Field::DefineJSObjects(this);
    143 
    144   // ObjDefIDs 15 - 17
    145   CJS_Global::DefineJSObjects(this);
    146   CJS_Icon::DefineJSObjects(this);
    147   CJS_Util::DefineJSObjects(this);
    148 
    149   // ObjDefIDs 18 - 20 (these can't fail, return void).
    150   CJS_PublicMethods::DefineJSObjects(this);
    151   CJS_GlobalConsts::DefineJSObjects(this);
    152   CJS_GlobalArrays::DefineJSObjects(this);
    153 
    154   // ObjDefIDs 21 - 23.
    155   CJS_TimerObj::DefineJSObjects(this);
    156   CJS_PrintParamsObj::DefineJSObjects(this);
    157   CJS_Annot::DefineJSObjects(this);
    158 }
    159 
    160 IJS_EventContext* CJS_Runtime::NewEventContext() {
    161   m_EventContextArray.push_back(pdfium::MakeUnique<CJS_EventContext>(this));
    162   return m_EventContextArray.back().get();
    163 }
    164 
    165 void CJS_Runtime::ReleaseEventContext(IJS_EventContext* pContext) {
    166   auto it = std::find(m_EventContextArray.begin(), m_EventContextArray.end(),
    167                       pdfium::FakeUniquePtr<CJS_EventContext>(
    168                           static_cast<CJS_EventContext*>(pContext)));
    169   if (it != m_EventContextArray.end())
    170     m_EventContextArray.erase(it);
    171 }
    172 
    173 CJS_EventContext* CJS_Runtime::GetCurrentEventContext() const {
    174   return m_EventContextArray.empty() ? nullptr
    175                                      : m_EventContextArray.back().get();
    176 }
    177 
    178 void CJS_Runtime::SetFormFillEnvToDocument() {
    179   v8::Isolate::Scope isolate_scope(GetIsolate());
    180   v8::HandleScope handle_scope(GetIsolate());
    181   v8::Local<v8::Context> context = NewLocalContext();
    182   v8::Context::Scope context_scope(context);
    183 
    184   v8::Local<v8::Object> pThis = GetThisObj();
    185   if (pThis.IsEmpty())
    186     return;
    187 
    188   if (CFXJS_Engine::GetObjDefnID(pThis) != CJS_Document::GetObjDefnID())
    189     return;
    190 
    191   CJS_Document* pJSDocument =
    192       static_cast<CJS_Document*>(GetObjectPrivate(pThis));
    193   if (!pJSDocument)
    194     return;
    195 
    196   Document* pDocument = static_cast<Document*>(pJSDocument->GetEmbedObject());
    197   if (!pDocument)
    198     return;
    199 
    200   pDocument->SetFormFillEnv(m_pFormFillEnv.Get());
    201 }
    202 
    203 CPDFSDK_FormFillEnvironment* CJS_Runtime::GetFormFillEnv() const {
    204   return m_pFormFillEnv.Get();
    205 }
    206 
    207 int CJS_Runtime::ExecuteScript(const WideString& script, WideString* info) {
    208   FXJSErr error = {};
    209   int nRet = Execute(script, &error);
    210   if (nRet < 0) {
    211     *info = WideString::Format(L"[ Line: %05d { %ls } ] : %s", error.linnum - 1,
    212                                error.srcline, error.message);
    213   }
    214   return nRet;
    215 }
    216 
    217 bool CJS_Runtime::AddEventToSet(const FieldEvent& event) {
    218   return m_FieldEventSet.insert(event).second;
    219 }
    220 
    221 void CJS_Runtime::RemoveEventFromSet(const FieldEvent& event) {
    222   m_FieldEventSet.erase(event);
    223 }
    224 
    225 #ifdef PDF_ENABLE_XFA
    226 WideString ChangeObjName(const WideString& str) {
    227   WideString sRet = str;
    228   sRet.Replace(L"_", L".");
    229   return sRet;
    230 }
    231 
    232 bool CJS_Runtime::GetValueByName(const ByteStringView& utf8Name,
    233                                  CFXJSE_Value* pValue) {
    234   v8::Isolate::Scope isolate_scope(GetIsolate());
    235   v8::HandleScope handle_scope(GetIsolate());
    236   v8::Local<v8::Context> context = NewLocalContext();
    237   v8::Context::Scope context_scope(context);
    238   v8::Local<v8::Value> propvalue = context->Global()->Get(
    239       v8::String::NewFromUtf8(GetIsolate(), utf8Name.unterminated_c_str(),
    240                               v8::String::kNormalString, utf8Name.GetLength()));
    241   if (propvalue.IsEmpty()) {
    242     pValue->SetUndefined();
    243     return false;
    244   }
    245   pValue->ForceSetValue(propvalue);
    246   return true;
    247 }
    248 
    249 bool CJS_Runtime::SetValueByName(const ByteStringView& utf8Name,
    250                                  CFXJSE_Value* pValue) {
    251   if (utf8Name.IsEmpty() || !pValue)
    252     return false;
    253 
    254   v8::Isolate* pIsolate = GetIsolate();
    255   v8::Isolate::Scope isolate_scope(pIsolate);
    256   v8::HandleScope handle_scope(pIsolate);
    257   v8::Local<v8::Context> context = NewLocalContext();
    258   v8::Context::Scope context_scope(context);
    259   v8::Local<v8::Value> propvalue =
    260       v8::Local<v8::Value>::New(GetIsolate(), pValue->DirectGetValue());
    261   context->Global()->Set(
    262       v8::String::NewFromUtf8(pIsolate, utf8Name.unterminated_c_str(),
    263                               v8::String::kNormalString, utf8Name.GetLength()),
    264       propvalue);
    265   return true;
    266 }
    267 #endif
    268 
    269 v8::Local<v8::Value> CJS_Runtime::MaybeCoerceToNumber(
    270     v8::Local<v8::Value> value) {
    271   bool bAllowNaN = false;
    272   if (value->IsString()) {
    273     ByteString bstr = ByteString::FromUnicode(ToWideString(value));
    274     if (bstr.GetLength() == 0)
    275       return value;
    276     if (bstr == "NaN")
    277       bAllowNaN = true;
    278   }
    279 
    280   v8::Isolate* pIsolate = GetIsolate();
    281   v8::TryCatch try_catch(pIsolate);
    282   v8::MaybeLocal<v8::Number> maybeNum =
    283       value->ToNumber(pIsolate->GetCurrentContext());
    284   if (maybeNum.IsEmpty())
    285     return value;
    286 
    287   v8::Local<v8::Number> num = maybeNum.ToLocalChecked();
    288   if (std::isnan(num->Value()) && !bAllowNaN)
    289     return value;
    290 
    291   return num;
    292 }
    293