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