/* 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 gecko_bindings::structs::{ServoBundledURI, URLExtraData}; use gecko_bindings::structs::mozilla::css::URLValueData; use gecko_bindings::structs::root::{nsStyleImageRequest, RustString}; use gecko_bindings::structs::root::mozilla::css::ImageValue; use gecko_bindings::sugar::refptr::RefPtr; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use parser::ParserContext; use servo_arc::{Arc, RawOffsetArc}; use std::fmt; use std::mem; use style_traits::{ToCss, ParseError}; /// A specified url() value for gecko. Gecko does not eagerly resolve SpecifiedUrls. #[derive(Clone, Debug, PartialEq)] pub struct SpecifiedUrl { /// The URL in unresolved string form. /// /// Refcounted since cloning this should be cheap and data: uris can be /// really large. serialization: Arc, /// The URL extra data. pub extra_data: RefPtr, /// Cache ImageValue, if any, so that we can reuse it while rematching a /// a property with this specified url value. pub image_value: Option>, } trivial_to_computed_value!(SpecifiedUrl); impl SpecifiedUrl { /// Try to parse a URL from a string value that is a valid CSS token for a /// URL. /// /// Returns `Err` in the case that extra_data is incomplete. pub fn parse_from_string<'a>(url: String, context: &ParserContext) -> Result> { Ok(SpecifiedUrl { serialization: Arc::new(url), extra_data: context.url_data.clone(), image_value: None, }) } /// Returns true if the URL is definitely invalid. We don't eagerly resolve /// URLs in gecko, so we just return false here. /// use its |resolved| status. pub fn is_invalid(&self) -> bool { false } /// Convert from URLValueData to SpecifiedUrl. pub unsafe fn from_url_value_data(url: &URLValueData) -> Result { Ok(SpecifiedUrl { serialization: if url.mUsingRustString { let arc_type = url.mStrings.mRustString.as_ref() as *const _ as *const RawOffsetArc; Arc::from_raw_offset((*arc_type).clone()) } else { Arc::new(url.mStrings.mString.as_ref().to_string()) }, extra_data: url.mExtraData.to_safe(), image_value: None, }) } /// Convert from nsStyleImageRequest to SpecifiedUrl. pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Result { if image_request.mImageValue.mRawPtr.is_null() { return Err(()); } let image_value = image_request.mImageValue.mRawPtr.as_ref().unwrap(); let ref url_value_data = image_value._base; let mut result = try!(Self::from_url_value_data(url_value_data)); result.build_image_value(); Ok(result) } /// Returns true if this URL looks like a fragment. /// See https://drafts.csswg.org/css-values/#local-urls pub fn is_fragment(&self) -> bool { self.as_str().chars().next().map_or(false, |c| c == '#') } /// Return the resolved url as string, or the empty string if it's invalid. /// /// FIXME(bholley): This returns the unresolved URL while the servo version /// returns the resolved URL. pub fn as_str(&self) -> &str { &*self.serialization } /// Little helper for Gecko's ffi. pub fn as_slice_components(&self) -> (*const u8, usize) { (self.serialization.as_str().as_ptr(), self.serialization.as_str().len()) } /// Create a bundled URI suitable for sending to Gecko /// to be constructed into a css::URLValue pub fn for_ffi(&self) -> ServoBundledURI { let (ptr, len) = self.as_slice_components(); ServoBundledURI { mURLString: ptr, mURLStringLength: len as u32, mExtraData: self.extra_data.get(), } } /// Build and carry an image value on request. pub fn build_image_value(&mut self) { use gecko_bindings::bindings::Gecko_ImageValue_Create; debug_assert_eq!(self.image_value, None); self.image_value = { unsafe { let arc_offset = Arc::into_raw_offset(self.serialization.clone()); let ptr = Gecko_ImageValue_Create( self.for_ffi(), mem::transmute::<_, RawOffsetArc>(arc_offset)); // We do not expect Gecko_ImageValue_Create returns null. debug_assert!(!ptr.is_null()); Some(RefPtr::from_addrefed(ptr)) } } } } impl ToCss for SpecifiedUrl { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { dest.write_str("url(")?; self.serialization.to_css(dest)?; dest.write_str(")") } } impl MallocSizeOf for SpecifiedUrl { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { use gecko_bindings::bindings::Gecko_ImageValue_SizeOfIncludingThis; let mut n = 0; // XXX: measure `serialization` once bug 1397971 lands // We ignore `extra_data`, because RefPtr is tricky, and there aren't // many of them in practise (sharing is common). if let Some(ref image_value) = self.image_value { // Although this is a RefPtr, this is the primary reference because // SpecifiedUrl is responsible for creating the image_value. So we // measure unconditionally here. n += unsafe { Gecko_ImageValue_SizeOfIncludingThis(image_value.clone().get()) }; } n } }