1 /* 2 * (C) 1999-2003 Lars Knoll (knoll (at) 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 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 43 StylePropertySerializer::StylePropertySerializer(const StylePropertySet& properties) 44 : m_propertySet(properties) 45 { 46 } 47 48 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 311 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 400 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 418 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 453 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 482 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 536 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 684 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 714 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 739 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