aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/layout_wrapper.rs42
-rw-r--r--components/style/dom.rs17
-rw-r--r--components/style/gecko/snapshot.rs10
-rw-r--r--components/style/gecko/wrapper.rs38
-rw-r--r--components/style/restyle_hints.rs30
-rw-r--r--components/style/servo/selector_parser.rs8
6 files changed, 134 insertions, 11 deletions
diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs
index 9ce38c6a187..f7f5396dc8d 100644
--- a/components/script/layout_wrapper.rs
+++ b/components/script/layout_wrapper.rs
@@ -69,7 +69,8 @@ use style::dom::{PresentationalHintsSynthesizer, TElement, TNode, UnsafeNode};
use style::element_state::*;
use style::font_metrics::ServoMetricsProvider;
use style::properties::{ComputedValues, PropertyDeclarationBlock};
-use style::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, extended_filtering};
+use style::selector_parser::{AttrValue as SelectorAttrValue, NonTSPseudoClass, PseudoClassStringArg};
+use style::selector_parser::{PseudoElement, SelectorImpl, extended_filtering};
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
use style::sink::Push;
use style::str::is_whitespace;
@@ -498,6 +499,39 @@ impl<'le> TElement for ServoLayoutElement<'le> {
fn has_css_transitions(&self) -> bool {
unreachable!("this should be only called on gecko");
}
+
+ #[inline]
+ fn lang_attr(&self) -> Option<SelectorAttrValue> {
+ self.get_attr(&ns!(xml), &local_name!("lang"))
+ .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
+ .map(|v| String::from(v as &str))
+ }
+
+ fn match_element_lang(&self,
+ override_lang: Option<Option<SelectorAttrValue>>,
+ value: &PseudoClassStringArg)
+ -> bool
+ {
+ // Servo supports :lang() from CSS Selectors 4, which can take a comma-
+ // separated list of language tags in the pseudo-class, and which
+ // performs RFC 4647 extended filtering matching on them.
+ //
+ // FIXME(heycam): This is wrong, since extended_filtering accepts
+ // a string containing commas (separating each language tag in
+ // a list) but the pseudo-class instead should be parsing and
+ // storing separate <ident> or <string>s for each language tag.
+ //
+ // FIXME(heycam): Look at `element`'s document's Content-Language
+ // HTTP header for language tags to match `value` against. To
+ // do this, we should make `get_lang_for_layout` return an Option,
+ // so we can decide when to fall back to the Content-Language check.
+ let element_lang = match override_lang {
+ Some(Some(lang)) => lang,
+ Some(None) => String::new(),
+ None => self.element.get_lang_for_layout(),
+ };
+ extended_filtering(&element_lang, &*value)
+ }
}
impl<'le> PartialEq for ServoLayoutElement<'le> {
@@ -690,11 +724,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
NonTSPseudoClass::AnyLink => self.is_link(),
NonTSPseudoClass::Visited => false,
- // FIXME(heycam): This is wrong, since extended_filtering accepts
- // a string containing commas (separating each language tag in
- // a list) but the pseudo-class instead should be parsing and
- // storing separate <ident> or <string>s for each language tag.
- NonTSPseudoClass::Lang(ref lang) => extended_filtering(&*self.element.get_lang_for_layout(), &*lang),
+ NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, &*lang),
NonTSPseudoClass::ServoNonZeroBorder => unsafe {
match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &local_name!("border")) {
diff --git a/components/style/dom.rs b/components/style/dom.rs
index 57011643301..64e11b2faf0 100644
--- a/components/style/dom.rs
+++ b/components/style/dom.rs
@@ -17,7 +17,8 @@ use properties::{ComputedValues, PropertyDeclarationBlock};
#[cfg(feature = "gecko")] use properties::animated_properties::AnimationValue;
#[cfg(feature = "gecko")] use properties::animated_properties::TransitionProperty;
use rule_tree::CascadeLevel;
-use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement};
+use selector_parser::{AttrValue, ElementExt, PreExistingComputedValues};
+use selector_parser::{PseudoClassStringArg, PseudoElement};
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
use shared_lock::Locked;
use sink::Push;
@@ -597,6 +598,20 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
existing_transitions: &HashMap<TransitionProperty,
Arc<AnimationValue>>)
-> bool;
+
+ /// Returns the value of the `xml:lang=""` attribute (or, if appropriate,
+ /// the `lang=""` attribute) on this element.
+ fn lang_attr(&self) -> Option<AttrValue>;
+
+ /// Returns whether this element's language matches the language tag
+ /// `value`. If `override_lang` is not `None`, it specifies the value
+ /// of the `xml:lang=""` or `lang=""` attribute to use in place of
+ /// looking at the element and its ancestors. (This argument is used
+ /// to implement matching of `:lang()` against snapshots.)
+ fn match_element_lang(&self,
+ override_lang: Option<Option<AttrValue>>,
+ value: &PseudoClassStringArg)
+ -> bool;
}
/// Trait abstracting over different kinds of dirty-descendants bits.
diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs
index 4c8fe00e602..d238151fd9a 100644
--- a/components/style/gecko/snapshot.rs
+++ b/components/style/gecko/snapshot.rs
@@ -181,4 +181,14 @@ impl ElementSnapshot for GeckoElementSnapshot {
callback,
bindings::Gecko_SnapshotClassOrClassList)
}
+
+ #[inline]
+ fn lang_attr(&self) -> Option<Atom> {
+ let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) };
+ if ptr.is_null() {
+ None
+ } else {
+ Some(unsafe { Atom::from_addrefed(ptr) })
+ }
+ }
}
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index d9baeb79312..cc4411ffaf5 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -45,6 +45,7 @@ use gecko_bindings::bindings::Gecko_GetStyleContext;
use gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_IsSignificantChild;
+use gecko_bindings::bindings::Gecko_MatchLang;
use gecko_bindings::bindings::Gecko_MatchStringArgPseudo;
use gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;
use gecko_bindings::bindings::Gecko_UpdateAnimations;
@@ -66,7 +67,7 @@ use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty};
use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel;
-use selector_parser::ElementExt;
+use selector_parser::{AttrValue, ElementExt, PseudoClassStringArg};
use selectors::Element;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
@@ -1023,6 +1024,35 @@ impl<'le> TElement for GeckoElement<'le> {
before_change_style,
after_change_style).does_animate()
}
+
+ #[inline]
+ fn lang_attr(&self) -> Option<AttrValue> {
+ let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
+ if ptr.is_null() {
+ None
+ } else {
+ Some(unsafe { Atom::from_addrefed(ptr) })
+ }
+ }
+
+ fn match_element_lang(&self,
+ override_lang: Option<Option<AttrValue>>,
+ value: &PseudoClassStringArg)
+ -> bool
+ {
+ // Gecko supports :lang() from CSS Selectors 3, which only accepts a
+ // single language tag, and which performs simple dash-prefix matching
+ // on it.
+ debug_assert!(value.len() > 0 && value[value.len() - 1] == 0,
+ "expected value to be null terminated");
+ let override_lang_ptr = match &override_lang {
+ &Some(Some(ref atom)) => atom.as_ptr(),
+ _ => ptr::null_mut(),
+ };
+ unsafe {
+ Gecko_MatchLang(self.0, override_lang_ptr, override_lang.is_some(), value.as_ptr())
+ }
+ }
}
impl<'le> PartialEq for GeckoElement<'le> {
@@ -1408,11 +1438,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
matches_complex_selector(s, 0, self, context, flags_setter)
})
}
+ NonTSPseudoClass::Lang(ref lang_arg) => {
+ self.match_element_lang(None, lang_arg)
+ }
NonTSPseudoClass::MozSystemMetric(ref s) |
NonTSPseudoClass::MozLocaleDir(ref s) |
NonTSPseudoClass::MozEmptyExceptChildrenWithLocalname(ref s) |
- NonTSPseudoClass::Dir(ref s) |
- NonTSPseudoClass::Lang(ref s) => {
+ NonTSPseudoClass::Dir(ref s) => {
unsafe {
let mut set_slow_selector = false;
let matches = Gecko_MatchStringArgPseudo(self.0,
diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs
index 44eec13f13c..e89ef8dfef7 100644
--- a/components/style/restyle_hints.rs
+++ b/components/style/restyle_hints.rs
@@ -555,8 +555,12 @@ pub trait ElementSnapshot : Sized {
/// only be called if `has_attrs()` returns true.
fn each_class<F>(&self, F)
where F: FnMut(&Atom);
+
+ /// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
+ fn lang_attr(&self) -> Option<AttrValue>;
}
+#[derive(Clone)]
struct ElementWrapper<'a, E>
where E: TElement,
{
@@ -606,6 +610,26 @@ impl<'a, E> ElementWrapper<'a, E>
None => ElementState::empty(),
}
}
+
+ /// Returns the value of the `xml:lang=""` (or, if appropriate, `lang=""`)
+ /// attribute from this element's snapshot or the closest ancestor
+ /// element snapshot with the attribute specified.
+ fn get_lang(&self) -> Option<AttrValue> {
+ let mut current = self.clone();
+ loop {
+ let lang = match self.snapshot() {
+ Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(),
+ _ => current.element.lang_attr(),
+ };
+ if lang.is_some() {
+ return lang;
+ }
+ match current.parent_element() {
+ Some(parent) => current = parent,
+ None => return None,
+ }
+ }
+ }
}
impl<'a, E> fmt::Debug for ElementWrapper<'a, E>
@@ -707,6 +731,12 @@ impl<'a, E> Element for ElementWrapper<'a, E>
}
}
+ // :lang() needs to match using the closest ancestor xml:lang="" or
+ // lang="" attribtue from snapshots.
+ NonTSPseudoClass::Lang(ref lang_arg) => {
+ return self.element.match_element_lang(Some(self.get_lang()), lang_arg);
+ }
+
_ => {}
}
diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs
index b754a35462d..43d3d5dffa0 100644
--- a/components/style/servo/selector_parser.rs
+++ b/components/style/servo/selector_parser.rs
@@ -13,7 +13,7 @@ use dom::{OpaqueNode, TElement, TNode};
use element_state::ElementState;
use fnv::FnvHashMap;
use restyle_hints::ElementSnapshot;
-use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
+use selector_parser::{AttrValue as SelectorAttrValue, ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::Element;
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::parser::SelectorMethods;
@@ -588,6 +588,12 @@ impl ElementSnapshot for ServoElementSnapshot {
}
}
}
+
+ fn lang_attr(&self) -> Option<SelectorAttrValue> {
+ self.get_attr(&ns!(xml), &local_name!("lang"))
+ .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
+ .map(|v| String::from(v as &str))
+ }
}
impl ServoElementSnapshot {