Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSSOM: Whole ton of things #14241

Merged
merged 13 commits into from Nov 23, 2016
Next

Add insertRule() and deleteRule() on CSSStyleSheet

  • Loading branch information
Manishearth committed Nov 23, 2016
commit 1d20d75cb2e8153768d177e0e9f1f6f054816014
@@ -73,6 +73,12 @@ impl CSSRule {
StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent, s)),
}
}

/// Sets owner sheet/rule to null
pub fn disown(&self) {
self.parent.set(None);
// should set parent rule to None when we add parent rule support
}
}

impl CSSRuleMethods for CSSRule {
@@ -2,14 +2,19 @@
* 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/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CSSRuleListBinding;
use dom::bindings::codegen::Bindings::CSSRuleListBinding::CSSRuleListMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::cssrule::CSSRule;
use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use style::stylesheets::CssRules;
use style::parser::ParserContextExtraData;
use style::stylesheets::{CssRules, Origin};
use style::stylesheets::CssRule as StyleCssRule;

no_jsmanaged_fields!(CssRules);

@@ -19,7 +24,7 @@ pub struct CSSRuleList {
sheet: JS<CSSStyleSheet>,
#[ignore_heap_size_of = "Arc"]
rules: CssRules,
dom_rules: Vec<MutNullableHeap<JS<CSSRule>>>
dom_rules: DOMRefCell<Vec<MutNullableHeap<JS<CSSRule>>>>
}

impl CSSRuleList {
@@ -30,7 +35,7 @@ impl CSSRuleList {
reflector_: Reflector::new(),
sheet: JS::from_ref(sheet),
rules: rules,
dom_rules: dom_rules,
dom_rules: DOMRefCell::new(dom_rules),
}
}

@@ -40,12 +45,83 @@ impl CSSRuleList {
window,
CSSRuleListBinding::Wrap)
}

// https://drafts.csswg.org/cssom/#insert-a-css-rule
pub fn insert_rule(&self, rule: &str, idx: u32) -> Fallible<u32> {
/// Insert an item into a vector, appending if it is out of bounds
fn insert<T>(vec: &mut Vec<T>, index: usize, item: T) {
if index >= vec.len() {
vec.push(item);
} else {
vec.insert(index, item);
}
}
let global = self.global();
let window = global.as_window();
let doc = window.Document();
let index = idx as usize;

// Step 1, 2
// XXXManishearth get url from correct location
// XXXManishearth should we also store the namespace map?

This comment has been minimized.

@Manishearth

Manishearth Nov 16, 2016

Author Member

I'm considering just filing issues for these

let new_rule = try!(StyleCssRule::from_str(&rule, Origin::Author,
doc.url().clone(),
ParserContextExtraData::default())
.map_err(|_| Error::Syntax));

{
let rules = self.rules.0.read();
// Step 3, 4
if index > rules.len() {
return Err(Error::IndexSize);
}

// XXXManishearth Step 5 (throw HierarchyRequestError in invalid situations)

This comment has been minimized.

@Manishearth

Manishearth Nov 16, 2016

Author Member

spec is very vague here. will have to check against gecko (and file an issue)


// Step 6
if let StyleCssRule::Namespace(..) = new_rule {
if !CssRules::only_ns_or_import(&rules) {
return Err(Error::InvalidState);
}
}
}

insert(&mut self.rules.0.write(), index, new_rule.clone());
let dom_rule = CSSRule::new_specific(&window, &self.sheet, new_rule);
insert(&mut self.dom_rules.borrow_mut(),
index, MutNullableHeap::new(Some(&*dom_rule)));
Ok((idx))
}

// https://drafts.csswg.org/cssom/#remove-a-css-rule
pub fn remove_rule(&self, index: u32) -> ErrorResult {
let index = index as usize;

{
let rules = self.rules.0.read();
if index >= rules.len() {
return Err(Error::IndexSize);
}
let ref rule = rules[index];
if let StyleCssRule::Namespace(..) = *rule {
if !CssRules::only_ns_or_import(&rules) {
return Err(Error::InvalidState);
}
}
}

let mut dom_rules = self.dom_rules.borrow_mut();
self.rules.0.write().remove(index);
dom_rules[index].get().map(|r| r.disown());
dom_rules.remove(index);
Ok(())
}
}

impl CSSRuleListMethods for CSSRuleList {
// https://drafts.csswg.org/cssom/#ref-for-dom-cssrulelist-item-1
fn Item(&self, idx: u32) -> Option<Root<CSSRule>> {
self.dom_rules.get(idx as usize).map(|rule| {
self.dom_rules.borrow().get(idx as usize).map(|rule| {
rule.or_init(|| {
CSSRule::new_specific(self.global().as_window(),
&self.sheet,
@@ -56,7 +132,7 @@ impl CSSRuleListMethods for CSSRuleList {

// https://drafts.csswg.org/cssom/#dom-cssrulelist-length
fn Length(&self) -> u32 {
self.dom_rules.len() as u32
self.dom_rules.borrow().len() as u32
}

// check-tidy: no specs after this line
@@ -4,6 +4,7 @@

use dom::bindings::codegen::Bindings::CSSStyleSheetBinding;
use dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::js::{JS, Root, MutNullableHeap};
use dom::bindings::reflector::{reflect_dom_object, Reflectable};
use dom::bindings::str::DOMString;
@@ -40,12 +41,31 @@ impl CSSStyleSheet {
window,
CSSStyleSheetBinding::Wrap)
}

fn rulelist(&self) -> Root<CSSRuleList> {
self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(),
self,
self.style_stylesheet.rules.clone()))
}
}

impl CSSStyleSheetMethods for CSSStyleSheet {
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules
fn CssRules(&self) -> Root<CSSRuleList> {
self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(), self, self.style_stylesheet.rules.clone()))
// XXXManishearth check origin clean flag
self.rulelist()
}

// https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule
fn InsertRule(&self, rule: DOMString, index: u32) -> Fallible<u32> {
// XXXManishearth check origin clean flag
self.rulelist().insert_rule(&rule, index)
}

// https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule
fn DeleteRule(&self, index: u32) -> ErrorResult {
// XXXManishearth check origin clean flag
self.rulelist().remove_rule(index)
}
}

@@ -7,6 +7,6 @@
interface CSSStyleSheet : StyleSheet {
// readonly attribute CSSRule? ownerRule;
[SameObject] readonly attribute CSSRuleList cssRules;
// unsigned long insertRule(DOMString rule, unsigned long index);
// void deleteRule(unsigned long index);
[Throws] unsigned long insertRule(DOMString rule, unsigned long index);
[Throws] void deleteRule(unsigned long index);
};
@@ -6,7 +6,7 @@

use {Atom, Prefix, Namespace};
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes};
use cssparser::{AtRuleType, RuleListParser, Token};
use cssparser::{AtRuleType, RuleListParser, SourcePosition, Token};
use cssparser::ToCss as ParserToCss;
use encoding::EncodingRef;
use error_reporting::ParseErrorReporter;
@@ -60,6 +60,18 @@ impl From<Vec<CssRule>> for CssRules {
}
}

impl CssRules {
// used in CSSOM
pub fn only_ns_or_import(rules: &[CssRule]) -> bool {
rules.iter().all(|r| {
match *r {
CssRule::Namespace(..) /* | CssRule::Import(..) */ => true,
_ => false
}
})
}
}

#[derive(Debug)]
pub struct Stylesheet {
/// List of rules in the order they were found (important for
@@ -92,6 +104,21 @@ pub enum CssRule {
Keyframes(Arc<RwLock<KeyframesRule>>),
}

/// Error reporter which silently forgets errors
struct MemoryHoleReporter;

impl ParseErrorReporter for MemoryHoleReporter {
fn report_error(&self,
_: &mut Parser,
_: SourcePosition,
_: &str) {
// do nothing
}
fn clone(&self) -> Box<ParseErrorReporter + Send + Sync> {
Box::new(MemoryHoleReporter)
}
}

impl CssRule {
/// Call `f` with the slice of rules directly contained inside this rule.
///
@@ -114,6 +141,28 @@ impl CssRule {
}
}
}

pub fn from_str(css: &str, origin: Origin,
base_url: Url, extra_data: ParserContextExtraData) -> Result<Self, ()> {
let error_reporter = Box::new(MemoryHoleReporter);
let mut namespaces = Namespaces::default();
let rule_parser = TopLevelRuleParser {
context: ParserContext::new_with_extra_data(origin, &base_url, error_reporter.clone(),
extra_data),
state: Cell::new(State::Start),
namespaces: &mut namespaces,
};
let mut input = Parser::new(css);
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
if let Some(Ok(rule)) = iter.next() {
if iter.next().is_some() {
return Err(());
}
return Ok(rule);
} else {
return Err(());
}
}
}

impl ToCss for CssRule {
@@ -220,7 +269,6 @@ impl ToCss for StyleRule {
}
}


impl Stylesheet {
pub fn from_bytes(bytes: &[u8],
base_url: ServoUrl,
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.