lewp_css/domain/at_rules/document/
url_matching_function.rs

1// This file is part of css. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT. No part of predicator, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
2// Copyright © 2017 The developers of css. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT.
3
4use {
5    super::Document,
6    crate::{
7        domain::SpecifiedUrl,
8        parsers::{Parse, ParserContext},
9        CustomParseError,
10    },
11    cssparser::{
12        serialize_string,
13        BasicParseError,
14        BasicParseErrorKind,
15        ParseError,
16        Parser,
17        ToCss,
18        Token,
19    },
20    std::fmt,
21};
22
23/// A URL matching function for a `@document` rule's condition.
24#[derive(Clone, Debug)]
25pub enum UrlMatchingFunction {
26    /// Exact URL matching function.
27    /// It evaluates to true whenever the URL of the document being styled is exactly the URL given.
28    Url(SpecifiedUrl),
29
30    /// URL prefix matching function.
31    /// 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).
32    /// When the argument is the empty string, it evaluates to true for all documents.
33    UrlPrefix(String),
34
35    /// Domain matching function.
36    /// 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.
37    Domain(String),
38
39    /// Regular expression matching function.
40    /// It evaluates to true whenever the regular expression matches the entirety of the URL of the document being styled.
41    RegExp(String),
42}
43
44macro_rules! parse_quoted_or_unquoted_string {
45    ($input:ident, $url_matching_function:expr) => {
46        $input.parse_nested_block(|input| {
47            let start = input.position();
48            input
49                .parse_entirely(|input| match input.next() {
50                    Ok(&Token::QuotedString(ref value)) => {
51                        Ok($url_matching_function(value.as_ref().to_owned()))
52                    }
53                    Ok(t) => Err(ParseError::from(BasicParseError {
54                        kind: BasicParseErrorKind::UnexpectedToken(t.clone()),
55                        location: input.state().source_location(),
56                    })),
57                    Err(e) => Err(e.into()),
58                })
59                .or_else(|_: ParseError<'i, CustomParseError<'i>>| {
60                    while let Ok(_) = input.next() {}
61
62                    Ok($url_matching_function(
63                        input.slice_from(start).to_string(),
64                    ))
65                })
66        })
67    };
68}
69
70impl ToCss for UrlMatchingFunction {
71    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
72        use self::UrlMatchingFunction::*;
73
74        match *self {
75            Url(ref url) => url.to_css(dest),
76
77            UrlPrefix(ref url_prefix) => {
78                dest.write_str("url-prefix(")?;
79                serialize_string(url_prefix, dest)?;
80                dest.write_char(')')
81            }
82
83            Domain(ref domain) => {
84                dest.write_str("domain(")?;
85                serialize_string(domain, dest)?;
86                dest.write_char(')')
87            }
88
89            RegExp(ref regex) => {
90                dest.write_str("regexp(")?;
91                serialize_string(regex, dest)?;
92                dest.write_char(')')
93            }
94        }
95    }
96}
97
98impl UrlMatchingFunction {
99    /// Parse a URL matching function for a `@document` rule's condition.
100    pub(crate) fn parse<'i, 't>(
101        context: &ParserContext,
102        input: &mut Parser<'i, 't>,
103    ) -> Result<UrlMatchingFunction, ParseError<'i, CustomParseError<'i>>> {
104        if input
105            .r#try(|input| input.expect_function_matching("url-prefix"))
106            .is_ok()
107        {
108            parse_quoted_or_unquoted_string!(
109                input,
110                UrlMatchingFunction::UrlPrefix
111            )
112        } else if input
113            .r#try(|input| input.expect_function_matching("domain"))
114            .is_ok()
115        {
116            parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::Domain)
117        } else if input
118            .r#try(|input| input.expect_function_matching("regexp"))
119            .is_ok()
120        {
121            input.parse_nested_block(|input| {
122                Ok(UrlMatchingFunction::RegExp(
123                    input.expect_string()?.as_ref().to_owned(),
124                ))
125            })
126        } else if let Ok(url) =
127            input.r#try(|input| SpecifiedUrl::parse(context, input))
128        {
129            Ok(UrlMatchingFunction::Url(url))
130        } else {
131            Err(ParseError::from(
132                CustomParseError::DocumentAtRuleUrlMatchingFunctionWasInvalid,
133            ))
134        }
135    }
136
137    /// Evaluate a document condition.
138    pub fn evaluate<D: Document>(&self, document: &D) -> bool {
139        document.documentMatchesUrl(self)
140    }
141}