1 // Copyright 2017 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/xfa/cjx_instancemanager.h" 8 9 #include <algorithm> 10 #include <vector> 11 12 #include "fxjs/cfxjse_engine.h" 13 #include "fxjs/cfxjse_value.h" 14 #include "fxjs/js_resources.h" 15 #include "xfa/fxfa/cxfa_ffnotify.h" 16 #include "xfa/fxfa/parser/cxfa_document.h" 17 #include "xfa/fxfa/parser/cxfa_instancemanager.h" 18 #include "xfa/fxfa/parser/cxfa_layoutprocessor.h" 19 #include "xfa/fxfa/parser/cxfa_occur.h" 20 21 const CJX_MethodSpec CJX_InstanceManager::MethodSpecs[] = { 22 {"addInstance", addInstance_static}, 23 {"insertInstance", insertInstance_static}, 24 {"moveInstance", moveInstance_static}, 25 {"removeInstance", removeInstance_static}, 26 {"setInstances", setInstances_static}}; 27 28 CJX_InstanceManager::CJX_InstanceManager(CXFA_InstanceManager* mgr) 29 : CJX_Node(mgr) { 30 DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs)); 31 } 32 33 CJX_InstanceManager::~CJX_InstanceManager() {} 34 35 int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) { 36 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); 37 int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin; 38 if (iDesired < iMin) { 39 ThrowTooManyOccurancesException(L"min"); 40 return 1; 41 } 42 43 int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax; 44 if (iMax >= 0 && iDesired > iMax) { 45 ThrowTooManyOccurancesException(L"max"); 46 return 2; 47 } 48 49 int32_t iCount = GetXFANode()->GetCount(); 50 if (iDesired == iCount) 51 return 0; 52 53 if (iDesired < iCount) { 54 WideString wsInstManagerName = GetCData(XFA_Attribute::Name); 55 WideString wsInstanceName = WideString( 56 wsInstManagerName.IsEmpty() 57 ? wsInstManagerName 58 : wsInstManagerName.Right(wsInstManagerName.GetLength() - 1)); 59 uint32_t dInstanceNameHash = 60 FX_HashCode_GetW(wsInstanceName.AsStringView(), false); 61 CXFA_Node* pPrevSibling = iDesired == 0 62 ? GetXFANode() 63 : GetXFANode()->GetItemIfExists(iDesired - 1); 64 if (!pPrevSibling) { 65 // TODO(dsinclair): Better error? 66 ThrowIndexOutOfBoundsException(); 67 return 0; 68 } 69 70 while (iCount > iDesired) { 71 CXFA_Node* pRemoveInstance = pPrevSibling->GetNextSibling(); 72 if (pRemoveInstance->GetElementType() != XFA_Element::Subform && 73 pRemoveInstance->GetElementType() != XFA_Element::SubformSet) { 74 continue; 75 } 76 if (pRemoveInstance->GetElementType() == XFA_Element::InstanceManager) { 77 NOTREACHED(); 78 break; 79 } 80 if (pRemoveInstance->GetNameHash() == dInstanceNameHash) { 81 GetXFANode()->RemoveItem(pRemoveInstance, true); 82 iCount--; 83 } 84 } 85 } else { 86 while (iCount < iDesired) { 87 CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(true); 88 if (!pNewInstance) 89 return 0; 90 91 GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false); 92 ++iCount; 93 94 CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); 95 if (!pNotify) 96 return 0; 97 98 pNotify->RunNodeInitialize(pNewInstance); 99 } 100 } 101 102 CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor(); 103 if (pLayoutPro) { 104 pLayoutPro->AddChangedContainer( 105 ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); 106 } 107 return 0; 108 } 109 110 int32_t CJX_InstanceManager::MoveInstance(int32_t iTo, int32_t iFrom) { 111 int32_t iCount = GetXFANode()->GetCount(); 112 if (iFrom > iCount || iTo > iCount - 1) { 113 ThrowIndexOutOfBoundsException(); 114 return 1; 115 } 116 if (iFrom < 0 || iTo < 0 || iFrom == iTo) 117 return 0; 118 119 CXFA_Node* pMoveInstance = GetXFANode()->GetItemIfExists(iFrom); 120 if (!pMoveInstance) { 121 ThrowIndexOutOfBoundsException(); 122 return 1; 123 } 124 125 GetXFANode()->RemoveItem(pMoveInstance, false); 126 GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true); 127 CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor(); 128 if (pLayoutPro) { 129 pLayoutPro->AddChangedContainer( 130 ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); 131 } 132 return 0; 133 } 134 135 CJS_Return CJX_InstanceManager::moveInstance( 136 CJS_V8* runtime, 137 const std::vector<v8::Local<v8::Value>>& params) { 138 if (params.size() != 2) 139 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 140 141 int32_t iFrom = runtime->ToInt32(params[0]); 142 int32_t iTo = runtime->ToInt32(params[1]); 143 MoveInstance(iTo, iFrom); 144 145 CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); 146 if (!pNotify) 147 return CJS_Return(true); 148 149 CXFA_Node* pToInstance = GetXFANode()->GetItemIfExists(iTo); 150 if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform) 151 pNotify->RunSubformIndexChange(pToInstance); 152 153 CXFA_Node* pFromInstance = GetXFANode()->GetItemIfExists(iFrom); 154 if (pFromInstance && 155 pFromInstance->GetElementType() == XFA_Element::Subform) { 156 pNotify->RunSubformIndexChange(pFromInstance); 157 } 158 159 return CJS_Return(true); 160 } 161 162 CJS_Return CJX_InstanceManager::removeInstance( 163 CJS_V8* runtime, 164 const std::vector<v8::Local<v8::Value>>& params) { 165 if (params.size() != 1) 166 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 167 168 int32_t iIndex = runtime->ToInt32(params[0]); 169 int32_t iCount = GetXFANode()->GetCount(); 170 if (iIndex < 0 || iIndex >= iCount) 171 return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError)); 172 173 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); 174 int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin; 175 if (iCount - 1 < iMin) 176 return CJS_Return(JSGetStringFromID(JSMessage::kTooManyOccurances)); 177 178 CXFA_Node* pRemoveInstance = GetXFANode()->GetItemIfExists(iIndex); 179 if (!pRemoveInstance) 180 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 181 182 GetXFANode()->RemoveItem(pRemoveInstance, true); 183 184 CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); 185 if (pNotify) { 186 for (int32_t i = iIndex; i < iCount - 1; i++) { 187 CXFA_Node* pSubformInstance = GetXFANode()->GetItemIfExists(i); 188 if (pSubformInstance && 189 pSubformInstance->GetElementType() == XFA_Element::Subform) { 190 pNotify->RunSubformIndexChange(pSubformInstance); 191 } 192 } 193 } 194 CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor(); 195 if (pLayoutPro) { 196 pLayoutPro->AddChangedContainer( 197 ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); 198 } 199 return CJS_Return(true); 200 } 201 202 CJS_Return CJX_InstanceManager::setInstances( 203 CJS_V8* runtime, 204 const std::vector<v8::Local<v8::Value>>& params) { 205 if (params.size() != 1) 206 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 207 208 SetInstances(runtime->ToInt32(params[0])); 209 return CJS_Return(true); 210 } 211 212 CJS_Return CJX_InstanceManager::addInstance( 213 CJS_V8* runtime, 214 const std::vector<v8::Local<v8::Value>>& params) { 215 if (!params.empty() && params.size() != 1) 216 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 217 218 bool fFlags = true; 219 if (params.size() == 1) 220 fFlags = runtime->ToBoolean(params[0]); 221 222 int32_t iCount = GetXFANode()->GetCount(); 223 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); 224 int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax; 225 if (iMax >= 0 && iCount >= iMax) 226 return CJS_Return(JSGetStringFromID(JSMessage::kTooManyOccurances)); 227 228 CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(fFlags); 229 if (!pNewInstance) 230 return CJS_Return(runtime->NewNull()); 231 232 GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false); 233 234 CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); 235 if (pNotify) { 236 pNotify->RunNodeInitialize(pNewInstance); 237 238 CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor(); 239 if (pLayoutPro) { 240 pLayoutPro->AddChangedContainer( 241 ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); 242 } 243 } 244 245 CFXJSE_Value* value = 246 GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewInstance); 247 if (!value) 248 return CJS_Return(runtime->NewNull()); 249 250 return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate())); 251 } 252 253 CJS_Return CJX_InstanceManager::insertInstance( 254 CJS_V8* runtime, 255 const std::vector<v8::Local<v8::Value>>& params) { 256 if (params.size() != 1 && params.size() != 2) 257 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 258 259 int32_t iIndex = runtime->ToInt32(params[0]); 260 bool bBind = false; 261 if (params.size() == 2) 262 bBind = runtime->ToBoolean(params[1]); 263 264 int32_t iCount = GetXFANode()->GetCount(); 265 if (iIndex < 0 || iIndex > iCount) 266 return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError)); 267 268 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); 269 int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax; 270 if (iMax >= 0 && iCount >= iMax) 271 return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError)); 272 273 CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(bBind); 274 if (!pNewInstance) 275 return CJS_Return(runtime->NewNull()); 276 277 GetXFANode()->InsertItem(pNewInstance, iIndex, iCount, true); 278 279 CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); 280 if (pNotify) { 281 pNotify->RunNodeInitialize(pNewInstance); 282 CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor(); 283 if (pLayoutPro) { 284 pLayoutPro->AddChangedContainer( 285 ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); 286 } 287 } 288 289 CFXJSE_Value* value = 290 GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewInstance); 291 if (!value) 292 return CJS_Return(runtime->NewNull()); 293 294 return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate())); 295 } 296 297 void CJX_InstanceManager::max(CFXJSE_Value* pValue, 298 bool bSetting, 299 XFA_Attribute eAttribute) { 300 if (bSetting) { 301 ThrowInvalidPropertyException(); 302 return; 303 } 304 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); 305 pValue->SetInteger(occur ? occur->GetMax() : CXFA_Occur::kDefaultMax); 306 } 307 308 void CJX_InstanceManager::min(CFXJSE_Value* pValue, 309 bool bSetting, 310 XFA_Attribute eAttribute) { 311 if (bSetting) { 312 ThrowInvalidPropertyException(); 313 return; 314 } 315 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); 316 pValue->SetInteger(occur ? occur->GetMin() : CXFA_Occur::kDefaultMin); 317 } 318 319 void CJX_InstanceManager::count(CFXJSE_Value* pValue, 320 bool bSetting, 321 XFA_Attribute eAttribute) { 322 if (bSetting) { 323 pValue->SetInteger(GetXFANode()->GetCount()); 324 return; 325 } 326 SetInstances(pValue->ToInteger()); 327 } 328