servo/components/style/stylesheets/document_rule.rs
Bobby Holley c99bcdd4b8 Run rustfmt on selectors, servo_arc, and style.
This was generated with:

./mach cargo fmt --package selectors &&
./mach cargo fmt --package servo_arc &&
./mach cargo fmt --package style

Using rustfmt 0.4.1-nightly (a4462d1 2018-03-26)
2018-04-10 17:35:15 -07:00

259 lines
9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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/. */
//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
//! We implement the prefixed `@-moz-document`.
use cssparser::{Parser, SourceLocation};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use media_queries::Device;
use parser::{Parse, ParserContext};
use servo_arc::Arc;
use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
use shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt::{self, Write};
use str::CssStringWriter;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use stylesheets::CssRules;
use values::CssUrl;
#[derive(Debug)]
/// A @-moz-document rule
pub struct DocumentRule {
/// The parsed condition
pub condition: DocumentCondition,
/// Child rules
pub rules: Arc<Locked<CssRules>>,
/// The line and column of the rule's source code.
pub source_location: SourceLocation,
}
impl DocumentRule {
/// Measure heap usage.
#[cfg(feature = "gecko")]
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
// Measurement of other fields may be added later.
self.rules.unconditional_shallow_size_of(ops) +
self.rules.read_with(guard).size_of(guard, ops)
}
}
impl ToCssWithGuard for DocumentRule {
fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
dest.write_str("@-moz-document ")?;
self.condition.to_css(&mut CssWriter::new(dest))?;
dest.write_str(" {")?;
for rule in self.rules.read_with(guard).0.iter() {
dest.write_str(" ")?;
rule.to_css(guard, dest)?;
}
dest.write_str(" }")
}
}
impl DeepCloneWithLock for DocumentRule {
/// Deep clones this DocumentRule.
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
params: &DeepCloneParams,
) -> Self {
let rules = self.rules.read_with(guard);
DocumentRule {
condition: self.condition.clone(),
rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
source_location: self.source_location.clone(),
}
}
}
/// A URL matching function for a `@document` rule's condition.
#[derive(Clone, Debug, ToCss)]
pub enum UrlMatchingFunction {
/// Exact URL matching function. It evaluates to true whenever the
/// URL of the document being styled is exactly the URL given.
Url(CssUrl),
/// URL prefix matching function. It evaluates to true whenever the
/// URL of the document being styled has the argument to the
/// function as an initial substring (which is true when the two
/// strings are equal). When the argument is the empty string,
/// it evaluates to true for all documents.
#[css(function)]
UrlPrefix(String),
/// Domain matching function. It evaluates to true whenever the URL
/// of the document being styled has a host subcomponent and that
/// host subcomponent is exactly the argument to the domain()
/// function or a final substring of the host component is a
/// period (U+002E) immediately followed by the argument to the
/// domain() function.
#[css(function)]
Domain(String),
/// Regular expression matching function. It evaluates to true
/// whenever the regular expression matches the entirety of the URL
/// of the document being styled.
#[css(function)]
Regexp(String),
}
macro_rules! parse_quoted_or_unquoted_string {
($input:ident, $url_matching_function:expr) => {
$input.parse_nested_block(|input| {
let start = input.position();
input
.parse_entirely(|input| {
let string = input.expect_string()?;
Ok($url_matching_function(string.as_ref().to_owned()))
})
.or_else(|_: ParseError| {
while let Ok(_) = input.next() {}
Ok($url_matching_function(input.slice_from(start).to_string()))
})
})
};
}
impl UrlMatchingFunction {
/// Parse a URL matching function for a`@document` rule's condition.
pub fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if input
.try(|input| input.expect_function_matching("url-prefix"))
.is_ok()
{
return parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::UrlPrefix);
}
if input
.try(|input| input.expect_function_matching("domain"))
.is_ok()
{
return parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::Domain);
}
if input
.try(|input| input.expect_function_matching("regexp"))
.is_ok()
{
return input.parse_nested_block(|input| {
Ok(UrlMatchingFunction::Regexp(
input.expect_string()?.as_ref().to_owned(),
))
});
}
let url = CssUrl::parse(context, input)?;
Ok(UrlMatchingFunction::Url(url))
}
#[cfg(feature = "gecko")]
/// Evaluate a URL matching function.
pub fn evaluate(&self, device: &Device) -> bool {
use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
use nsstring::nsCStr;
let func = match *self {
UrlMatchingFunction::Url(_) => GeckoUrlMatchingFunction::eURL,
UrlMatchingFunction::UrlPrefix(_) => GeckoUrlMatchingFunction::eURLPrefix,
UrlMatchingFunction::Domain(_) => GeckoUrlMatchingFunction::eDomain,
UrlMatchingFunction::Regexp(_) => GeckoUrlMatchingFunction::eRegExp,
};
let pattern = nsCStr::from(match *self {
UrlMatchingFunction::Url(ref url) => url.as_str(),
UrlMatchingFunction::UrlPrefix(ref pat) |
UrlMatchingFunction::Domain(ref pat) |
UrlMatchingFunction::Regexp(ref pat) => pat,
});
unsafe { Gecko_DocumentRule_UseForPresentation(device.pres_context(), &*pattern, func) }
}
#[cfg(not(feature = "gecko"))]
/// Evaluate a URL matching function.
pub fn evaluate(&self, _: &Device) -> bool {
false
}
}
/// A `@document` rule's condition.
///
/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document>
///
/// The `@document` rule's condition is written as a comma-separated list of
/// URL matching functions, and the condition evaluates to true whenever any
/// one of those functions evaluates to true.
#[css(comma)]
#[derive(Clone, Debug, ToCss)]
pub struct DocumentCondition(#[css(iterable)] Vec<UrlMatchingFunction>);
impl DocumentCondition {
/// Parse a document condition.
pub fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let conditions =
input.parse_comma_separated(|input| UrlMatchingFunction::parse(context, input))?;
let condition = DocumentCondition(conditions);
if !condition.allowed_in(context) {
return Err(
input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(
"-moz-document".into(),
)),
);
}
Ok(condition)
}
/// Evaluate a document condition.
pub fn evaluate(&self, device: &Device) -> bool {
self.0
.iter()
.any(|url_matching_function| url_matching_function.evaluate(device))
}
#[cfg(feature = "servo")]
fn allowed_in(&self, _: &ParserContext) -> bool {
false
}
#[cfg(feature = "gecko")]
fn allowed_in(&self, context: &ParserContext) -> bool {
use gecko_bindings::structs;
use stylesheets::Origin;
if context.stylesheet_origin != Origin::Author {
return true;
}
if unsafe { structs::StaticPrefs_sVarCache_layout_css_moz_document_content_enabled } {
return true;
}
if !unsafe {
structs::StaticPrefs_sVarCache_layout_css_moz_document_url_prefix_hack_enabled
} {
return false;
}
// Allow a single url-prefix() for compatibility.
//
// See bug 1446470 and dependencies.
if self.0.len() != 1 {
return false;
}
// NOTE(emilio): This technically allows url-prefix("") too, but...
match self.0[0] {
UrlMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(),
_ => false,
}
}
}