aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/selector_parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/style/selector_parser.rs')
-rw-r--r--components/style/selector_parser.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs
new file mode 100644
index 00000000000..b2d55c6551b
--- /dev/null
+++ b/components/style/selector_parser.rs
@@ -0,0 +1,137 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! The pseudo-classes and pseudo-elements supported by the style system.
+
+use matching::{common_style_affecting_attributes, CommonStyleAffectingAttributeMode};
+use selectors::Element;
+use selectors::parser::{AttrSelector, SelectorImpl};
+
+pub type AttrValue = <TheSelectorImpl as SelectorImpl>::AttrValue;
+
+#[cfg(feature = "servo")]
+pub use servo::selector_parser::*;
+
+#[cfg(feature = "gecko")]
+pub use gecko::selector_parser::*;
+
+#[cfg(feature = "servo")]
+pub use servo::selector_parser::ServoSelectorImpl as TheSelectorImpl;
+
+#[cfg(feature = "gecko")]
+pub use gecko::selector_parser::GeckoSelectorImpl as TheSelectorImpl;
+
+#[cfg(feature = "servo")]
+pub use servo::selector_parser::ServoElementSnapshot as Snapshot;
+
+#[cfg(feature = "gecko")]
+pub use gecko::snapshot::GeckoElementSnapshot as Snapshot;
+
+#[cfg(feature = "servo")]
+pub use servo::restyle_damage::ServoRestyleDamage as RestyleDamage;
+
+#[cfg(feature = "gecko")]
+pub use gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;
+
+/// This function determines if a pseudo-element is eagerly cascaded or not.
+///
+/// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
+/// `::before` and `::after`). They inherit styles normally as another
+/// selector would do, and they're part of the cascade.
+///
+/// Lazy pseudo-elements are affected by selector matching, but they're only
+/// computed when needed, and not before. They're useful for general
+/// pseudo-elements that are not very common.
+///
+/// Note that in Servo lazy pseudo-elements are restricted to a subset of
+/// selectors, so you can't use it for public pseudo-elements. This is not the
+/// case with Gecko though.
+///
+/// Precomputed ones skip the cascade process entirely, mostly as an
+/// optimisation since they are private pseudo-elements (like
+/// `::-servo-details-content`).
+///
+/// This pseudo-elements are resolved on the fly using *only* global rules
+/// (rules of the form `*|*`), and applying them to the parent style.
+///
+/// If you're implementing a public selector that the end-user might customize,
+/// then you probably need to make it eager.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PseudoElementCascadeType {
+ Eager,
+ Lazy,
+ Precomputed,
+}
+
+impl PseudoElementCascadeType {
+ #[inline]
+ pub fn is_eager(&self) -> bool {
+ *self == PseudoElementCascadeType::Eager
+ }
+
+ #[inline]
+ pub fn is_lazy(&self) -> bool {
+ *self == PseudoElementCascadeType::Lazy
+ }
+
+ #[inline]
+ pub fn is_precomputed(&self) -> bool {
+ *self == PseudoElementCascadeType::Precomputed
+ }
+}
+
+pub trait ElementExt: Element<Impl=TheSelectorImpl> {
+ fn is_link(&self) -> bool;
+}
+
+impl TheSelectorImpl {
+ #[inline]
+ pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
+ where F: FnMut(PseudoElement)
+ {
+ Self::each_pseudo_element(|pseudo| {
+ if Self::pseudo_element_cascade_type(&pseudo).is_eager() {
+ fun(pseudo)
+ }
+ })
+ }
+
+ #[inline]
+ pub fn each_precomputed_pseudo_element<F>(mut fun: F)
+ where F: FnMut(PseudoElement)
+ {
+ Self::each_pseudo_element(|pseudo| {
+ if Self::pseudo_element_cascade_type(&pseudo).is_precomputed() {
+ fun(pseudo)
+ }
+ })
+ }
+}
+
+pub fn attr_exists_selector_is_shareable(attr_selector: &AttrSelector<TheSelectorImpl>) -> bool {
+ // NB(pcwalton): If you update this, remember to update the corresponding list in
+ // `can_share_style_with()` as well.
+ common_style_affecting_attributes().iter().any(|common_attr_info| {
+ common_attr_info.attr_name == attr_selector.name && match common_attr_info.mode {
+ CommonStyleAffectingAttributeMode::IsPresent(_) => true,
+ CommonStyleAffectingAttributeMode::IsEqual(..) => false,
+ }
+ })
+}
+
+pub fn attr_equals_selector_is_shareable(attr_selector: &AttrSelector<TheSelectorImpl>,
+ value: &AttrValue) -> bool {
+ // FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
+ // here because the UA style otherwise disables all style sharing completely.
+ // FIXME(SimonSapin): should this be the attribute *name* rather than value?
+ atom!("dir") == *value ||
+ common_style_affecting_attributes().iter().any(|common_attr_info| {
+ common_attr_info.attr_name == attr_selector.name && match common_attr_info.mode {
+ CommonStyleAffectingAttributeMode::IsEqual(ref target_value, _) => {
+ *target_value == *value
+ }
+ CommonStyleAffectingAttributeMode::IsPresent(_) => false,
+ }
+ })
+}