| /* |
| * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) |
| * 1999 Waldo Bastian (bastian@kde.org) |
| * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2013 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_CSS_CSSSELECTOR_H_ |
| #define SKY_ENGINE_CORE_CSS_CSSSELECTOR_H_ |
| |
| #include "sky/engine/core/dom/QualifiedName.h" |
| #include "sky/engine/core/rendering/style/RenderStyleConstants.h" |
| #include "sky/engine/wtf/OwnPtr.h" |
| #include "sky/engine/wtf/PassOwnPtr.h" |
| |
| namespace blink { |
| class CSSSelectorList; |
| |
| // This class represents a selector for a StyleRule. |
| |
| // CSS selector representation is somewhat complicated and subtle. A representative list of selectors is |
| // in CSSSelectorTest; run it in a debug build to see useful debugging output. |
| // |
| // ** tagHistory() and relation(): |
| // |
| // Selectors are represented as a linked list of simple selectors (defined more or less according to |
| // http://www.w3.org/TR/css3-selectors/#simple-selectors-dfn). The tagHistory() method returns the next |
| // simple selector in the list. The relation() method returns the relationship of the current simple selector to |
| // the one in tagHistory(). For example, the CSS selector .a.b #c is represented as: |
| // |
| // selectorText(): .a.b #c |
| // --> (relation == Descendant) |
| // selectorText(): .a.b |
| // --> (relation == SubSelector) |
| // selectorText(): .b |
| // |
| // Note that currently a bare selector such as ".a" has a relation() of Descendant. This is a bug - instead the relation should be |
| // "None". |
| // |
| // The order of tagHistory() varies depending on the situation. |
| // * Relations using combinators (http://www.w3.org/TR/css3-selectors/#combinators), such as descendant, sibling, etc., are parsed |
| // right-to-left (in the example above, this is why .c is earlier in the tagHistory() chain than .a.b). |
| // * SubSelector relations are parsed left-to-right in most cases (such as the .a.b example above); a counter-example is the |
| // ::content pseudo-element. Most (all?) other pseudo elements and pseudo classes are parsed left-to-right. |
| // * ShadowPseudo relations are parsed right-to-left. Example: summary::-webkit-details-marker is parsed as: |
| // selectorText(): summary::-webkit-details-marker |
| // --> (relation == ShadowPseudo) |
| // selectorText(): summary |
| // |
| // ** match(): |
| // |
| // The match of the current simple selector tells us the type of selector, such as class, id, tagname, or pseudo-class. |
| // Inline comments in the Match enum give examples of when each type would occur. |
| // |
| // ** value(), attribute(): |
| // |
| // value() tells you the value of the simple selector. For example, for class selectors, value() will tell you the class string, |
| // and for id selectors it will tell you the id(). See below for the special case of attribute selectors. |
| // |
| // ** Attribute selectors. |
| // |
| // Attribute selectors return the attribute name in the attribute() method. The value() method returns the value matched against |
| // in case of selectors like [attr="value"]. |
| |
| class CSSSelector { |
| WTF_MAKE_FAST_ALLOCATED; |
| public: |
| CSSSelector(); |
| CSSSelector(const CSSSelector&); |
| explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false); |
| |
| ~CSSSelector(); |
| |
| /** |
| * Re-create selector text from selector's data |
| */ |
| String selectorText(const String& = "") const; |
| |
| // checks if the 2 selectors (including sub selectors) agree. |
| bool operator==(const CSSSelector&) const; |
| |
| // tag == -1 means apply to all elements (Selector = *) |
| |
| /* how the attribute value has to match.... Default is Exact */ |
| enum Match { |
| Unknown = 0, |
| Tag, // Example: div |
| Id, // Example: #id |
| Class, // example: .class |
| PseudoClass, // Example: :hover |
| PseudoElement, // Example: ::first-line |
| Exact, // Example: E[foo="bar"] |
| Set, // Example: E[foo] |
| FirstAttributeSelectorMatch = Exact, |
| }; |
| |
| enum PseudoType { |
| PseudoNotParsed = 0, |
| PseudoUnknown, |
| PseudoHover, |
| PseudoActive, |
| PseudoLang, |
| PseudoHost, |
| }; |
| |
| enum AttributeMatchType { |
| CaseSensitive, |
| CaseInsensitive, |
| }; |
| |
| PseudoType pseudoType() const |
| { |
| if (m_pseudoType == PseudoNotParsed) |
| extractPseudoType(); |
| return static_cast<PseudoType>(m_pseudoType); |
| } |
| |
| static PseudoType parsePseudoType(const AtomicString&); |
| |
| // Selectors are kept in an array by CSSSelectorList. The next component of the selector is |
| // the next item in the array. |
| const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); } |
| |
| const QualifiedName& tagQName() const; |
| const AtomicString& value() const; |
| |
| // WARNING: Use of QualifiedName by attribute() is a lie. |
| // attribute() will return a QualifiedName with prefix and namespaceURI |
| // set to starAtom to mean "matches any namespace". Be very careful |
| // how you use the returned QualifiedName. |
| // http://www.w3.org/TR/css3-selectors/#attrnmsp |
| const QualifiedName& attribute() const; |
| AttributeMatchType attributeMatchType() const; |
| // Returns the argument of a parameterized selector. For example, :host(a) would have an argument of "a". |
| const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; } |
| const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; } |
| |
| #ifndef NDEBUG |
| void show() const; |
| void show(int indent) const; |
| #endif |
| |
| void setValue(const AtomicString&); |
| void setAttribute(const QualifiedName&, AttributeMatchType); |
| void setArgument(const AtomicString&); |
| void setSelectorList(PassOwnPtr<CSSSelectorList>); |
| |
| bool matchesPseudoElement() const; |
| bool isAttributeSelector() const; |
| bool isHostPseudoClass() const; |
| |
| Match match() const { return static_cast<Match>(m_match); } |
| void setMatch(Match match) |
| { |
| m_match = match; |
| ASSERT(static_cast<Match>(m_match) == match); // using a bitfield. |
| } |
| |
| bool isLastInSelectorList() const { return m_isLastInSelectorList; } |
| void setLastInSelectorList() { m_isLastInSelectorList = true; } |
| bool isLastInTagHistory() const { return m_isLastInTagHistory; } |
| void setNotLastInTagHistory() { m_isLastInTagHistory = false; } |
| |
| // http://dev.w3.org/csswg/selectors4/#compound |
| bool isCompound() const; |
| |
| private: |
| mutable unsigned m_match : 4; // enum Match |
| mutable unsigned m_pseudoType : 8; // PseudoType |
| unsigned m_isLastInSelectorList : 1; |
| unsigned m_isLastInTagHistory : 1; |
| unsigned m_hasRareData : 1; |
| unsigned m_tagIsForNamespaceRule : 1; |
| |
| void extractPseudoType() const; |
| |
| // Hide. |
| CSSSelector& operator=(const CSSSelector&); |
| |
| struct RareData : public RefCounted<RareData> { |
| static PassRefPtr<RareData> create(const AtomicString& value) { return adoptRef(new RareData(value)); } |
| ~RareData(); |
| |
| AtomicString m_value; |
| union { |
| AttributeMatchType m_attributeMatchType; // used for attribute selector (with value) |
| } m_bits; |
| QualifiedName m_attribute; // used for attribute selector |
| AtomicString m_argument; // Used for :contains, :lang |
| OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not |
| |
| private: |
| RareData(const AtomicString& value); |
| }; |
| void createRareData(); |
| |
| union DataUnion { |
| DataUnion() : m_value(0) { } |
| StringImpl* m_value; |
| StringImpl* m_tagQName; |
| RareData* m_rareData; |
| } m_data; |
| }; |
| |
| inline const QualifiedName& CSSSelector::attribute() const |
| { |
| ASSERT(isAttributeSelector()); |
| ASSERT(m_hasRareData); |
| return m_data.m_rareData->m_attribute; |
| } |
| |
| inline CSSSelector::AttributeMatchType CSSSelector::attributeMatchType() const |
| { |
| ASSERT(isAttributeSelector()); |
| ASSERT(m_hasRareData); |
| return m_data.m_rareData->m_bits.m_attributeMatchType; |
| } |
| |
| inline bool CSSSelector::matchesPseudoElement() const |
| { |
| if (m_pseudoType == PseudoUnknown) |
| extractPseudoType(); |
| return m_match == PseudoElement; |
| } |
| |
| inline bool CSSSelector::isHostPseudoClass() const |
| { |
| return m_match == PseudoClass && m_pseudoType == PseudoHost; |
| } |
| |
| inline bool CSSSelector::isAttributeSelector() const |
| { |
| return m_match >= FirstAttributeSelectorMatch; |
| } |
| |
| inline void CSSSelector::setValue(const AtomicString& value) |
| { |
| ASSERT(m_match != Tag); |
| ASSERT(m_pseudoType == PseudoNotParsed); |
| // Need to do ref counting manually for the union. |
| if (m_hasRareData) { |
| m_data.m_rareData->m_value = value; |
| return; |
| } |
| if (m_data.m_value) |
| m_data.m_value->deref(); |
| m_data.m_value = value.impl(); |
| m_data.m_value->ref(); |
| } |
| |
| inline CSSSelector::CSSSelector() |
| : m_match(Unknown) |
| , m_pseudoType(PseudoNotParsed) |
| , m_isLastInSelectorList(false) |
| , m_isLastInTagHistory(true) |
| , m_hasRareData(false) |
| , m_tagIsForNamespaceRule(false) |
| { |
| } |
| |
| inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule) |
| : m_match(Tag) |
| , m_pseudoType(PseudoNotParsed) |
| , m_isLastInSelectorList(false) |
| , m_isLastInTagHistory(true) |
| , m_hasRareData(false) |
| , m_tagIsForNamespaceRule(tagIsForNamespaceRule) |
| { |
| m_data.m_tagQName = tagQName.localName().impl(); |
| m_data.m_tagQName->ref(); |
| } |
| |
| inline CSSSelector::CSSSelector(const CSSSelector& o) |
| : m_match(o.m_match) |
| , m_pseudoType(o.m_pseudoType) |
| , m_isLastInSelectorList(o.m_isLastInSelectorList) |
| , m_isLastInTagHistory(o.m_isLastInTagHistory) |
| , m_hasRareData(o.m_hasRareData) |
| , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule) |
| { |
| if (o.m_match == Tag) { |
| m_data.m_tagQName = o.m_data.m_tagQName; |
| m_data.m_tagQName->ref(); |
| } else if (o.m_hasRareData) { |
| m_data.m_rareData = o.m_data.m_rareData; |
| m_data.m_rareData->ref(); |
| } else if (o.m_data.m_value) { |
| m_data.m_value = o.m_data.m_value; |
| m_data.m_value->ref(); |
| } |
| } |
| |
| inline CSSSelector::~CSSSelector() |
| { |
| if (m_match == Tag) |
| m_data.m_tagQName->deref(); |
| else if (m_hasRareData) |
| m_data.m_rareData->deref(); |
| else if (m_data.m_value) |
| m_data.m_value->deref(); |
| } |
| |
| inline const QualifiedName& CSSSelector::tagQName() const |
| { |
| ASSERT(m_match == Tag); |
| return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName); |
| } |
| |
| inline const AtomicString& CSSSelector::value() const |
| { |
| ASSERT(m_match != Tag); |
| if (m_hasRareData) |
| return m_data.m_rareData->m_value; |
| // AtomicString is really just a StringImpl* so the cast below is safe. |
| // FIXME: Perhaps call sites could be changed to accept StringImpl? |
| return *reinterpret_cast<const AtomicString*>(&m_data.m_value); |
| } |
| |
| } // namespace blink |
| |
| #endif // SKY_ENGINE_CORE_CSS_CSSSELECTOR_H_ |