1use crate::derives::*;
8use crate::parser::ParserContext;
9use crate::stylesheets::CorsMode;
10use crate::values::computed::{Context, ToComputedValue};
11use servo_arc::Arc;
12use std::fmt::{self, Write};
13use std::ops::Deref;
14use style_traits::{CssWriter, 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!(
53 "If servo wants to share stylesheets across processes, ToShmem for Url must be implemented"
54 );
55 }
56}
57
58impl Deref for CssUrl {
59 type Target = CssUrlData;
60 fn deref(&self) -> &Self::Target {
61 &self.0
62 }
63}
64
65impl CssUrl {
66 pub(super) fn new_from_string(
71 url: String,
72 context: &ParserContext,
73 _cors_mode: CorsMode,
74 ) -> Self {
75 let serialization = Arc::new(url);
76 let resolved = (!serialization.is_empty())
79 .then(|| context.url_data.0.join(&serialization))
80 .and_then(Result::ok)
81 .map(Arc::new);
82 CssUrl(Arc::new(CssUrlData {
83 original: Some(serialization),
84 resolved: resolved,
85 }))
86 }
87
88 pub fn is_invalid(&self) -> bool {
91 self.resolved.is_none()
92 }
93
94 pub fn is_fragment(&self) -> bool {
101 error!("Can't determine whether the url is a fragment.");
102 false
103 }
104
105 pub fn url(&self) -> Option<&Arc<Url>> {
107 self.resolved.as_ref()
108 }
109
110 pub fn as_str(&self) -> &str {
114 match self.resolved {
115 Some(ref url) => url.as_str(),
116 None => "",
117 }
118 }
119
120 pub fn for_cascade(url: Arc<::url::Url>) -> Self {
123 CssUrl(Arc::new(CssUrlData {
124 original: None,
125 resolved: Some(url),
126 }))
127 }
128
129 pub fn new_for_testing(url: &str) -> Self {
131 CssUrl(Arc::new(CssUrlData {
132 original: Some(Arc::new(url.into())),
133 resolved: (!url.is_empty())
134 .then(|| ::url::Url::parse(url))
135 .and_then(Result::ok)
136 .map(Arc::new),
137 }))
138 }
139}
140
141impl PartialEq for CssUrl {
142 fn eq(&self, other: &Self) -> bool {
143 self.resolved == other.resolved
146 }
147}
148
149impl Eq for CssUrl {}
150
151impl ToCss for CssUrl {
152 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
153 where
154 W: Write,
155 {
156 let string = match self.0.original {
157 Some(ref original) => &**original,
158 None => match self.resolved {
159 Some(ref url) => url.as_str(),
160 None => "about:invalid",
164 },
165 };
166
167 dest.write_str("url(")?;
168 string.to_css(dest)?;
169 dest.write_char(')')
170 }
171}
172
173pub type SpecifiedUrl = CssUrl;
175
176impl ToComputedValue for SpecifiedUrl {
177 type ComputedValue = ComputedUrl;
178
179 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
182 match self.resolved {
183 Some(ref url) => ComputedUrl::Valid(url.clone()),
184 None => match self.original {
185 Some(ref url) => ComputedUrl::Invalid(url.clone()),
186 None => {
187 unreachable!("Found specified url with neither resolved or original URI!");
188 },
189 },
190 }
191 }
192
193 fn from_computed_value(computed: &ComputedUrl) -> Self {
194 let data = match *computed {
195 ComputedUrl::Valid(ref url) => CssUrlData {
196 original: None,
197 resolved: Some(url.clone()),
198 },
199 ComputedUrl::Invalid(ref url) => CssUrlData {
200 original: Some(url.clone()),
201 resolved: None,
202 },
203 };
204 CssUrl(Arc::new(data))
205 }
206}
207
208#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
210pub enum ComputedUrl {
211 Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
213 Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
215}
216
217impl ComputedUrl {
218 pub fn url(&self) -> Option<&Arc<Url>> {
220 match *self {
221 ComputedUrl::Valid(ref url) => Some(url),
222 _ => None,
223 }
224 }
225}
226
227impl ToCss for ComputedUrl {
228 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
229 where
230 W: Write,
231 {
232 let string = match *self {
233 ComputedUrl::Valid(ref url) => url.as_str(),
234 ComputedUrl::Invalid(ref invalid_string) => invalid_string,
235 };
236
237 dest.write_str("url(")?;
238 string.to_css(dest)?;
239 dest.write_char(')')
240 }
241}