use crate::derives::*;
use crate::parser::ParserContext;
use crate::stylesheets::CorsMode;
use crate::values::computed::{Context, ToComputedValue};
use servo_arc::Arc;
use std::fmt::{self, Write};
use std::ops::Deref;
use style_traits::{CssWriter, ToCss};
use to_shmem::{SharedMemoryBuilder, ToShmem};
use url::Url;
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]
#[css(function = "url")]
#[repr(C)]
pub struct CssUrl(#[ignore_malloc_size_of = "Arc"] pub Arc<CssUrlData>);
#[derive(Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]
#[repr(C)]
pub struct CssUrlData {
#[ignore_malloc_size_of = "Arc"]
original: Option<Arc<String>>,
#[ignore_malloc_size_of = "Arc"]
resolved: Option<Arc<Url>>,
}
impl ToShmem for CssUrl {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented");
}
}
impl Deref for CssUrl {
type Target = CssUrlData;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl CssUrl {
pub(super) fn new_from_string(
url: String,
context: &ParserContext,
_cors_mode: CorsMode,
) -> Self {
let serialization = Arc::new(url);
let resolved = (!serialization.is_empty())
.then(|| context.url_data.0.join(&serialization))
.and_then(Result::ok)
.map(Arc::new);
CssUrl(Arc::new(CssUrlData {
original: Some(serialization),
resolved: resolved,
}))
}
pub fn is_invalid(&self) -> bool {
self.resolved.is_none()
}
pub fn is_fragment(&self) -> bool {
error!("Can't determine whether the url is a fragment.");
false
}
pub fn url(&self) -> Option<&Arc<Url>> {
self.resolved.as_ref()
}
pub fn as_str(&self) -> &str {
match self.resolved {
Some(ref url) => url.as_str(),
None => "",
}
}
pub fn for_cascade(url: Arc<::url::Url>) -> Self {
CssUrl(Arc::new(CssUrlData {
original: None,
resolved: Some(url),
}))
}
pub fn new_for_testing(url: &str) -> Self {
CssUrl(Arc::new(CssUrlData {
original: Some(Arc::new(url.into())),
resolved: (!url.is_empty())
.then(|| ::url::Url::parse(url))
.and_then(Result::ok)
.map(Arc::new),
}))
}
}
impl PartialEq for CssUrl {
fn eq(&self, other: &Self) -> bool {
self.resolved == other.resolved
}
}
impl Eq for CssUrl {}
impl ToCss for CssUrl {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
let string = match self.0.original {
Some(ref original) => &**original,
None => match self.resolved {
Some(ref url) => url.as_str(),
None => "about:invalid",
},
};
dest.write_str("url(")?;
string.to_css(dest)?;
dest.write_char(')')
}
}
pub type SpecifiedUrl = CssUrl;
impl ToComputedValue for SpecifiedUrl {
type ComputedValue = ComputedUrl;
fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
match self.resolved {
Some(ref url) => ComputedUrl::Valid(url.clone()),
None => match self.original {
Some(ref url) => ComputedUrl::Invalid(url.clone()),
None => {
unreachable!("Found specified url with neither resolved or original URI!");
},
},
}
}
fn from_computed_value(computed: &ComputedUrl) -> Self {
let data = match *computed {
ComputedUrl::Valid(ref url) => CssUrlData {
original: None,
resolved: Some(url.clone()),
},
ComputedUrl::Invalid(ref url) => CssUrlData {
original: Some(url.clone()),
resolved: None,
},
};
CssUrl(Arc::new(data))
}
}
#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
pub enum ComputedUrl {
Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
}
impl ComputedUrl {
pub fn url(&self) -> Option<&Arc<Url>> {
match *self {
ComputedUrl::Valid(ref url) => Some(url),
_ => None,
}
}
}
impl ToCss for ComputedUrl {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
let string = match *self {
ComputedUrl::Valid(ref url) => url.as_str(),
ComputedUrl::Invalid(ref invalid_string) => invalid_string,
};
dest.write_str("url(")?;
string.to_css(dest)?;
dest.write_char(')')
}
}