• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
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 "core/fpdfdoc/cpdf_formfield.h"
8 
9 #include <map>
10 #include <set>
11 #include <utility>
12 
13 #include "constants/form_fields.h"
14 #include "constants/form_flags.h"
15 #include "core/fpdfapi/font/cpdf_font.h"
16 #include "core/fpdfapi/page/cpdf_docpagedata.h"
17 #include "core/fpdfapi/parser/cfdf_document.h"
18 #include "core/fpdfapi/parser/cpdf_array.h"
19 #include "core/fpdfapi/parser/cpdf_dictionary.h"
20 #include "core/fpdfapi/parser/cpdf_name.h"
21 #include "core/fpdfapi/parser/cpdf_number.h"
22 #include "core/fpdfapi/parser/cpdf_stream.h"
23 #include "core/fpdfapi/parser/cpdf_string.h"
24 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
25 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
26 #include "core/fpdfdoc/cpdf_defaultappearance.h"
27 #include "core/fpdfdoc/cpdf_formcontrol.h"
28 #include "core/fpdfdoc/cpdf_generateap.h"
29 #include "core/fpdfdoc/cpdf_interactiveform.h"
30 #include "core/fxcrt/stl_util.h"
31 #include "third_party/base/check.h"
32 #include "third_party/base/containers/contains.h"
33 
34 namespace {
35 
GetFieldAttrRecursive(const CPDF_Dictionary * pFieldDict,const ByteString & name,int nLevel)36 RetainPtr<const CPDF_Object> GetFieldAttrRecursive(
37     const CPDF_Dictionary* pFieldDict,
38     const ByteString& name,
39     int nLevel) {
40   static constexpr int kGetFieldMaxRecursion = 32;
41   if (!pFieldDict || nLevel > kGetFieldMaxRecursion)
42     return nullptr;
43 
44   RetainPtr<const CPDF_Object> pAttr = pFieldDict->GetDirectObjectFor(name);
45   if (pAttr)
46     return pAttr;
47 
48   return GetFieldAttrRecursive(
49       pFieldDict->GetDictFor(pdfium::form_fields::kParent).Get(), name,
50       nLevel + 1);
51 }
52 
53 }  // namespace
54 
55 // static
IntToFormFieldType(int value)56 absl::optional<FormFieldType> CPDF_FormField::IntToFormFieldType(int value) {
57   if (value >= static_cast<int>(FormFieldType::kUnknown) &&
58       value < static_cast<int>(kFormFieldTypeCount)) {
59     return static_cast<FormFieldType>(value);
60   }
61   return absl::nullopt;
62 }
63 
64 // static
GetFieldAttrForDict(const CPDF_Dictionary * pFieldDict,const ByteString & name)65 RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttrForDict(
66     const CPDF_Dictionary* pFieldDict,
67     const ByteString& name) {
68   return GetFieldAttrRecursive(pFieldDict, name, 0);
69 }
70 
71 // static
GetMutableFieldAttrForDict(CPDF_Dictionary * pFieldDict,const ByteString & name)72 RetainPtr<CPDF_Object> CPDF_FormField::GetMutableFieldAttrForDict(
73     CPDF_Dictionary* pFieldDict,
74     const ByteString& name) {
75   return pdfium::WrapRetain(const_cast<CPDF_Object*>(
76       GetFieldAttrRecursive(pFieldDict, name, 0).Get()));
77 }
78 
79 // static
GetFullNameForDict(const CPDF_Dictionary * pFieldDict)80 WideString CPDF_FormField::GetFullNameForDict(
81     const CPDF_Dictionary* pFieldDict) {
82   WideString full_name;
83   std::set<const CPDF_Dictionary*> visited;
84   const CPDF_Dictionary* pLevel = pFieldDict;
85   while (pLevel) {
86     visited.insert(pLevel);
87     WideString short_name = pLevel->GetUnicodeTextFor(pdfium::form_fields::kT);
88     if (!short_name.IsEmpty()) {
89       if (full_name.IsEmpty())
90         full_name = std::move(short_name);
91       else
92         full_name = short_name + L'.' + full_name;
93     }
94     pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent).Get();
95     if (pdfium::Contains(visited, pLevel))
96       break;
97   }
98   return full_name;
99 }
100 
CPDF_FormField(CPDF_InteractiveForm * pForm,RetainPtr<CPDF_Dictionary> pDict)101 CPDF_FormField::CPDF_FormField(CPDF_InteractiveForm* pForm,
102                                RetainPtr<CPDF_Dictionary> pDict)
103     : m_pForm(pForm), m_pDict(std::move(pDict)) {
104   InitFieldFlags();
105 }
106 
107 CPDF_FormField::~CPDF_FormField() = default;
108 
InitFieldFlags()109 void CPDF_FormField::InitFieldFlags() {
110   RetainPtr<const CPDF_Object> ft_attr =
111       GetFieldAttrInternal(pdfium::form_fields::kFT);
112   ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString();
113   uint32_t flags = GetFieldFlags();
114   m_bRequired = flags & pdfium::form_flags::kRequired;
115   m_bNoExport = flags & pdfium::form_flags::kNoExport;
116 
117   if (type_name == pdfium::form_fields::kBtn) {
118     if (flags & pdfium::form_flags::kButtonRadio) {
119       m_Type = kRadioButton;
120       m_bIsUnison = flags & pdfium::form_flags::kButtonRadiosInUnison;
121     } else if (flags & pdfium::form_flags::kButtonPushbutton) {
122       m_Type = kPushButton;
123     } else {
124       m_Type = kCheckBox;
125       m_bIsUnison = true;
126     }
127   } else if (type_name == pdfium::form_fields::kTx) {
128     if (flags & pdfium::form_flags::kTextFileSelect)
129       m_Type = kFile;
130     else if (flags & pdfium::form_flags::kTextRichText)
131       m_Type = kRichText;
132     else
133       m_Type = kText;
134   } else if (type_name == pdfium::form_fields::kCh) {
135     if (flags & pdfium::form_flags::kChoiceCombo) {
136       m_Type = kComboBox;
137     } else {
138       m_Type = kListBox;
139       m_bIsMultiSelectListBox = flags & pdfium::form_flags::kChoiceMultiSelect;
140     }
141     m_bUseSelectedIndices = UseSelectedIndicesObject();
142   } else if (type_name == pdfium::form_fields::kSig) {
143     m_Type = kSign;
144   }
145 }
146 
GetFullName() const147 WideString CPDF_FormField::GetFullName() const {
148   return GetFullNameForDict(m_pDict.Get());
149 }
150 
GetFieldAttr(const ByteString & name) const151 RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttr(
152     const ByteString& name) const {
153   return GetFieldAttrInternal(name);
154 }
155 
GetFieldDict() const156 RetainPtr<const CPDF_Dictionary> CPDF_FormField::GetFieldDict() const {
157   return pdfium::WrapRetain(GetFieldDictInternal());
158 }
159 
ResetField()160 bool CPDF_FormField::ResetField() {
161   switch (m_Type) {
162     case kCheckBox:
163     case kRadioButton: {
164       int iCount = CountControls();
165       // TODO(weili): Check whether anything special needs to be done for
166       // |m_bIsUnison|.
167       for (int i = 0; i < iCount; i++) {
168         CheckControl(i, GetControl(i)->IsDefaultChecked(),
169                      NotificationOption::kDoNotNotify);
170       }
171       m_pForm->NotifyAfterCheckedStatusChange(this);
172       break;
173     }
174     case kComboBox:
175     case kListBox: {
176       ClearSelection(NotificationOption::kDoNotNotify);
177       WideString csValue;
178       int iIndex = GetDefaultSelectedItem();
179       if (iIndex >= 0)
180         csValue = GetOptionLabel(iIndex);
181       if (!NotifyListOrComboBoxBeforeChange(csValue)) {
182         return false;
183       }
184       SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
185       NotifyListOrComboBoxAfterChange();
186       break;
187     }
188     case kText:
189     case kRichText:
190     case kFile:
191     default: {
192       WideString csDValue;
193       WideString csValue;
194       {
195         // Limit scope of |pDV| and |pV| because they may get invalidated
196         // during notification below.
197         RetainPtr<const CPDF_Object> pDV = GetDefaultValueObject();
198         if (pDV)
199           csDValue = pDV->GetUnicodeText();
200 
201         RetainPtr<const CPDF_Object> pV = GetValueObject();
202         if (pV)
203           csValue = pV->GetUnicodeText();
204       }
205 
206       bool bHasRV = !!GetFieldAttrInternal(pdfium::form_fields::kRV);
207       if (!bHasRV && (csDValue == csValue))
208         return false;
209 
210       if (!m_pForm->NotifyBeforeValueChange(this, csDValue))
211         return false;
212 
213       {
214         // Limit scope of |pDV| because it may get invalidated during
215         // notification below.
216         RetainPtr<const CPDF_Object> pDV = GetDefaultValueObject();
217         if (pDV) {
218           RetainPtr<CPDF_Object> pClone = pDV->Clone();
219           if (!pClone)
220             return false;
221 
222           m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone));
223           if (bHasRV) {
224             m_pDict->SetFor(pdfium::form_fields::kRV, pDV->Clone());
225           }
226         } else {
227           m_pDict->RemoveFor(pdfium::form_fields::kV);
228           m_pDict->RemoveFor(pdfium::form_fields::kRV);
229         }
230       }
231       m_pForm->NotifyAfterValueChange(this);
232       break;
233     }
234   }
235   return true;
236 }
237 
CountControls() const238 int CPDF_FormField::CountControls() const {
239   return fxcrt::CollectionSize<int>(GetControls());
240 }
241 
GetControl(int index) const242 CPDF_FormControl* CPDF_FormField::GetControl(int index) const {
243   return GetControls()[index];
244 }
245 
GetControlIndex(const CPDF_FormControl * pControl) const246 int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const {
247   if (!pControl)
248     return -1;
249 
250   const auto& controls = GetControls();
251   auto it = std::find(controls.begin(), controls.end(), pControl);
252   if (it == controls.end())
253     return -1;
254 
255   return pdfium::base::checked_cast<int>(it - controls.begin());
256 }
257 
GetFieldType() const258 FormFieldType CPDF_FormField::GetFieldType() const {
259   switch (m_Type) {
260     case kPushButton:
261       return FormFieldType::kPushButton;
262     case kCheckBox:
263       return FormFieldType::kCheckBox;
264     case kRadioButton:
265       return FormFieldType::kRadioButton;
266     case kComboBox:
267       return FormFieldType::kComboBox;
268     case kListBox:
269       return FormFieldType::kListBox;
270     case kText:
271     case kRichText:
272     case kFile:
273       return FormFieldType::kTextField;
274     case kSign:
275       return FormFieldType::kSignature;
276     default:
277       return FormFieldType::kUnknown;
278   }
279 }
280 
GetAdditionalAction() const281 CPDF_AAction CPDF_FormField::GetAdditionalAction() const {
282   RetainPtr<const CPDF_Object> pObj =
283       GetFieldAttrInternal(pdfium::form_fields::kAA);
284   return CPDF_AAction(pObj ? pObj->GetDict() : nullptr);
285 }
286 
GetAlternateName() const287 WideString CPDF_FormField::GetAlternateName() const {
288   RetainPtr<const CPDF_Object> pObj =
289       GetFieldAttrInternal(pdfium::form_fields::kTU);
290   return pObj ? pObj->GetUnicodeText() : WideString();
291 }
292 
GetMappingName() const293 WideString CPDF_FormField::GetMappingName() const {
294   RetainPtr<const CPDF_Object> pObj =
295       GetFieldAttrInternal(pdfium::form_fields::kTM);
296   return pObj ? pObj->GetUnicodeText() : WideString();
297 }
298 
GetFieldFlags() const299 uint32_t CPDF_FormField::GetFieldFlags() const {
300   RetainPtr<const CPDF_Object> pObj =
301       GetFieldAttrInternal(pdfium::form_fields::kFf);
302   return pObj ? pObj->GetInteger() : 0;
303 }
304 
SetFieldFlags(uint32_t dwFlags)305 void CPDF_FormField::SetFieldFlags(uint32_t dwFlags) {
306   m_pDict->SetNewFor<CPDF_Number>(pdfium::form_fields::kFf,
307                                   static_cast<int>(dwFlags));
308 }
309 
GetValue(bool bDefault) const310 WideString CPDF_FormField::GetValue(bool bDefault) const {
311   if (GetType() == kCheckBox || GetType() == kRadioButton)
312     return GetCheckValue(bDefault);
313 
314   RetainPtr<const CPDF_Object> pValue =
315       bDefault ? GetDefaultValueObject() : GetValueObject();
316   if (!pValue) {
317     if (!bDefault && m_Type != kText)
318       pValue = GetDefaultValueObject();
319     if (!pValue)
320       return WideString();
321   }
322 
323   switch (pValue->GetType()) {
324     case CPDF_Object::kString:
325     case CPDF_Object::kStream:
326       return pValue->GetUnicodeText();
327     case CPDF_Object::kArray: {
328       RetainPtr<const CPDF_Object> pNewValue =
329           pValue->AsArray()->GetDirectObjectAt(0);
330       if (pNewValue)
331         return pNewValue->GetUnicodeText();
332       break;
333     }
334     default:
335       break;
336   }
337   return WideString();
338 }
339 
GetValue() const340 WideString CPDF_FormField::GetValue() const {
341   return GetValue(false);
342 }
343 
GetDefaultValue() const344 WideString CPDF_FormField::GetDefaultValue() const {
345   return GetValue(true);
346 }
347 
SetValue(const WideString & value,bool bDefault,NotificationOption notify)348 bool CPDF_FormField::SetValue(const WideString& value,
349                               bool bDefault,
350                               NotificationOption notify) {
351   switch (m_Type) {
352     case kCheckBox:
353     case kRadioButton: {
354       SetCheckValue(value, bDefault, notify);
355       return true;
356     }
357     case kFile:
358     case kRichText:
359     case kText:
360     case kComboBox: {
361       WideString csValue = value;
362       if (notify == NotificationOption::kNotify &&
363           !m_pForm->NotifyBeforeValueChange(this, csValue)) {
364         return false;
365       }
366       ByteString key(bDefault ? pdfium::form_fields::kDV
367                               : pdfium::form_fields::kV);
368       m_pDict->SetNewFor<CPDF_String>(key, csValue.AsStringView());
369       int iIndex = FindOption(csValue);
370       if (iIndex < 0) {
371         if (m_Type == kRichText && !bDefault) {
372           m_pDict->SetFor(pdfium::form_fields::kRV,
373                           m_pDict->GetObjectFor(key)->Clone());
374         }
375         m_pDict->RemoveFor("I");
376       } else {
377         if (!bDefault) {
378           ClearSelection(NotificationOption::kDoNotNotify);
379           SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
380         }
381       }
382       if (notify == NotificationOption::kNotify)
383         m_pForm->NotifyAfterValueChange(this);
384       break;
385     }
386     case kListBox: {
387       int iIndex = FindOption(value);
388       if (iIndex < 0)
389         return false;
390 
391       if (bDefault && iIndex == GetDefaultSelectedItem())
392         return false;
393 
394       if (notify == NotificationOption::kNotify &&
395           !m_pForm->NotifyBeforeSelectionChange(this, value)) {
396         return false;
397       }
398       if (!bDefault) {
399         ClearSelection(NotificationOption::kDoNotNotify);
400         SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
401       }
402       if (notify == NotificationOption::kNotify)
403         m_pForm->NotifyAfterSelectionChange(this);
404       break;
405     }
406     default:
407       break;
408   }
409   return true;
410 }
411 
SetValue(const WideString & value,NotificationOption notify)412 bool CPDF_FormField::SetValue(const WideString& value,
413                               NotificationOption notify) {
414   return SetValue(value, false, notify);
415 }
416 
GetMaxLen() const417 int CPDF_FormField::GetMaxLen() const {
418   RetainPtr<const CPDF_Object> pObj = GetFieldAttrInternal("MaxLen");
419   if (pObj)
420     return pObj->GetInteger();
421 
422   for (auto& pControl : GetControls()) {
423     if (!pControl)
424       continue;
425 
426     RetainPtr<const CPDF_Dictionary> pWidgetDict = pControl->GetWidgetDict();
427     if (pWidgetDict->KeyExist("MaxLen"))
428       return pWidgetDict->GetIntegerFor("MaxLen");
429   }
430   return 0;
431 }
432 
CountSelectedItems() const433 int CPDF_FormField::CountSelectedItems() const {
434   const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
435   if (!pValue)
436     return 0;
437 
438   if (pValue->IsString() || pValue->IsNumber())
439     return pValue->GetString().IsEmpty() ? 0 : 1;
440   const CPDF_Array* pArray = pValue->AsArray();
441   return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
442 }
443 
GetSelectedIndex(int index) const444 int CPDF_FormField::GetSelectedIndex(int index) const {
445   const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
446   if (!pValue)
447     return -1;
448 
449   if (pValue->IsNumber())
450     return pValue->GetInteger();
451 
452   WideString sel_value;
453   if (pValue->IsString()) {
454     if (index != 0)
455       return -1;
456     sel_value = pValue->GetUnicodeText();
457   } else {
458     const CPDF_Array* pArray = pValue->AsArray();
459     if (!pArray || index < 0)
460       return -1;
461 
462     RetainPtr<const CPDF_Object> elementValue =
463         pArray->GetDirectObjectAt(index);
464     sel_value = elementValue ? elementValue->GetUnicodeText() : WideString();
465   }
466   if (index < CountSelectedOptions()) {
467     int iOptIndex = GetSelectedOptionIndex(index);
468     WideString csOpt = GetOptionValue(iOptIndex);
469     if (csOpt == sel_value)
470       return iOptIndex;
471   }
472   for (int i = 0; i < CountOptions(); i++) {
473     if (sel_value == GetOptionValue(i))
474       return i;
475   }
476   return -1;
477 }
478 
ClearSelection(NotificationOption notify)479 bool CPDF_FormField::ClearSelection(NotificationOption notify) {
480   if (notify == NotificationOption::kNotify) {
481     WideString csValue;
482     int iIndex = GetSelectedIndex(0);
483     if (iIndex >= 0)
484       csValue = GetOptionLabel(iIndex);
485     if (!NotifyListOrComboBoxBeforeChange(csValue))
486       return false;
487   }
488   m_pDict->RemoveFor(pdfium::form_fields::kV);
489   m_pDict->RemoveFor("I");
490   if (notify == NotificationOption::kNotify)
491     NotifyListOrComboBoxAfterChange();
492   return true;
493 }
494 
IsItemSelected(int index) const495 bool CPDF_FormField::IsItemSelected(int index) const {
496   DCHECK(GetType() == kComboBox || GetType() == kListBox);
497   if (index < 0 || index >= CountOptions())
498     return false;
499 
500   // First consider the /I entry if it is valid, then fall back to the /V entry.
501   return m_bUseSelectedIndices ? IsSelectedIndex(index)
502                                : IsSelectedOption(GetOptionValue(index));
503 }
504 
SetItemSelection(int index,NotificationOption notify)505 bool CPDF_FormField::SetItemSelection(int index, NotificationOption notify) {
506   DCHECK(GetType() == kComboBox || GetType() == kListBox);
507   if (index < 0 || index >= CountOptions())
508     return false;
509 
510   WideString opt_value = GetOptionValue(index);
511   if (notify == NotificationOption::kNotify &&
512       !NotifyListOrComboBoxBeforeChange(opt_value)) {
513     return false;
514   }
515 
516   SetItemSelectionSelected(index, opt_value);
517 
518   // UseSelectedIndicesObject() has a non-trivial linearithmic run-time, so run
519   // only if necessary.
520   if (!m_bUseSelectedIndices)
521     m_bUseSelectedIndices = UseSelectedIndicesObject();
522 
523   if (notify == NotificationOption::kNotify)
524     NotifyListOrComboBoxAfterChange();
525   return true;
526 }
527 
SetItemSelectionSelected(int index,const WideString & opt_value)528 void CPDF_FormField::SetItemSelectionSelected(int index,
529                                               const WideString& opt_value) {
530   if (GetType() != kListBox) {
531     m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV,
532                                     opt_value.AsStringView());
533     auto pI = m_pDict->SetNewFor<CPDF_Array>("I");
534     pI->AppendNew<CPDF_Number>(index);
535     return;
536   }
537 
538   SelectOption(index);
539   if (!m_bIsMultiSelectListBox) {
540     m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV,
541                                     opt_value.AsStringView());
542     return;
543   }
544 
545   auto pArray = m_pDict->SetNewFor<CPDF_Array>(pdfium::form_fields::kV);
546   for (int i = 0; i < CountOptions(); i++) {
547     if (i == index || IsItemSelected(i))
548       pArray->AppendNew<CPDF_String>(GetOptionValue(i).AsStringView());
549   }
550 }
551 
GetDefaultSelectedItem() const552 int CPDF_FormField::GetDefaultSelectedItem() const {
553   DCHECK(GetType() == kComboBox || GetType() == kListBox);
554   RetainPtr<const CPDF_Object> pValue = GetDefaultValueObject();
555   if (!pValue)
556     return -1;
557   WideString csDV = pValue->GetUnicodeText();
558   if (csDV.IsEmpty())
559     return -1;
560   for (int i = 0; i < CountOptions(); i++) {
561     if (csDV == GetOptionValue(i))
562       return i;
563   }
564   return -1;
565 }
566 
CountOptions() const567 int CPDF_FormField::CountOptions() const {
568   RetainPtr<const CPDF_Array> pArray = ToArray(GetFieldAttrInternal("Opt"));
569   return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
570 }
571 
GetOptionText(int index,int sub_index) const572 WideString CPDF_FormField::GetOptionText(int index, int sub_index) const {
573   RetainPtr<const CPDF_Array> pArray = ToArray(GetFieldAttrInternal("Opt"));
574   if (!pArray)
575     return WideString();
576 
577   RetainPtr<const CPDF_Object> pOption = pArray->GetDirectObjectAt(index);
578   if (!pOption)
579     return WideString();
580 
581   const CPDF_Array* pOptionArray = pOption->AsArray();
582   if (pOptionArray)
583     pOption = pOptionArray->GetDirectObjectAt(sub_index);
584 
585   if (!pOption)
586     return WideString();
587 
588   const CPDF_String* pString = pOption->AsString();
589   return pString ? pString->GetUnicodeText() : WideString();
590 }
591 
GetOptionLabel(int index) const592 WideString CPDF_FormField::GetOptionLabel(int index) const {
593   return GetOptionText(index, 1);
594 }
595 
GetOptionValue(int index) const596 WideString CPDF_FormField::GetOptionValue(int index) const {
597   return GetOptionText(index, 0);
598 }
599 
FindOption(const WideString & csOptValue) const600 int CPDF_FormField::FindOption(const WideString& csOptValue) const {
601   for (int i = 0; i < CountOptions(); i++) {
602     if (GetOptionValue(i) == csOptValue)
603       return i;
604   }
605   return -1;
606 }
607 
CheckControl(int iControlIndex,bool bChecked,NotificationOption notify)608 bool CPDF_FormField::CheckControl(int iControlIndex,
609                                   bool bChecked,
610                                   NotificationOption notify) {
611   DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
612   CPDF_FormControl* pControl = GetControl(iControlIndex);
613   if (!pControl)
614     return false;
615   if (!bChecked && pControl->IsChecked() == bChecked)
616     return false;
617 
618   const WideString csWExport = pControl->GetExportValue();
619   int iCount = CountControls();
620   for (int i = 0; i < iCount; i++) {
621     CPDF_FormControl* pCtrl = GetControl(i);
622     if (m_bIsUnison) {
623       WideString csEValue = pCtrl->GetExportValue();
624       if (csEValue == csWExport) {
625         if (pCtrl->GetOnStateName() == pControl->GetOnStateName())
626           pCtrl->CheckControl(bChecked);
627         else if (bChecked)
628           pCtrl->CheckControl(false);
629       } else if (bChecked) {
630         pCtrl->CheckControl(false);
631       }
632     } else {
633       if (i == iControlIndex)
634         pCtrl->CheckControl(bChecked);
635       else if (bChecked)
636         pCtrl->CheckControl(false);
637     }
638   }
639 
640   RetainPtr<const CPDF_Object> pOpt = GetFieldAttrInternal("Opt");
641   if (!ToArray(pOpt)) {
642     ByteString csBExport = PDF_EncodeText(csWExport.AsStringView());
643     if (bChecked) {
644       m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
645     } else {
646       ByteString csV;
647       const CPDF_Object* pV = GetValueObject();
648       if (pV)
649         csV = pV->GetString();
650       if (csV == csBExport)
651         m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, "Off");
652     }
653   } else if (bChecked) {
654     m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV,
655                                   ByteString::FormatInteger(iControlIndex));
656   }
657   if (notify == NotificationOption::kNotify)
658     m_pForm->NotifyAfterCheckedStatusChange(this);
659   return true;
660 }
661 
GetCheckValue(bool bDefault) const662 WideString CPDF_FormField::GetCheckValue(bool bDefault) const {
663   DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
664   WideString csExport = L"Off";
665   int iCount = CountControls();
666   for (int i = 0; i < iCount; i++) {
667     CPDF_FormControl* pControl = GetControl(i);
668     bool bChecked =
669         bDefault ? pControl->IsDefaultChecked() : pControl->IsChecked();
670     if (bChecked) {
671       csExport = pControl->GetExportValue();
672       break;
673     }
674   }
675   return csExport;
676 }
677 
SetCheckValue(const WideString & value,bool bDefault,NotificationOption notify)678 bool CPDF_FormField::SetCheckValue(const WideString& value,
679                                    bool bDefault,
680                                    NotificationOption notify) {
681   DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
682   int iCount = CountControls();
683   for (int i = 0; i < iCount; i++) {
684     CPDF_FormControl* pControl = GetControl(i);
685     WideString csExport = pControl->GetExportValue();
686     bool val = csExport == value;
687     if (!bDefault) {
688       CheckControl(GetControlIndex(pControl), val,
689                    NotificationOption::kDoNotNotify);
690     }
691     if (val)
692       break;
693   }
694   if (notify == NotificationOption::kNotify)
695     m_pForm->NotifyAfterCheckedStatusChange(this);
696   return true;
697 }
698 
GetTopVisibleIndex() const699 int CPDF_FormField::GetTopVisibleIndex() const {
700   RetainPtr<const CPDF_Object> pObj = GetFieldAttrInternal("TI");
701   return pObj ? pObj->GetInteger() : 0;
702 }
703 
CountSelectedOptions() const704 int CPDF_FormField::CountSelectedOptions() const {
705   RetainPtr<const CPDF_Array> pArray = ToArray(GetSelectedIndicesObject());
706   return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
707 }
708 
GetSelectedOptionIndex(int index) const709 int CPDF_FormField::GetSelectedOptionIndex(int index) const {
710   if (index < 0)
711     return 0;
712 
713   RetainPtr<const CPDF_Array> pArray = ToArray(GetSelectedIndicesObject());
714   if (!pArray)
715     return -1;
716 
717   return index < fxcrt::CollectionSize<int>(*pArray)
718              ? pArray->GetIntegerAt(index)
719              : -1;
720 }
721 
IsSelectedOption(const WideString & wsOptValue) const722 bool CPDF_FormField::IsSelectedOption(const WideString& wsOptValue) const {
723   RetainPtr<const CPDF_Object> pValueObject = GetValueObject();
724   if (!pValueObject)
725     return false;
726 
727   const CPDF_Array* pValueArray = pValueObject->AsArray();
728   if (pValueArray) {
729     CPDF_ArrayLocker locker(pValueArray);
730     for (const auto& pObj : locker) {
731       if (pObj->IsString() && pObj->GetUnicodeText() == wsOptValue)
732         return true;
733     }
734   }
735 
736   return pValueObject->IsString() &&
737          pValueObject->GetUnicodeText() == wsOptValue;
738 }
739 
IsSelectedIndex(int iOptIndex) const740 bool CPDF_FormField::IsSelectedIndex(int iOptIndex) const {
741   RetainPtr<const CPDF_Object> pSelectedIndicesObject =
742       GetSelectedIndicesObject();
743   if (!pSelectedIndicesObject)
744     return false;
745 
746   const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray();
747   if (pSelectedIndicesArray) {
748     CPDF_ArrayLocker locker(pSelectedIndicesArray);
749     for (const auto& pObj : locker) {
750       if (pObj->IsNumber() && pObj->GetInteger() == iOptIndex)
751         return true;
752     }
753   }
754 
755   return pSelectedIndicesObject->IsNumber() &&
756          pSelectedIndicesObject->GetInteger() == iOptIndex;
757 }
758 
SelectOption(int iOptIndex)759 void CPDF_FormField::SelectOption(int iOptIndex) {
760   RetainPtr<CPDF_Array> pArray = m_pDict->GetOrCreateArrayFor("I");
761   for (size_t i = 0; i < pArray->size(); i++) {
762     int iFind = pArray->GetIntegerAt(i);
763     if (iFind == iOptIndex)
764       return;
765 
766     if (iFind > iOptIndex) {
767       pArray->InsertNewAt<CPDF_Number>(i, iOptIndex);
768       return;
769     }
770   }
771   pArray->AppendNew<CPDF_Number>(iOptIndex);
772 }
773 
UseSelectedIndicesObject() const774 bool CPDF_FormField::UseSelectedIndicesObject() const {
775   DCHECK(GetType() == kComboBox || GetType() == kListBox);
776 
777   RetainPtr<const CPDF_Object> pSelectedIndicesObject =
778       GetSelectedIndicesObject();
779   if (!pSelectedIndicesObject)
780     return false;
781 
782   // If there's not value object, then just use the indices object.
783   RetainPtr<const CPDF_Object> pValueObject = GetValueObject();
784   if (!pValueObject)
785     return true;
786 
787   // Verify that the selected indices object is either an array or a number and
788   // count the number of indices.
789   size_t selected_indices_size;
790   const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray();
791   if (pSelectedIndicesArray)
792     selected_indices_size = pSelectedIndicesArray->size();
793   else if (pSelectedIndicesObject->IsNumber())
794     selected_indices_size = 1;
795   else
796     return false;
797 
798   // Verify that the number of values is equal to |selected_indices_size|. Then,
799   // count the number of occurrences of each of the distinct values in the
800   // values object.
801   std::map<WideString, size_t> values;
802   const CPDF_Array* pValueArray = pValueObject->AsArray();
803   if (pValueArray) {
804     if (pValueArray->size() != selected_indices_size)
805       return false;
806     CPDF_ArrayLocker locker(pValueArray);
807     for (const auto& pObj : locker) {
808       if (pObj->IsString())
809         values[pObj->GetUnicodeText()]++;
810     }
811   } else if (pValueObject->IsString()) {
812     if (selected_indices_size != 1)
813       return false;
814     values[pValueObject->GetUnicodeText()]++;
815   }
816 
817   // Validate each index in the selected indices object. Then, verify that items
818   // identified by selected indices entry do not differ from those in the values
819   // entry of the field dictionary.
820   const int num_options = CountOptions();
821   if (pSelectedIndicesArray) {
822     CPDF_ArrayLocker locker(pSelectedIndicesArray);
823     for (const auto& pObj : locker) {
824       if (!pObj->IsNumber())
825         return false;
826 
827       int index = pObj->GetInteger();
828       if (index < 0 || index >= num_options)
829         return false;
830 
831       WideString wsOptValue = GetOptionValue(index);
832       auto it = values.find(wsOptValue);
833       if (it == values.end())
834         return false;
835 
836       it->second--;
837       if (it->second == 0)
838         values.erase(it);
839     }
840 
841     return values.empty();
842   }
843 
844   DCHECK(pSelectedIndicesObject->IsNumber());
845   int index = pSelectedIndicesObject->GetInteger();
846   if (index < 0 || index >= num_options)
847     return false;
848 
849   return pdfium::Contains(values, GetOptionValue(index));
850 }
851 
NotifyListOrComboBoxBeforeChange(const WideString & value)852 bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) {
853   switch (GetType()) {
854     case kListBox:
855       return m_pForm->NotifyBeforeSelectionChange(this, value);
856     case kComboBox:
857       return m_pForm->NotifyBeforeValueChange(this, value);
858     default:
859       return true;
860   }
861 }
862 
NotifyListOrComboBoxAfterChange()863 void CPDF_FormField::NotifyListOrComboBoxAfterChange() {
864   switch (GetType()) {
865     case kListBox:
866       m_pForm->NotifyAfterSelectionChange(this);
867       break;
868     case kComboBox:
869       m_pForm->NotifyAfterValueChange(this);
870       break;
871     default:
872       break;
873   }
874 }
875 
GetFieldAttrInternal(const ByteString & name) const876 RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttrInternal(
877     const ByteString& name) const {
878   return GetFieldAttrRecursive(m_pDict.Get(), name, 0);
879 }
880 
GetFieldDictInternal() const881 const CPDF_Dictionary* CPDF_FormField::GetFieldDictInternal() const {
882   return m_pDict.Get();
883 }
884 
GetDefaultValueObject() const885 RetainPtr<const CPDF_Object> CPDF_FormField::GetDefaultValueObject() const {
886   return GetFieldAttrInternal(pdfium::form_fields::kDV);
887 }
888 
GetValueObject() const889 RetainPtr<const CPDF_Object> CPDF_FormField::GetValueObject() const {
890   return GetFieldAttrInternal(pdfium::form_fields::kV);
891 }
892 
GetSelectedIndicesObject() const893 RetainPtr<const CPDF_Object> CPDF_FormField::GetSelectedIndicesObject() const {
894   DCHECK(GetType() == kComboBox || GetType() == kListBox);
895   return GetFieldAttrInternal("I");
896 }
897 
GetValueOrSelectedIndicesObject() const898 RetainPtr<const CPDF_Object> CPDF_FormField::GetValueOrSelectedIndicesObject()
899     const {
900   DCHECK(GetType() == kComboBox || GetType() == kListBox);
901   RetainPtr<const CPDF_Object> pValue = GetValueObject();
902   return pValue ? pValue : GetSelectedIndicesObject();
903 }
904 
GetControls() const905 const std::vector<UnownedPtr<CPDF_FormControl>>& CPDF_FormField::GetControls()
906     const {
907   return m_pForm->GetControlsForField(this);
908 }
909