1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
6
7 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
8 #include "third_party/blink/renderer/core/css/css_border_image.h"
9 #include "third_party/blink/renderer/core/css/css_border_image_slice_value.h"
10 #include "third_party/blink/renderer/core/css/css_color_value.h"
11 #include "third_party/blink/renderer/core/css/css_counter_value.h"
12 #include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
13 #include "third_party/blink/renderer/core/css/css_font_family_value.h"
14 #include "third_party/blink/renderer/core/css/css_font_style_range_value.h"
15 #include "third_party/blink/renderer/core/css/css_function_value.h"
16 #include "third_party/blink/renderer/core/css/css_grid_auto_repeat_value.h"
17 #include "third_party/blink/renderer/core/css/css_grid_line_names_value.h"
18 #include "third_party/blink/renderer/core/css/css_initial_value.h"
19 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
20 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
21 #include "third_party/blink/renderer/core/css/css_quad_value.h"
22 #include "third_party/blink/renderer/core/css/css_reflect_value.h"
23 #include "third_party/blink/renderer/core/css/css_shadow_value.h"
24 #include "third_party/blink/renderer/core/css/css_string_value.h"
25 #include "third_party/blink/renderer/core/css/css_timing_function_value.h"
26 #include "third_party/blink/renderer/core/css/css_uri_value.h"
27 #include "third_party/blink/renderer/core/css/css_value.h"
28 #include "third_party/blink/renderer/core/css/css_value_list.h"
29 #include "third_party/blink/renderer/core/css/css_value_pair.h"
30 #include "third_party/blink/renderer/core/css/cssom/cross_thread_color_value.h"
31 #include "third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.h"
32 #include "third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.h"
33 #include "third_party/blink/renderer/core/css/cssom/cross_thread_unparsed_value.h"
34 #include "third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.h"
35 #include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
36 #include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
37 #include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
38 #include "third_party/blink/renderer/core/css/cssom/css_unsupported_color_value.h"
39 #include "third_party/blink/renderer/core/css/style_color.h"
40 #include "third_party/blink/renderer/core/layout/layout_block.h"
41 #include "third_party/blink/renderer/core/layout/layout_box.h"
42 #include "third_party/blink/renderer/core/layout/layout_grid.h"
43 #include "third_party/blink/renderer/core/layout/svg/transform_helper.h"
44 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
45 #include "third_party/blink/renderer/core/style/style_svg_resource.h"
46 #include "third_party/blink/renderer/core/style_property_shorthand.h"
47 #include "third_party/blink/renderer/core/svg_element_type_helpers.h"
48 #include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
49 #include "third_party/blink/renderer/platform/transforms/matrix_transform_operation.h"
50 #include "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h"
51 #include "third_party/blink/renderer/platform/transforms/skew_transform_operation.h"
52
53 namespace blink {
54
55 // TODO(rjwright): make this const
ZoomAdjustedPixelValueForLength(const Length & length,const ComputedStyle & style)56 CSSValue* ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
57 const Length& length,
58 const ComputedStyle& style) {
59 if (length.IsFixed())
60 return ZoomAdjustedPixelValue(length.Value(), style);
61 return CSSValue::Create(length, style.EffectiveZoom());
62 }
63
ValueForPosition(const LengthPoint & position,const ComputedStyle & style)64 CSSValue* ComputedStyleUtils::ValueForPosition(const LengthPoint& position,
65 const ComputedStyle& style) {
66 DCHECK_EQ(position.X().IsAuto(), position.Y().IsAuto());
67 if (position.X().IsAuto())
68 return CSSIdentifierValue::Create(CSSValueID::kAuto);
69
70 return MakeGarbageCollected<CSSValuePair>(
71 ZoomAdjustedPixelValueForLength(position.X(), style),
72 ZoomAdjustedPixelValueForLength(position.Y(), style),
73 CSSValuePair::kKeepIdenticalValues);
74 }
75
ValueForOffset(const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)76 CSSValue* ComputedStyleUtils::ValueForOffset(const ComputedStyle& style,
77 const LayoutObject* layout_object,
78 bool allow_visited_style) {
79 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
80 if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
81 CSSValue* position = ValueForPosition(style.OffsetPosition(), style);
82 auto* position_identifier_value = DynamicTo<CSSIdentifierValue>(position);
83 if (!position_identifier_value)
84 list->Append(*position);
85 else
86 DCHECK(position_identifier_value->GetValueID() == CSSValueID::kAuto);
87 }
88
89 static const CSSProperty* longhands[3] = {&GetCSSPropertyOffsetPath(),
90 &GetCSSPropertyOffsetDistance(),
91 &GetCSSPropertyOffsetRotate()};
92 for (const CSSProperty* longhand : longhands) {
93 const CSSValue* value = longhand->CSSValueFromComputedStyle(
94 style, layout_object, allow_visited_style);
95 DCHECK(value);
96 list->Append(*value);
97 }
98
99 if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
100 CSSValue* anchor = ValueForPosition(style.OffsetAnchor(), style);
101 auto* anchor_identifier_value = DynamicTo<CSSIdentifierValue>(anchor);
102 if (!anchor_identifier_value) {
103 // Add a slash before anchor.
104 CSSValueList* result = CSSValueList::CreateSlashSeparated();
105 result->Append(*list);
106 result->Append(*anchor);
107 return result;
108 }
109 DCHECK(anchor_identifier_value->GetValueID() == CSSValueID::kAuto);
110 }
111 return list;
112 }
113
CurrentColorOrValidColor(const ComputedStyle & style,const StyleColor & color,CSSValuePhase value_phase)114 CSSValue* ComputedStyleUtils::CurrentColorOrValidColor(
115 const ComputedStyle& style,
116 const StyleColor& color,
117 CSSValuePhase value_phase) {
118 // This function does NOT look at visited information, so that computed style
119 // doesn't expose that.
120 if (value_phase == CSSValuePhase::kComputedValue && color.IsSystemColor() &&
121 RuntimeEnabledFeatures::CSSSystemColorComputeToSelfEnabled()) {
122 return CSSIdentifierValue::Create(color.GetColorKeyword());
123 }
124 return cssvalue::CSSColorValue::Create(
125 color.Resolve(style.GetCurrentColor(), style.UsedColorScheme()).Rgb());
126 }
127
BorderSideColor(const ComputedStyle & style,const StyleColor & color,EBorderStyle border_style,bool visited_link)128 const blink::Color ComputedStyleUtils::BorderSideColor(
129 const ComputedStyle& style,
130 const StyleColor& color,
131 EBorderStyle border_style,
132 bool visited_link) {
133 Color current_color;
134 if (visited_link) {
135 current_color = style.GetInternalVisitedCurrentColor();
136 } else if (border_style == EBorderStyle::kInset ||
137 border_style == EBorderStyle::kOutset ||
138 border_style == EBorderStyle::kRidge ||
139 border_style == EBorderStyle::kGroove) {
140 // FIXME: Treating styled borders with initial color differently causes
141 // problems, see crbug.com/316559, crbug.com/276231
142 current_color = blink::Color(238, 238, 238);
143 } else {
144 current_color = style.GetCurrentColor();
145 }
146 return color.Resolve(current_color, style.UsedColorScheme());
147 }
148
BackgroundImageOrWebkitMaskImage(const ComputedStyle & style,bool allow_visited_style,const FillLayer & fill_layer)149 const CSSValue* ComputedStyleUtils::BackgroundImageOrWebkitMaskImage(
150 const ComputedStyle& style,
151 bool allow_visited_style,
152 const FillLayer& fill_layer) {
153 CSSValueList* list = CSSValueList::CreateCommaSeparated();
154 const FillLayer* curr_layer = &fill_layer;
155 for (; curr_layer; curr_layer = curr_layer->Next()) {
156 if (curr_layer->GetImage()) {
157 list->Append(*curr_layer->GetImage()->ComputedCSSValue(
158 style, allow_visited_style));
159 } else {
160 list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone));
161 }
162 }
163 return list;
164 }
165
ValueForFillSize(const FillSize & fill_size,const ComputedStyle & style)166 const CSSValue* ComputedStyleUtils::ValueForFillSize(
167 const FillSize& fill_size,
168 const ComputedStyle& style) {
169 if (fill_size.type == EFillSizeType::kContain)
170 return CSSIdentifierValue::Create(CSSValueID::kContain);
171
172 if (fill_size.type == EFillSizeType::kCover)
173 return CSSIdentifierValue::Create(CSSValueID::kCover);
174
175 if (fill_size.size.Height().IsAuto()) {
176 return ZoomAdjustedPixelValueForLength(fill_size.size.Width(), style);
177 }
178
179 return MakeGarbageCollected<CSSValuePair>(
180 ZoomAdjustedPixelValueForLength(fill_size.size.Width(), style),
181 ZoomAdjustedPixelValueForLength(fill_size.size.Height(), style),
182 CSSValuePair::kKeepIdenticalValues);
183 }
184
BackgroundImageOrWebkitMaskSize(const ComputedStyle & style,const FillLayer & fill_layer)185 const CSSValue* ComputedStyleUtils::BackgroundImageOrWebkitMaskSize(
186 const ComputedStyle& style,
187 const FillLayer& fill_layer) {
188 CSSValueList* list = CSSValueList::CreateCommaSeparated();
189 const FillLayer* curr_layer = &fill_layer;
190 for (; curr_layer; curr_layer = curr_layer->Next())
191 list->Append(*ValueForFillSize(curr_layer->Size(), style));
192 return list;
193 }
194
CreatePositionListForLayer(const CSSProperty & property,const FillLayer & layer,const ComputedStyle & style)195 const CSSValueList* ComputedStyleUtils::CreatePositionListForLayer(
196 const CSSProperty& property,
197 const FillLayer& layer,
198 const ComputedStyle& style) {
199 CSSValueList* position_list = CSSValueList::CreateSpaceSeparated();
200 if (layer.IsBackgroundXOriginSet()) {
201 DCHECK(property.IDEquals(CSSPropertyID::kBackgroundPosition) ||
202 property.IDEquals(CSSPropertyID::kWebkitMaskPosition));
203 position_list->Append(
204 *CSSIdentifierValue::Create(layer.BackgroundXOrigin()));
205 }
206 position_list->Append(
207 *ZoomAdjustedPixelValueForLength(layer.PositionX(), style));
208 if (layer.IsBackgroundYOriginSet()) {
209 DCHECK(property.IDEquals(CSSPropertyID::kBackgroundPosition) ||
210 property.IDEquals(CSSPropertyID::kWebkitMaskPosition));
211 position_list->Append(
212 *CSSIdentifierValue::Create(layer.BackgroundYOrigin()));
213 }
214 position_list->Append(
215 *ZoomAdjustedPixelValueForLength(layer.PositionY(), style));
216 return position_list;
217 }
218
ValueForFillRepeat(EFillRepeat x_repeat,EFillRepeat y_repeat)219 const CSSValue* ComputedStyleUtils::ValueForFillRepeat(EFillRepeat x_repeat,
220 EFillRepeat y_repeat) {
221 // For backwards compatibility, if both values are equal, just return one of
222 // them. And if the two values are equivalent to repeat-x or repeat-y, just
223 // return the shorthand.
224 if (x_repeat == y_repeat)
225 return CSSIdentifierValue::Create(x_repeat);
226 if (x_repeat == EFillRepeat::kRepeatFill &&
227 y_repeat == EFillRepeat::kNoRepeatFill)
228 return CSSIdentifierValue::Create(CSSValueID::kRepeatX);
229 if (x_repeat == EFillRepeat::kNoRepeatFill &&
230 y_repeat == EFillRepeat::kRepeatFill)
231 return CSSIdentifierValue::Create(CSSValueID::kRepeatY);
232
233 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
234 list->Append(*CSSIdentifierValue::Create(x_repeat));
235 list->Append(*CSSIdentifierValue::Create(y_repeat));
236 return list;
237 }
238
ValuesForBackgroundShorthand(const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)239 const CSSValueList* ComputedStyleUtils::ValuesForBackgroundShorthand(
240 const ComputedStyle& style,
241 const LayoutObject* layout_object,
242 bool allow_visited_style) {
243 CSSValueList* result = CSSValueList::CreateCommaSeparated();
244 const FillLayer* curr_layer = &style.BackgroundLayers();
245 for (; curr_layer; curr_layer = curr_layer->Next()) {
246 CSSValueList* list = CSSValueList::CreateSlashSeparated();
247 CSSValueList* before_slash = CSSValueList::CreateSpaceSeparated();
248 if (!curr_layer->Next()) { // color only for final layer
249 const CSSValue* value =
250 GetCSSPropertyBackgroundColor().CSSValueFromComputedStyle(
251 style, layout_object, allow_visited_style);
252 DCHECK(value);
253 before_slash->Append(*value);
254 }
255 before_slash->Append(curr_layer->GetImage()
256 ? *curr_layer->GetImage()->ComputedCSSValue(
257 style, allow_visited_style)
258 : *CSSIdentifierValue::Create(CSSValueID::kNone));
259 before_slash->Append(
260 *ValueForFillRepeat(curr_layer->RepeatX(), curr_layer->RepeatY()));
261 before_slash->Append(*CSSIdentifierValue::Create(curr_layer->Attachment()));
262 before_slash->Append(*CreatePositionListForLayer(
263 GetCSSPropertyBackgroundPosition(), *curr_layer, style));
264 list->Append(*before_slash);
265 CSSValueList* after_slash = CSSValueList::CreateSpaceSeparated();
266 after_slash->Append(*ValueForFillSize(curr_layer->Size(), style));
267 after_slash->Append(*CSSIdentifierValue::Create(curr_layer->Origin()));
268 after_slash->Append(*CSSIdentifierValue::Create(curr_layer->Clip()));
269 list->Append(*after_slash);
270 result->Append(*list);
271 }
272 return result;
273 }
274
BackgroundRepeatOrWebkitMaskRepeat(const FillLayer * curr_layer)275 const CSSValue* ComputedStyleUtils::BackgroundRepeatOrWebkitMaskRepeat(
276 const FillLayer* curr_layer) {
277 CSSValueList* list = CSSValueList::CreateCommaSeparated();
278 for (; curr_layer; curr_layer = curr_layer->Next()) {
279 list->Append(
280 *ValueForFillRepeat(curr_layer->RepeatX(), curr_layer->RepeatY()));
281 }
282 return list;
283 }
284
BackgroundPositionOrWebkitMaskPosition(const CSSProperty & resolved_property,const ComputedStyle & style,const FillLayer * curr_layer)285 const CSSValue* ComputedStyleUtils::BackgroundPositionOrWebkitMaskPosition(
286 const CSSProperty& resolved_property,
287 const ComputedStyle& style,
288 const FillLayer* curr_layer) {
289 CSSValueList* list = CSSValueList::CreateCommaSeparated();
290 for (; curr_layer; curr_layer = curr_layer->Next()) {
291 list->Append(
292 *CreatePositionListForLayer(resolved_property, *curr_layer, style));
293 }
294 return list;
295 }
296
BackgroundPositionXOrWebkitMaskPositionX(const ComputedStyle & style,const FillLayer * curr_layer)297 const CSSValue* ComputedStyleUtils::BackgroundPositionXOrWebkitMaskPositionX(
298 const ComputedStyle& style,
299 const FillLayer* curr_layer) {
300 CSSValueList* list = CSSValueList::CreateCommaSeparated();
301 for (; curr_layer; curr_layer = curr_layer->Next()) {
302 const Length& from_edge = curr_layer->PositionX();
303 if (curr_layer->BackgroundXOrigin() == BackgroundEdgeOrigin::kRight) {
304 // TODO(crbug.com/610627): This should use two-value syntax once the
305 // parser accepts it.
306 list->Append(*ZoomAdjustedPixelValueForLength(
307 from_edge.SubtractFromOneHundredPercent(), style));
308 } else {
309 list->Append(*ZoomAdjustedPixelValueForLength(from_edge, style));
310 }
311 }
312 return list;
313 }
314
BackgroundPositionYOrWebkitMaskPositionY(const ComputedStyle & style,const FillLayer * curr_layer)315 const CSSValue* ComputedStyleUtils::BackgroundPositionYOrWebkitMaskPositionY(
316 const ComputedStyle& style,
317 const FillLayer* curr_layer) {
318 CSSValueList* list = CSSValueList::CreateCommaSeparated();
319 for (; curr_layer; curr_layer = curr_layer->Next()) {
320 const Length& from_edge = curr_layer->PositionY();
321 if (curr_layer->BackgroundYOrigin() == BackgroundEdgeOrigin::kBottom) {
322 // TODO(crbug.com/610627): This should use two-value syntax once the
323 // parser accepts it.
324 list->Append(*ZoomAdjustedPixelValueForLength(
325 from_edge.SubtractFromOneHundredPercent(), style));
326 } else {
327 list->Append(*ZoomAdjustedPixelValueForLength(from_edge, style));
328 }
329 }
330 return list;
331 }
332
333 cssvalue::CSSBorderImageSliceValue*
ValueForNinePieceImageSlice(const NinePieceImage & image)334 ComputedStyleUtils::ValueForNinePieceImageSlice(const NinePieceImage& image) {
335 // Create the slices.
336 CSSPrimitiveValue* top = nullptr;
337 CSSPrimitiveValue* right = nullptr;
338 CSSPrimitiveValue* bottom = nullptr;
339 CSSPrimitiveValue* left = nullptr;
340
341 // TODO(alancutter): Make this code aware of calc lengths.
342 if (image.ImageSlices().Top().IsPercentOrCalc()) {
343 top = CSSNumericLiteralValue::Create(
344 image.ImageSlices().Top().Value(),
345 CSSPrimitiveValue::UnitType::kPercentage);
346 } else {
347 top = CSSNumericLiteralValue::Create(image.ImageSlices().Top().Value(),
348 CSSPrimitiveValue::UnitType::kNumber);
349 }
350
351 if (image.ImageSlices().Right() == image.ImageSlices().Top() &&
352 image.ImageSlices().Bottom() == image.ImageSlices().Top() &&
353 image.ImageSlices().Left() == image.ImageSlices().Top()) {
354 right = top;
355 bottom = top;
356 left = top;
357 } else {
358 if (image.ImageSlices().Right().IsPercentOrCalc()) {
359 right = CSSNumericLiteralValue::Create(
360 image.ImageSlices().Right().Value(),
361 CSSPrimitiveValue::UnitType::kPercentage);
362 } else {
363 right =
364 CSSNumericLiteralValue::Create(image.ImageSlices().Right().Value(),
365 CSSPrimitiveValue::UnitType::kNumber);
366 }
367
368 if (image.ImageSlices().Bottom() == image.ImageSlices().Top() &&
369 image.ImageSlices().Right() == image.ImageSlices().Left()) {
370 bottom = top;
371 left = right;
372 } else {
373 if (image.ImageSlices().Bottom().IsPercentOrCalc()) {
374 bottom = CSSNumericLiteralValue::Create(
375 image.ImageSlices().Bottom().Value(),
376 CSSPrimitiveValue::UnitType::kPercentage);
377 } else {
378 bottom = CSSNumericLiteralValue::Create(
379 image.ImageSlices().Bottom().Value(),
380 CSSPrimitiveValue::UnitType::kNumber);
381 }
382
383 if (image.ImageSlices().Left() == image.ImageSlices().Right()) {
384 left = right;
385 } else {
386 if (image.ImageSlices().Left().IsPercentOrCalc()) {
387 left = CSSNumericLiteralValue::Create(
388 image.ImageSlices().Left().Value(),
389 CSSPrimitiveValue::UnitType::kPercentage);
390 } else {
391 left = CSSNumericLiteralValue::Create(
392 image.ImageSlices().Left().Value(),
393 CSSPrimitiveValue::UnitType::kNumber);
394 }
395 }
396 }
397 }
398
399 return MakeGarbageCollected<cssvalue::CSSBorderImageSliceValue>(
400 MakeGarbageCollected<CSSQuadValue>(top, right, bottom, left,
401 CSSQuadValue::kSerializeAsQuad),
402 image.Fill());
403 }
404
ValueForBorderImageLength(const BorderImageLength & border_image_length,const ComputedStyle & style)405 CSSValue* ValueForBorderImageLength(
406 const BorderImageLength& border_image_length,
407 const ComputedStyle& style) {
408 if (border_image_length.IsNumber()) {
409 return CSSNumericLiteralValue::Create(border_image_length.Number(),
410 CSSPrimitiveValue::UnitType::kNumber);
411 }
412 return CSSValue::Create(border_image_length.length(), style.EffectiveZoom());
413 }
414
ValueForNinePieceImageQuad(const BorderImageLengthBox & box,const ComputedStyle & style)415 CSSQuadValue* ComputedStyleUtils::ValueForNinePieceImageQuad(
416 const BorderImageLengthBox& box,
417 const ComputedStyle& style) {
418 // Create the slices.
419 CSSValue* top = nullptr;
420 CSSValue* right = nullptr;
421 CSSValue* bottom = nullptr;
422 CSSValue* left = nullptr;
423
424 top = ValueForBorderImageLength(box.Top(), style);
425
426 if (box.Right() == box.Top() && box.Bottom() == box.Top() &&
427 box.Left() == box.Top()) {
428 right = top;
429 bottom = top;
430 left = top;
431 } else {
432 right = ValueForBorderImageLength(box.Right(), style);
433
434 if (box.Bottom() == box.Top() && box.Right() == box.Left()) {
435 bottom = top;
436 left = right;
437 } else {
438 bottom = ValueForBorderImageLength(box.Bottom(), style);
439
440 if (box.Left() == box.Right())
441 left = right;
442 else
443 left = ValueForBorderImageLength(box.Left(), style);
444 }
445 }
446 return MakeGarbageCollected<CSSQuadValue>(top, right, bottom, left,
447 CSSQuadValue::kSerializeAsQuad);
448 }
449
ValueForRepeatRule(int rule)450 CSSValueID ValueForRepeatRule(int rule) {
451 switch (rule) {
452 case kRepeatImageRule:
453 return CSSValueID::kRepeat;
454 case kRoundImageRule:
455 return CSSValueID::kRound;
456 case kSpaceImageRule:
457 return CSSValueID::kSpace;
458 default:
459 return CSSValueID::kStretch;
460 }
461 }
462
ValueForNinePieceImageRepeat(const NinePieceImage & image)463 CSSValue* ComputedStyleUtils::ValueForNinePieceImageRepeat(
464 const NinePieceImage& image) {
465 CSSIdentifierValue* horizontal_repeat = nullptr;
466 CSSIdentifierValue* vertical_repeat = nullptr;
467
468 horizontal_repeat =
469 CSSIdentifierValue::Create(ValueForRepeatRule(image.HorizontalRule()));
470 if (image.HorizontalRule() == image.VerticalRule()) {
471 vertical_repeat = horizontal_repeat;
472 } else {
473 vertical_repeat =
474 CSSIdentifierValue::Create(ValueForRepeatRule(image.VerticalRule()));
475 }
476 return MakeGarbageCollected<CSSValuePair>(horizontal_repeat, vertical_repeat,
477 CSSValuePair::kDropIdenticalValues);
478 }
479
ValueForNinePieceImage(const NinePieceImage & image,const ComputedStyle & style,bool allow_visited_style)480 CSSValue* ComputedStyleUtils::ValueForNinePieceImage(
481 const NinePieceImage& image,
482 const ComputedStyle& style,
483 bool allow_visited_style) {
484 if (!image.HasImage())
485 return CSSIdentifierValue::Create(CSSValueID::kNone);
486
487 // Image first.
488 CSSValue* image_value = nullptr;
489 if (image.GetImage()) {
490 image_value =
491 image.GetImage()->ComputedCSSValue(style, allow_visited_style);
492 }
493
494 // Create the image slice.
495 cssvalue::CSSBorderImageSliceValue* image_slices =
496 ValueForNinePieceImageSlice(image);
497
498 // Create the border area slices.
499 CSSValue* border_slices =
500 ValueForNinePieceImageQuad(image.BorderSlices(), style);
501
502 // Create the border outset.
503 CSSValue* outset = ValueForNinePieceImageQuad(image.Outset(), style);
504
505 // Create the repeat rules.
506 CSSValue* repeat = ValueForNinePieceImageRepeat(image);
507
508 return CreateBorderImageValue(image_value, image_slices, border_slices,
509 outset, repeat);
510 }
511
ValueForReflection(const StyleReflection * reflection,const ComputedStyle & style,bool allow_visited_style)512 CSSValue* ComputedStyleUtils::ValueForReflection(
513 const StyleReflection* reflection,
514 const ComputedStyle& style,
515 bool allow_visited_style) {
516 if (!reflection)
517 return CSSIdentifierValue::Create(CSSValueID::kNone);
518
519 CSSPrimitiveValue* offset = nullptr;
520 // TODO(alancutter): Make this work correctly for calc lengths.
521 if (reflection->Offset().IsPercentOrCalc()) {
522 offset = CSSNumericLiteralValue::Create(
523 reflection->Offset().Percent(),
524 CSSPrimitiveValue::UnitType::kPercentage);
525 } else {
526 offset = ZoomAdjustedPixelValue(reflection->Offset().Value(), style);
527 }
528
529 CSSIdentifierValue* direction = nullptr;
530 switch (reflection->Direction()) {
531 case kReflectionBelow:
532 direction = CSSIdentifierValue::Create(CSSValueID::kBelow);
533 break;
534 case kReflectionAbove:
535 direction = CSSIdentifierValue::Create(CSSValueID::kAbove);
536 break;
537 case kReflectionLeft:
538 direction = CSSIdentifierValue::Create(CSSValueID::kLeft);
539 break;
540 case kReflectionRight:
541 direction = CSSIdentifierValue::Create(CSSValueID::kRight);
542 break;
543 }
544
545 return MakeGarbageCollected<cssvalue::CSSReflectValue>(
546 direction, offset,
547 ValueForNinePieceImage(reflection->Mask(), style, allow_visited_style));
548 }
549
MinWidthOrMinHeightAuto(const ComputedStyle & style)550 CSSValue* ComputedStyleUtils::MinWidthOrMinHeightAuto(
551 const ComputedStyle& style) {
552 if (style.IsFlexOrGridOrCustomItem() && !style.IsEnsuredInDisplayNone())
553 return CSSIdentifierValue::Create(CSSValueID::kAuto);
554 return ZoomAdjustedPixelValue(0, style);
555 }
556
ValueForPositionOffset(const ComputedStyle & style,const CSSProperty & property,const LayoutObject * layout_object)557 CSSValue* ComputedStyleUtils::ValueForPositionOffset(
558 const ComputedStyle& style,
559 const CSSProperty& property,
560 const LayoutObject* layout_object) {
561 std::pair<const Length*, const Length*> positions;
562 bool is_horizontal_property;
563 switch (property.PropertyID()) {
564 case CSSPropertyID::kLeft:
565 positions = std::make_pair(&style.Left(), &style.Right());
566 is_horizontal_property = true;
567 break;
568 case CSSPropertyID::kRight:
569 positions = std::make_pair(&style.Right(), &style.Left());
570 is_horizontal_property = true;
571 break;
572 case CSSPropertyID::kTop:
573 positions = std::make_pair(&style.Top(), &style.Bottom());
574 is_horizontal_property = false;
575 break;
576 case CSSPropertyID::kBottom:
577 positions = std::make_pair(&style.Bottom(), &style.Top());
578 is_horizontal_property = false;
579 break;
580 default:
581 NOTREACHED();
582 return nullptr;
583 }
584 DCHECK(positions.first && positions.second);
585
586 const Length& offset = *positions.first;
587 const Length& opposite = *positions.second;
588
589 const auto* box = DynamicTo<LayoutBox>(layout_object);
590 if (offset.IsPercentOrCalc() && box && layout_object->IsPositioned()) {
591 LayoutUnit containing_block_size;
592 if (layout_object->IsStickyPositioned()) {
593 const LayoutBox& enclosing_scrollport_box = box->EnclosingScrollportBox();
594 bool use_inline_size = is_horizontal_property ==
595 enclosing_scrollport_box.IsHorizontalWritingMode();
596 containing_block_size =
597 use_inline_size ? enclosing_scrollport_box.ContentLogicalWidth()
598 : enclosing_scrollport_box.ContentLogicalHeight();
599 } else {
600 containing_block_size =
601 is_horizontal_property ==
602 layout_object->ContainingBlock()->IsHorizontalWritingMode()
603 ? box->ContainingBlockLogicalWidthForContent()
604 : box->ContainingBlockLogicalHeightForGetComputedStyle();
605 }
606
607 return ZoomAdjustedPixelValue(ValueForLength(offset, containing_block_size),
608 style);
609 }
610
611 if (offset.IsAuto() && layout_object) {
612 // If the property applies to a positioned element and the resolved value of
613 // the display property is not none, the resolved value is the used value.
614 // Position offsets have special meaning for position sticky so we return
615 // auto when offset.isAuto() on a sticky position object:
616 // https://crbug.com/703816.
617 if (layout_object->IsRelPositioned()) {
618 // If e.g. left is auto and right is not auto, then left's computed value
619 // is negative right. So we get the opposite length unit and see if it is
620 // auto.
621 if (opposite.IsAuto()) {
622 return CSSNumericLiteralValue::Create(
623 0, CSSPrimitiveValue::UnitType::kPixels);
624 }
625
626 if (opposite.IsPercentOrCalc()) {
627 if (box) {
628 LayoutUnit containing_block_size =
629 is_horizontal_property == layout_object->ContainingBlock()
630 ->IsHorizontalWritingMode()
631 ? box->ContainingBlockLogicalWidthForContent()
632 : box->ContainingBlockLogicalHeightForGetComputedStyle();
633 return ZoomAdjustedPixelValue(
634 -FloatValueForLength(opposite, containing_block_size), style);
635 }
636 // FIXME: fall back to auto for position:relative, display:inline
637 return CSSIdentifierValue::Create(CSSValueID::kAuto);
638 }
639
640 // Length doesn't provide operator -, so multiply by -1.
641 Length negated_opposite = opposite;
642 negated_opposite *= -1.f;
643 return ZoomAdjustedPixelValueForLength(negated_opposite, style);
644 }
645
646 if (layout_object->IsOutOfFlowPositioned() && layout_object->IsBox()) {
647 // For fixed and absolute positioned elements, the top, left, bottom, and
648 // right are defined relative to the corresponding sides of the containing
649 // block.
650 LayoutBlock* container = layout_object->ContainingBlock();
651
652 // clientOffset is the distance from this object's border edge to the
653 // container's padding edge. Thus it includes margins which we subtract
654 // below.
655 const LayoutSize client_offset =
656 box->LocationOffset() -
657 LayoutSize(container->ClientLeft(), container->ClientTop());
658 LayoutUnit position;
659
660 switch (property.PropertyID()) {
661 case CSSPropertyID::kLeft:
662 position = client_offset.Width() - box->MarginLeft();
663 break;
664 case CSSPropertyID::kTop:
665 position = client_offset.Height() - box->MarginTop();
666 break;
667 case CSSPropertyID::kRight:
668 position = container->ClientWidth() - box->MarginRight() -
669 (box->OffsetWidth() + client_offset.Width());
670 break;
671 case CSSPropertyID::kBottom:
672 position = container->ClientHeight() - box->MarginBottom() -
673 (box->OffsetHeight() + client_offset.Height());
674 break;
675 default:
676 NOTREACHED();
677 }
678 return ZoomAdjustedPixelValue(position, style);
679 }
680 }
681
682 if (offset.IsAuto())
683 return CSSIdentifierValue::Create(CSSValueID::kAuto);
684
685 return ZoomAdjustedPixelValueForLength(offset, style);
686 }
687
ValueForItemPositionWithOverflowAlignment(const StyleSelfAlignmentData & data)688 CSSValueList* ComputedStyleUtils::ValueForItemPositionWithOverflowAlignment(
689 const StyleSelfAlignmentData& data) {
690 CSSValueList* result = CSSValueList::CreateSpaceSeparated();
691 if (data.PositionType() == ItemPositionType::kLegacy)
692 result->Append(*CSSIdentifierValue::Create(CSSValueID::kLegacy));
693 if (data.GetPosition() == ItemPosition::kBaseline) {
694 result->Append(*MakeGarbageCollected<CSSValuePair>(
695 CSSIdentifierValue::Create(CSSValueID::kBaseline),
696 CSSIdentifierValue::Create(CSSValueID::kBaseline),
697 CSSValuePair::kDropIdenticalValues));
698 } else if (data.GetPosition() == ItemPosition::kLastBaseline) {
699 result->Append(*MakeGarbageCollected<CSSValuePair>(
700 CSSIdentifierValue::Create(CSSValueID::kLast),
701 CSSIdentifierValue::Create(CSSValueID::kBaseline),
702 CSSValuePair::kDropIdenticalValues));
703 } else {
704 if (data.GetPosition() >= ItemPosition::kCenter &&
705 data.Overflow() != OverflowAlignment::kDefault)
706 result->Append(*CSSIdentifierValue::Create(data.Overflow()));
707 if (data.GetPosition() == ItemPosition::kLegacy)
708 result->Append(*CSSIdentifierValue::Create(CSSValueID::kNormal));
709 else
710 result->Append(*CSSIdentifierValue::Create(data.GetPosition()));
711 }
712 DCHECK_LE(result->length(), 2u);
713 return result;
714 }
715
716 CSSValueList*
ValueForContentPositionAndDistributionWithOverflowAlignment(const StyleContentAlignmentData & data)717 ComputedStyleUtils::ValueForContentPositionAndDistributionWithOverflowAlignment(
718 const StyleContentAlignmentData& data) {
719 CSSValueList* result = CSSValueList::CreateSpaceSeparated();
720 // Handle content-distribution values
721 if (data.Distribution() != ContentDistributionType::kDefault)
722 result->Append(*CSSIdentifierValue::Create(data.Distribution()));
723
724 // Handle content-position values (either as fallback or actual value)
725 switch (data.GetPosition()) {
726 case ContentPosition::kNormal:
727 // Handle 'normal' value, not valid as content-distribution fallback.
728 if (data.Distribution() == ContentDistributionType::kDefault) {
729 result->Append(*CSSIdentifierValue::Create(CSSValueID::kNormal));
730 }
731 break;
732 case ContentPosition::kLastBaseline:
733 result->Append(*MakeGarbageCollected<CSSValuePair>(
734 CSSIdentifierValue::Create(CSSValueID::kLast),
735 CSSIdentifierValue::Create(CSSValueID::kBaseline),
736 CSSValuePair::kDropIdenticalValues));
737 break;
738 default:
739 // Handle overflow-alignment (only allowed for content-position values)
740 if ((data.GetPosition() >= ContentPosition::kCenter ||
741 data.Distribution() != ContentDistributionType::kDefault) &&
742 data.Overflow() != OverflowAlignment::kDefault)
743 result->Append(*CSSIdentifierValue::Create(data.Overflow()));
744 result->Append(*CSSIdentifierValue::Create(data.GetPosition()));
745 }
746
747 DCHECK_GT(result->length(), 0u);
748 DCHECK_LE(result->length(), 3u);
749 return result;
750 }
751
ValueForLineHeight(const ComputedStyle & style)752 CSSValue* ComputedStyleUtils::ValueForLineHeight(const ComputedStyle& style) {
753 const Length& length = style.LineHeight();
754 if (length.IsNegative())
755 return CSSIdentifierValue::Create(CSSValueID::kNormal);
756
757 return ZoomAdjustedPixelValue(
758 FloatValueForLength(length, style.GetFontDescription().ComputedSize()),
759 style);
760 }
761
ComputedValueForLineHeight(const ComputedStyle & style)762 CSSValue* ComputedStyleUtils::ComputedValueForLineHeight(
763 const ComputedStyle& style) {
764 const Length& length = style.LineHeight();
765 if (length.IsNegative())
766 return CSSIdentifierValue::Create(CSSValueID::kNormal);
767
768 if (length.IsPercent()) {
769 return CSSNumericLiteralValue::Create(length.GetFloatValue() / 100.0,
770 CSSPrimitiveValue::UnitType::kNumber);
771 } else {
772 return ZoomAdjustedPixelValue(
773 FloatValueForLength(length, style.GetFontDescription().ComputedSize()),
774 style);
775 }
776 }
777
IdentifierForFamily(const AtomicString & family)778 CSSValueID IdentifierForFamily(const AtomicString& family) {
779 if (family == font_family_names::kWebkitCursive)
780 return CSSValueID::kCursive;
781 if (family == font_family_names::kWebkitFantasy)
782 return CSSValueID::kFantasy;
783 if (family == font_family_names::kWebkitMonospace)
784 return CSSValueID::kMonospace;
785 if (family == font_family_names::kWebkitSansSerif)
786 return CSSValueID::kSansSerif;
787 if (family == font_family_names::kWebkitSerif)
788 return CSSValueID::kSerif;
789 return CSSValueID::kInvalid;
790 }
791
ValueForFamily(const AtomicString & family)792 CSSValue* ValueForFamily(const AtomicString& family) {
793 CSSValueID family_identifier = IdentifierForFamily(family);
794 if (IsValidCSSValueID(family_identifier))
795 return CSSIdentifierValue::Create(family_identifier);
796 return CSSFontFamilyValue::Create(family.GetString());
797 }
798
ValueForFontFamily(const ComputedStyle & style)799 CSSValueList* ComputedStyleUtils::ValueForFontFamily(
800 const ComputedStyle& style) {
801 const FontFamily& first_family = style.GetFontDescription().Family();
802 CSSValueList* list = CSSValueList::CreateCommaSeparated();
803 for (const FontFamily* family = &first_family; family;
804 family = family->Next())
805 list->Append(*ValueForFamily(family->Family()));
806 return list;
807 }
808
ValueForFontSize(const ComputedStyle & style)809 CSSPrimitiveValue* ComputedStyleUtils::ValueForFontSize(
810 const ComputedStyle& style) {
811 return ZoomAdjustedPixelValue(style.GetFontDescription().ComputedSize(),
812 style);
813 }
814
ValueForFontStretch(const ComputedStyle & style)815 CSSPrimitiveValue* ComputedStyleUtils::ValueForFontStretch(
816 const ComputedStyle& style) {
817 return CSSNumericLiteralValue::Create(
818 style.GetFontDescription().Stretch(),
819 CSSPrimitiveValue::UnitType::kPercentage);
820 }
821
ValueForFontStyle(const ComputedStyle & style)822 CSSValue* ComputedStyleUtils::ValueForFontStyle(const ComputedStyle& style) {
823 FontSelectionValue angle = style.GetFontDescription().Style();
824 if (angle == NormalSlopeValue()) {
825 return CSSIdentifierValue::Create(CSSValueID::kNormal);
826 }
827
828 if (angle == ItalicSlopeValue()) {
829 return CSSIdentifierValue::Create(CSSValueID::kItalic);
830 }
831
832 // The spec says: 'The lack of a number represents an angle of
833 // "20deg"', but since we compute that to 'italic' (handled above),
834 // we don't perform any special treatment of that value here.
835 CSSValueList* oblique_values = CSSValueList::CreateSpaceSeparated();
836 oblique_values->Append(*CSSNumericLiteralValue::Create(
837 angle, CSSPrimitiveValue::UnitType::kDegrees));
838 return MakeGarbageCollected<cssvalue::CSSFontStyleRangeValue>(
839 *CSSIdentifierValue::Create(CSSValueID::kOblique), *oblique_values);
840 }
841
ValueForFontWeight(const ComputedStyle & style)842 CSSNumericLiteralValue* ComputedStyleUtils::ValueForFontWeight(
843 const ComputedStyle& style) {
844 return CSSNumericLiteralValue::Create(style.GetFontDescription().Weight(),
845 CSSPrimitiveValue::UnitType::kNumber);
846 }
847
ValueForFontVariantCaps(const ComputedStyle & style)848 CSSIdentifierValue* ComputedStyleUtils::ValueForFontVariantCaps(
849 const ComputedStyle& style) {
850 FontDescription::FontVariantCaps variant_caps =
851 style.GetFontDescription().VariantCaps();
852 switch (variant_caps) {
853 case FontDescription::kCapsNormal:
854 return CSSIdentifierValue::Create(CSSValueID::kNormal);
855 case FontDescription::kSmallCaps:
856 return CSSIdentifierValue::Create(CSSValueID::kSmallCaps);
857 case FontDescription::kAllSmallCaps:
858 return CSSIdentifierValue::Create(CSSValueID::kAllSmallCaps);
859 case FontDescription::kPetiteCaps:
860 return CSSIdentifierValue::Create(CSSValueID::kPetiteCaps);
861 case FontDescription::kAllPetiteCaps:
862 return CSSIdentifierValue::Create(CSSValueID::kAllPetiteCaps);
863 case FontDescription::kUnicase:
864 return CSSIdentifierValue::Create(CSSValueID::kUnicase);
865 case FontDescription::kTitlingCaps:
866 return CSSIdentifierValue::Create(CSSValueID::kTitlingCaps);
867 default:
868 NOTREACHED();
869 return nullptr;
870 }
871 }
872
ValueForFontVariantLigatures(const ComputedStyle & style)873 CSSValue* ComputedStyleUtils::ValueForFontVariantLigatures(
874 const ComputedStyle& style) {
875 FontDescription::LigaturesState common_ligatures_state =
876 style.GetFontDescription().CommonLigaturesState();
877 FontDescription::LigaturesState discretionary_ligatures_state =
878 style.GetFontDescription().DiscretionaryLigaturesState();
879 FontDescription::LigaturesState historical_ligatures_state =
880 style.GetFontDescription().HistoricalLigaturesState();
881 FontDescription::LigaturesState contextual_ligatures_state =
882 style.GetFontDescription().ContextualLigaturesState();
883 if (common_ligatures_state == FontDescription::kNormalLigaturesState &&
884 discretionary_ligatures_state == FontDescription::kNormalLigaturesState &&
885 historical_ligatures_state == FontDescription::kNormalLigaturesState &&
886 contextual_ligatures_state == FontDescription::kNormalLigaturesState)
887 return CSSIdentifierValue::Create(CSSValueID::kNormal);
888
889 if (common_ligatures_state == FontDescription::kDisabledLigaturesState &&
890 discretionary_ligatures_state ==
891 FontDescription::kDisabledLigaturesState &&
892 historical_ligatures_state == FontDescription::kDisabledLigaturesState &&
893 contextual_ligatures_state == FontDescription::kDisabledLigaturesState)
894 return CSSIdentifierValue::Create(CSSValueID::kNone);
895
896 CSSValueList* value_list = CSSValueList::CreateSpaceSeparated();
897 if (common_ligatures_state != FontDescription::kNormalLigaturesState) {
898 value_list->Append(*CSSIdentifierValue::Create(
899 common_ligatures_state == FontDescription::kDisabledLigaturesState
900 ? CSSValueID::kNoCommonLigatures
901 : CSSValueID::kCommonLigatures));
902 }
903 if (discretionary_ligatures_state != FontDescription::kNormalLigaturesState) {
904 value_list->Append(*CSSIdentifierValue::Create(
905 discretionary_ligatures_state ==
906 FontDescription::kDisabledLigaturesState
907 ? CSSValueID::kNoDiscretionaryLigatures
908 : CSSValueID::kDiscretionaryLigatures));
909 }
910 if (historical_ligatures_state != FontDescription::kNormalLigaturesState) {
911 value_list->Append(*CSSIdentifierValue::Create(
912 historical_ligatures_state == FontDescription::kDisabledLigaturesState
913 ? CSSValueID::kNoHistoricalLigatures
914 : CSSValueID::kHistoricalLigatures));
915 }
916 if (contextual_ligatures_state != FontDescription::kNormalLigaturesState) {
917 value_list->Append(*CSSIdentifierValue::Create(
918 contextual_ligatures_state == FontDescription::kDisabledLigaturesState
919 ? CSSValueID::kNoContextual
920 : CSSValueID::kContextual));
921 }
922 return value_list;
923 }
924
ValueForFontVariantNumeric(const ComputedStyle & style)925 CSSValue* ComputedStyleUtils::ValueForFontVariantNumeric(
926 const ComputedStyle& style) {
927 FontVariantNumeric variant_numeric =
928 style.GetFontDescription().VariantNumeric();
929 if (variant_numeric.IsAllNormal())
930 return CSSIdentifierValue::Create(CSSValueID::kNormal);
931
932 CSSValueList* value_list = CSSValueList::CreateSpaceSeparated();
933 if (variant_numeric.NumericFigureValue() !=
934 FontVariantNumeric::kNormalFigure) {
935 value_list->Append(*CSSIdentifierValue::Create(
936 variant_numeric.NumericFigureValue() == FontVariantNumeric::kLiningNums
937 ? CSSValueID::kLiningNums
938 : CSSValueID::kOldstyleNums));
939 }
940 if (variant_numeric.NumericSpacingValue() !=
941 FontVariantNumeric::kNormalSpacing) {
942 value_list->Append(*CSSIdentifierValue::Create(
943 variant_numeric.NumericSpacingValue() ==
944 FontVariantNumeric::kProportionalNums
945 ? CSSValueID::kProportionalNums
946 : CSSValueID::kTabularNums));
947 }
948 if (variant_numeric.NumericFractionValue() !=
949 FontVariantNumeric::kNormalFraction) {
950 value_list->Append(*CSSIdentifierValue::Create(
951 variant_numeric.NumericFractionValue() ==
952 FontVariantNumeric::kDiagonalFractions
953 ? CSSValueID::kDiagonalFractions
954 : CSSValueID::kStackedFractions));
955 }
956 if (variant_numeric.OrdinalValue() == FontVariantNumeric::kOrdinalOn)
957 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kOrdinal));
958 if (variant_numeric.SlashedZeroValue() == FontVariantNumeric::kSlashedZeroOn)
959 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kSlashedZero));
960
961 return value_list;
962 }
963
ValueForFontStretchAsKeyword(const ComputedStyle & style)964 CSSIdentifierValue* ValueForFontStretchAsKeyword(const ComputedStyle& style) {
965 FontSelectionValue stretch_value = style.GetFontDescription().Stretch();
966 CSSValueID value_id = CSSValueID::kInvalid;
967 if (stretch_value == UltraCondensedWidthValue())
968 value_id = CSSValueID::kUltraCondensed;
969 if (stretch_value == UltraCondensedWidthValue())
970 value_id = CSSValueID::kUltraCondensed;
971 if (stretch_value == ExtraCondensedWidthValue())
972 value_id = CSSValueID::kExtraCondensed;
973 if (stretch_value == CondensedWidthValue())
974 value_id = CSSValueID::kCondensed;
975 if (stretch_value == SemiCondensedWidthValue())
976 value_id = CSSValueID::kSemiCondensed;
977 if (stretch_value == NormalWidthValue())
978 value_id = CSSValueID::kNormal;
979 if (stretch_value == SemiExpandedWidthValue())
980 value_id = CSSValueID::kSemiExpanded;
981 if (stretch_value == ExpandedWidthValue())
982 value_id = CSSValueID::kExpanded;
983 if (stretch_value == ExtraExpandedWidthValue())
984 value_id = CSSValueID::kExtraExpanded;
985 if (stretch_value == UltraExpandedWidthValue())
986 value_id = CSSValueID::kUltraExpanded;
987
988 if (IsValidCSSValueID(value_id))
989 return CSSIdentifierValue::Create(value_id);
990 return nullptr;
991 }
992
ValueForFontVariantEastAsian(const ComputedStyle & style)993 CSSValue* ComputedStyleUtils::ValueForFontVariantEastAsian(
994 const ComputedStyle& style) {
995 FontVariantEastAsian east_asian =
996 style.GetFontDescription().VariantEastAsian();
997 if (east_asian.IsAllNormal())
998 return CSSIdentifierValue::Create(CSSValueID::kNormal);
999
1000 CSSValueList* value_list = CSSValueList::CreateSpaceSeparated();
1001 switch (east_asian.Form()) {
1002 case FontVariantEastAsian::kNormalForm:
1003 break;
1004 case FontVariantEastAsian::kJis78:
1005 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis78));
1006 break;
1007 case FontVariantEastAsian::kJis83:
1008 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis83));
1009 break;
1010 case FontVariantEastAsian::kJis90:
1011 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis90));
1012 break;
1013 case FontVariantEastAsian::kJis04:
1014 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis04));
1015 break;
1016 case FontVariantEastAsian::kSimplified:
1017 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kSimplified));
1018 break;
1019 case FontVariantEastAsian::kTraditional:
1020 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kTraditional));
1021 break;
1022 default:
1023 NOTREACHED();
1024 }
1025 switch (east_asian.Width()) {
1026 case FontVariantEastAsian::kNormalWidth:
1027 break;
1028 case FontVariantEastAsian::kFullWidth:
1029 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kFullWidth));
1030 break;
1031 case FontVariantEastAsian::kProportionalWidth:
1032 value_list->Append(
1033 *CSSIdentifierValue::Create(CSSValueID::kProportionalWidth));
1034 break;
1035 default:
1036 NOTREACHED();
1037 }
1038 if (east_asian.Ruby())
1039 value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kRuby));
1040 return value_list;
1041 }
1042
ValueForFont(const ComputedStyle & style)1043 CSSValue* ComputedStyleUtils::ValueForFont(const ComputedStyle& style) {
1044 auto AppendIfNotNormal = [](CSSValueList* list, const CSSValue& value) {
1045 auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
1046 if (identifier_value &&
1047 identifier_value->GetValueID() == CSSValueID::kNormal) {
1048 return;
1049 }
1050
1051 list->Append(value);
1052 };
1053
1054 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1055 AppendIfNotNormal(list, *ValueForFontStyle(style));
1056
1057 // Check that non-initial font-variant subproperties are not conflicting with
1058 // this serialization.
1059 CSSValue* ligatures_value = ValueForFontVariantLigatures(style);
1060 CSSValue* numeric_value = ValueForFontVariantNumeric(style);
1061 CSSValue* east_asian_value = ValueForFontVariantEastAsian(style);
1062 // FIXME: Use DataEquivalent<CSSValue>(...) once http://crbug.com/729447 is
1063 // resolved.
1064 if (!DataEquivalent(ligatures_value,
1065 static_cast<CSSValue*>(
1066 CSSIdentifierValue::Create(CSSValueID::kNormal))) ||
1067 !DataEquivalent(numeric_value,
1068 static_cast<CSSValue*>(
1069 CSSIdentifierValue::Create(CSSValueID::kNormal))) ||
1070 !DataEquivalent(east_asian_value,
1071 static_cast<CSSValue*>(
1072 CSSIdentifierValue::Create(CSSValueID::kNormal))))
1073 return nullptr;
1074
1075 if (!ValueForFontStretchAsKeyword(style))
1076 return nullptr;
1077
1078 CSSIdentifierValue* caps_value = ValueForFontVariantCaps(style);
1079 if (caps_value->GetValueID() != CSSValueID::kNormal &&
1080 caps_value->GetValueID() != CSSValueID::kSmallCaps)
1081 return nullptr;
1082 AppendIfNotNormal(list, *caps_value);
1083
1084 {
1085 CSSNumericLiteralValue* font_weight = ValueForFontWeight(style);
1086 if (font_weight->DoubleValue() != NormalWeightValue())
1087 list->Append(*font_weight);
1088 }
1089
1090 AppendIfNotNormal(list, *ValueForFontStretchAsKeyword(style));
1091
1092 {
1093 CSSValue* line_height = ValueForLineHeight(style);
1094 auto* identifier_line_height = DynamicTo<CSSIdentifierValue>(line_height);
1095 if (identifier_line_height &&
1096 identifier_line_height->GetValueID() == CSSValueID::kNormal) {
1097 list->Append(*ValueForFontSize(style));
1098 } else {
1099 // Add a slash between size and line-height.
1100 CSSValueList* size_and_line_height = CSSValueList::CreateSlashSeparated();
1101 size_and_line_height->Append(*ValueForFontSize(style));
1102 size_and_line_height->Append(*line_height);
1103
1104 list->Append(*size_and_line_height);
1105 }
1106 }
1107
1108 list->Append(*ValueForFontFamily(style));
1109
1110 return list;
1111 }
1112
SpecifiedValueForGridTrackBreadth(const GridLength & track_breadth,const ComputedStyle & style)1113 CSSValue* SpecifiedValueForGridTrackBreadth(const GridLength& track_breadth,
1114 const ComputedStyle& style) {
1115 if (!track_breadth.IsLength()) {
1116 return CSSNumericLiteralValue::Create(
1117 track_breadth.Flex(), CSSPrimitiveValue::UnitType::kFraction);
1118 }
1119
1120 const Length& track_breadth_length = track_breadth.length();
1121 if (track_breadth_length.IsAuto())
1122 return CSSIdentifierValue::Create(CSSValueID::kAuto);
1123 return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
1124 track_breadth_length, style);
1125 }
1126
SpecifiedValueForGridTrackSize(const GridTrackSize & track_size,const ComputedStyle & style)1127 CSSValue* ComputedStyleUtils::SpecifiedValueForGridTrackSize(
1128 const GridTrackSize& track_size,
1129 const ComputedStyle& style) {
1130 switch (track_size.GetType()) {
1131 case kLengthTrackSizing:
1132 return SpecifiedValueForGridTrackBreadth(track_size.MinTrackBreadth(),
1133 style);
1134 case kMinMaxTrackSizing: {
1135 if (track_size.MinTrackBreadth().IsAuto() &&
1136 track_size.MaxTrackBreadth().IsFlex()) {
1137 return CSSNumericLiteralValue::Create(
1138 track_size.MaxTrackBreadth().Flex(),
1139 CSSPrimitiveValue::UnitType::kFraction);
1140 }
1141
1142 auto* min_max_track_breadths =
1143 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMinmax);
1144 min_max_track_breadths->Append(*SpecifiedValueForGridTrackBreadth(
1145 track_size.MinTrackBreadth(), style));
1146 min_max_track_breadths->Append(*SpecifiedValueForGridTrackBreadth(
1147 track_size.MaxTrackBreadth(), style));
1148 return min_max_track_breadths;
1149 }
1150 case kFitContentTrackSizing: {
1151 auto* fit_content_track_breadth =
1152 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kFitContent);
1153 fit_content_track_breadth->Append(*SpecifiedValueForGridTrackBreadth(
1154 track_size.FitContentTrackBreadth(), style));
1155 return fit_content_track_breadth;
1156 }
1157 }
1158 NOTREACHED();
1159 return nullptr;
1160 }
1161
1162 class OrderedNamedLinesCollector {
1163 STACK_ALLOCATED();
1164
1165 public:
OrderedNamedLinesCollector(const ComputedStyle & style,bool is_row_axis)1166 OrderedNamedLinesCollector(const ComputedStyle& style, bool is_row_axis)
1167 : ordered_named_grid_lines_(is_row_axis
1168 ? style.OrderedNamedGridColumnLines()
1169 : style.OrderedNamedGridRowLines()),
1170 ordered_named_auto_repeat_grid_lines_(
1171 is_row_axis ? style.AutoRepeatOrderedNamedGridColumnLines()
1172 : style.AutoRepeatOrderedNamedGridRowLines()) {}
1173 OrderedNamedLinesCollector(const OrderedNamedLinesCollector&) = delete;
1174 OrderedNamedLinesCollector& operator=(const OrderedNamedLinesCollector&) =
1175 delete;
1176 virtual ~OrderedNamedLinesCollector() = default;
1177
IsEmpty() const1178 bool IsEmpty() const {
1179 return ordered_named_grid_lines_.IsEmpty() &&
1180 ordered_named_auto_repeat_grid_lines_.IsEmpty();
1181 }
1182 virtual void CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue&,
1183 size_t index) const;
1184
1185 protected:
1186 enum NamedLinesType { kNamedLines, kAutoRepeatNamedLines };
1187 void AppendLines(cssvalue::CSSGridLineNamesValue&,
1188 size_t index,
1189 NamedLinesType) const;
1190
1191 const OrderedNamedGridLines& ordered_named_grid_lines_;
1192 const OrderedNamedGridLines& ordered_named_auto_repeat_grid_lines_;
1193 };
1194
1195 class OrderedNamedLinesCollectorInsideRepeat
1196 : public OrderedNamedLinesCollector {
1197 public:
OrderedNamedLinesCollectorInsideRepeat(const ComputedStyle & style,bool is_row_axis)1198 OrderedNamedLinesCollectorInsideRepeat(const ComputedStyle& style,
1199 bool is_row_axis)
1200 : OrderedNamedLinesCollector(style, is_row_axis) {}
1201 void CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue&,
1202 size_t index) const override;
1203 };
1204
1205 class OrderedNamedLinesCollectorInGridLayout
1206 : public OrderedNamedLinesCollector {
1207 public:
OrderedNamedLinesCollectorInGridLayout(const ComputedStyle & style,bool is_row_axis,size_t auto_repeat_tracks_count,size_t auto_repeat_track_list_length)1208 OrderedNamedLinesCollectorInGridLayout(const ComputedStyle& style,
1209 bool is_row_axis,
1210 size_t auto_repeat_tracks_count,
1211 size_t auto_repeat_track_list_length)
1212 : OrderedNamedLinesCollector(style, is_row_axis),
1213 insertion_point_(is_row_axis
1214 ? style.GridAutoRepeatColumnsInsertionPoint()
1215 : style.GridAutoRepeatRowsInsertionPoint()),
1216 auto_repeat_total_tracks_(auto_repeat_tracks_count),
1217 auto_repeat_track_list_length_(auto_repeat_track_list_length) {}
1218 void CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue&,
1219 size_t index) const override;
1220
1221 private:
1222 size_t insertion_point_;
1223 size_t auto_repeat_total_tracks_;
1224 size_t auto_repeat_track_list_length_;
1225 };
1226
1227 // RJW
AppendLines(cssvalue::CSSGridLineNamesValue & line_names_value,size_t index,NamedLinesType type) const1228 void OrderedNamedLinesCollector::AppendLines(
1229 cssvalue::CSSGridLineNamesValue& line_names_value,
1230 size_t index,
1231 NamedLinesType type) const {
1232 auto iter = type == kNamedLines
1233 ? ordered_named_grid_lines_.find(index)
1234 : ordered_named_auto_repeat_grid_lines_.find(index);
1235 auto end_iter = type == kNamedLines
1236 ? ordered_named_grid_lines_.end()
1237 : ordered_named_auto_repeat_grid_lines_.end();
1238 if (iter == end_iter)
1239 return;
1240
1241 for (auto line_name : iter->value) {
1242 line_names_value.Append(
1243 *MakeGarbageCollected<CSSCustomIdentValue>(AtomicString(line_name)));
1244 }
1245 }
1246
CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue & line_names_value,size_t i) const1247 void OrderedNamedLinesCollector::CollectLineNamesForIndex(
1248 cssvalue::CSSGridLineNamesValue& line_names_value,
1249 size_t i) const {
1250 DCHECK(!IsEmpty());
1251 AppendLines(line_names_value, i, kNamedLines);
1252 }
1253
CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue & line_names_value,size_t i) const1254 void OrderedNamedLinesCollectorInsideRepeat::CollectLineNamesForIndex(
1255 cssvalue::CSSGridLineNamesValue& line_names_value,
1256 size_t i) const {
1257 DCHECK(!IsEmpty());
1258 AppendLines(line_names_value, i, kAutoRepeatNamedLines);
1259 }
1260
1261 // RJW
CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue & line_names_value,size_t i) const1262 void OrderedNamedLinesCollectorInGridLayout::CollectLineNamesForIndex(
1263 cssvalue::CSSGridLineNamesValue& line_names_value,
1264 size_t i) const {
1265 DCHECK(!IsEmpty());
1266 if (auto_repeat_track_list_length_ == 0LU || i < insertion_point_) {
1267 AppendLines(line_names_value, i, kNamedLines);
1268 return;
1269 }
1270
1271 DCHECK(auto_repeat_total_tracks_);
1272
1273 if (i > insertion_point_ + auto_repeat_total_tracks_) {
1274 AppendLines(line_names_value, i - (auto_repeat_total_tracks_ - 1),
1275 kNamedLines);
1276 return;
1277 }
1278
1279 if (i == insertion_point_) {
1280 AppendLines(line_names_value, i, kNamedLines);
1281 AppendLines(line_names_value, 0, kAutoRepeatNamedLines);
1282 return;
1283 }
1284
1285 if (i == insertion_point_ + auto_repeat_total_tracks_) {
1286 AppendLines(line_names_value, auto_repeat_track_list_length_,
1287 kAutoRepeatNamedLines);
1288 AppendLines(line_names_value, insertion_point_ + 1, kNamedLines);
1289 return;
1290 }
1291
1292 size_t auto_repeat_index_in_first_repetition =
1293 (i - insertion_point_) % auto_repeat_track_list_length_;
1294 if (!auto_repeat_index_in_first_repetition && i > insertion_point_) {
1295 AppendLines(line_names_value, auto_repeat_track_list_length_,
1296 kAutoRepeatNamedLines);
1297 }
1298 AppendLines(line_names_value, auto_repeat_index_in_first_repetition,
1299 kAutoRepeatNamedLines);
1300 }
1301
AddValuesForNamedGridLinesAtIndex(OrderedNamedLinesCollector & collector,size_t i,CSSValueList & list)1302 void AddValuesForNamedGridLinesAtIndex(OrderedNamedLinesCollector& collector,
1303 size_t i,
1304 CSSValueList& list) {
1305 if (collector.IsEmpty())
1306 return;
1307
1308 auto* line_names = MakeGarbageCollected<cssvalue::CSSGridLineNamesValue>();
1309 collector.CollectLineNamesForIndex(*line_names, i);
1310 if (line_names->length())
1311 list.Append(*line_names);
1312 }
1313
ValueForGridTrackSizeList(GridTrackSizingDirection direction,const ComputedStyle & style)1314 CSSValue* ComputedStyleUtils::ValueForGridTrackSizeList(
1315 GridTrackSizingDirection direction,
1316 const ComputedStyle& style) {
1317 const Vector<GridTrackSize>& auto_track_sizes =
1318 direction == kForColumns ? style.GridAutoColumns().LegacyTrackList()
1319 : style.GridAutoRows().LegacyTrackList();
1320
1321 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1322 for (auto& track_size : auto_track_sizes) {
1323 list->Append(*SpecifiedValueForGridTrackSize(track_size, style));
1324 }
1325 return list;
1326 }
1327
1328 template <typename T, typename F>
PopulateGridTrackList(CSSValueList * list,OrderedNamedLinesCollector & collector,const Vector<T> & tracks,F getTrackSize,int start,int end,int offset=0)1329 void PopulateGridTrackList(CSSValueList* list,
1330 OrderedNamedLinesCollector& collector,
1331 const Vector<T>& tracks,
1332 F getTrackSize,
1333 int start,
1334 int end,
1335 int offset = 0) {
1336 DCHECK_LE(0, start);
1337 DCHECK_LE(start, end);
1338 DCHECK_LE((unsigned)end, tracks.size());
1339 for (int i = start; i < end; ++i) {
1340 if (i + offset >= 0)
1341 AddValuesForNamedGridLinesAtIndex(collector, i + offset, *list);
1342 list->Append(*getTrackSize(tracks[i]));
1343 }
1344 if (end + offset >= 0)
1345 AddValuesForNamedGridLinesAtIndex(collector, end + offset, *list);
1346 }
1347
1348 template <typename T, typename F>
PopulateGridTrackList(CSSValueList * list,OrderedNamedLinesCollector & collector,const Vector<T> & tracks,F getTrackSize,int offset=0)1349 void PopulateGridTrackList(CSSValueList* list,
1350 OrderedNamedLinesCollector& collector,
1351 const Vector<T>& tracks,
1352 F getTrackSize,
1353 int offset = 0) {
1354 PopulateGridTrackList<T>(list, collector, tracks, getTrackSize, 0,
1355 tracks.size(), offset);
1356 }
1357
ValueForGridTrackList(GridTrackSizingDirection direction,const LayoutObject * layout_object,const ComputedStyle & style)1358 CSSValue* ComputedStyleUtils::ValueForGridTrackList(
1359 GridTrackSizingDirection direction,
1360 const LayoutObject* layout_object,
1361 const ComputedStyle& style) {
1362 bool is_row_axis = direction == kForColumns;
1363 const Vector<GridTrackSize>& track_sizes =
1364 is_row_axis ? style.GridTemplateColumns().LegacyTrackList()
1365 : style.GridTemplateRows().LegacyTrackList();
1366 const Vector<GridTrackSize>& auto_repeat_track_sizes =
1367 is_row_axis ? style.GridAutoRepeatColumns() : style.GridAutoRepeatRows();
1368 bool is_layout_grid = layout_object && layout_object->IsLayoutGrid();
1369
1370 // Handle the 'none' case.
1371 bool track_list_is_empty =
1372 track_sizes.IsEmpty() && auto_repeat_track_sizes.IsEmpty();
1373 if (is_layout_grid && track_list_is_empty) {
1374 // For grids we should consider every listed track, whether implicitly or
1375 // explicitly created. Empty grids have a sole grid line per axis.
1376 auto& positions = is_row_axis
1377 ? ToLayoutGrid(layout_object)->ColumnPositions()
1378 : ToLayoutGrid(layout_object)->RowPositions();
1379 track_list_is_empty = positions.size() == 1;
1380 }
1381
1382 if (track_list_is_empty)
1383 return CSSIdentifierValue::Create(CSSValueID::kNone);
1384
1385 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1386
1387 // If the element is a grid container, the resolved value is the used value,
1388 // specifying track sizes in pixels and expanding the repeat() notation.
1389 if (is_layout_grid) {
1390 const auto* grid = ToLayoutGrid(layout_object);
1391 OrderedNamedLinesCollectorInGridLayout collector(
1392 style, is_row_axis, grid->AutoRepeatCountForDirection(direction),
1393 auto_repeat_track_sizes.size());
1394 // Named grid line indices are relative to the explicit grid, but we are
1395 // including all tracks. So we need to subtract the number of leading
1396 // implicit tracks in order to get the proper line index.
1397 int offset = -grid->ExplicitGridStartForDirection(direction);
1398 PopulateGridTrackList(
1399 list, collector, grid->TrackSizesForComputedStyle(direction),
1400 [&](const LayoutUnit& v) { return ZoomAdjustedPixelValue(v, style); },
1401 offset);
1402 return list;
1403 }
1404
1405 // Otherwise, the resolved value is the computed value, preserving repeat().
1406 OrderedNamedLinesCollector collector(style, is_row_axis);
1407 auto getTrackSize = [&](const GridTrackSize& v) {
1408 return SpecifiedValueForGridTrackSize(v, style);
1409 };
1410
1411 if (auto_repeat_track_sizes.IsEmpty()) {
1412 // If there's no auto repeat(), just add all the line names and track sizes.
1413 PopulateGridTrackList(list, collector, track_sizes, getTrackSize);
1414 return list;
1415 }
1416
1417 // Add the line names and track sizes that precede the auto repeat().
1418 size_t auto_repeat_insertion_point =
1419 is_row_axis ? style.GridAutoRepeatColumnsInsertionPoint()
1420 : style.GridAutoRepeatRowsInsertionPoint();
1421 PopulateGridTrackList(list, collector, track_sizes, getTrackSize, 0,
1422 auto_repeat_insertion_point);
1423
1424 // Add a CSSGridAutoRepeatValue with the contents of the auto repeat().
1425 AutoRepeatType auto_repeat_type = is_row_axis
1426 ? style.GridAutoRepeatColumnsType()
1427 : style.GridAutoRepeatRowsType();
1428 CSSValueList* repeated_values =
1429 MakeGarbageCollected<cssvalue::CSSGridAutoRepeatValue>(
1430 auto_repeat_type == AutoRepeatType::kAutoFill ? CSSValueID::kAutoFill
1431 : CSSValueID::kAutoFit);
1432 OrderedNamedLinesCollectorInsideRepeat repeat_collector(style, is_row_axis);
1433 PopulateGridTrackList(repeated_values, repeat_collector,
1434 auto_repeat_track_sizes, getTrackSize);
1435 list->Append(*repeated_values);
1436
1437 // Add the line names and track sizes that follow the auto repeat().
1438 PopulateGridTrackList(list, collector, track_sizes, getTrackSize,
1439 auto_repeat_insertion_point, track_sizes.size(), 1);
1440 return list;
1441 }
1442
ValueForGridPosition(const GridPosition & position)1443 CSSValue* ComputedStyleUtils::ValueForGridPosition(
1444 const GridPosition& position) {
1445 if (position.IsAuto())
1446 return CSSIdentifierValue::Create(CSSValueID::kAuto);
1447
1448 if (position.IsNamedGridArea())
1449 return MakeGarbageCollected<CSSCustomIdentValue>(position.NamedGridLine());
1450
1451 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1452 if (position.IsSpan()) {
1453 list->Append(*CSSIdentifierValue::Create(CSSValueID::kSpan));
1454 list->Append(*CSSNumericLiteralValue::Create(
1455 position.SpanPosition(), CSSPrimitiveValue::UnitType::kNumber));
1456 } else {
1457 list->Append(*CSSNumericLiteralValue::Create(
1458 position.IntegerPosition(), CSSPrimitiveValue::UnitType::kNumber));
1459 }
1460
1461 if (!position.NamedGridLine().IsNull()) {
1462 list->Append(
1463 *MakeGarbageCollected<CSSCustomIdentValue>(position.NamedGridLine()));
1464 }
1465 return list;
1466 }
1467
IsSVGObjectWithWidthAndHeight(const LayoutObject & layout_object)1468 static bool IsSVGObjectWithWidthAndHeight(const LayoutObject& layout_object) {
1469 DCHECK(layout_object.IsSVGChild());
1470 return layout_object.IsSVGImage() || layout_object.IsSVGForeignObject() ||
1471 (layout_object.IsSVGShape() &&
1472 IsA<SVGRectElement>(layout_object.GetNode()));
1473 }
1474
UsedBoxSize(const LayoutObject & layout_object)1475 FloatSize ComputedStyleUtils::UsedBoxSize(const LayoutObject& layout_object) {
1476 if (layout_object.IsSVGChild() &&
1477 IsSVGObjectWithWidthAndHeight(layout_object)) {
1478 FloatSize size(layout_object.ObjectBoundingBox().Size());
1479 // The object bounding box does not have zoom applied. Multiply with zoom
1480 // here since we'll divide by it when we produce the CSS value.
1481 size.Scale(layout_object.StyleRef().EffectiveZoom());
1482 return size;
1483 }
1484 if (!layout_object.IsBox())
1485 return FloatSize();
1486 const auto& box = To<LayoutBox>(layout_object);
1487 return FloatSize(box.StyleRef().BoxSizing() == EBoxSizing::kBorderBox
1488 ? box.BorderBoxRect().Size()
1489 : box.ComputedCSSContentBoxRect().Size());
1490 }
1491
RenderTextDecorationFlagsToCSSValue(TextDecoration text_decoration)1492 CSSValue* ComputedStyleUtils::RenderTextDecorationFlagsToCSSValue(
1493 TextDecoration text_decoration) {
1494 // Blink value is ignored.
1495 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1496 if (EnumHasFlags(text_decoration, TextDecoration::kUnderline))
1497 list->Append(*CSSIdentifierValue::Create(CSSValueID::kUnderline));
1498 if (EnumHasFlags(text_decoration, TextDecoration::kOverline))
1499 list->Append(*CSSIdentifierValue::Create(CSSValueID::kOverline));
1500 if (EnumHasFlags(text_decoration, TextDecoration::kLineThrough))
1501 list->Append(*CSSIdentifierValue::Create(CSSValueID::kLineThrough));
1502
1503 if (!list->length())
1504 return CSSIdentifierValue::Create(CSSValueID::kNone);
1505 return list;
1506 }
1507
ValueForTextDecorationStyle(ETextDecorationStyle text_decoration_style)1508 CSSValue* ComputedStyleUtils::ValueForTextDecorationStyle(
1509 ETextDecorationStyle text_decoration_style) {
1510 switch (text_decoration_style) {
1511 case ETextDecorationStyle::kSolid:
1512 return CSSIdentifierValue::Create(CSSValueID::kSolid);
1513 case ETextDecorationStyle::kDouble:
1514 return CSSIdentifierValue::Create(CSSValueID::kDouble);
1515 case ETextDecorationStyle::kDotted:
1516 return CSSIdentifierValue::Create(CSSValueID::kDotted);
1517 case ETextDecorationStyle::kDashed:
1518 return CSSIdentifierValue::Create(CSSValueID::kDashed);
1519 case ETextDecorationStyle::kWavy:
1520 return CSSIdentifierValue::Create(CSSValueID::kWavy);
1521 }
1522
1523 NOTREACHED();
1524 return CSSInitialValue::Create();
1525 }
1526
ValueForTextDecorationSkipInk(ETextDecorationSkipInk text_decoration_skip_ink)1527 CSSValue* ComputedStyleUtils::ValueForTextDecorationSkipInk(
1528 ETextDecorationSkipInk text_decoration_skip_ink) {
1529 if (text_decoration_skip_ink == ETextDecorationSkipInk::kNone)
1530 return CSSIdentifierValue::Create(CSSValueID::kNone);
1531 return CSSIdentifierValue::Create(CSSValueID::kAuto);
1532 }
1533
TouchActionFlagsToCSSValue(TouchAction touch_action)1534 CSSValue* ComputedStyleUtils::TouchActionFlagsToCSSValue(
1535 TouchAction touch_action) {
1536 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1537 if (touch_action == TouchAction::kAuto) {
1538 list->Append(*CSSIdentifierValue::Create(CSSValueID::kAuto));
1539 } else if (touch_action == TouchAction::kNone) {
1540 list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone));
1541 } else if (touch_action == TouchAction::kManipulation) {
1542 list->Append(*CSSIdentifierValue::Create(CSSValueID::kManipulation));
1543 } else {
1544 if ((touch_action & TouchAction::kPanX) == TouchAction::kPanX)
1545 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanX));
1546 else if ((touch_action & TouchAction::kPanLeft) != TouchAction::kNone)
1547 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanLeft));
1548 else if ((touch_action & TouchAction::kPanRight) != TouchAction::kNone)
1549 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanRight));
1550 if ((touch_action & TouchAction::kPanY) == TouchAction::kPanY)
1551 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanY));
1552 else if ((touch_action & TouchAction::kPanUp) != TouchAction::kNone)
1553 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanUp));
1554 else if ((touch_action & TouchAction::kPanDown) != TouchAction::kNone)
1555 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanDown));
1556
1557 if ((touch_action & TouchAction::kPinchZoom) == TouchAction::kPinchZoom)
1558 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPinchZoom));
1559 }
1560
1561 DCHECK(list->length());
1562 return list;
1563 }
1564
ValueForWillChange(const Vector<CSSPropertyID> & will_change_properties,bool will_change_contents,bool will_change_scroll_position)1565 CSSValue* ComputedStyleUtils::ValueForWillChange(
1566 const Vector<CSSPropertyID>& will_change_properties,
1567 bool will_change_contents,
1568 bool will_change_scroll_position) {
1569 CSSValueList* list = CSSValueList::CreateCommaSeparated();
1570 if (will_change_contents)
1571 list->Append(*CSSIdentifierValue::Create(CSSValueID::kContents));
1572 if (will_change_scroll_position)
1573 list->Append(*CSSIdentifierValue::Create(CSSValueID::kScrollPosition));
1574 for (wtf_size_t i = 0; i < will_change_properties.size(); ++i) {
1575 list->Append(
1576 *MakeGarbageCollected<CSSCustomIdentValue>(will_change_properties[i]));
1577 }
1578 if (!list->length())
1579 list->Append(*CSSIdentifierValue::Create(CSSValueID::kAuto));
1580 return list;
1581 }
1582
ValueForAnimationDelay(const CSSTimingData * timing_data)1583 CSSValue* ComputedStyleUtils::ValueForAnimationDelay(
1584 const CSSTimingData* timing_data) {
1585 CSSValueList* list = CSSValueList::CreateCommaSeparated();
1586 if (timing_data) {
1587 for (wtf_size_t i = 0; i < timing_data->DelayList().size(); ++i) {
1588 list->Append(*CSSNumericLiteralValue::Create(
1589 timing_data->DelayList()[i], CSSPrimitiveValue::UnitType::kSeconds));
1590 }
1591 } else {
1592 list->Append(*CSSNumericLiteralValue::Create(
1593 CSSTimingData::InitialDelay(), CSSPrimitiveValue::UnitType::kSeconds));
1594 }
1595 return list;
1596 }
1597
ValueForAnimationDirection(Timing::PlaybackDirection direction)1598 CSSValue* ComputedStyleUtils::ValueForAnimationDirection(
1599 Timing::PlaybackDirection direction) {
1600 switch (direction) {
1601 case Timing::PlaybackDirection::NORMAL:
1602 return CSSIdentifierValue::Create(CSSValueID::kNormal);
1603 case Timing::PlaybackDirection::ALTERNATE_NORMAL:
1604 return CSSIdentifierValue::Create(CSSValueID::kAlternate);
1605 case Timing::PlaybackDirection::REVERSE:
1606 return CSSIdentifierValue::Create(CSSValueID::kReverse);
1607 case Timing::PlaybackDirection::ALTERNATE_REVERSE:
1608 return CSSIdentifierValue::Create(CSSValueID::kAlternateReverse);
1609 default:
1610 NOTREACHED();
1611 return nullptr;
1612 }
1613 }
1614
ValueForAnimationDuration(const CSSTimingData * timing_data)1615 CSSValue* ComputedStyleUtils::ValueForAnimationDuration(
1616 const CSSTimingData* timing_data) {
1617 CSSValueList* list = CSSValueList::CreateCommaSeparated();
1618 if (timing_data) {
1619 for (wtf_size_t i = 0; i < timing_data->DurationList().size(); ++i) {
1620 list->Append(*CSSNumericLiteralValue::Create(
1621 timing_data->DurationList()[i],
1622 CSSPrimitiveValue::UnitType::kSeconds));
1623 }
1624 } else {
1625 list->Append(
1626 *CSSNumericLiteralValue::Create(CSSTimingData::InitialDuration(),
1627 CSSPrimitiveValue::UnitType::kSeconds));
1628 }
1629 return list;
1630 }
1631
ValueForAnimationFillMode(Timing::FillMode fill_mode)1632 CSSValue* ComputedStyleUtils::ValueForAnimationFillMode(
1633 Timing::FillMode fill_mode) {
1634 switch (fill_mode) {
1635 case Timing::FillMode::NONE:
1636 return CSSIdentifierValue::Create(CSSValueID::kNone);
1637 case Timing::FillMode::FORWARDS:
1638 return CSSIdentifierValue::Create(CSSValueID::kForwards);
1639 case Timing::FillMode::BACKWARDS:
1640 return CSSIdentifierValue::Create(CSSValueID::kBackwards);
1641 case Timing::FillMode::BOTH:
1642 return CSSIdentifierValue::Create(CSSValueID::kBoth);
1643 default:
1644 NOTREACHED();
1645 return nullptr;
1646 }
1647 }
1648
ValueForAnimationIterationCount(double iteration_count)1649 CSSValue* ComputedStyleUtils::ValueForAnimationIterationCount(
1650 double iteration_count) {
1651 if (iteration_count == std::numeric_limits<double>::infinity())
1652 return CSSIdentifierValue::Create(CSSValueID::kInfinite);
1653 return CSSNumericLiteralValue::Create(iteration_count,
1654 CSSPrimitiveValue::UnitType::kNumber);
1655 }
1656
ValueForAnimationPlayState(EAnimPlayState play_state)1657 CSSValue* ComputedStyleUtils::ValueForAnimationPlayState(
1658 EAnimPlayState play_state) {
1659 if (play_state == EAnimPlayState::kPlaying)
1660 return CSSIdentifierValue::Create(CSSValueID::kRunning);
1661 DCHECK_EQ(play_state, EAnimPlayState::kPaused);
1662 return CSSIdentifierValue::Create(CSSValueID::kPaused);
1663 }
1664
CreateTimingFunctionValue(const TimingFunction * timing_function)1665 CSSValue* ComputedStyleUtils::CreateTimingFunctionValue(
1666 const TimingFunction* timing_function) {
1667 switch (timing_function->GetType()) {
1668 case TimingFunction::Type::CUBIC_BEZIER: {
1669 const auto* bezier_timing_function =
1670 To<CubicBezierTimingFunction>(timing_function);
1671 if (bezier_timing_function->GetEaseType() !=
1672 CubicBezierTimingFunction::EaseType::CUSTOM) {
1673 CSSValueID value_id = CSSValueID::kInvalid;
1674 switch (bezier_timing_function->GetEaseType()) {
1675 case CubicBezierTimingFunction::EaseType::EASE:
1676 value_id = CSSValueID::kEase;
1677 break;
1678 case CubicBezierTimingFunction::EaseType::EASE_IN:
1679 value_id = CSSValueID::kEaseIn;
1680 break;
1681 case CubicBezierTimingFunction::EaseType::EASE_OUT:
1682 value_id = CSSValueID::kEaseOut;
1683 break;
1684 case CubicBezierTimingFunction::EaseType::EASE_IN_OUT:
1685 value_id = CSSValueID::kEaseInOut;
1686 break;
1687 default:
1688 NOTREACHED();
1689 return nullptr;
1690 }
1691 return CSSIdentifierValue::Create(value_id);
1692 }
1693 return MakeGarbageCollected<cssvalue::CSSCubicBezierTimingFunctionValue>(
1694 bezier_timing_function->X1(), bezier_timing_function->Y1(),
1695 bezier_timing_function->X2(), bezier_timing_function->Y2());
1696 }
1697
1698 case TimingFunction::Type::STEPS: {
1699 const auto* steps_timing_function =
1700 To<StepsTimingFunction>(timing_function);
1701 StepsTimingFunction::StepPosition position =
1702 steps_timing_function->GetStepPosition();
1703 int steps = steps_timing_function->NumberOfSteps();
1704
1705 // Canonical form of step timing function is step(n, type) or step(n) even
1706 // if initially parsed as step-start or step-end.
1707 return MakeGarbageCollected<cssvalue::CSSStepsTimingFunctionValue>(
1708 steps, position);
1709 }
1710
1711 default:
1712 return CSSIdentifierValue::Create(CSSValueID::kLinear);
1713 }
1714 }
1715
ValueForAnimationTimingFunction(const CSSTimingData * timing_data)1716 CSSValue* ComputedStyleUtils::ValueForAnimationTimingFunction(
1717 const CSSTimingData* timing_data) {
1718 CSSValueList* list = CSSValueList::CreateCommaSeparated();
1719 if (timing_data) {
1720 for (wtf_size_t i = 0; i < timing_data->TimingFunctionList().size(); ++i) {
1721 list->Append(*CreateTimingFunctionValue(
1722 timing_data->TimingFunctionList()[i].get()));
1723 }
1724 } else {
1725 list->Append(*CreateTimingFunctionValue(
1726 CSSTimingData::InitialTimingFunction().get()));
1727 }
1728 return list;
1729 }
1730
ValuesForBorderRadiusCorner(const LengthSize & radius,const ComputedStyle & style)1731 CSSValueList* ComputedStyleUtils::ValuesForBorderRadiusCorner(
1732 const LengthSize& radius,
1733 const ComputedStyle& style) {
1734 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1735 if (radius.Width().IsPercent()) {
1736 list->Append(*CSSNumericLiteralValue::Create(
1737 radius.Width().Percent(), CSSPrimitiveValue::UnitType::kPercentage));
1738 } else {
1739 list->Append(*ZoomAdjustedPixelValueForLength(radius.Width(), style));
1740 }
1741 if (radius.Height().IsPercent()) {
1742 list->Append(*CSSNumericLiteralValue::Create(
1743 radius.Height().Percent(), CSSPrimitiveValue::UnitType::kPercentage));
1744 } else {
1745 list->Append(*ZoomAdjustedPixelValueForLength(radius.Height(), style));
1746 }
1747 return list;
1748 }
1749
ValueForBorderRadiusCorner(const LengthSize & radius,const ComputedStyle & style)1750 CSSValue* ComputedStyleUtils::ValueForBorderRadiusCorner(
1751 const LengthSize& radius,
1752 const ComputedStyle& style) {
1753 return MakeGarbageCollected<CSSValuePair>(
1754 ZoomAdjustedPixelValueForLength(radius.Width(), style),
1755 ZoomAdjustedPixelValueForLength(radius.Height(), style),
1756 CSSValuePair::kDropIdenticalValues);
1757 }
1758
ValueForTransformationMatrix(const TransformationMatrix & matrix,float zoom,bool force_matrix3d)1759 CSSFunctionValue* ComputedStyleUtils::ValueForTransformationMatrix(
1760 const TransformationMatrix& matrix,
1761 float zoom,
1762 bool force_matrix3d) {
1763 if (matrix.IsAffine() && !force_matrix3d) {
1764 auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix);
1765 // CSS matrix values are returned in column-major order.
1766 double values[6] = {matrix.A(), matrix.B(), //
1767 matrix.C(), matrix.D(), //
1768 // E and F are pixel lengths so unzoom
1769 matrix.E() / zoom, matrix.F() / zoom};
1770 for (double value : values) {
1771 result->Append(*CSSNumericLiteralValue::Create(
1772 value, CSSPrimitiveValue::UnitType::kNumber));
1773 }
1774 return result;
1775 } else {
1776 CSSFunctionValue* result =
1777 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix3d);
1778 // CSS matrix values are returned in column-major order.
1779 double values[16] = {
1780 // Note that the transformation matrix operates on (Length^3 * R).
1781 // Each column contains 3 scalars followed by a reciprocal length
1782 // (with a value in 1/px) which must be unzoomed accordingly.
1783 matrix.M11(), matrix.M12(), matrix.M13(), matrix.M14() * zoom,
1784 matrix.M21(), matrix.M22(), matrix.M23(), matrix.M24() * zoom,
1785 matrix.M31(), matrix.M32(), matrix.M33(), matrix.M34() * zoom,
1786 // Last column has 3 pixel lengths and a scalar
1787 matrix.M41() / zoom, matrix.M42() / zoom, matrix.M43() / zoom,
1788 matrix.M44()};
1789 for (double value : values) {
1790 result->Append(*CSSNumericLiteralValue::Create(
1791 value, CSSPrimitiveValue::UnitType::kNumber));
1792 }
1793 return result;
1794 }
1795 }
1796
1797 // We collapse functions like translateX into translate, since we will reify
1798 // them as a translate anyway.
ValueForTransformOperation(const TransformOperation & operation,float zoom,FloatSize box_size)1799 CSSFunctionValue* ComputedStyleUtils::ValueForTransformOperation(
1800 const TransformOperation& operation,
1801 float zoom,
1802 FloatSize box_size) {
1803 switch (operation.GetType()) {
1804 case TransformOperation::kScaleX:
1805 case TransformOperation::kScaleY:
1806 case TransformOperation::kScaleZ:
1807 case TransformOperation::kScale:
1808 case TransformOperation::kScale3D: {
1809 const auto& scale = To<ScaleTransformOperation>(operation);
1810 CSSFunctionValue* result = MakeGarbageCollected<CSSFunctionValue>(
1811 operation.Is3DOperation() ? CSSValueID::kScale3d
1812 : CSSValueID::kScale);
1813 result->Append(*CSSNumericLiteralValue::Create(
1814 scale.X(), CSSPrimitiveValue::UnitType::kNumber));
1815 result->Append(*CSSNumericLiteralValue::Create(
1816 scale.Y(), CSSPrimitiveValue::UnitType::kNumber));
1817 if (operation.Is3DOperation()) {
1818 result->Append(*CSSNumericLiteralValue::Create(
1819 scale.Z(), CSSPrimitiveValue::UnitType::kNumber));
1820 }
1821 return result;
1822 }
1823 case TransformOperation::kTranslateX:
1824 case TransformOperation::kTranslateY:
1825 case TransformOperation::kTranslateZ:
1826 case TransformOperation::kTranslate:
1827 case TransformOperation::kTranslate3D: {
1828 const auto& translate = To<TranslateTransformOperation>(operation);
1829 CSSFunctionValue* result = MakeGarbageCollected<CSSFunctionValue>(
1830 operation.Is3DOperation() ? CSSValueID::kTranslate3d
1831 : CSSValueID::kTranslate);
1832 result->Append(*CSSPrimitiveValue::CreateFromLength(translate.X(), zoom));
1833 result->Append(*CSSPrimitiveValue::CreateFromLength(translate.Y(), zoom));
1834 if (operation.Is3DOperation()) {
1835 // Since this is pixel length, we must unzoom (CreateFromLength above
1836 // does the division internally).
1837 result->Append(*CSSNumericLiteralValue::Create(
1838 translate.Z() / zoom, CSSPrimitiveValue::UnitType::kPixels));
1839 }
1840 return result;
1841 }
1842 case TransformOperation::kRotateX:
1843 case TransformOperation::kRotateY:
1844 case TransformOperation::kRotate3D: {
1845 const auto& rotate = To<RotateTransformOperation>(operation);
1846 CSSFunctionValue* result =
1847 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kRotate3d);
1848 result->Append(*CSSNumericLiteralValue::Create(
1849 rotate.X(), CSSPrimitiveValue::UnitType::kNumber));
1850 result->Append(*CSSNumericLiteralValue::Create(
1851 rotate.Y(), CSSPrimitiveValue::UnitType::kNumber));
1852 result->Append(*CSSNumericLiteralValue::Create(
1853 rotate.Z(), CSSPrimitiveValue::UnitType::kNumber));
1854 result->Append(*CSSNumericLiteralValue::Create(
1855 rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
1856 return result;
1857 }
1858 case TransformOperation::kRotate: {
1859 const auto& rotate = To<RotateTransformOperation>(operation);
1860 auto* result =
1861 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kRotate);
1862 result->Append(*CSSNumericLiteralValue::Create(
1863 rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
1864 return result;
1865 }
1866 case TransformOperation::kRotateAroundOrigin: {
1867 // TODO(https://github.com/w3c/csswg-drafts/issues/5011):
1868 // Update this once there is consensus.
1869 TransformationMatrix matrix;
1870 operation.Apply(matrix, FloatSize(0, 0));
1871 return ValueForTransformationMatrix(matrix, zoom,
1872 /*force_matrix3d=*/false);
1873 }
1874 case TransformOperation::kSkewX: {
1875 const auto& skew = To<SkewTransformOperation>(operation);
1876 auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkewX);
1877 result->Append(*CSSNumericLiteralValue::Create(
1878 skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees));
1879 return result;
1880 }
1881 case TransformOperation::kSkewY: {
1882 const auto& skew = To<SkewTransformOperation>(operation);
1883 auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkewY);
1884 result->Append(*CSSNumericLiteralValue::Create(
1885 skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees));
1886 return result;
1887 }
1888 case TransformOperation::kSkew: {
1889 const auto& skew = To<SkewTransformOperation>(operation);
1890 auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkew);
1891 result->Append(*CSSNumericLiteralValue::Create(
1892 skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees));
1893 result->Append(*CSSNumericLiteralValue::Create(
1894 skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees));
1895 return result;
1896 }
1897 case TransformOperation::kPerspective: {
1898 const auto& perspective = To<PerspectiveTransformOperation>(operation);
1899 auto* result =
1900 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kPerspective);
1901 result->Append(*CSSNumericLiteralValue::Create(
1902 perspective.Perspective() / zoom,
1903 CSSPrimitiveValue::UnitType::kPixels));
1904 return result;
1905 }
1906 case TransformOperation::kMatrix: {
1907 const auto& matrix = To<MatrixTransformOperation>(operation).Matrix();
1908 return ValueForTransformationMatrix(matrix, zoom,
1909 /*force_matrix3d=*/false);
1910 }
1911 case TransformOperation::kMatrix3D: {
1912 const auto& matrix = To<Matrix3DTransformOperation>(operation).Matrix();
1913 // Force matrix3d serialization
1914 return ValueForTransformationMatrix(matrix, zoom,
1915 /*force_matrix3d=*/true);
1916 }
1917 case TransformOperation::kInterpolated:
1918 // TODO(https://github.com/w3c/csswg-drafts/issues/2854):
1919 // Deferred interpolations are currently unreperesentable in CSS.
1920 // This currently converts the operation to a matrix, using box_size if
1921 // provided, 0x0 if not (returning all but the relative translate
1922 // portion of the transform). Update this once the spec is updated.
1923 TransformationMatrix matrix;
1924 operation.Apply(matrix, box_size);
1925 return ValueForTransformationMatrix(matrix, zoom,
1926 /*force_matrix3d=*/false);
1927 }
1928 }
1929
ValueForTransformList(const TransformOperations & transform_list,float zoom,FloatSize box_size)1930 CSSValue* ComputedStyleUtils::ValueForTransformList(
1931 const TransformOperations& transform_list,
1932 float zoom,
1933 FloatSize box_size) {
1934 if (!transform_list.Operations().size())
1935 return CSSIdentifierValue::Create(CSSValueID::kNone);
1936
1937 CSSValueList* components = CSSValueList::CreateSpaceSeparated();
1938 for (const auto& operation : transform_list.Operations()) {
1939 CSSValue* op_value = ValueForTransformOperation(*operation, zoom, box_size);
1940 components->Append(*op_value);
1941 }
1942 return components;
1943 }
1944
ReferenceBoxForTransform(const LayoutObject & layout_object,UsePixelSnappedBox pixel_snap_box)1945 FloatRect ComputedStyleUtils::ReferenceBoxForTransform(
1946 const LayoutObject& layout_object,
1947 UsePixelSnappedBox pixel_snap_box) {
1948 if (layout_object.IsSVGChild())
1949 return TransformHelper::ComputeReferenceBox(layout_object);
1950 if (layout_object.IsBox()) {
1951 const auto& layout_box = To<LayoutBox>(layout_object);
1952 if (pixel_snap_box == kUsePixelSnappedBox)
1953 return FloatRect(layout_box.PixelSnappedBorderBoxRect());
1954 return FloatRect(layout_box.BorderBoxRect());
1955 }
1956 return FloatRect();
1957 }
1958
ComputedTransformList(const ComputedStyle & style,const LayoutObject * layout_object)1959 CSSValue* ComputedStyleUtils::ComputedTransformList(
1960 const ComputedStyle& style,
1961 const LayoutObject* layout_object) {
1962 FloatSize box_size(0, 0);
1963 if (layout_object)
1964 box_size = ReferenceBoxForTransform(*layout_object).Size();
1965
1966 return ValueForTransformList(style.Transform(), style.EffectiveZoom(),
1967 box_size);
1968 }
1969
ResolvedTransform(const LayoutObject * layout_object,const ComputedStyle & style)1970 CSSValue* ComputedStyleUtils::ResolvedTransform(
1971 const LayoutObject* layout_object,
1972 const ComputedStyle& style) {
1973 if (!layout_object || !style.HasTransform())
1974 return CSSIdentifierValue::Create(CSSValueID::kNone);
1975
1976 FloatRect reference_box = ReferenceBoxForTransform(*layout_object);
1977
1978 TransformationMatrix transform;
1979 style.ApplyTransform(transform, reference_box,
1980 ComputedStyle::kExcludeTransformOrigin,
1981 ComputedStyle::kExcludeMotionPath,
1982 ComputedStyle::kExcludeIndependentTransformProperties);
1983
1984 // FIXME: Need to print out individual functions
1985 // (https://bugs.webkit.org/show_bug.cgi?id=23924)
1986 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
1987 list->Append(*ValueForTransformationMatrix(transform, style.EffectiveZoom(),
1988 /*force_matrix3d=*/false));
1989
1990 return list;
1991 }
1992
CreateTransitionPropertyValue(const CSSTransitionData::TransitionProperty & property)1993 CSSValue* ComputedStyleUtils::CreateTransitionPropertyValue(
1994 const CSSTransitionData::TransitionProperty& property) {
1995 if (property.property_type == CSSTransitionData::kTransitionNone)
1996 return CSSIdentifierValue::Create(CSSValueID::kNone);
1997 if (property.property_type == CSSTransitionData::kTransitionUnknownProperty)
1998 return MakeGarbageCollected<CSSCustomIdentValue>(property.property_string);
1999 DCHECK_EQ(property.property_type,
2000 CSSTransitionData::kTransitionKnownProperty);
2001 return MakeGarbageCollected<CSSCustomIdentValue>(
2002 CSSUnresolvedProperty::Get(property.unresolved_property)
2003 .GetPropertyNameAtomicString());
2004 }
2005
ValueForTransitionProperty(const CSSTransitionData * transition_data)2006 CSSValue* ComputedStyleUtils::ValueForTransitionProperty(
2007 const CSSTransitionData* transition_data) {
2008 CSSValueList* list = CSSValueList::CreateCommaSeparated();
2009 if (transition_data) {
2010 for (wtf_size_t i = 0; i < transition_data->PropertyList().size(); ++i) {
2011 list->Append(
2012 *CreateTransitionPropertyValue(transition_data->PropertyList()[i]));
2013 }
2014 } else {
2015 list->Append(*CSSIdentifierValue::Create(CSSValueID::kAll));
2016 }
2017 return list;
2018 }
2019
ValueForQuoteType(const QuoteType quote_type)2020 CSSValueID ValueForQuoteType(const QuoteType quote_type) {
2021 switch (quote_type) {
2022 case QuoteType::kNoOpen:
2023 return CSSValueID::kNoOpenQuote;
2024 case QuoteType::kNoClose:
2025 return CSSValueID::kNoCloseQuote;
2026 case QuoteType::kClose:
2027 return CSSValueID::kCloseQuote;
2028 case QuoteType::kOpen:
2029 return CSSValueID::kOpenQuote;
2030 }
2031 NOTREACHED();
2032 return CSSValueID::kInvalid;
2033 }
2034
ValueForContentData(const ComputedStyle & style,bool allow_visited_style)2035 CSSValue* ComputedStyleUtils::ValueForContentData(const ComputedStyle& style,
2036 bool allow_visited_style) {
2037 if (style.ContentPreventsBoxGeneration())
2038 return CSSIdentifierValue::Create(CSSValueID::kNone);
2039 if (style.ContentBehavesAsNormal())
2040 return CSSIdentifierValue::Create(CSSValueID::kNormal);
2041
2042 CSSValueList* outer_list = CSSValueList::CreateSlashSeparated();
2043 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2044
2045 // Alternative text optionally specified after a forward slash appearing after
2046 // the last content list item.
2047 CSSStringValue* alt_text = nullptr;
2048 for (const ContentData* content_data = style.GetContentData(); content_data;
2049 content_data = content_data->Next()) {
2050 if (content_data->IsCounter()) {
2051 const CounterContent* counter =
2052 To<CounterContentData>(content_data)->Counter();
2053 DCHECK(counter);
2054 auto* identifier =
2055 MakeGarbageCollected<CSSCustomIdentValue>(counter->Identifier());
2056 auto* separator =
2057 MakeGarbageCollected<CSSStringValue>(counter->Separator());
2058 CSSValueID list_style_ident = CSSValueID::kNone;
2059 if (counter->ListStyle() != EListStyleType::kNone) {
2060 // TODO(sashab): Change this to use a converter instead of
2061 // CSSPrimitiveValueMappings.
2062 list_style_ident =
2063 CSSIdentifierValue::Create(counter->ListStyle())->GetValueID();
2064 }
2065 CSSIdentifierValue* list_style =
2066 CSSIdentifierValue::Create(list_style_ident);
2067 list->Append(*MakeGarbageCollected<cssvalue::CSSCounterValue>(
2068 identifier, list_style, separator));
2069 } else if (content_data->IsImage()) {
2070 const StyleImage* image = To<ImageContentData>(content_data)->GetImage();
2071 DCHECK(image);
2072 list->Append(*image->ComputedCSSValue(style, allow_visited_style));
2073 } else if (content_data->IsText()) {
2074 list->Append(*MakeGarbageCollected<CSSStringValue>(
2075 To<TextContentData>(content_data)->GetText()));
2076 } else if (content_data->IsQuote()) {
2077 const QuoteType quote_type = To<QuoteContentData>(content_data)->Quote();
2078 list->Append(*CSSIdentifierValue::Create(ValueForQuoteType(quote_type)));
2079 } else if (content_data->IsAltText()) {
2080 alt_text = MakeGarbageCollected<CSSStringValue>(
2081 To<AltTextContentData>(content_data)->GetText());
2082 } else {
2083 NOTREACHED();
2084 }
2085 }
2086 DCHECK(list->length());
2087
2088 outer_list->Append(*list);
2089 if (alt_text)
2090 outer_list->Append(*alt_text);
2091 return outer_list;
2092 }
2093
ValueForCounterDirectives(const ComputedStyle & style,CounterNode::Type type)2094 CSSValue* ComputedStyleUtils::ValueForCounterDirectives(
2095 const ComputedStyle& style,
2096 CounterNode::Type type) {
2097 const CounterDirectiveMap* map = style.GetCounterDirectives();
2098 if (!map)
2099 return CSSIdentifierValue::Create(CSSValueID::kNone);
2100
2101 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2102 for (const auto& item : *map) {
2103 bool is_valid_counter_value = false;
2104 switch (type) {
2105 case CounterNode::kIncrementType:
2106 is_valid_counter_value = item.value.IsIncrement();
2107 break;
2108 case CounterNode::kResetType:
2109 is_valid_counter_value = item.value.IsReset();
2110 break;
2111 case CounterNode::kSetType:
2112 is_valid_counter_value = item.value.IsSet();
2113 break;
2114 }
2115
2116 if (!is_valid_counter_value)
2117 continue;
2118
2119 list->Append(*MakeGarbageCollected<CSSCustomIdentValue>(item.key));
2120 int32_t number = 0;
2121 switch (type) {
2122 case CounterNode::kIncrementType:
2123 number = item.value.IncrementValue();
2124 break;
2125 case CounterNode::kResetType:
2126 number = item.value.ResetValue();
2127 break;
2128 case CounterNode::kSetType:
2129 number = item.value.SetValue();
2130 break;
2131 }
2132 list->Append(*CSSNumericLiteralValue::Create(
2133 (double)number, CSSPrimitiveValue::UnitType::kInteger));
2134 }
2135
2136 if (!list->length())
2137 return CSSIdentifierValue::Create(CSSValueID::kNone);
2138
2139 return list;
2140 }
2141
ValueForShape(const ComputedStyle & style,bool allow_visited_style,ShapeValue * shape_value)2142 CSSValue* ComputedStyleUtils::ValueForShape(const ComputedStyle& style,
2143 bool allow_visited_style,
2144 ShapeValue* shape_value) {
2145 if (!shape_value)
2146 return CSSIdentifierValue::Create(CSSValueID::kNone);
2147 if (shape_value->GetType() == ShapeValue::kBox)
2148 return CSSIdentifierValue::Create(shape_value->CssBox());
2149 if (shape_value->GetType() == ShapeValue::kImage) {
2150 if (shape_value->GetImage()) {
2151 return shape_value->GetImage()->ComputedCSSValue(style,
2152 allow_visited_style);
2153 }
2154 return CSSIdentifierValue::Create(CSSValueID::kNone);
2155 }
2156
2157 DCHECK_EQ(shape_value->GetType(), ShapeValue::kShape);
2158
2159 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2160 list->Append(*ValueForBasicShape(style, shape_value->Shape()));
2161 if (shape_value->CssBox() != CSSBoxType::kMissing)
2162 list->Append(*CSSIdentifierValue::Create(shape_value->CssBox()));
2163 return list;
2164 }
2165
ValueForBorderRadiusShorthand(const ComputedStyle & style)2166 CSSValueList* ComputedStyleUtils::ValueForBorderRadiusShorthand(
2167 const ComputedStyle& style) {
2168 CSSValueList* list = CSSValueList::CreateSlashSeparated();
2169
2170 bool show_horizontal_bottom_left = style.BorderTopRightRadius().Width() !=
2171 style.BorderBottomLeftRadius().Width();
2172 bool show_horizontal_bottom_right =
2173 show_horizontal_bottom_left || (style.BorderBottomRightRadius().Width() !=
2174 style.BorderTopLeftRadius().Width());
2175 bool show_horizontal_top_right =
2176 show_horizontal_bottom_right || (style.BorderTopRightRadius().Width() !=
2177 style.BorderTopLeftRadius().Width());
2178
2179 bool show_vertical_bottom_left = style.BorderTopRightRadius().Height() !=
2180 style.BorderBottomLeftRadius().Height();
2181 bool show_vertical_bottom_right =
2182 show_vertical_bottom_left || (style.BorderBottomRightRadius().Height() !=
2183 style.BorderTopLeftRadius().Height());
2184 bool show_vertical_top_right =
2185 show_vertical_bottom_right || (style.BorderTopRightRadius().Height() !=
2186 style.BorderTopLeftRadius().Height());
2187
2188 CSSValueList* top_left_radius =
2189 ValuesForBorderRadiusCorner(style.BorderTopLeftRadius(), style);
2190 CSSValueList* top_right_radius =
2191 ValuesForBorderRadiusCorner(style.BorderTopRightRadius(), style);
2192 CSSValueList* bottom_right_radius =
2193 ValuesForBorderRadiusCorner(style.BorderBottomRightRadius(), style);
2194 CSSValueList* bottom_left_radius =
2195 ValuesForBorderRadiusCorner(style.BorderBottomLeftRadius(), style);
2196
2197 CSSValueList* horizontal_radii = CSSValueList::CreateSpaceSeparated();
2198 horizontal_radii->Append(top_left_radius->Item(0));
2199 if (show_horizontal_top_right)
2200 horizontal_radii->Append(top_right_radius->Item(0));
2201 if (show_horizontal_bottom_right)
2202 horizontal_radii->Append(bottom_right_radius->Item(0));
2203 if (show_horizontal_bottom_left)
2204 horizontal_radii->Append(bottom_left_radius->Item(0));
2205
2206 list->Append(*horizontal_radii);
2207
2208 CSSValueList* vertical_radii = CSSValueList::CreateSpaceSeparated();
2209 vertical_radii->Append(top_left_radius->Item(1));
2210 if (show_vertical_top_right)
2211 vertical_radii->Append(top_right_radius->Item(1));
2212 if (show_vertical_bottom_right)
2213 vertical_radii->Append(bottom_right_radius->Item(1));
2214 if (show_vertical_bottom_left)
2215 vertical_radii->Append(bottom_left_radius->Item(1));
2216
2217 if (!vertical_radii->Equals(To<CSSValueList>(list->Item(0))))
2218 list->Append(*vertical_radii);
2219
2220 return list;
2221 }
2222
StrokeDashArrayToCSSValueList(const SVGDashArray & dashes,const ComputedStyle & style)2223 CSSValue* ComputedStyleUtils::StrokeDashArrayToCSSValueList(
2224 const SVGDashArray& dashes,
2225 const ComputedStyle& style) {
2226 if (dashes.data.IsEmpty())
2227 return CSSIdentifierValue::Create(CSSValueID::kNone);
2228
2229 CSSValueList* list = CSSValueList::CreateCommaSeparated();
2230 for (const Length& dash_length : dashes.data) {
2231 list->Append(*ZoomAdjustedPixelValueForLength(dash_length, style));
2232 }
2233
2234 return list;
2235 }
2236
ValueForSVGPaint(const SVGPaint & paint,const ComputedStyle & style)2237 CSSValue* ComputedStyleUtils::ValueForSVGPaint(const SVGPaint& paint,
2238 const ComputedStyle& style) {
2239 if (paint.type >= SVG_PAINTTYPE_URI_NONE) {
2240 CSSValueList* values = CSSValueList::CreateSpaceSeparated();
2241 values->Append(
2242 *MakeGarbageCollected<cssvalue::CSSURIValue>(paint.GetUrl()));
2243 if (paint.type == SVG_PAINTTYPE_URI_NONE) {
2244 values->Append(*CSSIdentifierValue::Create(CSSValueID::kNone));
2245 } else if (paint.type == SVG_PAINTTYPE_URI_COLOR) {
2246 values->Append(*CurrentColorOrValidColor(style, paint.GetColor(),
2247 CSSValuePhase::kComputedValue));
2248 }
2249 return values;
2250 }
2251 if (paint.type == SVG_PAINTTYPE_NONE)
2252 return CSSIdentifierValue::Create(CSSValueID::kNone);
2253
2254 return CurrentColorOrValidColor(style, paint.GetColor(),
2255 CSSValuePhase::kComputedValue);
2256 }
2257
ValueForSVGResource(const StyleSVGResource * resource)2258 CSSValue* ComputedStyleUtils::ValueForSVGResource(
2259 const StyleSVGResource* resource) {
2260 if (resource)
2261 return MakeGarbageCollected<cssvalue::CSSURIValue>(resource->Url());
2262 return CSSIdentifierValue::Create(CSSValueID::kNone);
2263 }
2264
ValueForShadowData(const ShadowData & shadow,const ComputedStyle & style,bool use_spread,CSSValuePhase value_phase)2265 CSSValue* ComputedStyleUtils::ValueForShadowData(const ShadowData& shadow,
2266 const ComputedStyle& style,
2267 bool use_spread,
2268 CSSValuePhase value_phase) {
2269 CSSPrimitiveValue* x = ZoomAdjustedPixelValue(shadow.X(), style);
2270 CSSPrimitiveValue* y = ZoomAdjustedPixelValue(shadow.Y(), style);
2271 CSSPrimitiveValue* blur = ZoomAdjustedPixelValue(shadow.Blur(), style);
2272 CSSPrimitiveValue* spread =
2273 use_spread ? ZoomAdjustedPixelValue(shadow.Spread(), style) : nullptr;
2274 CSSIdentifierValue* shadow_style =
2275 shadow.Style() == ShadowStyle::kNormal
2276 ? nullptr
2277 : CSSIdentifierValue::Create(CSSValueID::kInset);
2278 CSSValue* color =
2279 CurrentColorOrValidColor(style, shadow.GetColor(), value_phase);
2280 return MakeGarbageCollected<CSSShadowValue>(x, y, blur, spread, shadow_style,
2281 color);
2282 }
2283
ValueForShadowList(const ShadowList * shadow_list,const ComputedStyle & style,bool use_spread,CSSValuePhase value_phase)2284 CSSValue* ComputedStyleUtils::ValueForShadowList(const ShadowList* shadow_list,
2285 const ComputedStyle& style,
2286 bool use_spread,
2287 CSSValuePhase value_phase) {
2288 if (!shadow_list)
2289 return CSSIdentifierValue::Create(CSSValueID::kNone);
2290
2291 CSSValueList* list = CSSValueList::CreateCommaSeparated();
2292 wtf_size_t shadow_count = shadow_list->Shadows().size();
2293 for (wtf_size_t i = 0; i < shadow_count; ++i) {
2294 list->Append(*ValueForShadowData(shadow_list->Shadows()[i], style,
2295 use_spread, value_phase));
2296 }
2297 return list;
2298 }
2299
ValueForFilter(const ComputedStyle & style,const FilterOperations & filter_operations)2300 CSSValue* ComputedStyleUtils::ValueForFilter(
2301 const ComputedStyle& style,
2302 const FilterOperations& filter_operations) {
2303 if (filter_operations.Operations().IsEmpty())
2304 return CSSIdentifierValue::Create(CSSValueID::kNone);
2305
2306 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2307
2308 CSSFunctionValue* filter_value = nullptr;
2309
2310 for (const auto& operation : filter_operations.Operations()) {
2311 FilterOperation* filter_operation = operation.Get();
2312 switch (filter_operation->GetType()) {
2313 case FilterOperation::REFERENCE:
2314 filter_value = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kUrl);
2315 filter_value->Append(*MakeGarbageCollected<CSSStringValue>(
2316 To<ReferenceFilterOperation>(filter_operation)->Url()));
2317 break;
2318 case FilterOperation::GRAYSCALE:
2319 filter_value =
2320 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kGrayscale);
2321 filter_value->Append(*CSSNumericLiteralValue::Create(
2322 To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
2323 CSSPrimitiveValue::UnitType::kNumber));
2324 break;
2325 case FilterOperation::SEPIA:
2326 filter_value =
2327 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSepia);
2328 filter_value->Append(*CSSNumericLiteralValue::Create(
2329 To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
2330 CSSPrimitiveValue::UnitType::kNumber));
2331 break;
2332 case FilterOperation::SATURATE:
2333 filter_value =
2334 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSaturate);
2335 filter_value->Append(*CSSNumericLiteralValue::Create(
2336 To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
2337 CSSPrimitiveValue::UnitType::kNumber));
2338 break;
2339 case FilterOperation::HUE_ROTATE:
2340 filter_value =
2341 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kHueRotate);
2342 filter_value->Append(*CSSNumericLiteralValue::Create(
2343 To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
2344 CSSPrimitiveValue::UnitType::kDegrees));
2345 break;
2346 case FilterOperation::INVERT:
2347 filter_value =
2348 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kInvert);
2349 filter_value->Append(*CSSNumericLiteralValue::Create(
2350 To<BasicComponentTransferFilterOperation>(filter_operation)
2351 ->Amount(),
2352 CSSPrimitiveValue::UnitType::kNumber));
2353 break;
2354 case FilterOperation::OPACITY:
2355 filter_value =
2356 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kOpacity);
2357 filter_value->Append(*CSSNumericLiteralValue::Create(
2358 To<BasicComponentTransferFilterOperation>(filter_operation)
2359 ->Amount(),
2360 CSSPrimitiveValue::UnitType::kNumber));
2361 break;
2362 case FilterOperation::BRIGHTNESS:
2363 filter_value =
2364 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kBrightness);
2365 filter_value->Append(*CSSNumericLiteralValue::Create(
2366 To<BasicComponentTransferFilterOperation>(filter_operation)
2367 ->Amount(),
2368 CSSPrimitiveValue::UnitType::kNumber));
2369 break;
2370 case FilterOperation::CONTRAST:
2371 filter_value =
2372 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kContrast);
2373 filter_value->Append(*CSSNumericLiteralValue::Create(
2374 To<BasicComponentTransferFilterOperation>(filter_operation)
2375 ->Amount(),
2376 CSSPrimitiveValue::UnitType::kNumber));
2377 break;
2378 case FilterOperation::BLUR:
2379 filter_value =
2380 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kBlur);
2381 filter_value->Append(*ZoomAdjustedPixelValue(
2382 To<BlurFilterOperation>(filter_operation)->StdDeviation().Value(),
2383 style));
2384 break;
2385 case FilterOperation::DROP_SHADOW: {
2386 const auto& drop_shadow_operation =
2387 To<DropShadowFilterOperation>(*filter_operation);
2388 filter_value =
2389 MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kDropShadow);
2390 // We want our computed style to look like that of a text shadow (has
2391 // neither spread nor inset style).
2392 filter_value->Append(
2393 *ValueForShadowData(drop_shadow_operation.Shadow(), style, false,
2394 CSSValuePhase::kComputedValue));
2395 break;
2396 }
2397 default:
2398 NOTREACHED();
2399 break;
2400 }
2401 list->Append(*filter_value);
2402 }
2403
2404 return list;
2405 }
2406
ValueForScrollSnapType(const cc::ScrollSnapType & type,const ComputedStyle & style)2407 CSSValue* ComputedStyleUtils::ValueForScrollSnapType(
2408 const cc::ScrollSnapType& type,
2409 const ComputedStyle& style) {
2410 if (!type.is_none) {
2411 if (type.strictness == cc::SnapStrictness::kProximity)
2412 return CSSIdentifierValue::Create(type.axis);
2413 return MakeGarbageCollected<CSSValuePair>(
2414 CSSIdentifierValue::Create(type.axis),
2415 CSSIdentifierValue::Create(type.strictness),
2416 CSSValuePair::kDropIdenticalValues);
2417 }
2418 return CSSIdentifierValue::Create(CSSValueID::kNone);
2419 }
2420
ValueForScrollSnapAlign(const cc::ScrollSnapAlign & align,const ComputedStyle & style)2421 CSSValue* ComputedStyleUtils::ValueForScrollSnapAlign(
2422 const cc::ScrollSnapAlign& align,
2423 const ComputedStyle& style) {
2424 return MakeGarbageCollected<CSSValuePair>(
2425 CSSIdentifierValue::Create(align.alignment_block),
2426 CSSIdentifierValue::Create(align.alignment_inline),
2427 CSSValuePair::kDropIdenticalValues);
2428 }
2429
2430 // Returns a suitable value for the page-break-(before|after) property, given
2431 // the computed value of the more general break-(before|after) property.
ValueForPageBreakBetween(EBreakBetween break_value)2432 CSSValue* ComputedStyleUtils::ValueForPageBreakBetween(
2433 EBreakBetween break_value) {
2434 switch (break_value) {
2435 case EBreakBetween::kAvoidColumn:
2436 case EBreakBetween::kColumn:
2437 case EBreakBetween::kRecto:
2438 case EBreakBetween::kVerso:
2439 case EBreakBetween::kAvoidPage:
2440 return nullptr;
2441 case EBreakBetween::kPage:
2442 return CSSIdentifierValue::Create(CSSValueID::kAlways);
2443 default:
2444 return CSSIdentifierValue::Create(break_value);
2445 }
2446 }
2447
2448 // Returns a suitable value for the -webkit-column-break-(before|after)
2449 // property, given the computed value of the more general break-(before|after)
2450 // property.
ValueForWebkitColumnBreakBetween(EBreakBetween break_value)2451 CSSValue* ComputedStyleUtils::ValueForWebkitColumnBreakBetween(
2452 EBreakBetween break_value) {
2453 switch (break_value) {
2454 case EBreakBetween::kAvoidPage:
2455 case EBreakBetween::kLeft:
2456 case EBreakBetween::kPage:
2457 case EBreakBetween::kRecto:
2458 case EBreakBetween::kRight:
2459 case EBreakBetween::kVerso:
2460 return nullptr;
2461 case EBreakBetween::kColumn:
2462 return CSSIdentifierValue::Create(CSSValueID::kAlways);
2463 case EBreakBetween::kAvoidColumn:
2464 return CSSIdentifierValue::Create(CSSValueID::kAvoid);
2465 default:
2466 return CSSIdentifierValue::Create(break_value);
2467 }
2468 }
2469
2470 // Returns a suitable value for the page-break-inside property, given the
2471 // computed value of the more general break-inside property.
ValueForPageBreakInside(EBreakInside break_value)2472 CSSValue* ComputedStyleUtils::ValueForPageBreakInside(
2473 EBreakInside break_value) {
2474 switch (break_value) {
2475 case EBreakInside::kAvoidColumn:
2476 return nullptr;
2477 case EBreakInside::kAvoidPage:
2478 return CSSIdentifierValue::Create(CSSValueID::kAvoid);
2479 default:
2480 return CSSIdentifierValue::Create(break_value);
2481 }
2482 }
2483
2484 // Returns a suitable value for the -webkit-column-break-inside property, given
2485 // the computed value of the more general break-inside property.
ValueForWebkitColumnBreakInside(EBreakInside break_value)2486 CSSValue* ComputedStyleUtils::ValueForWebkitColumnBreakInside(
2487 EBreakInside break_value) {
2488 switch (break_value) {
2489 case EBreakInside::kAvoidPage:
2490 return nullptr;
2491 case EBreakInside::kAvoidColumn:
2492 return CSSIdentifierValue::Create(CSSValueID::kAvoid);
2493 default:
2494 return CSSIdentifierValue::Create(break_value);
2495 }
2496 }
2497
2498 // https://drafts.csswg.org/cssom/#resolved-value
2499 //
2500 // For 'width' and 'height':
2501 //
2502 // If the property applies to the element or pseudo-element and the resolved
2503 // value of the display property is not none or contents, then the resolved
2504 // value is the used value. Otherwise the resolved value is the computed value
2505 // (https://drafts.csswg.org/css-cascade-4/#computed-value).
2506 //
2507 // (Note that the computed value exists even when the property does not apply.)
WidthOrHeightShouldReturnUsedValue(const LayoutObject * object)2508 bool ComputedStyleUtils::WidthOrHeightShouldReturnUsedValue(
2509 const LayoutObject* object) {
2510 // The display property is 'none'.
2511 if (!object)
2512 return false;
2513 // Non-root SVG objects return the resolved value except <image>,
2514 // <rect> and <foreignObject> which return the used value.
2515 if (object->IsSVGChild())
2516 return IsSVGObjectWithWidthAndHeight(*object);
2517 // According to
2518 // http://www.w3.org/TR/CSS2/visudet.html#the-width-property and
2519 // http://www.w3.org/TR/CSS2/visudet.html#the-height-property, the "width" or
2520 // "height" property does not apply to non-atomic inline elements.
2521 return object->IsAtomicInlineLevel() || !object->IsInline();
2522 }
2523
ValuesForShorthandProperty(const StylePropertyShorthand & shorthand,const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)2524 CSSValueList* ComputedStyleUtils::ValuesForShorthandProperty(
2525 const StylePropertyShorthand& shorthand,
2526 const ComputedStyle& style,
2527 const LayoutObject* layout_object,
2528 bool allow_visited_style) {
2529 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2530 for (unsigned i = 0; i < shorthand.length(); ++i) {
2531 const CSSValue* value =
2532 shorthand.properties()[i]->CSSValueFromComputedStyle(
2533 style, layout_object, allow_visited_style);
2534 DCHECK(value);
2535 list->Append(*value);
2536 }
2537 return list;
2538 }
2539
ValuesForGapShorthand(const StylePropertyShorthand & shorthand,const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)2540 CSSValuePair* ComputedStyleUtils::ValuesForGapShorthand(
2541 const StylePropertyShorthand& shorthand,
2542 const ComputedStyle& style,
2543 const LayoutObject* layout_object,
2544 bool allow_visited_style) {
2545 const CSSValue* row_gap_value =
2546 shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object,
2547 allow_visited_style);
2548 const CSSValue* column_gap_value =
2549 shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object,
2550 allow_visited_style);
2551
2552 return MakeGarbageCollected<CSSValuePair>(row_gap_value, column_gap_value,
2553 CSSValuePair::kDropIdenticalValues);
2554 }
2555
ValuesForGridShorthand(const StylePropertyShorthand & shorthand,const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)2556 CSSValueList* ComputedStyleUtils::ValuesForGridShorthand(
2557 const StylePropertyShorthand& shorthand,
2558 const ComputedStyle& style,
2559 const LayoutObject* layout_object,
2560 bool allow_visited_style) {
2561 CSSValueList* list = CSSValueList::CreateSlashSeparated();
2562 for (unsigned i = 0; i < shorthand.length(); ++i) {
2563 const CSSValue* value =
2564 shorthand.properties()[i]->CSSValueFromComputedStyle(
2565 style, layout_object, allow_visited_style);
2566 DCHECK(value);
2567 list->Append(*value);
2568 }
2569 return list;
2570 }
2571
ValuesForSidesShorthand(const StylePropertyShorthand & shorthand,const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)2572 CSSValueList* ComputedStyleUtils::ValuesForSidesShorthand(
2573 const StylePropertyShorthand& shorthand,
2574 const ComputedStyle& style,
2575 const LayoutObject* layout_object,
2576 bool allow_visited_style) {
2577 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2578 // Assume the properties are in the usual order top, right, bottom, left.
2579 const CSSValue* top_value =
2580 shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object,
2581 allow_visited_style);
2582 const CSSValue* right_value =
2583 shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object,
2584 allow_visited_style);
2585 const CSSValue* bottom_value =
2586 shorthand.properties()[2]->CSSValueFromComputedStyle(style, layout_object,
2587 allow_visited_style);
2588 const CSSValue* left_value =
2589 shorthand.properties()[3]->CSSValueFromComputedStyle(style, layout_object,
2590 allow_visited_style);
2591
2592 // All 4 properties must be specified.
2593 if (!top_value || !right_value || !bottom_value || !left_value)
2594 return nullptr;
2595
2596 bool show_left = !DataEquivalent(right_value, left_value);
2597 bool show_bottom = !DataEquivalent(top_value, bottom_value) || show_left;
2598 bool show_right = !DataEquivalent(top_value, right_value) || show_bottom;
2599
2600 list->Append(*top_value);
2601 if (show_right)
2602 list->Append(*right_value);
2603 if (show_bottom)
2604 list->Append(*bottom_value);
2605 if (show_left)
2606 list->Append(*left_value);
2607
2608 return list;
2609 }
2610
ValuesForInlineBlockShorthand(const StylePropertyShorthand & shorthand,const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)2611 CSSValuePair* ComputedStyleUtils::ValuesForInlineBlockShorthand(
2612 const StylePropertyShorthand& shorthand,
2613 const ComputedStyle& style,
2614 const LayoutObject* layout_object,
2615 bool allow_visited_style) {
2616 const CSSValue* start_value =
2617 shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object,
2618 allow_visited_style);
2619 const CSSValue* end_value =
2620 shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object,
2621 allow_visited_style);
2622 // Both properties must be specified.
2623 if (!start_value || !end_value)
2624 return nullptr;
2625
2626 auto* pair = MakeGarbageCollected<CSSValuePair>(
2627 start_value, end_value, CSSValuePair::kDropIdenticalValues);
2628 return pair;
2629 }
2630
ValuesForPlaceShorthand(const StylePropertyShorthand & shorthand,const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)2631 CSSValuePair* ComputedStyleUtils::ValuesForPlaceShorthand(
2632 const StylePropertyShorthand& shorthand,
2633 const ComputedStyle& style,
2634 const LayoutObject* layout_object,
2635 bool allow_visited_style) {
2636 const CSSValue* align_value =
2637 shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object,
2638 allow_visited_style);
2639 const CSSValue* justify_value =
2640 shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object,
2641 allow_visited_style);
2642
2643 return MakeGarbageCollected<CSSValuePair>(align_value, justify_value,
2644 CSSValuePair::kDropIdenticalValues);
2645 }
2646
ExpandNoneLigaturesValue()2647 static CSSValue* ExpandNoneLigaturesValue() {
2648 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2649 list->Append(*CSSIdentifierValue::Create(CSSValueID::kNoCommonLigatures));
2650 list->Append(
2651 *CSSIdentifierValue::Create(CSSValueID::kNoDiscretionaryLigatures));
2652 list->Append(*CSSIdentifierValue::Create(CSSValueID::kNoHistoricalLigatures));
2653 list->Append(*CSSIdentifierValue::Create(CSSValueID::kNoContextual));
2654 return list;
2655 }
2656
ValuesForFontVariantProperty(const ComputedStyle & style,const LayoutObject * layout_object,bool allow_visited_style)2657 CSSValue* ComputedStyleUtils::ValuesForFontVariantProperty(
2658 const ComputedStyle& style,
2659 const LayoutObject* layout_object,
2660 bool allow_visited_style) {
2661 enum VariantShorthandCases {
2662 kAllNormal,
2663 kNoneLigatures,
2664 kConcatenateNonNormal
2665 };
2666 StylePropertyShorthand shorthand = fontVariantShorthand();
2667 VariantShorthandCases shorthand_case = kAllNormal;
2668 for (unsigned i = 0; i < shorthand.length(); ++i) {
2669 const CSSValue* value =
2670 shorthand.properties()[i]->CSSValueFromComputedStyle(
2671 style, layout_object, allow_visited_style);
2672
2673 auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
2674 if (shorthand_case == kAllNormal && identifier_value &&
2675 identifier_value->GetValueID() == CSSValueID::kNone &&
2676 shorthand.properties()[i]->IDEquals(
2677 CSSPropertyID::kFontVariantLigatures)) {
2678 shorthand_case = kNoneLigatures;
2679 } else if (!(identifier_value &&
2680 identifier_value->GetValueID() == CSSValueID::kNormal)) {
2681 shorthand_case = kConcatenateNonNormal;
2682 break;
2683 }
2684 }
2685
2686 switch (shorthand_case) {
2687 case kAllNormal:
2688 return CSSIdentifierValue::Create(CSSValueID::kNormal);
2689 case kNoneLigatures:
2690 return CSSIdentifierValue::Create(CSSValueID::kNone);
2691 case kConcatenateNonNormal: {
2692 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2693 for (unsigned i = 0; i < shorthand.length(); ++i) {
2694 const CSSValue* value =
2695 shorthand.properties()[i]->CSSValueFromComputedStyle(
2696 style, layout_object, allow_visited_style);
2697 DCHECK(value);
2698 auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
2699 if (identifier_value &&
2700 identifier_value->GetValueID() == CSSValueID::kNone) {
2701 list->Append(*ExpandNoneLigaturesValue());
2702 } else if (!(identifier_value &&
2703 identifier_value->GetValueID() == CSSValueID::kNormal)) {
2704 list->Append(*value);
2705 }
2706 }
2707 return list;
2708 }
2709 default:
2710 NOTREACHED();
2711 return nullptr;
2712 }
2713 }
2714
2715 // Returns up to two values for 'scroll-customization' property. The values
2716 // correspond to the customization values for 'x' and 'y' axes.
ScrollCustomizationFlagsToCSSValue(scroll_customization::ScrollDirection scroll_customization)2717 CSSValue* ComputedStyleUtils::ScrollCustomizationFlagsToCSSValue(
2718 scroll_customization::ScrollDirection scroll_customization) {
2719 CSSValueList* list = CSSValueList::CreateSpaceSeparated();
2720 if (scroll_customization == scroll_customization::kScrollDirectionAuto) {
2721 list->Append(*CSSIdentifierValue::Create(CSSValueID::kAuto));
2722 } else if (scroll_customization ==
2723 scroll_customization::kScrollDirectionNone) {
2724 list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone));
2725 } else {
2726 if ((scroll_customization & scroll_customization::kScrollDirectionPanX) ==
2727 scroll_customization::kScrollDirectionPanX)
2728 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanX));
2729 else if (scroll_customization &
2730 scroll_customization::kScrollDirectionPanLeft)
2731 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanLeft));
2732 else if (scroll_customization &
2733 scroll_customization::kScrollDirectionPanRight)
2734 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanRight));
2735 if ((scroll_customization & scroll_customization::kScrollDirectionPanY) ==
2736 scroll_customization::kScrollDirectionPanY)
2737 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanY));
2738 else if (scroll_customization & scroll_customization::kScrollDirectionPanUp)
2739 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanUp));
2740 else if (scroll_customization &
2741 scroll_customization::kScrollDirectionPanDown)
2742 list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanDown));
2743 }
2744
2745 DCHECK(list->length());
2746 return list;
2747 }
2748
ValueForGapLength(const base::Optional<Length> & gap_length,const ComputedStyle & style)2749 CSSValue* ComputedStyleUtils::ValueForGapLength(
2750 const base::Optional<Length>& gap_length,
2751 const ComputedStyle& style) {
2752 if (!gap_length)
2753 return CSSIdentifierValue::Create(CSSValueID::kNormal);
2754 return ZoomAdjustedPixelValueForLength(*gap_length, style);
2755 }
2756
ValueForStyleName(const StyleName & name)2757 CSSValue* ComputedStyleUtils::ValueForStyleName(const StyleName& name) {
2758 if (name.IsCustomIdent())
2759 return MakeGarbageCollected<CSSCustomIdentValue>(name.GetValue());
2760 return MakeGarbageCollected<CSSStringValue>(name.GetValue());
2761 }
2762
ValueForStyleNameOrKeyword(const StyleNameOrKeyword & value)2763 CSSValue* ComputedStyleUtils::ValueForStyleNameOrKeyword(
2764 const StyleNameOrKeyword& value) {
2765 if (value.IsKeyword())
2766 return CSSIdentifierValue::Create(value.GetKeyword());
2767 return ValueForStyleName(value.GetName());
2768 }
2769
ValueForStyleAutoColor(const ComputedStyle & style,const StyleAutoColor & color,CSSValuePhase value_phase)2770 const CSSValue* ComputedStyleUtils::ValueForStyleAutoColor(
2771 const ComputedStyle& style,
2772 const StyleAutoColor& color,
2773 CSSValuePhase value_phase) {
2774 if (color.IsAutoColor()) {
2775 return cssvalue::CSSColorValue::Create(
2776 StyleColor::CurrentColor()
2777 .Resolve(style.GetCurrentColor(), style.UsedColorScheme())
2778 .Rgb());
2779 }
2780 return ComputedStyleUtils::CurrentColorOrValidColor(
2781 style, color.ToStyleColor(), value_phase);
2782 }
2783
2784 std::unique_ptr<CrossThreadStyleValue>
CrossThreadStyleValueFromCSSStyleValue(CSSStyleValue * style_value)2785 ComputedStyleUtils::CrossThreadStyleValueFromCSSStyleValue(
2786 CSSStyleValue* style_value) {
2787 switch (style_value->GetType()) {
2788 case CSSStyleValue::StyleValueType::kKeywordType:
2789 return std::make_unique<CrossThreadKeywordValue>(
2790 To<CSSKeywordValue>(style_value)->value().IsolatedCopy());
2791 case CSSStyleValue::StyleValueType::kUnitType:
2792 return std::make_unique<CrossThreadUnitValue>(
2793 To<CSSUnitValue>(style_value)->value(),
2794 To<CSSUnitValue>(style_value)->GetInternalUnit());
2795 case CSSStyleValue::StyleValueType::kUnsupportedColorType:
2796 return std::make_unique<CrossThreadColorValue>(
2797 To<CSSUnsupportedColorValue>(style_value)->Value());
2798 case CSSStyleValue::StyleValueType::kUnparsedType:
2799 return std::make_unique<CrossThreadUnparsedValue>(
2800 To<CSSUnparsedValue>(style_value)->ToString().IsolatedCopy());
2801 default:
2802 // Make an isolated copy to ensure that it is safe to pass cross thread.
2803 return std::make_unique<CrossThreadUnsupportedValue>(
2804 style_value->toString().IsolatedCopy());
2805 }
2806 }
2807
ComputedPropertyValue(const CSSProperty & property,const ComputedStyle & style,const LayoutObject * layout_object)2808 const CSSValue* ComputedStyleUtils::ComputedPropertyValue(
2809 const CSSProperty& property,
2810 const ComputedStyle& style,
2811 const LayoutObject* layout_object) {
2812 switch (property.PropertyID()) {
2813 // Computed value is usually relative so that multiple fonts in child
2814 // elements work properly, but resolved value is always a pixel length.
2815 case CSSPropertyID::kLineHeight:
2816 return ComputedStyleUtils::ComputedValueForLineHeight(style);
2817
2818 // Returns a transform list instead of converting to a (resolved) matrix.
2819 case CSSPropertyID::kTransform:
2820 return ComputedStyleUtils::ComputedTransformList(style, layout_object);
2821
2822 // For the following properties, the resolved value is the used value, which
2823 // is not what we want. Obtain the computed value instead.
2824 case CSSPropertyID::kBackgroundColor:
2825 return ComputedStyleUtils::CurrentColorOrValidColor(
2826 style, style.BackgroundColor(), CSSValuePhase::kComputedValue);
2827 case CSSPropertyID::kBorderBlockEndColor:
2828 case CSSPropertyID::kBorderBlockStartColor:
2829 case CSSPropertyID::kBorderInlineEndColor:
2830 case CSSPropertyID::kBorderInlineStartColor:
2831 return ComputedStyleUtils::ComputedPropertyValue(
2832 property.ResolveDirectionAwareProperty(style.Direction(),
2833 style.GetWritingMode()),
2834 style, layout_object);
2835 case CSSPropertyID::kBorderBottomColor:
2836 return ComputedStyleUtils::CurrentColorOrValidColor(
2837 style, style.BorderBottomColor(), CSSValuePhase::kComputedValue);
2838 case CSSPropertyID::kBorderLeftColor:
2839 return ComputedStyleUtils::CurrentColorOrValidColor(
2840 style, style.BorderLeftColor(), CSSValuePhase::kComputedValue);
2841 case CSSPropertyID::kBorderRightColor:
2842 return ComputedStyleUtils::CurrentColorOrValidColor(
2843 style, style.BorderRightColor(), CSSValuePhase::kComputedValue);
2844 case CSSPropertyID::kBorderTopColor:
2845 return ComputedStyleUtils::CurrentColorOrValidColor(
2846 style, style.BorderTopColor(), CSSValuePhase::kComputedValue);
2847 case CSSPropertyID::kBoxShadow:
2848 return ComputedStyleUtils::ValueForShadowList(
2849 style.BoxShadow(), style, true, CSSValuePhase::kComputedValue);
2850 case CSSPropertyID::kCaretColor:
2851 return ComputedStyleUtils::ValueForStyleAutoColor(
2852 style, style.CaretColor(), CSSValuePhase::kComputedValue);
2853 case CSSPropertyID::kColor:
2854 return ComputedStyleUtils::CurrentColorOrValidColor(
2855 style, style.GetColor(), CSSValuePhase::kComputedValue);
2856 case CSSPropertyID::kOutlineColor:
2857 return ComputedStyleUtils::CurrentColorOrValidColor(
2858 style, style.OutlineColor(), CSSValuePhase::kComputedValue);
2859
2860 // For all other properties, the resolved value is either always the same
2861 // as the computed value (most properties), or the same as the computed
2862 // value when there is no layout box ('width' and friends).
2863 default:
2864 return property.CSSValueFromComputedStyle(
2865 style, /*layout_object=*/nullptr, false);
2866 }
2867 }
2868
2869 } // namespace blink
2870