1use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::stylesheets::CorsMode;
10use crate::values::computed::{Context, ToComputedValue};
11use cssparser::Parser;
12use servo_arc::Arc;
13use std::fmt::{self, Write};
14use std::ops::Deref;
15use style_traits::{CssWriter, ParseError, ToCss};
16use to_shmem::{SharedMemoryBuilder, ToShmem};
17use url::Url;
18
19#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]
28#[css(function = "url")]
29#[repr(C)]
30pub struct CssUrl(#[ignore_malloc_size_of = "Arc"] pub Arc<CssUrlData>);
31
32#[derive(Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]
35#[repr(C)]
36pub struct CssUrlData {
37 #[ignore_malloc_size_of = "Arc"]
44 original: Option<Arc<String>>,
45
46 #[ignore_malloc_size_of = "Arc"]
48 resolved: Option<Arc<Url>>,
49}
50
51impl ToShmem for CssUrl {
52 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
53 unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented");
54 }
55}
56
57impl Deref for CssUrl {
58 type Target = CssUrlData;
59 fn deref(&self) -> &Self::Target {
60 &self.0
61 }
62}
63
64impl CssUrl {
65 pub fn parse_from_string(url: String, context: &ParserContext, _: CorsMode) -> Self {
70 let serialization = Arc::new(url);
71 let resolved = (!serialization.is_empty())
74 .then(|| context.url_data.0.join(&serialization))
75 .and_then(Result::ok)
76 .map(Arc::new);
77 CssUrl(Arc::new(CssUrlData {
78 original: Some(serialization),
79 resolved: resolved,
80 }))
81 }
82
83 pub fn is_invalid(&self) -> bool {
86 self.resolved.is_none()
87 }
88
89 pub fn is_fragment(&self) -> bool {
96 error!("Can't determine whether the url is a fragment.");
97 false
98 }
99
100 pub fn url(&self) -> Option<&Arc<Url>> {
102 self.resolved.as_ref()
103 }
104
105 pub fn as_str(&self) -> &str {
109 match self.resolved {
110 Some(ref url) => url.as_str(),
111 None => "",
112 }
113 }
114
115 pub fn for_cascade(url: Arc<::url::Url>) -> Self {
118 CssUrl(Arc::new(CssUrlData {
119 original: None,
120 resolved: Some(url),
121 }))
122 }
123
124 pub fn new_for_testing(url: &str) -> Self {
126 CssUrl(Arc::new(CssUrlData {
127 original: Some(Arc::new(url.into())),
128 resolved: (!url.is_empty())
129 .then(|| ::url::Url::parse(url))
130 .and_then(Result::ok)
131 .map(Arc::new),
132 }))
133 }
134
135 pub fn parse_with_cors_mode<'i, 't>(
141 context: &ParserContext,
142 input: &mut Parser<'i, 't>,
143 cors_mode: CorsMode,
144 ) -> Result<Self, ParseError<'i>> {
145 let url = input.expect_url()?;
146 Ok(Self::parse_from_string(
147 url.as_ref().to_owned(),
148 context,
149 cors_mode,
150 ))
151 }
152}
153
154impl Parse for CssUrl {
155 fn parse<'i, 't>(
156 context: &ParserContext,
157 input: &mut Parser<'i, 't>,
158 ) -> Result<Self, ParseError<'i>> {
159 Self::parse_with_cors_mode(context, input, CorsMode::None)
160 }
161}
162
163impl PartialEq for CssUrl {
164 fn eq(&self, other: &Self) -> bool {
165 self.resolved == other.resolved
168 }
169}
170
171impl Eq for CssUrl {}
172
173impl ToCss for CssUrl {
174 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
175 where
176 W: Write,
177 {
178 let string = match self.0.original {
179 Some(ref original) => &**original,
180 None => match self.resolved {
181 Some(ref url) => url.as_str(),
182 None => "about:invalid",
186 },
187 };
188
189 dest.write_str("url(")?;
190 string.to_css(dest)?;
191 dest.write_char(')')
192 }
193}
194
195pub type SpecifiedUrl = CssUrl;
197
198impl ToComputedValue for SpecifiedUrl {
199 type ComputedValue = ComputedUrl;
200
201 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
204 match self.resolved {
205 Some(ref url) => ComputedUrl::Valid(url.clone()),
206 None => match self.original {
207 Some(ref url) => ComputedUrl::Invalid(url.clone()),
208 None => {
209 unreachable!("Found specified url with neither resolved or original URI!");
210 },
211 },
212 }
213 }
214
215 fn from_computed_value(computed: &ComputedUrl) -> Self {
216 let data = match *computed {
217 ComputedUrl::Valid(ref url) => CssUrlData {
218 original: None,
219 resolved: Some(url.clone()),
220 },
221 ComputedUrl::Invalid(ref url) => CssUrlData {
222 original: Some(url.clone()),
223 resolved: None,
224 },
225 };
226 CssUrl(Arc::new(data))
227 }
228}
229
230#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
232pub enum ComputedUrl {
233 Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
235 Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
237}
238
239impl ComputedUrl {
240 pub fn url(&self) -> Option<&Arc<Url>> {
242 match *self {
243 ComputedUrl::Valid(ref url) => Some(url),
244 _ => None,
245 }
246 }
247}
248
249impl ToCss for ComputedUrl {
250 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
251 where
252 W: Write,
253 {
254 let string = match *self {
255 ComputedUrl::Valid(ref url) => url.as_str(),
256 ComputedUrl::Invalid(ref invalid_string) => invalid_string,
257 };
258
259 dest.write_str("url(")?;
260 string.to_css(dest)?;
261 dest.write_char(')')
262 }
263}