// Copyright 2016 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 "xfa/fxfa/parser/cxfa_node.h"

#include <math.h>
#include <stdint.h>

#include <algorithm>
#include <array>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>

#include "core/fxcrt/autorestorer.h"
#include "core/fxcrt/cfx_read_only_container_stream.h"
#include "core/fxcrt/check.h"
#include "core/fxcrt/check_op.h"
#include "core/fxcrt/containers/contains.h"
#include "core/fxcrt/data_vector.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/notreached.h"
#include "core/fxcrt/span.h"
#include "core/fxcrt/stl_util.h"
#include "core/fxcrt/xml/cfx_xmldocument.h"
#include "core/fxcrt/xml/cfx_xmlelement.h"
#include "core/fxcrt/xml/cfx_xmlnode.h"
#include "core/fxcrt/xml/cfx_xmltext.h"
#include "core/fxge/dib/cfx_dibitmap.h"
#include "core/fxge/fx_font.h"
#include "fxjs/fxv8.h"
#include "fxjs/gc/container_trace.h"
#include "fxjs/xfa/cfxjse_engine.h"
#include "fxjs/xfa/cfxjse_isolatetracker.h"
#include "fxjs/xfa/cjx_node.h"
#include "xfa/fde/cfde_textout.h"
#include "xfa/fgas/crt/cfgas_decimal.h"
#include "xfa/fgas/crt/locale_iface.h"
#include "xfa/fgas/font/cfgas_fontmgr.h"
#include "xfa/fgas/font/cfgas_gefont.h"
#include "xfa/fxfa/cxfa_eventparam.h"
#include "xfa/fxfa/cxfa_ffapp.h"
#include "xfa/fxfa/cxfa_ffdocview.h"
#include "xfa/fxfa/cxfa_ffnotify.h"
#include "xfa/fxfa/cxfa_fontmgr.h"
#include "xfa/fxfa/cxfa_textprovider.h"
#include "xfa/fxfa/parser/cxfa_accessiblecontent.h"
#include "xfa/fxfa/parser/cxfa_acrobat.h"
#include "xfa/fxfa/parser/cxfa_acrobat7.h"
#include "xfa/fxfa/parser/cxfa_adbe_jsconsole.h"
#include "xfa/fxfa/parser/cxfa_adbe_jsdebugger.h"
#include "xfa/fxfa/parser/cxfa_addsilentprint.h"
#include "xfa/fxfa/parser/cxfa_addviewerpreferences.h"
#include "xfa/fxfa/parser/cxfa_adjustdata.h"
#include "xfa/fxfa/parser/cxfa_adobeextensionlevel.h"
#include "xfa/fxfa/parser/cxfa_agent.h"
#include "xfa/fxfa/parser/cxfa_alwaysembed.h"
#include "xfa/fxfa/parser/cxfa_amd.h"
#include "xfa/fxfa/parser/cxfa_appearancefilter.h"
#include "xfa/fxfa/parser/cxfa_arc.h"
#include "xfa/fxfa/parser/cxfa_area.h"
#include "xfa/fxfa/parser/cxfa_arraynodelist.h"
#include "xfa/fxfa/parser/cxfa_assist.h"
#include "xfa/fxfa/parser/cxfa_attachnodelist.h"
#include "xfa/fxfa/parser/cxfa_attributes.h"
#include "xfa/fxfa/parser/cxfa_autosave.h"
#include "xfa/fxfa/parser/cxfa_barcode.h"
#include "xfa/fxfa/parser/cxfa_base.h"
#include "xfa/fxfa/parser/cxfa_batchoutput.h"
#include "xfa/fxfa/parser/cxfa_behavioroverride.h"
#include "xfa/fxfa/parser/cxfa_bind.h"
#include "xfa/fxfa/parser/cxfa_binditems.h"
#include "xfa/fxfa/parser/cxfa_bookend.h"
#include "xfa/fxfa/parser/cxfa_boolean.h"
#include "xfa/fxfa/parser/cxfa_border.h"
#include "xfa/fxfa/parser/cxfa_break.h"
#include "xfa/fxfa/parser/cxfa_breakafter.h"
#include "xfa/fxfa/parser/cxfa_breakbefore.h"
#include "xfa/fxfa/parser/cxfa_button.h"
#include "xfa/fxfa/parser/cxfa_cache.h"
#include "xfa/fxfa/parser/cxfa_calculate.h"
#include "xfa/fxfa/parser/cxfa_calendarsymbols.h"
#include "xfa/fxfa/parser/cxfa_caption.h"
#include "xfa/fxfa/parser/cxfa_certificate.h"
#include "xfa/fxfa/parser/cxfa_certificates.h"
#include "xfa/fxfa/parser/cxfa_change.h"
#include "xfa/fxfa/parser/cxfa_checkbutton.h"
#include "xfa/fxfa/parser/cxfa_choicelist.h"
#include "xfa/fxfa/parser/cxfa_color.h"
#include "xfa/fxfa/parser/cxfa_comb.h"
#include "xfa/fxfa/parser/cxfa_command.h"
#include "xfa/fxfa/parser/cxfa_common.h"
#include "xfa/fxfa/parser/cxfa_compress.h"
#include "xfa/fxfa/parser/cxfa_compression.h"
#include "xfa/fxfa/parser/cxfa_compresslogicalstructure.h"
#include "xfa/fxfa/parser/cxfa_compressobjectstream.h"
#include "xfa/fxfa/parser/cxfa_config.h"
#include "xfa/fxfa/parser/cxfa_conformance.h"
#include "xfa/fxfa/parser/cxfa_connect.h"
#include "xfa/fxfa/parser/cxfa_connectionset.h"
#include "xfa/fxfa/parser/cxfa_connectstring.h"
#include "xfa/fxfa/parser/cxfa_contentarea.h"
#include "xfa/fxfa/parser/cxfa_contentcopy.h"
#include "xfa/fxfa/parser/cxfa_copies.h"
#include "xfa/fxfa/parser/cxfa_corner.h"
#include "xfa/fxfa/parser/cxfa_creator.h"
#include "xfa/fxfa/parser/cxfa_currencysymbol.h"
#include "xfa/fxfa/parser/cxfa_currencysymbols.h"
#include "xfa/fxfa/parser/cxfa_currentpage.h"
#include "xfa/fxfa/parser/cxfa_data.h"
#include "xfa/fxfa/parser/cxfa_datagroup.h"
#include "xfa/fxfa/parser/cxfa_datamodel.h"
#include "xfa/fxfa/parser/cxfa_datavalue.h"
#include "xfa/fxfa/parser/cxfa_date.h"
#include "xfa/fxfa/parser/cxfa_datepattern.h"
#include "xfa/fxfa/parser/cxfa_datepatterns.h"
#include "xfa/fxfa/parser/cxfa_datetime.h"
#include "xfa/fxfa/parser/cxfa_datetimeedit.h"
#include "xfa/fxfa/parser/cxfa_datetimesymbols.h"
#include "xfa/fxfa/parser/cxfa_day.h"
#include "xfa/fxfa/parser/cxfa_daynames.h"
#include "xfa/fxfa/parser/cxfa_debug.h"
#include "xfa/fxfa/parser/cxfa_decimal.h"
#include "xfa/fxfa/parser/cxfa_defaulttypeface.h"
#include "xfa/fxfa/parser/cxfa_defaultui.h"
#include "xfa/fxfa/parser/cxfa_delete.h"
#include "xfa/fxfa/parser/cxfa_delta.h"
#include "xfa/fxfa/parser/cxfa_desc.h"
#include "xfa/fxfa/parser/cxfa_destination.h"
#include "xfa/fxfa/parser/cxfa_digestmethod.h"
#include "xfa/fxfa/parser/cxfa_digestmethods.h"
#include "xfa/fxfa/parser/cxfa_document.h"
#include "xfa/fxfa/parser/cxfa_document_builder.h"
#include "xfa/fxfa/parser/cxfa_documentassembly.h"
#include "xfa/fxfa/parser/cxfa_draw.h"
#include "xfa/fxfa/parser/cxfa_driver.h"
#include "xfa/fxfa/parser/cxfa_dsigdata.h"
#include "xfa/fxfa/parser/cxfa_duplexoption.h"
#include "xfa/fxfa/parser/cxfa_dynamicrender.h"
#include "xfa/fxfa/parser/cxfa_edge.h"
#include "xfa/fxfa/parser/cxfa_effectiveinputpolicy.h"
#include "xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h"
#include "xfa/fxfa/parser/cxfa_embed.h"
#include "xfa/fxfa/parser/cxfa_encoding.h"
#include "xfa/fxfa/parser/cxfa_encodings.h"
#include "xfa/fxfa/parser/cxfa_encrypt.h"
#include "xfa/fxfa/parser/cxfa_encryption.h"
#include "xfa/fxfa/parser/cxfa_encryptionlevel.h"
#include "xfa/fxfa/parser/cxfa_encryptionmethod.h"
#include "xfa/fxfa/parser/cxfa_encryptionmethods.h"
#include "xfa/fxfa/parser/cxfa_enforce.h"
#include "xfa/fxfa/parser/cxfa_equate.h"
#include "xfa/fxfa/parser/cxfa_equaterange.h"
#include "xfa/fxfa/parser/cxfa_era.h"
#include "xfa/fxfa/parser/cxfa_eranames.h"
#include "xfa/fxfa/parser/cxfa_event.h"
#include "xfa/fxfa/parser/cxfa_exclgroup.h"
#include "xfa/fxfa/parser/cxfa_exclude.h"
#include "xfa/fxfa/parser/cxfa_excludens.h"
#include "xfa/fxfa/parser/cxfa_exdata.h"
#include "xfa/fxfa/parser/cxfa_execute.h"
#include "xfa/fxfa/parser/cxfa_exobject.h"
#include "xfa/fxfa/parser/cxfa_extras.h"
#include "xfa/fxfa/parser/cxfa_field.h"
#include "xfa/fxfa/parser/cxfa_fill.h"
#include "xfa/fxfa/parser/cxfa_filter.h"
#include "xfa/fxfa/parser/cxfa_fliplabel.h"
#include "xfa/fxfa/parser/cxfa_float.h"
#include "xfa/fxfa/parser/cxfa_font.h"
#include "xfa/fxfa/parser/cxfa_fontinfo.h"
#include "xfa/fxfa/parser/cxfa_form.h"
#include "xfa/fxfa/parser/cxfa_format.h"
#include "xfa/fxfa/parser/cxfa_formfieldfilling.h"
#include "xfa/fxfa/parser/cxfa_groupparent.h"
#include "xfa/fxfa/parser/cxfa_handler.h"
#include "xfa/fxfa/parser/cxfa_hyphenation.h"
#include "xfa/fxfa/parser/cxfa_ifempty.h"
#include "xfa/fxfa/parser/cxfa_image.h"
#include "xfa/fxfa/parser/cxfa_imageedit.h"
#include "xfa/fxfa/parser/cxfa_includexdpcontent.h"
#include "xfa/fxfa/parser/cxfa_incrementalload.h"
#include "xfa/fxfa/parser/cxfa_incrementalmerge.h"
#include "xfa/fxfa/parser/cxfa_insert.h"
#include "xfa/fxfa/parser/cxfa_instancemanager.h"
#include "xfa/fxfa/parser/cxfa_integer.h"
#include "xfa/fxfa/parser/cxfa_interactive.h"
#include "xfa/fxfa/parser/cxfa_issuers.h"
#include "xfa/fxfa/parser/cxfa_items.h"
#include "xfa/fxfa/parser/cxfa_jog.h"
#include "xfa/fxfa/parser/cxfa_keep.h"
#include "xfa/fxfa/parser/cxfa_keyusage.h"
#include "xfa/fxfa/parser/cxfa_labelprinter.h"
#include "xfa/fxfa/parser/cxfa_layout.h"
#include "xfa/fxfa/parser/cxfa_level.h"
#include "xfa/fxfa/parser/cxfa_line.h"
#include "xfa/fxfa/parser/cxfa_linear.h"
#include "xfa/fxfa/parser/cxfa_linearized.h"
#include "xfa/fxfa/parser/cxfa_locale.h"
#include "xfa/fxfa/parser/cxfa_localeset.h"
#include "xfa/fxfa/parser/cxfa_localevalue.h"
#include "xfa/fxfa/parser/cxfa_lockdocument.h"
#include "xfa/fxfa/parser/cxfa_log.h"
#include "xfa/fxfa/parser/cxfa_manifest.h"
#include "xfa/fxfa/parser/cxfa_map.h"
#include "xfa/fxfa/parser/cxfa_margin.h"
#include "xfa/fxfa/parser/cxfa_mdp.h"
#include "xfa/fxfa/parser/cxfa_measurement.h"
#include "xfa/fxfa/parser/cxfa_medium.h"
#include "xfa/fxfa/parser/cxfa_mediuminfo.h"
#include "xfa/fxfa/parser/cxfa_meridiem.h"
#include "xfa/fxfa/parser/cxfa_meridiemnames.h"
#include "xfa/fxfa/parser/cxfa_message.h"
#include "xfa/fxfa/parser/cxfa_messaging.h"
#include "xfa/fxfa/parser/cxfa_mode.h"
#include "xfa/fxfa/parser/cxfa_modifyannots.h"
#include "xfa/fxfa/parser/cxfa_month.h"
#include "xfa/fxfa/parser/cxfa_monthnames.h"
#include "xfa/fxfa/parser/cxfa_msgid.h"
#include "xfa/fxfa/parser/cxfa_nameattr.h"
#include "xfa/fxfa/parser/cxfa_neverembed.h"
#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
#include "xfa/fxfa/parser/cxfa_numberofcopies.h"
#include "xfa/fxfa/parser/cxfa_numberpattern.h"
#include "xfa/fxfa/parser/cxfa_numberpatterns.h"
#include "xfa/fxfa/parser/cxfa_numbersymbol.h"
#include "xfa/fxfa/parser/cxfa_numbersymbols.h"
#include "xfa/fxfa/parser/cxfa_numericedit.h"
#include "xfa/fxfa/parser/cxfa_occur.h"
#include "xfa/fxfa/parser/cxfa_oid.h"
#include "xfa/fxfa/parser/cxfa_oids.h"
#include "xfa/fxfa/parser/cxfa_openaction.h"
#include "xfa/fxfa/parser/cxfa_operation.h"
#include "xfa/fxfa/parser/cxfa_output.h"
#include "xfa/fxfa/parser/cxfa_outputbin.h"
#include "xfa/fxfa/parser/cxfa_outputxsl.h"
#include "xfa/fxfa/parser/cxfa_overflow.h"
#include "xfa/fxfa/parser/cxfa_overprint.h"
#include "xfa/fxfa/parser/cxfa_packet.h"
#include "xfa/fxfa/parser/cxfa_packets.h"
#include "xfa/fxfa/parser/cxfa_pagearea.h"
#include "xfa/fxfa/parser/cxfa_pageoffset.h"
#include "xfa/fxfa/parser/cxfa_pagerange.h"
#include "xfa/fxfa/parser/cxfa_pageset.h"
#include "xfa/fxfa/parser/cxfa_pagination.h"
#include "xfa/fxfa/parser/cxfa_paginationoverride.h"
#include "xfa/fxfa/parser/cxfa_para.h"
#include "xfa/fxfa/parser/cxfa_part.h"
#include "xfa/fxfa/parser/cxfa_password.h"
#include "xfa/fxfa/parser/cxfa_passwordedit.h"
#include "xfa/fxfa/parser/cxfa_pattern.h"
#include "xfa/fxfa/parser/cxfa_pcl.h"
#include "xfa/fxfa/parser/cxfa_pdf.h"
#include "xfa/fxfa/parser/cxfa_pdfa.h"
#include "xfa/fxfa/parser/cxfa_permissions.h"
#include "xfa/fxfa/parser/cxfa_picktraybypdfsize.h"
#include "xfa/fxfa/parser/cxfa_picture.h"
#include "xfa/fxfa/parser/cxfa_plaintextmetadata.h"
#include "xfa/fxfa/parser/cxfa_presence.h"
#include "xfa/fxfa/parser/cxfa_present.h"
#include "xfa/fxfa/parser/cxfa_print.h"
#include "xfa/fxfa/parser/cxfa_printername.h"
#include "xfa/fxfa/parser/cxfa_printhighquality.h"
#include "xfa/fxfa/parser/cxfa_printscaling.h"
#include "xfa/fxfa/parser/cxfa_producer.h"
#include "xfa/fxfa/parser/cxfa_proto.h"
#include "xfa/fxfa/parser/cxfa_ps.h"
#include "xfa/fxfa/parser/cxfa_psmap.h"
#include "xfa/fxfa/parser/cxfa_query.h"
#include "xfa/fxfa/parser/cxfa_radial.h"
#include "xfa/fxfa/parser/cxfa_range.h"
#include "xfa/fxfa/parser/cxfa_reason.h"
#include "xfa/fxfa/parser/cxfa_reasons.h"
#include "xfa/fxfa/parser/cxfa_record.h"
#include "xfa/fxfa/parser/cxfa_recordset.h"
#include "xfa/fxfa/parser/cxfa_rectangle.h"
#include "xfa/fxfa/parser/cxfa_ref.h"
#include "xfa/fxfa/parser/cxfa_relevant.h"
#include "xfa/fxfa/parser/cxfa_rename.h"
#include "xfa/fxfa/parser/cxfa_renderpolicy.h"
#include "xfa/fxfa/parser/cxfa_rootelement.h"
#include "xfa/fxfa/parser/cxfa_runscripts.h"
#include "xfa/fxfa/parser/cxfa_script.h"
#include "xfa/fxfa/parser/cxfa_scriptmodel.h"
#include "xfa/fxfa/parser/cxfa_select.h"
#include "xfa/fxfa/parser/cxfa_setproperty.h"
#include "xfa/fxfa/parser/cxfa_severity.h"
#include "xfa/fxfa/parser/cxfa_sharptext.h"
#include "xfa/fxfa/parser/cxfa_sharpxhtml.h"
#include "xfa/fxfa/parser/cxfa_sharpxml.h"
#include "xfa/fxfa/parser/cxfa_signature.h"
#include "xfa/fxfa/parser/cxfa_signatureproperties.h"
#include "xfa/fxfa/parser/cxfa_signdata.h"
#include "xfa/fxfa/parser/cxfa_signing.h"
#include "xfa/fxfa/parser/cxfa_silentprint.h"
#include "xfa/fxfa/parser/cxfa_soapaction.h"
#include "xfa/fxfa/parser/cxfa_soapaddress.h"
#include "xfa/fxfa/parser/cxfa_solid.h"
#include "xfa/fxfa/parser/cxfa_source.h"
#include "xfa/fxfa/parser/cxfa_sourceset.h"
#include "xfa/fxfa/parser/cxfa_speak.h"
#include "xfa/fxfa/parser/cxfa_staple.h"
#include "xfa/fxfa/parser/cxfa_startnode.h"
#include "xfa/fxfa/parser/cxfa_startpage.h"
#include "xfa/fxfa/parser/cxfa_stipple.h"
#include "xfa/fxfa/parser/cxfa_stroke.h"
#include "xfa/fxfa/parser/cxfa_subform.h"
#include "xfa/fxfa/parser/cxfa_subformset.h"
#include "xfa/fxfa/parser/cxfa_subjectdn.h"
#include "xfa/fxfa/parser/cxfa_subjectdns.h"
#include "xfa/fxfa/parser/cxfa_submit.h"
#include "xfa/fxfa/parser/cxfa_submitformat.h"
#include "xfa/fxfa/parser/cxfa_submiturl.h"
#include "xfa/fxfa/parser/cxfa_subsetbelow.h"
#include "xfa/fxfa/parser/cxfa_suppressbanner.h"
#include "xfa/fxfa/parser/cxfa_tagged.h"
#include "xfa/fxfa/parser/cxfa_template.h"
#include "xfa/fxfa/parser/cxfa_templatecache.h"
#include "xfa/fxfa/parser/cxfa_text.h"
#include "xfa/fxfa/parser/cxfa_textedit.h"
#include "xfa/fxfa/parser/cxfa_threshold.h"
#include "xfa/fxfa/parser/cxfa_time.h"
#include "xfa/fxfa/parser/cxfa_timepattern.h"
#include "xfa/fxfa/parser/cxfa_timepatterns.h"
#include "xfa/fxfa/parser/cxfa_timestamp.h"
#include "xfa/fxfa/parser/cxfa_to.h"
#include "xfa/fxfa/parser/cxfa_tooltip.h"
#include "xfa/fxfa/parser/cxfa_trace.h"
#include "xfa/fxfa/parser/cxfa_transform.h"
#include "xfa/fxfa/parser/cxfa_traversal.h"
#include "xfa/fxfa/parser/cxfa_traverse.h"
#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
#include "xfa/fxfa/parser/cxfa_type.h"
#include "xfa/fxfa/parser/cxfa_typeface.h"
#include "xfa/fxfa/parser/cxfa_typefaces.h"
#include "xfa/fxfa/parser/cxfa_ui.h"
#include "xfa/fxfa/parser/cxfa_update.h"
#include "xfa/fxfa/parser/cxfa_uri.h"
#include "xfa/fxfa/parser/cxfa_user.h"
#include "xfa/fxfa/parser/cxfa_validate.h"
#include "xfa/fxfa/parser/cxfa_validateapprovalsignatures.h"
#include "xfa/fxfa/parser/cxfa_validationmessaging.h"
#include "xfa/fxfa/parser/cxfa_value.h"
#include "xfa/fxfa/parser/cxfa_variables.h"
#include "xfa/fxfa/parser/cxfa_version.h"
#include "xfa/fxfa/parser/cxfa_versioncontrol.h"
#include "xfa/fxfa/parser/cxfa_viewerpreferences.h"
#include "xfa/fxfa/parser/cxfa_webclient.h"
#include "xfa/fxfa/parser/cxfa_whitespace.h"
#include "xfa/fxfa/parser/cxfa_window.h"
#include "xfa/fxfa/parser/cxfa_wsdladdress.h"
#include "xfa/fxfa/parser/cxfa_wsdlconnection.h"
#include "xfa/fxfa/parser/cxfa_xdc.h"
#include "xfa/fxfa/parser/cxfa_xdp.h"
#include "xfa/fxfa/parser/cxfa_xfa.h"
#include "xfa/fxfa/parser/cxfa_xmlconnection.h"
#include "xfa/fxfa/parser/cxfa_xsdconnection.h"
#include "xfa/fxfa/parser/cxfa_xsl.h"
#include "xfa/fxfa/parser/cxfa_zpl.h"
#include "xfa/fxfa/parser/xfa_basic_data.h"
#include "xfa/fxfa/parser/xfa_utils.h"

class CXFA_FieldLayoutData;
class CXFA_ImageEditData;
class CXFA_ImageLayoutData;
class CXFA_TextEditData;
class CXFA_TextLayoutData;

namespace {

constexpr uint8_t kMaxExecuteRecursion = 2;

constexpr std::array<const uint8_t, 128> kInvBase64 = {{
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255,
    255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
    255, 255, 255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
    25,  255, 255, 255, 255, 255, 255, 26,  27,  28,  29,  30,  31,  32,  33,
    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
    49,  50,  51,  255, 255, 255, 255, 255,
}};

inline uint8_t GetInvBase64(uint8_t x) {
  return (x & 128) == 0 ? kInvBase64[x] : 255;
}

DataVector<uint8_t> XFA_RemoveBase64Whitespace(
    pdfium::span<const uint8_t> spStr) {
  DataVector<uint8_t> result;
  result.reserve(spStr.size());
  for (uint8_t ch : spStr) {
    if (GetInvBase64(ch) != 255 || ch == '=') {
      result.push_back(ch);
    }
  }
  return result;
}

DataVector<uint8_t> XFA_Base64Decode(const ByteString& bsStr) {
  DataVector<uint8_t> result;
  if (bsStr.IsEmpty()) {
    return result;
  }

  DataVector<uint8_t> buffer =
      XFA_RemoveBase64Whitespace(bsStr.unsigned_span());
  result.reserve(3 * (buffer.size() / 4));

  uint32_t dwLimb = 0;
  for (size_t i = 0; i + 3 < buffer.size(); i += 4) {
    if (buffer[i] == '=' || buffer[i + 1] == '=' || buffer[i + 2] == '=' ||
        buffer[i + 3] == '=') {
      if (buffer[i] == '=' || buffer[i + 1] == '=') {
        break;
      }
      if (buffer[i + 2] == '=') {
        dwLimb = ((uint32_t)kInvBase64[buffer[i]] << 6) |
                 ((uint32_t)kInvBase64[buffer[i + 1]]);
        result.push_back((uint8_t)(dwLimb >> 4) & 0xFF);
      } else {
        dwLimb = ((uint32_t)kInvBase64[buffer[i]] << 12) |
                 ((uint32_t)kInvBase64[buffer[i + 1]] << 6) |
                 ((uint32_t)kInvBase64[buffer[i + 2]]);
        result.push_back((uint8_t)(dwLimb >> 10) & 0xFF);
        result.push_back((uint8_t)(dwLimb >> 2) & 0xFF);
      }
    } else {
      dwLimb = ((uint32_t)kInvBase64[buffer[i]] << 18) |
               ((uint32_t)kInvBase64[buffer[i + 1]] << 12) |
               ((uint32_t)kInvBase64[buffer[i + 2]] << 6) |
               ((uint32_t)kInvBase64[buffer[i + 3]]);
      result.push_back((uint8_t)(dwLimb >> 16) & 0xff);
      result.push_back((uint8_t)(dwLimb >> 8) & 0xff);
      result.push_back((uint8_t)(dwLimb) & 0xff);
    }
  }
  return result;
}

FXCODEC_IMAGE_TYPE XFA_GetImageType(const WideString& wsType) {
  WideString wsContentType(wsType);
  if (wsContentType.EqualsASCIINoCase("image/jpg")) {
    return FXCODEC_IMAGE_JPG;
  }

#ifdef PDF_ENABLE_XFA_BMP
  if (wsContentType.EqualsASCIINoCase("image/bmp")) {
    return FXCODEC_IMAGE_BMP;
  }
#endif  // PDF_ENABLE_XFA_BMP

#ifdef PDF_ENABLE_XFA_GIF
  if (wsContentType.EqualsASCIINoCase("image/gif")) {
    return FXCODEC_IMAGE_GIF;
  }
#endif  // PDF_ENABLE_XFA_GIF

#ifdef PDF_ENABLE_XFA_PNG
  if (wsContentType.EqualsASCIINoCase("image/png")) {
    return FXCODEC_IMAGE_PNG;
  }
#endif  // PDF_ENABLE_XFA_PNG

#ifdef PDF_ENABLE_XFA_TIFF
  if (wsContentType.EqualsASCII("image/tif")) {
    return FXCODEC_IMAGE_TIFF;
  }
#endif  // PDF_ENABLE_XFA_TIFF

  return FXCODEC_IMAGE_UNKNOWN;
}

RetainPtr<CFX_DIBitmap> XFA_LoadImageData(CXFA_FFDoc* doc,
                                          CXFA_Image* pImage,
                                          bool& bNameImage,
                                          int32_t& iImageXDpi,
                                          int32_t& iImageYDpi) {
  WideString wsHref = pImage->GetHref();
  WideString wsImage = pImage->GetContent();
  if (wsHref.IsEmpty() && wsImage.IsEmpty()) {
    return nullptr;
  }

  FXCODEC_IMAGE_TYPE type = XFA_GetImageType(pImage->GetContentType());

  RetainPtr<IFX_SeekableReadStream> pImageFileRead;
  if (wsImage.GetLength() > 0) {
    XFA_AttributeValue iEncoding = pImage->GetTransferEncoding();
    if (iEncoding == XFA_AttributeValue::Base64) {
      DataVector<uint8_t> buffer = XFA_Base64Decode(wsImage.ToUTF8());
      if (!buffer.empty()) {
        pImageFileRead =
            pdfium::MakeRetain<CFX_ReadOnlyDataVectorStream>(std::move(buffer));
      }
    } else {
      pImageFileRead =
          pdfium::MakeRetain<CFX_ReadOnlyByteStringStream>(wsImage.ToDefANSI());
    }
  } else {
    WideString wsURL = wsHref;
    if (!(wsURL.First(7).EqualsASCII("http://") ||
          wsURL.First(6).EqualsASCII("ftp://"))) {
      RetainPtr<CFX_DIBitmap> pBitmap =
          doc->GetPDFNamedImage(wsURL.AsStringView(), iImageXDpi, iImageYDpi);
      if (pBitmap) {
        bNameImage = true;
        return pBitmap;
      }
    }
    pImageFileRead = doc->OpenLinkedFile(wsURL);
  }
  if (!pImageFileRead) {
    return nullptr;
  }

  bNameImage = false;
  return XFA_LoadImageFromBuffer(std::move(pImageFileRead), type, iImageXDpi,
                                 iImageYDpi);
}

bool SplitDateTime(const WideString& wsDateTime,
                   WideString& wsDate,
                   WideString& wsTime) {
  wsDate.clear();
  wsTime.clear();
  if (wsDateTime.IsEmpty()) {
    return false;
  }

  auto nSplitIndex = wsDateTime.Find('T');
  if (!nSplitIndex.has_value()) {
    nSplitIndex = wsDateTime.Find(' ');
  }
  if (!nSplitIndex.has_value()) {
    return false;
  }

  wsDate = wsDateTime.First(nSplitIndex.value());
  if (!wsDate.IsEmpty()) {
    if (!std::any_of(wsDate.begin(), wsDate.end(),
                     [](wchar_t c) { return FXSYS_IsDecimalDigit(c); })) {
      return false;
    }
  }
  wsTime = wsDateTime.Substr(nSplitIndex.value() + 1);
  if (!wsTime.IsEmpty()) {
    if (!std::any_of(wsTime.begin(), wsTime.end(),
                     [](wchar_t c) { return FXSYS_IsDecimalDigit(c); })) {
      return false;
    }
  }
  return true;
}

// Stack allocated. Using containers of members would be correct here
// if advanced GC worked with STL.
using NodeSet = std::set<cppgc::Member<CXFA_Node>>;
using NodeSetPair = std::pair<NodeSet, NodeSet>;
using NodeSetPairMap = std::map<uint32_t, NodeSetPair>;
using NodeSetPairMapMap = std::map<CXFA_Node*, NodeSetPairMap>;
using NodeVector = std::vector<cppgc::Member<CXFA_Node>>;

NodeVector NodesSortedByDocumentIdx(const NodeSet& rgNodeSet) {
  if (rgNodeSet.empty()) {
    return NodeVector();
  }

  NodeVector rgNodeArray;
  CXFA_Node* pCommonParent = (*rgNodeSet.begin())->GetParent();
  for (CXFA_Node* pNode = pCommonParent->GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pdfium::Contains(rgNodeSet, pNode)) {
      rgNodeArray.push_back(pNode);
    }
  }
  return rgNodeArray;
}

NodeSetPair* NodeSetPairForNode(CXFA_Node* pNode, NodeSetPairMapMap* map) {
  CXFA_Node* pParentNode = pNode->GetParent();
  uint32_t dwNameHash = pNode->GetNameHash();
  if (!pParentNode || !dwNameHash) {
    return nullptr;
  }

  return &((*map)[pParentNode][dwNameHash]);
}

void ReorderDataNodes(const NodeSet& sSet1,
                      const NodeSet& sSet2,
                      bool bInsertBefore) {
  NodeSetPairMapMap rgMap;
  for (CXFA_Node* pNode : sSet1) {
    NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
    if (pNodeSetPair) {
      pNodeSetPair->first.insert(pNode);
    }
  }
  for (CXFA_Node* pNode : sSet2) {
    NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
    if (pNodeSetPair) {
      if (pdfium::Contains(pNodeSetPair->first, pNode)) {
        pNodeSetPair->first.erase(pNode);
      } else {
        pNodeSetPair->second.insert(pNode);
      }
    }
  }
  for (auto& iter1 : rgMap) {
    NodeSetPairMap* pNodeSetPairMap = &iter1.second;
    for (auto& iter2 : *pNodeSetPairMap) {
      NodeSetPair* pNodeSetPair = &iter2.second;
      if (!pNodeSetPair->first.empty() && !pNodeSetPair->second.empty()) {
        NodeVector rgNodeArray1 = NodesSortedByDocumentIdx(pNodeSetPair->first);
        NodeVector rgNodeArray2 =
            NodesSortedByDocumentIdx(pNodeSetPair->second);
        CXFA_Node* pParentNode = nullptr;
        CXFA_Node* pBeforeNode = nullptr;
        if (bInsertBefore) {
          pBeforeNode = rgNodeArray2.front();
          pParentNode = pBeforeNode->GetParent();
        } else {
          CXFA_Node* pLastNode = rgNodeArray2.back();
          pParentNode = pLastNode->GetParent();
          pBeforeNode = pLastNode->GetNextSibling();
        }
        for (auto& pCurNode : rgNodeArray1) {
          pParentNode->RemoveChildAndNotify(pCurNode, true);
          pParentNode->InsertChildAndNotify(pCurNode, pBeforeNode);
        }
      }
    }
    pNodeSetPairMap->clear();
  }
}

float GetEdgeThickness(const std::vector<CXFA_Stroke*>& strokes,
                       bool b3DStyle,
                       int32_t nIndex) {
  float fThickness = 0.0f;
  CXFA_Stroke* stroke = strokes[nIndex * 2 + 1];
  if (stroke->IsVisible()) {
    if (nIndex == 0) {
      fThickness += 2.5f;
    }

    fThickness += stroke->GetThickness() * (b3DStyle ? 4 : 2);
  }
  return fThickness;
}

WideString FormatNumStr(const WideString& wsValue, LocaleIface* pLocale) {
  if (wsValue.IsEmpty()) {
    return WideString();
  }

  WideString wsSrcNum = wsValue;
  WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
  bool bNeg = false;
  if (wsSrcNum.Front() == '-') {
    bNeg = true;
    wsSrcNum.Delete(0, 1);
  }

  size_t dot_index = wsSrcNum.Find('.').value_or(wsSrcNum.GetLength());
  if (dot_index == 0) {
    return WideString();
  }

  size_t nPos = dot_index % 3;
  WideString wsOutput;
  for (size_t i = 0; i < dot_index; i++) {
    if (i % 3 == nPos && i != 0) {
      wsOutput += wsGroupSymbol;
    }

    wsOutput += wsSrcNum[i];
  }
  if (dot_index < wsSrcNum.GetLength()) {
    wsOutput += pLocale->GetDecimalSymbol();
    wsOutput += wsSrcNum.Substr(dot_index + 1);
  }
  if (bNeg) {
    return pLocale->GetMinusSymbol() + wsOutput;
  }

  return wsOutput;
}

CXFA_Node* FindFirstSiblingNamedInList(CXFA_Node* parent,
                                       uint32_t dwNameHash,
                                       Mask<XFA_NodeFilter> dwFilter);
CXFA_Node* FindFirstSiblingOfClassInList(CXFA_Node* parent,
                                         XFA_Element element,
                                         Mask<XFA_NodeFilter> dwFilter);

CXFA_Node* FindFirstSiblingNamed(CXFA_Node* parent, uint32_t dwNameHash) {
  CXFA_Node* result = FindFirstSiblingNamedInList(parent, dwNameHash,
                                                  XFA_NodeFilter::kProperties);
  if (result) {
    return result;
  }

  return FindFirstSiblingNamedInList(parent, dwNameHash,
                                     XFA_NodeFilter::kChildren);
}

CXFA_Node* FindFirstSiblingNamedInList(CXFA_Node* parent,
                                       uint32_t dwNameHash,
                                       Mask<XFA_NodeFilter> dwFilter) {
  for (CXFA_Node* child : parent->GetNodeListWithFilter(dwFilter)) {
    if (child->GetNameHash() == dwNameHash) {
      return child;
    }

    CXFA_Node* result = FindFirstSiblingNamed(child, dwNameHash);
    if (result) {
      return result;
    }
  }
  return nullptr;
}

CXFA_Node* FindFirstSiblingOfClass(CXFA_Node* parent, XFA_Element element) {
  CXFA_Node* result = FindFirstSiblingOfClassInList(
      parent, element, XFA_NodeFilter::kProperties);
  if (result) {
    return result;
  }

  return FindFirstSiblingOfClassInList(parent, element,
                                       XFA_NodeFilter::kChildren);
}

CXFA_Node* FindFirstSiblingOfClassInList(CXFA_Node* parent,
                                         XFA_Element element,
                                         Mask<XFA_NodeFilter> dwFilter) {
  for (CXFA_Node* child : parent->GetNodeListWithFilter(dwFilter)) {
    if (child->GetElementType() == element) {
      return child;
    }

    CXFA_Node* result = FindFirstSiblingOfClass(child, element);
    if (result) {
      return result;
    }
  }
  return nullptr;
}

WideString GetNameExpressionSinglePath(CXFA_Node* pNode) {
  const bool bIsProperty = pNode->IsProperty();
  const bool bIsClassIndex =
      pNode->IsUnnamed() ||
      (bIsProperty && pNode->GetElementType() != XFA_Element::PageSet);
  const wchar_t* pszFormat;
  WideString ws;
  if (bIsClassIndex) {
    pszFormat = L"#%ls[%zu]";
    ws = WideString::FromASCII(pNode->GetClassName());
  } else {
    pszFormat = L"%ls[%zu]";
    ws = pNode->JSObject()->GetCData(XFA_Attribute::Name);
    ws.Replace(L".", L"\\.");
  }

  return WideString::Format(pszFormat, ws.c_str(),
                            pNode->GetIndex(bIsProperty, bIsClassIndex));
}

void TraverseSiblings(CXFA_Node* parent,
                      uint32_t dwNameHash,
                      std::vector<CXFA_Node*>* pSiblings,
                      bool bIsClassName) {
  DCHECK(parent);
  DCHECK(pSiblings);
  for (CXFA_Node* child :
       parent->GetNodeListWithFilter(XFA_NodeFilter::kChildren)) {
    if (child->GetElementType() == XFA_Element::Variables) {
      continue;
    }

    if (bIsClassName) {
      if (child->GetClassHashCode() == dwNameHash) {
        pSiblings->push_back(child);
      }
    } else {
      if (child->GetNameHash() == dwNameHash) {
        pSiblings->push_back(child);
      }
    }
    if (child->IsTransparent() &&
        child->GetElementType() != XFA_Element::PageSet) {
      TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName);
    }
  }
}

void TraversePropertiesOrSiblings(CXFA_Node* parent,
                                  uint32_t dwNameHash,
                                  std::vector<CXFA_Node*>* pSiblings,
                                  bool bIsClassName) {
  DCHECK(parent);
  DCHECK(pSiblings);
  for (CXFA_Node* child :
       parent->GetNodeListWithFilter(XFA_NodeFilter::kProperties)) {
    if (bIsClassName) {
      if (child->GetClassHashCode() == dwNameHash) {
        pSiblings->push_back(child);
      }
    } else {
      if (child->GetNameHash() == dwNameHash) {
        if (child->GetElementType() != XFA_Element::PageSet &&
            child->GetElementType() != XFA_Element::Extras &&
            child->GetElementType() != XFA_Element::Items) {
          pSiblings->push_back(child);
        }
      }
    }
    if (child->IsUnnamed() && child->GetElementType() == XFA_Element::PageSet) {
      TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName);
    }
  }
  if (pSiblings->empty()) {
    TraverseSiblings(parent, dwNameHash, pSiblings, bIsClassName);
  }
}

}  // namespace

class CXFA_WidgetLayoutData
    : public cppgc::GarbageCollected<CXFA_WidgetLayoutData> {
 public:
  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
  virtual ~CXFA_WidgetLayoutData() = default;

  virtual void Trace(cppgc::Visitor* visitor) const {}

  virtual CXFA_FieldLayoutData* AsFieldLayoutData() { return nullptr; }
  virtual CXFA_ImageLayoutData* AsImageLayoutData() { return nullptr; }
  virtual CXFA_TextLayoutData* AsTextLayoutData() { return nullptr; }

  float GetWidgetHeight() const { return widget_height_; }
  void SetWidgetHeight(float height) { widget_height_ = height; }

 protected:
  CXFA_WidgetLayoutData() = default;

 private:
  float widget_height_ = -1.0f;
};

class CXFA_TextLayoutData final : public CXFA_WidgetLayoutData {
 public:
  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
  ~CXFA_TextLayoutData() override = default;

  void Trace(cppgc::Visitor* visitor) const override {
    CXFA_WidgetLayoutData::Trace(visitor);
    visitor->Trace(text_layout_);
    visitor->Trace(text_provider_);
  }

  CXFA_TextLayoutData* AsTextLayoutData() override { return this; }

  CXFA_TextLayout* GetTextLayout() const { return text_layout_; }
  CXFA_TextProvider* GetTextProvider() const { return text_provider_; }

  void LoadText(CXFA_FFDoc* doc, CXFA_Node* pNode) {
    if (text_layout_) {
      return;
    }

    text_provider_ = cppgc::MakeGarbageCollected<CXFA_TextProvider>(
        doc->GetHeap()->GetAllocationHandle(), pNode,
        CXFA_TextProvider::Type::kText);
    text_layout_ = cppgc::MakeGarbageCollected<CXFA_TextLayout>(
        doc->GetHeap()->GetAllocationHandle(), doc, text_provider_);
  }

 private:
  CXFA_TextLayoutData() = default;

  cppgc::Member<CXFA_TextLayout> text_layout_;
  cppgc::Member<CXFA_TextProvider> text_provider_;
};

class CXFA_ImageLayoutData final : public CXFA_WidgetLayoutData {
 public:
  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
  ~CXFA_ImageLayoutData() override = default;

  CXFA_ImageLayoutData* AsImageLayoutData() override { return this; }

  bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) {
    if (dibitmap_) {
      return true;
    }

    CXFA_Value* value = pNode->GetFormValueIfExists();
    if (!value) {
      return false;
    }

    CXFA_Image* image = value->GetImageIfExists();
    if (!image) {
      return false;
    }

    pNode->SetLayoutImage(
        XFA_LoadImageData(doc, image, named_image_, image_xdpi_, image_ydpi_));
    return !!dibitmap_;
  }

  CFX_Size GetDpi() const { return CFX_Size(image_xdpi_, image_ydpi_); }
  RetainPtr<CFX_DIBitmap> GetBitmap() { return dibitmap_; }
  void SetBitmap(RetainPtr<CFX_DIBitmap> pBitmap) {
    dibitmap_ = std::move(pBitmap);
  }

 private:
  CXFA_ImageLayoutData() = default;

  bool named_image_ = false;
  int32_t image_xdpi_ = 0;
  int32_t image_ydpi_ = 0;
  RetainPtr<CFX_DIBitmap> dibitmap_;
};

class CXFA_FieldLayoutData : public CXFA_WidgetLayoutData {
 public:
  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
  ~CXFA_FieldLayoutData() override = default;

  void Trace(cppgc::Visitor* visitor) const override {
    CXFA_WidgetLayoutData::Trace(visitor);
    visitor->Trace(cap_text_layout_);
    visitor->Trace(cap_text_provider_);
  }
  CXFA_FieldLayoutData* AsFieldLayoutData() override { return this; }

  virtual CXFA_ImageEditData* AsImageEditData() { return nullptr; }
  virtual CXFA_TextEditData* AsTextEditData() { return nullptr; }

  bool LoadCaption(CXFA_FFDoc* doc, CXFA_Node* pNode) {
    if (cap_text_layout_) {
      return true;
    }
    CXFA_Caption* caption = pNode->GetCaptionIfExists();
    if (!caption || caption->IsHidden()) {
      return false;
    }

    cap_text_provider_ = cppgc::MakeGarbageCollected<CXFA_TextProvider>(
        doc->GetHeap()->GetAllocationHandle(), pNode,
        CXFA_TextProvider::Type::kCaption);
    cap_text_layout_ = cppgc::MakeGarbageCollected<CXFA_TextLayout>(
        doc->GetHeap()->GetAllocationHandle(), doc, cap_text_provider_);
    return true;
  }

  cppgc::Member<CXFA_TextLayout> cap_text_layout_;
  cppgc::Member<CXFA_TextProvider> cap_text_provider_;
  std::unique_ptr<CFDE_TextOut> text_out_;
  std::vector<float> field_split_array_;

 protected:
  CXFA_FieldLayoutData() = default;
};

class CXFA_TextEditData final : public CXFA_FieldLayoutData {
 public:
  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
  ~CXFA_TextEditData() override = default;

  CXFA_TextEditData* AsTextEditData() override { return this; }

 protected:
  CXFA_TextEditData() = default;
};

class CXFA_ImageEditData final : public CXFA_FieldLayoutData {
 public:
  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
  ~CXFA_ImageEditData() override = default;

  CXFA_ImageEditData* AsImageEditData() override { return this; }

  bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) {
    if (dibitmap_) {
      return true;
    }

    CXFA_Value* value = pNode->GetFormValueIfExists();
    if (!value) {
      return false;
    }

    CXFA_Image* image = value->GetImageIfExists();
    if (!image) {
      return false;
    }

    pNode->SetEditImage(
        XFA_LoadImageData(doc, image, named_image_, image_xdpi_, image_ydpi_));
    return !!dibitmap_;
  }

  CFX_Size GetDpi() const { return CFX_Size(image_xdpi_, image_ydpi_); }
  RetainPtr<CFX_DIBitmap> GetBitmap() { return dibitmap_; }
  void SetBitmap(RetainPtr<CFX_DIBitmap> pBitmap) {
    dibitmap_ = std::move(pBitmap);
  }

 private:
  CXFA_ImageEditData() = default;

  bool named_image_ = false;
  int32_t image_xdpi_ = 0;
  int32_t image_ydpi_ = 0;
  RetainPtr<CFX_DIBitmap> dibitmap_;
};

CXFA_Node::CXFA_Node(CXFA_Document* doc,
                     XFA_PacketType ePacket,
                     Mask<XFA_XDPPACKET> validPackets,
                     XFA_ObjectType oType,
                     XFA_Element eType,
                     pdfium::span<const PropertyData> properties,
                     pdfium::span<const AttributeData> attributes,
                     CJX_Object* js_object)
    : CXFA_Object(doc, oType, eType, js_object),
      properties_(properties),
      attributes_(attributes),
      valid_packets_(validPackets),
      packet_(ePacket) {
  DCHECK(document_);
}

CXFA_Node::~CXFA_Node() = default;

void CXFA_Node::Trace(cppgc::Visitor* visitor) const {
  CXFA_Object::Trace(visitor);
  GCedTreeNodeMixin<CXFA_Node>::Trace(visitor);
  visitor->Trace(aux_node_);
  visitor->Trace(layout_data_);
  visitor->Trace(ui_);
  ContainerTrace(visitor, binding_nodes_);
}

CXFA_Node* CXFA_Node::Clone(bool bRecursive) {
  CXFA_Node* pClone = document_->CreateNode(packet_, element_type_);
  if (!pClone) {
    return nullptr;
  }

  JSObject()->MergeAllData(pClone);
  pClone->UpdateNameHash();
  if (IsNeedSavingXMLNode()) {
    CFX_XMLNode* pCloneXML;
    if (IsAttributeInXML()) {
      WideString wsName = JSObject()
                              ->TryAttribute(XFA_Attribute::Name, false)
                              .value_or(WideString());
      auto* pCloneXMLElement =
          GetXMLDocument()->CreateNode<CFX_XMLElement>(wsName);

      WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
      if (!wsValue.IsEmpty()) {
        auto* text = GetXMLDocument()->CreateNode<CFX_XMLText>(wsValue);
        pCloneXMLElement->AppendLastChild(text);
      }

      pCloneXML = pCloneXMLElement;
      pClone->JSObject()->SetEnum(XFA_Attribute::Contains,
                                  XFA_AttributeValue::Unknown, false);
    } else {
      pCloneXML = xml_node_->Clone(GetXMLDocument());
    }
    pClone->SetXMLMappingNode(pCloneXML);
  }
  if (bRecursive) {
    for (CXFA_Node* pChild = GetFirstChild(); pChild;
         pChild = pChild->GetNextSibling()) {
      pClone->InsertChildAndNotify(pChild->Clone(bRecursive), nullptr);
    }
  }
  pClone->SetInitializedFlagAndNotify();
  pClone->SetBindingNode(nullptr);
  return pClone;
}

CXFA_Node* CXFA_Node::GetNextContainerSibling() const {
  for (auto* pNode = GetNextSibling(); pNode; pNode = pNode->GetNextSibling()) {
    if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode) {
      return pNode;
    }
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetPrevContainerSibling() const {
  for (auto* pNode = GetPrevSibling(); pNode; pNode = pNode->GetPrevSibling()) {
    if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode) {
      return pNode;
    }
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetFirstContainerChild() const {
  for (auto* pNode = GetFirstChild(); pNode; pNode = pNode->GetNextSibling()) {
    if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode) {
      return pNode;
    }
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetContainerParent() const {
  for (auto* pNode = GetParent(); pNode; pNode = pNode->GetParent()) {
    if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode) {
      return pNode;
    }
  }
  return nullptr;
}

bool CXFA_Node::IsValidInPacket(XFA_PacketType packet) const {
  uint32_t bitflag = 1 << static_cast<uint8_t>(packet);
  return !!(valid_packets_ & static_cast<XFA_XDPPACKET>(bitflag));
}

const CXFA_Node::PropertyData* CXFA_Node::GetPropertyData(
    XFA_Element property) const {
  DCHECK(property != XFA_Element::Unknown);
  for (const auto& prop : properties_) {
    if (prop.property == property) {
      return &prop;
    }
  }
  return nullptr;
}

bool CXFA_Node::HasProperty(XFA_Element property) const {
  return !!GetPropertyData(property);
}

bool CXFA_Node::HasPropertyFlag(XFA_Element property,
                                XFA_PropertyFlag flag) const {
  const PropertyData* data = GetPropertyData(property);
  return data && !!(data->flags & flag);
}

uint8_t CXFA_Node::PropertyOccurrenceCount(XFA_Element property) const {
  const PropertyData* data = GetPropertyData(property);
  return data ? data->occurrence_count : 0;
}

std::pair<CXFA_Node*, int32_t> CXFA_Node::GetProperty(
    int32_t index,
    XFA_Element eProperty) const {
  if (index < 0 || index >= PropertyOccurrenceCount(eProperty)) {
    return {nullptr, 0};
  }

  int32_t iCount = 0;
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() == eProperty) {
      iCount++;
      if (iCount > index) {
        return {pNode, iCount};
      }
    }
  }
  return {nullptr, iCount};
}

CXFA_Node* CXFA_Node::GetOrCreateProperty(int32_t index,
                                          XFA_Element eProperty) {
  if (index < 0 || index >= PropertyOccurrenceCount(eProperty)) {
    return nullptr;
  }

  auto [node, iCount] = GetProperty(index, eProperty);
  if (node) {
    return node;
  }

  if (HasPropertyFlag(eProperty, XFA_PropertyFlag::kOneOf)) {
    for (CXFA_Node* pNode = GetFirstChild(); pNode;
         pNode = pNode->GetNextSibling()) {
      if (HasPropertyFlag(pNode->GetElementType(), XFA_PropertyFlag::kOneOf)) {
        return nullptr;
      }
    }
  }

  CXFA_Node* pNewNode = nullptr;
  for (; iCount <= index; ++iCount) {
    pNewNode = GetDocument()->CreateNode(GetPacketType(), eProperty);
    if (!pNewNode) {
      return nullptr;
    }

    InsertChildAndNotify(pNewNode, nullptr);
    pNewNode->SetInitializedFlagAndNotify();
  }
  return pNewNode;
}

std::optional<XFA_Element> CXFA_Node::GetFirstPropertyWithFlag(
    XFA_PropertyFlag flag) const {
  for (const auto& prop : properties_) {
    if (prop.flags & flag) {
      return prop.property;
    }
  }
  return std::nullopt;
}

const CXFA_Node::AttributeData* CXFA_Node::GetAttributeData(
    XFA_Attribute attr) const {
  DCHECK(attr != XFA_Attribute::Unknown);
  for (const auto& cur_attr : attributes_) {
    if (cur_attr.attribute == attr) {
      return &cur_attr;
    }
  }
  return nullptr;
}

bool CXFA_Node::HasAttribute(XFA_Attribute attr) const {
  return !!GetAttributeData(attr);
}

XFA_Attribute CXFA_Node::GetAttribute(size_t i) const {
  return i < attributes_.size() ? attributes_[i].attribute
                                : XFA_Attribute::Unknown;
}

XFA_AttributeType CXFA_Node::GetAttributeType(XFA_Attribute type) const {
  const AttributeData* data = GetAttributeData(type);
  return data ? data->type : XFA_AttributeType::CData;
}

std::vector<CXFA_Node*> CXFA_Node::GetNodeListForType(XFA_Element eTypeFilter) {
  std::vector<CXFA_Node*> nodes;
  for (CXFA_Node* pChild = GetFirstChild(); pChild;
       pChild = pChild->GetNextSibling()) {
    if (pChild->GetElementType() == eTypeFilter) {
      nodes.push_back(pChild);
    }
  }
  return nodes;
}

std::vector<CXFA_Node*> CXFA_Node::GetNodeListWithFilter(
    Mask<XFA_NodeFilter> dwFilter) {
  if (!dwFilter) {
    return std::vector<CXFA_Node*>();
  }

  const bool bFilterChildren = !!(dwFilter & XFA_NodeFilter::kChildren);
  const bool bFilterProperties = !!(dwFilter & XFA_NodeFilter::kProperties);
  const bool bFilterOneOfProperties =
      !!(dwFilter & XFA_NodeFilter::kOneOfProperty);

  std::vector<CXFA_Node*> nodes;
  if (bFilterChildren && bFilterProperties && !bFilterOneOfProperties) {
    for (CXFA_Node* pChild = GetFirstChild(); pChild;
         pChild = pChild->GetNextSibling()) {
      nodes.push_back(pChild);
    }
    return nodes;
  }

  for (CXFA_Node* pChild = GetFirstChild(); pChild;
       pChild = pChild->GetNextSibling()) {
    if (HasProperty(pChild->GetElementType())) {
      if (bFilterProperties) {
        nodes.push_back(pChild);
      } else if (bFilterOneOfProperties &&
                 HasPropertyFlag(pChild->GetElementType(),
                                 XFA_PropertyFlag::kOneOf)) {
        nodes.push_back(pChild);
      } else if (bFilterChildren &&
                 (pChild->GetElementType() == XFA_Element::Variables ||
                  pChild->GetElementType() == XFA_Element::PageSet)) {
        nodes.push_back(pChild);
      }
    } else if (bFilterChildren) {
      nodes.push_back(pChild);
    }
  }

  if (!bFilterOneOfProperties || !nodes.empty()) {
    return nodes;
  }

  std::optional<XFA_Element> property =
      GetFirstPropertyWithFlag(XFA_PropertyFlag::kDefaultOneOf);
  if (!property.has_value()) {
    return nodes;
  }

  CXFA_Node* pNewNode =
      document_->CreateNode(GetPacketType(), property.value());
  if (pNewNode) {
    InsertChildAndNotify(pNewNode, nullptr);
    pNewNode->SetInitializedFlagAndNotify();
    nodes.push_back(pNewNode);
  }
  return nodes;
}

CXFA_Node* CXFA_Node::CreateSamePacketNode(XFA_Element eType) {
  CXFA_Node* pNode = document_->CreateNode(packet_, eType);
  if (!pNode) {
    return nullptr;
  }

  pNode->SetInitializedFlagAndNotify();
  return pNode;
}

CXFA_Node* CXFA_Node::CloneTemplateToForm(bool bRecursive) {
  CHECK_EQ(packet_, XFA_PacketType::Template);
  CXFA_Node* pClone =
      document_->CreateNode(XFA_PacketType::Form, element_type_);
  if (!pClone) {
    return nullptr;
  }

  pClone->SetTemplateNode(this);
  pClone->UpdateNameHash();
  pClone->SetXMLMappingNode(GetXMLMappingNode());
  if (bRecursive) {
    for (CXFA_Node* pChild = GetFirstChild(); pChild;
         pChild = pChild->GetNextSibling()) {
      if (pChild->GetPacketType() != XFA_PacketType::Template) {
        continue;
      }
      pClone->InsertChildAndNotify(pChild->CloneTemplateToForm(bRecursive),
                                   nullptr);
    }
  }
  pClone->SetInitializedFlagAndNotify();
  return pClone;
}

CXFA_Node* CXFA_Node::GetTemplateNodeIfExists() const {
  return aux_node_;
}

void CXFA_Node::SetTemplateNode(CXFA_Node* pTemplateNode) {
  aux_node_ = pTemplateNode;
}

CXFA_Node* CXFA_Node::GetBindData() {
  DCHECK_EQ(GetPacketType(), XFA_PacketType::Form);
  return GetBindingNode();
}

std::vector<CXFA_Node*> CXFA_Node::GetBindItemsCopy() const {
  return std::vector<CXFA_Node*>(binding_nodes_.begin(), binding_nodes_.end());
}

void CXFA_Node::AddBindItem(CXFA_Node* pFormNode) {
  DCHECK(pFormNode);

  if (BindsFormItems()) {
    if (!pdfium::Contains(binding_nodes_, pFormNode)) {
      binding_nodes_.emplace_back(pFormNode);
    }
    return;
  }

  CXFA_Node* pOldFormItem = GetBindingNode();
  if (!pOldFormItem) {
    SetBindingNode(pFormNode);
    return;
  }
  if (pOldFormItem == pFormNode) {
    return;
  }

  binding_nodes_.clear();
  binding_nodes_.push_back(pOldFormItem);
  binding_nodes_.push_back(pFormNode);
  node_flags_ |= XFA_NodeFlag::kBindFormItems;
}

bool CXFA_Node::RemoveBindItem(CXFA_Node* pFormNode) {
  if (BindsFormItems()) {
    auto it = std::ranges::find(binding_nodes_, pFormNode);
    if (it != binding_nodes_.end()) {
      binding_nodes_.erase(it);
    }

    if (binding_nodes_.size() == 1) {
      node_flags_.Clear(XFA_NodeFlag::kBindFormItems);
      return true;
    }
    return !binding_nodes_.empty();
  }

  CXFA_Node* pOldFormItem = GetBindingNode();
  if (pOldFormItem != pFormNode) {
    return !!pOldFormItem;
  }

  SetBindingNode(nullptr);
  return false;
}

bool CXFA_Node::HasBindItem() const {
  return GetPacketType() == XFA_PacketType::Datasets && GetBindingNode();
}

CXFA_Node* CXFA_Node::GetContainerNode() {
  if (GetPacketType() != XFA_PacketType::Form) {
    return nullptr;
  }
  XFA_Element eType = GetElementType();
  if (eType == XFA_Element::ExclGroup) {
    return nullptr;
  }
  CXFA_Node* pParentNode = GetParent();
  if (pParentNode && pParentNode->GetElementType() == XFA_Element::ExclGroup) {
    return nullptr;
  }

  if (eType == XFA_Element::Field) {
    if (IsChoiceListMultiSelect()) {
      return nullptr;
    }

    WideString wsPicture = GetPictureContent(XFA_ValuePicture::kDataBind);
    if (!wsPicture.IsEmpty()) {
      return this;
    }

    CXFA_Node* pDataNode = GetBindData();
    if (!pDataNode) {
      return nullptr;
    }

    CXFA_Node* pFieldNode = nullptr;
    for (auto* pFormNode : pDataNode->GetBindItemsCopy()) {
      if (!pFormNode || pFormNode->HasRemovedChildren()) {
        continue;
      }
      pFieldNode = pFormNode->IsWidgetReady() ? pFormNode : nullptr;
      if (pFieldNode) {
        wsPicture = pFieldNode->GetPictureContent(XFA_ValuePicture::kDataBind);
      }
      if (!wsPicture.IsEmpty()) {
        break;
      }

      pFieldNode = nullptr;
    }
    return pFieldNode;
  }

  CXFA_Node* pGrandNode = pParentNode ? pParentNode->GetParent() : nullptr;
  CXFA_Node* pValueNode =
      (pParentNode && pParentNode->GetElementType() == XFA_Element::Value)
          ? pParentNode
          : nullptr;
  if (!pValueNode) {
    pValueNode =
        (pGrandNode && pGrandNode->GetElementType() == XFA_Element::Value)
            ? pGrandNode
            : nullptr;
  }
  CXFA_Node* pParentOfValueNode =
      pValueNode ? pValueNode->GetParent() : nullptr;
  return pParentOfValueNode ? pParentOfValueNode->GetContainerNode() : nullptr;
}

GCedLocaleIface* CXFA_Node::GetLocale() {
  std::optional<WideString> localeName = GetLocaleName();
  if (!localeName.has_value()) {
    return nullptr;
  }
  if (localeName.value().EqualsASCII("ambient")) {
    return GetDocument()->GetLocaleMgr()->GetDefLocale();
  }
  return GetDocument()->GetLocaleMgr()->GetLocaleByName(localeName.value());
}

std::optional<WideString> CXFA_Node::GetLocaleName() {
  CXFA_Node* pForm = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form));
  if (!pForm) {
    return std::nullopt;
  }

  CXFA_Subform* pTopSubform =
      pForm->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
  if (!pTopSubform) {
    return std::nullopt;
  }

  std::optional<WideString> localeName;
  CXFA_Node* pLocaleNode = this;
  do {
    localeName =
        pLocaleNode->JSObject()->TryCData(XFA_Attribute::Locale, false);
    if (localeName.has_value()) {
      return localeName;
    }

    pLocaleNode = pLocaleNode->GetParent();
  } while (pLocaleNode && pLocaleNode != pTopSubform);

  CXFA_Node* pConfig = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Config));
  localeName = GetDocument()->GetLocaleMgr()->GetConfigLocaleName(pConfig);
  if (localeName.has_value()) {
    return localeName;
  }

  if (pTopSubform) {
    localeName =
        pTopSubform->JSObject()->TryCData(XFA_Attribute::Locale, false);
    if (localeName.has_value()) {
      return localeName;
    }
  }

  LocaleIface* pLocale = GetDocument()->GetLocaleMgr()->GetDefLocale();
  if (!pLocale) {
    return std::nullopt;
  }

  return pLocale->GetName();
}

XFA_AttributeValue CXFA_Node::GetIntact() {
  CXFA_Keep* pKeep = GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
  auto layout = JSObject()->TryEnum(XFA_Attribute::Layout, true);
  XFA_AttributeValue eLayoutType =
      layout.value_or(XFA_AttributeValue::Position);
  if (pKeep) {
    std::optional<XFA_AttributeValue> intact =
        GetIntactFromKeep(pKeep, eLayoutType);
    if (intact.has_value()) {
      return intact.value();
    }
  }

  switch (GetElementType()) {
    case XFA_Element::Subform:
      switch (eLayoutType) {
        case XFA_AttributeValue::Position:
        case XFA_AttributeValue::Row:
          return XFA_AttributeValue::ContentArea;
        default:
          return XFA_AttributeValue::None;
      }
    case XFA_Element::Field: {
      CXFA_Node* parent = GetParent();
      if (!parent || parent->GetElementType() == XFA_Element::PageArea) {
        return XFA_AttributeValue::ContentArea;
      }
      if (parent->GetIntact() != XFA_AttributeValue::None) {
        return XFA_AttributeValue::ContentArea;
      }

      auto value = parent->JSObject()->TryEnum(XFA_Attribute::Layout, true);
      XFA_AttributeValue eParLayout =
          value.value_or(XFA_AttributeValue::Position);
      if (eParLayout == XFA_AttributeValue::Position ||
          eParLayout == XFA_AttributeValue::Row ||
          eParLayout == XFA_AttributeValue::Table) {
        return XFA_AttributeValue::None;
      }

      XFA_VERSION version = document_->GetCurVersionMode();
      if (eParLayout == XFA_AttributeValue::Tb && version < XFA_VERSION_208) {
        std::optional<CXFA_Measurement> measureH =
            JSObject()->TryMeasure(XFA_Attribute::H, false);
        if (measureH.has_value()) {
          return XFA_AttributeValue::ContentArea;
        }
      }
      return XFA_AttributeValue::None;
    }
    case XFA_Element::Draw:
      return XFA_AttributeValue::ContentArea;
    default:
      return XFA_AttributeValue::None;
  }
}

WideString CXFA_Node::GetNameExpression() {
  WideString wsName = GetNameExpressionSinglePath(this);
  CXFA_Node* parent = GetParent();
  while (parent) {
    WideString wsParent = GetNameExpressionSinglePath(parent);
    wsParent += L".";
    wsParent += wsName;
    wsName = std::move(wsParent);
    parent = parent->GetParent();
  }
  return wsName;
}

CXFA_Node* CXFA_Node::GetDataDescriptionNode() {
  if (packet_ == XFA_PacketType::Datasets) {
    return aux_node_;
  }
  return nullptr;
}

void CXFA_Node::SetDataDescriptionNode(CXFA_Node* pDataDescriptionNode) {
  DCHECK_EQ(packet_, XFA_PacketType::Datasets);
  aux_node_ = pDataDescriptionNode;
}

CXFA_Node* CXFA_Node::GetModelNode() {
  switch (GetPacketType()) {
    case XFA_PacketType::Xdp:
      return document_->GetRoot();
    case XFA_PacketType::Config:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_Config));
    case XFA_PacketType::Template:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_Template));
    case XFA_PacketType::Form:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_Form));
    case XFA_PacketType::Datasets:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_Datasets));
    case XFA_PacketType::LocaleSet:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_LocaleSet));
    case XFA_PacketType::ConnectionSet:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_ConnectionSet));
    case XFA_PacketType::SourceSet:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_SourceSet));
    case XFA_PacketType::Xdc:
      return ToNode(document_->GetXFAObject(XFA_HASHCODE_Xdc));
    default:
      return this;
  }
}

size_t CXFA_Node::CountChildren(XFA_Element eType, bool bOnlyChild) {
  size_t count = 0;
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() != eType && eType != XFA_Element::Unknown) {
      continue;
    }
    if (bOnlyChild && HasProperty(pNode->GetElementType())) {
      continue;
    }
    ++count;
  }
  return count;
}

CXFA_Node* CXFA_Node::GetChildInternal(size_t index,
                                       XFA_Element eType,
                                       bool bOnlyChild) const {
  size_t count = 0;
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() != eType && eType != XFA_Element::Unknown) {
      continue;
    }
    if (bOnlyChild && HasProperty(pNode->GetElementType())) {
      continue;
    }
    if (count == index) {
      return pNode;
    }

    ++count;
  }
  return nullptr;
}

bool CXFA_Node::IsAncestorOf(const CXFA_Node* that) const {
  while (that) {
    if (this == that) {
      return true;
    }
    that = that->GetParent();
  }
  return false;
}

void CXFA_Node::InsertChildAndNotify(int32_t index, CXFA_Node* pNode) {
  InsertChildAndNotify(pNode, GetNthChild(index));
}

void CXFA_Node::InsertChildAndNotify(CXFA_Node* pNode, CXFA_Node* pBeforeNode) {
  CHECK(!pNode->GetParent());
  CHECK(!pBeforeNode || pBeforeNode->GetParent() == this);
  pNode->ClearFlag(XFA_NodeFlag::kHasRemovedChildren);
  InsertBefore(pNode, pBeforeNode);

  CXFA_FFNotify* pNotify = document_->GetNotify();
  if (pNotify) {
    pNotify->OnChildAdded(this);
  }

  if (!IsNeedSavingXMLNode() || !pNode->xml_node_) {
    return;
  }

  DCHECK(!pNode->xml_node_->GetParent());
  xml_node_->InsertBefore(pNode->xml_node_,
                          pBeforeNode ? pBeforeNode->xml_node_ : nullptr);
}

void CXFA_Node::RemoveChildAndNotify(CXFA_Node* pNode, bool bNotify) {
  CHECK(pNode);
  if (pNode->GetParent() != this) {
    return;
  }

  pNode->SetFlag(XFA_NodeFlag::kHasRemovedChildren);
  GCedTreeNodeMixin<CXFA_Node>::RemoveChild(pNode);
  OnRemoved(bNotify);

  if (!IsNeedSavingXMLNode() || !pNode->xml_node_) {
    return;
  }

  if (!pNode->IsAttributeInXML()) {
    xml_node_->RemoveChild(pNode->xml_node_);
    return;
  }

  DCHECK_EQ(pNode->xml_node_, xml_node_);
  CFX_XMLElement* pXMLElement = ToXMLElement(pNode->xml_node_);
  if (pXMLElement) {
    WideString wsAttributeName =
        pNode->JSObject()->GetCData(XFA_Attribute::QualifiedName);
    pXMLElement->RemoveAttribute(wsAttributeName);
  }

  WideString wsName = pNode->JSObject()
                          ->TryAttribute(XFA_Attribute::Name, false)
                          .value_or(WideString());

  auto* pNewXMLElement = GetXMLDocument()->CreateNode<CFX_XMLElement>(wsName);
  WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
  if (!wsValue.IsEmpty()) {
    auto* text = GetXMLDocument()->CreateNode<CFX_XMLText>(wsValue);
    pNewXMLElement->AppendLastChild(text);
  }
  pNode->xml_node_ = pNewXMLElement;
  pNode->JSObject()->SetEnum(XFA_Attribute::Contains,
                             XFA_AttributeValue::Unknown, false);
}

CXFA_Node* CXFA_Node::GetFirstChildByName(WideStringView wsName) const {
  return GetFirstChildByName(FX_HashCode_GetW(wsName));
}

CXFA_Node* CXFA_Node::GetFirstChildByName(uint32_t dwNameHash) const {
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetNameHash() == dwNameHash) {
      return pNode;
    }
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetFirstChildByClassInternal(XFA_Element eType) const {
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() == eType) {
      return pNode;
    }
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetNextSameNameSibling(uint32_t dwNameHash) const {
  for (CXFA_Node* pNode = GetNextSibling(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetNameHash() == dwNameHash) {
      return pNode;
    }
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetNextSameNameSiblingInternal(
    WideStringView wsNodeName) const {
  return GetNextSameNameSibling(FX_HashCode_GetW(wsNodeName));
}

CXFA_Node* CXFA_Node::GetNextSameClassSiblingInternal(XFA_Element eType) const {
  for (CXFA_Node* pNode = GetNextSibling(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() == eType) {
      return pNode;
    }
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetOneChildNamed(WideStringView wsName) {
  return FindFirstSiblingNamed(this, FX_HashCode_GetW(wsName));
}

CXFA_Node* CXFA_Node::GetOneChildOfClass(WideStringView wsClass) {
  XFA_Element element = XFA_GetElementByName(wsClass);
  if (element == XFA_Element::Unknown) {
    return nullptr;
  }

  return FindFirstSiblingOfClass(this, element);
}

std::vector<CXFA_Node*> CXFA_Node::GetSiblings(bool bIsClassName) {
  std::vector<CXFA_Node*> siblings;
  CXFA_Node* parent = GetParent();
  if (!parent) {
    return siblings;
  }
  if (!parent->HasProperty(GetElementType())) {
    parent = GetTransparentParent();
    if (!parent) {
      return siblings;
    }
  }

  uint32_t dwNameHash = bIsClassName ? GetClassHashCode() : GetNameHash();
  TraversePropertiesOrSiblings(parent, dwNameHash, &siblings, bIsClassName);
  return siblings;
}

size_t CXFA_Node::GetIndex(bool bIsProperty, bool bIsClassIndex) {
  CXFA_Node* parent = GetParent();
  if (!parent) {
    return 0;
  }

  if (!bIsProperty) {
    parent = GetTransparentParent();
    if (!parent) {
      return 0;
    }
  }
  uint32_t dwHashName = bIsClassIndex ? GetClassHashCode() : GetNameHash();
  std::vector<CXFA_Node*> siblings;
  TraversePropertiesOrSiblings(parent, dwHashName, &siblings, bIsClassIndex);
  for (size_t i = 0; i < siblings.size(); ++i) {
    if (siblings[i] == this) {
      return i;
    }
  }
  return 0;
}

size_t CXFA_Node::GetIndexByName() {
  return GetIndex(IsProperty(), /*bIsClassIndex=*/false);
}

size_t CXFA_Node::GetIndexByClassName() {
  return GetIndex(IsProperty(), /*bIsClassIndex=*/true);
}

CXFA_Node* CXFA_Node::GetInstanceMgrOfSubform() {
  CXFA_Node* pInstanceMgr = nullptr;
  if (packet_ == XFA_PacketType::Form) {
    CXFA_Node* pParentNode = GetParent();
    if (!pParentNode || pParentNode->GetElementType() == XFA_Element::Area) {
      return pInstanceMgr;
    }

    for (CXFA_Node* pNode = GetPrevSibling(); pNode;
         pNode = pNode->GetPrevSibling()) {
      XFA_Element eType = pNode->GetElementType();
      if ((eType == XFA_Element::Subform || eType == XFA_Element::SubformSet) &&
          pNode->name_hash_ != name_hash_) {
        break;
      }
      if (eType == XFA_Element::InstanceManager) {
        WideString wsName = JSObject()->GetCData(XFA_Attribute::Name);
        WideString wsInstName =
            pNode->JSObject()->GetCData(XFA_Attribute::Name);
        if (wsInstName.GetLength() > 0 && wsInstName.Front() == '_' &&
            wsInstName.Substr(1) == wsName) {
          pInstanceMgr = pNode;
        }
        break;
      }
    }
  }
  return pInstanceMgr;
}

CXFA_Occur* CXFA_Node::GetOccurIfExists() {
  return GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
}

bool CXFA_Node::HasFlag(XFA_NodeFlag dwFlag) const {
  if (node_flags_ & dwFlag) {
    return true;
  }
  if (dwFlag == XFA_NodeFlag::kHasRemovedChildren) {
    return GetParent() && GetParent()->HasFlag(dwFlag);
  }
  return false;
}

void CXFA_Node::SetInitializedFlagAndNotify() {
  if (!IsInitialized()) {
    CXFA_FFNotify* pNotify = document_->GetNotify();
    if (pNotify) {
      pNotify->OnNodeReady(this);
    }
  }
  node_flags_ |= XFA_NodeFlag::kInitialized;
}

void CXFA_Node::SetFlag(XFA_NodeFlag dwFlag) {
  node_flags_ |= dwFlag;
}

void CXFA_Node::ClearFlag(XFA_NodeFlag dwFlag) {
  node_flags_.Clear(dwFlag);
}

bool CXFA_Node::IsAttributeInXML() {
  return JSObject()->GetEnum(XFA_Attribute::Contains) ==
         XFA_AttributeValue::MetaData;
}

void CXFA_Node::OnRemoved(bool bNotify) const {
  if (!bNotify) {
    return;
  }

  CXFA_FFNotify* pNotify = document_->GetNotify();
  if (pNotify) {
    pNotify->OnChildRemoved();
  }
}

void CXFA_Node::UpdateNameHash() {
  WideString wsName = JSObject()->GetCData(XFA_Attribute::Name);
  name_hash_ = FX_HashCode_GetW(wsName.AsStringView());
}

CFX_XMLNode* CXFA_Node::CreateXMLMappingNode() {
  if (!xml_node_) {
    xml_node_ = GetXMLDocument()->CreateNode<CFX_XMLElement>(
        JSObject()->GetCData(XFA_Attribute::Name));
  }
  return xml_node_;
}

bool CXFA_Node::IsNeedSavingXMLNode() const {
  return xml_node_ && (GetPacketType() == XFA_PacketType::Datasets ||
                       GetElementType() == XFA_Element::Xfa);
}

CXFA_Node* CXFA_Node::GetItemIfExists(int32_t index) {
  int32_t iCount = 0;
  uint32_t dwNameHash = 0;
  for (CXFA_Node* pNode = GetNextSibling(); pNode;
       pNode = pNode->GetNextSibling()) {
    XFA_Element eCurType = pNode->GetElementType();
    if (eCurType == XFA_Element::InstanceManager) {
      break;
    }
    if ((eCurType != XFA_Element::Subform) &&
        (eCurType != XFA_Element::SubformSet)) {
      continue;
    }
    if (iCount == 0) {
      WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
      WideString wsInstName = JSObject()->GetCData(XFA_Attribute::Name);
      if (wsInstName.GetLength() < 1 || wsInstName.Front() != '_' ||
          wsInstName.Substr(1) != wsName) {
        return nullptr;
      }
      dwNameHash = pNode->GetNameHash();
    }
    if (dwNameHash != pNode->GetNameHash()) {
      break;
    }

    iCount++;
    if (iCount > index) {
      return pNode;
    }
  }
  return nullptr;
}

int32_t CXFA_Node::GetCount() {
  int32_t iCount = 0;
  uint32_t dwNameHash = 0;
  for (CXFA_Node* pNode = GetNextSibling(); pNode;
       pNode = pNode->GetNextSibling()) {
    XFA_Element eCurType = pNode->GetElementType();
    if (eCurType == XFA_Element::InstanceManager) {
      break;
    }
    if ((eCurType != XFA_Element::Subform) &&
        (eCurType != XFA_Element::SubformSet)) {
      continue;
    }
    if (iCount == 0) {
      WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
      WideString wsInstName = JSObject()->GetCData(XFA_Attribute::Name);
      if (wsInstName.GetLength() < 1 || wsInstName.Front() != '_' ||
          wsInstName.Substr(1) != wsName) {
        return iCount;
      }
      dwNameHash = pNode->GetNameHash();
    }
    if (dwNameHash != pNode->GetNameHash()) {
      break;
    }

    iCount++;
  }
  return iCount;
}

void CXFA_Node::InsertItem(CXFA_Node* pNewInstance,
                           int32_t iPos,
                           int32_t iCount,
                           bool bMoveDataBindingNodes) {
  if (iCount < 0) {
    iCount = GetCount();
  }
  if (iPos < 0) {
    iPos = iCount;
  }
  if (iPos == iCount) {
    CXFA_Node* item = GetItemIfExists(iCount - 1);
    if (!item) {
      return;
    }

    CXFA_Node* pNextSibling =
        iCount > 0 ? item->GetNextSibling() : GetNextSibling();
    GetParent()->InsertChildAndNotify(pNewInstance, pNextSibling);
    if (bMoveDataBindingNodes) {
      NodeSet sNew;
      CXFA_NodeIteratorTemplate<CXFA_Node,
                                CXFA_TraverseStrategy_XFAContainerNode>
          sIteratorNew(pNewInstance);
      for (CXFA_Node* pNode = sIteratorNew.GetCurrent(); pNode;
           pNode = sIteratorNew.MoveToNext()) {
        CXFA_Node* pDataNode = pNode->GetBindData();
        if (pDataNode) {
          sNew.insert(pDataNode);
        }
      }
      NodeSet sAfter;
      CXFA_NodeIteratorTemplate<CXFA_Node,
                                CXFA_TraverseStrategy_XFAContainerNode>
          sIteratorAfter(pNextSibling);
      for (CXFA_Node* pNode = sIteratorAfter.GetCurrent(); pNode;
           pNode = sIteratorAfter.MoveToNext()) {
        CXFA_Node* pDataNode = pNode->GetBindData();
        if (pDataNode) {
          sAfter.insert(pDataNode);
        }
      }
      ReorderDataNodes(sNew, sAfter, false);
    }
  } else {
    CXFA_Node* pBeforeInstance = GetItemIfExists(iPos);
    if (!pBeforeInstance) {
      // TODO(dsinclair): What should happen here?
      return;
    }

    GetParent()->InsertChildAndNotify(pNewInstance, pBeforeInstance);
    if (bMoveDataBindingNodes) {
      NodeSet sNew;
      CXFA_NodeIteratorTemplate<CXFA_Node,
                                CXFA_TraverseStrategy_XFAContainerNode>
          sIteratorNew(pNewInstance);
      for (CXFA_Node* pNode = sIteratorNew.GetCurrent(); pNode;
           pNode = sIteratorNew.MoveToNext()) {
        CXFA_Node* pDataNode = pNode->GetBindData();
        if (pDataNode) {
          sNew.insert(pDataNode);
        }
      }
      NodeSet sBefore;
      CXFA_NodeIteratorTemplate<CXFA_Node,
                                CXFA_TraverseStrategy_XFAContainerNode>
          sIteratorBefore(pBeforeInstance);
      for (CXFA_Node* pNode = sIteratorBefore.GetCurrent(); pNode;
           pNode = sIteratorBefore.MoveToNext()) {
        CXFA_Node* pDataNode = pNode->GetBindData();
        if (pDataNode) {
          sBefore.insert(pDataNode);
        }
      }
      ReorderDataNodes(sNew, sBefore, true);
    }
  }
}

void CXFA_Node::RemoveItem(CXFA_Node* pRemoveInstance,
                           bool bRemoveDataBinding) {
  GetParent()->RemoveChildAndNotify(pRemoveInstance, true);
  if (!bRemoveDataBinding) {
    return;
  }

  CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
      sIterator(pRemoveInstance);
  for (CXFA_Node* pFormNode = sIterator.GetCurrent(); pFormNode;
       pFormNode = sIterator.MoveToNext()) {
    CXFA_Node* pDataNode = pFormNode->GetBindData();
    if (!pDataNode) {
      continue;
    }

    if (!pDataNode->RemoveBindItem(pFormNode)) {
      if (CXFA_Node* pDataParent = pDataNode->GetParent()) {
        pDataParent->RemoveChildAndNotify(pDataNode, true);
      }
    }
    pFormNode->SetBindingNode(nullptr);
  }
}

CXFA_Node* CXFA_Node::CreateInstanceIfPossible(bool bDataMerge) {
  CXFA_Document* document = GetDocument();
  CXFA_Node* pTemplateNode = GetTemplateNodeIfExists();
  if (!pTemplateNode) {
    return nullptr;
  }

  CXFA_Node* pFormParent = GetParent();
  CXFA_Node* pDataScope = nullptr;
  for (CXFA_Node* pRootBoundNode = pFormParent;
       pRootBoundNode && pRootBoundNode->IsContainerNode();
       pRootBoundNode = pRootBoundNode->GetParent()) {
    pDataScope = pRootBoundNode->GetBindData();
    if (pDataScope) {
      break;
    }
  }
  if (!pDataScope) {
    pDataScope = ToNode(document->GetXFAObject(XFA_HASHCODE_Record));
    DCHECK(pDataScope);
  }

  CXFA_Node* pInstance = document->DataMerge_CopyContainer(
      pTemplateNode, pFormParent, pDataScope, true, bDataMerge, true);
  if (pInstance) {
    document->DataMerge_UpdateBindingRelations(pInstance);
    pFormParent->RemoveChildAndNotify(pInstance, true);
  }
  return pInstance;
}

std::optional<bool> CXFA_Node::GetDefaultBoolean(XFA_Attribute attr) const {
  std::optional<void*> value =
      GetDefaultValue(attr, XFA_AttributeType::Boolean);
  if (!value.has_value()) {
    return std::nullopt;
  }
  return !!value.value();
}

std::optional<int32_t> CXFA_Node::GetDefaultInteger(XFA_Attribute attr) const {
  std::optional<void*> value =
      GetDefaultValue(attr, XFA_AttributeType::Integer);
  if (!value.has_value()) {
    return std::nullopt;
  }
  return static_cast<int32_t>(reinterpret_cast<uintptr_t>(value.value()));
}

std::optional<CXFA_Measurement> CXFA_Node::GetDefaultMeasurement(
    XFA_Attribute attr) const {
  std::optional<void*> value =
      GetDefaultValue(attr, XFA_AttributeType::Measure);
  if (!value.has_value()) {
    return std::nullopt;
  }

  WideString str = WideString(static_cast<const wchar_t*>(value.value()));
  return CXFA_Measurement(str.AsStringView());
}

std::optional<WideString> CXFA_Node::GetDefaultCData(XFA_Attribute attr) const {
  std::optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::CData);
  if (!value.has_value()) {
    return std::nullopt;
  }

  return WideString(static_cast<const wchar_t*>(value.value()));
}

std::optional<XFA_AttributeValue> CXFA_Node::GetDefaultEnum(
    XFA_Attribute attr) const {
  std::optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Enum);
  if (!value.has_value()) {
    return std::nullopt;
  }
  return static_cast<XFA_AttributeValue>(
      reinterpret_cast<uintptr_t>(value.value()));
}

std::optional<void*> CXFA_Node::GetDefaultValue(XFA_Attribute attr,
                                                XFA_AttributeType eType) const {
  const AttributeData* data = GetAttributeData(attr);
  if (!data || data->type != eType) {
    return std::nullopt;
  }
  return data->default_value;
}

void CXFA_Node::SendAttributeChangeMessage(XFA_Attribute eAttribute,
                                           bool bScriptModify) {
  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
  if (!pNotify) {
    return;
  }

  if (GetPacketType() != XFA_PacketType::Form) {
    pNotify->OnValueChanged(this, eAttribute, this, this);
    return;
  }

  bool bNeedFindContainer = false;
  switch (GetElementType()) {
    case XFA_Element::Caption:
      bNeedFindContainer = true;
      pNotify->OnValueChanged(this, eAttribute, this, GetParent());
      break;
    case XFA_Element::Font:
    case XFA_Element::Para: {
      bNeedFindContainer = true;
      CXFA_Node* pParentNode = GetParent();
      if (pParentNode->GetElementType() == XFA_Element::Caption) {
        pNotify->OnValueChanged(this, eAttribute, pParentNode,
                                pParentNode->GetParent());
      } else {
        pNotify->OnValueChanged(this, eAttribute, this, pParentNode);
      }
      break;
    }
    case XFA_Element::Margin: {
      bNeedFindContainer = true;
      CXFA_Node* pParentNode = GetParent();
      XFA_Element eParentType = pParentNode->GetElementType();
      if (pParentNode->IsContainerNode()) {
        pNotify->OnValueChanged(this, eAttribute, this, pParentNode);
      } else if (eParentType == XFA_Element::Caption) {
        pNotify->OnValueChanged(this, eAttribute, pParentNode,
                                pParentNode->GetParent());
      } else {
        CXFA_Node* pNode = pParentNode->GetParent();
        if (pNode && pNode->GetElementType() == XFA_Element::Ui) {
          pNotify->OnValueChanged(this, eAttribute, pNode, pNode->GetParent());
        }
      }
      break;
    }
    case XFA_Element::Comb: {
      CXFA_Node* pEditNode = GetParent();
      XFA_Element eUIType = pEditNode->GetElementType();
      if (pEditNode && (eUIType == XFA_Element::DateTimeEdit ||
                        eUIType == XFA_Element::NumericEdit ||
                        eUIType == XFA_Element::TextEdit)) {
        CXFA_Node* pUINode = pEditNode->GetParent();
        if (pUINode) {
          pNotify->OnValueChanged(this, eAttribute, pUINode,
                                  pUINode->GetParent());
        }
      }
      break;
    }
    case XFA_Element::Button:
    case XFA_Element::Barcode:
    case XFA_Element::ChoiceList:
    case XFA_Element::DateTimeEdit:
    case XFA_Element::NumericEdit:
    case XFA_Element::PasswordEdit:
    case XFA_Element::TextEdit: {
      CXFA_Node* pUINode = GetParent();
      if (pUINode) {
        pNotify->OnValueChanged(this, eAttribute, pUINode,
                                pUINode->GetParent());
      }
      break;
    }
    case XFA_Element::CheckButton: {
      bNeedFindContainer = true;
      CXFA_Node* pUINode = GetParent();
      if (pUINode) {
        pNotify->OnValueChanged(this, eAttribute, pUINode,
                                pUINode->GetParent());
      }
      break;
    }
    case XFA_Element::Keep:
    case XFA_Element::Bookend:
    case XFA_Element::Break:
    case XFA_Element::BreakAfter:
    case XFA_Element::BreakBefore:
    case XFA_Element::Overflow:
      bNeedFindContainer = true;
      break;
    case XFA_Element::Area:
    case XFA_Element::Draw:
    case XFA_Element::ExclGroup:
    case XFA_Element::Field:
    case XFA_Element::Subform:
    case XFA_Element::SubformSet:
      pNotify->OnContainerChanged();
      pNotify->OnValueChanged(this, eAttribute, this, this);
      break;
    case XFA_Element::Sharptext:
    case XFA_Element::Sharpxml:
    case XFA_Element::SharpxHTML: {
      CXFA_Node* pTextNode = GetParent();
      if (!pTextNode) {
        return;
      }

      CXFA_Node* pValueNode = pTextNode->GetParent();
      if (!pValueNode) {
        return;
      }

      XFA_Element eType = pValueNode->GetElementType();
      if (eType == XFA_Element::Value) {
        bNeedFindContainer = true;
        CXFA_Node* pNode = pValueNode->GetParent();
        if (pNode && pNode->IsContainerNode()) {
          if (bScriptModify) {
            pValueNode = pNode;
          }

          pNotify->OnValueChanged(this, eAttribute, pValueNode, pNode);
        } else {
          pNotify->OnValueChanged(this, eAttribute, pNode, pNode->GetParent());
        }
      } else {
        if (eType == XFA_Element::Items) {
          CXFA_Node* pNode = pValueNode->GetParent();
          if (pNode && pNode->IsContainerNode()) {
            pNotify->OnValueChanged(this, eAttribute, pValueNode, pNode);
          }
        }
      }
      break;
    }
    default:
      break;
  }

  if (!bNeedFindContainer) {
    return;
  }

  CXFA_Node* pParent = this;
  while (pParent && !pParent->IsContainerNode()) {
    pParent = pParent->GetParent();
  }

  if (pParent) {
    pNotify->OnContainerChanged();
  }
}

void CXFA_Node::SyncValue(const WideString& wsValue, bool bNotify) {
  WideString wsFormatValue = wsValue;
  CXFA_Node* pContainerNode = GetContainerNode();
  if (pContainerNode) {
    wsFormatValue = pContainerNode->GetFormatDataValue(wsValue);
  }

  JSObject()->SetContent(wsValue, wsFormatValue, bNotify, false, true);
}

WideString CXFA_Node::GetRawValue() const {
  return JSObject()->GetContent(false);
}

int32_t CXFA_Node::GetRotate() const {
  std::optional<int32_t> degrees =
      JSObject()->TryInteger(XFA_Attribute::Rotate, false);
  return degrees.has_value() ? XFA_MapRotation(degrees.value()) / 90 * 90 : 0;
}

CXFA_Border* CXFA_Node::GetBorderIfExists() const {
  return JSObject()->GetProperty<CXFA_Border>(0, XFA_Element::Border);
}

CXFA_Border* CXFA_Node::GetOrCreateBorderIfPossible() {
  return JSObject()->GetOrCreateProperty<CXFA_Border>(0, XFA_Element::Border);
}

CXFA_Caption* CXFA_Node::GetCaptionIfExists() const {
  return JSObject()->GetProperty<CXFA_Caption>(0, XFA_Element::Caption);
}

CXFA_Font* CXFA_Node::GetOrCreateFontIfPossible() {
  return JSObject()->GetOrCreateProperty<CXFA_Font>(0, XFA_Element::Font);
}

CXFA_Font* CXFA_Node::GetFontIfExists() const {
  return JSObject()->GetProperty<CXFA_Font>(0, XFA_Element::Font);
}

float CXFA_Node::GetFontSize() const {
  CXFA_Font* font = GetFontIfExists();
  float fFontSize = font ? font->GetFontSize() : 10.0f;
  return fFontSize < 0.1f ? 10.0f : fFontSize;
}

float CXFA_Node::GetLineHeight() const {
  float fLineHeight = 0;
  CXFA_Para* para = GetParaIfExists();
  if (para) {
    fLineHeight = para->GetLineHeight();
  }

  if (fLineHeight < 1) {
    fLineHeight = GetFontSize() * 1.2f;
  }
  return fLineHeight;
}

FX_ARGB CXFA_Node::GetTextColor() const {
  CXFA_Font* font = GetFontIfExists();
  return font ? font->GetColor() : 0xFF000000;
}

CXFA_Margin* CXFA_Node::GetMarginIfExists() const {
  return JSObject()->GetProperty<CXFA_Margin>(0, XFA_Element::Margin);
}

CXFA_Para* CXFA_Node::GetParaIfExists() const {
  return JSObject()->GetProperty<CXFA_Para>(0, XFA_Element::Para);
}

bool CXFA_Node::IsOpenAccess() const {
  for (auto* pNode = this; pNode; pNode = pNode->GetContainerParent()) {
    XFA_AttributeValue iAcc = pNode->JSObject()->GetEnum(XFA_Attribute::Access);
    if (iAcc != XFA_AttributeValue::Open) {
      return false;
    }
  }
  return true;
}

CXFA_Value* CXFA_Node::GetDefaultValueIfExists() {
  CXFA_Node* pTemNode = GetTemplateNodeIfExists();
  return pTemNode ? pTemNode->JSObject()->GetProperty<CXFA_Value>(
                        0, XFA_Element::Value)
                  : nullptr;
}

CXFA_Value* CXFA_Node::GetFormValueIfExists() const {
  return JSObject()->GetProperty<CXFA_Value>(0, XFA_Element::Value);
}

CXFA_Calculate* CXFA_Node::GetCalculateIfExists() const {
  return JSObject()->GetProperty<CXFA_Calculate>(0, XFA_Element::Calculate);
}

CXFA_Validate* CXFA_Node::GetValidateIfExists() const {
  return JSObject()->GetProperty<CXFA_Validate>(0, XFA_Element::Validate);
}

CXFA_Validate* CXFA_Node::GetOrCreateValidateIfPossible() {
  return JSObject()->GetOrCreateProperty<CXFA_Validate>(0,
                                                        XFA_Element::Validate);
}

CXFA_Bind* CXFA_Node::GetBindIfExists() const {
  return JSObject()->GetProperty<CXFA_Bind>(0, XFA_Element::Bind);
}

std::optional<XFA_AttributeValue> CXFA_Node::GetIntactFromKeep(
    const CXFA_Keep* pKeep,
    XFA_AttributeValue eLayoutType) const {
  std::optional<XFA_AttributeValue> intact =
      pKeep->JSObject()->TryEnum(XFA_Attribute::Intact, false);
  if (!intact.has_value()) {
    return std::nullopt;
  }

  if (intact.value() != XFA_AttributeValue::None ||
      eLayoutType != XFA_AttributeValue::Row ||
      document_->GetCurVersionMode() >= XFA_VERSION_208) {
    return intact;
  }

  CXFA_Node* pPreviewRow = GetPrevContainerSibling();
  if (!pPreviewRow || pPreviewRow->JSObject()->GetEnum(XFA_Attribute::Layout) !=
                          XFA_AttributeValue::Row) {
    return intact;
  }

  std::optional<XFA_AttributeValue> value =
      pKeep->JSObject()->TryEnum(XFA_Attribute::Previous, false);
  if (value == XFA_AttributeValue::ContentArea ||
      value == XFA_AttributeValue::PageArea) {
    return XFA_AttributeValue::ContentArea;
  }

  CXFA_Keep* pNode =
      pPreviewRow->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
  if (!pNode) {
    return intact;
  }

  std::optional<XFA_AttributeValue> ret =
      pNode->JSObject()->TryEnum(XFA_Attribute::Next, false);
  if (ret == XFA_AttributeValue::ContentArea ||
      ret == XFA_AttributeValue::PageArea) {
    return XFA_AttributeValue::ContentArea;
  }
  return intact;
}

std::optional<float> CXFA_Node::TryWidth() {
  return JSObject()->TryMeasureAsFloat(XFA_Attribute::W);
}

std::optional<float> CXFA_Node::TryHeight() {
  return JSObject()->TryMeasureAsFloat(XFA_Attribute::H);
}

std::optional<float> CXFA_Node::TryMinWidth() {
  return JSObject()->TryMeasureAsFloat(XFA_Attribute::MinW);
}

std::optional<float> CXFA_Node::TryMinHeight() {
  return JSObject()->TryMeasureAsFloat(XFA_Attribute::MinH);
}

std::optional<float> CXFA_Node::TryMaxWidth() {
  return JSObject()->TryMeasureAsFloat(XFA_Attribute::MaxW);
}

std::optional<float> CXFA_Node::TryMaxHeight() {
  return JSObject()->TryMeasureAsFloat(XFA_Attribute::MaxH);
}

CXFA_Node* CXFA_Node::GetExclGroupIfExists() {
  CXFA_Node* pExcl = GetParent();
  if (!pExcl || pExcl->GetElementType() != XFA_Element::ExclGroup) {
    return nullptr;
  }
  return pExcl;
}

XFA_EventError CXFA_Node::ProcessEvent(CXFA_FFDocView* pDocView,
                                       XFA_AttributeValue iActivity,
                                       CXFA_EventParam* pEventParam) {
  if (GetElementType() == XFA_Element::Draw) {
    return XFA_EventError::kNotExist;
  }

  std::vector<CXFA_Event*> eventArray =
      GetEventByActivity(iActivity, pEventParam->is_form_ready_);
  bool first = true;
  XFA_EventError iRet = XFA_EventError::kNotExist;
  for (CXFA_Event* event : eventArray) {
    XFA_EventError result =
        ProcessEventInternal(pDocView, iActivity, event, pEventParam);
    if (first || result == XFA_EventError::kSuccess) {
      iRet = result;
    }
    first = false;
  }
  return iRet;
}

XFA_EventError CXFA_Node::ProcessEventInternal(CXFA_FFDocView* pDocView,
                                               XFA_AttributeValue iActivity,
                                               CXFA_Event* event,
                                               CXFA_EventParam* pEventParam) {
  if (!event) {
    return XFA_EventError::kNotExist;
  }

  switch (event->GetEventType()) {
    case XFA_Element::Execute:
      break;
    case XFA_Element::Script:
      if (iActivity == XFA_AttributeValue::DocClose) {
        // Too late, scripting engine already gone.
        return XFA_EventError::kNotExist;
      }
      return ExecuteScript(pDocView, event->GetScriptIfExists(), pEventParam);
    case XFA_Element::SignData:
      break;
    case XFA_Element::Submit: {
      return XFA_EventError::kDisabled;
    }
    default:
      break;
  }
  return XFA_EventError::kNotExist;
}

XFA_EventError CXFA_Node::ProcessCalculate(CXFA_FFDocView* pDocView) {
  if (GetElementType() == XFA_Element::Draw) {
    return XFA_EventError::kNotExist;
  }

  CXFA_Calculate* calc = GetCalculateIfExists();
  if (!calc) {
    return XFA_EventError::kNotExist;
  }
  if (IsUserInteractive()) {
    return XFA_EventError::kDisabled;
  }

  CXFA_EventParam EventParam(XFA_EVENT_Calculate);
  EventParam.targeted_ = false;
  XFA_EventError iRet =
      ExecuteScript(pDocView, calc->GetScriptIfExists(), &EventParam);
  if (iRet != XFA_EventError::kSuccess) {
    return iRet;
  }

  if (GetRawValue() != EventParam.result_) {
    SetValue(XFA_ValuePicture::kRaw, EventParam.result_);
    pDocView->UpdateUIDisplay(this, nullptr);
  }
  return XFA_EventError::kSuccess;
}

void CXFA_Node::ProcessScriptTestValidate(CXFA_FFDocView* pDocView,
                                          CXFA_Validate* validate,
                                          bool bVersionFlag) {
  CXFA_FFApp::CallbackIface* pAppProvider =
      pDocView->GetDoc()->GetApp()->GetAppProvider();
  if (!pAppProvider) {
    return;
  }

  WideString wsTitle = pAppProvider->GetAppTitle();
  WideString wsScriptMsg = validate->GetScriptMessageText();
  if (validate->GetScriptTest() == XFA_AttributeValue::Warning) {
    if (IsUserInteractive()) {
      return;
    }
    if (wsScriptMsg.IsEmpty()) {
      wsScriptMsg = GetValidateMessage(false, bVersionFlag);
    }

    if (bVersionFlag) {
      pAppProvider->MsgBox(wsScriptMsg, wsTitle,
                           static_cast<uint32_t>(AlertIcon::kWarning),
                           static_cast<uint32_t>(AlertButton::kOK));
      return;
    }
    if (pAppProvider->MsgBox(wsScriptMsg, wsTitle,
                             static_cast<uint32_t>(AlertIcon::kWarning),
                             static_cast<uint32_t>(AlertButton::kYesNo)) ==
        static_cast<uint32_t>(AlertReturn::kYes)) {
      SetFlag(XFA_NodeFlag::kUserInteractive);
    }
    return;
  }

  if (wsScriptMsg.IsEmpty()) {
    wsScriptMsg = GetValidateMessage(true, bVersionFlag);
  }
  pAppProvider->MsgBox(wsScriptMsg, wsTitle,
                       static_cast<uint32_t>(AlertIcon::kError),
                       static_cast<uint32_t>(AlertButton::kOK));
}

XFA_EventError CXFA_Node::ProcessFormatTestValidate(CXFA_FFDocView* pDocView,
                                                    CXFA_Validate* validate,
                                                    bool bVersionFlag) {
  WideString wsPicture = validate->GetPicture();
  if (wsPicture.IsEmpty()) {
    return XFA_EventError::kNotExist;
  }

  WideString wsRawValue = GetRawValue();
  if (wsRawValue.IsEmpty()) {
    return XFA_EventError::kError;
  }

  GCedLocaleIface* pLocale = GetLocale();
  if (!pLocale) {
    return XFA_EventError::kNotExist;
  }

  CXFA_LocaleValue lcValue = XFA_GetLocaleValue(this);
  if (lcValue.ValidateValue(lcValue.GetValue(), wsPicture, pLocale, nullptr)) {
    return XFA_EventError::kSuccess;
  }

  CXFA_FFApp::CallbackIface* pAppProvider =
      pDocView->GetDoc()->GetApp()->GetAppProvider();
  if (!pAppProvider) {
    return XFA_EventError::kNotExist;
  }

  WideString wsFormatMsg = validate->GetFormatMessageText();
  WideString wsTitle = pAppProvider->GetAppTitle();
  if (validate->GetFormatTest() == XFA_AttributeValue::Error) {
    if (wsFormatMsg.IsEmpty()) {
      wsFormatMsg = GetValidateMessage(true, bVersionFlag);
    }
    pAppProvider->MsgBox(wsFormatMsg, wsTitle,
                         static_cast<uint32_t>(AlertIcon::kError),
                         static_cast<uint32_t>(AlertButton::kOK));
    return XFA_EventError::kError;
  }

  if (wsFormatMsg.IsEmpty()) {
    wsFormatMsg = GetValidateMessage(false, bVersionFlag);
  }

  if (bVersionFlag) {
    pAppProvider->MsgBox(wsFormatMsg, wsTitle,
                         static_cast<uint32_t>(AlertIcon::kWarning),
                         static_cast<uint32_t>(AlertButton::kOK));
    return XFA_EventError::kError;
  }

  if (pAppProvider->MsgBox(wsFormatMsg, wsTitle,
                           static_cast<uint32_t>(AlertIcon::kWarning),
                           static_cast<uint32_t>(AlertButton::kYesNo)) ==
      static_cast<uint32_t>(AlertReturn::kYes)) {
    SetFlag(XFA_NodeFlag::kUserInteractive);
  }

  return XFA_EventError::kError;
}

XFA_EventError CXFA_Node::ProcessNullTestValidate(CXFA_FFDocView* pDocView,
                                                  CXFA_Validate* validate,
                                                  int32_t iFlags,
                                                  bool bVersionFlag) {
  if (!GetValue(XFA_ValuePicture::kRaw).IsEmpty()) {
    return XFA_EventError::kSuccess;
  }
  if (is_null_ && pre_null_) {
    return XFA_EventError::kSuccess;
  }

  XFA_AttributeValue eNullTest = validate->GetNullTest();
  WideString wsNullMsg = validate->GetNullMessageText();
  if (iFlags & 0x01) {
    if (eNullTest == XFA_AttributeValue::Disabled) {
      return XFA_EventError::kSuccess;
    }

    if (!wsNullMsg.IsEmpty()) {
      pDocView->AddNullTestMsg(wsNullMsg);
    }
    return XFA_EventError::kError;
  }
  if (wsNullMsg.IsEmpty() && bVersionFlag &&
      eNullTest != XFA_AttributeValue::Disabled) {
    return XFA_EventError::kError;
  }
  CXFA_FFApp::CallbackIface* pAppProvider =
      pDocView->GetDoc()->GetApp()->GetAppProvider();
  if (!pAppProvider) {
    return XFA_EventError::kNotExist;
  }

  WideString wsCaptionName;
  WideString wsTitle = pAppProvider->GetAppTitle();
  switch (eNullTest) {
    case XFA_AttributeValue::Error: {
      if (wsNullMsg.IsEmpty()) {
        wsCaptionName = GetValidateCaptionName(bVersionFlag);
        wsNullMsg = wsCaptionName + L" cannot be blank.";
      }
      pAppProvider->MsgBox(wsNullMsg, wsTitle,
                           static_cast<uint32_t>(AlertIcon::kStatus),
                           static_cast<uint32_t>(AlertButton::kOK));
      return XFA_EventError::kError;
    }
    case XFA_AttributeValue::Warning: {
      if (IsUserInteractive()) {
        return XFA_EventError::kSuccess;
      }

      if (wsNullMsg.IsEmpty()) {
        wsCaptionName = GetValidateCaptionName(bVersionFlag);
        wsNullMsg = wsCaptionName +
                    L" cannot be blank. To ignore validations for " +
                    wsCaptionName + L", click Ignore.";
      }
      if (pAppProvider->MsgBox(wsNullMsg, wsTitle,
                               static_cast<uint32_t>(AlertIcon::kWarning),
                               static_cast<uint32_t>(AlertButton::kYesNo)) ==
          static_cast<uint32_t>(AlertReturn::kYes)) {
        SetFlag(XFA_NodeFlag::kUserInteractive);
      }
      return XFA_EventError::kError;
    }
    case XFA_AttributeValue::Disabled:
    default:
      break;
  }
  return XFA_EventError::kSuccess;
}

XFA_EventError CXFA_Node::ProcessValidate(CXFA_FFDocView* pDocView,
                                          int32_t iFlags) {
  if (GetElementType() == XFA_Element::Draw) {
    return XFA_EventError::kNotExist;
  }

  CXFA_Validate* validate = GetValidateIfExists();
  if (!validate) {
    return XFA_EventError::kNotExist;
  }

  const bool bInitDoc = validate->NeedsInitApp();
  const bool bStatus =
      pDocView->GetLayoutStatus() != CXFA_FFDocView::LayoutStatus::kEnd;

  XFA_EventError iFormat = XFA_EventError::kNotExist;
  CXFA_Script* script = validate->GetScriptIfExists();
  bool hasBoolResult = (bInitDoc || bStatus) && GetRawValue().IsEmpty();
  CXFA_Node::BoolScriptResult result = {XFA_EventError::kNotExist, false};
  if (script) {
    CXFA_EventParam eParam(XFA_EVENT_Validate);
    result = ExecuteBoolScript(pDocView, script, &eParam);
  }

  XFA_VERSION version = pDocView->GetDoc()->GetXFADoc()->GetCurVersionMode();
  bool bVersionFlag = version < XFA_VERSION_208;

  if (bInitDoc) {
    validate->ClearFlag(XFA_NodeFlag::kNeedsInitApp);
  } else {
    iFormat = ProcessFormatTestValidate(pDocView, validate, bVersionFlag);
    if (!bVersionFlag) {
      bVersionFlag = pDocView->GetDoc()->GetXFADoc()->is_scripting();
    }
    XFA_EventErrorAccumulate(
        &result.xfa_event_result,
        ProcessNullTestValidate(pDocView, validate, iFlags, bVersionFlag));
  }
  if (result.xfa_event_result == XFA_EventError::kSuccess &&
      iFormat != XFA_EventError::kSuccess && hasBoolResult &&
      !result.script_result) {
    ProcessScriptTestValidate(pDocView, validate, bVersionFlag);
  }
  XFA_EventErrorAccumulate(&result.xfa_event_result, iFormat);
  return result.xfa_event_result;
}

WideString CXFA_Node::GetValidateCaptionName(bool bVersionFlag) {
  WideString wsCaptionName;

  if (!bVersionFlag) {
    CXFA_Caption* caption = GetCaptionIfExists();
    if (caption) {
      CXFA_Value* capValue = caption->GetValueIfExists();
      if (capValue) {
        CXFA_Text* captionText = capValue->GetTextIfExists();
        if (captionText) {
          wsCaptionName = captionText->GetContent();
        }
      }
    }
  }
  if (!wsCaptionName.IsEmpty()) {
    return wsCaptionName;
  }
  return JSObject()->GetCData(XFA_Attribute::Name);
}

WideString CXFA_Node::GetValidateMessage(bool bError, bool bVersionFlag) {
  WideString wsCaptionName = GetValidateCaptionName(bVersionFlag);
  if (bVersionFlag) {
    return wsCaptionName + L" validation failed";
  }
  WideString result =
      L"The value you entered for " + wsCaptionName + L" is invalid.";
  if (!bError) {
    result +=
        L" To ignore validations for " + wsCaptionName + L", click Ignore.";
  }
  return result;
}

XFA_EventError CXFA_Node::ExecuteScript(CXFA_FFDocView* pDocView,
                                        CXFA_Script* script,
                                        CXFA_EventParam* pEventParam) {
  return ExecuteBoolScript(pDocView, script, pEventParam).xfa_event_result;
}

CXFA_Node::BoolScriptResult CXFA_Node::ExecuteBoolScript(
    CXFA_FFDocView* pDocView,
    CXFA_Script* script,
    CXFA_EventParam* pEventParam) {
  if (execute_recursion_depth_ > kMaxExecuteRecursion) {
    return {XFA_EventError::kSuccess, false};
  }

  DCHECK(pEventParam);
  if (!script) {
    return {XFA_EventError::kNotExist, false};
  }
  if (script->GetRunAt() == XFA_AttributeValue::Server) {
    return {XFA_EventError::kDisabled, false};
  }

  WideString wsExpression = script->GetExpression();
  if (wsExpression.IsEmpty()) {
    return {XFA_EventError::kNotExist, false};
  }

  CXFA_Script::Type eScriptType = script->GetContentType();
  if (eScriptType == CXFA_Script::Type::Unknown) {
    return {XFA_EventError::kSuccess, false};
  }

  CXFA_FFDoc* doc = pDocView->GetDoc();
  CFXJSE_Engine* context = doc->GetXFADoc()->GetScriptContext();
  CFXJSE_Engine::EventParamScope paramScope(
      context, pEventParam->targeted_ ? this : nullptr, pEventParam);
  context->SetRunAtType(script->GetRunAt());

  std::vector<cppgc::Persistent<CXFA_Node>> refNodes;
  if (pEventParam->type_ == XFA_EVENT_InitCalculate ||
      pEventParam->type_ == XFA_EVENT_Calculate) {
    context->SetNodesOfRunScript(&refNodes);
  }

  CFXJSE_Context::ExecutionResult exec_result;
  {
    AutoRestorer<uint8_t> restorer(&execute_recursion_depth_);
    ++execute_recursion_depth_;
    exec_result =
        context->RunScript(eScriptType, wsExpression.AsStringView(), this);
  }

  XFA_EventError iRet = XFA_EventError::kError;
  v8::Isolate* isolate = context->GetIsolate();
  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(isolate);
  v8::Local<v8::Value> value =
      v8::Local<v8::Value>::New(isolate, exec_result.value);

  if (exec_result.status) {
    iRet = XFA_EventError::kSuccess;
    if (pEventParam->type_ == XFA_EVENT_Calculate ||
        pEventParam->type_ == XFA_EVENT_InitCalculate) {
      if (!value.IsEmpty() && !fxv8::IsUndefined(value)) {
        if (!fxv8::IsNull(value)) {
          pEventParam->result_ =
              fxv8::ReentrantToWideStringHelper(isolate, value);
        }

        iRet = XFA_EventError::kSuccess;
      } else {
        iRet = XFA_EventError::kError;
      }
      if (pEventParam->type_ == XFA_EVENT_InitCalculate) {
        if ((iRet == XFA_EventError::kSuccess) &&
            (GetRawValue() != pEventParam->result_)) {
          SetValue(XFA_ValuePicture::kRaw, pEventParam->result_);
          pDocView->AddValidateNode(this);
        }
      }
      for (CXFA_Node* pRefNode : refNodes) {
        if (pRefNode == this) {
          continue;
        }

        CJX_Object::CalcData* pGlobalData =
            pRefNode->JSObject()->GetOrCreateCalcData(doc->GetHeap());
        if (!pdfium::Contains(pGlobalData->globals_, this)) {
          pGlobalData->globals_.push_back(this);
        }
      }
    }
  }
  context->SetNodesOfRunScript(nullptr);

  return {iRet, !value.IsEmpty() && fxv8::IsBoolean(value) &&
                    fxv8::ReentrantToBooleanHelper(isolate, value)};
}

std::pair<XFA_FFWidgetType, CXFA_Ui*>
CXFA_Node::CreateChildUIAndValueNodesIfNeeded() {
  XFA_Element eType = GetElementType();
  DCHECK(eType == XFA_Element::Field || eType == XFA_Element::Draw);

  // Both Field and Draw have a UI property. We should always be able to
  // retrieve or create the UI element. If we can't something is wrong.
  CXFA_Ui* pUI = JSObject()->GetOrCreateProperty<CXFA_Ui>(0, XFA_Element::Ui);
  DCHECK(pUI);

  CXFA_Node* pUIChild = nullptr;
  // Search through the children of the UI node to see if we have any of our
  // One-Of entries. If so, that is the node associated with our UI.
  for (CXFA_Node* pChild = pUI->GetFirstChild(); pChild;
       pChild = pChild->GetNextSibling()) {
    if (pUI->IsAOneOfChild(pChild)) {
      pUIChild = pChild;
      break;
    }
  }

  XFA_FFWidgetType widget_type = XFA_FFWidgetType::kNone;
  XFA_Element expected_ui_child_type = XFA_Element::Unknown;

  // Both Field and Draw nodes have a Value child. So, we should either always
  // have it, or always create it. If we don't get the Value child for some
  // reason something has gone really wrong.
  CXFA_Value* value =
      JSObject()->GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
  DCHECK(value);

  // The Value nodes only have One-Of children. So, if we have a first child
  // that child must be the type we want to use.
  CXFA_Node* child = value->GetFirstChild();
  if (child) {
    switch (child->GetElementType()) {
      case XFA_Element::Boolean:
        expected_ui_child_type = XFA_Element::CheckButton;
        break;
      case XFA_Element::Integer:
      case XFA_Element::Decimal:
      case XFA_Element::Float:
        expected_ui_child_type = XFA_Element::NumericEdit;
        break;
      case XFA_Element::ExData:
      case XFA_Element::Text:
        expected_ui_child_type = XFA_Element::TextEdit;
        widget_type = XFA_FFWidgetType::kText;
        break;
      case XFA_Element::Date:
      case XFA_Element::Time:
      case XFA_Element::DateTime:
        expected_ui_child_type = XFA_Element::DateTimeEdit;
        break;
      case XFA_Element::Image:
        expected_ui_child_type = XFA_Element::ImageEdit;
        widget_type = XFA_FFWidgetType::kImage;
        break;
      case XFA_Element::Arc:
        expected_ui_child_type = XFA_Element::DefaultUi;
        widget_type = XFA_FFWidgetType::kArc;
        break;
      case XFA_Element::Line:
        expected_ui_child_type = XFA_Element::DefaultUi;
        widget_type = XFA_FFWidgetType::kLine;
        break;
      case XFA_Element::Rectangle:
        expected_ui_child_type = XFA_Element::DefaultUi;
        widget_type = XFA_FFWidgetType::kRectangle;
        break;
      default:
        break;
    }
  }

  if (eType == XFA_Element::Draw) {
    if (pUIChild && pUIChild->GetElementType() == XFA_Element::TextEdit) {
      widget_type = XFA_FFWidgetType::kText;
    } else if (pUIChild &&
               pUIChild->GetElementType() == XFA_Element::ImageEdit) {
      widget_type = XFA_FFWidgetType::kImage;
    } else if (widget_type == XFA_FFWidgetType::kNone) {
      widget_type = XFA_FFWidgetType::kText;
    }
  } else if (eType == XFA_Element::Field) {
    if (pUIChild && pUIChild->GetElementType() == XFA_Element::DefaultUi) {
      widget_type = XFA_FFWidgetType::kTextEdit;
    } else if (pUIChild) {
      widget_type = pUIChild->GetDefaultFFWidgetType();
    } else if (expected_ui_child_type == XFA_Element::Unknown) {
      widget_type = XFA_FFWidgetType::kTextEdit;
    }
  } else {
    NOTREACHED();
  }

  if (!pUIChild) {
    if (expected_ui_child_type == XFA_Element::Unknown) {
      expected_ui_child_type = XFA_Element::TextEdit;
    }
    pUIChild = pUI->JSObject()->GetOrCreateProperty<CXFA_Node>(
        0, expected_ui_child_type);
  }

  CreateValueNodeIfNeeded(value, pUIChild);
  return {widget_type, pUI};
}

XFA_FFWidgetType CXFA_Node::GetDefaultFFWidgetType() const {
  NOTREACHED();
}

CXFA_Node* CXFA_Node::CreateUINodeIfNeeded(CXFA_Ui* ui, XFA_Element type) {
  return ui->JSObject()->GetOrCreateProperty<CXFA_Node>(0, type);
}

void CXFA_Node::CreateValueNodeIfNeeded(CXFA_Value* value,
                                        CXFA_Node* pUIChild) {
  // Value nodes only have one child. If we have one already we're done.
  if (value->GetFirstChild()) {
    return;
  }

  // Create the Value node for our UI if needed.
  XFA_Element valueType = pUIChild->GetValueNodeType();
  if (pUIChild->GetElementType() == XFA_Element::CheckButton) {
    CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
    if (pItems) {
      CXFA_Node* pItem =
          pItems->GetChild<CXFA_Node>(0, XFA_Element::Unknown, false);
      if (pItem) {
        valueType = pItem->GetElementType();
      }
    }
  }
  value->JSObject()->GetOrCreateProperty<CXFA_Node>(0, valueType);
}

XFA_Element CXFA_Node::GetValueNodeType() const {
  return XFA_Element::Text;
}

CXFA_Node* CXFA_Node::GetUIChildNode() {
  DCHECK(HasCreatedUIWidget());

  if (ff_widget_type_ != XFA_FFWidgetType::kNone) {
    return ui_ ? ui_->GetFirstChild() : nullptr;
  }

  XFA_Element type = GetElementType();
  if (type == XFA_Element::Field || type == XFA_Element::Draw) {
    std::tie(ff_widget_type_, ui_) = CreateChildUIAndValueNodesIfNeeded();
  } else if (type == XFA_Element::Subform) {
    ff_widget_type_ = XFA_FFWidgetType::kSubform;
  } else if (type == XFA_Element::ExclGroup) {
    ff_widget_type_ = XFA_FFWidgetType::kExclGroup;
  } else {
    NOTREACHED();
  }
  return ui_ ? ui_->GetFirstChild() : nullptr;
}

XFA_FFWidgetType CXFA_Node::GetFFWidgetType() {
  GetUIChildNode();
  return ff_widget_type_;
}

CXFA_Border* CXFA_Node::GetUIBorder() {
  CXFA_Node* pUIChild = GetUIChildNode();
  return pUIChild ? pUIChild->JSObject()->GetProperty<CXFA_Border>(
                        0, XFA_Element::Border)
                  : nullptr;
}

CFX_RectF CXFA_Node::GetUIMargin() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (!pUIChild) {
    return CFX_RectF();
  }

  CXFA_Margin* mgUI =
      pUIChild->JSObject()->GetProperty<CXFA_Margin>(0, XFA_Element::Margin);
  if (!mgUI) {
    return CFX_RectF();
  }

  CXFA_Border* border = GetUIBorder();
  if (border && border->GetPresence() != XFA_AttributeValue::Visible) {
    return CFX_RectF();
  }

  std::optional<float> left = mgUI->TryLeftInset();
  std::optional<float> top = mgUI->TryTopInset();
  std::optional<float> right = mgUI->TryRightInset();
  std::optional<float> bottom = mgUI->TryBottomInset();
  if (border) {
    auto [iType, bVisible, fThickness] = border->Get3DStyle();
    if (!left.has_value() || !top.has_value() || !right.has_value() ||
        !bottom.has_value()) {
      std::vector<CXFA_Stroke*> strokes = border->GetStrokes();
      if (!top.has_value()) {
        top = GetEdgeThickness(strokes, bVisible, 0);
      }
      if (!right.has_value()) {
        right = GetEdgeThickness(strokes, bVisible, 1);
      }
      if (!bottom.has_value()) {
        bottom = GetEdgeThickness(strokes, bVisible, 2);
      }
      if (!left.has_value()) {
        left = GetEdgeThickness(strokes, bVisible, 3);
      }
    }
  }
  return CFX_RectF(left.value_or(0.0), top.value_or(0.0), right.value_or(0.0),
                   bottom.value_or(0.0));
}

std::vector<CXFA_Event*> CXFA_Node::GetEventByActivity(
    XFA_AttributeValue iActivity,
    bool bIsFormReady) {
  std::vector<CXFA_Event*> events;
  for (CXFA_Node* node : GetNodeListForType(XFA_Element::Event)) {
    auto* event = static_cast<CXFA_Event*>(node);
    if (event->GetActivity() != iActivity) {
      continue;
    }

    if (iActivity != XFA_AttributeValue::Ready) {
      events.push_back(event);
      continue;
    }

    WideString wsRef = event->GetRef();
    if (bIsFormReady) {
      if (wsRef == WideStringView(L"$form")) {
        events.push_back(event);
      }
      continue;
    }

    if (wsRef == WideStringView(L"$layout")) {
      events.push_back(event);
    }
  }
  return events;
}

void CXFA_Node::ResetData() {
  WideString wsValue;
  switch (GetFFWidgetType()) {
    case XFA_FFWidgetType::kImageEdit: {
      CXFA_Value* imageValue = GetDefaultValueIfExists();
      CXFA_Image* image = imageValue ? imageValue->GetImageIfExists() : nullptr;
      WideString wsContentType, wsHref;
      if (image) {
        wsValue = image->GetContent();
        wsContentType = image->GetContentType();
        wsHref = image->GetHref();
      }
      SetImageEdit(wsContentType, wsHref, wsValue);
      break;
    }
    case XFA_FFWidgetType::kExclGroup: {
      CXFA_Node* pNextChild = GetFirstContainerChild();
      while (pNextChild) {
        CXFA_Node* pChild = pNextChild;
        if (!pChild->IsWidgetReady()) {
          continue;
        }

        bool done = false;
        if (wsValue.IsEmpty()) {
          CXFA_Value* defValue = pChild->GetDefaultValueIfExists();
          if (defValue) {
            wsValue = defValue->GetChildValueContent();
            SetValue(XFA_ValuePicture::kRaw, wsValue);
            pChild->SetValue(XFA_ValuePicture::kRaw, wsValue);
            done = true;
          }
        }
        if (!done) {
          CXFA_Items* pItems =
              pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
          if (!pItems) {
            continue;
          }

          WideString itemText;
          if (pItems->CountChildren(XFA_Element::Unknown, false) > 1) {
            itemText =
                pItems->GetChild<CXFA_Node>(1, XFA_Element::Unknown, false)
                    ->JSObject()
                    ->GetContent(false);
          }
          pChild->SetValue(XFA_ValuePicture::kRaw, itemText);
        }
        pNextChild = pChild->GetNextContainerSibling();
      }
      break;
    }
    case XFA_FFWidgetType::kChoiceList:
      ClearAllSelections();
      [[fallthrough]];
    default: {
      CXFA_Value* defValue = GetDefaultValueIfExists();
      if (defValue) {
        wsValue = defValue->GetChildValueContent();
      }

      SetValue(XFA_ValuePicture::kRaw, wsValue);
      break;
    }
  }
}

void CXFA_Node::SetImageEdit(const WideString& wsContentType,
                             const WideString& wsHref,
                             const WideString& wsData) {
  CXFA_Value* formValue = GetFormValueIfExists();
  CXFA_Image* image = formValue ? formValue->GetImageIfExists() : nullptr;
  if (image) {
    image->SetContentType(WideString(wsContentType));
    image->SetHref(wsHref);
  }

  JSObject()->SetContent(wsData, GetFormatDataValue(wsData), true, false, true);

  CXFA_Node* pBind = GetBindData();
  if (!pBind) {
    if (image) {
      image->SetTransferEncoding(XFA_AttributeValue::Base64);
    }
    return;
  }
  pBind->JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType);
  CXFA_Node* pHrefNode = pBind->GetFirstChild();
  if (pHrefNode) {
    pHrefNode->JSObject()->SetCData(XFA_Attribute::Value, wsHref);
    return;
  }
  CFX_XMLElement* pElement = ToXMLElement(pBind->GetXMLMappingNode());
  pElement->SetAttribute(L"href", wsHref);
}

void CXFA_Node::CalcCaptionSize(CXFA_FFDoc* doc, CFX_SizeF* pszCap) {
  CXFA_Caption* caption = GetCaptionIfExists();
  if (!caption || !caption->IsVisible()) {
    return;
  }

  LoadCaption(doc);

  const float fCapReserve = caption->GetReserve();
  const XFA_AttributeValue iCapPlacement = caption->GetPlacementType();
  const bool bReserveExit = fCapReserve > 0.01;
  const bool bVert = iCapPlacement == XFA_AttributeValue::Top ||
                     iCapPlacement == XFA_AttributeValue::Bottom;
  CXFA_TextLayout* pCapTextLayout =
      layout_data_->AsFieldLayoutData()->cap_text_layout_;
  if (pCapTextLayout) {
    if (!bVert && GetFFWidgetType() != XFA_FFWidgetType::kButton) {
      pszCap->width = fCapReserve;
    }

    CFX_SizeF minSize;
    *pszCap = pCapTextLayout->CalcSize(minSize, *pszCap);
    if (bReserveExit) {
      bVert ? pszCap->height = fCapReserve : pszCap->width = fCapReserve;
    }
  } else {
    float fFontSize = 10.0f;
    CXFA_Font* font = caption->GetFontIfExists();
    if (font) {
      fFontSize = font->GetFontSize();
    } else {
      CXFA_Font* widgetfont = GetFontIfExists();
      if (widgetfont) {
        fFontSize = widgetfont->GetFontSize();
      }
    }

    if (bVert) {
      pszCap->height = fCapReserve > 0 ? fCapReserve : fFontSize;
    } else {
      pszCap->width = fCapReserve > 0 ? fCapReserve : 0;
      pszCap->height = fFontSize;
    }
  }

  CXFA_Margin* captionMargin = caption->GetMarginIfExists();
  if (!captionMargin) {
    return;
  }

  float fLeftInset = captionMargin->GetLeftInset();
  float fTopInset = captionMargin->GetTopInset();
  float fRightInset = captionMargin->GetRightInset();
  float fBottomInset = captionMargin->GetBottomInset();
  if (bReserveExit) {
    bVert ? (pszCap->width += fLeftInset + fRightInset)
          : (pszCap->height += fTopInset + fBottomInset);
  } else {
    pszCap->width += fLeftInset + fRightInset;
    pszCap->height += fTopInset + fBottomInset;
  }
}

void CXFA_Node::CalculateFieldAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
  CFX_SizeF szCap;
  CalcCaptionSize(doc, &szCap);

  CFX_RectF rtUIMargin = GetUIMargin();
  pSize->width += rtUIMargin.left + rtUIMargin.width;
  pSize->height += rtUIMargin.top + rtUIMargin.height;
  if (szCap.width > 0 && szCap.height > 0) {
    CXFA_Caption* caption = GetCaptionIfExists();
    XFA_AttributeValue placement = caption
                                       ? caption->GetPlacementType()
                                       : CXFA_Caption::kDefaultPlacementType;
    switch (placement) {
      case XFA_AttributeValue::Left:
      case XFA_AttributeValue::Right:
      case XFA_AttributeValue::Inline: {
        pSize->width += szCap.width;
        pSize->height = std::max(pSize->height, szCap.height);
      } break;
      case XFA_AttributeValue::Top:
      case XFA_AttributeValue::Bottom: {
        pSize->height += szCap.height;
        pSize->width = std::max(pSize->width, szCap.width);
        break;
      }
      default:
        break;
    }
  }
  CalculateWidgetAutoSize(pSize);
}

void CXFA_Node::CalculateWidgetAutoSize(CFX_SizeF* pSize) {
  CXFA_Margin* margin = GetMarginIfExists();
  if (margin) {
    pSize->width += margin->GetLeftInset() + margin->GetRightInset();
    pSize->height += margin->GetTopInset() + margin->GetBottomInset();
  }

  CXFA_Para* para = GetParaIfExists();
  if (para) {
    pSize->width += para->GetMarginLeft() + para->GetTextIndent();
  }

  std::optional<float> width = TryWidth();
  if (width.has_value()) {
    pSize->width = width.value();
  } else {
    std::optional<float> min = TryMinWidth();
    if (min.has_value()) {
      pSize->width = std::max(pSize->width, min.value());
    }

    std::optional<float> max = TryMaxWidth();
    if (max.has_value() && max.value() > 0) {
      pSize->width = std::min(pSize->width, max.value());
    }
  }

  std::optional<float> height = TryHeight();
  if (height.has_value()) {
    pSize->height = height.value();
  } else {
    std::optional<float> min = TryMinHeight();
    if (min.has_value()) {
      pSize->height = std::max(pSize->height, min.value());
    }

    std::optional<float> max = TryMaxHeight();
    if (max.has_value() && max.value() > 0) {
      pSize->height = std::min(pSize->height, max.value());
    }
  }
}

void CXFA_Node::CalculateTextContentSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
  float fFontSize = GetFontSize();
  WideString wsText = GetValue(XFA_ValuePicture::kDisplay);
  if (wsText.IsEmpty()) {
    pSize->height += fFontSize;
    return;
  }

  if (wsText.Back() == L'\n') {
    wsText += L'\n';
  }

  CXFA_FieldLayoutData* layoutData = layout_data_->AsFieldLayoutData();
  if (!layoutData->text_out_) {
    layoutData->text_out_ = std::make_unique<CFDE_TextOut>();
    CFDE_TextOut* pTextOut = layoutData->text_out_.get();
    pTextOut->SetFont(GetFGASFont(doc));
    pTextOut->SetFontSize(fFontSize);
    pTextOut->SetLineBreakTolerance(fFontSize * 0.2f);
    pTextOut->SetLineSpace(GetLineHeight());

    FDE_TextStyle dwStyles;
    dwStyles.last_line_height_ = true;
    if (GetFFWidgetType() == XFA_FFWidgetType::kTextEdit && IsMultiLine()) {
      dwStyles.line_wrap_ = true;
    }

    pTextOut->SetStyles(dwStyles);
  }
  layoutData->text_out_->CalcLogicSize(wsText.AsStringView(), pSize);
}

void CXFA_Node::CalculateTextEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
  if (pSize->width > 0) {
    CFX_SizeF szOrz = *pSize;
    CFX_SizeF szCap;
    CalcCaptionSize(doc, &szCap);
    bool bCapExit = szCap.width > 0.01 && szCap.height > 0.01;
    XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown;
    if (bCapExit) {
      CXFA_Caption* caption = GetCaptionIfExists();
      iCapPlacement = caption ? caption->GetPlacementType()
                              : CXFA_Caption::kDefaultPlacementType;
      switch (iCapPlacement) {
        case XFA_AttributeValue::Left:
        case XFA_AttributeValue::Right:
        case XFA_AttributeValue::Inline: {
          pSize->width -= szCap.width;
          break;
        }
        default:
          break;
      }
    }
    CFX_RectF rtUIMargin = GetUIMargin();
    pSize->width -= rtUIMargin.left + rtUIMargin.width;
    CXFA_Margin* margin = GetMarginIfExists();
    if (margin) {
      pSize->width -= margin->GetLeftInset() + margin->GetRightInset();
    }

    CalculateTextContentSize(doc, pSize);
    pSize->height += rtUIMargin.top + rtUIMargin.height;
    if (bCapExit) {
      switch (iCapPlacement) {
        case XFA_AttributeValue::Left:
        case XFA_AttributeValue::Right:
        case XFA_AttributeValue::Inline: {
          pSize->height = std::max(pSize->height, szCap.height);
        } break;
        case XFA_AttributeValue::Top:
        case XFA_AttributeValue::Bottom: {
          pSize->height += szCap.height;
          break;
        }
        default:
          break;
      }
    }
    pSize->width = szOrz.width;
    CalculateWidgetAutoSize(pSize);
    return;
  }
  CalculateTextContentSize(doc, pSize);
  CalculateFieldAutoSize(doc, pSize);
}

void CXFA_Node::CalculateCheckButtonAutoSize(CXFA_FFDoc* doc,
                                             CFX_SizeF* pSize) {
  float fCheckSize = GetCheckButtonSize();
  *pSize = CFX_SizeF(fCheckSize, fCheckSize);
  CalculateFieldAutoSize(doc, pSize);
}

void CXFA_Node::CalculatePushButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
  CalcCaptionSize(doc, pSize);
  CalculateWidgetAutoSize(pSize);
}

CFX_SizeF CXFA_Node::CalculateImageSize(float img_width,
                                        float img_height,
                                        const CFX_Size& dpi) {
  CFX_RectF rtImage(0, 0, XFA_UnitPx2Pt(img_width, dpi.width),
                    XFA_UnitPx2Pt(img_height, dpi.height));

  CFX_RectF rtFit;
  std::optional<float> width = TryWidth();
  if (width.has_value()) {
    rtFit.width = width.value();
    GetWidthWithoutMargin(rtFit.width);
  } else {
    rtFit.width = rtImage.width;
  }

  std::optional<float> height = TryHeight();
  if (height.has_value()) {
    rtFit.height = height.value();
    GetHeightWithoutMargin(rtFit.height);
  } else {
    rtFit.height = rtImage.height;
  }

  return rtFit.Size();
}

void CXFA_Node::CalculateImageAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
  if (!GetLayoutImage()) {
    LoadLayoutImage(doc);
  }

  pSize->clear();
  RetainPtr<CFX_DIBitmap> pBitmap = GetLayoutImage();
  if (pBitmap) {
    *pSize = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
                                GetLayoutImageDpi());
  }
  CalculateWidgetAutoSize(pSize);
}

void CXFA_Node::CalculateImageEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
  if (!GetEditImage()) {
    LoadEditImage(doc);
  }
  pSize->clear();
  RetainPtr<CFX_DIBitmap> pBitmap = GetEditImage();
  if (pBitmap) {
    *pSize = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
                                GetEditImageDpi());
  }
  CalculateFieldAutoSize(doc, pSize);
}

bool CXFA_Node::LoadLayoutImage(CXFA_FFDoc* doc) {
  InitLayoutData(doc);
  return layout_data_->AsImageLayoutData()->LoadImageData(doc, this);
}

bool CXFA_Node::LoadEditImage(CXFA_FFDoc* doc) {
  InitLayoutData(doc);
  return layout_data_->AsFieldLayoutData()->AsImageEditData()->LoadImageData(
      doc, this);
}

CFX_Size CXFA_Node::GetLayoutImageDpi() const {
  return layout_data_->AsImageLayoutData()->GetDpi();
}

CFX_Size CXFA_Node::GetEditImageDpi() const {
  CXFA_ImageEditData* pData =
      layout_data_->AsFieldLayoutData()->AsImageEditData();
  return pData->GetDpi();
}

float CXFA_Node::CalculateWidgetAutoWidth(float fWidthCalc) {
  CXFA_Margin* margin = GetMarginIfExists();
  if (margin) {
    fWidthCalc += margin->GetLeftInset() + margin->GetRightInset();
  }

  std::optional<float> min = TryMinWidth();
  if (min.has_value()) {
    fWidthCalc = std::max(fWidthCalc, min.value());
  }

  std::optional<float> max = TryMaxWidth();
  if (max.has_value() && max.value() > 0) {
    fWidthCalc = std::min(fWidthCalc, max.value());
  }

  return fWidthCalc;
}

float CXFA_Node::GetWidthWithoutMargin(float fWidthCalc) const {
  CXFA_Margin* margin = GetMarginIfExists();
  if (margin) {
    fWidthCalc -= margin->GetLeftInset() + margin->GetRightInset();
  }
  return fWidthCalc;
}

float CXFA_Node::CalculateWidgetAutoHeight(float fHeightCalc) {
  CXFA_Margin* margin = GetMarginIfExists();
  if (margin) {
    fHeightCalc += margin->GetTopInset() + margin->GetBottomInset();
  }

  std::optional<float> min = TryMinHeight();
  if (min.has_value()) {
    fHeightCalc = std::max(fHeightCalc, min.value());
  }

  std::optional<float> max = TryMaxHeight();
  if (max.has_value() && max.value() > 0) {
    fHeightCalc = std::min(fHeightCalc, max.value());
  }

  return fHeightCalc;
}

float CXFA_Node::GetHeightWithoutMargin(float fHeightCalc) const {
  CXFA_Margin* margin = GetMarginIfExists();
  if (margin) {
    fHeightCalc -= margin->GetTopInset() + margin->GetBottomInset();
  }
  return fHeightCalc;
}

void CXFA_Node::StartWidgetLayout(CXFA_FFDoc* doc,
                                  float* pCalcWidth,
                                  float* pCalcHeight) {
  InitLayoutData(doc);

  if (GetFFWidgetType() == XFA_FFWidgetType::kText) {
    layout_data_->SetWidgetHeight(TryHeight().value_or(-1));
    StartTextLayout(doc, pCalcWidth, pCalcHeight);
    return;
  }
  if (*pCalcWidth > 0 && *pCalcHeight > 0) {
    return;
  }

  layout_data_->SetWidgetHeight(-1.0f);
  float fWidth = 0;
  if (*pCalcWidth > 0 && *pCalcHeight < 0) {
    std::optional<float> height = TryHeight();
    if (height.has_value()) {
      *pCalcHeight = height.value();
    } else {
      CFX_SizeF size = CalculateAccWidthAndHeight(doc, *pCalcWidth);
      *pCalcWidth = size.width;
      *pCalcHeight = size.height;
    }
    layout_data_->SetWidgetHeight(*pCalcHeight);
    return;
  }
  if (*pCalcWidth < 0 && *pCalcHeight < 0) {
    std::optional<float> height;
    std::optional<float> width = TryWidth();
    if (width.has_value()) {
      fWidth = width.value();
      height = TryHeight();
      if (height.has_value()) {
        *pCalcHeight = height.value();
      }
    }
    if (!width.has_value() || !height.has_value()) {
      CFX_SizeF size = CalculateAccWidthAndHeight(doc, fWidth);
      *pCalcWidth = size.width;
      *pCalcHeight = size.height;
    } else {
      *pCalcWidth = fWidth;
    }
  }
  layout_data_->SetWidgetHeight(*pCalcHeight);
}

CFX_SizeF CXFA_Node::CalculateAccWidthAndHeight(CXFA_FFDoc* doc, float fWidth) {
  CFX_SizeF sz(fWidth, layout_data_->GetWidgetHeight());
  switch (GetFFWidgetType()) {
    case XFA_FFWidgetType::kBarcode:
    case XFA_FFWidgetType::kChoiceList:
    case XFA_FFWidgetType::kSignature:
      CalculateFieldAutoSize(doc, &sz);
      break;
    case XFA_FFWidgetType::kImageEdit:
      CalculateImageEditAutoSize(doc, &sz);
      break;
    case XFA_FFWidgetType::kButton:
      CalculatePushButtonAutoSize(doc, &sz);
      break;
    case XFA_FFWidgetType::kCheckButton:
      CalculateCheckButtonAutoSize(doc, &sz);
      break;
    case XFA_FFWidgetType::kDateTimeEdit:
    case XFA_FFWidgetType::kNumericEdit:
    case XFA_FFWidgetType::kPasswordEdit:
    case XFA_FFWidgetType::kTextEdit:
      CalculateTextEditAutoSize(doc, &sz);
      break;
    case XFA_FFWidgetType::kImage:
      CalculateImageAutoSize(doc, &sz);
      break;
    case XFA_FFWidgetType::kArc:
    case XFA_FFWidgetType::kLine:
    case XFA_FFWidgetType::kRectangle:
    case XFA_FFWidgetType::kSubform:
    case XFA_FFWidgetType::kExclGroup:
      CalculateWidgetAutoSize(&sz);
      break;
    case XFA_FFWidgetType::kText:
    case XFA_FFWidgetType::kNone:
      break;
  }
  layout_data_->SetWidgetHeight(sz.height);
  return sz;
}

std::optional<float> CXFA_Node::FindSplitPos(CXFA_FFDocView* pDocView,
                                             size_t szBlockIndex,
                                             float fCalcHeight) {
  if (!HasCreatedUIWidget()) {
    return std::nullopt;
  }

  if (GetFFWidgetType() == XFA_FFWidgetType::kSubform) {
    return std::nullopt;
  }

  switch (GetFFWidgetType()) {
    case XFA_FFWidgetType::kText:
    case XFA_FFWidgetType::kTextEdit:
    case XFA_FFWidgetType::kNumericEdit:
    case XFA_FFWidgetType::kPasswordEdit:
      break;
    default:
      return 0.0f;
  }

  float fTopInset = 0;
  float fBottomInset = 0;
  if (szBlockIndex == 0) {
    CXFA_Margin* margin = GetMarginIfExists();
    if (margin) {
      fTopInset = margin->GetTopInset();
      fBottomInset = margin->GetBottomInset();
    }

    CFX_RectF rtUIMargin = GetUIMargin();
    fTopInset += rtUIMargin.top;
    fBottomInset += rtUIMargin.width;
  }
  if (GetFFWidgetType() == XFA_FFWidgetType::kText) {
    float fHeight = fCalcHeight;
    if (szBlockIndex == 0) {
      fCalcHeight -= fTopInset;
      fCalcHeight = std::max(fCalcHeight, 0.0f);
    }
    CXFA_TextLayout* pTextLayout =
        layout_data_->AsTextLayoutData()->GetTextLayout();
    fCalcHeight = pTextLayout->DoSplitLayout(
        szBlockIndex, fCalcHeight, layout_data_->GetWidgetHeight() - fTopInset);
    if (fCalcHeight != 0) {
      if (szBlockIndex == 0) {
        fCalcHeight += fTopInset;
      }
      if (fabs(fHeight - fCalcHeight) < kXFAWidgetPrecision) {
        return std::nullopt;
      }
    }
    return fCalcHeight;
  }

  XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown;
  float fCapReserve = 0;
  if (szBlockIndex == 0) {
    CXFA_Caption* caption = GetCaptionIfExists();
    if (caption && !caption->IsHidden()) {
      iCapPlacement = caption->GetPlacementType();
      fCapReserve = caption->GetReserve();
    }
    if (iCapPlacement == XFA_AttributeValue::Top &&
        fCalcHeight < fCapReserve + fTopInset) {
      return 0.0f;
    }
    if (iCapPlacement == XFA_AttributeValue::Bottom &&
        layout_data_->GetWidgetHeight() - fCapReserve - fBottomInset) {
      return 0.0f;
    }
    if (iCapPlacement != XFA_AttributeValue::Top) {
      fCapReserve = 0;
    }
  }
  CXFA_FieldLayoutData* pFieldData = layout_data_->AsFieldLayoutData();
  int32_t iLinesCount = 0;
  float fHeight = layout_data_->GetWidgetHeight();
  if (GetValue(XFA_ValuePicture::kDisplay).IsEmpty()) {
    iLinesCount = 1;
  } else {
    if (!pFieldData->text_out_) {
      CFX_SizeF size = CalculateAccWidthAndHeight(pDocView->GetDoc(),
                                                  TryWidth().value_or(0));
      fHeight = size.height;
    }

    iLinesCount = pFieldData->text_out_->GetTotalLines();
  }
  std::vector<float>* pFieldArray = &pFieldData->field_split_array_;
  size_t szFieldSplitCount = pFieldArray->size();
  if (szFieldSplitCount < szBlockIndex * 3) {
    return std::nullopt;
  }

  for (size_t i = 0; i < szBlockIndex * 3; i += 3) {
    iLinesCount -= static_cast<int32_t>((*pFieldArray)[i + 1]);
    fHeight -= (*pFieldArray)[i + 2];
  }
  if (iLinesCount == 0) {
    return std::nullopt;
  }

  float fLineHeight = GetLineHeight();
  float fFontSize = GetFontSize();
  float fTextHeight = iLinesCount * fLineHeight - fLineHeight + fFontSize;
  float fSpaceAbove = 0;
  float fStartOffset = 0;
  if (fHeight > 0.1f && szBlockIndex == 0) {
    fStartOffset = fTopInset;
    fHeight -= (fTopInset + fBottomInset);
    CXFA_Para* para = GetParaIfExists();
    if (para) {
      fSpaceAbove = para->GetSpaceAbove();
      float fSpaceBelow = para->GetSpaceBelow();
      fHeight -= (fSpaceAbove + fSpaceBelow);
      switch (para->GetVerticalAlign()) {
        case XFA_AttributeValue::Top:
          fStartOffset += fSpaceAbove;
          break;
        case XFA_AttributeValue::Middle:
          fStartOffset += ((fHeight - fTextHeight) / 2 + fSpaceAbove);
          break;
        case XFA_AttributeValue::Bottom:
          fStartOffset += (fHeight - fTextHeight + fSpaceAbove);
          break;
        default:
          NOTREACHED();
      }
    }
    if (fStartOffset < 0.1f) {
      fStartOffset = 0;
    }
  }
  if (szBlockIndex > 0) {
    size_t i = szBlockIndex - 1;
    fStartOffset = (*pFieldArray)[i * 3] - (*pFieldArray)[i * 3 + 2];
    if (fStartOffset < 0.1f) {
      fStartOffset = 0;
    }
  }
  if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
    (*pFieldArray)[0] = fStartOffset;
  } else {
    pFieldArray->push_back(fStartOffset);
  }

  XFA_VERSION version = pDocView->GetDoc()->GetXFADoc()->GetCurVersionMode();
  bool bCanSplitNoContent = false;
  auto value = GetParent()->JSObject()->TryEnum(XFA_Attribute::Layout, true);
  XFA_AttributeValue eLayoutMode = value.value_or(XFA_AttributeValue::Position);
  if ((eLayoutMode == XFA_AttributeValue::Position ||
       eLayoutMode == XFA_AttributeValue::Tb ||
       eLayoutMode == XFA_AttributeValue::Row ||
       eLayoutMode == XFA_AttributeValue::Table) &&
      version > XFA_VERSION_208) {
    bCanSplitNoContent = true;
  }
  if ((eLayoutMode == XFA_AttributeValue::Tb ||
       eLayoutMode == XFA_AttributeValue::Row ||
       eLayoutMode == XFA_AttributeValue::Table) &&
      version <= XFA_VERSION_208) {
    if (fStartOffset >= fCalcHeight) {
      return 0.0f;
    }

    bCanSplitNoContent = true;
  }
  if (!bCanSplitNoContent ||
      fCalcHeight - fTopInset - fSpaceAbove < fLineHeight) {
    return 0.0f;
  }

  if (fStartOffset + kXFAWidgetPrecision >= fCalcHeight) {
    if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
      (*pFieldArray)[szBlockIndex * 3 + 1] = 0;
      (*pFieldArray)[szBlockIndex * 3 + 2] = fCalcHeight;
    } else {
      pFieldArray->push_back(0);
      pFieldArray->push_back(fCalcHeight);
    }
    return std::nullopt;
  }

  if (fCalcHeight - fStartOffset < fLineHeight) {
    fCalcHeight = fStartOffset;
    if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
      (*pFieldArray)[szBlockIndex * 3 + 1] = 0;
      (*pFieldArray)[szBlockIndex * 3 + 2] = fCalcHeight;
    } else {
      pFieldArray->push_back(0);
      pFieldArray->push_back(fCalcHeight);
    }
    return fCalcHeight;
  }

  float fTextNum =
      fCalcHeight + kXFAWidgetPrecision - fCapReserve - fStartOffset;
  int32_t iLineNum = static_cast<int32_t>(
      (fTextNum + (fLineHeight - fFontSize)) / fLineHeight);
  if (iLineNum >= iLinesCount) {
    if (fCalcHeight - fStartOffset - fTextHeight >= fFontSize) {
      if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
        (*pFieldArray)[szBlockIndex * 3 + 1] = iLinesCount;
        (*pFieldArray)[szBlockIndex * 3 + 2] = fCalcHeight;
      } else {
        pFieldArray->push_back(iLinesCount);
        pFieldArray->push_back(fCalcHeight);
      }
      return std::nullopt;
    }
    if (fHeight - fStartOffset - fTextHeight < fFontSize) {
      iLineNum -= 1;
      if (iLineNum == 0) {
        return 0.0f;
      }
    } else {
      iLineNum = static_cast<int32_t>(fTextNum / fLineHeight);
    }
  }
  if (iLineNum <= 0) {
    return 0.0f;
  }

  float fSplitHeight = iLineNum * fLineHeight + fCapReserve + fStartOffset;
  if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
    (*pFieldArray)[szBlockIndex * 3 + 1] = iLineNum;
    (*pFieldArray)[szBlockIndex * 3 + 2] = fSplitHeight;
  } else {
    pFieldArray->push_back(iLineNum);
    pFieldArray->push_back(fSplitHeight);
  }
  if (fabs(fSplitHeight - fCalcHeight) < kXFAWidgetPrecision) {
    return std::nullopt;
  }
  return fSplitHeight;
}

void CXFA_Node::InitLayoutData(CXFA_FFDoc* doc) {
  if (layout_data_) {
    return;
  }

  switch (GetFFWidgetType()) {
    case XFA_FFWidgetType::kText:
      layout_data_ = cppgc::MakeGarbageCollected<CXFA_TextLayoutData>(
          doc->GetHeap()->GetAllocationHandle());
      return;
    case XFA_FFWidgetType::kTextEdit:
      layout_data_ = cppgc::MakeGarbageCollected<CXFA_TextEditData>(
          doc->GetHeap()->GetAllocationHandle());
      return;
    case XFA_FFWidgetType::kImage:
      layout_data_ = cppgc::MakeGarbageCollected<CXFA_ImageLayoutData>(
          doc->GetHeap()->GetAllocationHandle());
      return;
    case XFA_FFWidgetType::kImageEdit:
      layout_data_ = cppgc::MakeGarbageCollected<CXFA_ImageEditData>(
          doc->GetHeap()->GetAllocationHandle());
      return;
    default:
      break;
  }
  if (GetElementType() == XFA_Element::Field) {
    layout_data_ = cppgc::MakeGarbageCollected<CXFA_FieldLayoutData>(
        doc->GetHeap()->GetAllocationHandle());
    return;
  }
  layout_data_ = cppgc::MakeGarbageCollected<CXFA_WidgetLayoutData>(
      doc->GetHeap()->GetAllocationHandle());
}

void CXFA_Node::StartTextLayout(CXFA_FFDoc* doc,
                                float* pCalcWidth,
                                float* pCalcHeight) {
  InitLayoutData(doc);

  CXFA_TextLayoutData* pTextLayoutData = layout_data_->AsTextLayoutData();
  pTextLayoutData->LoadText(doc, this);

  CXFA_TextLayout* pTextLayout = pTextLayoutData->GetTextLayout();
  float fTextHeight = 0;
  if (*pCalcWidth > 0 && *pCalcHeight > 0) {
    float fWidth = GetWidthWithoutMargin(*pCalcWidth);
    pTextLayout->StartLayout(fWidth);
    fTextHeight = *pCalcHeight;
    fTextHeight = GetHeightWithoutMargin(fTextHeight);
    pTextLayout->DoLayout(fTextHeight);
    return;
  }
  if (*pCalcWidth > 0 && *pCalcHeight < 0) {
    float fWidth = GetWidthWithoutMargin(*pCalcWidth);
    pTextLayout->StartLayout(fWidth);
  }
  if (*pCalcWidth < 0 && *pCalcHeight < 0) {
    std::optional<float> width = TryWidth();
    if (width.has_value()) {
      pTextLayout->StartLayout(GetWidthWithoutMargin(width.value()));
      *pCalcWidth = width.value();
    } else {
      float fMaxWidth = CalculateWidgetAutoWidth(pTextLayout->StartLayout(-1));
      pTextLayout->StartLayout(GetWidthWithoutMargin(fMaxWidth));
      *pCalcWidth = fMaxWidth;
    }
  }
  if (layout_data_->GetWidgetHeight() < 0) {
    layout_data_->SetWidgetHeight(
        CalculateWidgetAutoHeight(pTextLayout->GetLayoutHeight()));
  }
  fTextHeight = layout_data_->GetWidgetHeight();
  fTextHeight = GetHeightWithoutMargin(fTextHeight);
  pTextLayout->DoLayout(fTextHeight);
  *pCalcHeight = layout_data_->GetWidgetHeight();
}

bool CXFA_Node::LoadCaption(CXFA_FFDoc* doc) {
  InitLayoutData(doc);
  return layout_data_->AsFieldLayoutData()->LoadCaption(doc, this);
}

CXFA_TextLayout* CXFA_Node::GetCaptionTextLayout() {
  return layout_data_ && layout_data_->AsFieldLayoutData()
             ? layout_data_->AsFieldLayoutData()->cap_text_layout_
             : nullptr;
}

CXFA_TextLayout* CXFA_Node::GetTextLayout() {
  return layout_data_ ? layout_data_->AsTextLayoutData()->GetTextLayout()
                      : nullptr;
}

RetainPtr<CFX_DIBitmap> CXFA_Node::GetLayoutImage() {
  return layout_data_ ? layout_data_->AsImageLayoutData()->GetBitmap()
                      : nullptr;
}

RetainPtr<CFX_DIBitmap> CXFA_Node::GetEditImage() {
  return layout_data_
             ? layout_data_->AsFieldLayoutData()->AsImageEditData()->GetBitmap()
             : nullptr;
}

void CXFA_Node::SetLayoutImage(RetainPtr<CFX_DIBitmap> newImage) {
  CXFA_ImageLayoutData* pData = layout_data_->AsImageLayoutData();
  if (pData->GetBitmap() != newImage) {
    pData->SetBitmap(std::move(newImage));
  }
}

void CXFA_Node::SetEditImage(RetainPtr<CFX_DIBitmap> newImage) {
  CXFA_ImageEditData* pData =
      layout_data_->AsFieldLayoutData()->AsImageEditData();
  if (pData->GetBitmap() != newImage) {
    pData->SetBitmap(std::move(newImage));
  }
}

RetainPtr<CFGAS_GEFont> CXFA_Node::GetFGASFont(CXFA_FFDoc* doc) {
  WideString wsFontName = L"Courier";
  uint32_t dwFontStyle = 0;
  CXFA_Font* font = GetFontIfExists();
  if (font) {
    if (font->IsBold()) {
      dwFontStyle |= pdfium::kFontStyleForceBold;
    }
    if (font->IsItalic()) {
      dwFontStyle |= pdfium::kFontStyleItalic;
    }

    wsFontName = font->GetTypeface();
  }
  return doc->GetApp()->GetXFAFontMgr()->GetFont(doc, wsFontName, dwFontStyle);
}

bool CXFA_Node::HasButtonRollover() const {
  const auto* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
  if (!pItems) {
    return false;
  }

  for (CXFA_Node* pText = pItems->GetFirstChild(); pText;
       pText = pText->GetNextSibling()) {
    if (pText->JSObject()
            ->GetCData(XFA_Attribute::Name)
            .EqualsASCII("rollover")) {
      return !pText->JSObject()->GetContent(false).IsEmpty();
    }
  }
  return false;
}

bool CXFA_Node::HasButtonDown() const {
  const auto* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
  if (!pItems) {
    return false;
  }

  for (CXFA_Node* pText = pItems->GetFirstChild(); pText;
       pText = pText->GetNextSibling()) {
    if (pText->JSObject()->GetCData(XFA_Attribute::Name).EqualsASCII("down")) {
      return !pText->JSObject()->GetContent(false).IsEmpty();
    }
  }
  return false;
}

bool CXFA_Node::IsRadioButton() {
  CXFA_Node* pParent = GetParent();
  return pParent && pParent->GetElementType() == XFA_Element::ExclGroup;
}

float CXFA_Node::GetCheckButtonSize() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (pUIChild) {
    return pUIChild->JSObject()->GetMeasureInUnit(XFA_Attribute::Size,
                                                  XFA_Unit::Pt);
  }
  return CXFA_Measurement(10, XFA_Unit::Pt).ToUnit(XFA_Unit::Pt);
}

XFA_CheckState CXFA_Node::GetCheckState() {
  WideString wsValue = GetRawValue();
  if (wsValue.IsEmpty()) {
    return XFA_CheckState::kOff;
  }

  auto* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
  if (!pItems) {
    return XFA_CheckState::kOff;
  }

  CXFA_Node* pText = pItems->GetFirstChild();
  int32_t i = 0;
  while (pText) {
    std::optional<WideString> wsContent =
        pText->JSObject()->TryContent(false, true);
    if (wsContent == wsValue) {
      return static_cast<XFA_CheckState>(i);
    }

    i++;
    pText = pText->GetNextSibling();
  }
  return XFA_CheckState::kOff;
}

void CXFA_Node::SetCheckState(XFA_CheckState eCheckState) {
  CXFA_Node* node = GetExclGroupIfExists();
  if (!node) {
    CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
    if (!pItems) {
      return;
    }

    int32_t i = -1;
    CXFA_Node* pText = pItems->GetFirstChild();
    WideString wsContent;
    while (pText) {
      i++;
      if (i == static_cast<int32_t>(eCheckState)) {
        wsContent = pText->JSObject()->GetContent(false);
        break;
      }
      pText = pText->GetNextSibling();
    }
    SyncValue(wsContent, true);

    return;
  }

  WideString wsValue;
  if (eCheckState != XFA_CheckState::kOff) {
    if (CXFA_Items* pItems =
            GetChild<CXFA_Items>(0, XFA_Element::Items, false)) {
      CXFA_Node* pText = pItems->GetFirstChild();
      if (pText) {
        wsValue = pText->JSObject()->GetContent(false);
      }
    }
  }
  CXFA_Node* pChild = node->GetFirstChild();
  for (; pChild; pChild = pChild->GetNextSibling()) {
    if (pChild->GetElementType() != XFA_Element::Field) {
      continue;
    }

    CXFA_Items* pItem =
        pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
    if (!pItem) {
      continue;
    }

    CXFA_Node* pItemchild = pItem->GetFirstChild();
    if (!pItemchild) {
      continue;
    }

    WideString text = pItemchild->JSObject()->GetContent(false);
    WideString wsChildValue = text;
    if (wsValue != text) {
      pItemchild = pItemchild->GetNextSibling();
      if (pItemchild) {
        wsChildValue = pItemchild->JSObject()->GetContent(false);
      } else {
        wsChildValue.clear();
      }
    }
    pChild->SyncValue(wsChildValue, true);
  }
  node->SyncValue(wsValue, true);
}

CXFA_Node* CXFA_Node::GetSelectedMember() {
  CXFA_Node* pSelectedMember = nullptr;
  WideString wsState = GetRawValue();
  if (wsState.IsEmpty()) {
    return pSelectedMember;
  }

  for (CXFA_Node* pNode = ToNode(GetFirstChild()); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetCheckState() == XFA_CheckState::kOn) {
      pSelectedMember = pNode;
      break;
    }
  }
  return pSelectedMember;
}

CXFA_Node* CXFA_Node::SetSelectedMember(WideStringView wsName) {
  uint32_t nameHash = FX_HashCode_GetW(wsName);
  for (CXFA_Node* pNode = ToNode(GetFirstChild()); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetNameHash() == nameHash) {
      pNode->SetCheckState(XFA_CheckState::kOn);
      return pNode;
    }
  }
  return nullptr;
}

void CXFA_Node::SetSelectedMemberByValue(WideStringView wsValue,
                                         bool bNotify,
                                         bool bScriptModify,
                                         bool bSyncData) {
  WideString wsExclGroup;
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() != XFA_Element::Field) {
      continue;
    }

    CXFA_Items* pItem =
        pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
    if (!pItem) {
      continue;
    }

    CXFA_Node* pItemchild = pItem->GetFirstChild();
    if (!pItemchild) {
      continue;
    }

    WideString wsChildValue = pItemchild->JSObject()->GetContent(false);
    if (wsValue != wsChildValue) {
      pItemchild = pItemchild->GetNextSibling();
      if (pItemchild) {
        wsChildValue = pItemchild->JSObject()->GetContent(false);
      } else {
        wsChildValue.clear();
      }
    } else {
      wsExclGroup = wsValue;
    }
    pNode->JSObject()->SetContent(wsChildValue, wsChildValue, bNotify,
                                  bScriptModify, false);
  }
  JSObject()->SetContent(wsExclGroup, wsExclGroup, bNotify, bScriptModify,
                         bSyncData);
}

CXFA_Node* CXFA_Node::GetExclGroupFirstMember() {
  CXFA_Node* pNode = GetFirstChild();
  while (pNode) {
    if (pNode->GetElementType() == XFA_Element::Field) {
      return pNode;
    }

    pNode = pNode->GetNextSibling();
  }
  return nullptr;
}

CXFA_Node* CXFA_Node::GetExclGroupNextMember(CXFA_Node* pNode) {
  if (!pNode) {
    return nullptr;
  }

  CXFA_Node* pNodeField = pNode->GetNextSibling();
  while (pNodeField) {
    if (pNodeField->GetElementType() == XFA_Element::Field) {
      return pNodeField;
    }

    pNodeField = pNodeField->GetNextSibling();
  }
  return nullptr;
}

bool CXFA_Node::IsChoiceListCommitOnSelect() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (pUIChild) {
    return pUIChild->JSObject()->GetEnum(XFA_Attribute::CommitOn) ==
           XFA_AttributeValue::Select;
  }
  return true;
}

bool CXFA_Node::IsChoiceListAllowTextEntry() {
  CXFA_Node* pUIChild = GetUIChildNode();
  return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::TextEntry);
}

bool CXFA_Node::IsChoiceListMultiSelect() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (pUIChild) {
    return pUIChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
           XFA_AttributeValue::MultiSelect;
  }
  return false;
}

bool CXFA_Node::IsListBox() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (!pUIChild) {
    return false;
  }

  XFA_AttributeValue attr = pUIChild->JSObject()->GetEnum(XFA_Attribute::Open);
  return attr == XFA_AttributeValue::Always ||
         attr == XFA_AttributeValue::MultiSelect;
}

size_t CXFA_Node::CountChoiceListItems(bool bSaveValue) {
  std::vector<CXFA_Node*> pItems;
  int32_t iCount = 0;
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() != XFA_Element::Items) {
      continue;
    }
    iCount++;
    pItems.push_back(pNode);
    if (iCount == 2) {
      break;
    }
  }
  if (iCount == 0) {
    return 0;
  }

  CXFA_Node* pItem = pItems[0];
  if (iCount > 1) {
    bool bItemOneHasSave =
        pItems[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
    bool bItemTwoHasSave =
        pItems[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave) {
      pItem = pItems[1];
    }
  }
  return pItem->CountChildren(XFA_Element::Unknown, false);
}

std::optional<WideString> CXFA_Node::GetChoiceListItem(int32_t nIndex,
                                                       bool bSaveValue) {
  std::vector<CXFA_Node*> pItemsArray;
  int32_t iCount = 0;
  for (CXFA_Node* pNode = GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() != XFA_Element::Items) {
      continue;
    }

    ++iCount;
    pItemsArray.push_back(pNode);
    if (iCount == 2) {
      break;
    }
  }
  if (iCount == 0) {
    return std::nullopt;
  }

  CXFA_Node* pItems = pItemsArray[0];
  if (iCount > 1) {
    bool bItemOneHasSave =
        pItemsArray[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
    bool bItemTwoHasSave =
        pItemsArray[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave) {
      pItems = pItemsArray[1];
    }
  }
  if (!pItems) {
    return std::nullopt;
  }

  CXFA_Node* pItem =
      pItems->GetChild<CXFA_Node>(nIndex, XFA_Element::Unknown, false);
  if (!pItem) {
    return std::nullopt;
  }

  return pItem->JSObject()->GetContent(false);
}

std::vector<WideString> CXFA_Node::GetChoiceListItems(bool bSaveValue) {
  std::vector<CXFA_Node*> items;
  for (CXFA_Node* pNode = GetFirstChild(); pNode && items.size() < 2;
       pNode = pNode->GetNextSibling()) {
    if (pNode->GetElementType() == XFA_Element::Items) {
      items.push_back(pNode);
    }
  }
  if (items.empty()) {
    return std::vector<WideString>();
  }

  CXFA_Node* pItem = items.front();
  if (items.size() > 1) {
    bool bItemOneHasSave =
        items[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
    bool bItemTwoHasSave =
        items[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave) {
      pItem = items[1];
    }
  }

  std::vector<WideString> wsTextArray;
  for (CXFA_Node* pNode = pItem->GetFirstChild(); pNode;
       pNode = pNode->GetNextSibling()) {
    wsTextArray.emplace_back(pNode->JSObject()->GetContent(false));
  }
  return wsTextArray;
}

int32_t CXFA_Node::CountSelectedItems() {
  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
  if (IsListBox() || !IsChoiceListAllowTextEntry()) {
    return fxcrt::CollectionSize<int32_t>(wsValueArray);
  }

  int32_t iSelected = 0;
  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
  for (const auto& value : wsValueArray) {
    if (pdfium::Contains(wsSaveTextArray, value)) {
      iSelected++;
    }
  }
  return iSelected;
}

int32_t CXFA_Node::GetSelectedItem(int32_t nIndex) {
  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
  if (!fxcrt::IndexInBounds(wsValueArray, nIndex)) {
    return -1;
  }

  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
  auto it = std::ranges::find(wsSaveTextArray, wsValueArray[nIndex]);
  return it != wsSaveTextArray.end()
             ? pdfium::checked_cast<int32_t>(it - wsSaveTextArray.begin())
             : -1;
}

std::vector<int32_t> CXFA_Node::GetSelectedItems() {
  std::vector<int32_t> iSelArray;
  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
  for (const auto& value : wsValueArray) {
    auto it = std::ranges::find(wsSaveTextArray, value);
    if (it != wsSaveTextArray.end()) {
      iSelArray.push_back(
          pdfium::checked_cast<int32_t>(it - wsSaveTextArray.begin()));
    }
  }
  return iSelArray;
}

std::vector<WideString> CXFA_Node::GetSelectedItemsValue() {
  WideString wsValue = GetRawValue();
  if (IsChoiceListMultiSelect()) {
    return fxcrt::Split(wsValue, L'\n');
  }

  std::vector<WideString> wsSelTextArray;
  wsSelTextArray.push_back(wsValue);
  return wsSelTextArray;
}

bool CXFA_Node::GetItemState(int32_t nIndex) {
  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
  return fxcrt::IndexInBounds(wsSaveTextArray, nIndex) &&
         pdfium::Contains(GetSelectedItemsValue(), wsSaveTextArray[nIndex]);
}

void CXFA_Node::SetItemState(int32_t nIndex,
                             bool bSelected,
                             bool bNotify,
                             bool bScriptModify) {
  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
  if (!fxcrt::IndexInBounds(wsSaveTextArray, nIndex)) {
    return;
  }

  int32_t iSel = -1;
  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
  auto value_iter = std::ranges::find(wsValueArray, wsSaveTextArray[nIndex]);
  if (value_iter != wsValueArray.end()) {
    iSel = pdfium::checked_cast<int32_t>(value_iter - wsValueArray.begin());
  }
  if (IsChoiceListMultiSelect()) {
    if (bSelected) {
      if (iSel < 0) {
        WideString wsValue = GetRawValue();
        if (!wsValue.IsEmpty()) {
          wsValue += L"\n";
        }
        wsValue += wsSaveTextArray[nIndex];
        JSObject()->SetContent(wsValue, wsValue, bNotify, bScriptModify, true);
      }
    } else if (iSel >= 0) {
      std::vector<int32_t> iSelArray = GetSelectedItems();
      auto selected_iter = std::ranges::find(iSelArray, nIndex);
      if (selected_iter != iSelArray.end()) {
        iSelArray.erase(selected_iter);
      }
      SetSelectedItems(iSelArray, bNotify, bScriptModify, true);
    }
  } else {
    if (bSelected) {
      if (iSel < 0) {
        WideString wsSaveText = wsSaveTextArray[nIndex];
        JSObject()->SetContent(wsSaveText, GetFormatDataValue(wsSaveText),
                               bNotify, bScriptModify, true);
      }
    } else if (iSel >= 0) {
      JSObject()->SetContent(WideString(), WideString(), bNotify, bScriptModify,
                             true);
    }
  }
}

void CXFA_Node::SetSelectedItems(const std::vector<int32_t>& iSelArray,
                                 bool bNotify,
                                 bool bScriptModify,
                                 bool bSyncData) {
  WideString wsValue;
  int32_t iSize = fxcrt::CollectionSize<int32_t>(iSelArray);
  if (iSize >= 1) {
    std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
    WideString wsItemValue;
    for (int32_t i = 0; i < iSize; i++) {
      wsItemValue = (iSize == 1) ? wsSaveTextArray[iSelArray[i]]
                                 : wsSaveTextArray[iSelArray[i]] + L"\n";
      wsValue += wsItemValue;
    }
  }
  WideString wsFormat(wsValue);
  if (!IsChoiceListMultiSelect()) {
    wsFormat = GetFormatDataValue(wsValue);
  }

  JSObject()->SetContent(wsValue, wsFormat, bNotify, bScriptModify, bSyncData);
}

void CXFA_Node::ClearAllSelections() {
  CXFA_Node* pBind = GetBindData();
  if (!pBind || !IsChoiceListMultiSelect()) {
    SyncValue(WideString(), false);
    return;
  }

  while (CXFA_Node* pChildNode = pBind->GetFirstChild()) {
    pBind->RemoveChildAndNotify(pChildNode, true);
  }
}

void CXFA_Node::InsertItem(const WideString& wsLabel,
                           const WideString& wsValue,
                           bool bNotify) {
  int32_t nIndex = -1;
  WideString wsNewValue(wsValue);
  if (wsNewValue.IsEmpty()) {
    wsNewValue = wsLabel;
  }

  std::vector<CXFA_Node*> listitems;
  for (CXFA_Node* pItem = GetFirstChild(); pItem;
       pItem = pItem->GetNextSibling()) {
    if (pItem->GetElementType() == XFA_Element::Items) {
      listitems.push_back(pItem);
    }
  }
  if (listitems.empty()) {
    CXFA_Node* pItems = CreateSamePacketNode(XFA_Element::Items);
    InsertChildAndNotify(-1, pItems);
    InsertListTextItem(pItems, wsLabel, nIndex);
    CXFA_Node* pSaveItems = CreateSamePacketNode(XFA_Element::Items);
    InsertChildAndNotify(-1, pSaveItems);
    pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false);
    InsertListTextItem(pSaveItems, wsNewValue, nIndex);
  } else if (listitems.size() > 1) {
    for (int32_t i = 0; i < 2; i++) {
      CXFA_Node* pNode = listitems[i];
      bool bHasSave = pNode->JSObject()->GetBoolean(XFA_Attribute::Save);
      if (bHasSave) {
        InsertListTextItem(pNode, wsNewValue, nIndex);
      } else {
        InsertListTextItem(pNode, wsLabel, nIndex);
      }
    }
  } else {
    CXFA_Node* pNode = listitems[0];
    pNode->JSObject()->SetBoolean(XFA_Attribute::Save, false, false);
    pNode->JSObject()->SetEnum(XFA_Attribute::Presence,
                               XFA_AttributeValue::Visible, false);
    CXFA_Node* pSaveItems = CreateSamePacketNode(XFA_Element::Items);
    InsertChildAndNotify(-1, pSaveItems);
    pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false);
    pSaveItems->JSObject()->SetEnum(XFA_Attribute::Presence,
                                    XFA_AttributeValue::Hidden, false);
    CXFA_Node* pListNode = pNode->GetFirstChild();
    int32_t i = 0;
    while (pListNode) {
      InsertListTextItem(pSaveItems, pListNode->JSObject()->GetContent(false),
                         i);
      ++i;

      pListNode = pListNode->GetNextSibling();
    }
    InsertListTextItem(pNode, wsLabel, nIndex);
    InsertListTextItem(pSaveItems, wsNewValue, nIndex);
  }
  if (bNotify) {
    GetDocument()->GetNotify()->OnWidgetListItemAdded(this, wsLabel, nIndex);
  }
}

WideString CXFA_Node::GetItemLabel(WideStringView wsValue) const {
  std::vector<CXFA_Node*> listitems;
  CXFA_Node* pItems = GetFirstChild();
  for (; pItems; pItems = pItems->GetNextSibling()) {
    if (pItems->GetElementType() != XFA_Element::Items) {
      continue;
    }
    listitems.push_back(pItems);
  }

  if (listitems.size() <= 1) {
    return WideString(wsValue);
  }

  CXFA_Node* pLabelItems = listitems[0];
  bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save);
  CXFA_Node* pSaveItems = nullptr;
  if (bSave) {
    pSaveItems = pLabelItems;
    pLabelItems = listitems[1];
  } else {
    pSaveItems = listitems[1];
  }

  int32_t iCount = 0;
  int32_t iSearch = -1;
  for (CXFA_Node* pChildItem = pSaveItems->GetFirstChild(); pChildItem;
       pChildItem = pChildItem->GetNextSibling()) {
    if (pChildItem->JSObject()->GetContent(false) == wsValue) {
      iSearch = iCount;
      break;
    }
    iCount++;
  }
  if (iSearch < 0) {
    return WideString();
  }

  CXFA_Node* pText =
      pLabelItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false);
  return pText ? pText->JSObject()->GetContent(false) : WideString();
}

WideString CXFA_Node::GetItemValue(WideStringView wsLabel) {
  int32_t iCount = 0;
  std::vector<CXFA_Node*> listitems;
  for (CXFA_Node* pItems = GetFirstChild(); pItems;
       pItems = pItems->GetNextSibling()) {
    if (pItems->GetElementType() != XFA_Element::Items) {
      continue;
    }
    iCount++;
    listitems.push_back(pItems);
  }
  if (iCount <= 1) {
    return WideString(wsLabel);
  }

  CXFA_Node* pLabelItems = listitems[0];
  bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save);
  CXFA_Node* pSaveItems = nullptr;
  if (bSave) {
    pSaveItems = pLabelItems;
    pLabelItems = listitems[1];
  } else {
    pSaveItems = listitems[1];
  }
  iCount = 0;

  int32_t iSearch = -1;
  WideString wsContent;
  CXFA_Node* pChildItem = pLabelItems->GetFirstChild();
  for (; pChildItem; pChildItem = pChildItem->GetNextSibling()) {
    if (pChildItem->JSObject()->GetContent(false) == wsLabel) {
      iSearch = iCount;
      break;
    }
    iCount++;
  }
  if (iSearch < 0) {
    return WideString();
  }

  CXFA_Node* pText =
      pSaveItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false);
  return pText ? pText->JSObject()->GetContent(false) : WideString();
}

bool CXFA_Node::DeleteItem(int32_t nIndex, bool bNotify, bool bScriptModify) {
  bool bSetValue = false;
  CXFA_Node* pItems = GetFirstChild();
  for (; pItems; pItems = pItems->GetNextSibling()) {
    if (pItems->GetElementType() != XFA_Element::Items) {
      continue;
    }

    if (nIndex < 0) {
      while (CXFA_Node* pNode = pItems->GetFirstChild()) {
        pItems->RemoveChildAndNotify(pNode, true);
      }
    } else {
      if (!bSetValue && pItems->JSObject()->GetBoolean(XFA_Attribute::Save)) {
        SetItemState(nIndex, false, true, bScriptModify);
        bSetValue = true;
      }
      int32_t i = 0;
      CXFA_Node* pNode = pItems->GetFirstChild();
      while (pNode) {
        if (i == nIndex) {
          pItems->RemoveChildAndNotify(pNode, true);
          break;
        }
        i++;
        pNode = pNode->GetNextSibling();
      }
    }
  }
  if (bNotify) {
    GetDocument()->GetNotify()->OnWidgetListItemRemoved(this, nIndex);
  }
  return true;
}

bool CXFA_Node::IsHorizontalScrollPolicyOff() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (pUIChild) {
    return pUIChild->JSObject()->GetEnum(XFA_Attribute::HScrollPolicy) ==
           XFA_AttributeValue::Off;
  }
  return false;
}

bool CXFA_Node::IsVerticalScrollPolicyOff() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (pUIChild) {
    return pUIChild->JSObject()->GetEnum(XFA_Attribute::VScrollPolicy) ==
           XFA_AttributeValue::Off;
  }
  return false;
}

std::optional<int32_t> CXFA_Node::GetNumberOfCells() {
  CXFA_Node* pUIChild = GetUIChildNode();
  if (!pUIChild) {
    return std::nullopt;
  }

  CXFA_Comb* pNode = pUIChild->GetChild<CXFA_Comb>(0, XFA_Element::Comb, false);
  if (!pNode) {
    return std::nullopt;
  }

  return pNode->JSObject()->GetInteger(XFA_Attribute::NumberOfCells);
}

bool CXFA_Node::IsMultiLine() {
  CXFA_Node* pUIChild = GetUIChildNode();
  return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::MultiLine);
}

std::pair<XFA_Element, int32_t> CXFA_Node::GetMaxChars() const {
  const auto* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
  if (pNode) {
    if (CXFA_Node* pChild = pNode->GetFirstChild()) {
      switch (pChild->GetElementType()) {
        case XFA_Element::Text:
          return {XFA_Element::Text,
                  pChild->JSObject()->GetInteger(XFA_Attribute::MaxChars)};
        case XFA_Element::ExData: {
          int32_t iMax =
              pChild->JSObject()->GetInteger(XFA_Attribute::MaxLength);
          return {XFA_Element::ExData, iMax < 0 ? 0 : iMax};
        }
        default:
          break;
      }
    }
  }
  return {XFA_Element::Unknown, 0};
}

int32_t CXFA_Node::GetFracDigits() const {
  const auto* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
  if (!pNode) {
    return -1;
  }

  const auto* pChild =
      pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
  if (!pChild) {
    return -1;
  }

  return pChild->JSObject()
      ->TryInteger(XFA_Attribute::FracDigits, true)
      .value_or(-1);
}

int32_t CXFA_Node::GetLeadDigits() const {
  const auto* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
  if (!pNode) {
    return -1;
  }

  const auto* pChild =
      pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
  if (!pChild) {
    return -1;
  }

  return pChild->JSObject()
      ->TryInteger(XFA_Attribute::LeadDigits, true)
      .value_or(-1);
}

bool CXFA_Node::SetValue(XFA_ValuePicture eValueType,
                         const WideString& wsValue) {
  if (wsValue.IsEmpty()) {
    SyncValue(wsValue, true);
    return true;
  }

  SetPreNull(IsNull());
  SetIsNull(false);

  WideString wsNewText(wsValue);
  WideString wsPicture = GetPictureContent(eValueType);
  bool bValidate = true;
  bool bSyncData = false;
  CXFA_Node* pNode = GetUIChildNode();
  if (!pNode) {
    return true;
  }

  XFA_Element eType = pNode->GetElementType();
  if (!wsPicture.IsEmpty()) {
    CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
    GCedLocaleIface* pLocale = GetLocale();
    CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
    bValidate =
        widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture);
    if (bValidate) {
      widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsNewText,
                                     wsPicture, pLocale, pLocaleMgr);
      wsNewText = widgetValue.GetValue();
      if (eType == XFA_Element::NumericEdit) {
        wsNewText = NumericLimit(wsNewText);
      }

      bSyncData = true;
    }
  } else if (eType == XFA_Element::NumericEdit) {
    if (!wsNewText.EqualsASCII("0")) {
      wsNewText = NumericLimit(wsNewText);
    }

    bSyncData = true;
  }
  if (eType != XFA_Element::NumericEdit || bSyncData) {
    SyncValue(wsNewText, true);
  }

  return bValidate;
}

WideString CXFA_Node::GetPictureContent(XFA_ValuePicture ePicture) {
  if (ePicture == XFA_ValuePicture::kRaw) {
    return WideString();
  }

  CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
  switch (ePicture) {
    case XFA_ValuePicture::kDisplay: {
      auto* pFormat = GetChild<CXFA_Format>(0, XFA_Element::Format, false);
      if (pFormat) {
        auto* pPicture =
            pFormat->GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
        if (pPicture) {
          std::optional<WideString> picture =
              pPicture->JSObject()->TryContent(false, true);
          if (picture.has_value()) {
            return picture.value();
          }
        }
      }

      LocaleIface* pLocale = GetLocale();
      if (!pLocale) {
        return WideString();
      }

      switch (widgetValue.GetType()) {
        case CXFA_LocaleValue::ValueType::kDate:
          return pLocale->GetDatePattern(
              LocaleIface::DateTimeSubcategory::kMedium);
        case CXFA_LocaleValue::ValueType::kTime:
          return pLocale->GetTimePattern(
              LocaleIface::DateTimeSubcategory::kMedium);
        case CXFA_LocaleValue::ValueType::kDateTime:
          return pLocale->GetDatePattern(
                     LocaleIface::DateTimeSubcategory::kMedium) +
                 L"T" +
                 pLocale->GetTimePattern(
                     LocaleIface::DateTimeSubcategory::kMedium);
        case CXFA_LocaleValue::ValueType::kDecimal:
        case CXFA_LocaleValue::ValueType::kFloat:
        default:
          return WideString();
      }
    }
    case XFA_ValuePicture::kEdit: {
      CXFA_Ui* pUI = GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
      if (pUI) {
        if (CXFA_Picture* pPicture =
                pUI->GetChild<CXFA_Picture>(0, XFA_Element::Picture, false)) {
          std::optional<WideString> picture =
              pPicture->JSObject()->TryContent(false, true);
          if (picture.has_value()) {
            return picture.value();
          }
        }
      }

      LocaleIface* pLocale = GetLocale();
      if (!pLocale) {
        return WideString();
      }

      switch (widgetValue.GetType()) {
        case CXFA_LocaleValue::ValueType::kDate:
          return pLocale->GetDatePattern(
              LocaleIface::DateTimeSubcategory::kShort);
        case CXFA_LocaleValue::ValueType::kTime:
          return pLocale->GetTimePattern(
              LocaleIface::DateTimeSubcategory::kShort);
        case CXFA_LocaleValue::ValueType::kDateTime:
          return pLocale->GetDatePattern(
                     LocaleIface::DateTimeSubcategory::kShort) +
                 L"T" +
                 pLocale->GetTimePattern(
                     LocaleIface::DateTimeSubcategory::kShort);
        default:
          return WideString();
      }
    }
    case XFA_ValuePicture::kDataBind: {
      CXFA_Bind* bind = GetBindIfExists();
      if (bind) {
        return bind->GetPicture();
      }
      break;
    }
    default:
      break;
  }
  return WideString();
}

WideString CXFA_Node::GetValue(XFA_ValuePicture eValueType) {
  WideString wsValue = JSObject()->GetContent(false);

  if (eValueType == XFA_ValuePicture::kDisplay) {
    wsValue = GetItemLabel(wsValue.AsStringView());
  }

  WideString wsPicture = GetPictureContent(eValueType);
  CXFA_Node* pNode = GetUIChildNode();
  if (!pNode) {
    return wsValue;
  }

  switch (pNode->GetElementType()) {
    case XFA_Element::ChoiceList: {
      if (eValueType == XFA_ValuePicture::kDisplay) {
        int32_t iSelItemIndex = GetSelectedItem(0);
        if (iSelItemIndex >= 0) {
          wsValue =
              GetChoiceListItem(iSelItemIndex, false).value_or(WideString());
          wsPicture.clear();
        }
      }
      break;
    }
    case XFA_Element::NumericEdit:
      if (eValueType != XFA_ValuePicture::kRaw && wsPicture.IsEmpty()) {
        LocaleIface* pLocale = GetLocale();
        if (eValueType == XFA_ValuePicture::kDisplay && pLocale) {
          wsValue = FormatNumStr(NormalizeNumStr(wsValue), pLocale);
        }
      }
      break;
    default:
      break;
  }
  if (wsPicture.IsEmpty()) {
    return wsValue;
  }

  GCedLocaleIface* pLocale = GetLocale();
  if (pLocale) {
    CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
    CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
    switch (widgetValue.GetType()) {
      case CXFA_LocaleValue::ValueType::kDate: {
        WideString wsDate, wsTime;
        if (SplitDateTime(wsValue, wsDate, wsTime)) {
          CXFA_LocaleValue date(CXFA_LocaleValue::ValueType::kDate, wsDate,
                                pLocaleMgr);
          if (date.FormatPatterns(wsValue, wsPicture, pLocale, eValueType)) {
            return wsValue;
          }
        }
        break;
      }
      case CXFA_LocaleValue::ValueType::kTime: {
        WideString wsDate, wsTime;
        if (SplitDateTime(wsValue, wsDate, wsTime)) {
          CXFA_LocaleValue time(CXFA_LocaleValue::ValueType::kTime, wsTime,
                                pLocaleMgr);
          if (time.FormatPatterns(wsValue, wsPicture, pLocale, eValueType)) {
            return wsValue;
          }
        }
        break;
      }
      default:
        break;
    }
    widgetValue.FormatPatterns(wsValue, wsPicture, pLocale, eValueType);
  }
  return wsValue;
}

WideString CXFA_Node::GetNormalizeDataValue(const WideString& wsValue) {
  if (wsValue.IsEmpty()) {
    return WideString();
  }

  WideString wsPicture = GetPictureContent(XFA_ValuePicture::kDataBind);
  if (wsPicture.IsEmpty()) {
    return wsValue;
  }

  CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
  GCedLocaleIface* pLocale = GetLocale();
  CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
  if (widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture)) {
    widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsValue, wsPicture,
                                   pLocale, pLocaleMgr);
    return widgetValue.GetValue();
  }
  return wsValue;
}

WideString CXFA_Node::GetFormatDataValue(const WideString& wsValue) {
  if (wsValue.IsEmpty()) {
    return WideString();
  }

  WideString wsPicture = GetPictureContent(XFA_ValuePicture::kDataBind);
  if (wsPicture.IsEmpty()) {
    return wsValue;
  }

  WideString wsFormattedValue = wsValue;
  GCedLocaleIface* pLocale = GetLocale();
  if (pLocale) {
    CXFA_Value* pNodeValue = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
    if (!pNodeValue) {
      return wsValue;
    }

    CXFA_Node* pValueChild = pNodeValue->GetFirstChild();
    if (!pValueChild) {
      return wsValue;
    }

    CXFA_LocaleValue::ValueType iVTType =
        XFA_GetLocaleValueType(pValueChild->GetElementType());
    CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
    CXFA_LocaleValue widgetValue(iVTType, wsValue, pLocaleMgr);
    switch (widgetValue.GetType()) {
      case CXFA_LocaleValue::ValueType::kDate: {
        WideString wsDate, wsTime;
        if (SplitDateTime(wsValue, wsDate, wsTime)) {
          CXFA_LocaleValue date(CXFA_LocaleValue::ValueType::kDate, wsDate,
                                pLocaleMgr);
          if (date.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
                                  XFA_ValuePicture::kDataBind)) {
            return wsFormattedValue;
          }
        }
        break;
      }
      case CXFA_LocaleValue::ValueType::kTime: {
        WideString wsDate, wsTime;
        if (SplitDateTime(wsValue, wsDate, wsTime)) {
          CXFA_LocaleValue time(CXFA_LocaleValue::ValueType::kTime, wsTime,
                                pLocaleMgr);
          if (time.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
                                  XFA_ValuePicture::kDataBind)) {
            return wsFormattedValue;
          }
        }
        break;
      }
      default:
        break;
    }
    widgetValue.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
                               XFA_ValuePicture::kDataBind);
  }
  return wsFormattedValue;
}

WideString CXFA_Node::NormalizeNumStr(const WideString& wsValue) {
  if (wsValue.IsEmpty()) {
    return WideString();
  }

  WideString wsOutput = wsValue;
  wsOutput.TrimFront('0');

  if (!wsOutput.IsEmpty() && wsOutput.Contains('.') && GetFracDigits() != -1) {
    wsOutput.TrimBack(L"0");
    wsOutput.TrimBack(L".");
  }
  if (wsOutput.IsEmpty() || wsOutput.Front() == '.') {
    wsOutput.InsertAtFront('0');
  }

  return wsOutput;
}

void CXFA_Node::InsertListTextItem(CXFA_Node* pItems,
                                   const WideString& wsText,
                                   int32_t nIndex) {
  CXFA_Node* pText = pItems->CreateSamePacketNode(XFA_Element::Text);
  pItems->InsertChildAndNotify(nIndex, pText);
  pText->JSObject()->SetContent(wsText, wsText, false, false, false);
}

WideString CXFA_Node::NumericLimit(const WideString& wsValue) {
  int32_t iLead = GetLeadDigits();
  int32_t iTread = GetFracDigits();
  if (iLead == -1 && iTread == -1) {
    return wsValue;
  }

  int32_t iCount = pdfium::checked_cast<int32_t>(wsValue.GetLength());
  if (iCount == 0) {
    return wsValue;
  }

  WideString wsRet;
  int32_t i = 0;
  if (wsValue[i] == L'-') {
    wsRet += L'-';
    i++;
  }

  int32_t iLead2 = 0;
  int32_t iTread2 = -1;
  for (; i < iCount; i++) {
    wchar_t wc = wsValue[i];
    if (FXSYS_IsDecimalDigit(wc)) {
      if (iLead >= 0) {
        iLead2++;
        if (iLead2 > iLead) {
          return L"0";
        }
      } else if (iTread2 >= 0) {
        iTread2++;
        if (iTread2 > iTread) {
          if (iTread != -1) {
            CFGAS_Decimal wsDeci = CFGAS_Decimal(wsValue.AsStringView());
            wsDeci.SetScale(iTread);
            wsRet = wsDeci.ToWideString();
          }
          return wsRet;
        }
      }
    } else if (wc == L'.') {
      iTread2 = 0;
      iLead = -1;
    }
    wsRet += wc;
  }
  return wsRet;
}

bool CXFA_Node::IsTransparent() const {
  XFA_Element type = GetElementType();
  return type == XFA_Element::SubformSet || type == XFA_Element::Area ||
         type == XFA_Element::Proto || (IsUnnamed() && IsContainerNode());
}

bool CXFA_Node::IsProperty() const {
  CXFA_Node* parent = GetParent();
  return parent && parent->HasProperty(GetElementType());
}

bool CXFA_Node::PresenceRequiresSpace() const {
  auto value = JSObject()->TryEnum(XFA_Attribute::Presence, true);
  XFA_AttributeValue ePresence = value.value_or(XFA_AttributeValue::Visible);
  return ePresence == XFA_AttributeValue::Visible ||
         ePresence == XFA_AttributeValue::Invisible;
}

void CXFA_Node::SetBindingNode(CXFA_Node* node) {
  binding_nodes_.clear();
  if (node) {
    binding_nodes_.emplace_back(node);
  }
}

void CXFA_Node::SetNodeAndDescendantsUnused() {
  CXFA_NodeIterator sIterator(this);
  for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
       pNode = sIterator.MoveToNext()) {
    pNode->SetFlag(XFA_NodeFlag::kUnusedNode);
  }
}

void CXFA_Node::SetToXML(const WideString& value) {
  auto* pNode = GetXMLMappingNode();
  switch (pNode->GetType()) {
    case CFX_XMLNode::Type::kElement: {
      auto* elem = static_cast<CFX_XMLElement*>(pNode);
      if (IsAttributeInXML()) {
        elem->SetAttribute(JSObject()->GetCData(XFA_Attribute::QualifiedName),
                           value);
        return;
      }

      bool bDeleteChildren = true;
      if (GetPacketType() == XFA_PacketType::Datasets) {
        for (CXFA_Node* pChildDataNode = GetFirstChild(); pChildDataNode;
             pChildDataNode = pChildDataNode->GetNextSibling()) {
          if (pChildDataNode->HasBindItems()) {
            bDeleteChildren = false;
            break;
          }
        }
      }
      if (bDeleteChildren) {
        elem->RemoveAllChildren();
      }

      auto* text = GetXMLDocument()->CreateNode<CFX_XMLText>(value);
      elem->AppendLastChild(text);
      break;
    }
    case CFX_XMLNode::Type::kText:
      ToXMLText(GetXMLMappingNode())->SetText(value);
      break;
    default:
      NOTREACHED();
  }
}

CXFA_Node* CXFA_Node::GetTransparentParent() {
  CXFA_Node* parent = GetParent();
  while (parent) {
    XFA_Element type = parent->GetElementType();
    if (type == XFA_Element::Variables ||
        (type != XFA_Element::SubformSet && !parent->IsUnnamed())) {
      return parent;
    }
    parent = parent->GetParent();
  }
  return nullptr;
}

CFX_XMLDocument* CXFA_Node::GetXMLDocument() const {
  return GetDocument()->GetNotify()->GetFFDoc()->GetXMLDocument();
}

// static
CXFA_Node* CXFA_Node::Create(CXFA_Document* doc,
                             XFA_Element element,
                             XFA_PacketType packet) {
  CXFA_Node* node = nullptr;
  switch (element) {
    case XFA_Element::Ps:
      node = cppgc::MakeGarbageCollected<CXFA_Ps>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::To:
      node = cppgc::MakeGarbageCollected<CXFA_To>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Ui:
      node = cppgc::MakeGarbageCollected<CXFA_Ui>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::RecordSet:
      node = cppgc::MakeGarbageCollected<CXFA_RecordSet>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SubsetBelow:
      node = cppgc::MakeGarbageCollected<CXFA_SubsetBelow>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SubformSet:
      node = cppgc::MakeGarbageCollected<CXFA_SubformSet>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AdobeExtensionLevel:
      node = cppgc::MakeGarbageCollected<CXFA_AdobeExtensionLevel>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Typeface:
      node = cppgc::MakeGarbageCollected<CXFA_Typeface>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Break:
      node = cppgc::MakeGarbageCollected<CXFA_Break>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::FontInfo:
      node = cppgc::MakeGarbageCollected<CXFA_FontInfo>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NumberPattern:
      node = cppgc::MakeGarbageCollected<CXFA_NumberPattern>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DynamicRender:
      node = cppgc::MakeGarbageCollected<CXFA_DynamicRender>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PrintScaling:
      node = cppgc::MakeGarbageCollected<CXFA_PrintScaling>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::CheckButton:
      node = cppgc::MakeGarbageCollected<CXFA_CheckButton>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DatePatterns:
      node = cppgc::MakeGarbageCollected<CXFA_DatePatterns>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SourceSet:
      node = cppgc::MakeGarbageCollected<CXFA_SourceSet>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Amd:
      node = cppgc::MakeGarbageCollected<CXFA_Amd>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Arc:
      node = cppgc::MakeGarbageCollected<CXFA_Arc>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Day:
      node = cppgc::MakeGarbageCollected<CXFA_Day>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Era:
      node = cppgc::MakeGarbageCollected<CXFA_Era>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Jog:
      node = cppgc::MakeGarbageCollected<CXFA_Jog>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Log:
      node = cppgc::MakeGarbageCollected<CXFA_Log>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Map:
      node = cppgc::MakeGarbageCollected<CXFA_Map>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Mdp:
      node = cppgc::MakeGarbageCollected<CXFA_Mdp>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::BreakBefore:
      node = cppgc::MakeGarbageCollected<CXFA_BreakBefore>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Oid:
      node = cppgc::MakeGarbageCollected<CXFA_Oid>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Pcl:
      node = cppgc::MakeGarbageCollected<CXFA_Pcl>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Pdf:
      node = cppgc::MakeGarbageCollected<CXFA_Pdf>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Ref:
      node = cppgc::MakeGarbageCollected<CXFA_Ref>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Uri:
      node = cppgc::MakeGarbageCollected<CXFA_Uri>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Xdc:
      node = cppgc::MakeGarbageCollected<CXFA_Xdc>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Xdp:
      node = cppgc::MakeGarbageCollected<CXFA_Xdp>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Xfa:
      node = cppgc::MakeGarbageCollected<CXFA_Xfa>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Xsl:
      node = cppgc::MakeGarbageCollected<CXFA_Xsl>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Zpl:
      node = cppgc::MakeGarbageCollected<CXFA_Zpl>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Cache:
      node = cppgc::MakeGarbageCollected<CXFA_Cache>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Margin:
      node = cppgc::MakeGarbageCollected<CXFA_Margin>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::KeyUsage:
      node = cppgc::MakeGarbageCollected<CXFA_KeyUsage>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Exclude:
      node = cppgc::MakeGarbageCollected<CXFA_Exclude>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ChoiceList:
      node = cppgc::MakeGarbageCollected<CXFA_ChoiceList>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Level:
      node = cppgc::MakeGarbageCollected<CXFA_Level>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::LabelPrinter:
      node = cppgc::MakeGarbageCollected<CXFA_LabelPrinter>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::CalendarSymbols:
      node = cppgc::MakeGarbageCollected<CXFA_CalendarSymbols>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Para:
      node = cppgc::MakeGarbageCollected<CXFA_Para>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Part:
      node = cppgc::MakeGarbageCollected<CXFA_Part>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Pdfa:
      node = cppgc::MakeGarbageCollected<CXFA_Pdfa>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Filter:
      node = cppgc::MakeGarbageCollected<CXFA_Filter>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Present:
      node = cppgc::MakeGarbageCollected<CXFA_Present>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Pagination:
      node = cppgc::MakeGarbageCollected<CXFA_Pagination>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Encoding:
      node = cppgc::MakeGarbageCollected<CXFA_Encoding>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Event:
      node = cppgc::MakeGarbageCollected<CXFA_Event>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Whitespace:
      node = cppgc::MakeGarbageCollected<CXFA_Whitespace>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DefaultUi:
      node = cppgc::MakeGarbageCollected<CXFA_DefaultUi>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DataModel:
      node = cppgc::MakeGarbageCollected<CXFA_DataModel>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Barcode:
      node = cppgc::MakeGarbageCollected<CXFA_Barcode>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::TimePattern:
      node = cppgc::MakeGarbageCollected<CXFA_TimePattern>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::BatchOutput:
      node = cppgc::MakeGarbageCollected<CXFA_BatchOutput>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Enforce:
      node = cppgc::MakeGarbageCollected<CXFA_Enforce>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::CurrencySymbols:
      node = cppgc::MakeGarbageCollected<CXFA_CurrencySymbols>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AddSilentPrint:
      node = cppgc::MakeGarbageCollected<CXFA_AddSilentPrint>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Rename:
      node = cppgc::MakeGarbageCollected<CXFA_Rename>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Operation:
      node = cppgc::MakeGarbageCollected<CXFA_Operation>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Typefaces:
      node = cppgc::MakeGarbageCollected<CXFA_Typefaces>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SubjectDNs:
      node = cppgc::MakeGarbageCollected<CXFA_SubjectDNs>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Issuers:
      node = cppgc::MakeGarbageCollected<CXFA_Issuers>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::WsdlConnection:
      node = cppgc::MakeGarbageCollected<CXFA_WsdlConnection>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Debug:
      node = cppgc::MakeGarbageCollected<CXFA_Debug>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Delta:
      node = cppgc::MakeGarbageCollected<CXFA_Delta>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::EraNames:
      node = cppgc::MakeGarbageCollected<CXFA_EraNames>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ModifyAnnots:
      node = cppgc::MakeGarbageCollected<CXFA_ModifyAnnots>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::StartNode:
      node = cppgc::MakeGarbageCollected<CXFA_StartNode>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Button:
      node = cppgc::MakeGarbageCollected<CXFA_Button>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Format:
      node = cppgc::MakeGarbageCollected<CXFA_Format>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Border:
      node = cppgc::MakeGarbageCollected<CXFA_Border>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Area:
      node = cppgc::MakeGarbageCollected<CXFA_Area>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Hyphenation:
      node = cppgc::MakeGarbageCollected<CXFA_Hyphenation>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Text:
      node = cppgc::MakeGarbageCollected<CXFA_Text>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Time:
      node = cppgc::MakeGarbageCollected<CXFA_Time>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Type:
      node = cppgc::MakeGarbageCollected<CXFA_Type>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Overprint:
      node = cppgc::MakeGarbageCollected<CXFA_Overprint>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Certificates:
      node = cppgc::MakeGarbageCollected<CXFA_Certificates>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::EncryptionMethods:
      node = cppgc::MakeGarbageCollected<CXFA_EncryptionMethods>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SetProperty:
      node = cppgc::MakeGarbageCollected<CXFA_SetProperty>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PrinterName:
      node = cppgc::MakeGarbageCollected<CXFA_PrinterName>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::StartPage:
      node = cppgc::MakeGarbageCollected<CXFA_StartPage>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PageOffset:
      node = cppgc::MakeGarbageCollected<CXFA_PageOffset>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DateTime:
      node = cppgc::MakeGarbageCollected<CXFA_DateTime>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Comb:
      node = cppgc::MakeGarbageCollected<CXFA_Comb>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Pattern:
      node = cppgc::MakeGarbageCollected<CXFA_Pattern>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::IfEmpty:
      node = cppgc::MakeGarbageCollected<CXFA_IfEmpty>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SuppressBanner:
      node = cppgc::MakeGarbageCollected<CXFA_SuppressBanner>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::OutputBin:
      node = cppgc::MakeGarbageCollected<CXFA_OutputBin>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Field:
      node = cppgc::MakeGarbageCollected<CXFA_Field>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Agent:
      node = cppgc::MakeGarbageCollected<CXFA_Agent>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::OutputXSL:
      node = cppgc::MakeGarbageCollected<CXFA_OutputXSL>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AdjustData:
      node = cppgc::MakeGarbageCollected<CXFA_AdjustData>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AutoSave:
      node = cppgc::MakeGarbageCollected<CXFA_AutoSave>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ContentArea:
      node = cppgc::MakeGarbageCollected<CXFA_ContentArea>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::WsdlAddress:
      node = cppgc::MakeGarbageCollected<CXFA_WsdlAddress>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Solid:
      node = cppgc::MakeGarbageCollected<CXFA_Solid>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DateTimeSymbols:
      node = cppgc::MakeGarbageCollected<CXFA_DateTimeSymbols>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::EncryptionLevel:
      node = cppgc::MakeGarbageCollected<CXFA_EncryptionLevel>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Edge:
      node = cppgc::MakeGarbageCollected<CXFA_Edge>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Stipple:
      node = cppgc::MakeGarbageCollected<CXFA_Stipple>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Attributes:
      node = cppgc::MakeGarbageCollected<CXFA_Attributes>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::VersionControl:
      node = cppgc::MakeGarbageCollected<CXFA_VersionControl>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Meridiem:
      node = cppgc::MakeGarbageCollected<CXFA_Meridiem>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ExclGroup:
      node = cppgc::MakeGarbageCollected<CXFA_ExclGroup>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ToolTip:
      node = cppgc::MakeGarbageCollected<CXFA_ToolTip>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Compress:
      node = cppgc::MakeGarbageCollected<CXFA_Compress>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Reason:
      node = cppgc::MakeGarbageCollected<CXFA_Reason>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Execute:
      node = cppgc::MakeGarbageCollected<CXFA_Execute>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ContentCopy:
      node = cppgc::MakeGarbageCollected<CXFA_ContentCopy>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DateTimeEdit:
      node = cppgc::MakeGarbageCollected<CXFA_DateTimeEdit>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Config:
      node = cppgc::MakeGarbageCollected<CXFA_Config>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Image:
      node = cppgc::MakeGarbageCollected<CXFA_Image>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SharpxHTML:
      node = cppgc::MakeGarbageCollected<CXFA_SharpxHTML>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NumberOfCopies:
      node = cppgc::MakeGarbageCollected<CXFA_NumberOfCopies>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::BehaviorOverride:
      node = cppgc::MakeGarbageCollected<CXFA_BehaviorOverride>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::TimeStamp:
      node = cppgc::MakeGarbageCollected<CXFA_TimeStamp>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Month:
      node = cppgc::MakeGarbageCollected<CXFA_Month>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ViewerPreferences:
      node = cppgc::MakeGarbageCollected<CXFA_ViewerPreferences>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ScriptModel:
      node = cppgc::MakeGarbageCollected<CXFA_ScriptModel>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Decimal:
      node = cppgc::MakeGarbageCollected<CXFA_Decimal>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Subform:
      node = cppgc::MakeGarbageCollected<CXFA_Subform>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Select:
      node = cppgc::MakeGarbageCollected<CXFA_Select>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Window:
      node = cppgc::MakeGarbageCollected<CXFA_Window>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::LocaleSet:
      node = cppgc::MakeGarbageCollected<CXFA_LocaleSet>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Handler:
      node = cppgc::MakeGarbageCollected<CXFA_Handler>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Presence:
      node = cppgc::MakeGarbageCollected<CXFA_Presence>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Record:
      node = cppgc::MakeGarbageCollected<CXFA_Record>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Embed:
      node = cppgc::MakeGarbageCollected<CXFA_Embed>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Version:
      node = cppgc::MakeGarbageCollected<CXFA_Version>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Command:
      node = cppgc::MakeGarbageCollected<CXFA_Command>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Copies:
      node = cppgc::MakeGarbageCollected<CXFA_Copies>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Staple:
      node = cppgc::MakeGarbageCollected<CXFA_Staple>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SubmitFormat:
      node = cppgc::MakeGarbageCollected<CXFA_SubmitFormat>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Boolean:
      node = cppgc::MakeGarbageCollected<CXFA_Boolean>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Message:
      node = cppgc::MakeGarbageCollected<CXFA_Message>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Output:
      node = cppgc::MakeGarbageCollected<CXFA_Output>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PsMap:
      node = cppgc::MakeGarbageCollected<CXFA_PsMap>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ExcludeNS:
      node = cppgc::MakeGarbageCollected<CXFA_ExcludeNS>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Assist:
      node = cppgc::MakeGarbageCollected<CXFA_Assist>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Picture:
      node = cppgc::MakeGarbageCollected<CXFA_Picture>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Traversal:
      node = cppgc::MakeGarbageCollected<CXFA_Traversal>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SilentPrint:
      node = cppgc::MakeGarbageCollected<CXFA_SilentPrint>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::WebClient:
      node = cppgc::MakeGarbageCollected<CXFA_WebClient>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Producer:
      node = cppgc::MakeGarbageCollected<CXFA_Producer>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Corner:
      node = cppgc::MakeGarbageCollected<CXFA_Corner>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::MsgId:
      node = cppgc::MakeGarbageCollected<CXFA_MsgId>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Color:
      node = cppgc::MakeGarbageCollected<CXFA_Color>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Keep:
      node = cppgc::MakeGarbageCollected<CXFA_Keep>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Query:
      node = cppgc::MakeGarbageCollected<CXFA_Query>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Insert:
      node = cppgc::MakeGarbageCollected<CXFA_Insert>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ImageEdit:
      node = cppgc::MakeGarbageCollected<CXFA_ImageEdit>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Validate:
      node = cppgc::MakeGarbageCollected<CXFA_Validate>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DigestMethods:
      node = cppgc::MakeGarbageCollected<CXFA_DigestMethods>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NumberPatterns:
      node = cppgc::MakeGarbageCollected<CXFA_NumberPatterns>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PageSet:
      node = cppgc::MakeGarbageCollected<CXFA_PageSet>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Integer:
      node = cppgc::MakeGarbageCollected<CXFA_Integer>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SoapAddress:
      node = cppgc::MakeGarbageCollected<CXFA_SoapAddress>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Equate:
      node = cppgc::MakeGarbageCollected<CXFA_Equate>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::FormFieldFilling:
      node = cppgc::MakeGarbageCollected<CXFA_FormFieldFilling>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PageRange:
      node = cppgc::MakeGarbageCollected<CXFA_PageRange>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Update:
      node = cppgc::MakeGarbageCollected<CXFA_Update>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ConnectString:
      node = cppgc::MakeGarbageCollected<CXFA_ConnectString>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Mode:
      node = cppgc::MakeGarbageCollected<CXFA_Mode>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Layout:
      node = cppgc::MakeGarbageCollected<CXFA_Layout>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Sharpxml:
      node = cppgc::MakeGarbageCollected<CXFA_Sharpxml>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::XsdConnection:
      node = cppgc::MakeGarbageCollected<CXFA_XsdConnection>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Traverse:
      node = cppgc::MakeGarbageCollected<CXFA_Traverse>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Encodings:
      node = cppgc::MakeGarbageCollected<CXFA_Encodings>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Template:
      node = cppgc::MakeGarbageCollected<CXFA_Template>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Acrobat:
      node = cppgc::MakeGarbageCollected<CXFA_Acrobat>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ValidationMessaging:
      node = cppgc::MakeGarbageCollected<CXFA_ValidationMessaging>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Signing:
      node = cppgc::MakeGarbageCollected<CXFA_Signing>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Script:
      node = cppgc::MakeGarbageCollected<CXFA_Script>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AddViewerPreferences:
      node = cppgc::MakeGarbageCollected<CXFA_AddViewerPreferences>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AlwaysEmbed:
      node = cppgc::MakeGarbageCollected<CXFA_AlwaysEmbed>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PasswordEdit:
      node = cppgc::MakeGarbageCollected<CXFA_PasswordEdit>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NumericEdit:
      node = cppgc::MakeGarbageCollected<CXFA_NumericEdit>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::EncryptionMethod:
      node = cppgc::MakeGarbageCollected<CXFA_EncryptionMethod>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Change:
      node = cppgc::MakeGarbageCollected<CXFA_Change>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PageArea:
      node = cppgc::MakeGarbageCollected<CXFA_PageArea>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SubmitUrl:
      node = cppgc::MakeGarbageCollected<CXFA_SubmitUrl>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Oids:
      node = cppgc::MakeGarbageCollected<CXFA_Oids>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Signature:
      node = cppgc::MakeGarbageCollected<CXFA_Signature>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ADBE_JSConsole:
      node = cppgc::MakeGarbageCollected<CXFA_ADBE_JSConsole>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Caption:
      node = cppgc::MakeGarbageCollected<CXFA_Caption>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Relevant:
      node = cppgc::MakeGarbageCollected<CXFA_Relevant>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::FlipLabel:
      node = cppgc::MakeGarbageCollected<CXFA_FlipLabel>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ExData:
      node = cppgc::MakeGarbageCollected<CXFA_ExData>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DayNames:
      node = cppgc::MakeGarbageCollected<CXFA_DayNames>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SoapAction:
      node = cppgc::MakeGarbageCollected<CXFA_SoapAction>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DefaultTypeface:
      node = cppgc::MakeGarbageCollected<CXFA_DefaultTypeface>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Manifest:
      node = cppgc::MakeGarbageCollected<CXFA_Manifest>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Overflow:
      node = cppgc::MakeGarbageCollected<CXFA_Overflow>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Linear:
      node = cppgc::MakeGarbageCollected<CXFA_Linear>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::CurrencySymbol:
      node = cppgc::MakeGarbageCollected<CXFA_CurrencySymbol>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Delete:
      node = cppgc::MakeGarbageCollected<CXFA_Delete>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DigestMethod:
      node = cppgc::MakeGarbageCollected<CXFA_DigestMethod>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::InstanceManager:
      node = cppgc::MakeGarbageCollected<CXFA_InstanceManager>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::EquateRange:
      node = cppgc::MakeGarbageCollected<CXFA_EquateRange>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Medium:
      node = cppgc::MakeGarbageCollected<CXFA_Medium>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::TextEdit:
      node = cppgc::MakeGarbageCollected<CXFA_TextEdit>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::TemplateCache:
      node = cppgc::MakeGarbageCollected<CXFA_TemplateCache>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::CompressObjectStream:
      node = cppgc::MakeGarbageCollected<CXFA_CompressObjectStream>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DataValue:
      node = cppgc::MakeGarbageCollected<CXFA_DataValue>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AccessibleContent:
      node = cppgc::MakeGarbageCollected<CXFA_AccessibleContent>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::IncludeXDPContent:
      node = cppgc::MakeGarbageCollected<CXFA_IncludeXDPContent>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::XmlConnection:
      node = cppgc::MakeGarbageCollected<CXFA_XmlConnection>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ValidateApprovalSignatures:
      node = cppgc::MakeGarbageCollected<CXFA_ValidateApprovalSignatures>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SignData:
      node = cppgc::MakeGarbageCollected<CXFA_SignData>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Packets:
      node = cppgc::MakeGarbageCollected<CXFA_Packets>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DatePattern:
      node = cppgc::MakeGarbageCollected<CXFA_DatePattern>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DuplexOption:
      node = cppgc::MakeGarbageCollected<CXFA_DuplexOption>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Base:
      node = cppgc::MakeGarbageCollected<CXFA_Base>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Bind:
      node = cppgc::MakeGarbageCollected<CXFA_Bind>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Compression:
      node = cppgc::MakeGarbageCollected<CXFA_Compression>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::User:
      node = cppgc::MakeGarbageCollected<CXFA_User>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Rectangle:
      node = cppgc::MakeGarbageCollected<CXFA_Rectangle>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::EffectiveOutputPolicy:
      node = cppgc::MakeGarbageCollected<CXFA_EffectiveOutputPolicy>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ADBE_JSDebugger:
      node = cppgc::MakeGarbageCollected<CXFA_ADBE_JSDebugger>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Acrobat7:
      node = cppgc::MakeGarbageCollected<CXFA_Acrobat7>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Interactive:
      node = cppgc::MakeGarbageCollected<CXFA_Interactive>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Locale:
      node = cppgc::MakeGarbageCollected<CXFA_Locale>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::CurrentPage:
      node = cppgc::MakeGarbageCollected<CXFA_CurrentPage>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Data:
      node = cppgc::MakeGarbageCollected<CXFA_Data>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Date:
      node = cppgc::MakeGarbageCollected<CXFA_Date>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Desc:
      node = cppgc::MakeGarbageCollected<CXFA_Desc>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Encrypt:
      node = cppgc::MakeGarbageCollected<CXFA_Encrypt>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Draw:
      node = cppgc::MakeGarbageCollected<CXFA_Draw>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Encryption:
      node = cppgc::MakeGarbageCollected<CXFA_Encryption>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::MeridiemNames:
      node = cppgc::MakeGarbageCollected<CXFA_MeridiemNames>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Messaging:
      node = cppgc::MakeGarbageCollected<CXFA_Messaging>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Speak:
      node = cppgc::MakeGarbageCollected<CXFA_Speak>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DataGroup:
      node = cppgc::MakeGarbageCollected<CXFA_DataGroup>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Common:
      node = cppgc::MakeGarbageCollected<CXFA_Common>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Sharptext:
      node = cppgc::MakeGarbageCollected<CXFA_Sharptext>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PaginationOverride:
      node = cppgc::MakeGarbageCollected<CXFA_PaginationOverride>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Reasons:
      node = cppgc::MakeGarbageCollected<CXFA_Reasons>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SignatureProperties:
      node = cppgc::MakeGarbageCollected<CXFA_SignatureProperties>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Threshold:
      node = cppgc::MakeGarbageCollected<CXFA_Threshold>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::AppearanceFilter:
      node = cppgc::MakeGarbageCollected<CXFA_AppearanceFilter>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Fill:
      node = cppgc::MakeGarbageCollected<CXFA_Fill>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Font:
      node = cppgc::MakeGarbageCollected<CXFA_Font>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Form:
      node = cppgc::MakeGarbageCollected<CXFA_Form>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::MediumInfo:
      node = cppgc::MakeGarbageCollected<CXFA_MediumInfo>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Certificate:
      node = cppgc::MakeGarbageCollected<CXFA_Certificate>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Password:
      node = cppgc::MakeGarbageCollected<CXFA_Password>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::RunScripts:
      node = cppgc::MakeGarbageCollected<CXFA_RunScripts>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Trace:
      node = cppgc::MakeGarbageCollected<CXFA_Trace>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Float:
      node = cppgc::MakeGarbageCollected<CXFA_Float>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::RenderPolicy:
      node = cppgc::MakeGarbageCollected<CXFA_RenderPolicy>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Destination:
      node = cppgc::MakeGarbageCollected<CXFA_Destination>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Value:
      node = cppgc::MakeGarbageCollected<CXFA_Value>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Bookend:
      node = cppgc::MakeGarbageCollected<CXFA_Bookend>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ExObject:
      node = cppgc::MakeGarbageCollected<CXFA_ExObject>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::OpenAction:
      node = cppgc::MakeGarbageCollected<CXFA_OpenAction>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NeverEmbed:
      node = cppgc::MakeGarbageCollected<CXFA_NeverEmbed>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::BindItems:
      node = cppgc::MakeGarbageCollected<CXFA_BindItems>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Calculate:
      node = cppgc::MakeGarbageCollected<CXFA_Calculate>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Print:
      node = cppgc::MakeGarbageCollected<CXFA_Print>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Extras:
      node = cppgc::MakeGarbageCollected<CXFA_Extras>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Proto:
      node = cppgc::MakeGarbageCollected<CXFA_Proto>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DSigData:
      node = cppgc::MakeGarbageCollected<CXFA_DSigData>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Creator:
      node = cppgc::MakeGarbageCollected<CXFA_Creator>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Connect:
      node = cppgc::MakeGarbageCollected<CXFA_Connect>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Permissions:
      node = cppgc::MakeGarbageCollected<CXFA_Permissions>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::ConnectionSet:
      node = cppgc::MakeGarbageCollected<CXFA_ConnectionSet>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Submit:
      node = cppgc::MakeGarbageCollected<CXFA_Submit>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Range:
      node = cppgc::MakeGarbageCollected<CXFA_Range>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Linearized:
      node = cppgc::MakeGarbageCollected<CXFA_Linearized>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Packet:
      node = cppgc::MakeGarbageCollected<CXFA_Packet>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::RootElement:
      node = cppgc::MakeGarbageCollected<CXFA_RootElement>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PlaintextMetadata:
      node = cppgc::MakeGarbageCollected<CXFA_PlaintextMetadata>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NumberSymbols:
      node = cppgc::MakeGarbageCollected<CXFA_NumberSymbols>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PrintHighQuality:
      node = cppgc::MakeGarbageCollected<CXFA_PrintHighQuality>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Driver:
      node = cppgc::MakeGarbageCollected<CXFA_Driver>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::IncrementalLoad:
      node = cppgc::MakeGarbageCollected<CXFA_IncrementalLoad>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::SubjectDN:
      node = cppgc::MakeGarbageCollected<CXFA_SubjectDN>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::CompressLogicalStructure:
      node = cppgc::MakeGarbageCollected<CXFA_CompressLogicalStructure>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::IncrementalMerge:
      node = cppgc::MakeGarbageCollected<CXFA_IncrementalMerge>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Radial:
      node = cppgc::MakeGarbageCollected<CXFA_Radial>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Variables:
      node = cppgc::MakeGarbageCollected<CXFA_Variables>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::TimePatterns:
      node = cppgc::MakeGarbageCollected<CXFA_TimePatterns>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::EffectiveInputPolicy:
      node = cppgc::MakeGarbageCollected<CXFA_EffectiveInputPolicy>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NameAttr:
      node = cppgc::MakeGarbageCollected<CXFA_NameAttr>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Conformance:
      node = cppgc::MakeGarbageCollected<CXFA_Conformance>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Transform:
      node = cppgc::MakeGarbageCollected<CXFA_Transform>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::LockDocument:
      node = cppgc::MakeGarbageCollected<CXFA_LockDocument>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::BreakAfter:
      node = cppgc::MakeGarbageCollected<CXFA_BreakAfter>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Line:
      node = cppgc::MakeGarbageCollected<CXFA_Line>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Source:
      node = cppgc::MakeGarbageCollected<CXFA_Source>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Occur:
      node = cppgc::MakeGarbageCollected<CXFA_Occur>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::PickTrayByPDFSize:
      node = cppgc::MakeGarbageCollected<CXFA_PickTrayByPDFSize>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::MonthNames:
      node = cppgc::MakeGarbageCollected<CXFA_MonthNames>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Severity:
      node = cppgc::MakeGarbageCollected<CXFA_Severity>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::GroupParent:
      node = cppgc::MakeGarbageCollected<CXFA_GroupParent>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DocumentAssembly:
      node = cppgc::MakeGarbageCollected<CXFA_DocumentAssembly>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::NumberSymbol:
      node = cppgc::MakeGarbageCollected<CXFA_NumberSymbol>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Tagged:
      node = cppgc::MakeGarbageCollected<CXFA_Tagged>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::Items:
      node = cppgc::MakeGarbageCollected<CXFA_Items>(
          doc->GetHeap()->GetAllocationHandle(), doc, packet);
      break;
    case XFA_Element::DataWindow:
    case XFA_Element::Deltas:
    case XFA_Element::EventPseudoModel:
    case XFA_Element::HostPseudoModel:
    case XFA_Element::LayoutPseudoModel:
    case XFA_Element::List:
    case XFA_Element::ListDuplicate:
    case XFA_Element::LogPseudoModel:
    case XFA_Element::Model:
    case XFA_Element::Node:
    case XFA_Element::NodeWithDesc:
    case XFA_Element::NodeWithUse:
    case XFA_Element::NodeWithValue:
    case XFA_Element::Object:
    case XFA_Element::SignaturePseudoModel:
    case XFA_Element::Tree:
    case XFA_Element::TreeList:
    case XFA_Element::Unknown:
      // These defined elements can not be made from an XML parse. Some are
      // not CXFA_Node sub-classes, some are only used as intermediate classes,
      // and so forth.
      return nullptr;
  }
  if (!node || !node->IsValidInPacket(packet)) {
    return nullptr;
  }
  return node;
}
