author | Ting-Yu Lin <tlin@mozilla.com> |
Mon, 31 Jul 2017 16:03:51 +0800 | |
changeset 618436 | 8322dc3ac8c50593d4a94959b98e555feda52bfb |
parent 618435 | bb6a9dde93e7e5f4cefb229b9094b2cbc9a128b2 |
child 618437 | 838437bd0e4df5b9525fcda760f5567c100e9f72 |
push id | 71327 |
push user | bmo:tlin@mozilla.com |
push date | Mon, 31 Jul 2017 13:18:18 +0000 |
milestone | 56.0a1 |
--- a/servo/components/script/dom/cssstylerule.rs +++ b/servo/components/script/dom/cssstylerule.rs @@ -88,16 +88,17 @@ impl CSSStyleRuleMethods for CSSStyleRul // https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext fn SetSelectorText(&self, value: DOMString) { // It's not clear from the spec if we should use the stylesheet's namespaces. // https://github.com/w3c/csswg-drafts/issues/1511 let namespaces = self.cssrule.parent_stylesheet().style_stylesheet().contents.namespaces.read(); let parser = SelectorParser { stylesheet_origin: Origin::Author, namespaces: &namespaces, + url_data: None, }; let mut css_parser = CssParserInput::new(&*value); let mut css_parser = CssParser::new(&mut css_parser); if let Ok(mut s) = SelectorList::parse(&parser, &mut css_parser) { // This mirrors what we do in CSSStyleOwner::mutate_associated_block. let mut guard = self.cssrule.shared_lock().write(); let mut stylerule = self.stylerule.write_with(&mut guard); mem::swap(&mut stylerule.selectors, &mut s);
--- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs +++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs @@ -40,55 +40,55 @@ */ macro_rules! apply_non_ts_list { ($apply_macro:ident) => { $apply_macro! { bare: [ ("unresolved", Unresolved, unresolved, IN_UNRESOLVED_STATE, _), ("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("link", Link, link, IN_UNVISITED_STATE, _), ("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _), ("visited", Visited, visited, IN_VISITED_STATE, _), ("active", Active, active, IN_ACTIVE_STATE, _), ("checked", Checked, checked, IN_CHECKED_STATE, _), ("disabled", Disabled, disabled, IN_DISABLED_STATE, _), ("enabled", Enabled, enabled, IN_ENABLED_STATE, _), ("focus", Focus, focus, IN_FOCUS_STATE, _), ("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _), ("hover", Hover, hover, IN_HOVER_STATE, _), ("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _), ("target", Target, target, IN_TARGET_STATE, _), ("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _), ("-moz-devtools-highlighted", MozDevtoolsHighlighted, mozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, _), ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, mozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, _), - // TODO(emilio): Needs pref check for - // full-screen-api.unprefix.enabled! + // TODO(emilio): Needs pref check for full-screen-api.unprefix.enabled! + // TODO(TYLin): Needs to use CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME? ("fullscreen", Fullscreen, fullscreen, IN_FULLSCREEN_STATE, _), ("-moz-full-screen", MozFullScreen, mozFullScreen, IN_FULLSCREEN_STATE, _), // TODO(emilio): This is inconsistently named (the capital R). ("-moz-focusring", MozFocusRing, mozFocusRing, IN_FOCUSRING_STATE, _), ("-moz-broken", MozBroken, mozBroken, IN_BROKEN_STATE, _), ("-moz-loading", MozLoading, mozLoading, IN_LOADING_STATE, _), - ("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("-moz-has-dir-attr", MozHasDirAttr, mozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-dir-attr-ltr", MozDirAttrLTR, mozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-dir-attr-rtl", MozDirAttrRTL, mozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, mozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-autofill", MozAutofill, mozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-autofill-preview", MozAutofillPreview, mozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-autofill", MozAutofill, mozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-autofill-preview", MozAutofillPreview, mozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-clicktoplay", MozHandlerClickToPlay, mozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, mozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, mozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-handler-clicktoplay", MozHandlerClickToPlay, mozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, mozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, mozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-disabled", MozHandlerDisabled, mozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-handler-blocked", MozHandlerBlocked, mozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-handler-crashed", MozHandlerCrashed, mozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-handler-disabled", MozHandlerDisabled, mozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-blocked", MozHandlerBlocked, mozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-crashed", MozHandlerCrashed, mozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, mozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _), ("required", Required, required, IN_REQUIRED_STATE, _), ("optional", Optional, optional, IN_OPTIONAL_STATE, _), ("valid", Valid, valid, IN_VALID_STATE, _), ("invalid", Invalid, invalid, IN_INVALID_STATE, _), ("in-range", InRange, inRange, IN_INRANGE_STATE, _), ("out-of-range", OutOfRange, outOfRange, IN_OUTOFRANGE_STATE, _), @@ -98,17 +98,17 @@ macro_rules! apply_non_ts_list { ("-moz-read-write", MozReadWrite, mozReadWrite, IN_MOZ_READWRITE_STATE, _), ("-moz-submit-invalid", MozSubmitInvalid, mozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _), ("-moz-ui-valid", MozUIValid, mozUIValid, IN_MOZ_UI_VALID_STATE, _), ("-moz-ui-invalid", MozUIInvalid, mozUIInvalid, IN_MOZ_UI_INVALID_STATE, _), ("-moz-meter-optimum", MozMeterOptimum, mozMeterOptimum, IN_OPTIMUM_STATE, _), ("-moz-meter-sub-optimum", MozMeterSubOptimum, mozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _), ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, mozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _), - ("-moz-user-disabled", MozUserDisabled, mozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-user-disabled", MozUserDisabled, mozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("-moz-first-node", MozFirstNode, firstNode, _, _), ("-moz-last-node", MozLastNode, lastNode, _, _), ("-moz-only-whitespace", MozOnlyWhitespace, mozOnlyWhitespace, _, _), ("-moz-native-anonymous", MozNativeAnonymous, mozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, mozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-is-html", MozIsHTML, mozIsHTML, _, _), ("-moz-placeholder", MozPlaceholder, mozPlaceholder, _, _),
--- a/servo/components/style/gecko/selector_parser.rs +++ b/servo/components/style/gecko/selector_parser.rs @@ -13,19 +13,22 @@ use selectors::visitor::SelectorVisitor; use std::fmt; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use style_traits::{ParseError, StyleParseError}; pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT}; pub use gecko::snapshot::SnapshotMap; bitflags! { + // See NonTSPseudoClass::is_enabled_in() flags NonTSPseudoClassFlag: u8 { - // See NonTSPseudoClass::is_internal() const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS = 1 << 0, + const PSEUDO_CLASS_ENABLED_IN_CHROME = 1 << 1, + const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME = + PSEUDO_CLASS_ENABLED_IN_UA_SHEETS.bits | PSEUDO_CLASS_ENABLED_IN_CHROME.bits, } } /// The type used for storing pseudo-class string arguments. pub type PseudoClassStringArg = Box<[u16]>; macro_rules! pseudo_class_name { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], @@ -117,36 +120,36 @@ impl SelectorMethods for NonTSPseudoClas } true } } impl NonTSPseudoClass { - /// A pseudo-class is internal if it can only be used inside - /// user agent style sheets. - pub fn is_internal(&self) -> bool { + /// Returns true if this pseudo-class is enabled under the context of + /// the given flag. + fn is_enabled_in(&self, flag: NonTSPseudoClassFlag) -> bool { macro_rules! check_flag { (_) => (false); - ($flags:expr) => ($flags.contains(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS)); + ($flags:expr) => ($flags.contains(flag)); } - macro_rules! pseudo_class_check_internal { + macro_rules! pseudo_class_check_is_enabled_in { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => check_flag!($flags),)* $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)* $(NonTSPseudoClass::$k_name(..) => check_flag!($k_flags),)* NonTSPseudoClass::MozAny(_) => false, } } } - apply_non_ts_list!(pseudo_class_check_internal) + apply_non_ts_list!(pseudo_class_check_is_enabled_in) } /// https://drafts.csswg.org/selectors-4/#useraction-pseudos /// /// We intentionally skip the link-related ones. pub fn is_safe_user_action_state(&self) -> bool { matches!(*self, NonTSPseudoClass::Hover | NonTSPseudoClass::Active | @@ -258,16 +261,31 @@ impl ::selectors::SelectorImpl for Selec #[inline] fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool { matches!(*pseudo_class, NonTSPseudoClass::Active | NonTSPseudoClass::Hover) } } +impl<'a> SelectorParser<'a> { + fn is_pseudo_class_enabled(&self, + pseudo_class: &NonTSPseudoClass) + -> bool { + let enabled_in_ua = pseudo_class.is_enabled_in(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS); + let enabled_in_chrome = pseudo_class.is_enabled_in(PSEUDO_CLASS_ENABLED_IN_CHROME); + if !enabled_in_ua && !enabled_in_chrome { + true + } else { + (enabled_in_ua && self.in_user_agent_stylesheet()) || + (enabled_in_chrome && self.in_chrome_stylesheet()) + } + } +} + impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { type Impl = SelectorImpl; type Error = StyleParseError<'i>; fn is_pseudo_element_allows_single_colon(name: &CowRcStr<'i>) -> bool { ::selectors::parser::is_css2_pseudo_element(name) || name.starts_with("-moz-tree-") // tree pseudo-elements } @@ -281,17 +299,17 @@ impl<'a, 'i> ::selectors::Parser<'i> for match_ignore_ascii_case! { &name, $($css => NonTSPseudoClass::$name,)* _ => return Err(::selectors::parser::SelectorParseError::UnexpectedIdent( name.clone()).into()) } } } let pseudo_class = apply_non_ts_list!(pseudo_class_parse); - if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() { + if self.is_pseudo_class_enabled(&pseudo_class) { Ok(pseudo_class) } else { Err(SelectorParseError::UnexpectedIdent(name).into()) } } fn parse_non_ts_functional_pseudo_class<'t>(&self, name: CowRcStr<'i>, @@ -326,17 +344,17 @@ impl<'a, 'i> ::selectors::Parser<'i> for } NonTSPseudoClass::MozAny(selectors.into_boxed_slice()) } _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into()) } } } let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse); - if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() { + if self.is_pseudo_class_enabled(&pseudo_class) { Ok(pseudo_class) } else { Err(SelectorParseError::UnexpectedIdent(name).into()) } } fn parse_pseudo_element(&self, name: CowRcStr<'i>) -> Result<PseudoElement, ParseError<'i>> { PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
--- a/servo/components/style/selector_parser.rs +++ b/servo/components/style/selector_parser.rs @@ -6,17 +6,17 @@ #![deny(missing_docs)] use cssparser::{Parser as CssParser, ParserInput}; use selectors::Element; use selectors::parser::SelectorList; use std::fmt::Debug; use style_traits::ParseError; -use stylesheets::{Origin, Namespaces}; +use stylesheets::{Origin, Namespaces, UrlExtraData}; /// A convenient alias for the type that represents an attribute value used for /// selector parser implementation. pub type AttrValue = <SelectorImpl as ::selectors::SelectorImpl>::AttrValue; #[cfg(feature = "servo")] pub use servo::selector_parser::*; @@ -47,38 +47,51 @@ pub type PreExistingComputedValues = ::g /// Servo's selector parser. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct SelectorParser<'a> { /// The origin of the stylesheet we're parsing. pub stylesheet_origin: Origin, /// The namespace set of the stylesheet. pub namespaces: &'a Namespaces, + /// The extra URL data of the stylesheet. + pub url_data: Option<&'a UrlExtraData>, } impl<'a> SelectorParser<'a> { /// Parse a selector list with an author origin and without taking into /// account namespaces. /// /// This is used for some DOM APIs like `querySelector`. pub fn parse_author_origin_no_namespace(input: &str) -> Result<SelectorList<SelectorImpl>, ParseError> { let namespaces = Namespaces::default(); let parser = SelectorParser { stylesheet_origin: Origin::Author, namespaces: &namespaces, + url_data: None, }; let mut input = ParserInput::new(input); SelectorList::parse(&parser, &mut CssParser::new(&mut input)) } /// Whether we're parsing selectors in a user-agent stylesheet. pub fn in_user_agent_stylesheet(&self) -> bool { matches!(self.stylesheet_origin, Origin::UserAgent) } + + /// Whether we're parsing selectors in a stylesheet that has chrome + /// privilege. + pub fn in_chrome_stylesheet(&self) -> bool { + if let Some(url_data) = self.url_data { + url_data.is_chrome() + } else { + false + } + } } /// This enumeration determines if a pseudo-element is eagerly cascaded or not. /// /// If you're implementing a public selector for `Servo` that the end-user might /// customize, then you probably need to make it eager. #[derive(Debug, Clone, PartialEq, Eq)] pub enum PseudoElementCascadeType {
--- a/servo/components/style/stylesheets/mod.rs +++ b/servo/components/style/stylesheets/mod.rs @@ -63,16 +63,21 @@ pub type UrlExtraData = impl UrlExtraData { /// Returns a string for the url. /// /// Unimplemented currently. pub fn as_str(&self) -> &str { // TODO "(stylo: not supported)" } + + /// True if this URL scheme is chrome. + pub fn is_chrome(&self) -> bool { + self.mIsChrome + } } // XXX We probably need to figure out whether we should mark Eq here. // It is currently marked so because properties::UnparsedValue wants Eq. #[cfg(feature = "gecko")] impl Eq for UrlExtraData {} /// Each style rule has an origin, which determines where it enters the cascade.
--- a/servo/components/style/stylesheets/rule_parser.rs +++ b/servo/components/style/stylesheets/rule_parser.rs @@ -502,16 +502,17 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> type QualifiedRule = CssRule; type Error = SelectorParseError<'i, StyleParseError<'i>>; fn parse_prelude<'t>(&mut self, input: &mut Parser<'i, 't>) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> { let selector_parser = SelectorParser { stylesheet_origin: self.stylesheet_origin, namespaces: self.context.namespaces.unwrap(), + url_data: Some(self.context.url_data), }; let location = get_location_with_offset(input.current_source_location(), self.context.line_number_offset); let selectors = SelectorList::parse(&selector_parser, input)?; Ok(QualifiedRuleParserPrelude { selectors: selectors,
--- a/servo/components/url/lib.rs +++ b/servo/components/url/lib.rs @@ -80,16 +80,20 @@ impl ServoUrl { self.0.scheme() } pub fn is_secure_scheme(&self) -> bool { let scheme = self.scheme(); scheme == "https" || scheme == "wss" } + pub fn is_chrome(&self) -> bool { + self.scheme() == "chrome" + } + pub fn as_str(&self) -> &str { self.0.as_str() } pub fn as_mut_url(&mut self) -> &mut Url { Arc::make_mut(&mut self.0) }
--- a/servo/tests/unit/style/parsing/selectors.rs +++ b/servo/tests/unit/style/parsing/selectors.rs @@ -9,16 +9,17 @@ use style::stylesheets::{Origin, Namespa use style_traits::ParseError; fn parse_selector<'i, 't>(input: &mut Parser<'i, 't>) -> Result<SelectorList<SelectorImpl>, ParseError<'i>> { let mut ns = Namespaces::default(); ns.prefixes.insert("svg".into(), (ns!(svg), ())); let parser = SelectorParser { stylesheet_origin: Origin::UserAgent, namespaces: &ns, + url_data: None, }; SelectorList::parse(&parser, input) } #[test] fn test_selectors() { assert_roundtrip!(parse_selector, "div"); assert_roundtrip!(parse_selector, "svg|circle");