1 /*
2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
4 * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
5 * Copyright (C) 2013 Intel Corporation. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24 #include "core/css/StylePropertySerializer.h"
25
26 #include "CSSValueKeywords.h"
27 #include "StylePropertyShorthand.h"
28 #include "core/css/RuntimeCSSEnabled.h"
29 #include "wtf/BitArray.h"
30 #include "wtf/text/StringBuilder.h"
31
32 using namespace std;
33
34 namespace WebCore {
35
isInitialOrInherit(const String & value)36 static bool isInitialOrInherit(const String& value)
37 {
38 DEFINE_STATIC_LOCAL(String, initial, ("initial"));
39 DEFINE_STATIC_LOCAL(String, inherit, ("inherit"));
40 return value.length() == 7 && (value == initial || value == inherit);
41 }
42
StylePropertySerializer(const StylePropertySet & properties)43 StylePropertySerializer::StylePropertySerializer(const StylePropertySet& properties)
44 : m_propertySet(properties)
45 {
46 }
47
asText() const48 String StylePropertySerializer::asText() const
49 {
50 StringBuilder result;
51
52 int positionXPropertyIndex = -1;
53 int positionYPropertyIndex = -1;
54 int repeatXPropertyIndex = -1;
55 int repeatYPropertyIndex = -1;
56
57 BitArray<numCSSProperties> shorthandPropertyUsed;
58 BitArray<numCSSProperties> shorthandPropertyAppeared;
59
60 unsigned size = m_propertySet.propertyCount();
61 unsigned numDecls = 0;
62 for (unsigned n = 0; n < size; ++n) {
63 StylePropertySet::PropertyReference property = m_propertySet.propertyAt(n);
64 CSSPropertyID propertyID = property.id();
65 // Only enabled or internal properties should be part of the style.
66 ASSERT(RuntimeCSSEnabled::isCSSPropertyEnabled(propertyID) || isInternalProperty(propertyID));
67 CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
68 CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
69 String value;
70
71 switch (propertyID) {
72 case CSSPropertyVariable:
73 if (numDecls++)
74 result.append(' ');
75 result.append(property.cssText());
76 continue;
77 case CSSPropertyBackgroundPositionX:
78 positionXPropertyIndex = n;
79 continue;
80 case CSSPropertyBackgroundPositionY:
81 positionYPropertyIndex = n;
82 continue;
83 case CSSPropertyBackgroundRepeatX:
84 repeatXPropertyIndex = n;
85 continue;
86 case CSSPropertyBackgroundRepeatY:
87 repeatYPropertyIndex = n;
88 continue;
89 case CSSPropertyContent:
90 if (property.value()->isValueList())
91 value = toCSSValueList(property.value())->customCSSText(AlwaysQuoteCSSString);
92 break;
93 case CSSPropertyBorderTopWidth:
94 case CSSPropertyBorderRightWidth:
95 case CSSPropertyBorderBottomWidth:
96 case CSSPropertyBorderLeftWidth:
97 if (!borderFallbackShorthandProperty)
98 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
99 case CSSPropertyBorderTopStyle:
100 case CSSPropertyBorderRightStyle:
101 case CSSPropertyBorderBottomStyle:
102 case CSSPropertyBorderLeftStyle:
103 if (!borderFallbackShorthandProperty)
104 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
105 case CSSPropertyBorderTopColor:
106 case CSSPropertyBorderRightColor:
107 case CSSPropertyBorderBottomColor:
108 case CSSPropertyBorderLeftColor:
109 if (!borderFallbackShorthandProperty)
110 borderFallbackShorthandProperty = CSSPropertyBorderColor;
111
112 // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
113 if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
114 value = borderPropertyValue(ReturnNullOnUncommonValues);
115 if (value.isNull())
116 shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
117 else
118 shorthandPropertyID = CSSPropertyBorder;
119 } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
120 shorthandPropertyID = CSSPropertyBorder;
121 if (!shorthandPropertyID)
122 shorthandPropertyID = borderFallbackShorthandProperty;
123 break;
124 case CSSPropertyWebkitBorderHorizontalSpacing:
125 case CSSPropertyWebkitBorderVerticalSpacing:
126 shorthandPropertyID = CSSPropertyBorderSpacing;
127 break;
128 case CSSPropertyFontFamily:
129 case CSSPropertyLineHeight:
130 case CSSPropertyFontSize:
131 case CSSPropertyFontStyle:
132 case CSSPropertyFontVariant:
133 case CSSPropertyFontWeight:
134 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
135 break;
136 case CSSPropertyListStyleType:
137 case CSSPropertyListStylePosition:
138 case CSSPropertyListStyleImage:
139 shorthandPropertyID = CSSPropertyListStyle;
140 break;
141 case CSSPropertyMarginTop:
142 case CSSPropertyMarginRight:
143 case CSSPropertyMarginBottom:
144 case CSSPropertyMarginLeft:
145 shorthandPropertyID = CSSPropertyMargin;
146 break;
147 case CSSPropertyOutlineWidth:
148 case CSSPropertyOutlineStyle:
149 case CSSPropertyOutlineColor:
150 shorthandPropertyID = CSSPropertyOutline;
151 break;
152 case CSSPropertyOverflowX:
153 case CSSPropertyOverflowY:
154 shorthandPropertyID = CSSPropertyOverflow;
155 break;
156 case CSSPropertyPaddingTop:
157 case CSSPropertyPaddingRight:
158 case CSSPropertyPaddingBottom:
159 case CSSPropertyPaddingLeft:
160 shorthandPropertyID = CSSPropertyPadding;
161 break;
162 case CSSPropertyTransitionProperty:
163 case CSSPropertyTransitionDuration:
164 case CSSPropertyTransitionTimingFunction:
165 case CSSPropertyTransitionDelay:
166 shorthandPropertyID = CSSPropertyTransition;
167 break;
168 case CSSPropertyWebkitAnimationName:
169 case CSSPropertyWebkitAnimationDuration:
170 case CSSPropertyWebkitAnimationTimingFunction:
171 case CSSPropertyWebkitAnimationDelay:
172 case CSSPropertyWebkitAnimationIterationCount:
173 case CSSPropertyWebkitAnimationDirection:
174 case CSSPropertyWebkitAnimationFillMode:
175 shorthandPropertyID = CSSPropertyWebkitAnimation;
176 break;
177 case CSSPropertyFlexDirection:
178 case CSSPropertyFlexWrap:
179 shorthandPropertyID = CSSPropertyFlexFlow;
180 break;
181 case CSSPropertyFlexBasis:
182 case CSSPropertyFlexGrow:
183 case CSSPropertyFlexShrink:
184 shorthandPropertyID = CSSPropertyFlex;
185 break;
186 case CSSPropertyWebkitMaskPositionX:
187 case CSSPropertyWebkitMaskPositionY:
188 case CSSPropertyWebkitMaskRepeatX:
189 case CSSPropertyWebkitMaskRepeatY:
190 case CSSPropertyWebkitMaskImage:
191 case CSSPropertyWebkitMaskRepeat:
192 case CSSPropertyWebkitMaskPosition:
193 case CSSPropertyWebkitMaskClip:
194 case CSSPropertyWebkitMaskOrigin:
195 shorthandPropertyID = CSSPropertyWebkitMask;
196 break;
197 case CSSPropertyWebkitTransformOriginX:
198 case CSSPropertyWebkitTransformOriginY:
199 case CSSPropertyWebkitTransformOriginZ:
200 shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
201 break;
202 case CSSPropertyWebkitTransitionProperty:
203 case CSSPropertyWebkitTransitionDuration:
204 case CSSPropertyWebkitTransitionTimingFunction:
205 case CSSPropertyWebkitTransitionDelay:
206 shorthandPropertyID = CSSPropertyWebkitTransition;
207 break;
208 default:
209 break;
210 }
211
212 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
213 if (shorthandPropertyID) {
214 if (shorthandPropertyUsed.get(shortPropertyIndex))
215 continue;
216 if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
217 value = m_propertySet.getPropertyValue(shorthandPropertyID);
218 shorthandPropertyAppeared.set(shortPropertyIndex);
219 }
220
221 if (!value.isNull()) {
222 if (shorthandPropertyID) {
223 propertyID = shorthandPropertyID;
224 shorthandPropertyUsed.set(shortPropertyIndex);
225 }
226 } else
227 value = property.value()->cssText();
228
229 if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
230 continue;
231
232 if (numDecls++)
233 result.append(' ');
234 result.append(getPropertyName(propertyID));
235 result.appendLiteral(": ");
236 result.append(value);
237 if (property.isImportant())
238 result.appendLiteral(" !important");
239 result.append(';');
240 }
241
242 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
243 // It is required because background-position-x/y are non-standard properties and WebKit generated output
244 // would not work in Firefox (<rdar://problem/5143183>)
245 // It would be a better solution if background-position was CSS_PAIR.
246 if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && m_propertySet.propertyAt(positionXPropertyIndex).isImportant() == m_propertySet.propertyAt(positionYPropertyIndex).isImportant()) {
247 StylePropertySet::PropertyReference positionXProperty = m_propertySet.propertyAt(positionXPropertyIndex);
248 StylePropertySet::PropertyReference positionYProperty = m_propertySet.propertyAt(positionYPropertyIndex);
249
250 if (numDecls++)
251 result.append(' ');
252 result.appendLiteral("background-position: ");
253 if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
254 result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
255 else {
256 result.append(positionXProperty.value()->cssText());
257 result.append(' ');
258 result.append(positionYProperty.value()->cssText());
259 }
260 if (positionXProperty.isImportant())
261 result.appendLiteral(" !important");
262 result.append(';');
263 } else {
264 if (positionXPropertyIndex != -1) {
265 if (numDecls++)
266 result.append(' ');
267 result.append(m_propertySet.propertyAt(positionXPropertyIndex).cssText());
268 }
269 if (positionYPropertyIndex != -1) {
270 if (numDecls++)
271 result.append(' ');
272 result.append(m_propertySet.propertyAt(positionYPropertyIndex).cssText());
273 }
274 }
275
276 // FIXME: We need to do the same for background-repeat.
277 if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && m_propertySet.propertyAt(repeatXPropertyIndex).isImportant() == m_propertySet.propertyAt(repeatYPropertyIndex).isImportant()) {
278 StylePropertySet::PropertyReference repeatXProperty = m_propertySet.propertyAt(repeatXPropertyIndex);
279 StylePropertySet::PropertyReference repeatYProperty = m_propertySet.propertyAt(repeatYPropertyIndex);
280
281 if (numDecls++)
282 result.append(' ');
283 result.appendLiteral("background-repeat: ");
284 if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
285 result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
286 else {
287 result.append(repeatXProperty.value()->cssText());
288 result.append(' ');
289 result.append(repeatYProperty.value()->cssText());
290 }
291 if (repeatXProperty.isImportant())
292 result.appendLiteral(" !important");
293 result.append(';');
294 } else {
295 if (repeatXPropertyIndex != -1) {
296 if (numDecls++)
297 result.append(' ');
298 result.append(m_propertySet.propertyAt(repeatXPropertyIndex).cssText());
299 }
300 if (repeatYPropertyIndex != -1) {
301 if (numDecls++)
302 result.append(' ');
303 result.append(m_propertySet.propertyAt(repeatYPropertyIndex).cssText());
304 }
305 }
306
307 ASSERT(!numDecls ^ !result.isEmpty());
308 return result.toString();
309 }
310
getPropertyValue(CSSPropertyID propertyID) const311 String StylePropertySerializer::getPropertyValue(CSSPropertyID propertyID) const
312 {
313 // Shorthand and 4-values properties
314 switch (propertyID) {
315 case CSSPropertyAnimation:
316 return getLayeredShorthandValue(animationShorthand());
317 case CSSPropertyBorderSpacing:
318 return borderSpacingValue(borderSpacingShorthand());
319 case CSSPropertyBackgroundPosition:
320 return getLayeredShorthandValue(backgroundPositionShorthand());
321 case CSSPropertyBackgroundRepeat:
322 return getLayeredShorthandValue(backgroundRepeatShorthand());
323 case CSSPropertyBackground:
324 return getLayeredShorthandValue(backgroundShorthand());
325 case CSSPropertyBorder:
326 return borderPropertyValue(OmitUncommonValues);
327 case CSSPropertyBorderTop:
328 return getShorthandValue(borderTopShorthand());
329 case CSSPropertyBorderRight:
330 return getShorthandValue(borderRightShorthand());
331 case CSSPropertyBorderBottom:
332 return getShorthandValue(borderBottomShorthand());
333 case CSSPropertyBorderLeft:
334 return getShorthandValue(borderLeftShorthand());
335 case CSSPropertyOutline:
336 return getShorthandValue(outlineShorthand());
337 case CSSPropertyBorderColor:
338 return get4Values(borderColorShorthand());
339 case CSSPropertyBorderWidth:
340 return get4Values(borderWidthShorthand());
341 case CSSPropertyBorderStyle:
342 return get4Values(borderStyleShorthand());
343 case CSSPropertyWebkitColumnRule:
344 return getShorthandValue(webkitColumnRuleShorthand());
345 case CSSPropertyWebkitColumns:
346 return getShorthandValue(webkitColumnsShorthand());
347 case CSSPropertyFlex:
348 return getShorthandValue(flexShorthand());
349 case CSSPropertyFlexFlow:
350 return getShorthandValue(flexFlowShorthand());
351 case CSSPropertyGridColumn:
352 return getShorthandValue(gridColumnShorthand());
353 case CSSPropertyGridRow:
354 return getShorthandValue(gridRowShorthand());
355 case CSSPropertyGridArea:
356 return getShorthandValue(gridAreaShorthand());
357 case CSSPropertyFont:
358 return fontValue();
359 case CSSPropertyMargin:
360 return get4Values(marginShorthand());
361 case CSSPropertyWebkitMarginCollapse:
362 return getShorthandValue(webkitMarginCollapseShorthand());
363 case CSSPropertyOverflow:
364 return getCommonValue(overflowShorthand());
365 case CSSPropertyPadding:
366 return get4Values(paddingShorthand());
367 case CSSPropertyTransition:
368 return getLayeredShorthandValue(transitionShorthand());
369 case CSSPropertyListStyle:
370 return getShorthandValue(listStyleShorthand());
371 case CSSPropertyWebkitMaskPosition:
372 return getLayeredShorthandValue(webkitMaskPositionShorthand());
373 case CSSPropertyWebkitMaskRepeat:
374 return getLayeredShorthandValue(webkitMaskRepeatShorthand());
375 case CSSPropertyWebkitMask:
376 return getLayeredShorthandValue(webkitMaskShorthand());
377 case CSSPropertyWebkitTextEmphasis:
378 return getShorthandValue(webkitTextEmphasisShorthand());
379 case CSSPropertyWebkitTextStroke:
380 return getShorthandValue(webkitTextStrokeShorthand());
381 case CSSPropertyWebkitTransformOrigin:
382 return getShorthandValue(webkitTransformOriginShorthand());
383 case CSSPropertyWebkitTransition:
384 return getLayeredShorthandValue(webkitTransitionShorthand());
385 case CSSPropertyWebkitAnimation:
386 return getLayeredShorthandValue(webkitAnimationShorthand());
387 case CSSPropertyMarker: {
388 RefPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyMarkerStart);
389 if (value)
390 return value->cssText();
391 return String();
392 }
393 case CSSPropertyBorderRadius:
394 return get4Values(borderRadiusShorthand());
395 default:
396 return String();
397 }
398 }
399
borderSpacingValue(const StylePropertyShorthand & shorthand) const400 String StylePropertySerializer::borderSpacingValue(const StylePropertyShorthand& shorthand) const
401 {
402 RefPtr<CSSValue> horizontalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[0]);
403 RefPtr<CSSValue> verticalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[1]);
404
405 // While standard border-spacing property does not allow specifying border-spacing-vertical without
406 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
407 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
408 if (!horizontalValue || !verticalValue)
409 return String();
410
411 String horizontalValueCSSText = horizontalValue->cssText();
412 String verticalValueCSSText = verticalValue->cssText();
413 if (horizontalValueCSSText == verticalValueCSSText)
414 return horizontalValueCSSText;
415 return horizontalValueCSSText + ' ' + verticalValueCSSText;
416 }
417
appendFontLonghandValueIfExplicit(CSSPropertyID propertyID,StringBuilder & result,String & commonValue) const418 void StylePropertySerializer::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
419 {
420 int foundPropertyIndex = m_propertySet.findPropertyIndex(propertyID);
421 if (foundPropertyIndex == -1)
422 return; // All longhands must have at least implicit values if "font" is specified.
423
424 if (m_propertySet.propertyAt(foundPropertyIndex).isImplicit()) {
425 commonValue = String();
426 return;
427 }
428
429 char prefix = '\0';
430 switch (propertyID) {
431 case CSSPropertyFontStyle:
432 break; // No prefix.
433 case CSSPropertyFontFamily:
434 case CSSPropertyFontVariant:
435 case CSSPropertyFontWeight:
436 prefix = ' ';
437 break;
438 case CSSPropertyLineHeight:
439 prefix = '/';
440 break;
441 default:
442 ASSERT_NOT_REACHED();
443 }
444
445 if (prefix && !result.isEmpty())
446 result.append(prefix);
447 String value = m_propertySet.propertyAt(foundPropertyIndex).value()->cssText();
448 result.append(value);
449 if (!commonValue.isNull() && commonValue != value)
450 commonValue = String();
451 }
452
fontValue() const453 String StylePropertySerializer::fontValue() const
454 {
455 int fontSizePropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontSize);
456 int fontFamilyPropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontFamily);
457 if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
458 return emptyString();
459
460 StylePropertySet::PropertyReference fontSizeProperty = m_propertySet.propertyAt(fontSizePropertyIndex);
461 StylePropertySet::PropertyReference fontFamilyProperty = m_propertySet.propertyAt(fontFamilyPropertyIndex);
462 if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
463 return emptyString();
464
465 String commonValue = fontSizeProperty.value()->cssText();
466 StringBuilder result;
467 appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
468 appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result, commonValue);
469 appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
470 if (!result.isEmpty())
471 result.append(' ');
472 result.append(fontSizeProperty.value()->cssText());
473 appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
474 if (!result.isEmpty())
475 result.append(' ');
476 result.append(fontFamilyProperty.value()->cssText());
477 if (isInitialOrInherit(commonValue))
478 return commonValue;
479 return result.toString();
480 }
481
get4Values(const StylePropertyShorthand & shorthand) const482 String StylePropertySerializer::get4Values(const StylePropertyShorthand& shorthand) const
483 {
484 // Assume the properties are in the usual order top, right, bottom, left.
485 int topValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[0]);
486 int rightValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[1]);
487 int bottomValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[2]);
488 int leftValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[3]);
489
490 if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
491 return String();
492
493 StylePropertySet::PropertyReference top = m_propertySet.propertyAt(topValueIndex);
494 StylePropertySet::PropertyReference right = m_propertySet.propertyAt(rightValueIndex);
495 StylePropertySet::PropertyReference bottom = m_propertySet.propertyAt(bottomValueIndex);
496 StylePropertySet::PropertyReference left = m_propertySet.propertyAt(leftValueIndex);
497
498 // All 4 properties must be specified.
499 if (!top.value() || !right.value() || !bottom.value() || !left.value())
500 return String();
501
502 if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
503 return getValueName(CSSValueInherit);
504
505 if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
506 if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
507 // All components are "initial" and "top" is not implicit.
508 return getValueName(CSSValueInitial);
509 }
510 return String();
511 }
512 if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
513 return String();
514
515 bool showLeft = !right.value()->equals(*left.value());
516 bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
517 bool showRight = !top.value()->equals(*right.value()) || showBottom;
518
519 StringBuilder result;
520 result.append(top.value()->cssText());
521 if (showRight) {
522 result.append(' ');
523 result.append(right.value()->cssText());
524 }
525 if (showBottom) {
526 result.append(' ');
527 result.append(bottom.value()->cssText());
528 }
529 if (showLeft) {
530 result.append(' ');
531 result.append(left.value()->cssText());
532 }
533 return result.toString();
534 }
535
getLayeredShorthandValue(const StylePropertyShorthand & shorthand) const536 String StylePropertySerializer::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
537 {
538 StringBuilder result;
539
540 const unsigned size = shorthand.length();
541 // Begin by collecting the properties into an array.
542 Vector< RefPtr<CSSValue> > values(size);
543 size_t numLayers = 0;
544
545 for (unsigned i = 0; i < size; ++i) {
546 values[i] = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
547 if (values[i]) {
548 if (values[i]->isBaseValueList()) {
549 CSSValueList* valueList = toCSSValueList(values[i].get());
550 numLayers = max(valueList->length(), numLayers);
551 } else
552 numLayers = max<size_t>(1U, numLayers);
553 }
554 }
555
556 String commonValue;
557 bool commonValueInitialized = false;
558
559 // Now stitch the properties together. Implicit initial values are flagged as such and
560 // can safely be omitted.
561 for (size_t i = 0; i < numLayers; i++) {
562 StringBuilder layerResult;
563 bool useRepeatXShorthand = false;
564 bool useRepeatYShorthand = false;
565 bool useSingleWordShorthand = false;
566 bool foundPositionYCSSProperty = false;
567 for (unsigned j = 0; j < size; j++) {
568 RefPtr<CSSValue> value;
569 if (values[j]) {
570 if (values[j]->isBaseValueList())
571 value = toCSSValueList(values[j].get())->item(i);
572 else {
573 value = values[j];
574
575 // Color only belongs in the last layer.
576 if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
577 if (i != numLayers - 1)
578 value = 0;
579 } else if (i) // Other singletons only belong in the first layer.
580 value = 0;
581 }
582 }
583
584 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
585 // then it was written with only one value. Here we figure out which value that was so we can
586 // report back correctly.
587 if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))
588 || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))) {
589
590 // BUG 49055: make sure the value was not reset in the layer check just above.
591 if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
592 || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
593 RefPtr<CSSValue> yValue;
594 RefPtr<CSSValue> nextValue = values[j + 1];
595 if (nextValue->isValueList())
596 yValue = toCSSValueList(nextValue.get())->itemWithoutBoundsCheck(i);
597 else
598 yValue = nextValue;
599
600 // background-repeat-x(y) or mask-repeat-x(y) may be like this : "initial, repeat". We can omit the implicit initial values
601 // before starting to compare their values.
602 if (value->isImplicitInitialValue() || yValue->isImplicitInitialValue())
603 continue;
604
605 // FIXME: At some point we need to fix this code to avoid returning an invalid shorthand,
606 // since some longhand combinations are not serializable into a single shorthand.
607 if (!value->isPrimitiveValue() || !yValue->isPrimitiveValue())
608 continue;
609
610 CSSValueID xId = toCSSPrimitiveValue(value.get())->getValueID();
611 CSSValueID yId = toCSSPrimitiveValue(yValue.get())->getValueID();
612 if (xId != yId) {
613 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
614 useRepeatXShorthand = true;
615 ++j;
616 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
617 useRepeatYShorthand = true;
618 continue;
619 }
620 } else {
621 useSingleWordShorthand = true;
622 ++j;
623 }
624 }
625 }
626
627 String valueText;
628 if (value && !value->isImplicitInitialValue()) {
629 if (!layerResult.isEmpty())
630 layerResult.append(' ');
631 if (foundPositionYCSSProperty
632 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
633 layerResult.appendLiteral("/ ");
634 if (!foundPositionYCSSProperty
635 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
636 continue;
637
638 if (useRepeatXShorthand) {
639 useRepeatXShorthand = false;
640 layerResult.append(getValueName(CSSValueRepeatX));
641 } else if (useRepeatYShorthand) {
642 useRepeatYShorthand = false;
643 layerResult.append(getValueName(CSSValueRepeatY));
644 } else {
645 if (useSingleWordShorthand)
646 useSingleWordShorthand = false;
647 valueText = value->cssText();
648 layerResult.append(valueText);
649 }
650
651 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
652 || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
653 foundPositionYCSSProperty = true;
654
655 // background-position is a special case: if only the first offset is specified,
656 // the second one defaults to "center", not the same value.
657 if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
658 commonValue = String();
659 }
660 }
661
662 if (!commonValueInitialized) {
663 commonValue = valueText;
664 commonValueInitialized = true;
665 } else if (!commonValue.isNull() && commonValue != valueText)
666 commonValue = String();
667 }
668
669 if (!layerResult.isEmpty()) {
670 if (!result.isEmpty())
671 result.appendLiteral(", ");
672 result.append(layerResult);
673 }
674 }
675
676 if (isInitialOrInherit(commonValue))
677 return commonValue;
678
679 if (result.isEmpty())
680 return String();
681 return result.toString();
682 }
683
getShorthandValue(const StylePropertyShorthand & shorthand) const684 String StylePropertySerializer::getShorthandValue(const StylePropertyShorthand& shorthand) const
685 {
686 String commonValue;
687 StringBuilder result;
688 for (unsigned i = 0; i < shorthand.length(); ++i) {
689 if (!m_propertySet.isPropertyImplicit(shorthand.properties()[i])) {
690 RefPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
691 if (!value)
692 return String();
693 String valueText = value->cssText();
694 if (!i)
695 commonValue = valueText;
696 else if (!commonValue.isNull() && commonValue != valueText)
697 commonValue = String();
698 if (value->isInitialValue())
699 continue;
700 if (!result.isEmpty())
701 result.append(' ');
702 result.append(valueText);
703 } else
704 commonValue = String();
705 }
706 if (isInitialOrInherit(commonValue))
707 return commonValue;
708 if (result.isEmpty())
709 return String();
710 return result.toString();
711 }
712
713 // only returns a non-null value if all properties have the same, non-null value
getCommonValue(const StylePropertyShorthand & shorthand) const714 String StylePropertySerializer::getCommonValue(const StylePropertyShorthand& shorthand) const
715 {
716 String res;
717 bool lastPropertyWasImportant = false;
718 for (unsigned i = 0; i < shorthand.length(); ++i) {
719 RefPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
720 // FIXME: CSSInitialValue::cssText should generate the right value.
721 if (!value)
722 return String();
723 String text = value->cssText();
724 if (text.isNull())
725 return String();
726 if (res.isNull())
727 res = text;
728 else if (res != text)
729 return String();
730
731 bool currentPropertyIsImportant = m_propertySet.propertyIsImportant(shorthand.properties()[i]);
732 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
733 return String();
734 lastPropertyWasImportant = currentPropertyIsImportant;
735 }
736 return res;
737 }
738
borderPropertyValue(CommonValueMode valueMode) const739 String StylePropertySerializer::borderPropertyValue(CommonValueMode valueMode) const
740 {
741 const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
742 String commonValue;
743 StringBuilder result;
744 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
745 String value = getCommonValue(properties[i]);
746 if (value.isNull()) {
747 if (valueMode == ReturnNullOnUncommonValues)
748 return String();
749 ASSERT(valueMode == OmitUncommonValues);
750 continue;
751 }
752 if (!i)
753 commonValue = value;
754 else if (!commonValue.isNull() && commonValue != value)
755 commonValue = String();
756 if (value == "initial")
757 continue;
758 if (!result.isEmpty())
759 result.append(' ');
760 result.append(value);
761 }
762 if (isInitialOrInherit(commonValue))
763 return commonValue;
764 return result.isEmpty() ? String() : result.toString();
765 }
766
767 }
768