1use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::stylesheets::CorsMode;
10use crate::values::computed::{Context, ToComputedValue};
11use cssparser::{Parser, SourceLocation};
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<'i>(
69 url: String,
70 url_start: usize,
71 url_end: usize,
72 context: &ParserContext,
73 cors_mode: CorsMode,
74 location: SourceLocation,
75 ) -> Result<Self, ParseError<'i>> {
76 use crate::custom_properties::AttrTaintedRange;
77 use style_traits::StyleParseErrorKind;
78 let range = AttrTaintedRange::new(url_start, url_end);
79 if context.disallow_urls_in_range(&range) {
80 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
81 }
82 Ok(Self::new_from_string(url, context, cors_mode))
83 }
84
85 pub fn new_from_untainted_string(
90 url: String,
91 context: &ParserContext,
92 cors_mode: CorsMode,
93 ) -> Self {
94 debug_assert!(context.attr_tainted_regions.is_empty());
95 Self::new_from_string(url, context, cors_mode)
96 }
97
98 fn new_from_string(url: String, context: &ParserContext, _cors_mode: CorsMode) -> Self {
103 let serialization = Arc::new(url);
104 let resolved = (!serialization.is_empty())
107 .then(|| context.url_data.0.join(&serialization))
108 .and_then(Result::ok)
109 .map(Arc::new);
110 CssUrl(Arc::new(CssUrlData {
111 original: Some(serialization),
112 resolved: resolved,
113 }))
114 }
115
116 pub fn is_invalid(&self) -> bool {
119 self.resolved.is_none()
120 }
121
122 pub fn is_fragment(&self) -> bool {
129 error!("Can't determine whether the url is a fragment.");
130 false
131 }
132
133 pub fn url(&self) -> Option<&Arc<Url>> {
135 self.resolved.as_ref()
136 }
137
138 pub fn as_str(&self) -> &str {
142 match self.resolved {
143 Some(ref url) => url.as_str(),
144 None => "",
145 }
146 }
147
148 pub fn for_cascade(url: Arc<::url::Url>) -> Self {
151 CssUrl(Arc::new(CssUrlData {
152 original: None,
153 resolved: Some(url),
154 }))
155 }
156
157 pub fn new_for_testing(url: &str) -> Self {
159 CssUrl(Arc::new(CssUrlData {
160 original: Some(Arc::new(url.into())),
161 resolved: (!url.is_empty())
162 .then(|| ::url::Url::parse(url))
163 .and_then(Result::ok)
164 .map(Arc::new),
165 }))
166 }
167
168 pub fn parse_with_cors_mode<'i, 't>(
174 context: &ParserContext,
175 input: &mut Parser<'i, 't>,
176 cors_mode: CorsMode,
177 ) -> Result<Self, ParseError<'i>> {
178 let start = input.position().byte_index();
179 let location = input.current_source_location();
180 let url = input.expect_url()?;
181 let end = input.position().byte_index();
182 Self::parse_from_string(
183 url.as_ref().to_owned(),
184 start,
185 end,
186 context,
187 cors_mode,
188 location,
189 )
190 }
191}
192
193impl Parse for CssUrl {
194 fn parse<'i, 't>(
195 context: &ParserContext,
196 input: &mut Parser<'i, 't>,
197 ) -> Result<Self, ParseError<'i>> {
198 Self::parse_with_cors_mode(context, input, CorsMode::None)
199 }
200}
201
202impl PartialEq for CssUrl {
203 fn eq(&self, other: &Self) -> bool {
204 self.resolved == other.resolved
207 }
208}
209
210impl Eq for CssUrl {}
211
212impl ToCss for CssUrl {
213 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
214 where
215 W: Write,
216 {
217 let string = match self.0.original {
218 Some(ref original) => &**original,
219 None => match self.resolved {
220 Some(ref url) => url.as_str(),
221 None => "about:invalid",
225 },
226 };
227
228 dest.write_str("url(")?;
229 string.to_css(dest)?;
230 dest.write_char(')')
231 }
232}
233
234pub type SpecifiedUrl = CssUrl;
236
237impl ToComputedValue for SpecifiedUrl {
238 type ComputedValue = ComputedUrl;
239
240 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
243 match self.resolved {
244 Some(ref url) => ComputedUrl::Valid(url.clone()),
245 None => match self.original {
246 Some(ref url) => ComputedUrl::Invalid(url.clone()),
247 None => {
248 unreachable!("Found specified url with neither resolved or original URI!");
249 },
250 },
251 }
252 }
253
254 fn from_computed_value(computed: &ComputedUrl) -> Self {
255 let data = match *computed {
256 ComputedUrl::Valid(ref url) => CssUrlData {
257 original: None,
258 resolved: Some(url.clone()),
259 },
260 ComputedUrl::Invalid(ref url) => CssUrlData {
261 original: Some(url.clone()),
262 resolved: None,
263 },
264 };
265 CssUrl(Arc::new(data))
266 }
267}
268
269#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
271pub enum ComputedUrl {
272 Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
274 Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
276}
277
278impl ComputedUrl {
279 pub fn url(&self) -> Option<&Arc<Url>> {
281 match *self {
282 ComputedUrl::Valid(ref url) => Some(url),
283 _ => None,
284 }
285 }
286}
287
288impl ToCss for ComputedUrl {
289 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
290 where
291 W: Write,
292 {
293 let string = match *self {
294 ComputedUrl::Valid(ref url) => url.as_str(),
295 ComputedUrl::Invalid(ref invalid_string) => invalid_string,
296 };
297
298 dest.write_str("url(")?;
299 string.to_css(dest)?;
300 dest.write_char(')')
301 }
302}