// Copyright 2014 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fpdfdoc/cpdf_formfield.h" #include #include #include #include "constants/form_fields.h" #include "constants/form_flags.h" #include "core/fpdfapi/font/cpdf_font.h" #include "core/fpdfapi/page/cpdf_docpagedata.h" #include "core/fpdfapi/parser/cfdf_document.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_name.h" #include "core/fpdfapi/parser/cpdf_number.h" #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fpdfapi/parser/fpdf_parser_decode.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" #include "core/fpdfdoc/cpdf_defaultappearance.h" #include "core/fpdfdoc/cpdf_formcontrol.h" #include "core/fpdfdoc/cpdf_generateap.h" #include "core/fpdfdoc/cpdf_interactiveform.h" #include "core/fxcrt/stl_util.h" #include "third_party/base/check.h" #include "third_party/base/containers/contains.h" namespace { RetainPtr GetFieldAttrRecursive( const CPDF_Dictionary* pFieldDict, const ByteString& name, int nLevel) { static constexpr int kGetFieldMaxRecursion = 32; if (!pFieldDict || nLevel > kGetFieldMaxRecursion) return nullptr; RetainPtr pAttr = pFieldDict->GetDirectObjectFor(name); if (pAttr) return pAttr; return GetFieldAttrRecursive( pFieldDict->GetDictFor(pdfium::form_fields::kParent).Get(), name, nLevel + 1); } } // namespace // static absl::optional CPDF_FormField::IntToFormFieldType(int value) { if (value >= static_cast(FormFieldType::kUnknown) && value < static_cast(kFormFieldTypeCount)) { return static_cast(value); } return absl::nullopt; } // static RetainPtr CPDF_FormField::GetFieldAttrForDict( const CPDF_Dictionary* pFieldDict, const ByteString& name) { return GetFieldAttrRecursive(pFieldDict, name, 0); } // static RetainPtr CPDF_FormField::GetMutableFieldAttrForDict( CPDF_Dictionary* pFieldDict, const ByteString& name) { return pdfium::WrapRetain(const_cast( GetFieldAttrRecursive(pFieldDict, name, 0).Get())); } // static WideString CPDF_FormField::GetFullNameForDict( const CPDF_Dictionary* pFieldDict) { WideString full_name; std::set visited; const CPDF_Dictionary* pLevel = pFieldDict; while (pLevel) { visited.insert(pLevel); WideString short_name = pLevel->GetUnicodeTextFor(pdfium::form_fields::kT); if (!short_name.IsEmpty()) { if (full_name.IsEmpty()) full_name = std::move(short_name); else full_name = short_name + L'.' + full_name; } pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent).Get(); if (pdfium::Contains(visited, pLevel)) break; } return full_name; } CPDF_FormField::CPDF_FormField(CPDF_InteractiveForm* pForm, RetainPtr pDict) : m_pForm(pForm), m_pDict(std::move(pDict)) { InitFieldFlags(); } CPDF_FormField::~CPDF_FormField() = default; void CPDF_FormField::InitFieldFlags() { RetainPtr ft_attr = GetFieldAttrInternal(pdfium::form_fields::kFT); ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString(); uint32_t flags = GetFieldFlags(); m_bRequired = flags & pdfium::form_flags::kRequired; m_bNoExport = flags & pdfium::form_flags::kNoExport; if (type_name == pdfium::form_fields::kBtn) { if (flags & pdfium::form_flags::kButtonRadio) { m_Type = kRadioButton; m_bIsUnison = flags & pdfium::form_flags::kButtonRadiosInUnison; } else if (flags & pdfium::form_flags::kButtonPushbutton) { m_Type = kPushButton; } else { m_Type = kCheckBox; m_bIsUnison = true; } } else if (type_name == pdfium::form_fields::kTx) { if (flags & pdfium::form_flags::kTextFileSelect) m_Type = kFile; else if (flags & pdfium::form_flags::kTextRichText) m_Type = kRichText; else m_Type = kText; } else if (type_name == pdfium::form_fields::kCh) { if (flags & pdfium::form_flags::kChoiceCombo) { m_Type = kComboBox; } else { m_Type = kListBox; m_bIsMultiSelectListBox = flags & pdfium::form_flags::kChoiceMultiSelect; } m_bUseSelectedIndices = UseSelectedIndicesObject(); } else if (type_name == pdfium::form_fields::kSig) { m_Type = kSign; } } WideString CPDF_FormField::GetFullName() const { return GetFullNameForDict(m_pDict.Get()); } RetainPtr CPDF_FormField::GetFieldAttr( const ByteString& name) const { return GetFieldAttrInternal(name); } RetainPtr CPDF_FormField::GetFieldDict() const { return pdfium::WrapRetain(GetFieldDictInternal()); } bool CPDF_FormField::ResetField() { switch (m_Type) { case kCheckBox: case kRadioButton: { int iCount = CountControls(); // TODO(weili): Check whether anything special needs to be done for // |m_bIsUnison|. for (int i = 0; i < iCount; i++) { CheckControl(i, GetControl(i)->IsDefaultChecked(), NotificationOption::kDoNotNotify); } m_pForm->NotifyAfterCheckedStatusChange(this); break; } case kComboBox: case kListBox: { ClearSelection(NotificationOption::kDoNotNotify); WideString csValue; int iIndex = GetDefaultSelectedItem(); if (iIndex >= 0) csValue = GetOptionLabel(iIndex); if (!NotifyListOrComboBoxBeforeChange(csValue)) { return false; } SetItemSelection(iIndex, NotificationOption::kDoNotNotify); NotifyListOrComboBoxAfterChange(); break; } case kText: case kRichText: case kFile: default: { WideString csDValue; WideString csValue; { // Limit scope of |pDV| and |pV| because they may get invalidated // during notification below. RetainPtr pDV = GetDefaultValueObject(); if (pDV) csDValue = pDV->GetUnicodeText(); RetainPtr pV = GetValueObject(); if (pV) csValue = pV->GetUnicodeText(); } bool bHasRV = !!GetFieldAttrInternal(pdfium::form_fields::kRV); if (!bHasRV && (csDValue == csValue)) return false; if (!m_pForm->NotifyBeforeValueChange(this, csDValue)) return false; { // Limit scope of |pDV| because it may get invalidated during // notification below. RetainPtr pDV = GetDefaultValueObject(); if (pDV) { RetainPtr pClone = pDV->Clone(); if (!pClone) return false; m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone)); if (bHasRV) { m_pDict->SetFor(pdfium::form_fields::kRV, pDV->Clone()); } } else { m_pDict->RemoveFor(pdfium::form_fields::kV); m_pDict->RemoveFor(pdfium::form_fields::kRV); } } m_pForm->NotifyAfterValueChange(this); break; } } return true; } int CPDF_FormField::CountControls() const { return fxcrt::CollectionSize(GetControls()); } CPDF_FormControl* CPDF_FormField::GetControl(int index) const { return GetControls()[index]; } int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const { if (!pControl) return -1; const auto& controls = GetControls(); auto it = std::find(controls.begin(), controls.end(), pControl); if (it == controls.end()) return -1; return pdfium::base::checked_cast(it - controls.begin()); } FormFieldType CPDF_FormField::GetFieldType() const { switch (m_Type) { case kPushButton: return FormFieldType::kPushButton; case kCheckBox: return FormFieldType::kCheckBox; case kRadioButton: return FormFieldType::kRadioButton; case kComboBox: return FormFieldType::kComboBox; case kListBox: return FormFieldType::kListBox; case kText: case kRichText: case kFile: return FormFieldType::kTextField; case kSign: return FormFieldType::kSignature; default: return FormFieldType::kUnknown; } } CPDF_AAction CPDF_FormField::GetAdditionalAction() const { RetainPtr pObj = GetFieldAttrInternal(pdfium::form_fields::kAA); return CPDF_AAction(pObj ? pObj->GetDict() : nullptr); } WideString CPDF_FormField::GetAlternateName() const { RetainPtr pObj = GetFieldAttrInternal(pdfium::form_fields::kTU); return pObj ? pObj->GetUnicodeText() : WideString(); } WideString CPDF_FormField::GetMappingName() const { RetainPtr pObj = GetFieldAttrInternal(pdfium::form_fields::kTM); return pObj ? pObj->GetUnicodeText() : WideString(); } uint32_t CPDF_FormField::GetFieldFlags() const { RetainPtr pObj = GetFieldAttrInternal(pdfium::form_fields::kFf); return pObj ? pObj->GetInteger() : 0; } void CPDF_FormField::SetFieldFlags(uint32_t dwFlags) { m_pDict->SetNewFor(pdfium::form_fields::kFf, static_cast(dwFlags)); } WideString CPDF_FormField::GetValue(bool bDefault) const { if (GetType() == kCheckBox || GetType() == kRadioButton) return GetCheckValue(bDefault); RetainPtr pValue = bDefault ? GetDefaultValueObject() : GetValueObject(); if (!pValue) { if (!bDefault && m_Type != kText) pValue = GetDefaultValueObject(); if (!pValue) return WideString(); } switch (pValue->GetType()) { case CPDF_Object::kString: case CPDF_Object::kStream: return pValue->GetUnicodeText(); case CPDF_Object::kArray: { RetainPtr pNewValue = pValue->AsArray()->GetDirectObjectAt(0); if (pNewValue) return pNewValue->GetUnicodeText(); break; } default: break; } return WideString(); } WideString CPDF_FormField::GetValue() const { return GetValue(false); } WideString CPDF_FormField::GetDefaultValue() const { return GetValue(true); } bool CPDF_FormField::SetValue(const WideString& value, bool bDefault, NotificationOption notify) { switch (m_Type) { case kCheckBox: case kRadioButton: { SetCheckValue(value, bDefault, notify); return true; } case kFile: case kRichText: case kText: case kComboBox: { WideString csValue = value; if (notify == NotificationOption::kNotify && !m_pForm->NotifyBeforeValueChange(this, csValue)) { return false; } ByteString key(bDefault ? pdfium::form_fields::kDV : pdfium::form_fields::kV); m_pDict->SetNewFor(key, csValue.AsStringView()); int iIndex = FindOption(csValue); if (iIndex < 0) { if (m_Type == kRichText && !bDefault) { m_pDict->SetFor(pdfium::form_fields::kRV, m_pDict->GetObjectFor(key)->Clone()); } m_pDict->RemoveFor("I"); } else { if (!bDefault) { ClearSelection(NotificationOption::kDoNotNotify); SetItemSelection(iIndex, NotificationOption::kDoNotNotify); } } if (notify == NotificationOption::kNotify) m_pForm->NotifyAfterValueChange(this); break; } case kListBox: { int iIndex = FindOption(value); if (iIndex < 0) return false; if (bDefault && iIndex == GetDefaultSelectedItem()) return false; if (notify == NotificationOption::kNotify && !m_pForm->NotifyBeforeSelectionChange(this, value)) { return false; } if (!bDefault) { ClearSelection(NotificationOption::kDoNotNotify); SetItemSelection(iIndex, NotificationOption::kDoNotNotify); } if (notify == NotificationOption::kNotify) m_pForm->NotifyAfterSelectionChange(this); break; } default: break; } return true; } bool CPDF_FormField::SetValue(const WideString& value, NotificationOption notify) { return SetValue(value, false, notify); } int CPDF_FormField::GetMaxLen() const { RetainPtr pObj = GetFieldAttrInternal("MaxLen"); if (pObj) return pObj->GetInteger(); for (auto& pControl : GetControls()) { if (!pControl) continue; RetainPtr pWidgetDict = pControl->GetWidgetDict(); if (pWidgetDict->KeyExist("MaxLen")) return pWidgetDict->GetIntegerFor("MaxLen"); } return 0; } int CPDF_FormField::CountSelectedItems() const { const CPDF_Object* pValue = GetValueOrSelectedIndicesObject(); if (!pValue) return 0; if (pValue->IsString() || pValue->IsNumber()) return pValue->GetString().IsEmpty() ? 0 : 1; const CPDF_Array* pArray = pValue->AsArray(); return pArray ? fxcrt::CollectionSize(*pArray) : 0; } int CPDF_FormField::GetSelectedIndex(int index) const { const CPDF_Object* pValue = GetValueOrSelectedIndicesObject(); if (!pValue) return -1; if (pValue->IsNumber()) return pValue->GetInteger(); WideString sel_value; if (pValue->IsString()) { if (index != 0) return -1; sel_value = pValue->GetUnicodeText(); } else { const CPDF_Array* pArray = pValue->AsArray(); if (!pArray || index < 0) return -1; RetainPtr elementValue = pArray->GetDirectObjectAt(index); sel_value = elementValue ? elementValue->GetUnicodeText() : WideString(); } if (index < CountSelectedOptions()) { int iOptIndex = GetSelectedOptionIndex(index); WideString csOpt = GetOptionValue(iOptIndex); if (csOpt == sel_value) return iOptIndex; } for (int i = 0; i < CountOptions(); i++) { if (sel_value == GetOptionValue(i)) return i; } return -1; } bool CPDF_FormField::ClearSelection(NotificationOption notify) { if (notify == NotificationOption::kNotify) { WideString csValue; int iIndex = GetSelectedIndex(0); if (iIndex >= 0) csValue = GetOptionLabel(iIndex); if (!NotifyListOrComboBoxBeforeChange(csValue)) return false; } m_pDict->RemoveFor(pdfium::form_fields::kV); m_pDict->RemoveFor("I"); if (notify == NotificationOption::kNotify) NotifyListOrComboBoxAfterChange(); return true; } bool CPDF_FormField::IsItemSelected(int index) const { DCHECK(GetType() == kComboBox || GetType() == kListBox); if (index < 0 || index >= CountOptions()) return false; // First consider the /I entry if it is valid, then fall back to the /V entry. return m_bUseSelectedIndices ? IsSelectedIndex(index) : IsSelectedOption(GetOptionValue(index)); } bool CPDF_FormField::SetItemSelection(int index, NotificationOption notify) { DCHECK(GetType() == kComboBox || GetType() == kListBox); if (index < 0 || index >= CountOptions()) return false; WideString opt_value = GetOptionValue(index); if (notify == NotificationOption::kNotify && !NotifyListOrComboBoxBeforeChange(opt_value)) { return false; } SetItemSelectionSelected(index, opt_value); // UseSelectedIndicesObject() has a non-trivial linearithmic run-time, so run // only if necessary. if (!m_bUseSelectedIndices) m_bUseSelectedIndices = UseSelectedIndicesObject(); if (notify == NotificationOption::kNotify) NotifyListOrComboBoxAfterChange(); return true; } void CPDF_FormField::SetItemSelectionSelected(int index, const WideString& opt_value) { if (GetType() != kListBox) { m_pDict->SetNewFor(pdfium::form_fields::kV, opt_value.AsStringView()); auto pI = m_pDict->SetNewFor("I"); pI->AppendNew(index); return; } SelectOption(index); if (!m_bIsMultiSelectListBox) { m_pDict->SetNewFor(pdfium::form_fields::kV, opt_value.AsStringView()); return; } auto pArray = m_pDict->SetNewFor(pdfium::form_fields::kV); for (int i = 0; i < CountOptions(); i++) { if (i == index || IsItemSelected(i)) pArray->AppendNew(GetOptionValue(i).AsStringView()); } } int CPDF_FormField::GetDefaultSelectedItem() const { DCHECK(GetType() == kComboBox || GetType() == kListBox); RetainPtr pValue = GetDefaultValueObject(); if (!pValue) return -1; WideString csDV = pValue->GetUnicodeText(); if (csDV.IsEmpty()) return -1; for (int i = 0; i < CountOptions(); i++) { if (csDV == GetOptionValue(i)) return i; } return -1; } int CPDF_FormField::CountOptions() const { RetainPtr pArray = ToArray(GetFieldAttrInternal("Opt")); return pArray ? fxcrt::CollectionSize(*pArray) : 0; } WideString CPDF_FormField::GetOptionText(int index, int sub_index) const { RetainPtr pArray = ToArray(GetFieldAttrInternal("Opt")); if (!pArray) return WideString(); RetainPtr pOption = pArray->GetDirectObjectAt(index); if (!pOption) return WideString(); const CPDF_Array* pOptionArray = pOption->AsArray(); if (pOptionArray) pOption = pOptionArray->GetDirectObjectAt(sub_index); if (!pOption) return WideString(); const CPDF_String* pString = pOption->AsString(); return pString ? pString->GetUnicodeText() : WideString(); } WideString CPDF_FormField::GetOptionLabel(int index) const { return GetOptionText(index, 1); } WideString CPDF_FormField::GetOptionValue(int index) const { return GetOptionText(index, 0); } int CPDF_FormField::FindOption(const WideString& csOptValue) const { for (int i = 0; i < CountOptions(); i++) { if (GetOptionValue(i) == csOptValue) return i; } return -1; } bool CPDF_FormField::CheckControl(int iControlIndex, bool bChecked, NotificationOption notify) { DCHECK(GetType() == kCheckBox || GetType() == kRadioButton); CPDF_FormControl* pControl = GetControl(iControlIndex); if (!pControl) return false; if (!bChecked && pControl->IsChecked() == bChecked) return false; const WideString csWExport = pControl->GetExportValue(); int iCount = CountControls(); for (int i = 0; i < iCount; i++) { CPDF_FormControl* pCtrl = GetControl(i); if (m_bIsUnison) { WideString csEValue = pCtrl->GetExportValue(); if (csEValue == csWExport) { if (pCtrl->GetOnStateName() == pControl->GetOnStateName()) pCtrl->CheckControl(bChecked); else if (bChecked) pCtrl->CheckControl(false); } else if (bChecked) { pCtrl->CheckControl(false); } } else { if (i == iControlIndex) pCtrl->CheckControl(bChecked); else if (bChecked) pCtrl->CheckControl(false); } } RetainPtr pOpt = GetFieldAttrInternal("Opt"); if (!ToArray(pOpt)) { ByteString csBExport = PDF_EncodeText(csWExport.AsStringView()); if (bChecked) { m_pDict->SetNewFor(pdfium::form_fields::kV, csBExport); } else { ByteString csV; const CPDF_Object* pV = GetValueObject(); if (pV) csV = pV->GetString(); if (csV == csBExport) m_pDict->SetNewFor(pdfium::form_fields::kV, "Off"); } } else if (bChecked) { m_pDict->SetNewFor(pdfium::form_fields::kV, ByteString::FormatInteger(iControlIndex)); } if (notify == NotificationOption::kNotify) m_pForm->NotifyAfterCheckedStatusChange(this); return true; } WideString CPDF_FormField::GetCheckValue(bool bDefault) const { DCHECK(GetType() == kCheckBox || GetType() == kRadioButton); WideString csExport = L"Off"; int iCount = CountControls(); for (int i = 0; i < iCount; i++) { CPDF_FormControl* pControl = GetControl(i); bool bChecked = bDefault ? pControl->IsDefaultChecked() : pControl->IsChecked(); if (bChecked) { csExport = pControl->GetExportValue(); break; } } return csExport; } bool CPDF_FormField::SetCheckValue(const WideString& value, bool bDefault, NotificationOption notify) { DCHECK(GetType() == kCheckBox || GetType() == kRadioButton); int iCount = CountControls(); for (int i = 0; i < iCount; i++) { CPDF_FormControl* pControl = GetControl(i); WideString csExport = pControl->GetExportValue(); bool val = csExport == value; if (!bDefault) { CheckControl(GetControlIndex(pControl), val, NotificationOption::kDoNotNotify); } if (val) break; } if (notify == NotificationOption::kNotify) m_pForm->NotifyAfterCheckedStatusChange(this); return true; } int CPDF_FormField::GetTopVisibleIndex() const { RetainPtr pObj = GetFieldAttrInternal("TI"); return pObj ? pObj->GetInteger() : 0; } int CPDF_FormField::CountSelectedOptions() const { RetainPtr pArray = ToArray(GetSelectedIndicesObject()); return pArray ? fxcrt::CollectionSize(*pArray) : 0; } int CPDF_FormField::GetSelectedOptionIndex(int index) const { if (index < 0) return 0; RetainPtr pArray = ToArray(GetSelectedIndicesObject()); if (!pArray) return -1; return index < fxcrt::CollectionSize(*pArray) ? pArray->GetIntegerAt(index) : -1; } bool CPDF_FormField::IsSelectedOption(const WideString& wsOptValue) const { RetainPtr pValueObject = GetValueObject(); if (!pValueObject) return false; const CPDF_Array* pValueArray = pValueObject->AsArray(); if (pValueArray) { CPDF_ArrayLocker locker(pValueArray); for (const auto& pObj : locker) { if (pObj->IsString() && pObj->GetUnicodeText() == wsOptValue) return true; } } return pValueObject->IsString() && pValueObject->GetUnicodeText() == wsOptValue; } bool CPDF_FormField::IsSelectedIndex(int iOptIndex) const { RetainPtr pSelectedIndicesObject = GetSelectedIndicesObject(); if (!pSelectedIndicesObject) return false; const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray(); if (pSelectedIndicesArray) { CPDF_ArrayLocker locker(pSelectedIndicesArray); for (const auto& pObj : locker) { if (pObj->IsNumber() && pObj->GetInteger() == iOptIndex) return true; } } return pSelectedIndicesObject->IsNumber() && pSelectedIndicesObject->GetInteger() == iOptIndex; } void CPDF_FormField::SelectOption(int iOptIndex) { RetainPtr pArray = m_pDict->GetOrCreateArrayFor("I"); for (size_t i = 0; i < pArray->size(); i++) { int iFind = pArray->GetIntegerAt(i); if (iFind == iOptIndex) return; if (iFind > iOptIndex) { pArray->InsertNewAt(i, iOptIndex); return; } } pArray->AppendNew(iOptIndex); } bool CPDF_FormField::UseSelectedIndicesObject() const { DCHECK(GetType() == kComboBox || GetType() == kListBox); RetainPtr pSelectedIndicesObject = GetSelectedIndicesObject(); if (!pSelectedIndicesObject) return false; // If there's not value object, then just use the indices object. RetainPtr pValueObject = GetValueObject(); if (!pValueObject) return true; // Verify that the selected indices object is either an array or a number and // count the number of indices. size_t selected_indices_size; const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray(); if (pSelectedIndicesArray) selected_indices_size = pSelectedIndicesArray->size(); else if (pSelectedIndicesObject->IsNumber()) selected_indices_size = 1; else return false; // Verify that the number of values is equal to |selected_indices_size|. Then, // count the number of occurrences of each of the distinct values in the // values object. std::map values; const CPDF_Array* pValueArray = pValueObject->AsArray(); if (pValueArray) { if (pValueArray->size() != selected_indices_size) return false; CPDF_ArrayLocker locker(pValueArray); for (const auto& pObj : locker) { if (pObj->IsString()) values[pObj->GetUnicodeText()]++; } } else if (pValueObject->IsString()) { if (selected_indices_size != 1) return false; values[pValueObject->GetUnicodeText()]++; } // Validate each index in the selected indices object. Then, verify that items // identified by selected indices entry do not differ from those in the values // entry of the field dictionary. const int num_options = CountOptions(); if (pSelectedIndicesArray) { CPDF_ArrayLocker locker(pSelectedIndicesArray); for (const auto& pObj : locker) { if (!pObj->IsNumber()) return false; int index = pObj->GetInteger(); if (index < 0 || index >= num_options) return false; WideString wsOptValue = GetOptionValue(index); auto it = values.find(wsOptValue); if (it == values.end()) return false; it->second--; if (it->second == 0) values.erase(it); } return values.empty(); } DCHECK(pSelectedIndicesObject->IsNumber()); int index = pSelectedIndicesObject->GetInteger(); if (index < 0 || index >= num_options) return false; return pdfium::Contains(values, GetOptionValue(index)); } bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) { switch (GetType()) { case kListBox: return m_pForm->NotifyBeforeSelectionChange(this, value); case kComboBox: return m_pForm->NotifyBeforeValueChange(this, value); default: return true; } } void CPDF_FormField::NotifyListOrComboBoxAfterChange() { switch (GetType()) { case kListBox: m_pForm->NotifyAfterSelectionChange(this); break; case kComboBox: m_pForm->NotifyAfterValueChange(this); break; default: break; } } RetainPtr CPDF_FormField::GetFieldAttrInternal( const ByteString& name) const { return GetFieldAttrRecursive(m_pDict.Get(), name, 0); } const CPDF_Dictionary* CPDF_FormField::GetFieldDictInternal() const { return m_pDict.Get(); } RetainPtr CPDF_FormField::GetDefaultValueObject() const { return GetFieldAttrInternal(pdfium::form_fields::kDV); } RetainPtr CPDF_FormField::GetValueObject() const { return GetFieldAttrInternal(pdfium::form_fields::kV); } RetainPtr CPDF_FormField::GetSelectedIndicesObject() const { DCHECK(GetType() == kComboBox || GetType() == kListBox); return GetFieldAttrInternal("I"); } RetainPtr CPDF_FormField::GetValueOrSelectedIndicesObject() const { DCHECK(GetType() == kComboBox || GetType() == kListBox); RetainPtr pValue = GetValueObject(); return pValue ? pValue : GetSelectedIndicesObject(); } const std::vector>& CPDF_FormField::GetControls() const { return m_pForm->GetControlsForField(this); }