зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 72858bb2ed9d (bug 1351427) for leaks detected by Linux x64 asan. r=backout
This commit is contained in:
Родитель
7f8a023f1e
Коммит
85e118ae7d
|
@ -8,12 +8,9 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "mozilla/intl/LocaleService.h"
|
||||
#include "OSPreferences.h"
|
||||
#include "mozIOSPreferences.h"
|
||||
#include "unicode/udatpg.h"
|
||||
|
||||
namespace mozilla {
|
||||
using namespace mozilla::intl;
|
||||
|
||||
nsCString* DateTimeFormat::mLocale = nullptr;
|
||||
|
||||
|
@ -87,115 +84,48 @@ DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// Get the date style for the formatter.
|
||||
nsAutoString skeletonDate;
|
||||
nsAutoString patternDate;
|
||||
bool haveSkeleton = true;
|
||||
// Get the date style for the formatter:
|
||||
UDateFormatStyle dateStyle;
|
||||
switch (aDateFormatSelector) {
|
||||
case kDateFormatLong:
|
||||
rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleLong,
|
||||
mozIOSPreferences::dateTimeFormatStyleNone,
|
||||
nsDependentCString(mLocale->get()),
|
||||
patternDate);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
haveSkeleton = false;
|
||||
dateStyle = UDAT_LONG;
|
||||
break;
|
||||
case kDateFormatShort:
|
||||
rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleShort,
|
||||
mozIOSPreferences::dateTimeFormatStyleNone,
|
||||
nsDependentCString(mLocale->get()),
|
||||
patternDate);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
haveSkeleton = false;
|
||||
dateStyle = UDAT_SHORT;
|
||||
break;
|
||||
case kDateFormatYearMonth:
|
||||
skeletonDate.AssignLiteral("yyyyMM");
|
||||
break;
|
||||
case kDateFormatYearMonthLong:
|
||||
skeletonDate.AssignLiteral("yyyyMMMM");
|
||||
break;
|
||||
case kDateFormatMonthLong:
|
||||
skeletonDate.AssignLiteral("MMMM");
|
||||
break;
|
||||
case kDateFormatWeekday:
|
||||
skeletonDate.AssignLiteral("EEE");
|
||||
dateStyle = UDAT_PATTERN;
|
||||
break;
|
||||
case kDateFormatNone:
|
||||
haveSkeleton = false;
|
||||
dateStyle = UDAT_NONE;
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Unknown nsDateFormatSelector");
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
if (haveSkeleton) {
|
||||
// Get pattern for skeleton.
|
||||
UDateTimePatternGenerator* patternGenerator = udatpg_open(mLocale->get(), &status);
|
||||
if (U_SUCCESS(status)) {
|
||||
int32_t patternLength;
|
||||
patternDate.SetLength(DATETIME_FORMAT_INITIAL_LEN);
|
||||
patternLength = udatpg_getBestPattern(patternGenerator,
|
||||
reinterpret_cast<const UChar*>(skeletonDate.BeginReading()),
|
||||
skeletonDate.Length(),
|
||||
reinterpret_cast<UChar*>(patternDate.BeginWriting()),
|
||||
DATETIME_FORMAT_INITIAL_LEN,
|
||||
&status);
|
||||
patternDate.SetLength(patternLength);
|
||||
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
udatpg_getBestPattern(patternGenerator,
|
||||
reinterpret_cast<const UChar*>(skeletonDate.BeginReading()),
|
||||
skeletonDate.Length(),
|
||||
reinterpret_cast<UChar*>(patternDate.BeginWriting()),
|
||||
patternLength,
|
||||
&status);
|
||||
}
|
||||
}
|
||||
udatpg_close(patternGenerator);
|
||||
}
|
||||
|
||||
// Get the time style for the formatter.
|
||||
nsAutoString patternTime;
|
||||
// Get the time style for the formatter:
|
||||
UDateFormatStyle timeStyle;
|
||||
switch (aTimeFormatSelector) {
|
||||
case kTimeFormatSeconds:
|
||||
rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone,
|
||||
mozIOSPreferences::dateTimeFormatStyleLong,
|
||||
nsDependentCString(mLocale->get()),
|
||||
patternTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
timeStyle = UDAT_MEDIUM;
|
||||
break;
|
||||
case kTimeFormatNoSeconds:
|
||||
rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone,
|
||||
mozIOSPreferences::dateTimeFormatStyleShort,
|
||||
nsDependentCString(mLocale->get()),
|
||||
patternTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
timeStyle = UDAT_SHORT;
|
||||
break;
|
||||
case kTimeFormatNone:
|
||||
timeStyle = UDAT_NONE;
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Unknown nsTimeFormatSelector");
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
nsAutoString pattern;
|
||||
if (patternTime.Length() == 0) {
|
||||
pattern.Assign(patternDate);
|
||||
} else if (patternDate.Length() == 0) {
|
||||
pattern.Assign(patternTime);
|
||||
} else {
|
||||
OSPreferences::GetDateTimeConnectorPattern(nsDependentCString(mLocale->get()), pattern);
|
||||
int32_t index = pattern.Find("{1}");
|
||||
if (index != kNotFound)
|
||||
pattern.Replace(index, 3, patternDate);
|
||||
index = pattern.Find("{0}");
|
||||
if (index != kNotFound)
|
||||
pattern.Replace(index, 3, patternTime);
|
||||
}
|
||||
// generate date/time string
|
||||
|
||||
// Generate date/time string.
|
||||
nsAutoString timeZoneID(u"GMT");
|
||||
if (aTimeParameters) {
|
||||
int32_t totalOffsetMinutes = (aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) / 60;
|
||||
|
@ -207,38 +137,100 @@ DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
}
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
UDateFormat* dateTimeFormat;
|
||||
if (dateStyle == UDAT_PATTERN) {
|
||||
nsAutoString pattern;
|
||||
|
||||
dateTimeFormat = udat_open(timeStyle, UDAT_NONE, mLocale->get(), nullptr, -1, nullptr, -1, &status);
|
||||
|
||||
if (U_SUCCESS(status) && dateTimeFormat) {
|
||||
int32_t patternLength;
|
||||
if (timeStyle != UDAT_NONE) {
|
||||
pattern.SetLength(DATETIME_FORMAT_INITIAL_LEN);
|
||||
patternLength = udat_toPattern(dateTimeFormat, FALSE, reinterpret_cast<UChar*>(pattern.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, &status);
|
||||
pattern.SetLength(patternLength);
|
||||
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
udat_toPattern(dateTimeFormat, FALSE, reinterpret_cast<UChar*>(pattern.BeginWriting()), patternLength, &status);
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString skeleton;
|
||||
switch (aDateFormatSelector) {
|
||||
case kDateFormatYearMonth:
|
||||
skeleton.AssignLiteral("yyyyMM ");
|
||||
break;
|
||||
case kDateFormatYearMonthLong:
|
||||
skeleton.AssignLiteral("yyyyMMMM ");
|
||||
break;
|
||||
case kDateFormatMonthLong:
|
||||
skeleton.AssignLiteral("MMMM ");
|
||||
break;
|
||||
case kDateFormatWeekday:
|
||||
skeleton.AssignLiteral("EEE ");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int32_t dateSkeletonLen = skeleton.Length();
|
||||
|
||||
if (timeStyle != UDAT_NONE) {
|
||||
skeleton.SetLength(DATETIME_FORMAT_INITIAL_LEN);
|
||||
int32_t skeletonLength = udatpg_getSkeleton(nullptr, reinterpret_cast<const UChar*>(pattern.BeginReading()), patternLength,
|
||||
reinterpret_cast<UChar*>(skeleton.BeginWriting() + dateSkeletonLen), DATETIME_FORMAT_INITIAL_LEN - dateSkeletonLen, &status);
|
||||
skeleton.SetLength(dateSkeletonLen + skeletonLength);
|
||||
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
udatpg_getSkeleton(nullptr, reinterpret_cast<const UChar*>(pattern.BeginReading()), patternLength,
|
||||
reinterpret_cast<UChar*>(skeleton.BeginWriting() + dateSkeletonLen), dateSkeletonLen + skeletonLength, &status);
|
||||
}
|
||||
}
|
||||
|
||||
UDateTimePatternGenerator* patternGenerator = udatpg_open(mLocale->get(), &status);
|
||||
if (U_SUCCESS(status)) {
|
||||
pattern.SetLength(DATETIME_FORMAT_INITIAL_LEN);
|
||||
patternLength = udatpg_getBestPattern(patternGenerator, reinterpret_cast<const UChar*>(skeleton.BeginReading()), skeleton.Length(),
|
||||
reinterpret_cast<UChar*>(pattern.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, &status);
|
||||
pattern.SetLength(patternLength);
|
||||
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
udatpg_getBestPattern(patternGenerator, reinterpret_cast<const UChar*>(skeleton.BeginReading()), skeleton.Length(),
|
||||
reinterpret_cast<UChar*>(pattern.BeginWriting()), patternLength, &status);
|
||||
}
|
||||
}
|
||||
|
||||
udatpg_close(patternGenerator);
|
||||
}
|
||||
|
||||
udat_close(dateTimeFormat);
|
||||
|
||||
if (aTimeParameters) {
|
||||
dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(),
|
||||
reinterpret_cast<const UChar*>(timeZoneID.BeginReading()),
|
||||
timeZoneID.Length(),
|
||||
reinterpret_cast<const UChar*>(pattern.BeginReading()),
|
||||
pattern.Length(),
|
||||
&status);
|
||||
dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), reinterpret_cast<const UChar*>(timeZoneID.BeginReading()), timeZoneID.Length(),
|
||||
reinterpret_cast<const UChar*>(pattern.BeginReading()), pattern.Length(), &status);
|
||||
} else {
|
||||
dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(),
|
||||
nullptr, -1,
|
||||
reinterpret_cast<const UChar*>(pattern.BeginReading()),
|
||||
pattern.Length(),
|
||||
&status);
|
||||
dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), nullptr, -1, reinterpret_cast<const UChar*>(pattern.BeginReading()), pattern.Length(), &status);
|
||||
}
|
||||
} else {
|
||||
if (aTimeParameters) {
|
||||
dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), reinterpret_cast<const UChar*>(timeZoneID.BeginReading()), timeZoneID.Length(), nullptr, -1, &status);
|
||||
} else {
|
||||
dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), nullptr, -1, nullptr, -1, &status);
|
||||
}
|
||||
}
|
||||
|
||||
if (U_SUCCESS(status) && dateTimeFormat) {
|
||||
aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN);
|
||||
dateTimeLen = udat_format(dateTimeFormat, aUDateTime,
|
||||
reinterpret_cast<UChar*>(aStringOut.BeginWriting()),
|
||||
DATETIME_FORMAT_INITIAL_LEN,
|
||||
nullptr,
|
||||
&status);
|
||||
dateTimeLen = udat_format(dateTimeFormat, aUDateTime, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, nullptr, &status);
|
||||
aStringOut.SetLength(dateTimeLen);
|
||||
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
udat_format(dateTimeFormat, aUDateTime,
|
||||
reinterpret_cast<UChar*>(aStringOut.BeginWriting()),
|
||||
dateTimeLen,
|
||||
nullptr,
|
||||
&status);
|
||||
udat_format(dateTimeFormat, aUDateTime, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), dateTimeLen, nullptr, &status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,9 +101,6 @@ public:
|
|||
*/
|
||||
bool GetSystemLocales(nsTArray<nsCString>& aRetVal);
|
||||
|
||||
static bool GetDateTimeConnectorPattern(const nsACString& aLocale,
|
||||
nsAString& aRetVal);
|
||||
|
||||
protected:
|
||||
nsTArray<nsCString> mSystemLocales;
|
||||
|
||||
|
@ -132,6 +129,9 @@ private:
|
|||
const nsACString& aLocale,
|
||||
nsAString& aRetVal);
|
||||
|
||||
bool GetDateTimeConnectorPattern(const nsACString& aLocale,
|
||||
nsAString& aRetVal);
|
||||
|
||||
/**
|
||||
* This is a host environment specific method that will be implemented
|
||||
* separately for each platform.
|
||||
|
|
|
@ -3,32 +3,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
// Normalise time.
|
||||
static nsAutoCString nt(nsAutoCString aDatetime)
|
||||
{
|
||||
nsAutoCString datetime = aDatetime;
|
||||
|
||||
// Replace "January 01" with "January 1" (found on Windows).
|
||||
int32_t ind = datetime.Find("January 01");
|
||||
if (ind != kNotFound)
|
||||
datetime.Replace(ind, 10, "January 1");
|
||||
|
||||
// Strip trailing " GMT" (found on Mac/Linux).
|
||||
ind = datetime.Find(" GMT");
|
||||
if (ind != kNotFound)
|
||||
datetime.Truncate(ind);
|
||||
|
||||
// Strip leading "Thursday, " or "Wednesday, " (found on Windows).
|
||||
ind = datetime.Find("Thursday, ");
|
||||
if (ind == 0)
|
||||
datetime.Replace(0, 10, "");
|
||||
ind = datetime.Find("Wednesday, ");
|
||||
if (ind == 0)
|
||||
datetime.Replace(0, 11, "");
|
||||
|
||||
return datetime;
|
||||
}
|
||||
|
||||
TEST(DateTimeFormat, FormatPRExplodedTime) {
|
||||
PRTime prTime = 0;
|
||||
PRExplodedTime prExplodedTime;
|
||||
|
@ -39,37 +13,37 @@ TEST(DateTimeFormat, FormatPRExplodedTime) {
|
|||
nsAutoString formattedTime;
|
||||
nsresult rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970, 12:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("January 1, 1970 at 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 19, 0, 1, 0, 1970, 4, 0, { (19 * 60), 0 } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970, 12:19:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("January 1, 1970 at 12:19:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 0, 7, 1, 0, 1970, 4, 0, { (6 * 60 * 60), (1 * 60 * 60) } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970, 7:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("January 1, 1970 at 7:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 29, 11, 1, 0, 1970, 4, 0, { (10 * 60 * 60) + (29 * 60), (1 * 60 * 60) } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1, 1970, 11:29:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("January 1, 1970 at 11:29:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 37, 23, 31, 11, 1969, 3, 364, { -(23 * 60), 0 } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("December 31, 1969, 11:37:00 PM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("December 31, 1969 at 11:37:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 0, 17, 31, 11, 1969, 3, 364, { -(7 * 60 * 60), 0 } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("December 31, 1969, 5:00:00 PM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("December 31, 1969 at 5:00:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
prExplodedTime = { 0, 0, 47, 14, 31, 11, 1969, 3, 364, { -((10 * 60 * 60) + (13 * 60)), (1 * 60 * 60) } };
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("December 31, 1969, 2:47:00 PM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("December 31, 1969 at 2:47:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
}
|
||||
|
||||
TEST(DateTimeFormat, DateFormatSelectors) {
|
||||
|
@ -94,11 +68,11 @@ TEST(DateTimeFormat, DateFormatSelectors) {
|
|||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatNoSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("01/1970, 12:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("01/1970, 12:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("01/1970, 12:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("01/1970, 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatWeekday, kTimeFormatNone, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
@ -106,11 +80,11 @@ TEST(DateTimeFormat, DateFormatSelectors) {
|
|||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatWeekday, kTimeFormatNoSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("Thu, 12:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("Thu 12:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatWeekday, kTimeFormatSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("Thu, 12:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get());
|
||||
ASSERT_STREQ("Thu 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче