Create common CXML_Object base class for CXML_Content and CXML_Element.

They should each know what they are rather than having an
external ChildRecord struct to track the type.

Change-Id: Ic647ba45569764073e944d30af1a96dccdc29eb3
Reviewed-on: https://pdfium-review.googlesource.com/5210
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 1abcc8d..a50a4ea 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -803,11 +803,14 @@
     "core/fxcrt/xml/cxml_attritem.h",
     "core/fxcrt/xml/cxml_attrmap.cpp",
     "core/fxcrt/xml/cxml_attrmap.h",
+    "core/fxcrt/xml/cxml_content.cpp",
     "core/fxcrt/xml/cxml_content.h",
     "core/fxcrt/xml/cxml_databufacc.cpp",
     "core/fxcrt/xml/cxml_databufacc.h",
     "core/fxcrt/xml/cxml_element.cpp",
     "core/fxcrt/xml/cxml_element.h",
+    "core/fxcrt/xml/cxml_object.cpp",
+    "core/fxcrt/xml/cxml_object.h",
     "core/fxcrt/xml/cxml_parser.cpp",
     "core/fxcrt/xml/cxml_parser.h",
   ]
diff --git a/core/fxcrt/xml/cxml_content.cpp b/core/fxcrt/xml/cxml_content.cpp
new file mode 100644
index 0000000..fe0c185
--- /dev/null
+++ b/core/fxcrt/xml/cxml_content.cpp
@@ -0,0 +1,20 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/xml/cxml_content.h"
+
+CXML_Content::CXML_Content(bool bCDATA, const CFX_WideStringC& content)
+    : m_bCDATA(bCDATA), m_Content(content) {}
+
+CXML_Content::~CXML_Content() {}
+
+CXML_Content* CXML_Content::AsContent() {
+  return this;
+}
+
+const CXML_Content* CXML_Content::AsContent() const {
+  return this;
+}
diff --git a/core/fxcrt/xml/cxml_content.h b/core/fxcrt/xml/cxml_content.h
index 261c622..641efe6 100644
--- a/core/fxcrt/xml/cxml_content.h
+++ b/core/fxcrt/xml/cxml_content.h
@@ -7,14 +7,17 @@
 #ifndef CORE_FXCRT_XML_CXML_CONTENT_H_
 #define CORE_FXCRT_XML_CXML_CONTENT_H_
 
-class CXML_Content {
- public:
-  CXML_Content() : m_bCDATA(false), m_Content() {}
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/xml/cxml_object.h"
 
-  void Set(bool bCDATA, const CFX_WideStringC& content) {
-    m_bCDATA = bCDATA;
-    m_Content = content;
-  }
+class CXML_Content : public CXML_Object {
+ public:
+  CXML_Content(bool bCDATA, const CFX_WideStringC& content);
+  ~CXML_Content() override;
+
+  // CXML_Object:
+  CXML_Content* AsContent() override;
+  const CXML_Content* AsContent() const override;
 
   bool m_bCDATA;
   CFX_WideString m_Content;
diff --git a/core/fxcrt/xml/cxml_element.cpp b/core/fxcrt/xml/cxml_element.cpp
index 95a6dba..ec0a73b 100644
--- a/core/fxcrt/xml/cxml_element.cpp
+++ b/core/fxcrt/xml/cxml_element.cpp
@@ -23,25 +23,16 @@
                            const CFX_ByteStringC& tagname)
     : m_pParent(pParent), m_QSpaceName(qSpace), m_TagName(tagname) {}
 
-CXML_Element::~CXML_Element() {
-  Empty();
+CXML_Element::~CXML_Element() {}
+
+CXML_Element* CXML_Element::AsElement() {
+  return this;
 }
 
-void CXML_Element::Empty() {
-  RemoveChildren();
+const CXML_Element* CXML_Element::AsElement() const {
+  return this;
 }
-void CXML_Element::RemoveChildren() {
-  for (const ChildRecord& record : m_Children) {
-    if (record.type == Content) {
-      delete static_cast<CXML_Content*>(record.child);
-    } else if (record.type == Element) {
-      CXML_Element* child = static_cast<CXML_Element*>(record.child);
-      child->RemoveChildren();
-      delete child;
-    }
-  }
-  m_Children.clear();
-}
+
 CFX_ByteString CXML_Element::GetTagName(bool bQualified) const {
   if (!bQualified || m_QSpaceName.IsEmpty()) {
     return m_TagName;
@@ -159,69 +150,47 @@
   return true;
 }
 
-CXML_Element::ChildType CXML_Element::GetChildType(uint32_t index) const {
-  return index < m_Children.size() ? m_Children[index].type : Invalid;
-}
-
-CFX_WideString CXML_Element::GetContent(uint32_t index) const {
-  if (index < m_Children.size() && m_Children[index].type == Content) {
-    CXML_Content* pContent =
-        static_cast<CXML_Content*>(m_Children[index].child);
-    if (pContent)
-      return pContent->m_Content;
-  }
-  return CFX_WideString();
-}
-
-CXML_Element* CXML_Element::GetElement(uint32_t index) const {
-  if (index < m_Children.size() && m_Children[index].type == Element)
-    return static_cast<CXML_Element*>(m_Children[index].child);
-  return nullptr;
-}
-
 uint32_t CXML_Element::CountElements(const CFX_ByteStringC& space,
                                      const CFX_ByteStringC& tag) const {
   int count = 0;
-  for (const ChildRecord& record : m_Children) {
-    if (record.type != Element)
-      continue;
-
-    CXML_Element* pKid = static_cast<CXML_Element*>(record.child);
-    if ((space.IsEmpty() || pKid->m_QSpaceName == space) &&
-        pKid->m_TagName == tag) {
+  for (const auto& pChild : m_Children) {
+    const CXML_Element* pKid = pChild->AsElement();
+    if (pKid && pKid->m_TagName == tag &&
+        (space.IsEmpty() || pKid->m_QSpaceName == space)) {
       count++;
     }
   }
   return count;
 }
 
+CXML_Object* CXML_Element::GetChild(uint32_t index) const {
+  return index < m_Children.size() ? m_Children[index].get() : nullptr;
+}
+
 CXML_Element* CXML_Element::GetElement(const CFX_ByteStringC& space,
                                        const CFX_ByteStringC& tag,
-                                       int index) const {
-  if (index < 0)
+                                       int nth) const {
+  if (nth < 0)
     return nullptr;
 
-  for (const ChildRecord& record : m_Children) {
-    if (record.type != Element)
-      continue;
-
-    CXML_Element* pKid = static_cast<CXML_Element*>(record.child);
-    if ((space.IsEmpty() || pKid->m_QSpaceName == space) &&
-        pKid->m_TagName == tag) {
-      if (index-- == 0)
+  for (const auto& pChild : m_Children) {
+    CXML_Element* pKid = pChild->AsElement();
+    if (pKid && pKid->m_TagName == tag &&
+        (space.IsEmpty() || pKid->m_QSpaceName == space)) {
+      if (nth-- == 0)
         return pKid;
     }
   }
   return nullptr;
 }
 
-uint32_t CXML_Element::FindElement(CXML_Element* pChild) const {
+uint32_t CXML_Element::FindElement(CXML_Element* pElement) const {
   int index = 0;
-  for (const ChildRecord& record : m_Children) {
-    if (record.type == Element &&
-        static_cast<CXML_Element*>(record.child) == pChild) {
+  for (const auto& pChild : m_Children) {
+    CXML_Element* pKid = pChild->AsElement();
+    if (pKid && pKid == pElement)
       return index;
-    }
+
     ++index;
   }
   return 0xFFFFFFFF;
diff --git a/core/fxcrt/xml/cxml_element.h b/core/fxcrt/xml/cxml_element.h
index 349deba..2eb6caf 100644
--- a/core/fxcrt/xml/cxml_element.h
+++ b/core/fxcrt/xml/cxml_element.h
@@ -12,19 +12,21 @@
 
 #include "core/fxcrt/fx_basic.h"
 #include "core/fxcrt/xml/cxml_attrmap.h"
+#include "core/fxcrt/xml/cxml_object.h"
 
-class CXML_Element {
+class CXML_Element : public CXML_Object {
  public:
-  enum ChildType { Invalid, Element, Content };
-
   static std::unique_ptr<CXML_Element> Parse(const void* pBuffer, size_t size);
 
   CXML_Element(const CXML_Element* pParent,
                const CFX_ByteStringC& qSpace,
                const CFX_ByteStringC& tagname);
-  ~CXML_Element();
+  ~CXML_Element() override;
 
-  void Empty();
+  // CXML_Object:
+  CXML_Element* AsElement() override;
+  const CXML_Element* AsElement() const override;
+
   CFX_ByteString GetTagName(bool bQualified = false) const;
   CFX_ByteString GetNamespace(bool bQualified = false) const;
   CFX_ByteString GetNamespaceURI(const CFX_ByteString& qName) const;
@@ -88,39 +90,25 @@
   }
 
   uint32_t CountChildren() const { return m_Children.size(); }
-  ChildType GetChildType(uint32_t index) const;
-  CFX_WideString GetContent(uint32_t index) const;
-  CXML_Element* GetElement(uint32_t index) const;
-  CXML_Element* GetElement(const CFX_ByteStringC& space,
-                           const CFX_ByteStringC& tag) const {
-    return GetElement(space, tag, 0);
-  }
-
   uint32_t CountElements(const CFX_ByteStringC& space,
                          const CFX_ByteStringC& tag) const;
+  CXML_Object* GetChild(uint32_t index) const;
   CXML_Element* GetElement(const CFX_ByteStringC& space,
                            const CFX_ByteStringC& tag,
-                           int index) const;
-
-  uint32_t FindElement(CXML_Element* pChild) const;
+                           int nth) const;
+  uint32_t FindElement(CXML_Element* pElement) const;
   void SetTag(const CFX_ByteStringC& qTagName);
-  void RemoveChildren();
   void RemoveChild(uint32_t index);
 
  private:
   friend class CXML_Parser;
   friend class CXML_Composer;
 
-  struct ChildRecord {
-    ChildType type;
-    void* child;  // CXML_Element and CXML_Content lack a common ancestor.
-  };
-
   const CXML_Element* const m_pParent;
   CFX_ByteString m_QSpaceName;
   CFX_ByteString m_TagName;
   CXML_AttrMap m_AttrMap;
-  std::vector<ChildRecord> m_Children;
+  std::vector<std::unique_ptr<CXML_Object>> m_Children;
 };
 
 #endif  // CORE_FXCRT_XML_CXML_ELEMENT_H_
diff --git a/core/fxcrt/xml/cxml_object.cpp b/core/fxcrt/xml/cxml_object.cpp
new file mode 100644
index 0000000..61e88cb
--- /dev/null
+++ b/core/fxcrt/xml/cxml_object.cpp
@@ -0,0 +1,25 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/xml/cxml_object.h"
+
+CXML_Object::~CXML_Object() {}
+
+CXML_Content* CXML_Object::AsContent() {
+  return nullptr;
+}
+
+CXML_Element* CXML_Object::AsElement() {
+  return nullptr;
+}
+
+const CXML_Content* CXML_Object::AsContent() const {
+  return nullptr;
+}
+
+const CXML_Element* CXML_Object::AsElement() const {
+  return nullptr;
+}
diff --git a/core/fxcrt/xml/cxml_object.h b/core/fxcrt/xml/cxml_object.h
new file mode 100644
index 0000000..e7f23aa
--- /dev/null
+++ b/core/fxcrt/xml/cxml_object.h
@@ -0,0 +1,45 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// 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
+
+#ifndef CORE_FXCRT_XML_CXML_OBJECT_H_
+#define CORE_FXCRT_XML_CXML_OBJECT_H_
+
+#include "core/fxcrt/fx_basic.h"
+
+class CXML_Content;
+class CXML_Element;
+
+class CXML_Object {
+ public:
+  virtual ~CXML_Object();
+
+  virtual CXML_Content* AsContent();
+  virtual const CXML_Content* AsContent() const;
+
+  virtual CXML_Element* AsElement();
+  virtual const CXML_Element* AsElement() const;
+
+ protected:
+  CXML_Object() {}
+};
+
+inline CXML_Content* ToContent(CXML_Object* pObj) {
+  return pObj ? pObj->AsContent() : nullptr;
+}
+
+inline const CXML_Content* ToContent(const CXML_Object* pObj) {
+  return pObj ? pObj->AsContent() : nullptr;
+}
+
+inline CXML_Element* ToElement(CXML_Object* pObj) {
+  return pObj ? pObj->AsElement() : nullptr;
+}
+
+inline const CXML_Element* ToElement(const CXML_Object* pObj) {
+  return pObj ? pObj->AsElement() : nullptr;
+}
+
+#endif  // CORE_FXCRT_XML_CXML_OBJECT_H_
diff --git a/core/fxcrt/xml/cxml_parser.cpp b/core/fxcrt/xml/cxml_parser.cpp
index 3c91d74..d43fbd6 100644
--- a/core/fxcrt/xml/cxml_parser.cpp
+++ b/core/fxcrt/xml/cxml_parser.cpp
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "core/fxcrt/fx_extension.h"
@@ -493,8 +494,7 @@
             if (!pSubElement)
               break;
 
-            pElement->m_Children.push_back(
-                {CXML_Element::Element, pSubElement.release()});
+            pElement->m_Children.push_back(std::move(pSubElement));
             SkipWhiteSpaces();
           }
           break;
@@ -537,7 +537,6 @@
   if (content.IsEmpty())
     return;
 
-  CXML_Content* pContent = new CXML_Content;
-  pContent->Set(bCDATA, content);
-  pElement->m_Children.push_back({CXML_Element::Content, pContent});
+  pElement->m_Children.push_back(
+      pdfium::MakeUnique<CXML_Content>(bCDATA, content));
 }
diff --git a/fpdfsdk/fpdf_ext.cpp b/fpdfsdk/fpdf_ext.cpp
index 8773d68..f598835 100644
--- a/fpdfsdk/fpdf_ext.cpp
+++ b/fpdfsdk/fpdf_ext.cpp
@@ -16,6 +16,7 @@
 #include "core/fpdfdoc/cpdf_metadata.h"
 #include "core/fxcrt/fx_basic.h"
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/xml/cxml_content.h"
 #include "core/fxcrt/xml/cxml_element.h"
 #include "fpdfsdk/fsdk_define.h"
 #include "third_party/base/ptr_util.h"
@@ -86,12 +87,13 @@
     if (space == "xmlns" && name == "adhocwf" &&
         value == L"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/") {
       CXML_Element* pVersion =
-          pElement->GetElement("adhocwf", cbName.AsStringC());
+          pElement->GetElement("adhocwf", cbName.AsStringC(), 0);
       if (!pVersion)
         continue;
-      CFX_WideString wsContent = pVersion->GetContent(0);
-      int nType = wsContent.GetInteger();
-      switch (nType) {
+      CXML_Content* pContent = ToContent(pVersion->GetChild(0));
+      if (!pContent)
+        continue;
+      switch (pContent->m_Content.GetInteger()) {
         case 1:
           FPDF_UnSupportError(FPDF_UNSP_DOC_SHAREDFORM_ACROBAT);
           break;
@@ -107,12 +109,9 @@
 
   uint32_t nCount = pElement->CountChildren();
   for (i = 0; i < (int)nCount; i++) {
-    CXML_Element::ChildType childType = pElement->GetChildType(i);
-    if (childType == CXML_Element::Element) {
-      CXML_Element* pChild = pElement->GetElement(i);
-      if (CheckSharedForm(pChild, cbName))
-        return true;
-    }
+    CXML_Element* pChild = ToElement(pElement->GetChild(i));
+    if (pChild && CheckSharedForm(pChild, cbName))
+      return true;
   }
   return false;
 }
diff --git a/fpdfsdk/pdfwindow/PWL_Edit.cpp b/fpdfsdk/pdfwindow/PWL_Edit.cpp
index b1285a8..9057274 100644
--- a/fpdfsdk/pdfwindow/PWL_Edit.cpp
+++ b/fpdfsdk/pdfwindow/PWL_Edit.cpp
@@ -13,6 +13,7 @@
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfdoc/cpvt_word.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/xml/cxml_content.h"
 #include "core/fxcrt/xml/cxml_element.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
@@ -55,30 +56,27 @@
     m_pEdit->SetText(swText);
     return;
   }
-
-  int32_t nCount = pXML->CountChildren();
-  bool bFirst = true;
-
   swText.clear();
 
+  bool bFirst = true;
+  int32_t nCount = pXML->CountChildren();
   for (int32_t i = 0; i < nCount; i++) {
-    CXML_Element* pSubElement = pXML->GetElement(i);
-    if (!pSubElement)
+    CXML_Element* pSubElement = ToElement(pXML->GetChild(i));
+    if (!pSubElement || !pSubElement->GetTagName().EqualNoCase("p"))
       continue;
 
-    CFX_ByteString tag = pSubElement->GetTagName();
-    if (tag.EqualNoCase("p")) {
-      int nChild = pSubElement->CountChildren();
-      CFX_WideString swSection;
-      for (int32_t j = 0; j < nChild; j++)
-        swSection += pSubElement->GetContent(j);
-
-      if (bFirst)
-        bFirst = false;
-      else
-        swText += FWL_VKEY_Return;
-      swText += swSection;
+    CFX_WideString swSection;
+    int nSubChild = pSubElement->CountChildren();
+    for (int32_t j = 0; j < nSubChild; j++) {
+      CXML_Content* pSubContent = ToContent(pSubElement->GetChild(j));
+      if (pSubContent)
+        swSection += pSubContent->m_Content;
     }
+    if (bFirst)
+      bFirst = false;
+    else
+      swText += FWL_VKEY_Return;
+    swText += swSection;
   }
 
   m_pEdit->SetText(swText);
diff --git a/xfa/fxfa/parser/cxfa_xmllocale.cpp b/xfa/fxfa/parser/cxfa_xmllocale.cpp
index dd1d7cd..4055c15 100644
--- a/xfa/fxfa/parser/cxfa_xmllocale.cpp
+++ b/xfa/fxfa/parser/cxfa_xmllocale.cpp
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "core/fxcrt/xml/cxml_content.h"
 #include "core/fxcrt/xml/cxml_element.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
@@ -60,7 +61,8 @@
     default:
       return CFX_WideString();
   }
-  CXML_Element* pElement = m_pLocaleData->GetElement("", bsSymbols.AsStringC());
+  CXML_Element* pElement =
+      m_pLocaleData->GetElement("", bsSymbols.AsStringC(), 0);
   if (!pElement)
     return CFX_WideString();
 
@@ -73,10 +75,16 @@
   if (!m_pLocaleData)
     return CFX_WideString();
 
-  CFX_ByteString bsSpace;
   CXML_Element* pNumberSymbols =
-      m_pLocaleData->GetElement(bsSpace.AsStringC(), "dateTimeSymbols");
-  return pNumberSymbols ? pNumberSymbols->GetContent(0) : CFX_WideString();
+      m_pLocaleData->GetElement("", "dateTimeSymbols", 0);
+  if (!pNumberSymbols)
+    return CFX_WideString();
+
+  CXML_Content* pContent = ToContent(pNumberSymbols->GetChild(0));
+  if (!pContent)
+    return CFX_WideString();
+
+  return pContent->m_Content;
 }
 
 CFX_WideString CXFA_XMLLocale::GetMonthName(int32_t nMonth, bool bAbbr) const {
@@ -105,15 +113,16 @@
   if (!m_pLocaleData)
     return CFX_WideString();
 
-  CXML_Element* pChild = m_pLocaleData->GetElement("", "calendarSymbols");
+  CXML_Element* pChild = m_pLocaleData->GetElement("", "calendarSymbols", 0);
   if (!pChild)
     return CFX_WideString();
 
   CFX_ByteString pstrSymbolNames = symbol + "Names";
   CXML_Element* pSymbolNames =
-      pChild->GetElement("", pstrSymbolNames.AsStringC());
+      pChild->GetElement("", pstrSymbolNames.AsStringC(), 0);
   if (!pSymbolNames)
     return CFX_WideString();
+
   if ((!!pSymbolNames->GetAttrInteger("abbr")) != bAbbr)
     pSymbolNames = pChild->GetElement("", pstrSymbolNames.AsStringC(), 1);
 
@@ -121,12 +130,16 @@
     return CFX_WideString();
 
   CXML_Element* pSymbolName = pSymbolNames->GetElement("", symbol, index);
-  return pSymbolName ? pSymbolName->GetContent(0) : CFX_WideString();
+  if (!pSymbolName)
+    return CFX_WideString();
+
+  CXML_Content* pContent = ToContent(pSymbolName->GetChild(0));
+  return pContent ? pContent->m_Content : CFX_WideString();
 }
 
 CFX_WideString CXFA_XMLLocale::GetDatePattern(
     FX_LOCALEDATETIMESUBCATEGORY eType) const {
-  CXML_Element* pElement = m_pLocaleData->GetElement("", "datePatterns");
+  CXML_Element* pElement = m_pLocaleData->GetElement("", "datePatterns", 0);
   if (!pElement)
     return CFX_WideString();
 
@@ -151,7 +164,7 @@
 
 CFX_WideString CXFA_XMLLocale::GetTimePattern(
     FX_LOCALEDATETIMESUBCATEGORY eType) const {
-  CXML_Element* pElement = m_pLocaleData->GetElement("", "timePatterns");
+  CXML_Element* pElement = m_pLocaleData->GetElement("", "timePatterns", 0);
   if (!pElement)
     return CFX_WideString();
 
@@ -176,7 +189,7 @@
 
 CFX_WideString CXFA_XMLLocale::GetNumPattern(
     FX_LOCALENUMSUBCATEGORY eType) const {
-  return m_pLocaleData->GetElement("", "numberPatterns")
+  return m_pLocaleData->GetElement("", "numberPatterns", 0)
              ? XFA_PatternToString(eType)
              : CFX_WideString();
 }
@@ -187,8 +200,10 @@
   int32_t iCount = pElement->CountElements("", bsTag);
   for (int32_t i = 0; i < iCount; i++) {
     CXML_Element* pChild = pElement->GetElement("", bsTag, i);
-    if (pChild->GetAttrValue("name") == wsName)
-      return pChild->GetContent(0);
+    if (pChild->GetAttrValue("name") == wsName) {
+      CXML_Content* pContent = ToContent(pChild->GetChild(0));
+      return pContent ? pContent->m_Content : CFX_WideString();
+    }
   }
   return CFX_WideString();
 }