use crate::media_queries::MediaList;
use crate::parser::{Parse, ParserContext};
use crate::shared_lock::{
DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
};
use crate::str::CssStringWriter;
use crate::stylesheets::{
layer_rule::LayerName, supports_rule::SupportsCondition, CssRule, CssRuleType,
StylesheetInDocument,
};
use crate::values::CssUrl;
use cssparser::{Parser, SourceLocation};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
use to_shmem::{SharedMemoryBuilder, ToShmem};
#[cfg(feature = "gecko")]
#[derive(Debug)]
pub enum ImportSheet {
Sheet(crate::gecko::data::GeckoStyleSheet),
Pending,
Refused,
}
#[cfg(feature = "gecko")]
impl ImportSheet {
pub fn new(sheet: crate::gecko::data::GeckoStyleSheet) -> Self {
ImportSheet::Sheet(sheet)
}
pub fn new_pending() -> Self {
ImportSheet::Pending
}
pub fn new_refused() -> Self {
ImportSheet::Refused
}
pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> {
match *self {
ImportSheet::Sheet(ref s) => {
debug_assert!(!s.hack_is_null());
if s.hack_is_null() {
return None;
}
Some(s)
},
ImportSheet::Refused | ImportSheet::Pending => None,
}
}
pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.as_sheet().and_then(|s| s.media(guard))
}
pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
match self.as_sheet() {
Some(s) => s.rules(guard),
None => &[],
}
}
}
#[cfg(feature = "gecko")]
impl DeepCloneWithLock for ImportSheet {
fn deep_clone_with_lock(
&self,
_lock: &SharedRwLock,
_guard: &SharedRwLockReadGuard,
) -> Self {
use crate::gecko::data::GeckoStyleSheet;
use crate::gecko_bindings::bindings;
match *self {
ImportSheet::Sheet(ref s) => {
let clone = unsafe {
bindings::Gecko_StyleSheet_Clone(s.raw() as *const _)
};
ImportSheet::Sheet(unsafe { GeckoStyleSheet::from_addrefed(clone) })
},
ImportSheet::Pending => ImportSheet::Pending,
ImportSheet::Refused => ImportSheet::Refused,
}
}
}
#[cfg(feature = "servo")]
#[derive(Debug)]
pub enum ImportSheet {
Sheet(::servo_arc::Arc<crate::stylesheets::Stylesheet>),
Refused,
}
#[cfg(feature = "servo")]
impl ImportSheet {
pub fn new(sheet: ::servo_arc::Arc<crate::stylesheets::Stylesheet>) -> Self {
ImportSheet::Sheet(sheet)
}
pub fn new_refused() -> Self {
ImportSheet::Refused
}
pub fn as_sheet(&self) -> Option<&::servo_arc::Arc<crate::stylesheets::Stylesheet>> {
match *self {
ImportSheet::Sheet(ref s) => Some(s),
ImportSheet::Refused => None,
}
}
pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.as_sheet().and_then(|s| s.media(guard))
}
pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
match self.as_sheet() {
Some(s) => s.rules(guard),
None => &[],
}
}
}
#[cfg(feature = "servo")]
impl DeepCloneWithLock for ImportSheet {
fn deep_clone_with_lock(
&self,
_lock: &SharedRwLock,
_guard: &SharedRwLockReadGuard,
) -> Self {
match *self {
ImportSheet::Sheet(ref s) => {
use servo_arc::Arc;
ImportSheet::Sheet(Arc::new((&**s).clone()))
},
ImportSheet::Refused => ImportSheet::Refused,
}
}
}
#[derive(Debug, Clone)]
pub enum ImportLayer {
None,
Anonymous,
Named(LayerName),
}
#[derive(Debug, Clone)]
pub struct ImportSupportsCondition {
pub condition: SupportsCondition,
pub enabled: bool,
}
impl ToCss for ImportLayer {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
ImportLayer::None => Ok(()),
ImportLayer::Anonymous => dest.write_str("layer"),
ImportLayer::Named(ref name) => {
dest.write_str("layer(")?;
name.to_css(dest)?;
dest.write_char(')')
},
}
}
}
#[derive(Debug)]
pub struct ImportRule {
pub url: CssUrl,
pub stylesheet: ImportSheet,
pub supports: Option<ImportSupportsCondition>,
pub layer: ImportLayer,
pub source_location: SourceLocation,
}
impl ImportRule {
pub fn parse_layer_and_supports<'i, 't>(
input: &mut Parser<'i, 't>,
context: &mut ParserContext,
) -> (ImportLayer, Option<ImportSupportsCondition>) {
let layer = if input
.try_parse(|input| input.expect_ident_matching("layer"))
.is_ok()
{
ImportLayer::Anonymous
} else {
input
.try_parse(|input| {
input.expect_function_matching("layer")?;
input
.parse_nested_block(|input| LayerName::parse(context, input))
.map(|name| ImportLayer::Named(name))
})
.ok()
.unwrap_or(ImportLayer::None)
};
let supports = if !static_prefs::pref!("layout.css.import-supports.enabled") {
None
} else {
input
.try_parse(SupportsCondition::parse_for_import)
.map(|condition| {
let enabled = context
.nest_for_rule(CssRuleType::Style, |context| condition.eval(context));
ImportSupportsCondition { condition, enabled }
})
.ok()
};
(layer, supports)
}
}
impl ToShmem for ImportRule {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
Err(String::from(
"ToShmem failed for ImportRule: cannot handle imported style sheets",
))
}
}
impl DeepCloneWithLock for ImportRule {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
) -> Self {
ImportRule {
url: self.url.clone(),
stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard),
supports: self.supports.clone(),
layer: self.layer.clone(),
source_location: self.source_location.clone(),
}
}
}
impl ToCssWithGuard for ImportRule {
fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
dest.write_str("@import ")?;
self.url.to_css(&mut CssWriter::new(dest))?;
if !matches!(self.layer, ImportLayer::None) {
dest.write_char(' ')?;
self.layer.to_css(&mut CssWriter::new(dest))?;
}
if let Some(ref supports) = self.supports {
dest.write_str(" supports(")?;
supports.condition.to_css(&mut CssWriter::new(dest))?;
dest.write_char(')')?;
}
if let Some(media) = self.stylesheet.media(guard) {
if !media.is_empty() {
dest.write_char(' ')?;
media.to_css(&mut CssWriter::new(dest))?;
}
}
dest.write_char(';')
}
}