style: Allow C++ URLValue objects to be lazily created from Rust SpecifiedUrls.

This avoids having to support storing refcounted URLValue objects in shared memory,
which would be tricky.

Depends on D17183

Differential Revision: https://phabricator.services.mozilla.com/D17184
This commit is contained in:
Cameron McCormack 2019-03-30 00:15:53 +00:00 committed by Emilio Cobos Álvarez
parent b6b5ddda71
commit 7e7a9e2ec5
3 changed files with 126 additions and 26 deletions

View file

@ -123,14 +123,11 @@ impl nsStyleImage {
match image { match image {
GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient), GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient),
GenericImage::Url(ref url) => unsafe { GenericImage::Url(ref url) => unsafe {
bindings::Gecko_SetLayerImageImageValue(self, (url.0).0.url_value.get()); bindings::Gecko_SetLayerImageImageValue(self, url.url_value_ptr())
}, },
GenericImage::Rect(ref image_rect) => { GenericImage::Rect(ref image_rect) => {
unsafe { unsafe {
bindings::Gecko_SetLayerImageImageValue( bindings::Gecko_SetLayerImageImageValue(self, image_rect.url.url_value_ptr());
self,
(image_rect.url.0).0.url_value.get(),
);
bindings::Gecko_InitializeImageCropRect(self); bindings::Gecko_InitializeImageCropRect(self);
// Set CropRect // Set CropRect

View file

@ -17,7 +17,9 @@ use cssparser::Parser;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString; use nsstring::nsCString;
use servo_arc::Arc; use servo_arc::Arc;
use std::collections::HashMap;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::sync::RwLock;
use style_traits::{CssWriter, ParseError, ToCss}; use style_traits::{CssWriter, ParseError, ToCss};
/// A CSS url() value for gecko. /// A CSS url() value for gecko.
@ -81,6 +83,20 @@ impl CssUrlData {
} }
} }
#[cfg(debug_assertions)]
impl Drop for CssUrlData {
fn drop(&mut self) {
assert!(
!URL_VALUE_TABLE
.read()
.unwrap()
.contains_key(&CssUrlDataKey(self as *mut _ as *const _)),
"All CssUrlData objects used as keys in URL_VALUE_TABLE should be \
from shared memory style sheets, and so should never be dropped",
);
}
}
impl Parse for CssUrl { impl Parse for CssUrl {
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
@ -104,6 +120,24 @@ impl MallocSizeOf for CssUrl {
} }
} }
/// A key type for URL_VALUE_TABLE.
#[derive(Hash, PartialEq, Eq)]
struct CssUrlDataKey(*const CssUrlData);
unsafe impl Sync for CssUrlDataKey {}
unsafe impl Send for CssUrlDataKey {}
/// The source of a Gecko URLValue object for a SpecifiedUrl.
#[derive(Clone, Debug)]
pub enum URLValueSource {
/// A strong reference to a Gecko URLValue object.
URLValue(RefPtr<URLValue>),
/// A CORSMode value used to lazily construct a Gecko URLValue object.
///
/// The lazily created object will be stored in URL_VALUE_TABLE.
CORSMode(CORSMode),
}
/// A specified non-image `url()` value. /// A specified non-image `url()` value.
#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)] #[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl { pub struct SpecifiedUrl {
@ -111,8 +145,20 @@ pub struct SpecifiedUrl {
pub url: CssUrl, pub url: CssUrl,
/// Gecko's URLValue so that we can reuse it while rematching a /// Gecko's URLValue so that we can reuse it while rematching a
/// property with this specified value. /// property with this specified value.
///
/// Box this to avoid SpecifiedUrl getting bigger than two words,
/// and increasing the size of PropertyDeclaration.
#[css(skip)] #[css(skip)]
pub url_value: RefPtr<URLValue>, url_value: Box<URLValueSource>,
}
fn make_url_value(url: &CssUrl, cors_mode: CORSMode) -> RefPtr<URLValue> {
unsafe {
let ptr = bindings::Gecko_URLValue_Create(url.0.clone().into_strong(), cors_mode);
// We do not expect Gecko_URLValue_Create returns null.
debug_assert!(!ptr.is_null());
RefPtr::from_addrefed(ptr)
}
} }
impl SpecifiedUrl { impl SpecifiedUrl {
@ -122,12 +168,7 @@ impl SpecifiedUrl {
} }
fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self { fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
let url_value = unsafe { let url_value = Box::new(URLValueSource::URLValue(make_url_value(&url, cors)));
let ptr = bindings::Gecko_URLValue_Create(url.0.clone().into_strong(), cors);
// We do not expect Gecko_URLValue_Create returns null.
debug_assert!(!ptr.is_null());
RefPtr::from_addrefed(ptr)
};
Self { url, url_value } Self { url, url_value }
} }
@ -140,6 +181,45 @@ impl SpecifiedUrl {
use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS; use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS) Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
} }
fn with_url_value<F, T>(&self, f: F) -> T
where
F: FnOnce(&RefPtr<URLValue>) -> T,
{
match *self.url_value {
URLValueSource::URLValue(ref r) => f(r),
URLValueSource::CORSMode(cors_mode) => {
{
let guard = URL_VALUE_TABLE.read().unwrap();
if let Some(r) = guard.get(&(CssUrlDataKey(&*self.url.0 as *const _))) {
return f(r);
}
}
let mut guard = URL_VALUE_TABLE.write().unwrap();
let r = guard
.entry(CssUrlDataKey(&*self.url.0 as *const _))
.or_insert_with(|| make_url_value(&self.url, cors_mode));
f(r)
},
}
}
/// Clone a new, strong reference to the Gecko URLValue.
pub fn clone_url_value(&self) -> RefPtr<URLValue> {
self.with_url_value(RefPtr::clone)
}
/// Get a raw pointer to the URLValue held by this SpecifiedUrl, for FFI.
pub fn url_value_ptr(&self) -> *mut URLValue {
self.with_url_value(RefPtr::get)
}
}
/// Clears URL_VALUE_TABLE. Entries in this table, which are for specified URL
/// values that come from shared memory style sheets, would otherwise persist
/// until the end of the process and be reported as leaks.
pub fn shutdown() {
URL_VALUE_TABLE.write().unwrap().clear();
} }
impl Parse for SpecifiedUrl { impl Parse for SpecifiedUrl {
@ -165,7 +245,7 @@ impl MallocSizeOf for SpecifiedUrl {
// Although this is a RefPtr, this is the primary reference because // Although this is a RefPtr, this is the primary reference because
// SpecifiedUrl is responsible for creating the url_value. So we // SpecifiedUrl is responsible for creating the url_value. So we
// measure unconditionally here. // measure unconditionally here.
n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) }; n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value_ptr()) };
n n
} }
} }
@ -258,7 +338,8 @@ impl ToCss for ComputedUrl {
where where
W: Write, W: Write,
{ {
serialize_computed_url(&self.0.url_value, dest, bindings::Gecko_GetComputedURLSpec) self.0
.with_url_value(|r| serialize_computed_url(r, dest, bindings::Gecko_GetComputedURLSpec))
} }
} }
@ -267,12 +348,20 @@ impl ComputedUrl {
pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self { pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
let css_url = &*url_value.mCssUrl.mRawPtr; let css_url = &*url_value.mCssUrl.mRawPtr;
let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc()); let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
ComputedUrl(SpecifiedUrl { url, url_value }) ComputedUrl(SpecifiedUrl {
url,
url_value: Box::new(URLValueSource::URLValue(url_value)),
})
}
/// Clone a new, strong reference to the Gecko URLValue.
pub fn clone_url_value(&self) -> RefPtr<URLValue> {
self.0.clone_url_value()
} }
/// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI. /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
pub fn url_value_ptr(&self) -> *mut URLValue { pub fn url_value_ptr(&self) -> *mut URLValue {
self.0.url_value.get() self.0.url_value_ptr()
} }
} }
@ -285,11 +374,9 @@ impl ToCss for ComputedImageUrl {
where where
W: Write, W: Write,
{ {
serialize_computed_url( (self.0).0.with_url_value(|r| {
&(self.0).0.url_value, serialize_computed_url(r, dest, bindings::Gecko_GetComputedImageURLSpec)
dest, })
bindings::Gecko_GetComputedImageURLSpec,
)
} }
} }
@ -299,11 +386,27 @@ impl ComputedImageUrl {
let url_value = image_request.mImageValue.to_safe(); let url_value = image_request.mImageValue.to_safe();
let css_url = &*url_value.mCssUrl.mRawPtr; let css_url = &*url_value.mCssUrl.mRawPtr;
let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc()); let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl { url, url_value })) ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl {
url,
url_value: Box::new(URLValueSource::URLValue(url_value)),
}))
}
/// Clone a new, strong reference to the Gecko URLValue.
pub fn clone_url_value(&self) -> RefPtr<URLValue> {
(self.0).0.clone_url_value()
} }
/// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI. /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
pub fn url_value_ptr(&self) -> *mut URLValue { pub fn url_value_ptr(&self) -> *mut URLValue {
(self.0).0.url_value.get() (self.0).0.url_value_ptr()
} }
} }
lazy_static! {
/// A table mapping CssUrlData objects to their lazily created Gecko
/// URLValue objects.
static ref URL_VALUE_TABLE: RwLock<HashMap<CssUrlDataKey, RefPtr<URLValue>>> = {
Default::default()
};
}

View file

@ -826,7 +826,7 @@ def set_gecko_property(ffi_name, expr):
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
match v { match v {
UrlOrNone::Url(ref url) => { UrlOrNone::Url(ref url) => {
self.gecko.${gecko_ffi_name}.set_move(url.0.url_value.clone()) self.gecko.${gecko_ffi_name}.set_move(url.clone_url_value())
} }
UrlOrNone::None => { UrlOrNone::None => {
unsafe { unsafe {
@ -3783,7 +3783,7 @@ fn static_assert() {
}, },
Url(ref url) => { Url(ref url) => {
unsafe { unsafe {
bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.0.url_value.get()); bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.url_value_ptr());
} }
}, },
} }
@ -4164,7 +4164,7 @@ fn set_style_svg_path(
% if ident == "clip_path": % if ident == "clip_path":
ShapeSource::ImageOrUrl(ref url) => { ShapeSource::ImageOrUrl(ref url) => {
unsafe { unsafe {
bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.0.url_value.get()) bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.url_value_ptr())
} }
} }
% elif ident == "shape_outside": % elif ident == "shape_outside":