use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs;
use crate::parser::{Parse, ParserContext};
use crate::stylesheets::{CorsMode, UrlExtraData};
use crate::values::computed::{Context, ToComputedValue};
use cssparser::Parser;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use servo_arc::Arc;
use std::collections::HashMap;
use std::fmt::{self, Write};
use std::mem::ManuallyDrop;
use std::sync::RwLock;
use style_traits::{CssWriter, ParseError, ToCss};
use to_shmem::{SharedMemoryBuilder, ToShmem};
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[css(function = "url")]
#[repr(C)]
pub struct CssUrl(pub Arc<CssUrlData>);
#[derive(Debug, SpecifiedValueInfo, ToCss, ToShmem)]
#[repr(C)]
pub struct CssUrlData {
serialization: crate::OwnedStr,
#[css(skip)]
pub extra_data: UrlExtraData,
#[css(skip)]
cors_mode: CorsMode,
#[css(skip)]
load_data: LoadDataSource,
}
impl PartialEq for CssUrlData {
fn eq(&self, other: &Self) -> bool {
self.serialization == other.serialization
&& self.extra_data == other.extra_data
&& self.cors_mode == other.cors_mode
}
}
#[repr(u8)]
#[derive(PartialOrd, PartialEq)]
pub enum NonLocalUriDependency {
No = 0,
Absolute,
Path,
Full,
}
impl NonLocalUriDependency {
fn scan(specified: &str) -> Self {
if specified.is_empty() || specified.starts_with('#') || specified.starts_with("data:") {
return Self::No;
}
if specified.starts_with('/')
|| specified.starts_with("http:")
|| specified.starts_with("https:")
{
return Self::Absolute;
}
if specified.starts_with('?') {
return Self::Full;
}
Self::Path
}
}
impl CssUrl {
pub fn parse_with_cors_mode<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
) -> Result<Self, ParseError<'i>> {
let url = input.expect_url()?;
Ok(Self::parse_from_string(
url.as_ref().to_owned(),
context,
cors_mode,
))
}
pub fn parse_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self {
use crate::use_counters::CustomUseCounter;
if let Some(counters) = context.use_counters {
if !counters
.custom
.recorded(CustomUseCounter::MaybeHasFullBaseUriDependency)
{
let dep = NonLocalUriDependency::scan(&url);
if dep >= NonLocalUriDependency::Absolute {
counters
.custom
.record(CustomUseCounter::HasNonLocalUriDependency);
}
if dep >= NonLocalUriDependency::Path {
counters
.custom
.record(CustomUseCounter::MaybeHasPathBaseUriDependency);
}
if dep >= NonLocalUriDependency::Full {
counters
.custom
.record(CustomUseCounter::MaybeHasFullBaseUriDependency);
}
}
}
CssUrl(Arc::new(CssUrlData {
serialization: url.into(),
extra_data: context.url_data.clone(),
cors_mode,
load_data: LoadDataSource::Owned(LoadData::default()),
}))
}
pub fn is_invalid(&self) -> bool {
false
}
#[inline]
pub fn is_fragment(&self) -> bool {
self.0.is_fragment()
}
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl CssUrlData {
pub fn is_fragment(&self) -> bool {
self.as_str()
.as_bytes()
.iter()
.next()
.map_or(false, |b| *b == b'#')
}
pub fn as_str(&self) -> &str {
&*self.serialization
}
}
impl Parse for CssUrl {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Self::parse_with_cors_mode(context, input, CorsMode::None)
}
}
impl Eq for CssUrl {}
impl MallocSizeOf for CssUrl {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}
#[derive(Eq, Hash, PartialEq)]
struct LoadDataKey(*const LoadDataSource);
unsafe impl Sync for LoadDataKey {}
unsafe impl Send for LoadDataKey {}
bitflags! {
#[derive(Debug)]
#[repr(C)]
pub struct LoadDataFlags: u8 {
const TRIED_TO_RESOLVE_URI = 1 << 0;
const TRIED_TO_RESOLVE_IMAGE = 1 << 1;
}
}
unsafe impl Sync for LoadData {}
unsafe impl Send for LoadData {}
#[repr(C)]
#[derive(Debug)]
pub struct LoadData {
resolved_image: *mut structs::imgRequestProxy,
resolved_uri: *mut structs::nsIURI,
flags: LoadDataFlags,
}
impl Drop for LoadData {
fn drop(&mut self) {
unsafe { bindings::Gecko_LoadData_Drop(self) }
}
}
impl Default for LoadData {
fn default() -> Self {
Self {
resolved_image: std::ptr::null_mut(),
resolved_uri: std::ptr::null_mut(),
flags: LoadDataFlags::empty(),
}
}
}
#[derive(Debug)]
#[repr(u8, C)]
pub enum LoadDataSource {
Owned(LoadData),
Lazy,
}
impl LoadDataSource {
#[inline]
pub unsafe fn get(&self) -> *const LoadData {
match *self {
LoadDataSource::Owned(ref d) => return d,
LoadDataSource::Lazy => {},
}
let key = LoadDataKey(self);
{
let guard = LOAD_DATA_TABLE.read().unwrap();
if let Some(r) = guard.get(&key) {
return &**r;
}
}
let mut guard = LOAD_DATA_TABLE.write().unwrap();
let r = guard.entry(key).or_insert_with(Default::default);
&**r
}
}
impl ToShmem for LoadDataSource {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
Ok(ManuallyDrop::new(match self {
LoadDataSource::Owned(..) => LoadDataSource::Lazy,
LoadDataSource::Lazy => LoadDataSource::Lazy,
}))
}
}
pub type SpecifiedUrl = CssUrl;
pub fn shutdown() {
LOAD_DATA_TABLE.write().unwrap().clear();
}
impl ToComputedValue for SpecifiedUrl {
type ComputedValue = ComputedUrl;
#[inline]
fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
ComputedUrl(self.clone())
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
computed.0.clone()
}
}
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
#[repr(C)]
pub struct ComputedUrl(pub SpecifiedUrl);
impl ComputedUrl {
fn serialize_with<W>(
&self,
function: unsafe extern "C" fn(*const Self, *mut nsCString),
dest: &mut CssWriter<W>,
) -> fmt::Result
where
W: Write,
{
dest.write_str("url(")?;
unsafe {
let mut string = nsCString::new();
function(self, &mut string);
string.as_str_unchecked().to_css(dest)?;
}
dest.write_char(')')
}
}
impl ToCss for ComputedUrl {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.serialize_with(bindings::Gecko_GetComputedURLSpec, dest)
}
}
lazy_static! {
static ref LOAD_DATA_TABLE: RwLock<HashMap<LoadDataKey, Box<LoadData>>> = {
Default::default()
};
}