mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
style: Preserve CSS input exactly during sanitization.
This avoids the mutation due to the different serialization in some cases. Differential Revision: https://phabricator.services.mozilla.com/D56732
This commit is contained in:
parent
e8a3a71080
commit
02c30bccbd
2 changed files with 87 additions and 4 deletions
|
@ -60,6 +60,7 @@ pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
|
||||||
pub use self::style_rule::StyleRule;
|
pub use self::style_rule::StyleRule;
|
||||||
pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
|
pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
|
||||||
pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
|
pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
|
||||||
|
pub use self::stylesheet::{SanitizationData, SanitizationKind};
|
||||||
pub use self::supports_rule::SupportsRule;
|
pub use self::supports_rule::SupportsRule;
|
||||||
pub use self::viewport_rule::ViewportRule;
|
pub use self::viewport_rule::ViewportRule;
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ impl StylesheetContents {
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
line_number_offset: u32,
|
line_number_offset: u32,
|
||||||
use_counters: Option<&UseCounters>,
|
use_counters: Option<&UseCounters>,
|
||||||
|
sanitization_data: Option<&mut SanitizationData>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let namespaces = RwLock::new(Namespaces::default());
|
let namespaces = RwLock::new(Namespaces::default());
|
||||||
let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
|
let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
|
||||||
|
@ -94,6 +95,7 @@ impl StylesheetContents {
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
line_number_offset,
|
line_number_offset,
|
||||||
use_counters,
|
use_counters,
|
||||||
|
sanitization_data,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -341,6 +343,71 @@ impl StylesheetInDocument for DocumentStyleSheet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of sanitization to use when parsing a stylesheet.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum SanitizationKind {
|
||||||
|
/// Perform no sanitization.
|
||||||
|
None,
|
||||||
|
/// Allow only @font-face, style rules, and @namespace.
|
||||||
|
Standard,
|
||||||
|
/// Allow everything but conditional rules.
|
||||||
|
NoConditionalRules,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SanitizationKind {
|
||||||
|
fn allows(self, rule: &CssRule) -> bool {
|
||||||
|
debug_assert_ne!(self, SanitizationKind::None);
|
||||||
|
// NOTE(emilio): If this becomes more complex (not filtering just by
|
||||||
|
// top-level rules), we should thread all the data through nested rules
|
||||||
|
// and such. But this doesn't seem necessary at the moment.
|
||||||
|
let is_standard = matches!(self, SanitizationKind::Standard);
|
||||||
|
match *rule {
|
||||||
|
CssRule::Document(..) |
|
||||||
|
CssRule::Media(..) |
|
||||||
|
CssRule::Supports(..) |
|
||||||
|
CssRule::Import(..) => false,
|
||||||
|
|
||||||
|
CssRule::FontFace(..) |
|
||||||
|
CssRule::Namespace(..) |
|
||||||
|
CssRule::Style(..) => true,
|
||||||
|
|
||||||
|
CssRule::Keyframes(..) |
|
||||||
|
CssRule::Page(..) |
|
||||||
|
CssRule::FontFeatureValues(..) |
|
||||||
|
CssRule::Viewport(..) |
|
||||||
|
CssRule::CounterStyle(..) => !is_standard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A struct to hold the data relevant to style sheet sanitization.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SanitizationData {
|
||||||
|
kind: SanitizationKind,
|
||||||
|
output: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SanitizationData {
|
||||||
|
/// Create a new input for sanitization.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(kind: SanitizationKind) -> Option<Self> {
|
||||||
|
if matches!(kind, SanitizationKind::None) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self {
|
||||||
|
kind,
|
||||||
|
output: String::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take the sanitized output.
|
||||||
|
#[inline]
|
||||||
|
pub fn take(self) -> String {
|
||||||
|
self.output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Stylesheet {
|
impl Stylesheet {
|
||||||
/// Updates an empty stylesheet from a given string of text.
|
/// Updates an empty stylesheet from a given string of text.
|
||||||
pub fn update_from_str(
|
pub fn update_from_str(
|
||||||
|
@ -365,6 +432,7 @@ impl Stylesheet {
|
||||||
existing.contents.quirks_mode,
|
existing.contents.quirks_mode,
|
||||||
line_number_offset,
|
line_number_offset,
|
||||||
/* use_counters = */ None,
|
/* use_counters = */ None,
|
||||||
|
/* sanitization_data = */ None,
|
||||||
);
|
);
|
||||||
|
|
||||||
*existing.contents.url_data.write() = url_data;
|
*existing.contents.url_data.write() = url_data;
|
||||||
|
@ -391,6 +459,7 @@ impl Stylesheet {
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
line_number_offset: u32,
|
line_number_offset: u32,
|
||||||
use_counters: Option<&UseCounters>,
|
use_counters: Option<&UseCounters>,
|
||||||
|
mut sanitization_data: Option<&mut SanitizationData>,
|
||||||
) -> (Vec<CssRule>, Option<String>, Option<String>) {
|
) -> (Vec<CssRule>, Option<String>, Option<String>) {
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
|
let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
|
||||||
|
@ -419,12 +488,24 @@ impl Stylesheet {
|
||||||
{
|
{
|
||||||
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
|
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
|
||||||
|
|
||||||
while let Some(result) = iter.next() {
|
loop {
|
||||||
|
let rule_start = iter.input.position().byte_index();
|
||||||
|
let result = match iter.next() {
|
||||||
|
Some(result) => result,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
match result {
|
match result {
|
||||||
Ok(rule) => {
|
Ok(rule) => {
|
||||||
// Use a fallible push here, and if it fails, just
|
if let Some(ref mut data) = sanitization_data {
|
||||||
// fall out of the loop. This will cause the page to
|
if !data.kind.allows(&rule) {
|
||||||
// be shown incorrectly, but it's better than OOMing.
|
continue;
|
||||||
|
}
|
||||||
|
let end = iter.input.position().byte_index();
|
||||||
|
data.output.push_str(&css[rule_start..end]);
|
||||||
|
}
|
||||||
|
// Use a fallible push here, and if it fails, just fall
|
||||||
|
// out of the loop. This will cause the page to be
|
||||||
|
// shown incorrectly, but it's better than OOMing.
|
||||||
if rules.try_push(rule).is_err() {
|
if rules.try_push(rule).is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -470,6 +551,7 @@ impl Stylesheet {
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
line_number_offset,
|
line_number_offset,
|
||||||
/* use_counters = */ None,
|
/* use_counters = */ None,
|
||||||
|
/* sanitized_output = */ None,
|
||||||
);
|
);
|
||||||
|
|
||||||
Stylesheet {
|
Stylesheet {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue