mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
225 lines
7.4 KiB
Rust
225 lines
7.4 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
//! Common handling for the specified value CSS url() values.
|
|
|
|
use cssparser::{CssStringWriter, Parser};
|
|
#[cfg(feature = "gecko")]
|
|
use gecko_bindings::structs::ServoBundledURI;
|
|
#[cfg(feature = "gecko")]
|
|
use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
|
|
use parser::{Parse, ParserContext};
|
|
#[cfg(feature = "gecko")]
|
|
use parser::ParserContextExtraData;
|
|
use servo_url::ServoUrl;
|
|
use std::borrow::Cow;
|
|
use std::fmt::{self, Write};
|
|
use std::sync::Arc;
|
|
use style_traits::ToCss;
|
|
use values::HasViewportPercentage;
|
|
use values::computed::ComputedValueAsSpecified;
|
|
|
|
/// A set of data needed in Gecko to represent a URL.
|
|
#[derive(PartialEq, Clone, Debug)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Serialize, Deserialize, Eq))]
|
|
pub struct UrlExtraData {
|
|
/// The base URI.
|
|
#[cfg(feature = "gecko")]
|
|
pub base: GeckoArcURI,
|
|
/// The referrer.
|
|
#[cfg(feature = "gecko")]
|
|
pub referrer: GeckoArcURI,
|
|
/// The principal that originated this URI.
|
|
#[cfg(feature = "gecko")]
|
|
pub principal: GeckoArcPrincipal,
|
|
}
|
|
|
|
impl UrlExtraData {
|
|
/// Constructs a `UrlExtraData`.
|
|
#[cfg(feature = "servo")]
|
|
pub fn make_from(_: &ParserContext) -> Option<UrlExtraData> {
|
|
Some(UrlExtraData { })
|
|
}
|
|
|
|
/// Constructs a `UrlExtraData`.
|
|
#[cfg(feature = "gecko")]
|
|
pub fn make_from(context: &ParserContext) -> Option<UrlExtraData> {
|
|
match context.extra_data {
|
|
ParserContextExtraData {
|
|
base: Some(ref base),
|
|
referrer: Some(ref referrer),
|
|
principal: Some(ref principal),
|
|
} => {
|
|
Some(UrlExtraData {
|
|
base: base.clone(),
|
|
referrer: referrer.clone(),
|
|
principal: principal.clone(),
|
|
})
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A specified url() value.
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Serialize, Deserialize))]
|
|
pub struct SpecifiedUrl {
|
|
/// The original URI. This might be optional since we may insert computed
|
|
/// values of images into the cascade directly, and we don't bother to
|
|
/// convert their serialization.
|
|
///
|
|
/// Refcounted since cloning this should be cheap and data: uris can be
|
|
/// really large.
|
|
original: Option<Arc<String>>,
|
|
|
|
/// The resolved value for the url, if valid.
|
|
resolved: Option<ServoUrl>,
|
|
|
|
/// Extra data used for Stylo.
|
|
extra_data: UrlExtraData,
|
|
}
|
|
|
|
impl Parse for SpecifiedUrl {
|
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
let url = try!(input.expect_url());
|
|
Self::parse_from_string(url, context)
|
|
}
|
|
}
|
|
|
|
impl SpecifiedUrl {
|
|
/// Try to parse a URL from a string value that is a valid CSS token for a
|
|
/// URL.
|
|
///
|
|
/// Only returns `Err` for Gecko, in the case we can't construct a
|
|
/// `URLExtraData`.
|
|
pub fn parse_from_string<'a>(url: Cow<'a, str>,
|
|
context: &ParserContext)
|
|
-> Result<Self, ()> {
|
|
let extra_data = match UrlExtraData::make_from(context) {
|
|
Some(extra_data) => extra_data,
|
|
None => {
|
|
// FIXME(heycam) should ensure we always have a principal, etc.,
|
|
// when parsing style attributes and re-parsing due to CSS
|
|
// Variables.
|
|
warn!("stylo: skipping declaration without ParserContextExtraData");
|
|
return Err(())
|
|
},
|
|
};
|
|
|
|
let serialization = Arc::new(url.into_owned());
|
|
let resolved = context.base_url.join(&serialization).ok();
|
|
Ok(SpecifiedUrl {
|
|
original: Some(serialization),
|
|
resolved: resolved,
|
|
extra_data: extra_data,
|
|
})
|
|
}
|
|
|
|
/// Get this URL's extra data.
|
|
pub fn extra_data(&self) -> &UrlExtraData {
|
|
&self.extra_data
|
|
}
|
|
|
|
/// Returns the resolved url if it was valid.
|
|
pub fn url(&self) -> Option<&ServoUrl> {
|
|
self.resolved.as_ref()
|
|
}
|
|
|
|
/// Return the resolved url as string, or the empty string if it's invalid.
|
|
///
|
|
/// TODO(emilio): Should we return the original one if needed?
|
|
pub fn as_str(&self) -> &str {
|
|
match self.resolved {
|
|
Some(ref url) => url.as_str(),
|
|
None => "",
|
|
}
|
|
}
|
|
|
|
/// Little helper for Gecko's ffi.
|
|
#[cfg(feature = "gecko")]
|
|
pub fn as_slice_components(&self) -> Result<(*const u8, usize), (*const u8, usize)> {
|
|
match self.resolved {
|
|
Some(ref url) => Ok((url.as_str().as_ptr(), url.as_str().len())),
|
|
None => {
|
|
let url = self.original.as_ref()
|
|
.expect("We should always have either the original or the resolved value");
|
|
Err((url.as_str().as_ptr(), url.as_str().len()))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Creates an already specified url value from an already resolved URL
|
|
/// for insertion in the cascade.
|
|
pub fn for_cascade(url: ServoUrl, extra_data: UrlExtraData) -> Self {
|
|
SpecifiedUrl {
|
|
original: None,
|
|
resolved: Some(url),
|
|
extra_data: extra_data,
|
|
}
|
|
}
|
|
|
|
/// Gets a new url from a string for unit tests.
|
|
#[cfg(feature = "servo")]
|
|
pub fn new_for_testing(url: &str) -> Self {
|
|
SpecifiedUrl {
|
|
original: Some(Arc::new(url.into())),
|
|
resolved: ServoUrl::parse(url).ok(),
|
|
extra_data: UrlExtraData {}
|
|
}
|
|
}
|
|
|
|
/// Create a bundled URI suitable for sending to Gecko
|
|
/// to be constructed into a css::URLValue
|
|
#[cfg(feature = "gecko")]
|
|
pub fn for_ffi(&self) -> Option<ServoBundledURI> {
|
|
let extra_data = self.extra_data();
|
|
let (ptr, len) = match self.as_slice_components() {
|
|
Ok(value) => value,
|
|
Err(_) => return None,
|
|
};
|
|
Some(ServoBundledURI {
|
|
mURLString: ptr,
|
|
mURLStringLength: len as u32,
|
|
mBaseURI: extra_data.base.get(),
|
|
mReferrer: extra_data.referrer.get(),
|
|
mPrincipal: extra_data.principal.get(),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl PartialEq for SpecifiedUrl {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
// TODO(emilio): maybe we care about equality of the specified values if
|
|
// present? Seems not.
|
|
self.resolved == other.resolved &&
|
|
self.extra_data == other.extra_data
|
|
}
|
|
}
|
|
|
|
impl Eq for SpecifiedUrl {}
|
|
|
|
impl ToCss for SpecifiedUrl {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
try!(dest.write_str("url(\""));
|
|
let string = match self.original {
|
|
Some(ref original) => &**original,
|
|
None => match self.resolved {
|
|
Some(ref url) => url.as_str(),
|
|
// This can only happen if the url wasn't specified by the
|
|
// user *and* it's an invalid url that has been transformed
|
|
// back to specified value via the "uncompute" functionality.
|
|
None => "about:invalid",
|
|
}
|
|
};
|
|
|
|
try!(CssStringWriter::new(dest).write_str(string));
|
|
dest.write_str("\")")
|
|
}
|
|
}
|
|
|
|
// TODO(emilio): Maybe consider ComputedUrl to save a word in style structs?
|
|
impl ComputedValueAsSpecified for SpecifiedUrl {}
|
|
|
|
no_viewport_percentage!(SpecifiedUrl);
|