OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 #include "core/css/CSSPrimitiveValue.h" | 56 #include "core/css/CSSPrimitiveValue.h" |
57 #include "core/css/CSSPrimitiveValueMappings.h" | 57 #include "core/css/CSSPrimitiveValueMappings.h" |
58 #include "core/css/CSSPropertyMetadata.h" | 58 #include "core/css/CSSPropertyMetadata.h" |
59 #include "core/style/ComputedStyle.h" | 59 #include "core/style/ComputedStyle.h" |
60 #include "platform/Length.h" | 60 #include "platform/Length.h" |
61 #include "platform/LengthBox.h" | 61 #include "platform/LengthBox.h" |
62 #include "wtf/StdLibExtras.h" | 62 #include "wtf/StdLibExtras.h" |
63 | 63 |
64 namespace blink { | 64 namespace blink { |
65 | 65 |
66 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthWithZoom(const Le
ngth& length, float zoom) | 66 static AnimatableValue* createFromLengthWithZoom(const Length& length, float zoo
m) |
67 { | 67 { |
68 switch (length.type()) { | 68 switch (length.type()) { |
69 case Fixed: | 69 case Fixed: |
70 case Percent: | 70 case Percent: |
71 case Calculated: | 71 case Calculated: |
72 return AnimatableLength::create(length, zoom); | 72 return AnimatableLength::create(length, zoom); |
73 case Auto: | 73 case Auto: |
74 case Intrinsic: | 74 case Intrinsic: |
75 case MinIntrinsic: | 75 case MinIntrinsic: |
76 case MinContent: | 76 case MinContent: |
77 case MaxContent: | 77 case MaxContent: |
78 case FillAvailable: | 78 case FillAvailable: |
79 case FitContent: | 79 case FitContent: |
80 return AnimatableUnknown::create(CSSPrimitiveValue::create(length, 1)); | 80 return AnimatableUnknown::create(CSSPrimitiveValue::create(length, 1)); |
81 case MaxSizeNone: | 81 case MaxSizeNone: |
82 return AnimatableUnknown::create(CSSValueNone); | 82 return AnimatableUnknown::create(CSSValueNone); |
83 case ExtendToZoom: // Does not apply to elements. | 83 case ExtendToZoom: // Does not apply to elements. |
84 case DeviceWidth: | 84 case DeviceWidth: |
85 case DeviceHeight: | 85 case DeviceHeight: |
86 ASSERT_NOT_REACHED(); | 86 ASSERT_NOT_REACHED(); |
87 return nullptr; | 87 return nullptr; |
88 } | 88 } |
89 ASSERT_NOT_REACHED(); | 89 ASSERT_NOT_REACHED(); |
90 return nullptr; | 90 return nullptr; |
91 } | 91 } |
92 | 92 |
93 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLength(const Length& le
ngth, const ComputedStyle& style) | 93 static AnimatableValue* createFromLength(const Length& length, const ComputedSty
le& style) |
94 { | 94 { |
95 return createFromLengthWithZoom(length, style.effectiveZoom()); | 95 return createFromLengthWithZoom(length, style.effectiveZoom()); |
96 } | 96 } |
97 | 97 |
98 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromUnzoomedLength(const Un
zoomedLength& unzoomedLength) | 98 static AnimatableValue* createFromUnzoomedLength(const UnzoomedLength& unzoomedL
ength) |
99 { | 99 { |
100 return createFromLengthWithZoom(unzoomedLength.length(), 1); | 100 return createFromLengthWithZoom(unzoomedLength.length(), 1); |
101 } | 101 } |
102 | 102 |
103 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLineHeight(const Length
& length, const ComputedStyle& style) | 103 static AnimatableValue* createFromLineHeight(const Length& length, const Compute
dStyle& style) |
104 { | 104 { |
105 if (length.type() == Percent) { | 105 if (length.type() == Percent) { |
106 double value = length.value(); | 106 double value = length.value(); |
107 // -100% is used to represent "normal" line height. | 107 // -100% is used to represent "normal" line height. |
108 if (value == -100) | 108 if (value == -100) |
109 return AnimatableUnknown::create(CSSValueNormal); | 109 return AnimatableUnknown::create(CSSValueNormal); |
110 return AnimatableDouble::create(value); | 110 return AnimatableDouble::create(value); |
111 } | 111 } |
112 return createFromLength(length, style); | 112 return createFromLength(length, style); |
113 } | 113 } |
114 | 114 |
115 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromDouble(double va
lue) | 115 inline static AnimatableValue* createFromDouble(double value) |
116 { | 116 { |
117 return AnimatableDouble::create(value); | 117 return AnimatableDouble::create(value); |
118 } | 118 } |
119 | 119 |
120 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthBox(const
LengthBox& lengthBox, const ComputedStyle& style) | 120 inline static AnimatableValue* createFromLengthBox(const LengthBox& lengthBox, c
onst ComputedStyle& style) |
121 { | 121 { |
122 return AnimatableLengthBox::create( | 122 return AnimatableLengthBox::create( |
123 createFromLength(lengthBox.left(), style), | 123 createFromLength(lengthBox.left(), style), |
124 createFromLength(lengthBox.right(), style), | 124 createFromLength(lengthBox.right(), style), |
125 createFromLength(lengthBox.top(), style), | 125 createFromLength(lengthBox.top(), style), |
126 createFromLength(lengthBox.bottom(), style)); | 126 createFromLength(lengthBox.bottom(), style)); |
127 } | 127 } |
128 | 128 |
129 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromBorderImageLength(const
BorderImageLength& borderImageLength, const ComputedStyle& style) | 129 static AnimatableValue* createFromBorderImageLength(const BorderImageLength& bor
derImageLength, const ComputedStyle& style) |
130 { | 130 { |
131 if (borderImageLength.isNumber()) | 131 if (borderImageLength.isNumber()) |
132 return createFromDouble(borderImageLength.number()); | 132 return createFromDouble(borderImageLength.number()); |
133 return createFromLength(borderImageLength.length(), style); | 133 return createFromLength(borderImageLength.length(), style); |
134 } | 134 } |
135 | 135 |
136 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromBorderImageLengt
hBox(const BorderImageLengthBox& borderImageLengthBox, const ComputedStyle& styl
e) | 136 inline static AnimatableValue* createFromBorderImageLengthBox(const BorderImageL
engthBox& borderImageLengthBox, const ComputedStyle& style) |
137 { | 137 { |
138 return AnimatableLengthBox::create( | 138 return AnimatableLengthBox::create( |
139 createFromBorderImageLength(borderImageLengthBox.left(), style), | 139 createFromBorderImageLength(borderImageLengthBox.left(), style), |
140 createFromBorderImageLength(borderImageLengthBox.right(), style), | 140 createFromBorderImageLength(borderImageLengthBox.right(), style), |
141 createFromBorderImageLength(borderImageLengthBox.top(), style), | 141 createFromBorderImageLength(borderImageLengthBox.top(), style), |
142 createFromBorderImageLength(borderImageLengthBox.bottom(), style)); | 142 createFromBorderImageLength(borderImageLengthBox.bottom(), style)); |
143 } | 143 } |
144 | 144 |
145 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthBoxAndBool
(const LengthBox lengthBox, const bool flag, const ComputedStyle& style) | 145 inline static AnimatableValue* createFromLengthBoxAndBool(const LengthBox length
Box, const bool flag, const ComputedStyle& style) |
146 { | 146 { |
147 return AnimatableLengthBoxAndBool::create( | 147 return AnimatableLengthBoxAndBool::create( |
148 createFromLengthBox(lengthBox, style), | 148 createFromLengthBox(lengthBox, style), |
149 flag); | 149 flag); |
150 } | 150 } |
151 | 151 |
152 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromDoubleAndBool(do
uble number, const bool flag, const ComputedStyle& style) | 152 inline static AnimatableValue* createFromDoubleAndBool(double number, const bool
flag, const ComputedStyle& style) |
153 { | 153 { |
154 return AnimatableDoubleAndBool::create(number, flag); | 154 return AnimatableDoubleAndBool::create(number, flag); |
155 } | 155 } |
156 | 156 |
157 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthPoint(cons
t LengthPoint& lengthPoint, const ComputedStyle& style) | 157 inline static AnimatableValue* createFromLengthPoint(const LengthPoint& lengthPo
int, const ComputedStyle& style) |
158 { | 158 { |
159 return AnimatableLengthPoint::create( | 159 return AnimatableLengthPoint::create( |
160 createFromLength(lengthPoint.x(), style), | 160 createFromLength(lengthPoint.x(), style), |
161 createFromLength(lengthPoint.y(), style)); | 161 createFromLength(lengthPoint.y(), style)); |
162 } | 162 } |
163 | 163 |
164 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromTransformOrigin(
const TransformOrigin& transformOrigin, const ComputedStyle& style) | 164 inline static AnimatableValue* createFromTransformOrigin(const TransformOrigin&
transformOrigin, const ComputedStyle& style) |
165 { | 165 { |
166 return AnimatableLengthPoint3D::create( | 166 return AnimatableLengthPoint3D::create( |
167 createFromLength(transformOrigin.x(), style), | 167 createFromLength(transformOrigin.x(), style), |
168 createFromLength(transformOrigin.y(), style), | 168 createFromLength(transformOrigin.y(), style), |
169 createFromDouble(transformOrigin.z())); | 169 createFromDouble(transformOrigin.z())); |
170 } | 170 } |
171 | 171 |
172 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthSize(const
LengthSize& lengthSize, const ComputedStyle& style) | 172 inline static AnimatableValue* createFromLengthSize(const LengthSize& lengthSize
, const ComputedStyle& style) |
173 { | 173 { |
174 return AnimatableLengthSize::create( | 174 return AnimatableLengthSize::create( |
175 createFromLength(lengthSize.width(), style), | 175 createFromLength(lengthSize.width(), style), |
176 createFromLength(lengthSize.height(), style)); | 176 createFromLength(lengthSize.height(), style)); |
177 } | 177 } |
178 | 178 |
179 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromStyleImage(Style
Image* image) | 179 inline static AnimatableValue* createFromStyleImage(StyleImage* image) |
180 { | 180 { |
181 if (image) { | 181 if (image) { |
182 if (RefPtrWillBeRawPtr<CSSValue> cssValue = image->cssValue()) | 182 if (RefPtrWillBeRawPtr<CSSValue> cssValue = image->cssValue()) |
183 return AnimatableImage::create(cssValue.release()); | 183 return AnimatableImage::create(cssValue.release()); |
184 } | 184 } |
185 return AnimatableUnknown::create(CSSValueNone); | 185 return AnimatableUnknown::create(CSSValueNone); |
186 } | 186 } |
187 | 187 |
188 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromFillSize(const F
illSize& fillSize, const ComputedStyle& style) | 188 inline static AnimatableValue* createFromFillSize(const FillSize& fillSize, cons
t ComputedStyle& style) |
189 { | 189 { |
190 switch (fillSize.type) { | 190 switch (fillSize.type) { |
191 case SizeLength: | 191 case SizeLength: |
192 return createFromLengthSize(fillSize.size, style); | 192 return createFromLengthSize(fillSize.size, style); |
193 case Contain: | 193 case Contain: |
194 case Cover: | 194 case Cover: |
195 case SizeNone: | 195 case SizeNone: |
196 return AnimatableUnknown::create(CSSPrimitiveValue::create(fillSize.type
)); | 196 return AnimatableUnknown::create(CSSPrimitiveValue::create(fillSize.type
)); |
197 default: | 197 default: |
198 ASSERT_NOT_REACHED(); | 198 ASSERT_NOT_REACHED(); |
199 return nullptr; | 199 return nullptr; |
200 } | 200 } |
201 } | 201 } |
202 | 202 |
203 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromBackgroundPositi
on(const Length& length, bool originIsSet, BackgroundEdgeOrigin origin, const Co
mputedStyle& style) | 203 inline static AnimatableValue* createFromBackgroundPosition(const Length& length
, bool originIsSet, BackgroundEdgeOrigin origin, const ComputedStyle& style) |
204 { | 204 { |
205 if (!originIsSet || origin == LeftEdge || origin == TopEdge) | 205 if (!originIsSet || origin == LeftEdge || origin == TopEdge) |
206 return createFromLength(length, style); | 206 return createFromLength(length, style); |
207 return createFromLength(length.subtractFromOneHundredPercent(), style); | 207 return createFromLength(length.subtractFromOneHundredPercent(), style); |
208 } | 208 } |
209 | 209 |
210 template<CSSPropertyID property> | 210 template<CSSPropertyID property> |
211 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromFillLayers(const
FillLayer& fillLayers, const ComputedStyle& style) | 211 inline static AnimatableValue* createFromFillLayers(const FillLayer& fillLayers,
const ComputedStyle& style) |
212 { | 212 { |
213 WillBeHeapVector<RefPtrWillBeMember<AnimatableValue>> values; | 213 HeapVector<Member<AnimatableValue>> values; |
214 for (const FillLayer* fillLayer = &fillLayers; fillLayer; fillLayer = fillLa
yer->next()) { | 214 for (const FillLayer* fillLayer = &fillLayers; fillLayer; fillLayer = fillLa
yer->next()) { |
215 if (property == CSSPropertyBackgroundImage || property == CSSPropertyWeb
kitMaskImage) { | 215 if (property == CSSPropertyBackgroundImage || property == CSSPropertyWeb
kitMaskImage) { |
216 if (!fillLayer->isImageSet()) | 216 if (!fillLayer->isImageSet()) |
217 break; | 217 break; |
218 values.append(createFromStyleImage(fillLayer->image())); | 218 values.append(createFromStyleImage(fillLayer->image())); |
219 } else if (property == CSSPropertyBackgroundPositionX || property == CSS
PropertyWebkitMaskPositionX) { | 219 } else if (property == CSSPropertyBackgroundPositionX || property == CSS
PropertyWebkitMaskPositionX) { |
220 if (!fillLayer->isXPositionSet()) | 220 if (!fillLayer->isXPositionSet()) |
221 break; | 221 break; |
222 values.append(createFromBackgroundPosition(fillLayer->xPosition(), f
illLayer->isBackgroundXOriginSet(), fillLayer->backgroundXOrigin(), style)); | 222 values.append(createFromBackgroundPosition(fillLayer->xPosition(), f
illLayer->isBackgroundXOriginSet(), fillLayer->backgroundXOrigin(), style)); |
223 } else if (property == CSSPropertyBackgroundPositionY || property == CSS
PropertyWebkitMaskPositionY) { | 223 } else if (property == CSSPropertyBackgroundPositionY || property == CSS
PropertyWebkitMaskPositionY) { |
224 if (!fillLayer->isYPositionSet()) | 224 if (!fillLayer->isYPositionSet()) |
225 break; | 225 break; |
226 values.append(createFromBackgroundPosition(fillLayer->yPosition(), f
illLayer->isBackgroundYOriginSet(), fillLayer->backgroundYOrigin(), style)); | 226 values.append(createFromBackgroundPosition(fillLayer->yPosition(), f
illLayer->isBackgroundYOriginSet(), fillLayer->backgroundYOrigin(), style)); |
227 } else if (property == CSSPropertyBackgroundSize || property == CSSPrope
rtyWebkitMaskSize) { | 227 } else if (property == CSSPropertyBackgroundSize || property == CSSPrope
rtyWebkitMaskSize) { |
228 if (!fillLayer->isSizeSet()) | 228 if (!fillLayer->isSizeSet()) |
229 break; | 229 break; |
230 values.append(createFromFillSize(fillLayer->size(), style)); | 230 values.append(createFromFillSize(fillLayer->size(), style)); |
231 } else { | 231 } else { |
232 ASSERT_NOT_REACHED(); | 232 ASSERT_NOT_REACHED(); |
233 } | 233 } |
234 } | 234 } |
235 return AnimatableRepeatable::create(values); | 235 return AnimatableRepeatable::create(values); |
236 } | 236 } |
237 | 237 |
238 PassRefPtrWillBeRawPtr<AnimatableValue> CSSAnimatableValueFactory::createFromCol
or(CSSPropertyID property, const ComputedStyle& style) | 238 AnimatableValue* CSSAnimatableValueFactory::createFromColor(CSSPropertyID proper
ty, const ComputedStyle& style) |
239 { | 239 { |
240 Color color = style.colorIncludingFallback(property, false); | 240 Color color = style.colorIncludingFallback(property, false); |
241 Color visitedLinkColor = style.colorIncludingFallback(property, true); | 241 Color visitedLinkColor = style.colorIncludingFallback(property, true); |
242 return AnimatableColor::create(color, visitedLinkColor); | 242 return AnimatableColor::create(color, visitedLinkColor); |
243 } | 243 } |
244 | 244 |
245 inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromShapeValue(Shape
Value* value) | 245 inline static AnimatableValue* createFromShapeValue(ShapeValue* value) |
246 { | 246 { |
247 if (value) | 247 if (value) |
248 return AnimatableShapeValue::create(value); | 248 return AnimatableShapeValue::create(value); |
249 return AnimatableUnknown::create(CSSValueNone); | 249 return AnimatableUnknown::create(CSSValueNone); |
250 } | 250 } |
251 | 251 |
252 static double fontStretchToDouble(FontStretch fontStretch) | 252 static double fontStretchToDouble(FontStretch fontStretch) |
253 { | 253 { |
254 return static_cast<unsigned>(fontStretch); | 254 return static_cast<unsigned>(fontStretch); |
255 } | 255 } |
256 | 256 |
257 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromFontStretch(FontStretch
fontStretch) | 257 static AnimatableValue* createFromFontStretch(FontStretch fontStretch) |
258 { | 258 { |
259 return createFromDouble(fontStretchToDouble(fontStretch)); | 259 return createFromDouble(fontStretchToDouble(fontStretch)); |
260 } | 260 } |
261 | 261 |
262 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromTransformProperties(Pas
sRefPtr<TransformOperation> transform, PassRefPtr<TransformOperation> initialTra
nsform) | 262 static AnimatableValue* createFromTransformProperties(PassRefPtr<TransformOperat
ion> transform, PassRefPtr<TransformOperation> initialTransform) |
263 { | 263 { |
264 TransformOperations operation; | 264 TransformOperations operation; |
265 operation.operations().append(transform ? transform : initialTransform); | 265 operation.operations().append(transform ? transform : initialTransform); |
266 return AnimatableTransform::create(operation); | 266 return AnimatableTransform::create(operation); |
267 } | 267 } |
268 | 268 |
269 static double fontWeightToDouble(FontWeight fontWeight) | 269 static double fontWeightToDouble(FontWeight fontWeight) |
270 { | 270 { |
271 switch (fontWeight) { | 271 switch (fontWeight) { |
272 case FontWeight100: | 272 case FontWeight100: |
(...skipping 13 matching lines...) Expand all Loading... |
286 case FontWeight800: | 286 case FontWeight800: |
287 return 800; | 287 return 800; |
288 case FontWeight900: | 288 case FontWeight900: |
289 return 900; | 289 return 900; |
290 } | 290 } |
291 | 291 |
292 ASSERT_NOT_REACHED(); | 292 ASSERT_NOT_REACHED(); |
293 return 400; | 293 return 400; |
294 } | 294 } |
295 | 295 |
296 static PassRefPtrWillBeRawPtr<AnimatableValue> createFromFontWeight(FontWeight f
ontWeight) | 296 static AnimatableValue* createFromFontWeight(FontWeight fontWeight) |
297 { | 297 { |
298 return createFromDouble(fontWeightToDouble(fontWeight)); | 298 return createFromDouble(fontWeightToDouble(fontWeight)); |
299 } | 299 } |
300 | 300 |
301 static SVGPaintType normalizeSVGPaintType(SVGPaintType paintType) | 301 static SVGPaintType normalizeSVGPaintType(SVGPaintType paintType) |
302 { | 302 { |
303 // If the <paint> is 'currentColor', then create an AnimatableSVGPaint with | 303 // If the <paint> is 'currentColor', then create an AnimatableSVGPaint with |
304 // a <rgbcolor> type. This is similar in vein to the handling of colors. | 304 // a <rgbcolor> type. This is similar in vein to the handling of colors. |
305 return paintType == SVG_PAINTTYPE_CURRENTCOLOR ? SVG_PAINTTYPE_RGBCOLOR : pa
intType; | 305 return paintType == SVG_PAINTTYPE_CURRENTCOLOR ? SVG_PAINTTYPE_RGBCOLOR : pa
intType; |
306 } | 306 } |
307 | 307 |
308 // FIXME: Generate this function. | 308 // FIXME: Generate this function. |
309 PassRefPtrWillBeRawPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPro
pertyID property, const ComputedStyle& style) | 309 AnimatableValue* CSSAnimatableValueFactory::create(CSSPropertyID property, const
ComputedStyle& style) |
310 { | 310 { |
311 ASSERT(CSSPropertyMetadata::isInterpolableProperty(property)); | 311 ASSERT(CSSPropertyMetadata::isInterpolableProperty(property)); |
312 switch (property) { | 312 switch (property) { |
313 case CSSPropertyBackgroundColor: | 313 case CSSPropertyBackgroundColor: |
314 return createFromColor(property, style); | 314 return createFromColor(property, style); |
315 case CSSPropertyBackgroundImage: | 315 case CSSPropertyBackgroundImage: |
316 return createFromFillLayers<CSSPropertyBackgroundImage>(style.background
Layers(), style); | 316 return createFromFillLayers<CSSPropertyBackgroundImage>(style.background
Layers(), style); |
317 case CSSPropertyBackgroundPositionX: | 317 case CSSPropertyBackgroundPositionX: |
318 return createFromFillLayers<CSSPropertyBackgroundPositionX>(style.backgr
oundLayers(), style); | 318 return createFromFillLayers<CSSPropertyBackgroundPositionX>(style.backgr
oundLayers(), style); |
319 case CSSPropertyBackgroundPositionY: | 319 case CSSPropertyBackgroundPositionY: |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
591 if (style.hasAutoZIndex()) | 591 if (style.hasAutoZIndex()) |
592 return AnimatableUnknown::create(CSSValueAuto); | 592 return AnimatableUnknown::create(CSSValueAuto); |
593 return createFromDouble(style.zIndex()); | 593 return createFromDouble(style.zIndex()); |
594 default: | 594 default: |
595 ASSERT_NOT_REACHED(); | 595 ASSERT_NOT_REACHED(); |
596 return nullptr; | 596 return nullptr; |
597 } | 597 } |
598 } | 598 } |
599 | 599 |
600 } // namespace blink | 600 } // namespace blink |
OLD | NEW |