blob: 2e37dd78072dcabf63a3bf049a169b5bf2a28fa7 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008
9#include "SkDOM.h"
fmalita7a048692015-02-20 13:54:40 -080010#include "SkStream.h"
Mike Reedc3063e52017-01-07 16:16:02 -050011#include "SkXMLParser.h"
fmalita7a048692015-02-20 13:54:40 -080012#include "SkXMLWriter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013
Mike Reedc3063e52017-01-07 16:16:02 -050014bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000015 const char* elemName = dom.getName(node);
16
Mike Reedc3063e52017-01-07 16:16:02 -050017 if (this->startElement(elemName)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000018 return false;
Mike Reedc3063e52017-01-07 16:16:02 -050019 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000020
reed@android.com8a1c16f2008-12-17 15:59:43 +000021 SkDOM::AttrIter iter(dom, node);
22 const char* name, *value;
rmistry@google.comd6176b02012-08-23 18:14:13 +000023
Mike Reedc3063e52017-01-07 16:16:02 -050024 while ((name = iter.next(&value)) != nullptr) {
25 if (this->addAttribute(name, value)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000026 return false;
Mike Reedc3063e52017-01-07 16:16:02 -050027 }
28 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
Mike Reedc3063e52017-01-07 16:16:02 -050030 if ((node = dom.getFirstChild(node)) != nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 do {
Mike Reedc3063e52017-01-07 16:16:02 -050032 if (!this->parse(dom, node)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000033 return false;
Mike Reedc3063e52017-01-07 16:16:02 -050034 }
halcanary96fcdcc2015-08-27 07:41:13 -070035 } while ((node = dom.getNextSibling(node)) != nullptr);
Mike Reedc3063e52017-01-07 16:16:02 -050036 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000037 return !this->endElement(elemName);
38}
39
40/////////////////////////////////////////////////////////////////////////
41
42struct SkDOMAttr {
43 const char* fName;
44 const char* fValue;
45};
46
47struct SkDOMNode {
48 const char* fName;
49 SkDOMNode* fFirstChild;
50 SkDOMNode* fNextSibling;
Herb Derby70c60632017-02-14 15:21:07 -050051 SkDOMAttr* fAttrs;
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 uint16_t fAttrCount;
53 uint8_t fType;
54 uint8_t fPad;
55
Mike Reedc3063e52017-01-07 16:16:02 -050056 const SkDOMAttr* attrs() const {
Herb Derby70c60632017-02-14 15:21:07 -050057 return fAttrs;
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 }
Mike Reedc3063e52017-01-07 16:16:02 -050059
60 SkDOMAttr* attrs() {
Herb Derby70c60632017-02-14 15:21:07 -050061 return fAttrs;
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 }
63};
64
65/////////////////////////////////////////////////////////////////////////
66
Mike Reedf67c4592017-02-17 17:06:11 -050067#define kMinChunkSize 4096
reed@android.com8a1c16f2008-12-17 15:59:43 +000068
Mike Reedc3063e52017-01-07 16:16:02 -050069SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(nullptr) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000070
Mike Reedc3063e52017-01-07 16:16:02 -050071SkDOM::~SkDOM() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000072
Mike Reedc3063e52017-01-07 16:16:02 -050073const SkDOM::Node* SkDOM::getRootNode() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 return fRoot;
75}
76
Mike Reedc3063e52017-01-07 16:16:02 -050077const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 SkASSERT(node);
79 const Node* child = node->fFirstChild;
80
Mike Reedc3063e52017-01-07 16:16:02 -050081 if (name) {
82 for (; child != nullptr; child = child->fNextSibling) {
83 if (!strcmp(name, child->fName)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 break;
Mike Reedc3063e52017-01-07 16:16:02 -050085 }
86 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 }
88 return child;
89}
90
Mike Reedc3063e52017-01-07 16:16:02 -050091const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 SkASSERT(node);
93 const Node* sibling = node->fNextSibling;
Mike Reedc3063e52017-01-07 16:16:02 -050094 if (name) {
95 for (; sibling != nullptr; sibling = sibling->fNextSibling) {
96 if (!strcmp(name, sibling->fName)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 break;
Mike Reedc3063e52017-01-07 16:16:02 -050098 }
99 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 }
101 return sibling;
102}
103
Mike Reedc3063e52017-01-07 16:16:02 -0500104SkDOM::Type SkDOM::getType(const Node* node) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 SkASSERT(node);
106 return (Type)node->fType;
107}
108
Mike Reedc3063e52017-01-07 16:16:02 -0500109const char* SkDOM::getName(const Node* node) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 SkASSERT(node);
111 return node->fName;
112}
113
Mike Reedc3063e52017-01-07 16:16:02 -0500114const char* SkDOM::findAttr(const Node* node, const char name[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 SkASSERT(node);
116 const Attr* attr = node->attrs();
117 const Attr* stop = attr + node->fAttrCount;
118
Mike Reedc3063e52017-01-07 16:16:02 -0500119 while (attr < stop) {
120 if (!strcmp(attr->fName, name)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 return attr->fValue;
Mike Reedc3063e52017-01-07 16:16:02 -0500122 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 attr += 1;
124 }
halcanary96fcdcc2015-08-27 07:41:13 -0700125 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126}
127
128/////////////////////////////////////////////////////////////////////////////////////
129
Mike Reedc3063e52017-01-07 16:16:02 -0500130const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700131 return node->fAttrCount ? node->attrs() : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132}
133
Mike Reedc3063e52017-01-07 16:16:02 -0500134const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 SkASSERT(node);
Mike Reedc3063e52017-01-07 16:16:02 -0500136 if (attr == nullptr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700137 return nullptr;
Mike Reedc3063e52017-01-07 16:16:02 -0500138 }
halcanary96fcdcc2015-08-27 07:41:13 -0700139 return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140}
141
Mike Reedc3063e52017-01-07 16:16:02 -0500142const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 SkASSERT(node);
144 SkASSERT(attr);
145 return attr->fName;
146}
147
Mike Reedc3063e52017-01-07 16:16:02 -0500148const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 SkASSERT(node);
150 SkASSERT(attr);
151 return attr->fValue;
152}
153
154/////////////////////////////////////////////////////////////////////////////////////
155
Mike Reedc3063e52017-01-07 16:16:02 -0500156SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 SkASSERT(node);
158 fAttr = node->attrs();
159 fStop = fAttr + node->fAttrCount;
160}
161
Mike Reedc3063e52017-01-07 16:16:02 -0500162const char* SkDOM::AttrIter::next(const char** value) {
halcanary96fcdcc2015-08-27 07:41:13 -0700163 const char* name = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
Mike Reedc3063e52017-01-07 16:16:02 -0500165 if (fAttr < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 name = fAttr->fName;
167 if (value)
168 *value = fAttr->fValue;
169 fAttr += 1;
170 }
171 return name;
172}
173
174//////////////////////////////////////////////////////////////////////////////
175
176#include "SkXMLParser.h"
177#include "SkTDArray.h"
178
Herb Derby70c60632017-02-14 15:21:07 -0500179static char* dupstr(SkArenaAlloc* chunk, const char src[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 SkASSERT(chunk && src);
181 size_t len = strlen(src);
Herb Derby70c60632017-02-14 15:21:07 -0500182 char* dst = chunk->makeArrayDefault<char>(len + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 memcpy(dst, src, len + 1);
184 return dst;
185}
186
187class SkDOMParser : public SkXMLParser {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188public:
Herb Derby70c60632017-02-14 15:21:07 -0500189 SkDOMParser(SkArenaAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk) {
fmalita7a048692015-02-20 13:54:40 -0800190 fAlloc->reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700191 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 fLevel = 0;
193 fNeedToFlush = true;
194 }
195 SkDOM::Node* getRoot() const { return fRoot; }
196 SkXMLParserError fParserError;
fmalita7a048692015-02-20 13:54:40 -0800197
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198protected:
Mike Reedc3063e52017-01-07 16:16:02 -0500199 void flushAttributes() {
fmalita7a048692015-02-20 13:54:40 -0800200 SkASSERT(fLevel > 0);
201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 int attrCount = fAttrs.count();
203
Herb Derby70c60632017-02-14 15:21:07 -0500204 SkDOMAttr* attrs = fAlloc->makeArrayDefault<SkDOMAttr>(attrCount);
205 SkDOM::Node* node = fAlloc->make<SkDOM::Node>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206
207 node->fName = fElemName;
halcanary96fcdcc2015-08-27 07:41:13 -0700208 node->fFirstChild = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 node->fAttrCount = SkToU16(attrCount);
Herb Derby70c60632017-02-14 15:21:07 -0500210 node->fAttrs = attrs;
fmalita7a048692015-02-20 13:54:40 -0800211 node->fType = fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212
Mike Reedc3063e52017-01-07 16:16:02 -0500213 if (fRoot == nullptr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700214 node->fNextSibling = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 fRoot = node;
Mike Reedc3063e52017-01-07 16:16:02 -0500216 } else { // this adds siblings in reverse order. gets corrected in onEndElement()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 SkDOM::Node* parent = fParentStack.top();
218 SkASSERT(fRoot && parent);
219 node->fNextSibling = parent->fFirstChild;
220 parent->fFirstChild = node;
221 }
222 *fParentStack.push() = node;
223
mtklein067e90e2015-12-10 07:42:47 -0800224 sk_careful_memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 fAttrs.reset();
226
227 }
fmalita7a048692015-02-20 13:54:40 -0800228
229 bool onStartElement(const char elem[]) override {
230 this->startCommon(elem, SkDOM::kElement_Type);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 return false;
232 }
fmalita7a048692015-02-20 13:54:40 -0800233
234 bool onAddAttribute(const char name[], const char value[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 SkDOM::Attr* attr = fAttrs.append();
236 attr->fName = dupstr(fAlloc, name);
237 attr->fValue = dupstr(fAlloc, value);
238 return false;
239 }
fmalita7a048692015-02-20 13:54:40 -0800240
241 bool onEndElement(const char elem[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 --fLevel;
243 if (fNeedToFlush)
244 this->flushAttributes();
245 fNeedToFlush = false;
246
247 SkDOM::Node* parent;
248
249 fParentStack.pop(&parent);
250
251 SkDOM::Node* child = parent->fFirstChild;
halcanary96fcdcc2015-08-27 07:41:13 -0700252 SkDOM::Node* prev = nullptr;
Mike Reedc3063e52017-01-07 16:16:02 -0500253 while (child) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 SkDOM::Node* next = child->fNextSibling;
255 child->fNextSibling = prev;
256 prev = child;
257 child = next;
258 }
259 parent->fFirstChild = prev;
260 return false;
261 }
fmalita7a048692015-02-20 13:54:40 -0800262
263 bool onText(const char text[], int len) override {
264 SkString str(text, len);
265 this->startCommon(str.c_str(), SkDOM::kText_Type);
266 this->SkDOMParser::onEndElement(str.c_str());
267
268 return false;
269 }
270
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271private:
fmalita7a048692015-02-20 13:54:40 -0800272 void startCommon(const char elem[], SkDOM::Type type) {
Mike Reedc3063e52017-01-07 16:16:02 -0500273 if (fLevel > 0 && fNeedToFlush) {
fmalita7a048692015-02-20 13:54:40 -0800274 this->flushAttributes();
Mike Reedc3063e52017-01-07 16:16:02 -0500275 }
fmalita7a048692015-02-20 13:54:40 -0800276 fNeedToFlush = true;
277 fElemName = dupstr(fAlloc, elem);
278 fElemType = type;
279 ++fLevel;
280 }
281
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 SkTDArray<SkDOM::Node*> fParentStack;
Herb Derby70c60632017-02-14 15:21:07 -0500283 SkArenaAlloc* fAlloc;
fmalita7a048692015-02-20 13:54:40 -0800284 SkDOM::Node* fRoot;
285 bool fNeedToFlush;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286
287 // state needed for flushAttributes()
288 SkTDArray<SkDOM::Attr> fAttrs;
289 char* fElemName;
fmalita7a048692015-02-20 13:54:40 -0800290 SkDOM::Type fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 int fLevel;
292};
293
fmalita7445e862016-07-14 19:14:06 -0700294const SkDOM::Node* SkDOM::build(SkStream& docStream) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 SkDOMParser parser(&fAlloc);
fmalita7445e862016-07-14 19:14:06 -0700296 if (!parser.parse(docStream))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 {
298 SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
halcanary96fcdcc2015-08-27 07:41:13 -0700299 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 fAlloc.reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700301 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 }
303 fRoot = parser.getRoot();
304 return fRoot;
305}
306
307///////////////////////////////////////////////////////////////////////////
308
Mike Reedc3063e52017-01-07 16:16:02 -0500309static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 const char* elem = dom.getName(node);
fmalita7a048692015-02-20 13:54:40 -0800311 if (dom.getType(node) == SkDOM::kText_Type) {
312 SkASSERT(dom.countChildren(node) == 0);
313 parser->text(elem, SkToInt(strlen(elem)));
314 return;
315 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316
317 parser->startElement(elem);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000318
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 SkDOM::AttrIter iter(dom, node);
320 const char* name;
321 const char* value;
halcanary96fcdcc2015-08-27 07:41:13 -0700322 while ((name = iter.next(&value)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 parser->addAttribute(name, value);
324
halcanary96fcdcc2015-08-27 07:41:13 -0700325 node = dom.getFirstChild(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 while (node)
327 {
328 walk_dom(dom, node, parser);
halcanary96fcdcc2015-08-27 07:41:13 -0700329 node = dom.getNextSibling(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 }
331
332 parser->endElement(elem);
333}
334
Mike Reedc3063e52017-01-07 16:16:02 -0500335const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 SkDOMParser parser(&fAlloc);
337
338 walk_dom(dom, node, &parser);
339
340 fRoot = parser.getRoot();
341 return fRoot;
342}
343
fmalita7a048692015-02-20 13:54:40 -0800344SkXMLParser* SkDOM::beginParsing() {
345 SkASSERT(!fParser);
halcanary385fe4d2015-08-26 13:07:48 -0700346 fParser.reset(new SkDOMParser(&fAlloc));
fmalita7a048692015-02-20 13:54:40 -0800347
348 return fParser.get();
349}
350
351const SkDOM::Node* SkDOM::finishParsing() {
352 SkASSERT(fParser);
353 fRoot = fParser->getRoot();
mtklein852f15d2016-03-17 10:51:27 -0700354 fParser.reset();
fmalita7a048692015-02-20 13:54:40 -0800355
356 return fRoot;
357}
358
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359//////////////////////////////////////////////////////////////////////////
360
Mike Reedc3063e52017-01-07 16:16:02 -0500361int SkDOM::countChildren(const Node* node, const char elem[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 int count = 0;
363
364 node = this->getFirstChild(node, elem);
Mike Reedc3063e52017-01-07 16:16:02 -0500365 while (node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 count += 1;
367 node = this->getNextSibling(node, elem);
368 }
369 return count;
370}
371
372//////////////////////////////////////////////////////////////////////////
373
374#include "SkParse.h"
375
Mike Reedc3063e52017-01-07 16:16:02 -0500376bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 const char* vstr = this->findAttr(node, name);
378 return vstr && SkParse::FindS32(vstr, value);
379}
380
Mike Reedc3063e52017-01-07 16:16:02 -0500381bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 const char* vstr = this->findAttr(node, name);
383 return vstr && SkParse::FindScalars(vstr, value, count);
384}
385
Mike Reedc3063e52017-01-07 16:16:02 -0500386bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 const char* vstr = this->findAttr(node, name);
388 return vstr && SkParse::FindHex(vstr, value);
389}
390
Mike Reedc3063e52017-01-07 16:16:02 -0500391bool SkDOM::findBool(const Node* node, const char name[], bool* value) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 const char* vstr = this->findAttr(node, name);
393 return vstr && SkParse::FindBool(vstr, value);
394}
395
Mike Reedc3063e52017-01-07 16:16:02 -0500396int SkDOM::findList(const Node* node, const char name[], const char list[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 const char* vstr = this->findAttr(node, name);
398 return vstr ? SkParse::FindList(vstr, list) : -1;
399}
400
Mike Reedc3063e52017-01-07 16:16:02 -0500401bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 const char* vstr = this->findAttr(node, name);
403 return vstr && !strcmp(vstr, value);
404}
405
Mike Reedc3063e52017-01-07 16:16:02 -0500406bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 const char* vstr = this->findAttr(node, name);
408 int32_t value;
409 return vstr && SkParse::FindS32(vstr, &value) && value == target;
410}
411
Mike Reedc3063e52017-01-07 16:16:02 -0500412bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 const char* vstr = this->findAttr(node, name);
414 SkScalar value;
415 return vstr && SkParse::FindScalar(vstr, &value) && value == target;
416}
417
Mike Reedc3063e52017-01-07 16:16:02 -0500418bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 const char* vstr = this->findAttr(node, name);
420 uint32_t value;
421 return vstr && SkParse::FindHex(vstr, &value) && value == target;
422}
423
Mike Reedc3063e52017-01-07 16:16:02 -0500424bool SkDOM::hasBool(const Node* node, const char name[], bool target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 const char* vstr = this->findAttr(node, name);
426 bool value;
427 return vstr && SkParse::FindBool(vstr, &value) && value == target;
428}