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!("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(super) fn new_from_string(
69 url: String,
70 context: &ParserContext,
71 _cors_mode: CorsMode,
72 ) -> Self {
73 let serialization = Arc::new(url);
74 let resolved = (!serialization.is_empty())
77 .then(|| context.url_data.0.join(&serialization))
78 .and_then(Result::ok)
79 .map(Arc::new);
80 CssUrl(Arc::new(CssUrlData {
81 original: Some(serialization),
82 resolved: resolved,
83 }))
84 }
85
86 pub fn is_invalid(&self) -> bool {
89 self.resolved.is_none()
90 }
91
92 pub fn is_fragment(&self) -> bool {
99 error!("Can't determine whether the url is a fragment.");
100 false
101 }
102
103 pub fn url(&self) -> Option<&Arc<Url>> {
105 self.resolved.as_ref()
106 }
107
108 pub fn as_str(&self) -> &str {
112 match self.resolved {
113 Some(ref url) => url.as_str(),
114 None => "",
115 }
116 }
117
118 pub fn for_cascade(url: Arc<::url::Url>) -> Self {
121 CssUrl(Arc::new(CssUrlData {
122 original: None,
123 resolved: Some(url),
124 }))
125 }
126
127 pub fn new_for_testing(url: &str) -> Self {
129 CssUrl(Arc::new(CssUrlData {
130 original: Some(Arc::new(url.into())),
131 resolved: (!url.is_empty())
132 .then(|| ::url::Url::parse(url))
133 .and_then(Result::ok)
134 .map(Arc::new),
135 }))
136 }
137}
138
139impl PartialEq for CssUrl {
140 fn eq(&self, other: &Self) -> bool {
141 self.resolved == other.resolved
144 }
145}
146
147impl Eq for CssUrl {}
148
149impl ToCss for CssUrl {
150 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
151 where
152 W: Write,
153 {
154 let string = match self.0.original {
155 Some(ref original) => &**original,
156 None => match self.resolved {
157 Some(ref url) => url.as_str(),
158 None => "about:invalid",
162 },
163 };
164
165 dest.write_str("url(")?;
166 string.to_css(dest)?;
167 dest.write_char(')')
168 }
169}
170
171pub type SpecifiedUrl = CssUrl;
173
174impl ToComputedValue for SpecifiedUrl {
175 type ComputedValue = ComputedUrl;
176
177 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
180 match self.resolved {
181 Some(ref url) => ComputedUrl::Valid(url.clone()),
182 None => match self.original {
183 Some(ref url) => ComputedUrl::Invalid(url.clone()),
184 None => {
185 unreachable!("Found specified url with neither resolved or original URI!");
186 },
187 },
188 }
189 }
190
191 fn from_computed_value(computed: &ComputedUrl) -> Self {
192 let data = match *computed {
193 ComputedUrl::Valid(ref url) => CssUrlData {
194 original: None,
195 resolved: Some(url.clone()),
196 },
197 ComputedUrl::Invalid(ref url) => CssUrlData {
198 original: Some(url.clone()),
199 resolved: None,
200 },
201 };
202 CssUrl(Arc::new(data))
203 }
204}
205
206#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
208pub enum ComputedUrl {
209 Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
211 Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
213}
214
215impl ComputedUrl {
216 pub fn url(&self) -> Option<&Arc<Url>> {
218 match *self {
219 ComputedUrl::Valid(ref url) => Some(url),
220 _ => None,
221 }
222 }
223}
224
225impl ToCss for ComputedUrl {
226 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
227 where
228 W: Write,
229 {
230 let string = match *self {
231 ComputedUrl::Valid(ref url) => url.as_str(),
232 ComputedUrl::Invalid(ref invalid_string) => invalid_string,
233 };
234
235 dest.write_str("url(")?;
236 string.to_css(dest)?;
237 dest.write_char(')')
238 }
239}