/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) * Copyright (C) 2003-2011, 2013, 2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef SKY_ENGINE_CORE_DOM_ELEMENT_H_ #define SKY_ENGINE_CORE_DOM_ELEMENT_H_ #include "gen/sky/core/CSSPropertyNames.h" #include "gen/sky/core/HTMLNames.h" #include "sky/engine/core/css/CSSPrimitiveValue.h" #include "sky/engine/core/dom/Attribute.h" #include "sky/engine/core/dom/ContainerNode.h" #include "sky/engine/core/dom/ElementData.h" #include "sky/engine/core/dom/SpaceSplitString.h" #include "sky/engine/core/page/FocusType.h" #include "sky/engine/platform/heap/Handle.h" namespace blink { class ActiveAnimations; class Attr; class Attribute; class CSSStyleDeclaration; class Canvas; class ClientRect; class ClientRectList; class DOMTokenList; class Document; class ElementRareData; class ElementShadow; class ExceptionState; class Image; class IntSize; class LayoutCallback; class MutableStylePropertySet; class PaintingCallback; class PropertySetCSSStyleDeclaration; class PseudoElement; class ShadowRoot; class StylePropertySet; enum SpellcheckAttributeState { SpellcheckAttributeTrue, SpellcheckAttributeFalse, SpellcheckAttributeDefault }; class Element : public ContainerNode { DEFINE_WRAPPERTYPEINFO(); public: static PassRefPtr create(const QualifiedName&, Document*); virtual ~Element(); bool hasAttribute(const QualifiedName&) const; const AtomicString& getAttribute(const QualifiedName&) const; Vector> getAttributes(); bool hasAttributes() const; bool hasAttribute(const AtomicString& name) const; const AtomicString& getAttribute(const AtomicString& name) const; void setAttribute(const AtomicString& name, const AtomicString& value, ExceptionState&); void setAttribute(const AtomicString& name, ExceptionState& es) { setAttribute(name, String(), es); } void removeAttribute(const AtomicString& name); void removeAttribute(const QualifiedName&); // Passing nullAtom as the second parameter removes the attribute when calling either of these set methods. void setAttribute(const QualifiedName&, const AtomicString& value); void setSynchronizedLazyAttribute(const QualifiedName&, const AtomicString& value); // Typed getters and setters for language bindings. int getIntegralAttribute(const QualifiedName& attributeName) const; void setIntegralAttribute(const QualifiedName& attributeName, int value); unsigned getUnsignedIntegralAttribute(const QualifiedName& attributeName) const; void setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value); double getFloatingPointAttribute(const QualifiedName& attributeName, double fallbackValue = std::numeric_limits::quiet_NaN()) const; void setFloatingPointAttribute(const QualifiedName& attributeName, double value); const AtomicString& getIdAttribute() const; void setIdAttribute(const AtomicString&); const AtomicString& getClassAttribute() const; // Call this to get the value of the id attribute for style resolution purposes. // The value will already be lowercased if the document is in compatibility mode, // so this function is not suitable for non-style uses. const AtomicString& idForStyleResolution() const; // This getter takes care of synchronizing all attributes before returning the // AttributeCollection. If the Element has no attributes, an empty AttributeCollection // will be returned. This is not a trivial getter and its return value should be cached // for performance. AttributeCollection attributes() const; // This variant will not update the potentially invalid attributes. To be used when not interested // in style attribute or one of the SVG animation attributes. AttributeCollection attributesWithoutUpdate() const; int offsetLeft(); int offsetTop(); int offsetWidth(); int offsetHeight(); Element* offsetParent(); int clientLeft(); int clientTop(); int clientWidth(); int clientHeight(); PassRefPtr getClientRects(); PassRefPtr getBoundingClientRect(); void requestPaint(PassOwnPtr); virtual void didMoveToNewDocument(Document&) override; CSSStyleDeclaration* style(); const QualifiedName& tagQName() const { return m_tagName; } String tagName() const { return nodeName(); } bool hasTagName(const QualifiedName& tagName) const { return m_tagName == tagName; } // A fast function for checking the local name against another atomic string. bool hasLocalName(const AtomicString& other) const { return m_tagName.localName() == other; } virtual const AtomicString& localName() const override final { return m_tagName.localName(); } virtual String nodeName() const override; PassRefPtr cloneElementWithChildren(); PassRefPtr cloneElementWithoutChildren(); void setBooleanAttribute(const QualifiedName& name, bool); void invalidateStyleAttribute(); bool affectedByAttributeSelector(const AtomicString& attributeName) const; bool affectedByClassSelector(const AtomicString& classValue) const; bool affectedByIdSelector(const AtomicString& idValue) const; const StylePropertySet* inlineStyle() const { return elementData() ? elementData()->m_inlineStyle.get() : 0; } bool setInlineStyleProperty(CSSPropertyID, CSSValueID identifier); bool setInlineStyleProperty(CSSPropertyID, double value, CSSPrimitiveValue::UnitType); bool setInlineStyleProperty(CSSPropertyID, const String& value); bool removeInlineStyleProperty(CSSPropertyID); void removeAllInlineStyleProperties(); void synchronizeStyleAttributeInternal() const; enum AttributeModificationReason { ModifiedDirectly, ModifiedByCloning }; virtual void parseAttribute(const QualifiedName&, const AtomicString&); // Only called by the parser immediately after element construction. void parserSetAttributes(const Vector&); bool sharesSameElementData(const Element& other) const { return elementData() == other.elementData(); } // Clones attributes only. void cloneAttributesFromElement(const Element&); // Clones all attribute-derived data, including subclass specifics (through copyNonAttributeProperties.) void cloneDataFromElement(const Element&); bool hasEquivalentAttributes(const Element* other) const; void attach(const AttachContext& = AttachContext()) final; void detach(const AttachContext& = AttachContext()) final; virtual RenderObject* createRenderer(RenderStyle*); void recalcStyle(StyleRecalcChange); void setAnimationStyleChange(bool); void setNeedsAnimationStyleRecalc(); bool supportsStyleSharing() const; ElementShadow* shadow() const; ElementShadow& ensureShadow(); PassRefPtr ensureShadowRoot(ExceptionState&); ShadowRoot* shadowRoot() const; bool hasAuthorShadowRoot() const { return shadowRoot(); } double x() const; void setX(double); double y() const; void setY(double); double width() const; void setWidth(double); double height() const; void setHeight(double); double minContentWidth() const; void setMinContentWidth(double); double maxContentWidth() const; void setMaxContentWidth(double); double alphabeticBaseline() const; double ideographicBaseline() const; void setNeedsLayout(); void layout(); LayoutCallback* intrinsicWidthsComputer() const; LayoutCallback* layoutManager() const; void setLayoutManager(PassOwnPtr layoutManager, PassOwnPtr intrinsicWidthsComputer); RenderStyle* computedStyle(); bool isUpgradedCustomElement() const { return customElementState() == Upgraded; } bool isUnresolvedCustomElement() const { return customElementState() == WaitingForUpgrade; } AtomicString computeInheritedLanguage() const; virtual bool isURLAttribute(const Attribute&) const { return false; } virtual bool isLiveLink() const { return false; } KURL hrefURL() const; KURL getURLAttribute(const QualifiedName&) const; KURL getNonEmptyURLAttribute(const QualifiedName&) const; virtual const AtomicString imageSourceURL() const; void focus(bool restorePreviousSelection = true, FocusType = FocusTypeNone); void updateFocusAppearance(bool restorePreviousSelection); void blur(); // Whether this element can receive focus at all. Most elements are not // focusable but some elements, such as form controls and links, are. Unlike // rendererIsFocusable(), this method may be called when layout is not up to // date, so it must not use the renderer to determine focusability. virtual bool supportsFocus() const; // Whether the node can actually be focused. bool isFocusable() const; bool isKeyboardFocusable() const; void dispatchFocusEvent(Element* oldFocusedElement, FocusType); void dispatchBlurEvent(Element* newFocusedElement); void dispatchFocusInEvent(const AtomicString& eventType, Element* oldFocusedElement); void dispatchFocusOutEvent(const AtomicString& eventType, Element* newFocusedElement); bool matches(const String& selectors, ExceptionState&); DOMTokenList& classList(); virtual bool canContainRangeEndPoint() const override { return true; } bool isSpellCheckingEnabled() const; // FIXME: public for RenderTreeBuilder, we shouldn't expose this though. PassRefPtr styleForRenderer(); bool hasID() const; bool hasClass() const; const SpaceSplitString& classNames() const; ActiveAnimations* activeAnimations() const; ActiveAnimations& ensureActiveAnimations(); bool hasActiveAnimations() const; void synchronizeAttribute(const AtomicString& localName) const; MutableStylePropertySet& ensureMutableInlineStyle(); void clearMutableInlineStyleIfEmpty(); void setTabIndex(int); virtual short tabIndex() const override; String contentEditable() const; void setContentEditable(const String&, ExceptionState&); bool spellcheck() const; void setSpellcheck(bool); const AtomicString& dir(); void setDir(const AtomicString&); // Dart exposed: void paint(Canvas*); protected: Element(const QualifiedName& tagName, Document*, ConstructionType); const ElementData* elementData() const { return m_elementData.get(); } UniqueElementData& ensureUniqueElementData(); virtual void insertedInto(ContainerNode*) override; virtual void removedFrom(ContainerNode*) override; virtual void childrenChanged(const ChildrenChange&) override; // Subclasses may override this method to affect focusability. Unlike // supportsFocus, this method must be called on an up-to-date layout, so it // may use the renderer to reason about focusability. This method cannot be // moved to RenderObject because some focusable nodes don't have renderers, // e.g., HTMLOptionElement. virtual bool rendererIsFocusable() const; // classAttributeChanged() exists to share code between // parseAttribute (called via setAttribute()) and // svgAttributeChanged (called when element.className.baseValue is set) void classAttributeChanged(const AtomicString& newClassString); private: void attributeChanged(const QualifiedName&, const AtomicString&, AttributeModificationReason = ModifiedDirectly); bool classChangeNeedsStyleRecalc(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses); bool isElementNode() const = delete; // This will catch anyone doing an unnecessary check. bool isDocumentFragment() const = delete; // This will catch anyone doing an unnecessary check. bool isDocumentNode() const = delete; // This will catch anyone doing an unnecessary check. void styleAttributeChanged(const AtomicString& newStyleString); void inlineStyleChanged(); PropertySetCSSStyleDeclaration* inlineStyleCSSOMWrapper(); void setInlineStyleFromString(const AtomicString&); StyleRecalcChange recalcOwnStyle(StyleRecalcChange); void recalcChildStyle(StyleRecalcChange); enum SynchronizationOfLazyAttribute { NotInSynchronizationOfLazyAttribute = 0, InSynchronizationOfLazyAttribute }; void willModifyAttribute(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); void synchronizeAllAttributes() const; void updateId(const AtomicString& oldId, const AtomicString& newId); void updateId(TreeScope&, const AtomicString& oldId, const AtomicString& newId); virtual NodeType nodeType() const override final; void setAttributeInternal(size_t index, const QualifiedName&, const AtomicString& value, SynchronizationOfLazyAttribute); void appendAttributeInternal(const QualifiedName&, const AtomicString& value, SynchronizationOfLazyAttribute); void removeAttributeInternal(size_t index, SynchronizationOfLazyAttribute); void attributeChangedFromParserOrByCloning(const QualifiedName&, const AtomicString&, AttributeModificationReason); #ifndef NDEBUG virtual void formatForDebugger(char* buffer, unsigned length) const override; #endif virtual RenderStyle* virtualComputedStyle() override { return computedStyle(); } // cloneNode is private so that non-virtual cloneElementWithChildren and cloneElementWithoutChildren // are used instead. virtual PassRefPtr cloneNode(bool deep) override; virtual PassRefPtr cloneElementWithoutAttributesAndChildren(); QualifiedName m_tagName; SpellcheckAttributeState spellcheckAttributeState() const; void createUniqueElementData(); bool shouldInvalidateDistributionWhenAttributeChanged(ElementShadow*, const QualifiedName&, const AtomicString&); ElementRareData* elementRareData() const; ElementRareData& ensureElementRareData(); RefPtr m_elementData; OwnPtr m_layoutManager; OwnPtr m_intrinsicWidthsComputer; }; DEFINE_NODE_TYPE_CASTS(Element, isElementNode()); template bool isElementOfType(const Node&); template <> inline bool isElementOfType(const Node& node) { return node.isElementNode(); } template inline bool isElementOfType(const Element& element) { return isElementOfType(static_cast(element)); } template <> inline bool isElementOfType(const Element&) { return true; } // Type casting. template inline T& toElement(Node& node) { ASSERT_WITH_SECURITY_IMPLICATION(isElementOfType(node)); return static_cast(node); } template inline T* toElement(Node* node) { ASSERT_WITH_SECURITY_IMPLICATION(!node || isElementOfType(*node)); return static_cast(node); } template inline const T& toElement(const Node& node) { ASSERT_WITH_SECURITY_IMPLICATION(isElementOfType(node)); return static_cast(node); } template inline const T* toElement(const Node* node) { ASSERT_WITH_SECURITY_IMPLICATION(!node || isElementOfType(*node)); return static_cast(node); } template inline T* toElement(const RefPtr& node) { return toElement(node.get()); } inline Element* Node::parentElement() const { ContainerNode* parent = parentNode(); return parent && parent->isElementNode() ? toElement(parent) : 0; } inline void Element::synchronizeAttribute(const AtomicString& localName) const { if (!elementData()) return; if (localName == HTMLNames::styleAttr && elementData()->m_styleAttributeIsDirty) { ASSERT(isStyledElement()); synchronizeStyleAttributeInternal(); } } inline bool Element::hasAttribute(const QualifiedName& name) const { return hasAttribute(name.localName()); } inline bool Element::hasAttribute(const AtomicString& name) const { synchronizeAttribute(name); return elementData() && elementData()->attributes().findIndex(name) != kNotFound; } inline const AtomicString& Element::getAttribute(const QualifiedName& name) const { return getAttribute(name.localName()); } inline const AtomicString& Element::getAttribute(const AtomicString& name) const { if (!elementData()) return nullAtom; synchronizeAttribute(name); if (const Attribute* attribute = elementData()->attributes().find(name)) return attribute->value(); return nullAtom; } inline AttributeCollection Element::attributes() const { if (!elementData()) return AttributeCollection(); synchronizeAllAttributes(); return elementData()->attributes(); } inline AttributeCollection Element::attributesWithoutUpdate() const { if (!elementData()) return AttributeCollection(); return elementData()->attributes(); } inline bool Element::hasAttributes() const { return !attributes().isEmpty(); } inline const AtomicString& Element::idForStyleResolution() const { ASSERT(hasID()); return elementData()->idForStyleResolution(); } inline const AtomicString& Element::getIdAttribute() const { return hasID() ? getAttribute(HTMLNames::idAttr) : nullAtom; } inline const AtomicString& Element::getClassAttribute() const { if (!hasClass()) return nullAtom; return getAttribute(HTMLNames::classAttr); } inline void Element::setIdAttribute(const AtomicString& value) { setAttribute(HTMLNames::idAttr, value); } inline const SpaceSplitString& Element::classNames() const { ASSERT(hasClass()); ASSERT(elementData()); return elementData()->classNames(); } inline bool Element::hasID() const { return elementData() && elementData()->hasID(); } inline bool Element::hasClass() const { return elementData() && elementData()->hasClass(); } inline UniqueElementData& Element::ensureUniqueElementData() { if (!elementData() || !elementData()->isUnique()) createUniqueElementData(); return toUniqueElementData(*m_elementData); } inline void Node::insertedInto(ContainerNode* insertionPoint) { ASSERT(insertionPoint->inDocument() || isContainerNode()); if (insertionPoint->inDocument()) setFlag(InDocumentFlag); if (parentOrShadowHostNode()->isInShadowTree()) setFlag(IsInShadowTreeFlag); if (childNeedsDistributionRecalc() && !insertionPoint->childNeedsDistributionRecalc()) insertionPoint->markAncestorsWithChildNeedsDistributionRecalc(); } inline void Node::removedFrom(ContainerNode* insertionPoint) { ASSERT(insertionPoint->inDocument() || isContainerNode()); if (insertionPoint->inDocument()) clearFlag(InDocumentFlag); if (isInShadowTree() && !treeScope().rootNode().isShadowRoot()) clearFlag(IsInShadowTreeFlag); } inline ShadowRoot* Node::shadowRoot() const { if (!isElementNode()) return 0; return toElement(this)->shadowRoot(); } inline void Element::invalidateStyleAttribute() { ASSERT(elementData()); elementData()->m_styleAttributeIsDirty = true; } inline bool isShadowHost(const Node* node) { return node && node->isElementNode() && toElement(node)->shadow(); } inline bool isShadowHost(const Element* element) { return element && element->shadow(); } inline bool isAtShadowBoundary(const Element* element) { if (!element) return false; ContainerNode* parentNode = element->parentNode(); return parentNode && parentNode->isShadowRoot(); } // These macros do the same as their NODE equivalents but additionally provide a template specialization // for isElementOfType<>() so that the Traversal<> API works for these Element types. #define DEFINE_ELEMENT_TYPE_CASTS(thisType, predicate) \ template <> inline bool isElementOfType(const Node& node) { return node.predicate; } \ DEFINE_NODE_TYPE_CASTS(thisType, predicate) #define DEFINE_ELEMENT_TYPE_CASTS_WITH_FUNCTION(thisType) \ template <> inline bool isElementOfType(const Node& node) { return is##thisType(node); } \ DEFINE_NODE_TYPE_CASTS_WITH_FUNCTION(thisType) #define DECLARE_ELEMENT_FACTORY_WITH_TAGNAME(T) \ static PassRefPtr create(const QualifiedName&, Document&) #define DEFINE_ELEMENT_FACTORY_WITH_TAGNAME(T) \ PassRefPtr T::create(const QualifiedName& tagName, Document& document) \ { \ return adoptRef(new T(tagName, document)); \ } } // namespace #endif // SKY_ENGINE_CORE_DOM_ELEMENT_H_