1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Peter Kelly (pmk@post.com)
5  *           (C) 2001 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #ifndef Element_h
26 #define Element_h
27 
28 #include "Document.h"
29 #include "FragmentScriptingPermission.h"
30 #include "NamedNodeMap.h"
31 #include "ScrollTypes.h"
32 
33 namespace WebCore {
34 
35 class Attribute;
36 class ClientRect;
37 class ClientRectList;
38 class DOMStringMap;
39 class DOMTokenList;
40 class ElementRareData;
41 class IntSize;
42 class ShadowRoot;
43 class WebKitAnimationList;
44 
45 enum SpellcheckAttributeState {
46     SpellcheckAttributeTrue,
47     SpellcheckAttributeFalse,
48     SpellcheckAttributeDefault
49 };
50 
51 class Element : public ContainerNode {
52 public:
53     static PassRefPtr<Element> create(const QualifiedName&, Document*);
54     virtual ~Element();
55 
56     DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
57     DEFINE_ATTRIBUTE_EVENT_LISTENER(change);
58     DEFINE_ATTRIBUTE_EVENT_LISTENER(click);
59     DEFINE_ATTRIBUTE_EVENT_LISTENER(contextmenu);
60     DEFINE_ATTRIBUTE_EVENT_LISTENER(dblclick);
61     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragenter);
62     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragover);
63     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragleave);
64     DEFINE_ATTRIBUTE_EVENT_LISTENER(drop);
65     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart);
66     DEFINE_ATTRIBUTE_EVENT_LISTENER(drag);
67     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend);
68     DEFINE_ATTRIBUTE_EVENT_LISTENER(input);
69     DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid);
70     DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown);
71     DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress);
72     DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup);
73     DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown);
74     DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove);
75     DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout);
76     DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover);
77     DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseup);
78     DEFINE_ATTRIBUTE_EVENT_LISTENER(mousewheel);
79     DEFINE_ATTRIBUTE_EVENT_LISTENER(scroll);
80     DEFINE_ATTRIBUTE_EVENT_LISTENER(select);
81     DEFINE_ATTRIBUTE_EVENT_LISTENER(submit);
82 
83     // These four attribute event handler attributes are overridden by HTMLBodyElement
84     // and HTMLFrameSetElement to forward to the DOMWindow.
85     DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(blur);
86     DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(error);
87     DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(focus);
88     DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(load);
89 
90     // WebKit extensions
91     DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecut);
92     DEFINE_ATTRIBUTE_EVENT_LISTENER(cut);
93     DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecopy);
94     DEFINE_ATTRIBUTE_EVENT_LISTENER(copy);
95     DEFINE_ATTRIBUTE_EVENT_LISTENER(beforepaste);
96     DEFINE_ATTRIBUTE_EVENT_LISTENER(paste);
97     DEFINE_ATTRIBUTE_EVENT_LISTENER(reset);
98     DEFINE_ATTRIBUTE_EVENT_LISTENER(search);
99     DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart);
100 #if ENABLE(TOUCH_EVENTS)
101     DEFINE_ATTRIBUTE_EVENT_LISTENER(touchstart);
102     DEFINE_ATTRIBUTE_EVENT_LISTENER(touchmove);
103     DEFINE_ATTRIBUTE_EVENT_LISTENER(touchend);
104     DEFINE_ATTRIBUTE_EVENT_LISTENER(touchcancel);
105 #endif
106 #if ENABLE(FULLSCREEN_API)
107     DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenchange);
108 #endif
109 
110     virtual PassRefPtr<DocumentFragment> deprecatedCreateContextualFragment(const String&, FragmentScriptingPermission = FragmentScriptingAllowed);
111 
112     bool hasAttribute(const QualifiedName&) const;
113     const AtomicString& getAttribute(const QualifiedName&) const;
114     void setAttribute(const QualifiedName&, const AtomicString& value, ExceptionCode&);
115     void removeAttribute(const QualifiedName&, ExceptionCode&);
116 
117     // Typed getters and setters for language bindings.
118     int getIntegralAttribute(const QualifiedName& attributeName) const;
119     void setIntegralAttribute(const QualifiedName& attributeName, int value);
120     unsigned getUnsignedIntegralAttribute(const QualifiedName& attributeName) const;
121     void setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value);
122 
123     // Call this to get the value of an attribute that is known not to be the style
124     // attribute or one of the SVG animatable attributes.
125     bool fastHasAttribute(const QualifiedName&) const;
126     const AtomicString& fastGetAttribute(const QualifiedName&) const;
127 
128     bool hasAttributes() const;
129 
130     bool hasAttribute(const String& name) const;
131     bool hasAttributeNS(const String& namespaceURI, const String& localName) const;
132 
133     const AtomicString& getAttribute(const String& name) const;
134     const AtomicString& getAttributeNS(const String& namespaceURI, const String& localName) const;
135 
136     void setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode&);
137     void setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value, ExceptionCode&, FragmentScriptingPermission = FragmentScriptingAllowed);
138 
139     bool isIdAttributeName(const QualifiedName&) const;
140     const AtomicString& getIdAttribute() const;
141     void setIdAttribute(const AtomicString&);
142 
143     // Call this to get the value of the id attribute for style resolution purposes.
144     // The value will already be lowercased if the document is in compatibility mode,
145     // so this function is not suitable for non-style uses.
146     const AtomicString& idForStyleResolution() const;
147 
148     void scrollIntoView(bool alignToTop = true);
149     void scrollIntoViewIfNeeded(bool centerIfNeeded = true);
150 
151     void scrollByLines(int lines);
152     void scrollByPages(int pages);
153 
154     int offsetLeft();
155     int offsetTop();
156     int offsetWidth();
157     int offsetHeight();
158     Element* offsetParent();
159     int clientLeft();
160     int clientTop();
161     int clientWidth();
162     int clientHeight();
163     virtual int scrollLeft() const;
164     virtual int scrollTop() const;
165     virtual void setScrollLeft(int);
166     virtual void setScrollTop(int);
167     virtual int scrollWidth() const;
168     virtual int scrollHeight() const;
169 
170     IntRect boundsInWindowSpace() const;
171 
172     PassRefPtr<ClientRectList> getClientRects() const;
173     PassRefPtr<ClientRect> getBoundingClientRect() const;
174 
175     // Returns the absolute bounding box translated into screen coordinates:
176     IntRect screenRect() const;
177 
178     void removeAttribute(const String& name, ExceptionCode&);
179     void removeAttributeNS(const String& namespaceURI, const String& localName, ExceptionCode&);
180 
181     PassRefPtr<Attr> getAttributeNode(const String& name);
182     PassRefPtr<Attr> getAttributeNodeNS(const String& namespaceURI, const String& localName);
183     PassRefPtr<Attr> setAttributeNode(Attr*, ExceptionCode&);
184     PassRefPtr<Attr> setAttributeNodeNS(Attr*, ExceptionCode&);
185     PassRefPtr<Attr> removeAttributeNode(Attr*, ExceptionCode&);
186 
187     virtual CSSStyleDeclaration* style();
188 
tagQName()189     const QualifiedName& tagQName() const { return m_tagName; }
tagName()190     String tagName() const { return nodeName(); }
hasTagName(const QualifiedName & tagName)191     bool hasTagName(const QualifiedName& tagName) const { return m_tagName.matches(tagName); }
192 
193     // A fast function for checking the local name against another atomic string.
hasLocalName(const AtomicString & other)194     bool hasLocalName(const AtomicString& other) const { return m_tagName.localName() == other; }
hasLocalName(const QualifiedName & other)195     bool hasLocalName(const QualifiedName& other) const { return m_tagName.localName() == other.localName(); }
196 
localName()197     const AtomicString& localName() const { return m_tagName.localName(); }
prefix()198     const AtomicString& prefix() const { return m_tagName.prefix(); }
namespaceURI()199     const AtomicString& namespaceURI() const { return m_tagName.namespaceURI(); }
200 
201     virtual KURL baseURI() const;
202 
203     virtual String nodeName() const;
204 
205     PassRefPtr<Element> cloneElementWithChildren();
206     PassRefPtr<Element> cloneElementWithoutChildren();
207 
208     void normalizeAttributes();
209     String nodeNamePreservingCase() const;
210 
211     // convenience methods which ignore exceptions
212     void setAttribute(const QualifiedName&, const AtomicString& value);
213     void setBooleanAttribute(const QualifiedName& name, bool);
214     // Please don't use setCStringAttribute in performance-sensitive code;
215     // use a static AtomicString value instead to avoid the conversion overhead.
216     void setCStringAttribute(const QualifiedName&, const char* cStringValue);
217 
218     NamedNodeMap* attributes(bool readonly = false) const;
219 
220     // This method is called whenever an attribute is added, changed or removed.
221     virtual void attributeChanged(Attribute*, bool preserveDecls = false);
222 
223     void setAttributeMap(PassRefPtr<NamedNodeMap>, FragmentScriptingPermission = FragmentScriptingAllowed);
attributeMap()224     NamedNodeMap* attributeMap() const { return m_attributeMap.get(); }
225 
226     virtual void copyNonAttributeProperties(const Element* source);
227 
228     virtual void attach();
229     virtual void detach();
230     virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
231     virtual void recalcStyle(StyleChange = NoChange);
232 
233     ShadowRoot* shadowRoot() const;
234     ShadowRoot* ensureShadowRoot();
235     void removeShadowRoot();
236 
237     virtual const AtomicString& shadowPseudoId() const;
238 
239     RenderStyle* computedStyle(PseudoId = NOPSEUDO);
240 
241     AtomicString computeInheritedLanguage() const;
242 
243     void dispatchAttrRemovalEvent(Attribute*);
244     void dispatchAttrAdditionEvent(Attribute*);
245 
accessKeyAction(bool)246     virtual void accessKeyAction(bool /*sendToAnyEvent*/) { }
247 
248     virtual bool isURLAttribute(Attribute*) const;
249 
250     KURL getURLAttribute(const QualifiedName&) const;
251     KURL getNonEmptyURLAttribute(const QualifiedName&) const;
252 
253     virtual const QualifiedName& imageSourceAttributeName() const;
target()254     virtual String target() const { return String(); }
255 
256     virtual void focus(bool restorePreviousSelection = true);
257     virtual void updateFocusAppearance(bool restorePreviousSelection);
258     void blur();
259 
260     String innerText() const;
261     String outerText() const;
262 
263     virtual String title() const;
264 
265     String openTagStartToString() const;
266 
267     void updateId(const AtomicString& oldId, const AtomicString& newId);
268 
269     IntSize minimumSizeForResizing() const;
270     void setMinimumSizeForResizing(const IntSize&);
271 
272     // Use Document::registerForDocumentActivationCallbacks() to subscribe to these
documentWillBecomeInactive()273     virtual void documentWillBecomeInactive() { }
documentDidBecomeActive()274     virtual void documentDidBecomeActive() { }
275 
276     // Use Document::registerForMediaVolumeCallbacks() to subscribe to this
mediaVolumeDidChange()277     virtual void mediaVolumeDidChange() { }
278 
279     // Use Document::registerForPrivateBrowsingStateChangedCallbacks() to subscribe to this.
privateBrowsingStateDidChange()280     virtual void privateBrowsingStateDidChange() { }
281 
isFinishedParsingChildren()282     bool isFinishedParsingChildren() const { return isParsingChildrenFinished(); }
283     virtual void finishParsingChildren();
284     virtual void beginParsingChildren();
285 
286     // ElementTraversal API
287     Element* firstElementChild() const;
288     Element* lastElementChild() const;
289     Element* previousElementSibling() const;
290     Element* nextElementSibling() const;
291     unsigned childElementCount() const;
292 
293     bool webkitMatchesSelector(const String& selectors, ExceptionCode&);
294 
295     DOMTokenList* classList();
296     DOMTokenList* optionalClassList() const;
297 
298     DOMStringMap* dataset();
299 
300 #if ENABLE(MATHML)
isMathMLElement()301     virtual bool isMathMLElement() const { return false; }
302 #else
isMathMLElement()303     static bool isMathMLElement() { return false; }
304 #endif
305 
306 #if ENABLE(VIDEO)
isMediaElement()307     virtual bool isMediaElement() const { return false; }
308 #endif
309 
310 #if ENABLE(INPUT_SPEECH)
isInputFieldSpeechButtonElement()311     virtual bool isInputFieldSpeechButtonElement() const { return false; }
312 #endif
313 
isFormControlElement()314     virtual bool isFormControlElement() const { return false; }
isEnabledFormControl()315     virtual bool isEnabledFormControl() const { return true; }
isReadOnlyFormControl()316     virtual bool isReadOnlyFormControl() const { return false; }
isSpinButtonElement()317     virtual bool isSpinButtonElement() const { return false; }
isTextFormControl()318     virtual bool isTextFormControl() const { return false; }
isOptionalFormControl()319     virtual bool isOptionalFormControl() const { return false; }
isRequiredFormControl()320     virtual bool isRequiredFormControl() const { return false; }
isDefaultButtonForForm()321     virtual bool isDefaultButtonForForm() const { return false; }
willValidate()322     virtual bool willValidate() const { return false; }
isValidFormControlElement()323     virtual bool isValidFormControlElement() { return false; }
hasUnacceptableValue()324     virtual bool hasUnacceptableValue() const { return false; }
isInRange()325     virtual bool isInRange() const { return false; }
isOutOfRange()326     virtual bool isOutOfRange() const { return false; }
isFrameElementBase()327     virtual bool isFrameElementBase() const { return false; }
328 
formControlValueMatchesRenderer()329     virtual bool formControlValueMatchesRenderer() const { return false; }
setFormControlValueMatchesRenderer(bool)330     virtual void setFormControlValueMatchesRenderer(bool) { }
331 
formControlName()332     virtual const AtomicString& formControlName() const { return nullAtom; }
formControlType()333     virtual const AtomicString& formControlType() const { return nullAtom; }
334 
shouldSaveAndRestoreFormControlState()335     virtual bool shouldSaveAndRestoreFormControlState() const { return true; }
saveFormControlState(String &)336     virtual bool saveFormControlState(String&) const { return false; }
restoreFormControlState(const String &)337     virtual void restoreFormControlState(const String&) { }
338 
339     virtual bool wasChangedSinceLastFormControlChangeEvent() const;
340     virtual void setChangedSinceLastFormControlChangeEvent(bool);
dispatchFormControlChangeEvent()341     virtual void dispatchFormControlChangeEvent() { }
342 
343 #if ENABLE(SVG)
344     virtual bool childShouldCreateRenderer(Node*) const;
345 #endif
346 
347 #if ENABLE(FULLSCREEN_API)
348     enum {
349         ALLOW_KEYBOARD_INPUT = 1
350     };
351 
352     void webkitRequestFullScreen(unsigned short flags);
353 #endif
354 
355     virtual bool isSpellCheckingEnabled() const;
356 
357     PassRefPtr<WebKitAnimationList> webkitGetAnimations() const;
358 
359 protected:
Element(const QualifiedName & tagName,Document * document,ConstructionType type)360     Element(const QualifiedName& tagName, Document* document, ConstructionType type)
361         : ContainerNode(document, type)
362         , m_tagName(tagName)
363     {
364     }
365 
366     virtual void insertedIntoDocument();
367     virtual void removedFromDocument();
368     virtual void insertedIntoTree(bool);
369     virtual void removedFromTree(bool);
370     virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0);
371 
372     // The implementation of Element::attributeChanged() calls the following two functions.
373     // They are separated to allow a different flow of control in StyledElement::attributeChanged().
374     void recalcStyleIfNeededAfterAttributeChanged(Attribute*);
375     void updateAfterAttributeChanged(Attribute*);
376 
377     void idAttributeChanged(Attribute*);
378 
379 private:
380     void scrollByUnits(int units, ScrollGranularity);
381 
382     virtual void setPrefix(const AtomicString&, ExceptionCode&);
383     virtual NodeType nodeType() const;
384     virtual bool childTypeAllowed(NodeType) const;
385 
386     virtual PassRefPtr<Attribute> createAttribute(const QualifiedName&, const AtomicString& value);
387 
388 #ifndef NDEBUG
389     virtual void formatForDebugger(char* buffer, unsigned length) const;
390 #endif
391 
392     bool pseudoStyleCacheIsInvalid(const RenderStyle* currentStyle, RenderStyle* newStyle);
393 
394     void createAttributeMap() const;
395 
updateStyleAttribute()396     virtual void updateStyleAttribute() const { }
397 
398 #if ENABLE(SVG)
updateAnimatedSVGAttribute(const QualifiedName &)399     virtual void updateAnimatedSVGAttribute(const QualifiedName&) const { }
400 #endif
401 
402     void cancelFocusAppearanceUpdate();
403 
virtualPrefix()404     virtual const AtomicString& virtualPrefix() const { return prefix(); }
virtualLocalName()405     virtual const AtomicString& virtualLocalName() const { return localName(); }
virtualNamespaceURI()406     virtual const AtomicString& virtualNamespaceURI() const { return namespaceURI(); }
407     virtual RenderStyle* virtualComputedStyle(PseudoId pseudoElementSpecifier = NOPSEUDO) { return computedStyle(pseudoElementSpecifier); }
408 
409     // cloneNode is private so that non-virtual cloneElementWithChildren and cloneElementWithoutChildren
410     // are used instead.
411     virtual PassRefPtr<Node> cloneNode(bool deep);
412     virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const;
413 
414     QualifiedName m_tagName;
415     virtual NodeRareData* createRareData();
416 
417     ElementRareData* rareData() const;
418     ElementRareData* ensureRareData();
419 
420     SpellcheckAttributeState spellcheckAttributeState() const;
421 
422 private:
423     mutable RefPtr<NamedNodeMap> m_attributeMap;
424 };
425 
toElement(Node * node)426 inline Element* toElement(Node* node)
427 {
428     ASSERT(!node || node->isElementNode());
429     return static_cast<Element*>(node);
430 }
431 
toElement(const Node * node)432 inline const Element* toElement(const Node* node)
433 {
434     ASSERT(!node || node->isElementNode());
435     return static_cast<const Element*>(node);
436 }
437 
438 // This will catch anyone doing an unnecessary cast.
439 void toElement(const Element*);
440 
hasTagName(const QualifiedName & name)441 inline bool Node::hasTagName(const QualifiedName& name) const
442 {
443     return isElementNode() && toElement(this)->hasTagName(name);
444 }
445 
hasLocalName(const AtomicString & name)446 inline bool Node::hasLocalName(const AtomicString& name) const
447 {
448     return isElementNode() && toElement(this)->hasLocalName(name);
449 }
450 
hasAttributes()451 inline bool Node::hasAttributes() const
452 {
453     return isElementNode() && toElement(this)->hasAttributes();
454 }
455 
attributes()456 inline NamedNodeMap* Node::attributes() const
457 {
458     return isElementNode() ? toElement(this)->attributes() : 0;
459 }
460 
parentElement()461 inline Element* Node::parentElement() const
462 {
463     ContainerNode* parent = parentNode();
464     return parent && parent->isElementNode() ? toElement(parent) : 0;
465 }
466 
attributes(bool readonly)467 inline NamedNodeMap* Element::attributes(bool readonly) const
468 {
469     if (!isStyleAttributeValid())
470         updateStyleAttribute();
471 
472 #if ENABLE(SVG)
473     if (!areSVGAttributesValid())
474         updateAnimatedSVGAttribute(anyQName());
475 #endif
476 
477     if (!readonly && !m_attributeMap)
478         createAttributeMap();
479     return m_attributeMap.get();
480 }
481 
updateId(const AtomicString & oldId,const AtomicString & newId)482 inline void Element::updateId(const AtomicString& oldId, const AtomicString& newId)
483 {
484     if (!inDocument())
485         return;
486 
487     if (oldId == newId)
488         return;
489 
490     TreeScope* scope = treeScope();
491     if (!oldId.isEmpty())
492         scope->removeElementById(oldId, this);
493     if (!newId.isEmpty())
494         scope->addElementById(newId, this);
495 }
496 
fastHasAttribute(const QualifiedName & name)497 inline bool Element::fastHasAttribute(const QualifiedName& name) const
498 {
499     return m_attributeMap && m_attributeMap->getAttributeItem(name);
500 }
501 
fastGetAttribute(const QualifiedName & name)502 inline const AtomicString& Element::fastGetAttribute(const QualifiedName& name) const
503 {
504     if (m_attributeMap) {
505         if (Attribute* attribute = m_attributeMap->getAttributeItem(name))
506             return attribute->value();
507     }
508     return nullAtom;
509 }
510 
idForStyleResolution()511 inline const AtomicString& Element::idForStyleResolution() const
512 {
513     ASSERT(hasID());
514     return m_attributeMap->idForStyleResolution();
515 }
516 
isIdAttributeName(const QualifiedName & attributeName)517 inline bool Element::isIdAttributeName(const QualifiedName& attributeName) const
518 {
519     // FIXME: This check is probably not correct for the case where the document has an id attribute
520     // with a non-null namespace, because it will return false, a false negative, if the prefixes
521     // don't match but the local name and namespace both do. However, since this has been like this
522     // for a while and the code paths may be hot, we'll have to measure performance if we fix it.
523     return attributeName == document()->idAttributeName();
524 }
525 
getIdAttribute()526 inline const AtomicString& Element::getIdAttribute() const
527 {
528     return fastGetAttribute(document()->idAttributeName());
529 }
530 
setIdAttribute(const AtomicString & value)531 inline void Element::setIdAttribute(const AtomicString& value)
532 {
533     setAttribute(document()->idAttributeName(), value);
534 }
535 
shadowPseudoId()536 inline const AtomicString& Element::shadowPseudoId() const
537 {
538     return nullAtom;
539 }
540 
firstElementChild(const ContainerNode * container)541 inline Element* firstElementChild(const ContainerNode* container)
542 {
543     ASSERT_ARG(container, container);
544     Node* child = container->firstChild();
545     while (child && !child->isElementNode())
546         child = child->nextSibling();
547     return static_cast<Element*>(child);
548 }
549 
550 } // namespace
551 
552 #endif
553