Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support lang pseudo class #15464

Merged
merged 1 commit into from Feb 28, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Support lang pseudo class

  • Loading branch information
KiChjang committed Feb 28, 2017
commit 7456bcf02b7384dd7dabedf2ff2f9c47fc54af02
@@ -354,6 +354,7 @@ pub trait LayoutElementHelpers {
fn style_attribute(&self) -> *const Option<Arc<RwLock<PropertyDeclarationBlock>>>;
fn local_name(&self) -> &LocalName;
fn namespace(&self) -> &Namespace;
fn get_lang_for_layout(&self) -> String;
fn get_checked_state_for_layout(&self) -> bool;
fn get_indeterminate_state_for_layout(&self) -> bool;
fn get_state_for_layout(&self) -> ElementState;
@@ -693,6 +694,30 @@ impl LayoutElementHelpers for LayoutJS<Element> {
}
}

#[allow(unsafe_code)]
fn get_lang_for_layout(&self) -> String {
unsafe {
let mut current_node = Some(self.upcast::<Node>());

This comment has been minimized.

@emilio

emilio Feb 27, 2017

Member

I suggested this should be using Element itself, and doesn't seem addressed. If there's any particular reason it can't be done, or you think it's not worth it, please comment about it, otherwise, please do :)

This comment has been minimized.

@KiChjang

KiChjang Feb 27, 2017

Author Member

Perhaps it got lost from the GH notifications, but here is the original reply:

I'm not sure if it's possible to have a non-element node be a parent of an element. However your solution doesn't quite work, because we need some way of accessing the parent element of current_element, and there's no convenience method for this yet, meaning we'll need to upcast it back to a Node struct anyway just to call parent_node_ref.

This comment has been minimized.

@emilio

emilio Feb 28, 2017

Member

Oh, ok... I'm still kinda uneasy with the None => continue, but I can probably live with it?

while let Some(node) = current_node {
current_node = node.parent_node_ref();
match node.downcast::<Element>().map(|el| el.unsafe_get()) {
Some(elem) => {
if let Some(attr) = (*elem).get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
return attr.to_owned();
}
if let Some(attr) = (*elem).get_attr_val_for_layout(&ns!(), &local_name!("lang")) {
return attr.to_owned();
}
}
None => continue
}
}
// TODO: Check meta tags for a pragma-set default language
// TODO: Check HTTP Content-Language header
String::new()
}
}

#[inline]
#[allow(unsafe_code)]
fn get_checked_state_for_layout(&self) -> bool {
@@ -2372,6 +2397,10 @@ impl<'a> ::selectors::Element for Root<Element> {
}
},

// FIXME(#15746): This is wrong, we need to instead use extended filtering as per RFC4647
// https://tools.ietf.org/html/rfc4647#section-3.3.2
NonTSPseudoClass::Lang(ref lang) => lang.eq_ignore_ascii_case(&self.get_lang()),

NonTSPseudoClass::ReadOnly =>
!Element::state(self).contains(pseudo_class.state_flag()),

@@ -2576,6 +2605,19 @@ impl Element {
self.set_click_in_progress(false);
}

// https://html.spec.whatwg.org/multipage/#language
pub fn get_lang(&self) -> String {
self.upcast::<Node>().inclusive_ancestors().filter_map(|node| {
node.downcast::<Element>().and_then(|el| {
el.get_attribute(&ns!(xml), &local_name!("lang")).or_else(|| {
el.get_attribute(&ns!(), &local_name!("lang"))
}).map(|attr| String::from(attr.Value()))
})
// TODO: Check meta tags for a pragma-set default language
// TODO: Check HTTP Content-Language header
}).next().unwrap_or(String::new())
}

pub fn state(&self) -> ElementState {
self.state.get()
}
@@ -53,6 +53,7 @@ use selectors::matching::ElementSelectorFlags;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
@@ -618,6 +619,10 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
},
NonTSPseudoClass::Visited => false,

// FIXME(#15746): This is wrong, we need to instead use extended filtering as per RFC4647
// https://tools.ietf.org/html/rfc4647#section-3.3.2
NonTSPseudoClass::Lang(ref lang) => lang.eq_ignore_ascii_case(&self.element.get_lang_for_layout()),

NonTSPseudoClass::ServoNonZeroBorder => unsafe {
match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &local_name!("border")) {
None | Some(&AttrValue::UInt(_, 0)) => false,
@@ -8,7 +8,7 @@

use {Atom, Prefix, Namespace, LocalName};
use attr::{AttrIdentifier, AttrValue};
use cssparser::ToCss;
use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
use element_state::ElementState;
use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
@@ -100,44 +100,52 @@ impl PseudoElement {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub enum NonTSPseudoClass {
AnyLink,
Link,
Visited,
Active,
AnyLink,
Checked,
Disabled,
Enabled,
Focus,
Fullscreen,
Hover,
Enabled,
Disabled,
Checked,
Indeterminate,
ServoNonZeroBorder,
Lang(Box<str>),
Link,
PlaceholderShown,
ReadWrite,
ReadOnly,
PlaceholderShown,
ServoNonZeroBorder,
Target,
Visited,
}

impl ToCss for NonTSPseudoClass {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use self::NonTSPseudoClass::*;
if let Lang(ref lang) = *self {
dest.write_str(":lang(")?;
serialize_identifier(lang, dest)?;
return dest.write_str(")");
}

dest.write_str(match *self {
AnyLink => ":any-link",
Link => ":link",
Visited => ":visited",
Active => ":active",
AnyLink => ":any-link",
Checked => ":checked",
Disabled => ":disabled",
Enabled => ":enabled",
Focus => ":focus",
Fullscreen => ":fullscreen",
Hover => ":hover",
Enabled => ":enabled",
Disabled => ":disabled",
Checked => ":checked",
Indeterminate => ":indeterminate",
Lang(_) => unreachable!(),
Link => ":link",
PlaceholderShown => ":placeholder-shown",
ReadWrite => ":read-write",
ReadOnly => ":read-only",
PlaceholderShown => ":placeholder-shown",
Target => ":target",
ServoNonZeroBorder => ":-servo-nonzero-border",
Target => ":target",
Visited => ":visited",
})
}
}
@@ -162,6 +170,7 @@ impl NonTSPseudoClass {
Target => IN_TARGET_STATE,

AnyLink |
Lang(_) |
Link |
Visited |
ServoNonZeroBorder => ElementState::empty(),
@@ -204,21 +213,21 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
use self::NonTSPseudoClass::*;
let pseudo_class = match_ignore_ascii_case! { &name,
"any-link" => AnyLink,
"link" => Link,
"visited" => Visited,
"active" => Active,
"any-link" => AnyLink,
"checked" => Checked,
"disabled" => Disabled,
"enabled" => Enabled,
"focus" => Focus,
"fullscreen" => Fullscreen,
"hover" => Hover,
"enabled" => Enabled,
"disabled" => Disabled,
"checked" => Checked,
"indeterminate" => Indeterminate,
"link" => Link,
"placeholder-shown" => PlaceholderShown,
"read-write" => ReadWrite,
"read-only" => ReadOnly,
"placeholder-shown" => PlaceholderShown,
"target" => Target,
"visited" => Visited,
"-servo-nonzero-border" => {
if !self.in_user_agent_stylesheet() {
return Err(());
@@ -231,6 +240,19 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
Ok(pseudo_class)
}

fn parse_non_ts_functional_pseudo_class(&self,
name: Cow<str>,
parser: &mut CssParser)
-> Result<NonTSPseudoClass, ()> {
use self::NonTSPseudoClass::*;
let pseudo_class = match_ignore_ascii_case!{ &name,
"lang" => Lang(String::from(try!(parser.expect_ident_or_string())).into_boxed_str()),
_ => return Err(())
};

Ok(pseudo_class)
}

fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
use self::PseudoElement::*;
let pseudo_element = match_ignore_ascii_case! { &name,
@@ -149,3 +149,6 @@ skip: true
skip: true
[xhtml1print]
skip: true

[selectors-3_dev]
skip: false
@@ -0,0 +1,5 @@
[css3-selectors-lang-005.htm]
type: testharness
[A :lang value will match a lang attribute value when the latter contains additional subtags.]
expected: FAIL

@@ -0,0 +1,5 @@
[css3-selectors-lang-009.htm]
type: testharness
[A :lang value with a multiple subtags will match a lang attribute value with multiple subtags as long as the first part is the same.]

This comment has been minimized.

@emilio

emilio Feb 24, 2017

Member

I guess i should ask about all these failing tests? i know there were skipped before, but is there a problem in implementing this stuff right? if so, mind filing issues?

This comment has been minimized.

@KiChjang

KiChjang Feb 24, 2017

Author Member

That's essentially https://tools.ietf.org/html/rfc4647#section-3.3.2, I believe. Will file after this is merged.

This comment has been minimized.

@emilio

emilio Feb 25, 2017

Member

Why not filing it off-hand, and mark these (and your fixme) with that issue?

This comment has been minimized.

@KiChjang

KiChjang Feb 26, 2017

Author Member

Filed #15746.

expected: FAIL

@@ -0,0 +1,5 @@
[css3-selectors-lang-024.htm]
type: testharness
[A lang|= value will match a lang attribute value regardless of case differences.]
expected: FAIL

@@ -0,0 +1,5 @@
[css3-selectors-lang-035.htm]
type: testharness
[A lang|= value will match a lang attribute value regardless of case differences in the script tag.]
expected: FAIL

@@ -0,0 +1,5 @@
[css3-selectors-lang-044.htm]
type: testharness
[A lang= value will match a lang attribute value regardless of case differences.]
expected: FAIL

@@ -0,0 +1,5 @@
[css3-selectors-lang-055.htm]
type: testharness
[A lang= value will match a lang attribute value regardless of case differences in the script tag.]
expected: FAIL

@@ -0,0 +1,3 @@
[grid-first-letter-001.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-letter-002.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-letter-003.htm]
type: reftest
expected: FAIL
@@ -1,3 +1,3 @@
[case-sensitive-004.htm]
[grid-first-line-001.htm]
type: reftest
expected: FAIL
@@ -1,3 +1,3 @@
[lang-selector-003.htm]
[grid-first-line-002.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-line-003.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-letter-001.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-letter-002.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-letter-003.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-line-001.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-line-002.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-line-003.htm]
type: reftest
expected: FAIL
@@ -0,0 +1,5 @@
[css3-selectors-lang-005.xht]
type: testharness
[A :lang value will match a lang attribute value when the latter contains additional subtags.]
expected: FAIL

@@ -0,0 +1,5 @@
[css3-selectors-lang-009.xht]
type: testharness
[A :lang value with a multiple subtags will match a lang attribute value with multiple subtags as long as the first part is the same.]
expected: FAIL

@@ -0,0 +1,3 @@
[grid-first-letter-001.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-letter-002.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-letter-003.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-line-001.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-line-002.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-line-003.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-letter-001.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-letter-002.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-letter-003.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-line-001.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-line-002.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-inline-first-line-003.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,5 @@
[css3-selectors-lang-005.xht]
type: testharness
[A :lang value will match a lang attribute value when the latter contains additional subtags.]
expected: FAIL

@@ -0,0 +1,5 @@
[css3-selectors-lang-009.xht]
type: testharness
[A :lang value with a multiple subtags will match a lang attribute value with multiple subtags as long as the first part is the same.]
expected: FAIL

@@ -0,0 +1,3 @@
[grid-first-letter-001.xht]
type: reftest
expected: FAIL
@@ -0,0 +1,3 @@
[grid-first-letter-002.xht]
type: reftest
expected: FAIL
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.