1 // Copyright 2017 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxcrt/xml/cxml_element.h"
8
9 #include "core/fxcrt/xml/cxml_content.h"
10 #include "core/fxcrt/xml/cxml_parser.h"
11
12 namespace {
13
SplitQualifiedName(const ByteStringView & bsFullName,ByteStringView * bsSpace,ByteStringView * bsName)14 void SplitQualifiedName(const ByteStringView& bsFullName,
15 ByteStringView* bsSpace,
16 ByteStringView* bsName) {
17 if (bsFullName.IsEmpty())
18 return;
19
20 auto iStart = bsFullName.Find(':');
21 if (iStart.has_value()) {
22 *bsSpace = bsFullName.Left(iStart.value());
23 *bsName = bsFullName.Right(bsFullName.GetLength() - (iStart.value() + 1));
24 } else {
25 *bsName = bsFullName;
26 }
27 }
28
29 } // namespace
30
31 // static
Parse(const void * pBuffer,size_t size)32 std::unique_ptr<CXML_Element> CXML_Element::Parse(const void* pBuffer,
33 size_t size) {
34 CXML_Parser parser;
35 if (!parser.Init(static_cast<const uint8_t*>(pBuffer), size))
36 return nullptr;
37 return parser.ParseElement(nullptr, false);
38 }
39
CXML_Element(const CXML_Element * pParent,const ByteStringView & qSpace,const ByteStringView & tagname)40 CXML_Element::CXML_Element(const CXML_Element* pParent,
41 const ByteStringView& qSpace,
42 const ByteStringView& tagname)
43 : m_pParent(pParent), m_QSpaceName(qSpace), m_TagName(tagname) {}
44
~CXML_Element()45 CXML_Element::~CXML_Element() {}
46
AsElement()47 CXML_Element* CXML_Element::AsElement() {
48 return this;
49 }
50
AsElement() const51 const CXML_Element* CXML_Element::AsElement() const {
52 return this;
53 }
54
GetTagName() const55 ByteString CXML_Element::GetTagName() const {
56 return m_TagName;
57 }
58
GetNamespaceURI(const ByteString & qName) const59 ByteString CXML_Element::GetNamespaceURI(const ByteString& qName) const {
60 const CXML_Element* pElement = this;
61 do {
62 const WideString* pwsSpace;
63 if (qName.IsEmpty())
64 pwsSpace = pElement->Lookup("", "xmlns");
65 else
66 pwsSpace = pElement->Lookup("xmlns", qName);
67 if (pwsSpace)
68 return pwsSpace->UTF8Encode();
69
70 pElement = pElement->GetParent();
71 } while (pElement);
72 return ByteString();
73 }
74
GetAttrByIndex(size_t index,ByteString * space,ByteString * name,WideString * value) const75 void CXML_Element::GetAttrByIndex(size_t index,
76 ByteString* space,
77 ByteString* name,
78 WideString* value) const {
79 if (index >= m_AttrMap.size())
80 return;
81
82 const CXML_AttrItem& item = m_AttrMap[index];
83 *space = item.m_QSpaceName;
84 *name = item.m_AttrName;
85 *value = item.m_Value;
86 }
87
GetAttrValue(const ByteStringView & name) const88 WideString CXML_Element::GetAttrValue(const ByteStringView& name) const {
89 ByteStringView bsSpace;
90 ByteStringView bsName;
91 SplitQualifiedName(name, &bsSpace, &bsName);
92
93 WideString attr;
94 const WideString* pValue = Lookup(ByteString(bsSpace), ByteString(bsName));
95 if (pValue)
96 attr = *pValue;
97 return attr;
98 }
99
GetAttrInteger(const ByteStringView & name) const100 int CXML_Element::GetAttrInteger(const ByteStringView& name) const {
101 ByteStringView bsSpace;
102 ByteStringView bsName;
103 SplitQualifiedName(name, &bsSpace, &bsName);
104
105 const WideString* pwsValue = Lookup(ByteString(bsSpace), ByteString(bsName));
106 return pwsValue ? pwsValue->GetInteger() : 0;
107 }
108
CountElements(const ByteStringView & space,const ByteStringView & tag) const109 size_t CXML_Element::CountElements(const ByteStringView& space,
110 const ByteStringView& tag) const {
111 size_t count = 0;
112 for (const auto& pChild : m_Children) {
113 const CXML_Element* pKid = pChild->AsElement();
114 if (MatchesElement(pKid, space, tag))
115 count++;
116 }
117 return count;
118 }
119
GetChild(size_t index) const120 CXML_Object* CXML_Element::GetChild(size_t index) const {
121 return index < m_Children.size() ? m_Children[index].get() : nullptr;
122 }
123
GetElement(const ByteStringView & space,const ByteStringView & tag,size_t nth) const124 CXML_Element* CXML_Element::GetElement(const ByteStringView& space,
125 const ByteStringView& tag,
126 size_t nth) const {
127 for (const auto& pChild : m_Children) {
128 CXML_Element* pKid = pChild->AsElement();
129 if (MatchesElement(pKid, space, tag)) {
130 if (nth == 0)
131 return pKid;
132 --nth;
133 }
134 }
135 return nullptr;
136 }
137
SetAttribute(const ByteString & space,const ByteString & name,const WideString & value)138 void CXML_Element::SetAttribute(const ByteString& space,
139 const ByteString& name,
140 const WideString& value) {
141 for (CXML_AttrItem& item : m_AttrMap) {
142 if (item.Matches(space, name)) {
143 item.m_Value = value;
144 return;
145 }
146 }
147 m_AttrMap.push_back({space, name, WideString(value)});
148 }
149
150 // static
MatchesElement(const CXML_Element * pKid,const ByteStringView & space,const ByteStringView & tag)151 bool CXML_Element::MatchesElement(const CXML_Element* pKid,
152 const ByteStringView& space,
153 const ByteStringView& tag) {
154 return pKid && pKid->m_TagName == tag &&
155 (space.IsEmpty() || pKid->m_QSpaceName == space);
156 }
157
Lookup(const ByteString & space,const ByteString & name) const158 const WideString* CXML_Element::Lookup(const ByteString& space,
159 const ByteString& name) const {
160 for (const CXML_AttrItem& item : m_AttrMap) {
161 if (item.Matches(space, name))
162 return &item.m_Value;
163 }
164 return nullptr;
165 }
166