зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #14740 - Add support for keyword values for min-width and max-width (from glasserc:extremum-length); r=Manishearth
This is a follow-up to #14432 which got closed and can no longer be re-opened. This PR aims to add support for keyword-values max-content, min-content, fit-content, and fill-available to the properties that use them, namely min-width, min-height, max-width, and max-height. It's still untested because I still haven't figured out how to do that. I guess I should write or find some web page that uses these properties. Refs #13821. <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #13821 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 26de7c6bc48affbc2087b32649850f0733e567f0 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 5ad2826be45174e7a1ceaa739d0fc5ce1e3975be
This commit is contained in:
Родитель
428143ec78
Коммит
633be3efd1
|
@ -11,9 +11,10 @@ use cssparser::RGBA;
|
|||
use gecko_bindings::structs::{nsStyleCoord, StyleGridTrackBreadth, StyleShapeRadius};
|
||||
use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
|
||||
use std::cmp::max;
|
||||
use values::{Auto, Either, None_, Normal};
|
||||
use values::{Auto, Either, ExtremumLength, None_, Normal};
|
||||
use values::computed::{Angle, LengthOrPercentageOrNone, Number};
|
||||
use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
use values::computed::{MaxLength, MinLength};
|
||||
use values::computed::basic_shape::ShapeRadius;
|
||||
use values::specified::grid::{TrackBreadth, TrackKeyword};
|
||||
|
||||
|
@ -271,6 +272,74 @@ impl GeckoStyleCoordConvertible for Normal {
|
|||
}
|
||||
}
|
||||
|
||||
impl GeckoStyleCoordConvertible for ExtremumLength {
|
||||
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
|
||||
use gecko_bindings::structs::{NS_STYLE_WIDTH_AVAILABLE, NS_STYLE_WIDTH_FIT_CONTENT};
|
||||
use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT};
|
||||
coord.set_value(CoordDataValue::Enumerated(
|
||||
match *self {
|
||||
ExtremumLength::MaxContent => NS_STYLE_WIDTH_MAX_CONTENT,
|
||||
ExtremumLength::MinContent => NS_STYLE_WIDTH_MIN_CONTENT,
|
||||
ExtremumLength::FitContent => NS_STYLE_WIDTH_FIT_CONTENT,
|
||||
ExtremumLength::FillAvailable => NS_STYLE_WIDTH_AVAILABLE,
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
|
||||
use gecko_bindings::structs::{NS_STYLE_WIDTH_AVAILABLE, NS_STYLE_WIDTH_FIT_CONTENT};
|
||||
use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT};
|
||||
match coord.as_value() {
|
||||
CoordDataValue::Enumerated(NS_STYLE_WIDTH_MAX_CONTENT) =>
|
||||
Some(ExtremumLength::MaxContent),
|
||||
CoordDataValue::Enumerated(NS_STYLE_WIDTH_MIN_CONTENT) =>
|
||||
Some(ExtremumLength::MinContent),
|
||||
CoordDataValue::Enumerated(NS_STYLE_WIDTH_FIT_CONTENT) =>
|
||||
Some(ExtremumLength::FitContent),
|
||||
CoordDataValue::Enumerated(NS_STYLE_WIDTH_AVAILABLE) => Some(ExtremumLength::FillAvailable),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GeckoStyleCoordConvertible for MinLength {
|
||||
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
|
||||
match *self {
|
||||
MinLength::LengthOrPercentage(ref lop) => lop.to_gecko_style_coord(coord),
|
||||
MinLength::Auto => coord.set_value(CoordDataValue::Auto),
|
||||
MinLength::ExtremumLength(ref e) => e.to_gecko_style_coord(coord),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
|
||||
LengthOrPercentage::from_gecko_style_coord(coord).map(MinLength::LengthOrPercentage)
|
||||
.or_else(|| ExtremumLength::from_gecko_style_coord(coord).map(MinLength::ExtremumLength))
|
||||
.or_else(|| match coord.as_value() {
|
||||
CoordDataValue::Auto => Some(MinLength::Auto),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GeckoStyleCoordConvertible for MaxLength {
|
||||
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
|
||||
match *self {
|
||||
MaxLength::LengthOrPercentage(ref lop) => lop.to_gecko_style_coord(coord),
|
||||
MaxLength::None => coord.set_value(CoordDataValue::None),
|
||||
MaxLength::ExtremumLength(ref e) => e.to_gecko_style_coord(coord),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
|
||||
LengthOrPercentage::from_gecko_style_coord(coord).map(MaxLength::LengthOrPercentage)
|
||||
.or_else(|| ExtremumLength::from_gecko_style_coord(coord).map(MaxLength::ExtremumLength))
|
||||
.or_else(|| match coord.as_value() {
|
||||
CoordDataValue::None => Some(MaxLength::None),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a given RGBA value to `nscolor`.
|
||||
pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 {
|
||||
((rgba.alpha as u32) << 24) |
|
||||
|
|
|
@ -635,6 +635,8 @@ impl Debug for ${style_struct.gecko_struct_name} {
|
|||
"LengthOrPercentageOrAuto": impl_style_coord,
|
||||
"LengthOrPercentageOrNone": impl_style_coord,
|
||||
"LengthOrNone": impl_style_coord,
|
||||
"MaxLength": impl_style_coord,
|
||||
"MinLength": impl_style_coord,
|
||||
"Number": impl_simple,
|
||||
"Opacity": impl_simple,
|
||||
"CSSColor": impl_color,
|
||||
|
|
|
@ -33,6 +33,7 @@ use values::Either;
|
|||
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||
use values::computed::{BorderRadiusSize, ClipRect, LengthOrNone};
|
||||
use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
|
||||
use values::computed::{MaxLength, MinLength};
|
||||
use values::computed::position::{HorizontalPosition, Position, VerticalPosition};
|
||||
use values::computed::ToComputedValue;
|
||||
use values::specified::Angle as SpecifiedAngle;
|
||||
|
@ -630,6 +631,34 @@ impl Interpolate for LengthOrPercentageOrNone {
|
|||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Interpolate for MinLength {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(MinLength::LengthOrPercentage(ref this),
|
||||
MinLength::LengthOrPercentage(ref other)) => {
|
||||
this.interpolate(other, progress).map(MinLength::LengthOrPercentage)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Interpolate for MaxLength {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(MaxLength::LengthOrPercentage(ref this),
|
||||
MaxLength::LengthOrPercentage(ref other)) => {
|
||||
this.interpolate(other, progress).map(MaxLength::LengthOrPercentage)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-length
|
||||
impl Interpolate for LineHeight {
|
||||
|
|
|
@ -218,23 +218,40 @@ ${helpers.predefined_type("flex-basis",
|
|||
spec=spec % size,
|
||||
animatable=True, logical = logical)}
|
||||
|
||||
// min-width, min-height, min-block-size, min-inline-size
|
||||
${helpers.predefined_type("min-%s" % size,
|
||||
"LengthOrPercentage",
|
||||
"computed::LengthOrPercentage::Length(Au(0))",
|
||||
"parse_non_negative",
|
||||
needs_context=False,
|
||||
spec=spec % ("min-%s" % size),
|
||||
animatable=True, logical = logical)}
|
||||
% if product == "gecko":
|
||||
// min-width, min-height, min-block-size, min-inline-size
|
||||
${helpers.predefined_type("min-%s" % size,
|
||||
"MinLength",
|
||||
"computed::MinLength::LengthOrPercentage(" +
|
||||
"computed::LengthOrPercentage::Length(Au(0)))",
|
||||
spec=spec % ("min-%s" % size),
|
||||
animatable=True, logical = logical)}
|
||||
% else:
|
||||
${helpers.predefined_type("min-%s" % size,
|
||||
"LengthOrPercentage",
|
||||
"computed::LengthOrPercentage::Length(Au(0))",
|
||||
"parse_non_negative",
|
||||
needs_context=False,
|
||||
spec=spec % ("min-%s" % size),
|
||||
animatable=True, logical = logical)}
|
||||
% endif
|
||||
|
||||
// max-width, max-height, max-block-size, max-inline-size
|
||||
${helpers.predefined_type("max-%s" % size,
|
||||
"LengthOrPercentageOrNone",
|
||||
"computed::LengthOrPercentageOrNone::None",
|
||||
"parse_non_negative",
|
||||
needs_context=False,
|
||||
spec=spec % ("max-%s" % size),
|
||||
animatable=True, logical = logical)}
|
||||
% if product == "gecko":
|
||||
${helpers.predefined_type("max-%s" % size,
|
||||
"MaxLength",
|
||||
"computed::MaxLength::None",
|
||||
spec=spec % ("max-%s" % size),
|
||||
animatable=True, logical = logical)}
|
||||
% else:
|
||||
${helpers.predefined_type("max-%s" % size,
|
||||
"LengthOrPercentageOrNone",
|
||||
"computed::LengthOrPercentageOrNone::None",
|
||||
"parse_non_negative",
|
||||
needs_context=False,
|
||||
spec=spec % ("max-%s" % size),
|
||||
animatable=True, logical = logical)}
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
${helpers.single_keyword("box-sizing",
|
||||
|
|
|
@ -9,7 +9,7 @@ use ordered_float::NotNaN;
|
|||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use super::{Number, ToComputedValue, Context};
|
||||
use values::{Auto, CSSFloat, Either, None_, Normal, specified};
|
||||
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
|
||||
use values::specified::length::{FontRelativeLength, ViewportPercentageLength};
|
||||
|
||||
pub use cssparser::Color as CSSColor;
|
||||
|
@ -546,3 +546,113 @@ pub type LengthOrNumber = Either<Length, Number>;
|
|||
|
||||
/// Either a computed `<length>` or the `normal` keyword.
|
||||
pub type LengthOrNormal = Either<Length, Normal>;
|
||||
|
||||
/// A value suitable for a `min-width` or `min-height` property.
|
||||
/// See specified/values/length.rs for more details.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MinLength {
|
||||
LengthOrPercentage(LengthOrPercentage),
|
||||
Auto,
|
||||
ExtremumLength(ExtremumLength),
|
||||
}
|
||||
|
||||
impl ToComputedValue for specified::MinLength {
|
||||
type ComputedValue = MinLength;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> MinLength {
|
||||
match *self {
|
||||
specified::MinLength::LengthOrPercentage(ref lop) => {
|
||||
MinLength::LengthOrPercentage(lop.to_computed_value(context))
|
||||
}
|
||||
specified::MinLength::Auto => {
|
||||
MinLength::Auto
|
||||
}
|
||||
specified::MinLength::ExtremumLength(ref ext) => {
|
||||
MinLength::ExtremumLength(ext.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &MinLength) -> Self {
|
||||
match *computed {
|
||||
MinLength::Auto =>
|
||||
specified::MinLength::Auto,
|
||||
MinLength::LengthOrPercentage(ref lop) =>
|
||||
specified::MinLength::LengthOrPercentage(specified::LengthOrPercentage::from_computed_value(&lop)),
|
||||
MinLength::ExtremumLength(ref ext) =>
|
||||
specified::MinLength::ExtremumLength(ext.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for MinLength {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
MinLength::LengthOrPercentage(lop) =>
|
||||
lop.to_css(dest),
|
||||
MinLength::Auto =>
|
||||
dest.write_str("auto"),
|
||||
MinLength::ExtremumLength(ext) =>
|
||||
ext.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A value suitable for a `max-width` or `max-height` property.
|
||||
/// See specified/values/length.rs for more details.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MaxLength {
|
||||
LengthOrPercentage(LengthOrPercentage),
|
||||
None,
|
||||
ExtremumLength(ExtremumLength),
|
||||
}
|
||||
|
||||
impl ToComputedValue for specified::MaxLength {
|
||||
type ComputedValue = MaxLength;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> MaxLength {
|
||||
match *self {
|
||||
specified::MaxLength::LengthOrPercentage(ref lop) => {
|
||||
MaxLength::LengthOrPercentage(lop.to_computed_value(context))
|
||||
}
|
||||
specified::MaxLength::None => {
|
||||
MaxLength::None
|
||||
}
|
||||
specified::MaxLength::ExtremumLength(ref ext) => {
|
||||
MaxLength::ExtremumLength(ext.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &MaxLength) -> Self {
|
||||
match *computed {
|
||||
MaxLength::None =>
|
||||
specified::MaxLength::None,
|
||||
MaxLength::LengthOrPercentage(ref lop) =>
|
||||
specified::MaxLength::LengthOrPercentage(specified::LengthOrPercentage::from_computed_value(&lop)),
|
||||
MaxLength::ExtremumLength(ref ext) =>
|
||||
specified::MaxLength::ExtremumLength(ext.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for MaxLength {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
MaxLength::LengthOrPercentage(lop) =>
|
||||
lop.to_css(dest),
|
||||
MaxLength::None =>
|
||||
dest.write_str("none"),
|
||||
MaxLength::ExtremumLength(ext) =>
|
||||
ext.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ pub use super::specified::{Angle, BorderStyle, GridLine, Time, UrlOrNone};
|
|||
pub use super::specified::url::{SpecifiedUrl, UrlExtraData};
|
||||
pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone};
|
||||
pub use self::length::{MaxLength, MinLength};
|
||||
pub use self::position::Position;
|
||||
|
||||
pub mod basic_shape;
|
||||
|
|
|
@ -185,3 +185,11 @@ impl<A: ToComputedValue, B: ToComputedValue> ToComputedValue for Either<A, B> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A type for possible values for min- and max- flavors of width,
|
||||
// height, block-size, and inline-size.
|
||||
define_css_keyword_enum!(ExtremumLength:
|
||||
"max-content" => MaxContent,
|
||||
"min-content" => MinContent,
|
||||
"fit-content" => FitContent,
|
||||
"fill-available" => FillAvailable);
|
||||
|
|
|
@ -18,6 +18,7 @@ use style_traits::ToCss;
|
|||
use style_traits::values::specified::AllowedNumericType;
|
||||
use super::{Angle, Number, SimplifiedValueNode, SimplifiedSumNode, Time};
|
||||
use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal};
|
||||
use values::ExtremumLength;
|
||||
use values::computed::Context;
|
||||
|
||||
pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
|
||||
|
@ -1304,3 +1305,97 @@ impl LengthOrNumber {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A value suitable for a `min-width` or `min-height` property.
|
||||
/// Unlike `max-width` or `max-height` properties, a MinLength can be
|
||||
/// `auto`, and cannot be `none`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MinLength {
|
||||
LengthOrPercentage(LengthOrPercentage),
|
||||
Auto,
|
||||
ExtremumLength(ExtremumLength),
|
||||
}
|
||||
|
||||
impl HasViewportPercentage for MinLength {
|
||||
fn has_viewport_percentage(&self) -> bool {
|
||||
match *self {
|
||||
MinLength::LengthOrPercentage(ref lop) => lop.has_viewport_percentage(),
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for MinLength {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
MinLength::LengthOrPercentage(ref lop) =>
|
||||
lop.to_css(dest),
|
||||
MinLength::Auto =>
|
||||
dest.write_str("auto"),
|
||||
MinLength::ExtremumLength(ref ext) =>
|
||||
ext.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for MinLength {
|
||||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
input.try(ExtremumLength::parse).map(MinLength::ExtremumLength)
|
||||
.or_else(|()| input.try(LengthOrPercentage::parse_non_negative).map(MinLength::LengthOrPercentage))
|
||||
.or_else(|()| {
|
||||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||||
"auto" =>
|
||||
Ok(MinLength::Auto),
|
||||
_ => Err(())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A value suitable for a `max-width` or `max-height` property.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MaxLength {
|
||||
LengthOrPercentage(LengthOrPercentage),
|
||||
None,
|
||||
ExtremumLength(ExtremumLength),
|
||||
}
|
||||
|
||||
impl HasViewportPercentage for MaxLength {
|
||||
fn has_viewport_percentage(&self) -> bool {
|
||||
match *self {
|
||||
MaxLength::LengthOrPercentage(ref lop) => lop.has_viewport_percentage(),
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for MaxLength {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
MaxLength::LengthOrPercentage(ref lop) =>
|
||||
lop.to_css(dest),
|
||||
MaxLength::None =>
|
||||
dest.write_str("none"),
|
||||
MaxLength::ExtremumLength(ref ext) =>
|
||||
ext.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for MaxLength {
|
||||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
input.try(ExtremumLength::parse).map(MaxLength::ExtremumLength)
|
||||
.or_else(|()| input.try(LengthOrPercentage::parse_non_negative).map(MaxLength::LengthOrPercentage))
|
||||
.or_else(|()| {
|
||||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||||
"none" =>
|
||||
Ok(MaxLength::None),
|
||||
_ => Err(())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ pub use self::image::{SizeKeyword, VerticalDirection};
|
|||
pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage};
|
||||
pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrContent, NoCalcLength, CalcUnit};
|
||||
pub use self::length::{MaxLength, MinLength};
|
||||
pub use self::position::{HorizontalPosition, Position, VerticalPosition};
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
|
|
Загрузка…
Ссылка в новой задаче