blob: 62ace47f7ee0fd4a0ba9117401d6de014ddb3e03 [file] [log] [blame]
/*
* 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 THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_SELECTOR_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_SELECTOR_H_
#include <memory>
#include <utility>
#include "base/check_op.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/parser/css_nesting_type.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_mode.h"
#include "third_party/blink/renderer/core/dom/qualified_name.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/heap/visitor.h"
#include "third_party/blink/renderer/platform/wtf/bit_field.h"
#include "third_party/blink/renderer/platform/wtf/gc_plugin.h"
namespace WTF {
class StringBuilder;
} // namespace WTF
namespace blink {
class CSSParserContext;
class CSSSelectorList;
class Document;
class StyleRule;
// This class represents a simple 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.
//
// ** NextSimpleSelector() and Relation():
//
// Selectors are represented as an array of simple selectors (defined more
// or less according to
// http://www.w3.org/TR/css3-selectors/#simple-selectors-dfn).
// The NextInSimpleSelector() member function returns the next simple selector
// in the list. The Relation() member function returns the relationship of the
// current simple selector to the one in NextSimpleSelector(). For example, the
// CSS selector .a.b #c is represented as:
//
// SelectorText(): .a.b #c
// --> (relation == kDescendant)
// SelectorText(): .a.b
// --> (relation == kSubSelector)
// SelectorText(): .b
//
// The ordering of the simple selectors 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 simple selector chain than .a.b).
// * SubSelector relations are parsed left-to-right, such as the .a.b example
// above.
// * 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 CORE_EXPORT CSSSelector {
// CSSSelector typically lives on Oilpan; either in StyleRule's
// AdditionalBytes, as part of CSSSelectorList, or (during parsing)
// in a HeapVector. However, it is never really allocated as a separate
// Oilpan object, so it does not inherit from GarbageCollected.
DISALLOW_NEW();
public:
CSSSelector();
// NOTE: Will not deep-copy the selector list, if any.
CSSSelector(const CSSSelector&);
CSSSelector(CSSSelector&&);
explicit CSSSelector(const QualifiedName&, bool tag_is_implicit = false);
explicit CSSSelector(const StyleRule* parent_rule, bool is_implicit);
explicit CSSSelector(const AtomicString& pseudo_name, bool is_implicit);
~CSSSelector();
String SelectorText() const;
String SimpleSelectorTextForDebug() const;
CSSSelector& operator=(const CSSSelector&) = delete;
CSSSelector& operator=(CSSSelector&&);
bool operator==(const CSSSelector&) const = delete;
bool operator!=(const CSSSelector&) const = delete;
static constexpr unsigned kIdSpecificity = 0x010000;
static constexpr unsigned kClassLikeSpecificity = 0x000100;
static constexpr unsigned kTagSpecificity = 0x000001;
static constexpr unsigned kMaxValueMask = 0xffffff;
static constexpr unsigned kIdMask = 0xff0000;
static constexpr unsigned kClassMask = 0x00ff00;
static constexpr unsigned kElementMask = 0x0000ff;
// http://www.w3.org/TR/css3-selectors/#specificity
// We use 256 as the base of the specificity number system.
unsigned Specificity() const;
// Returns specificity components in decreasing order of significance.
std::array<uint8_t, 3> SpecificityTuple() const;
/* how the attribute value has to match.... Default is Exact */
enum MatchType {
kUnknown,
kInvalidList, // Used as a marker in CSSSelectorList.
kTag, // Example: div
kId, // Example: #id
kClass, // Example: .class
kPseudoClass, // Example: :nth-child(2)
kPseudoElement, // Example: ::first-line
kPagePseudoClass, // ??
kAttributeExact, // Example: E[foo="bar"]
kAttributeSet, // Example: E[foo]
kAttributeHyphen, // Example: E[foo|="bar"]
kAttributeList, // Example: E[foo~="bar"]
kAttributeContain, // css3: E[foo*="bar"]
kAttributeBegin, // css3: E[foo^="bar"]
kAttributeEnd, // css3: E[foo$="bar"]
kFirstAttributeSelectorMatch = kAttributeExact,
};
enum RelationType {
// No combinator. Used between simple selectors within the same compound.
kSubSelector,
// "Space" combinator
kDescendant,
// > combinator
kChild,
// + combinator
kDirectAdjacent,
// ~ combinator
kIndirectAdjacent,
// The relation types below are implicit combinators inserted at parse time
// before pseudo elements which match another flat tree element than the
// rest of the compound.
//
// Implicit combinator inserted before pseudo elements matching an element
// inside a UA shadow tree. This combinator allows the selector matching to
// cross a shadow root.
//
// Examples:
// input::placeholder, video::cue(i), video::--webkit-media-controls-panel
kUAShadow,
// Implicit combinator inserted before ::slotted() selectors.
kShadowSlot,
// Implicit combinator inserted before ::part() selectors which allows
// matching a ::part in shadow-including descendant tree for #host in
// "#host::part(button)".
kShadowPart,
// leftmost "Space" combinator of relative selector
kRelativeDescendant,
// leftmost > combinator of relative selector
kRelativeChild,
// leftmost + combinator of relative selector
kRelativeDirectAdjacent,
// leftmost ~ combinator of relative selector
kRelativeIndirectAdjacent,
// The following applies to selectors within @scope
// (see CSSNestingType::kScope):
//
// The kScopeActivation relation is implicitly inserted parse-time before
// any compound selector which contains either :scope or the parent
// selector (&). When encountered during selector matching,
// kScopeActivation will try the to match the rest of the selector (i.e. the
// NextSimpleSelector from that point) using activation roots as the :scope
// element, trying the nearest activation root first.
//
// See also StyleScopeActivation.
kScopeActivation,
};
enum PseudoType {
kPseudoActive,
kPseudoActiveViewTransition,
kPseudoActiveViewTransitionType,
kPseudoAfter,
kPseudoAny,
kPseudoAnyLink,
kPseudoAutofill,
kPseudoAutofillPreviewed,
kPseudoAutofillSelected,
kPseudoBackdrop,
kPseudoBefore,
kPseudoChecked,
kPseudoCornerPresent,
kPseudoDecrement,
kPseudoDefault,
kPseudoDetailsContent,
kPseudoDialogInTopLayer,
kPseudoDisabled,
kPseudoDoubleButton,
kPseudoDrag,
kPseudoEmpty,
kPseudoEnabled,
kPseudoEnd,
kPseudoFileSelectorButton,
kPseudoFirstChild,
kPseudoFirstLetter,
kPseudoFirstLine,
kPseudoFirstOfType,
kPseudoFirstPage,
kPseudoFocus,
kPseudoFocusVisible,
kPseudoFocusWithin,
kPseudoFullPageMedia,
kPseudoHorizontal,
kPseudoHover,
kPseudoIncrement,
kPseudoIndeterminate,
kPseudoInvalid,
kPseudoIs,
kPseudoLang,
kPseudoLastChild,
kPseudoLastOfType,
kPseudoLeftPage,
kPseudoLink,
kPseudoMarker,
kPseudoModal,
kPseudoNoButton,
kPseudoNot,
kPseudoNthChild, // Includes :nth-child(An+B of <selector>)
kPseudoNthLastChild,
kPseudoNthLastOfType,
kPseudoNthOfType,
kPseudoOnlyChild,
kPseudoOnlyOfType,
kPseudoOptional,
kPseudoParent, // Written as & (in nested rules).
kPseudoPart,
kPseudoPermissionGranted,
kPseudoPlaceholder,
kPseudoPlaceholderShown,
kPseudoReadOnly,
kPseudoReadWrite,
kPseudoRequired,
kPseudoResizer,
kPseudoRightPage,
kPseudoRoot,
kPseudoScope,
kPseudoScrollbar,
kPseudoScrollbarButton,
kPseudoScrollbarCorner,
kPseudoScrollbarThumb,
kPseudoScrollbarTrack,
kPseudoScrollbarTrackPiece,
kPseudoSelectDatalist,
kPseudoSelection,
kPseudoSelectorFragmentAnchor,
kPseudoSingleButton,
kPseudoStart,
// kPseudoState is for :state(foo). kPseudoStateDeprecated is for :--foo.
// :--foo is deprecated and is replacing :state(foo).
// TODO(crbug.com/1514397): Remove kPseudoStateDeprecatedSyntax after the
// deprecation is done.
kPseudoState,
kPseudoStateDeprecatedSyntax,
kPseudoTarget,
kPseudoUnknown,
// Something that was unparsable, but contained either a nesting
// selector (&), or a :scope pseudo-class, and must therefore be kept
// for serialization purposes.
kPseudoUnparsed,
kPseudoUserInvalid,
kPseudoUserValid,
kPseudoValid,
kPseudoVertical,
kPseudoVisited,
kPseudoWebKitAutofill,
kPseudoWebkitAnyLink,
kPseudoWhere,
kPseudoWindowInactive,
// TODO(foolip): When the unprefixed Fullscreen API is enabled, merge
// kPseudoFullScreen and kPseudoFullscreen into one. (kPseudoFullscreen is
// controlled by the FullscreenUnprefixed REF, but is otherwise an alias.)
kPseudoFullScreen,
kPseudoFullScreenAncestor,
kPseudoFullscreen,
kPseudoInRange,
kPseudoOutOfRange,
kPseudoPaused,
kPseudoPictureInPicture,
kPseudoPlaying,
kPseudoXrOverlay,
// Pseudo elements in UA ShadowRoots. Available in any stylesheets.
kPseudoWebKitCustomElement,
// Pseudo elements in UA ShadowRoots. Available only in UA stylesheets.
kPseudoBlinkInternalElement,
kPseudoClosed,
kPseudoCue,
kPseudoDefined,
kPseudoDir,
kPseudoFutureCue,
kPseudoGrammarError,
kPseudoHas,
kPseudoHasDatalist,
kPseudoHighlight,
kPseudoHost,
kPseudoHostContext,
kPseudoHostHasAppearance,
kPseudoIsHtml,
kPseudoListBox,
kPseudoMultiSelectFocus,
kPseudoOpen,
kPseudoPastCue,
kPseudoPopoverInTopLayer,
kPseudoPopoverOpen,
kPseudoRelativeAnchor,
kPseudoSlotted,
kPseudoSpatialNavigationFocus,
kPseudoSpellingError,
kPseudoTargetText,
// Always matches. See SetTrue().
kPseudoTrue,
kPseudoVideoPersistent,
kPseudoVideoPersistentAncestor,
// The following selectors are used to target pseudo elements created for
// ViewTransition.
// See https://drafts.csswg.org/css-view-transitions-1/#pseudo
// for details.
kPseudoViewTransition,
kPseudoViewTransitionGroup,
kPseudoViewTransitionImagePair,
kPseudoViewTransitionNew,
kPseudoViewTransitionOld,
};
enum class AttributeMatchType : int {
kCaseSensitive,
kCaseInsensitive,
kCaseSensitiveAlways,
};
PseudoType GetPseudoType() const {
return static_cast<PseudoType>(bits_.get<PseudoTypeField>());
}
void UpdatePseudoType(const AtomicString&,
const CSSParserContext&,
bool has_arguments,
CSSParserMode);
void SetUnparsedPlaceholder(CSSNestingType, const AtomicString&);
// If this simple selector contains a parent selector (&), returns kNesting.
// Otherwise, if this simple selector contains a :scope pseudo-class,
// returns kScope. Otherwise, returns kNone.
//
// Note that this means that a selector which contains both '&' and :scope
// (which can happen for kPseudoUnparsed) will return kNesting. This is OK,
// since any selector which is nest-containing is also treated as
// scope-containing during parsing.
CSSNestingType GetNestingType() const;
// Set this CSSSelector as a :true pseudo-class. This can be useful if you
// need to insert a special RelationType into a selector's NextSimpleSelector,
// but lack any existing/suitable CSSSelector to attach that RelationType to.
//
// Note that :true is always implicit (see IsImplicit).
void SetTrue();
void UpdatePseudoPage(const AtomicString&, const Document*);
static PseudoType NameToPseudoType(const AtomicString&,
bool has_arguments,
const Document* document);
static PseudoId GetPseudoId(PseudoType);
// Replaces the parent pointer held by kPseudoParent selectors found
// within this simple selector (including inner selector lists).
//
// See also StyleRule::Reparent().
void Reparent(StyleRule* old_parent, StyleRule* new_parent);
// Selectors are kept in an array by CSSSelectorList. The next component of
// the selector is the next item in the array.
const CSSSelector* NextSimpleSelector() const {
return IsLastInComplexSelector() ? nullptr : this + 1;
}
CSSSelector* NextSimpleSelector() {
return IsLastInComplexSelector() ? nullptr : this + 1;
}
static const AtomicString& UniversalSelectorAtom() { return g_null_atom; }
const QualifiedName& TagQName() const;
const StyleRule* ParentRule() const; // Only valid for kPseudoParent.
const AtomicString& Value() const;
const AtomicString& SerializingValue() const;
// WARNING: Use of QualifiedName by attribute() is a lie.
// attribute() will return a QualifiedName with prefix and namespaceURI
// set to g_star_atom 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 AttributeMatch() const;
bool IsCaseSensitiveAttribute() const;
// Returns the argument of a parameterized selector. For example, :lang(en-US)
// would have an argument of en-US.
// Note that :nth-* selectors don't store an argument and just store the
// numbers.
const AtomicString& Argument() const {
return HasRareData() ? data_.rare_data_->argument_ : g_null_atom;
}
const CSSSelectorList* SelectorList() const {
return HasRareData() ? data_.rare_data_->selector_list_.Get() : nullptr;
}
// Similar to SelectorList(), but also works for kPseudoParent
// (i.e., nested selectors); on &, will give the parent's selector list.
// Will return nullptr if no such list exists (e.g. if we are not a
// pseudo selector at all), or if we are a & rule that's in a non-nesting
// context (which is valid, but won't match anything).
const CSSSelector* SelectorListOrParent() const;
const Vector<AtomicString>& IdentList() const {
CHECK(HasRareData() && data_.rare_data_->ident_list_);
return *data_.rare_data_->ident_list_;
}
bool ContainsPseudoInsideHasPseudoClass() const {
return HasRareData() ? data_.rare_data_->bits_.has_.contains_pseudo_
: false;
}
bool ContainsComplexLogicalCombinationsInsideHasPseudoClass() const {
return HasRareData() ? data_.rare_data_->bits_.has_
.contains_complex_logical_combinations_
: false;
}
#if DCHECK_IS_ON()
void Show() const;
void Show(int indent) const;
#endif // DCHECK_IS_ON()
bool IsASCIILower(const AtomicString& value);
void SetValue(const AtomicString&, bool match_lower_case);
void SetAttribute(const QualifiedName&, AttributeMatchType);
void SetArgument(const AtomicString&);
void SetSelectorList(CSSSelectorList*);
void SetIdentList(std::unique_ptr<Vector<AtomicString>>);
void SetContainsPseudoInsideHasPseudoClass();
void SetContainsComplexLogicalCombinationsInsideHasPseudoClass();
void SetNth(int a, int b, CSSSelectorList* sub_selector);
bool MatchNth(unsigned count) const;
static bool IsAdjacentRelation(RelationType relation) {
return relation == kDirectAdjacent || relation == kIndirectAdjacent;
}
bool IsAttributeSelector() const {
return Match() >= kFirstAttributeSelectorMatch;
}
bool IsHostPseudoClass() const {
return GetPseudoType() == kPseudoHost ||
GetPseudoType() == kPseudoHostContext;
}
// Test for combinations including :host() or :host-context()
// (See Example 3 under https://drafts.csswg.org/selectors-4/#data-model).
bool IsOrContainsHostPseudoClass() const;
bool IsUserActionPseudoClass() const;
bool IsIdClassOrAttributeSelector() const;
RelationType Relation() const {
return static_cast<RelationType>(bits_.get<RelationField>());
}
void SetRelation(RelationType relation) {
bits_.set<RelationField>(relation);
DCHECK_EQ(Relation(),
relation); // using a bitfield.
}
MatchType Match() const {
return static_cast<MatchType>(bits_.get<MatchField>());
}
void SetMatch(MatchType match) {
bits_.set<MatchField>(match);
DCHECK_EQ(Match(), match); // using a bitfield.
}
bool IsLastInSelectorList() const {
return bits_.get<IsLastInSelectorListField>();
}
void SetLastInSelectorList(bool is_last) {
bits_.set<IsLastInSelectorListField>(is_last);
}
bool IsLastInComplexSelector() const {
return bits_.get<IsLastInComplexSelectorField>();
}
void SetLastInComplexSelector(bool is_last) {
bits_.set<IsLastInComplexSelectorField>(is_last);
}
// https://drafts.csswg.org/selectors/#compound
bool IsCompound() const;
enum LinkMatchMask {
kMatchLink = 1,
kMatchVisited = 2,
kMatchAll = kMatchLink | kMatchVisited
};
// True if :link or :visited pseudo-classes are found anywhere in
// the selector.
bool HasLinkOrVisited() const;
bool HasRareData() const { return bits_.get<HasRareDataField>(); }
bool IsForPage() const { return bits_.get<IsForPageField>(); }
void SetForPage() { bits_.set<IsForPageField>(true); }
bool IsCoveredByBucketing() const {
return bits_.get<IsCoveredByBucketingField>();
}
void SetCoveredByBucketing(bool value) {
bits_.set<IsCoveredByBucketingField>(value);
}
bool MatchesPseudoElement() const;
bool IsTreeAbidingPseudoElement() const;
bool IsAllowedAfterPart() const;
// Returns true if the immediately preceeding simple selector is ::part.
bool FollowsPart() const;
// Returns true if the immediately preceeding simple selector is ::slotted.
bool FollowsSlotted() const;
// True if the selector was added implicitly. This can happen for e.g.
// nested rules that would otherwise lack the scoping selector (:scope).
bool IsImplicit() const { return bits_.get<IsImplicitlyAddedField>(); }
// Returns true for simple selectors whose evaluation depends on DOM tree
// position like :first-of-type and :nth-child().
bool IsChildIndexedSelector() const;
// Signaling Rules
// ================
//
// Signaling rules are style rules whose declarations trigger
// a certain use-counter. The use-counter is triggered by StyleCascade
// when a signaling declaration satisfies all of the following:
//
// - The declaration is added to the cascade map.
// - Adding the declaration to the map actually changed the value,
// i.e. the cascaded value before/after isn't the same.
// - The declaration ultimately won the cascade, i.e. nothing else
// overwrote it.
//
// Note: the final goal of signaling rules is to hopefully unblock
// the following CSSWG issues:
//
// - https://github.com/w3c/csswg-drafts/issues/8738
// - https://github.com/w3c/csswg-drafts/issues/9492
//
// TODO(crbug.com/1517290): Remove signaling rules when we're done
// use-counting.
enum class Signal {
kNone = 0,
// WebFeature::kCSSBareDeclarationShift
kBareDeclarationShift = 1,
// WebFeature::kCSSNestedGroupRuleSpecificity
kNestedGroupRuleSpecificity = 2,
kMax = kNestedGroupRuleSpecificity,
};
void SetSignal(Signal signal) {
bits_.set<SignalField>(static_cast<unsigned>(signal));
}
Signal GetSignal() const {
return static_cast<Signal>(bits_.get<SignalField>());
}
// Invisible Rules
// ===============
//
// Invisible rules are rules which exist internally for use-counting
// purposes, but don't have any author-visible effect on the cascade,
// and are not otherwise reachable through APIs.
//
// Invisible rules are useful when used in conjunction with signaling rules
// (above), because it makes it possible to check if a given rule has
// any impact in the presence of some alternative/hypothetical rule.
//
// TODO(crbug.com/1517290): Remove invisible rules when we're done
// use-counting.
void SetInvisible() { bits_.set<IsInvisibleField>(true); }
bool IsInvisible() const { return bits_.get<IsInvisibleField>(); }
void Trace(Visitor* visitor) const;
static String FormatPseudoTypeForDebugging(PseudoType);
private:
// Trace() branches on the match/pseudo_type flags,
// RuleData bucketing sets is_covered_by_bucketing,
// and these could happen concurrently. This trips up TSan,
// even though the race is benign, so use an atomic read
// instead of C++ bitfields.
using BitField = WTF::ConcurrentlyReadBitField<uint32_t>;
using RelationField =
BitField::DefineFirstValue<uint32_t, 4>; // RelationType
using MatchField = RelationField::DefineNextValue<uint32_t, 4>; // MatchType
using PseudoTypeField =
MatchField::DefineNextValue<uint32_t, 8>; // PseudoType
using IsLastInSelectorListField = PseudoTypeField::DefineNextValue<bool, 1>;
using IsLastInComplexSelectorField =
IsLastInSelectorListField::DefineNextValue<bool, 1>;
using HasRareDataField =
IsLastInComplexSelectorField::DefineNextValue<bool, 1>;
using IsForPageField = HasRareDataField::DefineNextValue<bool, 1>;
using IsImplicitlyAddedField = IsForPageField::DefineNextValue<bool, 1>;
// If set, we don't need to check this simple selector when matching;
// it will always match, since we can only see the selector if we
// checked a given bucket. For instance, if we have a rule like
// #foo.bar, it will be put in the rule set bucket for #foo
// (ID selectors are prioritized over nearly everything), and we can
// mark #foo as covered by bucketing (but still need to check .bar).
// Of course, this doesn't cover ancestors or siblings; if we have
// something like .c .c.c, only the two rightmost selectors will get
// this bit set. Also, we often get into things like namespaces which
// makes this more conservative than we'd like (bucketing on e.g.
// tag names do not generally care about it).
//
// Furthermore, as a convention, matching such a rule would never set
// flags in MatchResult.
//
// This always starts out false, and is set when we bucket a given
// RuleData (by calling MarkAsCoveredByBucketing()).
using IsCoveredByBucketingField =
IsImplicitlyAddedField::DefineNextValue<bool, 1>;
using SignalField = IsCoveredByBucketingField::DefineNextValue<unsigned, 2>;
using IsInvisibleField = SignalField::DefineNextValue<bool, 1>;
BitField bits_;
void SetPseudoType(PseudoType pseudo_type) {
bits_.set<PseudoTypeField>(pseudo_type);
DCHECK_EQ(GetPseudoType(), pseudo_type); // using a bitfield.
}
unsigned SpecificityForOneSelector() const;
unsigned SpecificityForPage() const;
bool SerializeSimpleSelector(WTF::StringBuilder& builder) const;
const CSSSelector* SerializeCompound(WTF::StringBuilder&) const;
struct RareData : public GarbageCollected<RareData> {
explicit RareData(const AtomicString& value);
~RareData();
bool MatchNth(unsigned count);
int NthAValue() const { return bits_.nth_.a_; }
int NthBValue() const { return bits_.nth_.b_; }
AtomicString matching_value_;
AtomicString serializing_value_;
union {
struct {
int a_; // Used for :nth-*
int b_; // Used for :nth-*
} nth_;
struct {
AttributeMatchType
attribute_match_; // used for attribute selector (with value)
bool is_case_sensitive_attribute_;
} attr_;
struct {
// Used for :has() with pseudos in its argument. e.g. :has(:hover)
bool contains_pseudo_;
// Used for :has() with logical combinations (:is(), :where(), :not())
// containing complex selector in its argument. e.g. :has(:is(.a .b))
bool contains_complex_logical_combinations_;
} has_;
// See GetNestingType.
CSSNestingType unparsed_nesting_type_;
} bits_;
QualifiedName attribute_; // Used for attribute selector
AtomicString argument_; // Used for :contains, :lang, :dir, etc.
Member<CSSSelectorList>
selector_list_; // Used :is, :not, :-webkit-any, etc.
std::unique_ptr<Vector<AtomicString>>
ident_list_; // Used for ::part(), :active-view-transition-type().
void Trace(Visitor* visitor) const;
};
void CreateRareData();
// The type tag for DataUnion is actually inferred from multiple state
// variables in the containing CSSSelector using the following rules.
//
// if (Match() == kTag) {
// /* data_.tag_q_name_ is valid */
// } else if (Match() == kPseudoClass && GetPseudoType() == kPseudoParent) {
// /* data_.parent_rule_ is valid */
// } else if (HasRareData()) {
// /* data_.rare_data_ is valid */
// } else {
// /* data_.value_ is valid */
// }
//
// Note that it is important to placement-new and explicitly destruct the
// fields when shifting between types tags for a DataUnion! Otherwise there
// will be undefined behavior! This luckily only happens when transitioning
// from a normal |value_| to a |rare_data_|.
GC_PLUGIN_IGNORE("crbug.com/1146383")
union DataUnion {
enum ConstructUninitializedTag { kConstructUninitialized };
explicit DataUnion(ConstructUninitializedTag) {}
enum ConstructEmptyValueTag { kConstructEmptyValue };
explicit DataUnion(ConstructEmptyValueTag) : value_() {}
// A string `value` is used by many different selectors to store the string
// part of the selector. For example the name of a pseudo class (without
// the colon), the class name of a class selector (without the dot),
// the attribute of an attribute selector (without the brackets), etc.
explicit DataUnion(const AtomicString& value) : value_(value) {}
explicit DataUnion(const QualifiedName& tag_q_name)
: tag_q_name_(tag_q_name) {}
explicit DataUnion(const StyleRule* parent_rule)
: parent_rule_(parent_rule) {}
~DataUnion() {}
AtomicString value_;
QualifiedName tag_q_name_;
Member<RareData> rare_data_;
Member<const StyleRule> parent_rule_; // For & (parent in nest).
} data_;
};
inline const QualifiedName& CSSSelector::Attribute() const {
DCHECK(IsAttributeSelector());
DCHECK(HasRareData());
return data_.rare_data_->attribute_;
}
inline CSSSelector::AttributeMatchType CSSSelector::AttributeMatch() const {
DCHECK(IsAttributeSelector());
DCHECK(HasRareData());
return data_.rare_data_->bits_.attr_.attribute_match_;
}
inline bool CSSSelector::IsCaseSensitiveAttribute() const {
DCHECK(IsAttributeSelector());
DCHECK(HasRareData());
return data_.rare_data_->bits_.attr_.is_case_sensitive_attribute_;
}
inline bool CSSSelector::IsASCIILower(const AtomicString& value) {
for (wtf_size_t i = 0; i < value.length(); ++i) {
if (IsASCIIUpper(value[i])) {
return false;
}
}
return true;
}
inline void CSSSelector::SetValue(const AtomicString& value,
bool match_lower_case = false) {
DCHECK_NE(Match(), static_cast<unsigned>(kTag));
DCHECK(!(Match() == kPseudoClass && GetPseudoType() == kPseudoParent));
if (match_lower_case && !HasRareData() && !IsASCIILower(value)) {
CreateRareData();
}
if (!HasRareData()) {
data_.value_ = value;
return;
}
data_.rare_data_->matching_value_ =
match_lower_case ? value.LowerASCII() : value;
data_.rare_data_->serializing_value_ = value;
}
inline CSSSelector::CSSSelector()
: bits_(RelationField::encode(kSubSelector) | MatchField::encode(kUnknown) |
PseudoTypeField::encode(kPseudoUnknown) |
IsLastInSelectorListField::encode(false) |
IsLastInComplexSelectorField::encode(false) |
HasRareDataField::encode(false) | IsForPageField::encode(false) |
IsImplicitlyAddedField::encode(false) |
IsCoveredByBucketingField::encode(false) |
SignalField::encode(static_cast<unsigned>(Signal::kNone)) |
IsInvisibleField::encode(false)),
data_(DataUnion::kConstructEmptyValue) {}
inline CSSSelector::CSSSelector(const QualifiedName& tag_q_name,
bool tag_is_implicit)
: bits_(RelationField::encode(kSubSelector) | MatchField::encode(kTag) |
PseudoTypeField::encode(kPseudoUnknown) |
IsLastInSelectorListField::encode(false) |
IsLastInComplexSelectorField::encode(false) |
HasRareDataField::encode(false) | IsForPageField::encode(false) |
IsImplicitlyAddedField::encode(tag_is_implicit) |
IsCoveredByBucketingField::encode(false) |
SignalField::encode(static_cast<unsigned>(Signal::kNone)) |
IsInvisibleField::encode(false)),
data_(tag_q_name) {}
inline CSSSelector::CSSSelector(const StyleRule* parent_rule, bool is_implicit)
: bits_(RelationField::encode(kSubSelector) |
MatchField::encode(kPseudoClass) |
PseudoTypeField::encode(kPseudoParent) |
IsLastInSelectorListField::encode(false) |
IsLastInComplexSelectorField::encode(false) |
HasRareDataField::encode(false) | IsForPageField::encode(false) |
IsImplicitlyAddedField::encode(is_implicit) |
IsCoveredByBucketingField::encode(false) |
SignalField::encode(static_cast<unsigned>(Signal::kNone)) |
IsInvisibleField::encode(false)),
data_(parent_rule) {}
inline CSSSelector::CSSSelector(const AtomicString& pseudo_name,
bool is_implicit)
: bits_(RelationField::encode(kSubSelector) |
MatchField::encode(kPseudoClass) |
PseudoTypeField::encode(NameToPseudoType(pseudo_name,
/* has_arguments */ false,
/* document */ nullptr)) |
IsLastInSelectorListField::encode(false) |
IsLastInComplexSelectorField::encode(false) |
HasRareDataField::encode(false) | IsForPageField::encode(false) |
IsImplicitlyAddedField::encode(is_implicit) |
IsCoveredByBucketingField::encode(false) |
SignalField::encode(static_cast<unsigned>(Signal::kNone)) |
IsInvisibleField::encode(false)),
data_(pseudo_name) {}
inline CSSSelector::CSSSelector(const CSSSelector& o)
: bits_(o.bits_), data_(DataUnion::kConstructUninitialized) {
if (o.Match() == kTag) {
new (&data_.tag_q_name_) QualifiedName(o.data_.tag_q_name_);
} else if (o.Match() == kPseudoClass && o.GetPseudoType() == kPseudoParent) {
data_.parent_rule_ = o.data_.parent_rule_;
} else if (o.HasRareData()) {
data_.rare_data_ = o.data_.rare_data_; // Oilpan-managed.
} else {
new (&data_.value_) AtomicString(o.data_.value_);
}
}
inline CSSSelector::CSSSelector(CSSSelector&& o)
: data_(DataUnion::kConstructUninitialized) {
// Seemingly Clang started generating terrible code for the obvious move
// constructor (i.e., using similar code as in the copy constructor above)
// after moving to Oilpan, copying the bits one by one. We already allow
// memcpy + memset by traits, so we can do it by ourselves, too.
memcpy(this, &o, sizeof(*this));
memset(&o, 0, sizeof(o));
}
inline CSSSelector::~CSSSelector() {
if (Match() == kTag) {
data_.tag_q_name_.~QualifiedName();
} else if (Match() == kPseudoClass && GetPseudoType() == kPseudoParent)
; // Nothing to do.
else if (HasRareData())
; // Nothing to do.
else {
data_.value_.~AtomicString();
}
}
inline CSSSelector& CSSSelector::operator=(CSSSelector&& other) {
this->~CSSSelector();
new (this) CSSSelector(std::move(other));
return *this;
}
inline const QualifiedName& CSSSelector::TagQName() const {
DCHECK_EQ(Match(), static_cast<unsigned>(kTag));
return data_.tag_q_name_;
}
inline const StyleRule* CSSSelector::ParentRule() const {
DCHECK_EQ(Match(), static_cast<unsigned>(kPseudoClass));
DCHECK_EQ(GetPseudoType(), kPseudoParent);
return data_.parent_rule_.Get();
}
inline const AtomicString& CSSSelector::Value() const {
DCHECK_NE(Match(), static_cast<unsigned>(kTag));
if (HasRareData()) {
return data_.rare_data_->matching_value_;
}
return data_.value_;
}
inline const AtomicString& CSSSelector::SerializingValue() const {
DCHECK_NE(Match(), static_cast<unsigned>(kTag));
if (HasRareData()) {
return data_.rare_data_->serializing_value_;
}
return data_.value_;
}
inline bool CSSSelector::IsUserActionPseudoClass() const {
return GetPseudoType() == kPseudoHover || GetPseudoType() == kPseudoActive ||
GetPseudoType() == kPseudoFocus || GetPseudoType() == kPseudoDrag ||
GetPseudoType() == kPseudoFocusWithin ||
GetPseudoType() == kPseudoFocusVisible;
}
inline bool CSSSelector::IsIdClassOrAttributeSelector() const {
return IsAttributeSelector() || Match() == CSSSelector::kId ||
Match() == CSSSelector::kClass;
}
inline void swap(CSSSelector& a, CSSSelector& b) {
char tmp[sizeof(CSSSelector)];
memcpy(tmp, &a, sizeof(CSSSelector));
memcpy(&a, &b, sizeof(CSSSelector));
memcpy(&b, tmp, sizeof(CSSSelector));
}
// Converts descendant to relative descendant, child to relative child
// and so on. Subselector is converted to relative descendant.
// All others that don't have a corresponding relative combinator will
// call NOTREACHED().
CSSSelector::RelationType ConvertRelationToRelative(
CSSSelector::RelationType relation);
// Returns the maximum specificity within a list of selectors. This is typically
// used to calculate the specificity of selectors that have an inner selector
// list, e.g. :is(), :where() etc.
unsigned MaximumSpecificity(const CSSSelector* first_selector);
} // namespace blink
namespace WTF {
template <>
struct VectorTraits<blink::CSSSelector> : VectorTraitsBase<blink::CSSSelector> {
static const bool kCanInitializeWithMemset = true;
static const bool kCanClearUnusedSlotsWithMemset = true;
static const bool kCanMoveWithMemcpy = true;
};
} // namespace WTF
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_SELECTOR_H_