1 
2 /*
3  * Copyright 2006 The Android Open Source Project
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #include "SkDisplayXMLParser.h"
11 #include "SkAnimateMaker.h"
12 #include "SkDisplayApply.h"
13 #include "SkUtils.h"
14 #ifdef SK_DEBUG
15 #include "SkTime.h"
16 #endif
17 
18 static char const* const gErrorStrings[] = {
19     "unknown error ",
20     "apply scopes itself",
21     "display tree too deep (circular reference?) ",
22     "element missing parent ",
23     "element type not allowed in parent ",
24     "error adding <data> to <post> ",
25     "error adding to <matrix> ",
26     "error adding to <paint> ",
27     "error adding to <path> ",
28     "error in attribute value ",
29     "error in script ",
30     "expected movie in sink attribute ",
31     "field not in target ",
32     "number of offsets in gradient must match number of colors",
33     "no offset in gradient may be greater than one",
34     "last offset in gradient must be one",
35     "offsets in gradient must be increasing",
36     "first offset in gradient must be zero",
37     "gradient attribute \"points\" must have length of four",
38     "in include ",
39     "in movie ",
40     "include name unknown or missing ",
41     "index out of range ",
42     "movie name unknown or missing ",
43     "no parent available to resolve sink attribute ",
44     "parent element can't contain ",
45     "saveLayer must specify a bounds",
46     "target id not found ",
47     "unexpected type "
48 };
49 
~SkDisplayXMLParserError()50 SkDisplayXMLParserError::~SkDisplayXMLParserError() {
51 }
52 
getErrorString(SkString * str) const53 void SkDisplayXMLParserError::getErrorString(SkString* str) const {
54     if (fCode > kUnknownError)
55         str->set(gErrorStrings[fCode - kUnknownError]);
56     else
57         str->reset();
58     INHERITED::getErrorString(str);
59 }
60 
setInnerError(SkAnimateMaker * parent,const SkString & src)61 void SkDisplayXMLParserError::setInnerError(SkAnimateMaker* parent, const SkString& src) {
62     SkString inner;
63     getErrorString(&inner);
64     inner.prepend(": ");
65     inner.prependS32(getLineNumber());
66     inner.prepend(", line ");
67     inner.prepend(src);
68     parent->setErrorNoun(inner);
69 }
70 
71 
SkDisplayXMLParser(SkAnimateMaker & maker)72 SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker)
73     : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude),
74         fInSkia(maker.fInInclude), fCurrDisplayable(NULL)
75 {
76 }
77 
~SkDisplayXMLParser()78 SkDisplayXMLParser::~SkDisplayXMLParser() {
79     if (fCurrDisplayable && fMaker.fChildren.find(fCurrDisplayable) < 0)
80         delete fCurrDisplayable;
81     for (Parent* parPtr = fParents.begin() + 1; parPtr < fParents.end(); parPtr++) {
82         SkDisplayable* displayable = parPtr->fDisplayable;
83         if (displayable == fCurrDisplayable)
84             continue;
85         SkASSERT(fMaker.fChildren.find(displayable) < 0);
86         if (fMaker.fHelpers.find(displayable) < 0)
87             delete displayable;
88     }
89 }
90 
91 
92 
onAddAttribute(const char name[],const char value[])93 bool SkDisplayXMLParser::onAddAttribute(const char name[], const char value[]) {
94     return onAddAttributeLen(name, value, strlen(value));
95 }
96 
onAddAttributeLen(const char attrName[],const char attrValue[],size_t attrValueLen)97 bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[],
98                                         size_t attrValueLen)
99 {
100     if (fCurrDisplayable == NULL)    // this signals we should ignore attributes for this element
101         return strncmp(attrName, "xmlns", sizeof("xmlns") - 1) != 0;
102     SkDisplayable*  displayable = fCurrDisplayable;
103     SkDisplayTypes  type = fCurrType;
104 
105     if (strcmp(attrName, "id") == 0) {
106         if (fMaker.find(attrValue, attrValueLen, NULL)) {
107             fError->setNoun(attrValue, attrValueLen);
108             fError->setCode(SkXMLParserError::kDuplicateIDs);
109             return true;
110         }
111 #ifdef SK_DEBUG
112         displayable->_id.set(attrValue, attrValueLen);
113         displayable->id = displayable->_id.c_str();
114 #endif
115         fMaker.idsSet(attrValue, attrValueLen, displayable);
116         int parentIndex = fParents.count() - 1;
117         if (parentIndex > 0) {
118             SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
119             parent->setChildHasID();
120         }
121         return false;
122     }
123     const char* name = attrName;
124     const SkMemberInfo* info = SkDisplayType::GetMember(&fMaker, type, &name);
125     if (info == NULL) {
126         fError->setNoun(name);
127         fError->setCode(SkXMLParserError::kUnknownAttributeName);
128         return true;
129     }
130     if (info->setValue(fMaker, NULL, 0, info->getCount(), displayable, info->getType(), attrValue,
131             attrValueLen))
132         return false;
133     if (fMaker.fError.hasError()) {
134         fError->setNoun(attrValue, attrValueLen);
135         return true;
136     }
137     SkDisplayable* ref = NULL;
138     if (fMaker.find(attrValue, attrValueLen, &ref) == false) {
139         ref = fMaker.createInstance(attrValue, attrValueLen);
140         if (ref == NULL) {
141             fError->setNoun(attrValue, attrValueLen);
142             fError->setCode(SkXMLParserError::kErrorInAttributeValue);
143             return true;
144         } else
145             fMaker.helperAdd(ref);
146     }
147     if (info->fType != SkType_MemberProperty) {
148         fError->setNoun(name);
149         fError->setCode(SkXMLParserError::kUnknownAttributeName);
150         return true;
151     }
152     SkScriptValue scriptValue;
153     scriptValue.fOperand.fDisplayable = ref;
154     scriptValue.fType = ref->getType();
155     displayable->setProperty(info->propertyIndex(), scriptValue);
156     return false;
157 }
158 
159 #if defined(SK_BUILD_FOR_WIN32)
160     #define SK_strcasecmp   _stricmp
161     #define SK_strncasecmp  _strnicmp
162 #else
163     #define SK_strcasecmp   strcasecmp
164     #define SK_strncasecmp  strncasecmp
165 #endif
166 
onEndElement(const char elem[])167 bool SkDisplayXMLParser::onEndElement(const char elem[])
168 {
169     int parentIndex = fParents.count() - 1;
170     if (parentIndex >= 0) {
171         Parent& container = fParents[parentIndex];
172         SkDisplayable* displayable = container.fDisplayable;
173         fMaker.fEndDepth = parentIndex;
174         displayable->onEndElement(fMaker);
175         if (fMaker.fError.hasError())
176             return true;
177         if (parentIndex > 0) {
178             SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
179             bool result = parent->addChild(fMaker, displayable);
180             if (fMaker.hasError())
181                 return true;
182             if (result == false) {
183                 int infoCount;
184                 const SkMemberInfo* info =
185                     SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount);
186                 const SkMemberInfo* foundInfo;
187                 if ((foundInfo = searchContainer(info, infoCount)) != NULL) {
188                     parent->setReference(foundInfo, displayable);
189         //          if (displayable->isHelper() == false)
190                         fMaker.helperAdd(displayable);
191                 } else {
192                     fMaker.setErrorCode(SkDisplayXMLParserError::kElementTypeNotAllowedInParent);
193                     return true;
194                 }
195             }
196             if (parent->childrenNeedDisposing())
197                 delete displayable;
198         }
199         fParents.remove(parentIndex);
200     }
201     fCurrDisplayable = NULL;
202     if (fInInclude == false && SK_strcasecmp(elem, "screenplay") == 0) {
203         if (fMaker.fInMovie == false) {
204             fMaker.fEnableTime = fMaker.getAppTime();
205 #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
206             if (fMaker.fDebugTimeBase == (SkMSec) -1)
207                 fMaker.fDebugTimeBase = fMaker.fEnableTime;
208             SkString debugOut;
209             SkMSec time = fMaker.getAppTime();
210             debugOut.appendS32(time - fMaker.fDebugTimeBase);
211             debugOut.append(" onLoad enable=");
212             debugOut.appendS32(fMaker.fEnableTime - fMaker.fDebugTimeBase);
213             SkDebugf("%s\n", debugOut.c_str());
214 #endif
215             fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL);
216             if (fMaker.fError.hasError())
217                 return true;
218             fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
219 
220         }
221         fInSkia = false;
222     }
223     return false;
224 }
225 
onStartElement(const char name[])226 bool SkDisplayXMLParser::onStartElement(const char name[])
227 {
228     return onStartElementLen(name, strlen(name));
229 }
230 
onStartElementLen(const char name[],size_t len)231 bool SkDisplayXMLParser::onStartElementLen(const char name[], size_t len) {
232     fCurrDisplayable = NULL; // init so we'll ignore attributes if we exit early
233 
234     if (SK_strncasecmp(name, "screenplay", len) == 0) {
235         fInSkia = true;
236         if (fInInclude == false)
237             fMaker.idsSet(name, len, &fMaker.fScreenplay);
238         return false;
239     }
240     if (fInSkia == false)
241         return false;
242 
243     SkDisplayable* displayable = fMaker.createInstance(name, len);
244     if (displayable == NULL) {
245         fError->setNoun(name, len);
246         fError->setCode(SkXMLParserError::kUnknownElement);
247         return true;
248     }
249     SkDisplayTypes type = displayable->getType();
250     Parent record = { displayable, type };
251     *fParents.append() = record;
252     if (fParents.count() == 1)
253         fMaker.childrenAdd(displayable);
254     else {
255         Parent* parent = fParents.end() - 2;
256         if (displayable->setParent(parent->fDisplayable)) {
257             fError->setNoun(name, len);
258             getError()->setCode(SkDisplayXMLParserError::kParentElementCantContain);
259             return true;
260         }
261     }
262 
263     // set these for subsequent calls to addAttribute()
264     fCurrDisplayable = displayable;
265     fCurrType = type;
266     return false;
267 }
268 
searchContainer(const SkMemberInfo * infoBase,int infoCount)269 const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* infoBase,
270                                                          int infoCount) {
271     const SkMemberInfo* bestDisplayable = NULL;
272     const SkMemberInfo* lastResort = NULL;
273     for (int index = 0; index < infoCount; index++) {
274         const SkMemberInfo* info = &infoBase[index];
275         if (info->fType == SkType_BaseClassInfo) {
276             const SkMemberInfo* inherited = info->getInherited();
277             const SkMemberInfo* result = searchContainer(inherited, info->fCount);
278             if (result != NULL)
279                 return result;
280             continue;
281         }
282         Parent* container = fParents.end() - 1;
283         SkDisplayTypes type = (SkDisplayTypes) info->fType;
284         if (type == SkType_MemberProperty)
285             type = info->propertyType();
286         SkDisplayTypes containerType = container->fType;
287         if (type == containerType && (type == SkType_Rect || type == SkType_Polygon ||
288             type == SkType_Array || type == SkType_Int || type == SkType_Bitmap))
289             goto rectNext;
290         while (type != containerType) {
291             if (containerType == SkType_Displayable)
292                 goto next;
293             containerType = SkDisplayType::GetParent(&fMaker, containerType);
294             if (containerType == SkType_Unknown)
295                 goto next;
296         }
297         return info;
298 next:
299         if (type == SkType_Drawable || (type == SkType_Displayable &&
300             container->fDisplayable->isDrawable())) {
301 rectNext:
302             if (fParents.count() > 1) {
303                 Parent* parent = fParents.end() - 2;
304                 if (info == parent->fDisplayable->preferredChild(type))
305                     bestDisplayable = info;
306                 else
307                     lastResort = info;
308             }
309         }
310     }
311     if (bestDisplayable)
312         return bestDisplayable;
313     if (lastResort)
314         return lastResort;
315     return NULL;
316 }
317