Home | History | Annotate | Download | only in xfa
      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