1use crate::parser::{Parse, ParserContext};
8use crate::stylesheets::CorsMode;
9use crate::values::computed::{Context, ToComputedValue};
10use cssparser::Parser;
11use servo_arc::Arc;
12use std::fmt::{self, Write};
13use std::ops::Deref;
14use style_traits::{CssWriter, ParseError, ToCss};
15use to_shmem::{SharedMemoryBuilder, ToShmem};
16use url::Url;
17
18#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]
27#[css(function = "url")]
28#[repr(C)]
29pub struct CssUrl(#[ignore_malloc_size_of = "Arc"] pub Arc<CssUrlData>);
30
31#[derive(Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]
34#[repr(C)]
35pub struct CssUrlData {
36 #[ignore_malloc_size_of = "Arc"]
43 original: Option<Arc<String>>,
44
45 #[ignore_malloc_size_of = "Arc"]
47 resolved: Option<Arc<Url>>,
48}
49
50impl ToShmem for CssUrl {
51 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
52 unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented");
53 }
54}
55
56impl Deref for CssUrl {
57 type Target = CssUrlData;
58 fn deref(&self) -> &Self::Target {
59 &self.0
60 }
61}
62
63impl CssUrl {
64 pub fn parse_from_string(url: String, context: &ParserContext, _: CorsMode) -> Self {
69 let serialization = Arc::new(url);
70 let resolved = context.url_data.0.join(&serialization).ok().map(Arc::new);
71 CssUrl(Arc::new(CssUrlData {
72 original: Some(serialization),
73 resolved: resolved,
74 }))
75 }
76
77 pub fn is_invalid(&self) -> bool {
80 self.resolved.is_none()
81 }
82
83 pub fn is_fragment(&self) -> bool {
90 error!("Can't determine whether the url is a fragment.");
91 false
92 }
93
94 pub fn url(&self) -> Option<&Arc<Url>> {
96 self.resolved.as_ref()
97 }
98
99 pub fn as_str(&self) -> &str {
103 match self.resolved {
104 Some(ref url) => url.as_str(),
105 None => "",
106 }
107 }
108
109 pub fn for_cascade(url: Arc<::url::Url>) -> Self {
112 CssUrl(Arc::new(CssUrlData {
113 original: None,
114 resolved: Some(url),
115 }))
116 }
117
118 pub fn new_for_testing(url: &str) -> Self {
120 CssUrl(Arc::new(CssUrlData {
121 original: Some(Arc::new(url.into())),
122 resolved: ::url::Url::parse(url).ok().map(Arc::new),
123 }))
124 }
125
126 pub fn parse_with_cors_mode<'i, 't>(
132 context: &ParserContext,
133 input: &mut Parser<'i, 't>,
134 cors_mode: CorsMode,
135 ) -> Result<Self, ParseError<'i>> {
136 let url = input.expect_url()?;
137 Ok(Self::parse_from_string(
138 url.as_ref().to_owned(),
139 context,
140 cors_mode,
141 ))
142 }
143}
144
145impl Parse for CssUrl {
146 fn parse<'i, 't>(
147 context: &ParserContext,
148 input: &mut Parser<'i, 't>,
149 ) -> Result<Self, ParseError<'i>> {
150 Self::parse_with_cors_mode(context, input, CorsMode::None)
151 }
152}
153
154impl PartialEq for CssUrl {
155 fn eq(&self, other: &Self) -> bool {
156 self.resolved == other.resolved
159 }
160}
161
162impl Eq for CssUrl {}
163
164impl ToCss for CssUrl {
165 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
166 where
167 W: Write,
168 {
169 let string = match self.0.original {
170 Some(ref original) => &**original,
171 None => match self.resolved {
172 Some(ref url) => url.as_str(),
173 None => "about:invalid",
177 },
178 };
179
180 dest.write_str("url(")?;
181 string.to_css(dest)?;
182 dest.write_char(')')
183 }
184}
185
186pub type SpecifiedUrl = CssUrl;
188
189impl ToComputedValue for SpecifiedUrl {
190 type ComputedValue = ComputedUrl;
191
192 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
195 match self.resolved {
196 Some(ref url) => ComputedUrl::Valid(url.clone()),
197 None => match self.original {
198 Some(ref url) => ComputedUrl::Invalid(url.clone()),
199 None => {
200 unreachable!("Found specified url with neither resolved or original URI!");
201 },
202 },
203 }
204 }
205
206 fn from_computed_value(computed: &ComputedUrl) -> Self {
207 let data = match *computed {
208 ComputedUrl::Valid(ref url) => CssUrlData {
209 original: None,
210 resolved: Some(url.clone()),
211 },
212 ComputedUrl::Invalid(ref url) => CssUrlData {
213 original: Some(url.clone()),
214 resolved: None,
215 },
216 };
217 CssUrl(Arc::new(data))
218 }
219}
220
221#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
223pub enum ComputedUrl {
224 Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
226 Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
228}
229
230impl ComputedUrl {
231 pub fn url(&self) -> Option<&Arc<Url>> {
233 match *self {
234 ComputedUrl::Valid(ref url) => Some(url),
235 _ => None,
236 }
237 }
238}
239
240impl ToCss for ComputedUrl {
241 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
242 where
243 W: Write,
244 {
245 let string = match *self {
246 ComputedUrl::Valid(ref url) => url.as_str(),
247 ComputedUrl::Invalid(ref invalid_string) => invalid_string,
248 };
249
250 dest.write_str("url(")?;
251 string.to_css(dest)?;
252 dest.write_char(')')
253 }
254}