mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Auto merge of #17130 - Manishearth:stylo-attr, r=heycam,emilio
stylo: Handle attr() in `content` r=heycam https://bugzilla.mozilla.org/show_bug.cgi?id=1346693 <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17130) <!-- Reviewable:end -->
This commit is contained in:
commit
373d3b91dd
10 changed files with 186 additions and 75 deletions
|
@ -1399,6 +1399,9 @@ extern "C" {
|
||||||
pub fn Gecko_AddPropertyToSet(arg1: nsCSSPropertyIDSetBorrowedMut,
|
pub fn Gecko_AddPropertyToSet(arg1: nsCSSPropertyIDSetBorrowedMut,
|
||||||
arg2: nsCSSPropertyID);
|
arg2: nsCSSPropertyID);
|
||||||
}
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_RegisterNamespace(ns: *mut nsIAtom) -> i32;
|
||||||
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_Construct_Default_nsStyleFont(ptr: *mut nsStyleFont,
|
pub fn Gecko_Construct_Default_nsStyleFont(ptr: *mut nsStyleFont,
|
||||||
pres_context:
|
pres_context:
|
||||||
|
|
|
@ -273,11 +273,11 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_namespace(&self) -> Option<Namespace> {
|
fn default_namespace(&self) -> Option<Namespace> {
|
||||||
self.namespaces.default.clone()
|
self.namespaces.default.clone().as_ref().map(|&(ref ns, _)| ns.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
|
fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
|
||||||
self.namespaces.prefixes.get(prefix).cloned()
|
self.namespaces.prefixes.get(prefix).map(|&(ref ns, _)| ns.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
use context::QuirksMode;
|
use context::QuirksMode;
|
||||||
use cssparser::{Parser, SourcePosition, UnicodeRange};
|
use cssparser::{Parser, SourcePosition, UnicodeRange};
|
||||||
use error_reporting::ParseErrorReporter;
|
use error_reporting::ParseErrorReporter;
|
||||||
|
use parking_lot::RwLock;
|
||||||
use style_traits::OneOrMoreCommaSeparated;
|
use style_traits::OneOrMoreCommaSeparated;
|
||||||
use stylesheets::{CssRuleType, Origin, UrlExtraData};
|
use stylesheets::{CssRuleType, Origin, UrlExtraData, Namespaces};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// The mode to use when parsing values.
|
/// The mode to use when parsing values.
|
||||||
|
@ -81,6 +82,8 @@ pub struct ParserContext<'a> {
|
||||||
pub parsing_mode: ParsingMode,
|
pub parsing_mode: ParsingMode,
|
||||||
/// The quirks mode of this stylesheet.
|
/// The quirks mode of this stylesheet.
|
||||||
pub quirks_mode: QuirksMode,
|
pub quirks_mode: QuirksMode,
|
||||||
|
/// The list of all namespaces active in the current stylesheet
|
||||||
|
pub namespaces: Option<&'a RwLock<Namespaces>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ParserContext<'a> {
|
impl<'a> ParserContext<'a> {
|
||||||
|
@ -100,6 +103,7 @@ impl<'a> ParserContext<'a> {
|
||||||
line_number_offset: 0u64,
|
line_number_offset: 0u64,
|
||||||
parsing_mode: parsing_mode,
|
parsing_mode: parsing_mode,
|
||||||
quirks_mode: quirks_mode,
|
quirks_mode: quirks_mode,
|
||||||
|
namespaces: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +129,7 @@ impl<'a> ParserContext<'a> {
|
||||||
line_number_offset: context.line_number_offset,
|
line_number_offset: context.line_number_offset,
|
||||||
parsing_mode: context.parsing_mode,
|
parsing_mode: context.parsing_mode,
|
||||||
quirks_mode: context.quirks_mode,
|
quirks_mode: context.quirks_mode,
|
||||||
|
namespaces: context.namespaces,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +149,7 @@ impl<'a> ParserContext<'a> {
|
||||||
line_number_offset: line_number_offset,
|
line_number_offset: line_number_offset,
|
||||||
parsing_mode: parsing_mode,
|
parsing_mode: parsing_mode,
|
||||||
quirks_mode: quirks_mode,
|
quirks_mode: quirks_mode,
|
||||||
|
namespaces: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4401,12 +4401,12 @@ clip-path
|
||||||
as_utf16_and_forget(&value);
|
as_utf16_and_forget(&value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContentItem::Attr(ns, val) => {
|
ContentItem::Attr(attr) => {
|
||||||
self.gecko.mContents[i].mType = eStyleContentType_Attr;
|
self.gecko.mContents[i].mType = eStyleContentType_Attr;
|
||||||
let s = if let Some(ns) = ns {
|
let s = if let Some((_, ns)) = attr.namespace {
|
||||||
format!("{}|{}", ns, val)
|
format!("{}|{}", ns, attr.attribute)
|
||||||
} else {
|
} else {
|
||||||
val
|
attr.attribute.into()
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
// NB: we share allocators, so doing this is fine.
|
// NB: we share allocators, so doing this is fine.
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
use values::generics::CounterStyleOrNone;
|
use values::generics::CounterStyleOrNone;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
use values::specified::Attr;
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use super::list_style_type;
|
use super::list_style_type;
|
||||||
|
@ -36,6 +38,9 @@
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
type CounterStyleType = ::values::generics::CounterStyleOrNone;
|
type CounterStyleType = ::values::generics::CounterStyleOrNone;
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
use values::specified::Attr;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub enum ContentItem {
|
pub enum ContentItem {
|
||||||
|
@ -56,7 +61,7 @@
|
||||||
|
|
||||||
% if product == "gecko":
|
% if product == "gecko":
|
||||||
/// `attr([namespace? `|`]? ident)`
|
/// `attr([namespace? `|`]? ident)`
|
||||||
Attr(Option<String>, String),
|
Attr(Attr),
|
||||||
/// `url(url)`
|
/// `url(url)`
|
||||||
Url(SpecifiedUrl),
|
Url(SpecifiedUrl),
|
||||||
% endif
|
% endif
|
||||||
|
@ -90,14 +95,8 @@
|
||||||
ContentItem::NoCloseQuote => dest.write_str("no-close-quote"),
|
ContentItem::NoCloseQuote => dest.write_str("no-close-quote"),
|
||||||
|
|
||||||
% if product == "gecko":
|
% if product == "gecko":
|
||||||
ContentItem::Attr(ref ns, ref attr) => {
|
ContentItem::Attr(ref attr) => {
|
||||||
dest.write_str("attr(")?;
|
attr.to_css(dest)
|
||||||
if let Some(ref ns) = *ns {
|
|
||||||
cssparser::Token::Ident((&**ns).into()).to_css(dest)?;
|
|
||||||
dest.write_str("|")?;
|
|
||||||
}
|
|
||||||
cssparser::Token::Ident((&**attr).into()).to_css(dest)?;
|
|
||||||
dest.write_str(")")
|
|
||||||
}
|
}
|
||||||
ContentItem::Url(ref url) => url.to_css(dest),
|
ContentItem::Url(ref url) => url.to_css(dest),
|
||||||
% endif
|
% endif
|
||||||
|
@ -203,31 +202,7 @@
|
||||||
}),
|
}),
|
||||||
% if product == "gecko":
|
% if product == "gecko":
|
||||||
"attr" => input.parse_nested_block(|input| {
|
"attr" => input.parse_nested_block(|input| {
|
||||||
// Syntax is `[namespace? `|`]? ident`
|
Ok(ContentItem::Attr(Attr::parse_function(context, input)?))
|
||||||
// no spaces allowed
|
|
||||||
// FIXME (bug 1346693) we should be checking that
|
|
||||||
// this is a valid namespace and encoding it as a namespace
|
|
||||||
// number from the map
|
|
||||||
let first = input.try(|i| i.expect_ident()).ok().map(|i| i.into_owned());
|
|
||||||
if let Ok(token) = input.try(|i| i.next_including_whitespace()) {
|
|
||||||
match token {
|
|
||||||
Token::Delim('|') => {
|
|
||||||
// must be followed by an ident
|
|
||||||
let tok2 = input.next_including_whitespace()?;
|
|
||||||
if let Token::Ident(second) = tok2 {
|
|
||||||
return Ok(ContentItem::Attr(first, second.into_owned()))
|
|
||||||
} else {
|
|
||||||
return Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(first) = first {
|
|
||||||
Ok(ContentItem::Attr(None, first))
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
% endif
|
% endif
|
||||||
_ => return Err(())
|
_ => return Err(())
|
||||||
|
|
|
@ -430,11 +430,11 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_namespace(&self) -> Option<Namespace> {
|
fn default_namespace(&self) -> Option<Namespace> {
|
||||||
self.namespaces.default.clone()
|
self.namespaces.default.as_ref().map(|&(ref ns, _)| ns.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
|
fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
|
||||||
self.namespaces.prefixes.get(prefix).cloned()
|
self.namespaces.prefixes.get(prefix).map(|&(ref ns, _)| ns.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,9 +38,9 @@ use servo_config::prefs::PREFS;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
|
use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use std::{fmt, mem};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::fmt;
|
|
||||||
use std::mem::align_of;
|
use std::mem::align_of;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
@ -51,6 +51,7 @@ use stylearc::Arc;
|
||||||
use stylist::FnvHashMap;
|
use stylist::FnvHashMap;
|
||||||
use supports::SupportsCondition;
|
use supports::SupportsCondition;
|
||||||
use values::{CustomIdent, KeyframesName};
|
use values::{CustomIdent, KeyframesName};
|
||||||
|
use values::specified::NamespaceId;
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
use viewport::ViewportRule;
|
use viewport::ViewportRule;
|
||||||
|
|
||||||
|
@ -96,11 +97,13 @@ pub enum Origin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of namespaces applying to a given stylesheet.
|
/// A set of namespaces applying to a given stylesheet.
|
||||||
|
///
|
||||||
|
/// The namespace id is used in gecko
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct Namespaces {
|
pub struct Namespaces {
|
||||||
pub default: Option<Namespace>,
|
pub default: Option<(Namespace, NamespaceId)>,
|
||||||
pub prefixes: FnvHashMap<Prefix , Namespace>,
|
pub prefixes: FnvHashMap<Prefix, (Namespace, NamespaceId)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like gecko_bindings::structs::MallocSizeOf, but without the Option<> wrapper. Note that
|
/// Like gecko_bindings::structs::MallocSizeOf, but without the Option<> wrapper. Note that
|
||||||
|
@ -476,13 +479,13 @@ impl CssRule {
|
||||||
loader: Option<&StylesheetLoader>)
|
loader: Option<&StylesheetLoader>)
|
||||||
-> Result<(Self, State), SingleRuleParseError> {
|
-> Result<(Self, State), SingleRuleParseError> {
|
||||||
let error_reporter = NullReporter;
|
let error_reporter = NullReporter;
|
||||||
let mut namespaces = parent_stylesheet.namespaces.write();
|
let mut context = ParserContext::new(parent_stylesheet.origin,
|
||||||
let context = ParserContext::new(parent_stylesheet.origin,
|
|
||||||
&parent_stylesheet.url_data,
|
&parent_stylesheet.url_data,
|
||||||
&error_reporter,
|
&error_reporter,
|
||||||
None,
|
None,
|
||||||
PARSING_MODE_DEFAULT,
|
PARSING_MODE_DEFAULT,
|
||||||
parent_stylesheet.quirks_mode);
|
parent_stylesheet.quirks_mode);
|
||||||
|
context.namespaces = Some(&parent_stylesheet.namespaces);
|
||||||
let mut input = Parser::new(css);
|
let mut input = Parser::new(css);
|
||||||
|
|
||||||
// nested rules are in the body state
|
// nested rules are in the body state
|
||||||
|
@ -493,7 +496,6 @@ impl CssRule {
|
||||||
shared_lock: &parent_stylesheet.shared_lock,
|
shared_lock: &parent_stylesheet.shared_lock,
|
||||||
loader: loader,
|
loader: loader,
|
||||||
state: Cell::new(state),
|
state: Cell::new(state),
|
||||||
namespaces: &mut namespaces,
|
|
||||||
};
|
};
|
||||||
match parse_one_rule(&mut input, &mut rule_parser) {
|
match parse_one_rule(&mut input, &mut rule_parser) {
|
||||||
Ok(result) => Ok((result, rule_parser.state.get())),
|
Ok(result) => Ok((result, rule_parser.state.get())),
|
||||||
|
@ -1210,14 +1212,14 @@ impl Stylesheet {
|
||||||
stylesheet_loader: Option<&StylesheetLoader>,
|
stylesheet_loader: Option<&StylesheetLoader>,
|
||||||
error_reporter: &ParseErrorReporter,
|
error_reporter: &ParseErrorReporter,
|
||||||
line_number_offset: u64) {
|
line_number_offset: u64) {
|
||||||
let mut namespaces = Namespaces::default();
|
let namespaces = RwLock::new(Namespaces::default());
|
||||||
// FIXME: we really should update existing.url_data with the given url_data,
|
// FIXME: we really should update existing.url_data with the given url_data,
|
||||||
// otherwise newly inserted rule may not have the right base url.
|
// otherwise newly inserted rule may not have the right base url.
|
||||||
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
|
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
|
||||||
css, url_data, existing.origin, &mut namespaces,
|
css, url_data, existing.origin, &namespaces,
|
||||||
&existing.shared_lock, stylesheet_loader, error_reporter,
|
&existing.shared_lock, stylesheet_loader, error_reporter,
|
||||||
existing.quirks_mode, line_number_offset);
|
existing.quirks_mode, line_number_offset);
|
||||||
*existing.namespaces.write() = namespaces;
|
mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write());
|
||||||
existing.dirty_on_viewport_size_change
|
existing.dirty_on_viewport_size_change
|
||||||
.store(dirty_on_viewport_size_change, Ordering::Release);
|
.store(dirty_on_viewport_size_change, Ordering::Release);
|
||||||
|
|
||||||
|
@ -1229,7 +1231,7 @@ impl Stylesheet {
|
||||||
fn parse_rules(css: &str,
|
fn parse_rules(css: &str,
|
||||||
url_data: &UrlExtraData,
|
url_data: &UrlExtraData,
|
||||||
origin: Origin,
|
origin: Origin,
|
||||||
namespaces: &mut Namespaces,
|
namespaces: &RwLock<Namespaces>,
|
||||||
shared_lock: &SharedRwLock,
|
shared_lock: &SharedRwLock,
|
||||||
stylesheet_loader: Option<&StylesheetLoader>,
|
stylesheet_loader: Option<&StylesheetLoader>,
|
||||||
error_reporter: &ParseErrorReporter,
|
error_reporter: &ParseErrorReporter,
|
||||||
|
@ -1238,14 +1240,16 @@ impl Stylesheet {
|
||||||
-> (Vec<CssRule>, bool) {
|
-> (Vec<CssRule>, bool) {
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
let mut input = Parser::new(css);
|
let mut input = Parser::new(css);
|
||||||
|
let mut context = ParserContext::new_with_line_number_offset(origin, url_data, error_reporter,
|
||||||
|
line_number_offset,
|
||||||
|
PARSING_MODE_DEFAULT,
|
||||||
|
quirks_mode);
|
||||||
|
context.namespaces = Some(namespaces);
|
||||||
let rule_parser = TopLevelRuleParser {
|
let rule_parser = TopLevelRuleParser {
|
||||||
stylesheet_origin: origin,
|
stylesheet_origin: origin,
|
||||||
namespaces: namespaces,
|
|
||||||
shared_lock: shared_lock,
|
shared_lock: shared_lock,
|
||||||
loader: stylesheet_loader,
|
loader: stylesheet_loader,
|
||||||
context: ParserContext::new_with_line_number_offset(origin, url_data, error_reporter,
|
context: context,
|
||||||
line_number_offset, PARSING_MODE_DEFAULT,
|
|
||||||
quirks_mode),
|
|
||||||
state: Cell::new(State::Start),
|
state: Cell::new(State::Start),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1283,15 +1287,15 @@ impl Stylesheet {
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
line_number_offset: u64)
|
line_number_offset: u64)
|
||||||
-> Stylesheet {
|
-> Stylesheet {
|
||||||
let mut namespaces = Namespaces::default();
|
let namespaces = RwLock::new(Namespaces::default());
|
||||||
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
|
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
|
||||||
css, &url_data, origin, &mut namespaces,
|
css, &url_data, origin, &namespaces,
|
||||||
&shared_lock, stylesheet_loader, error_reporter, quirks_mode, line_number_offset,
|
&shared_lock, stylesheet_loader, error_reporter, quirks_mode, line_number_offset,
|
||||||
);
|
);
|
||||||
Stylesheet {
|
Stylesheet {
|
||||||
origin: origin,
|
origin: origin,
|
||||||
url_data: url_data,
|
url_data: url_data,
|
||||||
namespaces: RwLock::new(namespaces),
|
namespaces: namespaces,
|
||||||
rules: CssRules::new(rules, &shared_lock),
|
rules: CssRules::new(rules, &shared_lock),
|
||||||
media: media,
|
media: media,
|
||||||
shared_lock: shared_lock,
|
shared_lock: shared_lock,
|
||||||
|
@ -1470,7 +1474,6 @@ impl StylesheetLoader for NoOpLoader {
|
||||||
|
|
||||||
struct TopLevelRuleParser<'a> {
|
struct TopLevelRuleParser<'a> {
|
||||||
stylesheet_origin: Origin,
|
stylesheet_origin: Origin,
|
||||||
namespaces: &'a mut Namespaces,
|
|
||||||
shared_lock: &'a SharedRwLock,
|
shared_lock: &'a SharedRwLock,
|
||||||
loader: Option<&'a StylesheetLoader>,
|
loader: Option<&'a StylesheetLoader>,
|
||||||
context: ParserContext<'a>,
|
context: ParserContext<'a>,
|
||||||
|
@ -1483,7 +1486,6 @@ impl<'b> TopLevelRuleParser<'b> {
|
||||||
stylesheet_origin: self.stylesheet_origin,
|
stylesheet_origin: self.stylesheet_origin,
|
||||||
shared_lock: self.shared_lock,
|
shared_lock: self.shared_lock,
|
||||||
context: &self.context,
|
context: &self.context,
|
||||||
namespaces: self.namespaces,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1528,6 +1530,21 @@ enum AtRulePrelude {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fn register_namespace(ns: &Namespace) -> Result<i32, ()> {
|
||||||
|
let id = unsafe { ::gecko_bindings::bindings::Gecko_RegisterNamespace(ns.0.as_ptr()) };
|
||||||
|
if id == -1 {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
fn register_namespace(_: &Namespace) -> Result<(), ()> {
|
||||||
|
Ok(()) // servo doesn't use namespace ids
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
|
impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
|
||||||
type Prelude = AtRulePrelude;
|
type Prelude = AtRulePrelude;
|
||||||
type AtRule = CssRule;
|
type AtRule = CssRule;
|
||||||
|
@ -1586,12 +1603,16 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
|
||||||
let prefix_result = input.try(|input| input.expect_ident());
|
let prefix_result = input.try(|input| input.expect_ident());
|
||||||
let url = Namespace::from(try!(input.expect_url_or_string()));
|
let url = Namespace::from(try!(input.expect_url_or_string()));
|
||||||
|
|
||||||
|
let id = register_namespace(&url)?;
|
||||||
|
|
||||||
let opt_prefix = if let Ok(prefix) = prefix_result {
|
let opt_prefix = if let Ok(prefix) = prefix_result {
|
||||||
let prefix = Prefix::from(prefix);
|
let prefix = Prefix::from(prefix);
|
||||||
self.namespaces.prefixes.insert(prefix.clone(), url.clone());
|
self.context.namespaces.expect("namespaces must be set whilst parsing rules")
|
||||||
|
.write().prefixes.insert(prefix.clone(), (url.clone(), id));
|
||||||
Some(prefix)
|
Some(prefix)
|
||||||
} else {
|
} else {
|
||||||
self.namespaces.default = Some(url.clone());
|
self.context.namespaces.expect("namespaces must be set whilst parsing rules")
|
||||||
|
.write().default = Some((url.clone(), id));
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1650,7 +1671,6 @@ struct NestedRuleParser<'a, 'b: 'a> {
|
||||||
stylesheet_origin: Origin,
|
stylesheet_origin: Origin,
|
||||||
shared_lock: &'a SharedRwLock,
|
shared_lock: &'a SharedRwLock,
|
||||||
context: &'a ParserContext<'b>,
|
context: &'a ParserContext<'b>,
|
||||||
namespaces: &'b Namespaces,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> NestedRuleParser<'a, 'b> {
|
impl<'a, 'b> NestedRuleParser<'a, 'b> {
|
||||||
|
@ -1660,7 +1680,6 @@ impl<'a, 'b> NestedRuleParser<'a, 'b> {
|
||||||
stylesheet_origin: self.stylesheet_origin,
|
stylesheet_origin: self.stylesheet_origin,
|
||||||
shared_lock: self.shared_lock,
|
shared_lock: self.shared_lock,
|
||||||
context: &context,
|
context: &context,
|
||||||
namespaces: self.namespaces,
|
|
||||||
};
|
};
|
||||||
let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
|
let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
|
@ -1836,9 +1855,10 @@ impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
|
||||||
type QualifiedRule = CssRule;
|
type QualifiedRule = CssRule;
|
||||||
|
|
||||||
fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
|
fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
|
||||||
|
let ns = self.context.namespaces.expect("namespaces must be set when parsing rules").read();
|
||||||
let selector_parser = SelectorParser {
|
let selector_parser = SelectorParser {
|
||||||
stylesheet_origin: self.stylesheet_origin,
|
stylesheet_origin: self.stylesheet_origin,
|
||||||
namespaces: self.namespaces,
|
namespaces: &*ns,
|
||||||
};
|
};
|
||||||
SelectorList::parse(&selector_parser, input)
|
SelectorList::parse(&selector_parser, input)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
//!
|
//!
|
||||||
//! TODO(emilio): Enhance docs.
|
//! TODO(emilio): Enhance docs.
|
||||||
|
|
||||||
|
use Namespace;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use context::QuirksMode;
|
use context::QuirksMode;
|
||||||
use cssparser::{self, Parser, Token};
|
use cssparser::{self, Parser, Token, serialize_identifier};
|
||||||
use itoa;
|
use itoa;
|
||||||
use parser::{ParserContext, Parse};
|
use parser::{ParserContext, Parse};
|
||||||
use self::grid::TrackSizeOrRepeat;
|
use self::grid::TrackSizeOrRepeat;
|
||||||
|
@ -24,6 +25,7 @@ use super::computed::{self, Context};
|
||||||
use super::computed::{Shadow as ComputedShadow, ToComputedValue};
|
use super::computed::{Shadow as ComputedShadow, ToComputedValue};
|
||||||
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
|
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
|
||||||
use super::generics::grid::TrackList as GenericTrackList;
|
use super::generics::grid::TrackList as GenericTrackList;
|
||||||
|
use values::computed::ComputedValueAsSpecified;
|
||||||
use values::specified::calc::CalcNode;
|
use values::specified::calc::CalcNode;
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -1363,3 +1365,108 @@ impl AllowQuirks {
|
||||||
self == AllowQuirks::Yes && quirks_mode == QuirksMode::Quirks
|
self == AllowQuirks::Yes && quirks_mode == QuirksMode::Quirks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
/// A namespace ID
|
||||||
|
pub type NamespaceId = i32;
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
/// A namespace ID (used by gecko only)
|
||||||
|
pub type NamespaceId = ();
|
||||||
|
|
||||||
|
/// An attr(...) rule
|
||||||
|
///
|
||||||
|
/// `[namespace? `|`]? ident`
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub struct Attr {
|
||||||
|
/// Optional namespace
|
||||||
|
pub namespace: Option<(Namespace, NamespaceId)>,
|
||||||
|
/// Attribute name
|
||||||
|
pub attribute: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Attr {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Attr, ()> {
|
||||||
|
input.expect_function_matching("attr")?;
|
||||||
|
input.parse_nested_block(|i| Attr::parse_function(context, i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
/// Get the namespace id from the namespace map
|
||||||
|
pub fn get_id_for_namespace(namespace: &Namespace, context: &ParserContext) -> Result<NamespaceId, ()> {
|
||||||
|
if let Some(map) = context.namespaces {
|
||||||
|
if let Some(ref entry) = map.read().prefixes.get(&namespace.0) {
|
||||||
|
Ok(entry.1)
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if we don't have a namespace map (e.g. in inline styles)
|
||||||
|
// we can't parse namespaces
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
/// Get the namespace id from the namespace map
|
||||||
|
pub fn get_id_for_namespace(_: &Namespace, _: &ParserContext) -> Result<NamespaceId, ()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attr {
|
||||||
|
/// Parse contents of attr() assuming we have already parsed `attr` and are
|
||||||
|
/// within a parse_nested_block()
|
||||||
|
pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result<Attr, ()> {
|
||||||
|
// Syntax is `[namespace? `|`]? ident`
|
||||||
|
// no spaces allowed
|
||||||
|
let first = input.try(|i| i.expect_ident()).ok();
|
||||||
|
if let Ok(token) = input.try(|i| i.next_including_whitespace()) {
|
||||||
|
match token {
|
||||||
|
Token::Delim('|') => {
|
||||||
|
// must be followed by an ident
|
||||||
|
let second_token = match input.next_including_whitespace()? {
|
||||||
|
Token::Ident(second) => second,
|
||||||
|
_ => return Err(()),
|
||||||
|
};
|
||||||
|
let ns_with_id = if let Some(ns) = first {
|
||||||
|
let ns: Namespace = ns.into();
|
||||||
|
let id = get_id_for_namespace(&ns, context)?;
|
||||||
|
Some((ns, id))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
return Ok(Attr {
|
||||||
|
namespace: ns_with_id,
|
||||||
|
attribute: second_token.into_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => return Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(first) = first {
|
||||||
|
Ok(Attr {
|
||||||
|
namespace: None,
|
||||||
|
attribute: first.into_owned(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Attr {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
dest.write_str("attr(")?;
|
||||||
|
if let Some(ref ns) = self.namespace {
|
||||||
|
serialize_identifier(&ns.0.to_string(), dest)?;
|
||||||
|
dest.write_str("|")?;
|
||||||
|
}
|
||||||
|
serialize_identifier(&self.attribute, dest)?;
|
||||||
|
dest.write_str(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputedValueAsSpecified for Attr {}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use style::stylesheets::{Origin, Namespaces};
|
||||||
|
|
||||||
fn parse_selector(input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
|
fn parse_selector(input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
|
||||||
let mut ns = Namespaces::default();
|
let mut ns = Namespaces::default();
|
||||||
ns.prefixes.insert("svg".into(), ns!(svg));
|
ns.prefixes.insert("svg".into(), (ns!(svg), ()));
|
||||||
let parser = SelectorParser {
|
let parser = SelectorParser {
|
||||||
stylesheet_origin: Origin::UserAgent,
|
stylesheet_origin: Origin::UserAgent,
|
||||||
namespaces: &ns,
|
namespaces: &ns,
|
||||||
|
|
|
@ -70,7 +70,7 @@ fn test_parse_stylesheet() {
|
||||||
let stylesheet = Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
|
let stylesheet = Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
|
||||||
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
|
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
|
||||||
let mut namespaces = Namespaces::default();
|
let mut namespaces = Namespaces::default();
|
||||||
namespaces.default = Some(ns!(html));
|
namespaces.default = Some((ns!(html), ()));
|
||||||
let expected = Stylesheet {
|
let expected = Stylesheet {
|
||||||
origin: Origin::UserAgent,
|
origin: Origin::UserAgent,
|
||||||
media: Arc::new(stylesheet.shared_lock.wrap(MediaList::empty())),
|
media: Arc::new(stylesheet.shared_lock.wrap(MediaList::empty())),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue