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 = context.url_data.0.join(&serialization).ok().map(Arc::new);
72 CssUrl(Arc::new(CssUrlData {
73 original: Some(serialization),
74 resolved: resolved,
75 }))
76 }
77
78 pub fn is_invalid(&self) -> bool {
81 self.resolved.is_none()
82 }
83
84 pub fn is_fragment(&self) -> bool {
91 error!("Can't determine whether the url is a fragment.");
92 false
93 }
94
95 pub fn url(&self) -> Option<&Arc<Url>> {
97 self.resolved.as_ref()
98 }
99
100 pub fn as_str(&self) -> &str {
104 match self.resolved {
105 Some(ref url) => url.as_str(),
106 None => "",
107 }
108 }
109
110 pub fn for_cascade(url: Arc<::url::Url>) -> Self {
113 CssUrl(Arc::new(CssUrlData {
114 original: None,
115 resolved: Some(url),
116 }))
117 }
118
119 pub fn new_for_testing(url: &str) -> Self {
121 CssUrl(Arc::new(CssUrlData {
122 original: Some(Arc::new(url.into())),
123 resolved: ::url::Url::parse(url).ok().map(Arc::new),
124 }))
125 }
126
127 pub fn parse_with_cors_mode<'i, 't>(
133 context: &ParserContext,
134 input: &mut Parser<'i, 't>,
135 cors_mode: CorsMode,
136 ) -> Result<Self, ParseError<'i>> {
137 let url = input.expect_url()?;
138 Ok(Self::parse_from_string(
139 url.as_ref().to_owned(),
140 context,
141 cors_mode,
142 ))
143 }
144}
145
146impl Parse for CssUrl {
147 fn parse<'i, 't>(
148 context: &ParserContext,
149 input: &mut Parser<'i, 't>,
150 ) -> Result<Self, ParseError<'i>> {
151 Self::parse_with_cors_mode(context, input, CorsMode::None)
152 }
153}
154
155impl PartialEq for CssUrl {
156 fn eq(&self, other: &Self) -> bool {
157 self.resolved == other.resolved
160 }
161}
162
163impl Eq for CssUrl {}
164
165impl ToCss for CssUrl {
166 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
167 where
168 W: Write,
169 {
170 let string = match self.0.original {
171 Some(ref original) => &**original,
172 None => match self.resolved {
173 Some(ref url) => url.as_str(),
174 None => "about:invalid",
178 },
179 };
180
181 dest.write_str("url(")?;
182 string.to_css(dest)?;
183 dest.write_char(')')
184 }
185}
186
187pub type SpecifiedUrl = CssUrl;
189
190impl ToComputedValue for SpecifiedUrl {
191 type ComputedValue = ComputedUrl;
192
193 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
196 match self.resolved {
197 Some(ref url) => ComputedUrl::Valid(url.clone()),
198 None => match self.original {
199 Some(ref url) => ComputedUrl::Invalid(url.clone()),
200 None => {
201 unreachable!("Found specified url with neither resolved or original URI!");
202 },
203 },
204 }
205 }
206
207 fn from_computed_value(computed: &ComputedUrl) -> Self {
208 let data = match *computed {
209 ComputedUrl::Valid(ref url) => CssUrlData {
210 original: None,
211 resolved: Some(url.clone()),
212 },
213 ComputedUrl::Invalid(ref url) => CssUrlData {
214 original: Some(url.clone()),
215 resolved: None,
216 },
217 };
218 CssUrl(Arc::new(data))
219 }
220}
221
222#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
224pub enum ComputedUrl {
225 Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
227 Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
229}
230
231impl ComputedUrl {
232 pub fn url(&self) -> Option<&Arc<Url>> {
234 match *self {
235 ComputedUrl::Valid(ref url) => Some(url),
236 _ => None,
237 }
238 }
239}
240
241impl ToCss for ComputedUrl {
242 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
243 where
244 W: Write,
245 {
246 let string = match *self {
247 ComputedUrl::Valid(ref url) => url.as_str(),
248 ComputedUrl::Invalid(ref invalid_string) => invalid_string,
249 };
250
251 dest.write_str("url(")?;
252 string.to_css(dest)?;
253 dest.write_char(')')
254 }
255}