mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
Auto merge of #21068 - emilio:gecko-sync, r=emilio
style: Sync changes from mozilla-central. See each individual commit for details. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21068) <!-- Reviewable:end -->
This commit is contained in:
commit
615a127b99
21 changed files with 454 additions and 417 deletions
|
@ -20,6 +20,7 @@ use style::computed_values::background_attachment::single_value::T as Background
|
||||||
use style::computed_values::background_clip::single_value::T as BackgroundClip;
|
use style::computed_values::background_clip::single_value::T as BackgroundClip;
|
||||||
use style::computed_values::background_origin::single_value::T as BackgroundOrigin;
|
use style::computed_values::background_origin::single_value::T as BackgroundOrigin;
|
||||||
use style::computed_values::border_image_outset::T as BorderImageOutset;
|
use style::computed_values::border_image_outset::T as BorderImageOutset;
|
||||||
|
use style::properties::ComputedValues;
|
||||||
use style::properties::style_structs::{self, Background};
|
use style::properties::style_structs::{self, Background};
|
||||||
use style::values::Either;
|
use style::values::Either;
|
||||||
use style::values::computed::{Angle, GradientItem, BackgroundSize as ComputedBackgroundSize};
|
use style::values::computed::{Angle, GradientItem, BackgroundSize as ComputedBackgroundSize};
|
||||||
|
@ -429,7 +430,11 @@ fn convert_ellipse_size_keyword(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_gradient_stops(gradient_items: &[GradientItem], total_length: Au) -> Vec<GradientStop> {
|
fn convert_gradient_stops(
|
||||||
|
style: &ComputedValues,
|
||||||
|
gradient_items: &[GradientItem],
|
||||||
|
total_length: Au,
|
||||||
|
) -> Vec<GradientStop> {
|
||||||
// Determine the position of each stop per CSS-IMAGES § 3.4.
|
// Determine the position of each stop per CSS-IMAGES § 3.4.
|
||||||
|
|
||||||
// Only keep the color stops, discard the color interpolation hints.
|
// Only keep the color stops, discard the color interpolation hints.
|
||||||
|
@ -497,8 +502,8 @@ fn convert_gradient_stops(gradient_items: &[GradientItem], total_length: Au) ->
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let end_offset = position_to_offset(end_stop.position.unwrap(), total_length);
|
let end_offset = position_to_offset(end_stop.position.unwrap(), total_length);
|
||||||
stop_run = Some(StopRun {
|
stop_run = Some(StopRun {
|
||||||
start_offset: start_offset,
|
start_offset,
|
||||||
end_offset: end_offset,
|
end_offset,
|
||||||
start_index: i - 1,
|
start_index: i - 1,
|
||||||
stop_count: end_index,
|
stop_count: end_index,
|
||||||
})
|
})
|
||||||
|
@ -518,7 +523,7 @@ fn convert_gradient_stops(gradient_items: &[GradientItem], total_length: Au) ->
|
||||||
assert!(offset.is_finite());
|
assert!(offset.is_finite());
|
||||||
stops.push(GradientStop {
|
stops.push(GradientStop {
|
||||||
offset: offset,
|
offset: offset,
|
||||||
color: stop.color.to_layout(),
|
color: style.resolve_color(stop.color).to_layout(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
stops
|
stops
|
||||||
|
@ -533,6 +538,7 @@ fn as_gradient_extend_mode(repeating: bool) -> ExtendMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_linear_gradient(
|
pub fn convert_linear_gradient(
|
||||||
|
style: &ComputedValues,
|
||||||
size: Size2D<Au>,
|
size: Size2D<Au>,
|
||||||
stops: &[GradientItem],
|
stops: &[GradientItem],
|
||||||
direction: LineDirection,
|
direction: LineDirection,
|
||||||
|
@ -581,19 +587,20 @@ pub fn convert_linear_gradient(
|
||||||
// This is the length of the gradient line.
|
// This is the length of the gradient line.
|
||||||
let length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0));
|
let length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0));
|
||||||
|
|
||||||
let stops = convert_gradient_stops(stops, length);
|
let stops = convert_gradient_stops(style, stops, length);
|
||||||
|
|
||||||
let center = Point2D::new(size.width / 2, size.height / 2);
|
let center = Point2D::new(size.width / 2, size.height / 2);
|
||||||
|
|
||||||
Gradient {
|
Gradient {
|
||||||
start_point: (center - delta).to_layout(),
|
start_point: (center - delta).to_layout(),
|
||||||
end_point: (center + delta).to_layout(),
|
end_point: (center + delta).to_layout(),
|
||||||
stops: stops,
|
stops,
|
||||||
extend_mode: as_gradient_extend_mode(repeating),
|
extend_mode: as_gradient_extend_mode(repeating),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_radial_gradient(
|
pub fn convert_radial_gradient(
|
||||||
|
style: &ComputedValues,
|
||||||
size: Size2D<Au>,
|
size: Size2D<Au>,
|
||||||
stops: &[GradientItem],
|
stops: &[GradientItem],
|
||||||
shape: EndingShape,
|
shape: EndingShape,
|
||||||
|
@ -620,7 +627,7 @@ pub fn convert_radial_gradient(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let stops = convert_gradient_stops(stops, radius.width);
|
let stops = convert_gradient_stops(style, stops, radius.width);
|
||||||
|
|
||||||
RadialGradient {
|
RadialGradient {
|
||||||
center: center.to_layout(),
|
center: center.to_layout(),
|
||||||
|
|
|
@ -1116,6 +1116,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
let display_item = match gradient.kind {
|
let display_item = match gradient.kind {
|
||||||
GradientKind::Linear(angle_or_corner) => {
|
GradientKind::Linear(angle_or_corner) => {
|
||||||
let gradient = convert_linear_gradient(
|
let gradient = convert_linear_gradient(
|
||||||
|
style,
|
||||||
placement.tile_size,
|
placement.tile_size,
|
||||||
&gradient.items[..],
|
&gradient.items[..],
|
||||||
angle_or_corner,
|
angle_or_corner,
|
||||||
|
@ -1130,6 +1131,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
},
|
},
|
||||||
GradientKind::Radial(shape, center, _angle) => {
|
GradientKind::Radial(shape, center, _angle) => {
|
||||||
let gradient = convert_radial_gradient(
|
let gradient = convert_radial_gradient(
|
||||||
|
style,
|
||||||
placement.tile_size,
|
placement.tile_size,
|
||||||
&gradient.items[..],
|
&gradient.items[..],
|
||||||
shape,
|
shape,
|
||||||
|
@ -1298,6 +1300,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
Either::Second(Image::Gradient(ref gradient)) => Some(match gradient.kind {
|
Either::Second(Image::Gradient(ref gradient)) => Some(match gradient.kind {
|
||||||
GradientKind::Linear(angle_or_corner) => BorderDetails::Gradient(GradientBorder {
|
GradientKind::Linear(angle_or_corner) => BorderDetails::Gradient(GradientBorder {
|
||||||
gradient: convert_linear_gradient(
|
gradient: convert_linear_gradient(
|
||||||
|
style,
|
||||||
bounds.size,
|
bounds.size,
|
||||||
&gradient.items[..],
|
&gradient.items[..],
|
||||||
angle_or_corner,
|
angle_or_corner,
|
||||||
|
@ -1308,6 +1311,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
GradientKind::Radial(shape, center, _angle) => {
|
GradientKind::Radial(shape, center, _angle) => {
|
||||||
BorderDetails::RadialGradient(RadialGradientBorder {
|
BorderDetails::RadialGradient(RadialGradientBorder {
|
||||||
gradient: convert_radial_gradient(
|
gradient: convert_radial_gradient(
|
||||||
|
style,
|
||||||
bounds.size,
|
bounds.size,
|
||||||
&gradient.items[..],
|
&gradient.items[..],
|
||||||
shape,
|
shape,
|
||||||
|
|
|
@ -16,7 +16,7 @@ use dom::medialist::MediaList;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::media_queries::parse_media_query_list;
|
use style::media_queries::MediaList as StyleMediaList;
|
||||||
use style::parser::ParserContext;
|
use style::parser::ParserContext;
|
||||||
use style::shared_lock::{Locked, ToCssWithGuard};
|
use style::shared_lock::{Locked, ToCssWithGuard};
|
||||||
use style::stylesheets::{CssRuleType, MediaRule};
|
use style::stylesheets::{CssRuleType, MediaRule};
|
||||||
|
@ -79,8 +79,11 @@ impl CSSMediaRule {
|
||||||
ParsingMode::DEFAULT,
|
ParsingMode::DEFAULT,
|
||||||
quirks_mode);
|
quirks_mode);
|
||||||
|
|
||||||
let new_medialist = parse_media_query_list(&context, &mut input,
|
let new_medialist = StyleMediaList::parse(
|
||||||
window.css_error_reporter());
|
&context,
|
||||||
|
&mut input,
|
||||||
|
window.css_error_reporter(),
|
||||||
|
);
|
||||||
let mut guard = self.cssconditionrule.shared_lock().write();
|
let mut guard = self.cssconditionrule.shared_lock().write();
|
||||||
|
|
||||||
// Clone an Arc because we can’t borrow `guard` twice at the same time.
|
// Clone an Arc because we can’t borrow `guard` twice at the same time.
|
||||||
|
|
|
@ -29,7 +29,7 @@ use std::borrow::ToOwned;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use style::attr::AttrValue;
|
use style::attr::AttrValue;
|
||||||
use style::media_queries::parse_media_query_list;
|
use style::media_queries::MediaList;
|
||||||
use style::parser::ParserContext as CssParserContext;
|
use style::parser::ParserContext as CssParserContext;
|
||||||
use style::str::HTML_SPACE_CHARACTERS;
|
use style::str::HTML_SPACE_CHARACTERS;
|
||||||
use style::stylesheets::{CssRuleType, Stylesheet};
|
use style::stylesheets::{CssRuleType, Stylesheet};
|
||||||
|
@ -277,12 +277,21 @@ impl HTMLLinkElement {
|
||||||
let mut input = ParserInput::new(&mq_str);
|
let mut input = ParserInput::new(&mq_str);
|
||||||
let mut css_parser = CssParser::new(&mut input);
|
let mut css_parser = CssParser::new(&mut input);
|
||||||
let doc_url = document.url();
|
let doc_url = document.url();
|
||||||
let context = CssParserContext::new_for_cssom(&doc_url, Some(CssRuleType::Media),
|
// FIXME(emilio): This looks somewhat fishy, since we use the context
|
||||||
ParsingMode::DEFAULT,
|
// only to parse the media query list, CssRuleType::Media doesn't make
|
||||||
document.quirks_mode());
|
// much sense.
|
||||||
|
let context = CssParserContext::new_for_cssom(
|
||||||
|
&doc_url,
|
||||||
|
Some(CssRuleType::Media),
|
||||||
|
ParsingMode::DEFAULT,
|
||||||
|
document.quirks_mode(),
|
||||||
|
);
|
||||||
let window = document.window();
|
let window = document.window();
|
||||||
let media = parse_media_query_list(&context, &mut css_parser,
|
let media = MediaList::parse(
|
||||||
window.css_error_reporter());
|
&context,
|
||||||
|
&mut css_parser,
|
||||||
|
window.css_error_reporter(),
|
||||||
|
);
|
||||||
|
|
||||||
let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
|
let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
|
||||||
let integrity_val = im_attribute.r().map(|a| a.value());
|
let integrity_val = im_attribute.r().map(|a| a.value());
|
||||||
|
|
|
@ -21,7 +21,7 @@ use html5ever::{LocalName, Prefix};
|
||||||
use net_traits::ReferrerPolicy;
|
use net_traits::ReferrerPolicy;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use style::media_queries::parse_media_query_list;
|
use style::media_queries::MediaList;
|
||||||
use style::parser::ParserContext as CssParserContext;
|
use style::parser::ParserContext as CssParserContext;
|
||||||
use style::stylesheets::{CssRuleType, Stylesheet, Origin};
|
use style::stylesheets::{CssRuleType, Stylesheet, Origin};
|
||||||
use style_traits::ParsingMode;
|
use style_traits::ParsingMode;
|
||||||
|
@ -91,9 +91,11 @@ impl HTMLStyleElement {
|
||||||
let shared_lock = node.owner_doc().style_shared_lock().clone();
|
let shared_lock = node.owner_doc().style_shared_lock().clone();
|
||||||
let mut input = ParserInput::new(&mq_str);
|
let mut input = ParserInput::new(&mq_str);
|
||||||
let css_error_reporter = window.css_error_reporter();
|
let css_error_reporter = window.css_error_reporter();
|
||||||
let mq = Arc::new(shared_lock.wrap(parse_media_query_list(&context,
|
let mq = Arc::new(shared_lock.wrap(MediaList::parse(
|
||||||
&mut CssParser::new(&mut input),
|
&context,
|
||||||
css_error_reporter)));
|
&mut CssParser::new(&mut input),
|
||||||
|
css_error_reporter),
|
||||||
|
));
|
||||||
let loader = StylesheetLoader::for_element(self.upcast());
|
let loader = StylesheetLoader::for_element(self.upcast());
|
||||||
let sheet = Stylesheet::from_str(&data, window.get_url(),
|
let sheet = Stylesheet::from_str(&data, window.get_url(),
|
||||||
Origin::Author, mq,
|
Origin::Author, mq,
|
||||||
|
|
|
@ -13,8 +13,8 @@ use dom::cssstylesheet::CSSStyleSheet;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::media_queries::{MediaQuery, parse_media_query_list};
|
|
||||||
use style::media_queries::MediaList as StyleMediaList;
|
use style::media_queries::MediaList as StyleMediaList;
|
||||||
|
use style::media_queries::MediaQuery;
|
||||||
use style::parser::ParserContext;
|
use style::parser::ParserContext;
|
||||||
use style::shared_lock::{SharedRwLock, Locked};
|
use style::shared_lock::{SharedRwLock, Locked};
|
||||||
use style::stylesheets::CssRuleType;
|
use style::stylesheets::CssRuleType;
|
||||||
|
@ -80,8 +80,11 @@ impl MediaListMethods for MediaList {
|
||||||
let context = ParserContext::new_for_cssom(&url, Some(CssRuleType::Media),
|
let context = ParserContext::new_for_cssom(&url, Some(CssRuleType::Media),
|
||||||
ParsingMode::DEFAULT,
|
ParsingMode::DEFAULT,
|
||||||
quirks_mode);
|
quirks_mode);
|
||||||
*media_queries = parse_media_query_list(&context, &mut parser,
|
*media_queries = StyleMediaList::parse(
|
||||||
window.css_error_reporter());
|
&context,
|
||||||
|
&mut parser,
|
||||||
|
window.css_error_reporter(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom/#dom-medialist-length
|
// https://drafts.csswg.org/cssom/#dom-medialist-length
|
||||||
|
|
|
@ -1020,8 +1020,11 @@ impl WindowMethods for Window {
|
||||||
let context = CssParserContext::new_for_cssom(&url, Some(CssRuleType::Media),
|
let context = CssParserContext::new_for_cssom(&url, Some(CssRuleType::Media),
|
||||||
ParsingMode::DEFAULT,
|
ParsingMode::DEFAULT,
|
||||||
quirks_mode);
|
quirks_mode);
|
||||||
let media_query_list = media_queries::parse_media_query_list(&context, &mut parser,
|
let media_query_list = media_queries::MediaList::parse(
|
||||||
self.css_error_reporter());
|
&context,
|
||||||
|
&mut parser,
|
||||||
|
self.css_error_reporter(),
|
||||||
|
);
|
||||||
let document = self.Document();
|
let document = self.Document();
|
||||||
let mql = MediaQueryList::new(&document, media_query_list);
|
let mql = MediaQueryList::new(&document, media_query_list);
|
||||||
self.media_query_lists.push(&*mql);
|
self.media_query_lists.push(&*mql);
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
#![allow(non_snake_case, missing_docs)]
|
#![allow(non_snake_case, missing_docs)]
|
||||||
|
|
||||||
use gecko_bindings::bindings::RawServoCounterStyleRule;
|
use gecko_bindings::bindings::RawServoCounterStyleRule;
|
||||||
use gecko_bindings::bindings::RawServoDocumentRule;
|
|
||||||
use gecko_bindings::bindings::RawServoFontFeatureValuesRule;
|
use gecko_bindings::bindings::RawServoFontFeatureValuesRule;
|
||||||
use gecko_bindings::bindings::RawServoImportRule;
|
use gecko_bindings::bindings::RawServoImportRule;
|
||||||
use gecko_bindings::bindings::RawServoKeyframe;
|
use gecko_bindings::bindings::RawServoKeyframe;
|
||||||
use gecko_bindings::bindings::RawServoKeyframesRule;
|
use gecko_bindings::bindings::RawServoKeyframesRule;
|
||||||
use gecko_bindings::bindings::RawServoMediaRule;
|
use gecko_bindings::bindings::RawServoMediaRule;
|
||||||
|
use gecko_bindings::bindings::RawServoMozDocumentRule;
|
||||||
use gecko_bindings::bindings::RawServoNamespaceRule;
|
use gecko_bindings::bindings::RawServoNamespaceRule;
|
||||||
use gecko_bindings::bindings::RawServoPageRule;
|
use gecko_bindings::bindings::RawServoPageRule;
|
||||||
use gecko_bindings::bindings::RawServoRuleNode;
|
use gecko_bindings::bindings::RawServoRuleNode;
|
||||||
|
@ -98,7 +98,7 @@ impl_arc_ffi!(Locked<PageRule> => RawServoPageRule
|
||||||
impl_arc_ffi!(Locked<SupportsRule> => RawServoSupportsRule
|
impl_arc_ffi!(Locked<SupportsRule> => RawServoSupportsRule
|
||||||
[Servo_SupportsRule_AddRef, Servo_SupportsRule_Release]);
|
[Servo_SupportsRule_AddRef, Servo_SupportsRule_Release]);
|
||||||
|
|
||||||
impl_arc_ffi!(Locked<DocumentRule> => RawServoDocumentRule
|
impl_arc_ffi!(Locked<DocumentRule> => RawServoMozDocumentRule
|
||||||
[Servo_DocumentRule_AddRef, Servo_DocumentRule_Release]);
|
[Servo_DocumentRule_AddRef, Servo_DocumentRule_Release]);
|
||||||
|
|
||||||
impl_arc_ffi!(Locked<FontFeatureValuesRule> => RawServoFontFeatureValuesRule
|
impl_arc_ffi!(Locked<FontFeatureValuesRule> => RawServoFontFeatureValuesRule
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible};
|
use gecko::values::GeckoStyleCoordConvertible;
|
||||||
use gecko_bindings::bindings;
|
use gecko_bindings::bindings;
|
||||||
use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
|
use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
|
||||||
use gecko_bindings::structs::{nsresult, SheetType, nsStyleImage};
|
use gecko_bindings::structs::{nsresult, SheetType, nsStyleImage};
|
||||||
|
@ -358,7 +358,7 @@ impl nsStyleImage {
|
||||||
|
|
||||||
match *item {
|
match *item {
|
||||||
GradientItem::ColorStop(ref stop) => {
|
GradientItem::ColorStop(ref stop) => {
|
||||||
gecko_stop.mColor = convert_rgba_to_nscolor(&stop.color);
|
gecko_stop.mColor = stop.color.into();
|
||||||
gecko_stop.mIsInterpolationHint = false;
|
gecko_stop.mIsInterpolationHint = false;
|
||||||
coord.set(stop.position);
|
coord.set(stop.position);
|
||||||
},
|
},
|
||||||
|
@ -433,7 +433,6 @@ impl nsStyleImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_gradient(self: &nsStyleImage) -> Box<Gradient> {
|
unsafe fn get_gradient(self: &nsStyleImage) -> Box<Gradient> {
|
||||||
use gecko::values::convert_nscolor_to_rgba;
|
|
||||||
use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER;
|
use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER;
|
||||||
use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE;
|
use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE;
|
||||||
use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER;
|
use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER;
|
||||||
|
@ -601,7 +600,7 @@ impl nsStyleImage {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
GradientItem::ColorStop(ColorStop {
|
GradientItem::ColorStop(ColorStop {
|
||||||
color: convert_nscolor_to_rgba(stop.mColor),
|
color: stop.mColor.into(),
|
||||||
position: LengthOrPercentage::from_gecko_style_coord(&stop.mLocation),
|
position: LengthOrPercentage::from_gecko_style_coord(&stop.mLocation),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use properties::ComputedValues;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
|
||||||
use str::starts_with_ignore_ascii_case;
|
use str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
use style_traits::{CSSPixel, CssWriter, DevicePixel};
|
use style_traits::{CSSPixel, CssWriter, DevicePixel};
|
||||||
use style_traits::{ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
@ -118,8 +118,7 @@ impl Device {
|
||||||
|
|
||||||
/// Set the font size of the root element (for rem)
|
/// Set the font size of the root element (for rem)
|
||||||
pub fn set_root_font_size(&self, size: Au) {
|
pub fn set_root_font_size(&self, size: Au) {
|
||||||
self.root_font_size
|
self.root_font_size.store(size.0 as isize, Ordering::Relaxed)
|
||||||
.store(size.0 as isize, Ordering::Relaxed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the body text color for the "inherit color from body" quirk.
|
/// Sets the body text color for the "inherit color from body" quirk.
|
||||||
|
@ -229,7 +228,7 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The kind of matching that should be performed on a media feature value.
|
/// The kind of matching that should be performed on a media feature value.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
|
||||||
pub enum Range {
|
pub enum Range {
|
||||||
/// At least the specified value.
|
/// At least the specified value.
|
||||||
Min,
|
Min,
|
||||||
|
@ -241,7 +240,7 @@ pub enum Range {
|
||||||
|
|
||||||
/// A expression for gecko contains a reference to the media feature, the value
|
/// A expression for gecko contains a reference to the media feature, the value
|
||||||
/// the media query contained, and the range to evaluate.
|
/// the media query contained, and the range to evaluate.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, MallocSizeOf)]
|
||||||
pub struct Expression {
|
pub struct Expression {
|
||||||
feature: &'static nsMediaFeature,
|
feature: &'static nsMediaFeature,
|
||||||
value: Option<MediaExpressionValue>,
|
value: Option<MediaExpressionValue>,
|
||||||
|
@ -294,7 +293,7 @@ impl PartialEq for Expression {
|
||||||
/// If the first, this would need to store the relevant values.
|
/// If the first, this would need to store the relevant values.
|
||||||
///
|
///
|
||||||
/// See: https://github.com/w3c/csswg-drafts/issues/1968
|
/// See: https://github.com/w3c/csswg-drafts/issues/1968
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
||||||
pub enum MediaExpressionValue {
|
pub enum MediaExpressionValue {
|
||||||
/// A length.
|
/// A length.
|
||||||
Length(Length),
|
Length(Length),
|
||||||
|
@ -596,7 +595,7 @@ impl Expression {
|
||||||
Range::Equal
|
Range::Equal
|
||||||
};
|
};
|
||||||
|
|
||||||
let atom = Atom::from(feature_name);
|
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
|
||||||
match find_feature(|f| atom.as_ptr() == unsafe { *f.mName as *mut _ }) {
|
match find_feature(|f| atom.as_ptr() == unsafe { *f.mName as *mut _ }) {
|
||||||
Some(f) => Ok((f, range)),
|
Some(f) => Ok((f, range)),
|
||||||
None => Err(()),
|
None => Err(()),
|
||||||
|
|
|
@ -178,11 +178,13 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
|
||||||
as *const bindings::RawServoAuthorStyles)
|
as *const bindings::RawServoAuthorStyles)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles);
|
let author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles);
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
author_styles.quirks_mode == self.as_node().owner_doc().quirks_mode() ||
|
author_styles.quirks_mode == self.as_node().owner_doc().quirks_mode() ||
|
||||||
author_styles.stylesheets.is_empty()
|
author_styles.stylesheets.is_empty() ||
|
||||||
|
author_styles.stylesheets.dirty()
|
||||||
);
|
);
|
||||||
|
|
||||||
&author_styles.data
|
&author_styles.data
|
||||||
|
|
|
@ -1,353 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* 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/. */
|
|
||||||
|
|
||||||
//! [Media queries][mq].
|
|
||||||
//!
|
|
||||||
//! [mq]: https://drafts.csswg.org/mediaqueries/
|
|
||||||
|
|
||||||
use Atom;
|
|
||||||
use context::QuirksMode;
|
|
||||||
use cssparser::{Delimiter, Parser};
|
|
||||||
use cssparser::{ParserInput, Token};
|
|
||||||
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
|
||||||
use parser::{ParserContext, ParserErrorContext};
|
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
|
||||||
use std::fmt::{self, Write};
|
|
||||||
use str::string_as_ascii_lowercase;
|
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
|
||||||
use values::CustomIdent;
|
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
pub use servo::media_queries::{Device, Expression};
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub use gecko::media_queries::{Device, Expression};
|
|
||||||
|
|
||||||
/// A type that encapsulates a media query list.
|
|
||||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
|
||||||
#[css(comma)]
|
|
||||||
#[derive(Clone, Debug, ToCss)]
|
|
||||||
pub struct MediaList {
|
|
||||||
/// The list of media queries.
|
|
||||||
#[css(iterable)]
|
|
||||||
pub media_queries: Vec<MediaQuery>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MediaList {
|
|
||||||
/// Create an empty MediaList.
|
|
||||||
pub fn empty() -> Self {
|
|
||||||
MediaList {
|
|
||||||
media_queries: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
|
|
||||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
|
|
||||||
pub enum Qualifier {
|
|
||||||
/// Hide a media query from legacy UAs:
|
|
||||||
/// <https://drafts.csswg.org/mediaqueries/#mq-only>
|
|
||||||
Only,
|
|
||||||
/// Negate a media query:
|
|
||||||
/// <https://drafts.csswg.org/mediaqueries/#mq-not>
|
|
||||||
Not,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [media query][mq].
|
|
||||||
///
|
|
||||||
/// [mq]: https://drafts.csswg.org/mediaqueries/
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
|
||||||
pub struct MediaQuery {
|
|
||||||
/// The qualifier for this query.
|
|
||||||
pub qualifier: Option<Qualifier>,
|
|
||||||
/// The media type for this query, that can be known, unknown, or "all".
|
|
||||||
pub media_type: MediaQueryType,
|
|
||||||
/// The set of expressions that this media query contains.
|
|
||||||
pub expressions: Vec<Expression>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MediaQuery {
|
|
||||||
/// Return a media query that never matches, used for when we fail to parse
|
|
||||||
/// a given media query.
|
|
||||||
fn never_matching() -> Self {
|
|
||||||
Self {
|
|
||||||
qualifier: Some(Qualifier::Not),
|
|
||||||
media_type: MediaQueryType::All,
|
|
||||||
expressions: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for MediaQuery {
|
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
||||||
where
|
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
if let Some(qual) = self.qualifier {
|
|
||||||
qual.to_css(dest)?;
|
|
||||||
dest.write_char(' ')?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.media_type {
|
|
||||||
MediaQueryType::All => {
|
|
||||||
// We need to print "all" if there's a qualifier, or there's
|
|
||||||
// just an empty list of expressions.
|
|
||||||
//
|
|
||||||
// Otherwise, we'd serialize media queries like "(min-width:
|
|
||||||
// 40px)" in "all (min-width: 40px)", which is unexpected.
|
|
||||||
if self.qualifier.is_some() || self.expressions.is_empty() {
|
|
||||||
dest.write_str("all")?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.expressions.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
|
|
||||||
dest.write_str(" and ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expressions[0].to_css(dest)?;
|
|
||||||
|
|
||||||
for expr in self.expressions.iter().skip(1) {
|
|
||||||
dest.write_str(" and ")?;
|
|
||||||
expr.to_css(dest)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <http://dev.w3.org/csswg/mediaqueries-3/#media0>
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
|
||||||
pub enum MediaQueryType {
|
|
||||||
/// A media type that matches every device.
|
|
||||||
All,
|
|
||||||
/// A specific media type.
|
|
||||||
Concrete(MediaType),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MediaQueryType {
|
|
||||||
fn parse(ident: &str) -> Result<Self, ()> {
|
|
||||||
match_ignore_ascii_case! { ident,
|
|
||||||
"all" => return Ok(MediaQueryType::All),
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
|
|
||||||
// If parseable, accept this type as a concrete type.
|
|
||||||
MediaType::parse(ident).map(MediaQueryType::Concrete)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn matches(&self, other: MediaType) -> bool {
|
|
||||||
match *self {
|
|
||||||
MediaQueryType::All => true,
|
|
||||||
MediaQueryType::Concrete(ref known_type) => *known_type == other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/mediaqueries/#media-types>
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
|
||||||
pub struct MediaType(pub CustomIdent);
|
|
||||||
|
|
||||||
impl MediaType {
|
|
||||||
/// The `screen` media type.
|
|
||||||
pub fn screen() -> Self {
|
|
||||||
MediaType(CustomIdent(atom!("screen")))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `print` media type.
|
|
||||||
pub fn print() -> Self {
|
|
||||||
MediaType(CustomIdent(atom!("print")))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(name: &str) -> Result<Self, ()> {
|
|
||||||
// From https://drafts.csswg.org/mediaqueries/#mq-syntax:
|
|
||||||
//
|
|
||||||
// The <media-type> production does not include the keywords not, or, and, and only.
|
|
||||||
//
|
|
||||||
// Here we also perform the to-ascii-lowercase part of the serialization
|
|
||||||
// algorithm: https://drafts.csswg.org/cssom/#serializing-media-queries
|
|
||||||
match_ignore_ascii_case! { name,
|
|
||||||
"not" | "or" | "and" | "only" => Err(()),
|
|
||||||
_ => Ok(MediaType(CustomIdent(Atom::from(string_as_ascii_lowercase(name))))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl MediaQuery {
|
|
||||||
/// Parse a media query given css input.
|
|
||||||
///
|
|
||||||
/// Returns an error if any of the expressions is unknown.
|
|
||||||
pub fn parse<'i, 't>(
|
|
||||||
context: &ParserContext,
|
|
||||||
input: &mut Parser<'i, 't>,
|
|
||||||
) -> Result<MediaQuery, ParseError<'i>> {
|
|
||||||
let mut expressions = vec![];
|
|
||||||
|
|
||||||
let qualifier = if input
|
|
||||||
.try(|input| input.expect_ident_matching("only"))
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Some(Qualifier::Only)
|
|
||||||
} else if input
|
|
||||||
.try(|input| input.expect_ident_matching("not"))
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Some(Qualifier::Not)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let media_type = match input.try(|i| i.expect_ident_cloned()) {
|
|
||||||
Ok(ident) => MediaQueryType::parse(&*ident).map_err(|()| {
|
|
||||||
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
|
|
||||||
})?,
|
|
||||||
Err(_) => {
|
|
||||||
// Media type is only optional if qualifier is not specified.
|
|
||||||
if qualifier.is_some() {
|
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Without a media type, require at least one expression.
|
|
||||||
expressions.push(Expression::parse(context, input)?);
|
|
||||||
|
|
||||||
MediaQueryType::All
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse any subsequent expressions
|
|
||||||
loop {
|
|
||||||
if input
|
|
||||||
.try(|input| input.expect_ident_matching("and"))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return Ok(MediaQuery {
|
|
||||||
qualifier,
|
|
||||||
media_type,
|
|
||||||
expressions,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
expressions.push(Expression::parse(context, input)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a media query list from CSS.
|
|
||||||
///
|
|
||||||
/// Always returns a media query list. If any invalid media query is found, the
|
|
||||||
/// media query list is only filled with the equivalent of "not all", see:
|
|
||||||
///
|
|
||||||
/// <https://drafts.csswg.org/mediaqueries/#error-handling>
|
|
||||||
pub fn parse_media_query_list<R>(
|
|
||||||
context: &ParserContext,
|
|
||||||
input: &mut Parser,
|
|
||||||
error_reporter: &R,
|
|
||||||
) -> MediaList
|
|
||||||
where
|
|
||||||
R: ParseErrorReporter,
|
|
||||||
{
|
|
||||||
if input.is_exhausted() {
|
|
||||||
return MediaList::empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut media_queries = vec![];
|
|
||||||
loop {
|
|
||||||
let start_position = input.position();
|
|
||||||
match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {
|
|
||||||
Ok(mq) => {
|
|
||||||
media_queries.push(mq);
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
media_queries.push(MediaQuery::never_matching());
|
|
||||||
let location = err.location;
|
|
||||||
let error =
|
|
||||||
ContextualParseError::InvalidMediaRule(input.slice_from(start_position), err);
|
|
||||||
let error_context = ParserErrorContext { error_reporter };
|
|
||||||
context.log_css_error(&error_context, location, error);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
match input.next() {
|
|
||||||
Ok(&Token::Comma) => {},
|
|
||||||
Ok(_) => unreachable!(),
|
|
||||||
Err(_) => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaList {
|
|
||||||
media_queries: media_queries,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MediaList {
|
|
||||||
/// Evaluate a whole `MediaList` against `Device`.
|
|
||||||
pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
|
|
||||||
// Check if it is an empty media query list or any queries match (OR condition)
|
|
||||||
// https://drafts.csswg.org/mediaqueries-4/#mq-list
|
|
||||||
self.media_queries.is_empty() || self.media_queries.iter().any(|mq| {
|
|
||||||
let media_match = mq.media_type.matches(device.media_type());
|
|
||||||
|
|
||||||
// Check if all conditions match (AND condition)
|
|
||||||
let query_match = media_match &&
|
|
||||||
mq.expressions
|
|
||||||
.iter()
|
|
||||||
.all(|expression| expression.matches(&device, quirks_mode));
|
|
||||||
|
|
||||||
// Apply the logical NOT qualifier to the result
|
|
||||||
match mq.qualifier {
|
|
||||||
Some(Qualifier::Not) => !query_match,
|
|
||||||
_ => query_match,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this `MediaList` contains no media queries.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.media_queries.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a new media query item to the media list.
|
|
||||||
/// <https://drafts.csswg.org/cssom/#dom-medialist-appendmedium>
|
|
||||||
///
|
|
||||||
/// Returns true if added, false if fail to parse the medium string.
|
|
||||||
pub fn append_medium(&mut self, context: &ParserContext, new_medium: &str) -> bool {
|
|
||||||
let mut input = ParserInput::new(new_medium);
|
|
||||||
let mut parser = Parser::new(&mut input);
|
|
||||||
let new_query = match MediaQuery::parse(&context, &mut parser) {
|
|
||||||
Ok(query) => query,
|
|
||||||
Err(_) => {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// This algorithm doesn't actually matches the current spec,
|
|
||||||
// but it matches the behavior of Gecko and Edge.
|
|
||||||
// See https://github.com/w3c/csswg-drafts/issues/697
|
|
||||||
self.media_queries.retain(|query| query != &new_query);
|
|
||||||
self.media_queries.push(new_query);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete a media query from the media list.
|
|
||||||
/// <https://drafts.csswg.org/cssom/#dom-medialist-deletemedium>
|
|
||||||
///
|
|
||||||
/// Returns true if found and deleted, false otherwise.
|
|
||||||
pub fn delete_medium(&mut self, context: &ParserContext, old_medium: &str) -> bool {
|
|
||||||
let mut input = ParserInput::new(old_medium);
|
|
||||||
let mut parser = Parser::new(&mut input);
|
|
||||||
let old_query = match MediaQuery::parse(context, &mut parser) {
|
|
||||||
Ok(query) => query,
|
|
||||||
Err(_) => {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let old_len = self.media_queries.len();
|
|
||||||
self.media_queries.retain(|query| query != &old_query);
|
|
||||||
old_len != self.media_queries.len()
|
|
||||||
}
|
|
||||||
}
|
|
143
components/style/media_queries/media_list.rs
Normal file
143
components/style/media_queries/media_list.rs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
//! A media query list:
|
||||||
|
//!
|
||||||
|
//! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list
|
||||||
|
|
||||||
|
use context::QuirksMode;
|
||||||
|
use cssparser::{Delimiter, Parser};
|
||||||
|
use cssparser::{ParserInput, Token};
|
||||||
|
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
||||||
|
use parser::{ParserContext, ParserErrorContext};
|
||||||
|
use super::{Device, MediaQuery, Qualifier};
|
||||||
|
|
||||||
|
/// A type that encapsulates a media query list.
|
||||||
|
#[css(comma)]
|
||||||
|
#[derive(Clone, Debug, MallocSizeOf, ToCss)]
|
||||||
|
pub struct MediaList {
|
||||||
|
/// The list of media queries.
|
||||||
|
#[css(iterable)]
|
||||||
|
pub media_queries: Vec<MediaQuery>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MediaList {
|
||||||
|
/// Parse a media query list from CSS.
|
||||||
|
///
|
||||||
|
/// Always returns a media query list. If any invalid media query is
|
||||||
|
/// found, the media query list is only filled with the equivalent of
|
||||||
|
/// "not all", see:
|
||||||
|
///
|
||||||
|
/// <https://drafts.csswg.org/mediaqueries/#error-handling>
|
||||||
|
pub fn parse<R>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser,
|
||||||
|
error_reporter: &R,
|
||||||
|
) -> MediaList
|
||||||
|
where
|
||||||
|
R: ParseErrorReporter,
|
||||||
|
{
|
||||||
|
if input.is_exhausted() {
|
||||||
|
return Self::empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut media_queries = vec![];
|
||||||
|
loop {
|
||||||
|
let start_position = input.position();
|
||||||
|
match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {
|
||||||
|
Ok(mq) => {
|
||||||
|
media_queries.push(mq);
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
media_queries.push(MediaQuery::never_matching());
|
||||||
|
let location = err.location;
|
||||||
|
let error =
|
||||||
|
ContextualParseError::InvalidMediaRule(input.slice_from(start_position), err);
|
||||||
|
let error_context = ParserErrorContext { error_reporter };
|
||||||
|
context.log_css_error(&error_context, location, error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
match input.next() {
|
||||||
|
Ok(&Token::Comma) => {},
|
||||||
|
Ok(_) => unreachable!(),
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaList { media_queries }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an empty MediaList.
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
MediaList {
|
||||||
|
media_queries: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a whole `MediaList` against `Device`.
|
||||||
|
pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
|
||||||
|
// Check if it is an empty media query list or any queries match (OR condition)
|
||||||
|
// https://drafts.csswg.org/mediaqueries-4/#mq-list
|
||||||
|
self.media_queries.is_empty() || self.media_queries.iter().any(|mq| {
|
||||||
|
let media_match = mq.media_type.matches(device.media_type());
|
||||||
|
|
||||||
|
// Check if all conditions match (AND condition)
|
||||||
|
let query_match = media_match &&
|
||||||
|
mq.expressions
|
||||||
|
.iter()
|
||||||
|
.all(|expression| expression.matches(&device, quirks_mode));
|
||||||
|
|
||||||
|
// Apply the logical NOT qualifier to the result
|
||||||
|
match mq.qualifier {
|
||||||
|
Some(Qualifier::Not) => !query_match,
|
||||||
|
_ => query_match,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this `MediaList` contains no media queries.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.media_queries.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a new media query item to the media list.
|
||||||
|
/// <https://drafts.csswg.org/cssom/#dom-medialist-appendmedium>
|
||||||
|
///
|
||||||
|
/// Returns true if added, false if fail to parse the medium string.
|
||||||
|
pub fn append_medium(&mut self, context: &ParserContext, new_medium: &str) -> bool {
|
||||||
|
let mut input = ParserInput::new(new_medium);
|
||||||
|
let mut parser = Parser::new(&mut input);
|
||||||
|
let new_query = match MediaQuery::parse(&context, &mut parser) {
|
||||||
|
Ok(query) => query,
|
||||||
|
Err(_) => {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// This algorithm doesn't actually matches the current spec,
|
||||||
|
// but it matches the behavior of Gecko and Edge.
|
||||||
|
// See https://github.com/w3c/csswg-drafts/issues/697
|
||||||
|
self.media_queries.retain(|query| query != &new_query);
|
||||||
|
self.media_queries.push(new_query);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a media query from the media list.
|
||||||
|
/// <https://drafts.csswg.org/cssom/#dom-medialist-deletemedium>
|
||||||
|
///
|
||||||
|
/// Returns true if found and deleted, false otherwise.
|
||||||
|
pub fn delete_medium(&mut self, context: &ParserContext, old_medium: &str) -> bool {
|
||||||
|
let mut input = ParserInput::new(old_medium);
|
||||||
|
let mut parser = Parser::new(&mut input);
|
||||||
|
let old_query = match MediaQuery::parse(context, &mut parser) {
|
||||||
|
Ok(query) => query,
|
||||||
|
Err(_) => {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let old_len = self.media_queries.len();
|
||||||
|
self.media_queries.retain(|query| query != &old_query);
|
||||||
|
old_len != self.media_queries.len()
|
||||||
|
}
|
||||||
|
}
|
196
components/style/media_queries/media_query.rs
Normal file
196
components/style/media_queries/media_query.rs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
//! A media query:
|
||||||
|
//!
|
||||||
|
//! https://drafts.csswg.org/mediaqueries/#typedef-media-query
|
||||||
|
|
||||||
|
use Atom;
|
||||||
|
use cssparser::Parser;
|
||||||
|
use parser::ParserContext;
|
||||||
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
|
use std::fmt::{self, Write};
|
||||||
|
use str::string_as_ascii_lowercase;
|
||||||
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
use super::Expression;
|
||||||
|
use values::CustomIdent;
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
|
||||||
|
pub enum Qualifier {
|
||||||
|
/// Hide a media query from legacy UAs:
|
||||||
|
/// <https://drafts.csswg.org/mediaqueries/#mq-only>
|
||||||
|
Only,
|
||||||
|
/// Negate a media query:
|
||||||
|
/// <https://drafts.csswg.org/mediaqueries/#mq-not>
|
||||||
|
Not,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/mediaqueries/#media-types>
|
||||||
|
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
|
||||||
|
pub struct MediaType(pub CustomIdent);
|
||||||
|
|
||||||
|
impl MediaType {
|
||||||
|
/// The `screen` media type.
|
||||||
|
pub fn screen() -> Self {
|
||||||
|
MediaType(CustomIdent(atom!("screen")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `print` media type.
|
||||||
|
pub fn print() -> Self {
|
||||||
|
MediaType(CustomIdent(atom!("print")))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(name: &str) -> Result<Self, ()> {
|
||||||
|
// From https://drafts.csswg.org/mediaqueries/#mq-syntax:
|
||||||
|
//
|
||||||
|
// The <media-type> production does not include the keywords not, or, and, and only.
|
||||||
|
//
|
||||||
|
// Here we also perform the to-ascii-lowercase part of the serialization
|
||||||
|
// algorithm: https://drafts.csswg.org/cssom/#serializing-media-queries
|
||||||
|
match_ignore_ascii_case! { name,
|
||||||
|
"not" | "or" | "and" | "only" => Err(()),
|
||||||
|
_ => Ok(MediaType(CustomIdent(Atom::from(string_as_ascii_lowercase(name))))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [media query][mq].
|
||||||
|
///
|
||||||
|
/// [mq]: https://drafts.csswg.org/mediaqueries/
|
||||||
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
||||||
|
pub struct MediaQuery {
|
||||||
|
/// The qualifier for this query.
|
||||||
|
pub qualifier: Option<Qualifier>,
|
||||||
|
/// The media type for this query, that can be known, unknown, or "all".
|
||||||
|
pub media_type: MediaQueryType,
|
||||||
|
/// The set of expressions that this media query contains.
|
||||||
|
pub expressions: Vec<Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for MediaQuery {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
if let Some(qual) = self.qualifier {
|
||||||
|
qual.to_css(dest)?;
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.media_type {
|
||||||
|
MediaQueryType::All => {
|
||||||
|
// We need to print "all" if there's a qualifier, or there's
|
||||||
|
// just an empty list of expressions.
|
||||||
|
//
|
||||||
|
// Otherwise, we'd serialize media queries like "(min-width:
|
||||||
|
// 40px)" in "all (min-width: 40px)", which is unexpected.
|
||||||
|
if self.qualifier.is_some() || self.expressions.is_empty() {
|
||||||
|
dest.write_str("all")?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.expressions.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
|
||||||
|
dest.write_str(" and ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expressions[0].to_css(dest)?;
|
||||||
|
|
||||||
|
for expr in self.expressions.iter().skip(1) {
|
||||||
|
dest.write_str(" and ")?;
|
||||||
|
expr.to_css(dest)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MediaQuery {
|
||||||
|
/// Return a media query that never matches, used for when we fail to parse
|
||||||
|
/// a given media query.
|
||||||
|
pub fn never_matching() -> Self {
|
||||||
|
Self {
|
||||||
|
qualifier: Some(Qualifier::Not),
|
||||||
|
media_type: MediaQueryType::All,
|
||||||
|
expressions: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a media query given css input.
|
||||||
|
///
|
||||||
|
/// Returns an error if any of the expressions is unknown.
|
||||||
|
pub fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<MediaQuery, ParseError<'i>> {
|
||||||
|
let mut expressions = vec![];
|
||||||
|
|
||||||
|
let qualifier = input.try(Qualifier::parse).ok();
|
||||||
|
let media_type = match input.try(|i| i.expect_ident_cloned()) {
|
||||||
|
Ok(ident) => MediaQueryType::parse(&*ident).map_err(|()| {
|
||||||
|
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
|
||||||
|
})?,
|
||||||
|
Err(_) => {
|
||||||
|
// Media type is only optional if qualifier is not specified.
|
||||||
|
if qualifier.is_some() {
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Without a media type, require at least one expression.
|
||||||
|
expressions.push(Expression::parse(context, input)?);
|
||||||
|
|
||||||
|
MediaQueryType::All
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse any subsequent expressions
|
||||||
|
loop {
|
||||||
|
if input
|
||||||
|
.try(|input| input.expect_ident_matching("and"))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return Ok(MediaQuery {
|
||||||
|
qualifier,
|
||||||
|
media_type,
|
||||||
|
expressions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expressions.push(Expression::parse(context, input)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <http://dev.w3.org/csswg/mediaqueries-3/#media0>
|
||||||
|
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
|
||||||
|
pub enum MediaQueryType {
|
||||||
|
/// A media type that matches every device.
|
||||||
|
All,
|
||||||
|
/// A specific media type.
|
||||||
|
Concrete(MediaType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MediaQueryType {
|
||||||
|
fn parse(ident: &str) -> Result<Self, ()> {
|
||||||
|
match_ignore_ascii_case! { ident,
|
||||||
|
"all" => return Ok(MediaQueryType::All),
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
// If parseable, accept this type as a concrete type.
|
||||||
|
MediaType::parse(ident).map(MediaQueryType::Concrete)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this media query type matches a MediaType.
|
||||||
|
pub fn matches(&self, other: MediaType) -> bool {
|
||||||
|
match *self {
|
||||||
|
MediaQueryType::All => true,
|
||||||
|
MediaQueryType::Concrete(ref known_type) => *known_type == other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
components/style/media_queries/mod.rs
Normal file
18
components/style/media_queries/mod.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
//! [Media queries][mq].
|
||||||
|
//!
|
||||||
|
//! [mq]: https://drafts.csswg.org/mediaqueries/
|
||||||
|
|
||||||
|
mod media_list;
|
||||||
|
mod media_query;
|
||||||
|
|
||||||
|
pub use self::media_list::MediaList;
|
||||||
|
pub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier};
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
pub use servo::media_queries::{Device, Expression};
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub use gecko::media_queries::{Device, Expression};
|
|
@ -1739,7 +1739,9 @@ impl Animate for Quaternion {
|
||||||
|
|
||||||
let (this_weight, other_weight) = procedure.weights();
|
let (this_weight, other_weight) = procedure.weights();
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
(this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON ||
|
// Doule EPSILON since both this_weight and other_weght have calculation errors
|
||||||
|
// which are approximately equal to EPSILON.
|
||||||
|
(this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON * 2.0 ||
|
||||||
other_weight == 1.0f64 || other_weight == 0.0f64,
|
other_weight == 1.0f64 || other_weight == 0.0f64,
|
||||||
"animate should only be used for interpolating or accumulating transforms"
|
"animate should only be used for interpolating or accumulating transforms"
|
||||||
);
|
);
|
||||||
|
|
|
@ -626,6 +626,7 @@ ${helpers.single_keyword("-moz-appearance",
|
||||||
gecko_ffi_name="mAppearance",
|
gecko_ffi_name="mAppearance",
|
||||||
gecko_constant_prefix="ThemeWidgetType_NS_THEME",
|
gecko_constant_prefix="ThemeWidgetType_NS_THEME",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
|
alias="-webkit-appearance:layout.css.webkit-appearance.enabled",
|
||||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
|
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
|
||||||
animation_value_type="discrete")}
|
animation_value_type="discrete")}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListP
|
||||||
use cssparser::{BasicParseError, BasicParseErrorKind, CowRcStr, SourceLocation};
|
use cssparser::{BasicParseError, BasicParseErrorKind, CowRcStr, SourceLocation};
|
||||||
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
||||||
use font_face::parse_font_face_block;
|
use font_face::parse_font_face_block;
|
||||||
use media_queries::{parse_media_query_list, MediaList};
|
use media_queries::MediaList;
|
||||||
use parser::{Parse, ParserContext, ParserErrorContext};
|
use parser::{Parse, ParserContext, ParserErrorContext};
|
||||||
use properties::parse_property_declaration_list;
|
use properties::parse_property_declaration_list;
|
||||||
use selector_parser::{SelectorImpl, SelectorParser};
|
use selector_parser::{SelectorImpl, SelectorParser};
|
||||||
|
@ -197,8 +197,11 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
|
||||||
let url_string = input.expect_url_or_string()?.as_ref().to_owned();
|
let url_string = input.expect_url_or_string()?.as_ref().to_owned();
|
||||||
let url = CssUrl::parse_from_string(url_string, &self.context);
|
let url = CssUrl::parse_from_string(url_string, &self.context);
|
||||||
|
|
||||||
let media = parse_media_query_list(&self.context, input,
|
let media = MediaList::parse(
|
||||||
self.error_context.error_reporter);
|
&self.context,
|
||||||
|
input,
|
||||||
|
self.error_context.error_reporter,
|
||||||
|
);
|
||||||
let media = Arc::new(self.shared_lock.wrap(media));
|
let media = Arc::new(self.shared_lock.wrap(media));
|
||||||
|
|
||||||
let prelude = AtRuleNonBlockPrelude::Import(url, media, location);
|
let prelude = AtRuleNonBlockPrelude::Import(url, media, location);
|
||||||
|
@ -380,8 +383,11 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
|
||||||
|
|
||||||
match_ignore_ascii_case! { &*name,
|
match_ignore_ascii_case! { &*name,
|
||||||
"media" => {
|
"media" => {
|
||||||
let media_queries = parse_media_query_list(self.context, input,
|
let media_queries = MediaList::parse(
|
||||||
self.error_context.error_reporter);
|
self.context,
|
||||||
|
input,
|
||||||
|
self.error_context.error_reporter,
|
||||||
|
);
|
||||||
let arc = Arc::new(self.shared_lock.wrap(media_queries));
|
let arc = Arc::new(self.shared_lock.wrap(media_queries));
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,12 +7,11 @@
|
||||||
//!
|
//!
|
||||||
//! [image]: https://drafts.csswg.org/css-images/#image-values
|
//! [image]: https://drafts.csswg.org/css-images/#image-values
|
||||||
|
|
||||||
use cssparser::RGBA;
|
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use style_traits::{CssWriter, ToCss};
|
use style_traits::{CssWriter, ToCss};
|
||||||
use values::{Either, None_};
|
use values::{Either, None_};
|
||||||
use values::computed::{Angle, Context};
|
use values::computed::{Angle, Color, Context};
|
||||||
use values::computed::{Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue};
|
use values::computed::{Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use values::computed::Percentage;
|
use values::computed::Percentage;
|
||||||
|
@ -32,7 +31,7 @@ pub type Image = generic::Image<Gradient, MozImageRect, ComputedImageUrl>;
|
||||||
/// Computed values for a CSS gradient.
|
/// Computed values for a CSS gradient.
|
||||||
/// <https://drafts.csswg.org/css-images/#gradients>
|
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||||
pub type Gradient =
|
pub type Gradient =
|
||||||
generic::Gradient<LineDirection, Length, LengthOrPercentage, Position, RGBA, Angle>;
|
generic::Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle>;
|
||||||
|
|
||||||
/// A computed gradient kind.
|
/// A computed gradient kind.
|
||||||
pub type GradientKind =
|
pub type GradientKind =
|
||||||
|
@ -58,10 +57,10 @@ pub enum LineDirection {
|
||||||
pub type EndingShape = generic::EndingShape<Length, LengthOrPercentage>;
|
pub type EndingShape = generic::EndingShape<Length, LengthOrPercentage>;
|
||||||
|
|
||||||
/// A computed gradient item.
|
/// A computed gradient item.
|
||||||
pub type GradientItem = generic::GradientItem<RGBA, LengthOrPercentage>;
|
pub type GradientItem = generic::GradientItem<Color, LengthOrPercentage>;
|
||||||
|
|
||||||
/// A computed color stop.
|
/// A computed color stop.
|
||||||
pub type ColorStop = generic::ColorStop<RGBA, LengthOrPercentage>;
|
pub type ColorStop = generic::ColorStop<Color, LengthOrPercentage>;
|
||||||
|
|
||||||
/// Computed values for `-moz-image-rect(...)`.
|
/// Computed values for `-moz-image-rect(...)`.
|
||||||
pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, ComputedImageUrl>;
|
pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, ComputedImageUrl>;
|
||||||
|
|
|
@ -26,7 +26,7 @@ use values::generics::image::{self as generic, Circle, CompatMode, Ellipse, Shap
|
||||||
use values::generics::image::PaintWorklet;
|
use values::generics::image::PaintWorklet;
|
||||||
use values::generics::position::Position as GenericPosition;
|
use values::generics::position::Position as GenericPosition;
|
||||||
use values::specified::{Angle, Color, Length, LengthOrPercentage};
|
use values::specified::{Angle, Color, Length, LengthOrPercentage};
|
||||||
use values::specified::{Number, NumberOrPercentage, Percentage, RGBAColor};
|
use values::specified::{Number, NumberOrPercentage, Percentage};
|
||||||
use values::specified::position::{LegacyPosition, Position, PositionComponent, Side, X, Y};
|
use values::specified::position::{LegacyPosition, Position, PositionComponent, Side, X, Y};
|
||||||
use values::specified::url::SpecifiedImageUrl;
|
use values::specified::url::SpecifiedImageUrl;
|
||||||
|
|
||||||
|
@ -41,19 +41,13 @@ pub type Image = generic::Image<Gradient, MozImageRect, SpecifiedImageUrl>;
|
||||||
/// <https://drafts.csswg.org/css-images/#gradients>
|
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||||
#[cfg(not(feature = "gecko"))]
|
#[cfg(not(feature = "gecko"))]
|
||||||
pub type Gradient =
|
pub type Gradient =
|
||||||
generic::Gradient<LineDirection, Length, LengthOrPercentage, Position, RGBAColor, Angle>;
|
generic::Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle>;
|
||||||
|
|
||||||
/// Specified values for a CSS gradient.
|
/// Specified values for a CSS gradient.
|
||||||
/// <https://drafts.csswg.org/css-images/#gradients>
|
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub type Gradient = generic::Gradient<
|
pub type Gradient =
|
||||||
LineDirection,
|
generic::Gradient<LineDirection, Length, LengthOrPercentage, GradientPosition, Color, Angle>;
|
||||||
Length,
|
|
||||||
LengthOrPercentage,
|
|
||||||
GradientPosition,
|
|
||||||
RGBAColor,
|
|
||||||
Angle,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl SpecifiedValueInfo for Gradient {
|
impl SpecifiedValueInfo for Gradient {
|
||||||
const SUPPORTED_TYPES: u8 = CssType::GRADIENT;
|
const SUPPORTED_TYPES: u8 = CssType::GRADIENT;
|
||||||
|
@ -121,10 +115,10 @@ pub enum GradientPosition {
|
||||||
pub type EndingShape = generic::EndingShape<Length, LengthOrPercentage>;
|
pub type EndingShape = generic::EndingShape<Length, LengthOrPercentage>;
|
||||||
|
|
||||||
/// A specified gradient item.
|
/// A specified gradient item.
|
||||||
pub type GradientItem = generic::GradientItem<RGBAColor, LengthOrPercentage>;
|
pub type GradientItem = generic::GradientItem<Color, LengthOrPercentage>;
|
||||||
|
|
||||||
/// A computed color stop.
|
/// A computed color stop.
|
||||||
pub type ColorStop = generic::ColorStop<RGBAColor, LengthOrPercentage>;
|
pub type ColorStop = generic::ColorStop<Color, LengthOrPercentage>;
|
||||||
|
|
||||||
/// Specified values for `moz-image-rect`
|
/// Specified values for `moz-image-rect`
|
||||||
/// -moz-image-rect(<uri>, top, right, bottom, left);
|
/// -moz-image-rect(<uri>, top, right, bottom, left);
|
||||||
|
@ -957,7 +951,7 @@ impl Parse for ColorStop {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
Ok(ColorStop {
|
Ok(ColorStop {
|
||||||
color: RGBAColor::parse(context, input)?,
|
color: Color::parse(context, input)?,
|
||||||
position: input.try(|i| LengthOrPercentage::parse(context, i)).ok(),
|
position: input.try(|i| LengthOrPercentage::parse(context, i)).ok(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use style_traits::{ParseError, StyleParseErrorKind};
|
||||||
use values::CSSFloat;
|
use values::CSSFloat;
|
||||||
|
|
||||||
/// A specified resolution.
|
/// A specified resolution.
|
||||||
#[derive(Clone, Debug, PartialEq, ToCss)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
|
||||||
pub enum Resolution {
|
pub enum Resolution {
|
||||||
/// Dots per inch.
|
/// Dots per inch.
|
||||||
#[css(dimension)]
|
#[css(dimension)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue