Auto merge of #22777 - emilio:gecko-sync, r=emilio

style: Sync changes from mozilla-central.

<!-- 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/22777)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-01-29 08:19:14 -05:00 committed by GitHub
commit 65c2fcb460
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 246 additions and 388 deletions

View file

@ -209,6 +209,8 @@ partial interface CSSStyleDeclaration {
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString filter;
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString inset;
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString lineHeight;
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString line-height;

View file

@ -23,6 +23,7 @@ bench = []
bitflags = "1.0"
matches = "0.1"
cssparser = "0.25"
derive_more = "0.13"
log = "0.4"
fxhash = "0.2"
phf = "0.7.18"

View file

@ -23,7 +23,6 @@ use servo_arc::{Arc, HeaderWithLength, ThinArc};
use smallvec::{self, SmallVec};
use std::cmp;
use std::iter;
use std::ops::{Add, AddAssign};
use std::ptr;
use std::slice;
@ -222,44 +221,13 @@ impl SpecificityAndFlags {
const MAX_10BIT: u32 = (1u32 << 10) - 1;
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
#[derive(Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
struct Specificity {
id_selectors: u32,
class_like_selectors: u32,
element_selectors: u32,
}
impl AddAssign for Specificity {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.id_selectors += rhs.id_selectors;
self.class_like_selectors += rhs.class_like_selectors;
self.element_selectors += rhs.element_selectors;
}
}
impl Add for Specificity {
type Output = Specificity;
fn add(self, rhs: Specificity) -> Specificity {
Specificity {
id_selectors: self.id_selectors + rhs.id_selectors,
class_like_selectors: self.class_like_selectors + rhs.class_like_selectors,
element_selectors: self.element_selectors + rhs.element_selectors,
}
}
}
impl Default for Specificity {
fn default() -> Specificity {
Specificity {
id_selectors: 0,
class_like_selectors: 0,
element_selectors: 0,
}
}
}
impl From<u32> for Specificity {
#[inline]
fn from(value: u32) -> Specificity {

View file

@ -9,6 +9,8 @@
extern crate bitflags;
#[macro_use]
extern crate cssparser;
#[macro_use]
extern crate derive_more;
extern crate fxhash;
#[macro_use]
extern crate log;

View file

@ -31,6 +31,7 @@ bitflags = "1.0"
byteorder = "1.0"
cssparser = "0.25"
crossbeam-channel = { version = "0.3", optional = true }
derive_more = "0.13"
new_debug_unreachable = "1.0"
encoding_rs = {version = "0.8", optional = true}
euclid = "0.19"

View file

@ -304,7 +304,7 @@ impl ElementCascadeInputs {
/// Statistics gathered during the traversal. We gather statistics on each
/// thread and then combine them after the threads join via the Add
/// implementation below.
#[derive(Default)]
#[derive(AddAssign, Clone, Default)]
pub struct PerThreadTraversalStatistics {
/// The total number of elements traversed.
pub elements_traversed: u32,
@ -319,20 +319,6 @@ pub struct PerThreadTraversalStatistics {
pub styles_reused: u32,
}
/// Implementation of Add to aggregate statistics across different threads.
impl<'a> ops::Add for &'a PerThreadTraversalStatistics {
type Output = PerThreadTraversalStatistics;
fn add(self, other: Self) -> PerThreadTraversalStatistics {
PerThreadTraversalStatistics {
elements_traversed: self.elements_traversed + other.elements_traversed,
elements_styled: self.elements_styled + other.elements_styled,
elements_matched: self.elements_matched + other.elements_matched,
styles_shared: self.styles_shared + other.styles_shared,
styles_reused: self.styles_reused + other.styles_reused,
}
}
}
/// Statistics gathered during the traversal plus some information from
/// other sources including stylist.
#[derive(Default)]

View file

@ -161,10 +161,11 @@ pub fn traverse_dom<E, D>(
let parallel = maybe_tls.is_some();
if let Some(ref mut tls) = maybe_tls {
let slots = unsafe { tls.unsafe_get() };
aggregate = slots.iter().fold(aggregate, |acc, t| match *t.borrow() {
None => acc,
Some(ref cx) => &cx.statistics + &acc,
});
for slot in slots {
if let Some(ref cx) = *slot.borrow() {
aggregate += cx.statistics.clone();
}
}
}
if report_stats {

View file

@ -38,6 +38,8 @@ extern crate crossbeam_channel;
extern crate cssparser;
#[macro_use]
extern crate debug_unreachable;
#[macro_use]
extern crate derive_more;
extern crate euclid;
extern crate fallible;
extern crate fxhash;

View file

@ -793,6 +793,58 @@
% endif
</%def>
// A shorthand of kind `<property-1> <property-2>?` where both properties have
// the same type.
<%def name="two_properties_shorthand(
name,
first_property,
second_property,
parser_function,
needs_context=True,
**kwargs
)">
<%call expr="self.shorthand(name, sub_properties=' '.join([first_property, second_property]), **kwargs)">
#[allow(unused_imports)]
use crate::parser::Parse;
use crate::values::specified;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let parse_one = |_c: &ParserContext, input: &mut Parser<'i, 't>| {
% if needs_context:
${parser_function}(_c, input)
% else:
${parser_function}(input)
% endif
};
let first = parse_one(context, input)?;
let second =
input.try(|input| parse_one(context, input)).unwrap_or_else(|_| first.clone());
Ok(expanded! {
${to_rust_ident(first_property)}: first,
${to_rust_ident(second_property)}: second,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
let first = &self.${to_rust_ident(first_property)};
let second = &self.${to_rust_ident(second_property)};
first.to_css(dest)?;
if first != second {
dest.write_str(" ")?;
second.to_css(dest)?;
}
Ok(())
}
}
</%call>
</%def>
<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function,
needs_context=True, allow_quirks=False, **kwargs)">
<% sub_properties=' '.join(sub_property_pattern % side for side in PHYSICAL_SIDES) %>

View file

@ -61,15 +61,6 @@
)}
% endfor
${helpers.gecko_keyword_conversion(
Keyword('border-style',
"none solid double dotted dashed hidden groove ridge inset outset",
gecko_enum_prefix="StyleBorderStyle",
gecko_inexhaustive=True),
type="crate::values::specified::BorderStyle",
)}
// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthPercentage>
% for corner in ALL_CORNERS:
<%
corner_name = corner[0]

View file

@ -4,112 +4,28 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand
name="overflow"
flags="SHORTHAND_IN_GETCS"
sub_properties="overflow-x overflow-y"
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow"
>
use crate::properties::longhands::overflow_x::parse as parse_overflow;
% if product == "gecko":
use crate::properties::longhands::overflow_x::SpecifiedValue;
% endif
${helpers.two_properties_shorthand(
"overflow",
"overflow-x",
"overflow-y",
"specified::Overflow::parse",
flags="SHORTHAND_IN_GETCS",
needs_context=False,
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",
)}
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
% if product == "gecko":
use crate::gecko_bindings::structs;
let moz_kw_enabled = unsafe {
structs::StaticPrefs_sVarCache_layout_css_overflow_moz_scrollbars_enabled
};
if moz_kw_enabled {
let moz_kw_found = input.try(|input| {
try_match_ident_ignore_ascii_case! { input,
"-moz-scrollbars-horizontal" => {
Ok(expanded! {
overflow_x: SpecifiedValue::Scroll,
overflow_y: SpecifiedValue::Hidden,
})
}
"-moz-scrollbars-vertical" => {
Ok(expanded! {
overflow_x: SpecifiedValue::Hidden,
overflow_y: SpecifiedValue::Scroll,
})
}
"-moz-scrollbars-none" => {
Ok(expanded! {
overflow_x: SpecifiedValue::Hidden,
overflow_y: SpecifiedValue::Hidden,
})
}
}
});
if moz_kw_found.is_ok() {
return moz_kw_found
}
}
% endif
let overflow_x = parse_overflow(context, input)?;
let overflow_y =
input.try(|i| parse_overflow(context, i)).unwrap_or(overflow_x);
Ok(expanded! {
overflow_x: overflow_x,
overflow_y: overflow_y,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.overflow_x.to_css(dest)?;
if self.overflow_x != self.overflow_y {
dest.write_char(' ')?;
self.overflow_y.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
<%helpers:shorthand
name="overflow-clip-box"
sub_properties="overflow-clip-box-block overflow-clip-box-inline"
enabled_in="ua"
gecko_pref="layout.css.overflow-clip-box.enabled"
${helpers.two_properties_shorthand(
"overflow-clip-box",
"overflow-clip-box-block",
"overflow-clip-box-inline",
"specified::OverflowClipBox::parse",
enabled_in="ua",
needs_context=False,
gecko_pref="layout.css.overflow-clip-box.enabled",
spec="Internal, may be standardized in the future "
"(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)"
products="gecko"
>
use crate::values::specified::OverflowClipBox;
pub fn parse_value<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let block_value = OverflowClipBox::parse(input)?;
let inline_value =
input.try(|input| OverflowClipBox::parse(input)).unwrap_or(block_value);
Ok(expanded! {
overflow_clip_box_block: block_value,
overflow_clip_box_inline: inline_value,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.overflow_clip_box_block.to_css(dest)?;
if self.overflow_clip_box_block != self.overflow_clip_box_inline {
dest.write_str(" ")?;
self.overflow_clip_box_inline.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
"(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)",
products="gecko",
)}
macro_rules! try_parse_one {
($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
@ -417,36 +333,16 @@ macro_rules! try_parse_one {
}
</%helpers:shorthand>
<%helpers:shorthand name="overscroll-behavior" products="gecko"
gecko_pref="layout.css.overscroll-behavior.enabled"
sub_properties="overscroll-behavior-x overscroll-behavior-y"
spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties">
pub fn parse_value<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
use crate::values::specified::OverscrollBehavior;
let behavior_x = OverscrollBehavior::parse(input)?;
let behavior_y = input.try(OverscrollBehavior::parse).unwrap_or(behavior_x);
Ok(expanded! {
overscroll_behavior_x: behavior_x,
overscroll_behavior_y: behavior_y,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
// Serializes into the single keyword value if both overscroll-behavior-x and overscroll-behavior-y are same.
// Otherwise into two values separated by a space.
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.overscroll_behavior_x.to_css(dest)?;
if self.overscroll_behavior_y != self.overscroll_behavior_x {
dest.write_str(" ")?;
self.overscroll_behavior_y.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
${helpers.two_properties_shorthand(
"overscroll-behavior",
"overscroll-behavior-x",
"overscroll-behavior-y",
"specified::OverscrollBehavior::parse",
needs_context=False,
products="gecko",
gecko_pref="layout.css.overscroll-behavior.enabled",
spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
)}
<%helpers:shorthand
name="page-break-before"

View file

@ -4,51 +4,27 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
${helpers.four_sides_shorthand("margin", "margin-%s", "specified::LengthPercentageOrAuto::parse",
spec="https://drafts.csswg.org/css-box/#propdef-margin",
allowed_in_page_rule=True,
allow_quirks=True)}
${helpers.four_sides_shorthand(
"margin",
"margin-%s",
"specified::LengthPercentageOrAuto::parse",
spec="https://drafts.csswg.org/css-box/#propdef-margin",
allowed_in_page_rule=True,
allow_quirks=True,
)}
% for axis in ["block", "inline"]:
<%
spec = "https://drafts.csswg.org/css-logical/#propdef-margin-%s" % axis
%>
<%helpers:shorthand
name="margin-${axis}"
sub_properties="${' '.join(
'margin-%s-%s' % (axis, side)
for side in ['start', 'end']
)}"
spec="${spec}">
${helpers.two_properties_shorthand(
"margin-block",
"margin-block-start",
"margin-block-end",
"specified::LengthPercentageOrAuto::parse",
spec="https://drafts.csswg.org/css-logical/#propdef-margin-block"
)}
use crate::parser::Parse;
use crate::values::specified::length::LengthPercentageOrAuto;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let start_value = LengthPercentageOrAuto::parse(context, input)?;
let end_value =
input.try(|input| LengthPercentageOrAuto::parse(context, input))
.unwrap_or_else(|_| start_value.clone());
Ok(expanded! {
margin_${axis}_start: start_value,
margin_${axis}_end: end_value,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.margin_${axis}_start.to_css(dest)?;
if self.margin_${axis}_end != self.margin_${axis}_start {
dest.write_str(" ")?;
self.margin_${axis}_end.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
% endfor
${helpers.two_properties_shorthand(
"margin-inline",
"margin-inline-start",
"margin-inline-end",
"specified::LengthPercentageOrAuto::parse",
spec="https://drafts.csswg.org/css-logical/#propdef-margin-inline"
)}

View file

@ -4,50 +4,26 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
${helpers.four_sides_shorthand("padding", "padding-%s", "specified::NonNegativeLengthPercentage::parse",
spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
allow_quirks=True)}
${helpers.four_sides_shorthand(
"padding",
"padding-%s",
"specified::NonNegativeLengthPercentage::parse",
spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
allow_quirks=True,
)}
% for axis in ["block", "inline"]:
<%
spec = "https://drafts.csswg.org/css-logical/#propdef-padding-%s" % axis
%>
<%helpers:shorthand
name="padding-${axis}"
sub_properties="${' '.join(
'padding-%s-%s' % (axis, side)
for side in ['start', 'end']
)}"
spec="${spec}">
${helpers.two_properties_shorthand(
"padding-block",
"padding-block-start",
"padding-block-end",
"specified::NonNegativeLengthPercentage::parse",
spec="https://drafts.csswg.org/css-logical/#propdef-padding-block"
)}
use crate::parser::Parse;
use crate::values::specified::length::NonNegativeLengthPercentage;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let start_value = NonNegativeLengthPercentage::parse(context, input)?;
let end_value =
input.try(|input| NonNegativeLengthPercentage::parse(context, input))
.unwrap_or_else(|_| start_value.clone());
Ok(expanded! {
padding_${axis}_start: start_value,
padding_${axis}_end: end_value,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.padding_${axis}_start.to_css(dest)?;
if self.padding_${axis}_end != self.padding_${axis}_start {
dest.write_str(" ")?;
self.padding_${axis}_end.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
% endfor
${helpers.two_properties_shorthand(
"padding-inline",
"padding-inline-start",
"padding-inline-end",
"specified::NonNegativeLengthPercentage::parse",
spec="https://drafts.csswg.org/css-logical/#propdef-padding-inline"
)}

View file

@ -763,46 +763,27 @@
}
</%helpers:shorthand>
% for axis in ["block", "inline"]:
<%
spec = "https://drafts.csswg.org/css-logical/#propdef-inset-%s" % axis
%>
<%helpers:shorthand
name="inset-${axis}"
sub_properties="${' '.join(
'inset-%s-%s' % (axis, side)
for side in ['start', 'end']
)}"
spec="${spec}">
// See https://github.com/w3c/csswg-drafts/issues/3525 for the quirks stuff.
${helpers.four_sides_shorthand(
"inset",
"%s",
"specified::LengthPercentageOrAuto::parse",
spec="https://drafts.csswg.org/css-logical/#propdef-inset",
allow_quirks=False,
)}
use crate::parser::Parse;
use crate::values::specified::length::LengthPercentageOrAuto;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let start_value = LengthPercentageOrAuto::parse(context, input)?;
let end_value =
input.try(|input| LengthPercentageOrAuto::parse(context, input))
.unwrap_or_else(|_| start_value.clone());
${helpers.two_properties_shorthand(
"inset-block",
"inset-block-start",
"inset-block-end",
"specified::LengthPercentageOrAuto::parse",
spec="https://drafts.csswg.org/css-logical/#propdef-inset-block"
)}
Ok(expanded! {
inset_${axis}_start: start_value,
inset_${axis}_end: end_value,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.inset_${axis}_start.to_css(dest)?;
if self.inset_${axis}_end != self.inset_${axis}_start {
dest.write_str(" ")?;
self.inset_${axis}_end.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
% endfor
${helpers.two_properties_shorthand(
"inset-inline",
"inset-inline-start",
"inset-inline-end",
"specified::LengthPercentageOrAuto::parse",
spec="https://drafts.csswg.org/css-logical/#propdef-inset-inline"
)}

View file

@ -9,13 +9,12 @@ use crate::values::CSSFloat;
use num_traits::Zero;
use std::f64::consts::PI;
use std::fmt::{self, Write};
use std::ops::Add;
use std::{f32, f64};
use style_traits::{CssWriter, ToCss};
/// A computed angle in degrees.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToAnimatedZero)]
#[derive(Add, Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToAnimatedZero)]
pub struct Angle(CSSFloat);
impl ToCss for Angle {
@ -66,15 +65,6 @@ impl Angle {
}
}
impl Add for Angle {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Angle(self.0 + rhs.0)
}
}
impl Zero for Angle {
#[inline]
fn zero() -> Self {

View file

@ -38,7 +38,17 @@ pub type Perspective = GenericPerspective<NonNegativeLength>;
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss,
Clone,
Copy,
Debug,
Eq,
FromPrimitive,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
)]
#[repr(u8)]
/// A computed value for the `float` property.
@ -97,7 +107,17 @@ impl ToComputedValue for SpecifiedFloat {
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss,
Clone,
Copy,
Debug,
Eq,
FromPrimitive,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
)]
/// A computed value for the `clear` property.
pub enum Clear {

View file

@ -31,7 +31,7 @@ pub trait ComputeSquaredDistance {
}
/// A distance between two animatable values.
#[derive(Clone, Copy, Debug)]
#[derive(Add, Clone, Copy, Debug, From)]
pub struct SquaredDistance {
value: f64,
}
@ -114,24 +114,6 @@ impl SquaredDistance {
}
}
impl From<SquaredDistance> for f64 {
#[inline]
fn from(distance: SquaredDistance) -> Self {
distance.value
}
}
impl Add for SquaredDistance {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
SquaredDistance {
value: self.value + rhs.value,
}
}
}
impl Sum for SquaredDistance {
fn sum<I>(iter: I) -> Self
where

View file

@ -30,6 +30,7 @@ use style_traits::{CssWriter, ParseError, ToCss};
Copy,
Debug,
Eq,
FromPrimitive,
MallocSizeOf,
Ord,
Parse,

View file

@ -11,7 +11,6 @@ use crate::values::CSSFloat;
use cssparser::Parser;
use std::fmt::{self, Write};
use std::iter::{Cloned, Peekable};
use std::ops::AddAssign;
use std::slice;
use style_traits::values::SequenceWriter;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
@ -491,6 +490,7 @@ impl IsAbsolute {
/// The path coord type.
#[derive(
AddAssign,
Animate,
Clone,
ComputeSquaredDistance,
@ -513,14 +513,6 @@ impl CoordPair {
}
}
impl AddAssign for CoordPair {
#[inline]
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
self.1 += other.1;
}
}
/// The EllipticalArc flag type.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
#[repr(C)]