| 1 | /* |
| 2 | * (C) 1999-2003 Lars Knoll (knoll@kde.org) |
| 3 | * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 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 "StyleProperties.h" |
| 25 | |
| 26 | #include "CSSComputedStyleDeclaration.h" |
| 27 | #include "CSSCustomPropertyValue.h" |
| 28 | #include "CSSDeferredParser.h" |
| 29 | #include "CSSParser.h" |
| 30 | #include "CSSPendingSubstitutionValue.h" |
| 31 | #include "CSSPropertyParser.h" |
| 32 | #include "CSSTokenizer.h" |
| 33 | #include "CSSValueKeywords.h" |
| 34 | #include "CSSValueList.h" |
| 35 | #include "CSSValuePool.h" |
| 36 | #include "Color.h" |
| 37 | #include "Document.h" |
| 38 | #include "PropertySetCSSStyleDeclaration.h" |
| 39 | #include "StylePropertyShorthand.h" |
| 40 | #include "StylePropertyShorthandFunctions.h" |
| 41 | #include "StyleSheetContents.h" |
| 42 | #include <bitset> |
| 43 | #include <wtf/text/StringBuilder.h> |
| 44 | |
| 45 | #ifndef NDEBUG |
| 46 | #include <stdio.h> |
| 47 | #include <wtf/text/CString.h> |
| 48 | #endif |
| 49 | |
| 50 | namespace WebCore { |
| 51 | |
| 52 | static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count) |
| 53 | { |
| 54 | return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count; |
| 55 | } |
| 56 | |
| 57 | static bool isInitialOrInherit(const String& value) |
| 58 | { |
| 59 | return value.length() == 7 && (value == "initial" || value == "inherit" ); |
| 60 | } |
| 61 | |
| 62 | Ref<ImmutableStyleProperties> ImmutableStyleProperties::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode) |
| 63 | { |
| 64 | void* slot = WTF::fastMalloc(sizeForImmutableStylePropertiesWithPropertyCount(count)); |
| 65 | return adoptRef(*new (NotNull, slot) ImmutableStyleProperties(properties, count, cssParserMode)); |
| 66 | } |
| 67 | |
| 68 | Ref<ImmutableStyleProperties> StyleProperties::immutableCopyIfNeeded() const |
| 69 | { |
| 70 | if (is<ImmutableStyleProperties>(*this)) |
| 71 | return downcast<ImmutableStyleProperties>(const_cast<StyleProperties&>(*this)); |
| 72 | const MutableStyleProperties& mutableThis = downcast<MutableStyleProperties>(*this); |
| 73 | return ImmutableStyleProperties::create(mutableThis.m_propertyVector.data(), mutableThis.m_propertyVector.size(), cssParserMode()); |
| 74 | } |
| 75 | |
| 76 | MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode) |
| 77 | : StyleProperties(cssParserMode, MutablePropertiesType) |
| 78 | { |
| 79 | } |
| 80 | |
| 81 | MutableStyleProperties::MutableStyleProperties(const CSSProperty* properties, unsigned length) |
| 82 | : StyleProperties(HTMLStandardMode, MutablePropertiesType) |
| 83 | { |
| 84 | m_propertyVector.reserveInitialCapacity(length); |
| 85 | for (unsigned i = 0; i < length; ++i) |
| 86 | m_propertyVector.uncheckedAppend(properties[i]); |
| 87 | } |
| 88 | |
| 89 | MutableStyleProperties::~MutableStyleProperties() = default; |
| 90 | |
| 91 | ImmutableStyleProperties::ImmutableStyleProperties(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode) |
| 92 | : StyleProperties(cssParserMode, length) |
| 93 | { |
| 94 | StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(this->metadataArray()); |
| 95 | CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray()); |
| 96 | for (unsigned i = 0; i < length; ++i) { |
| 97 | metadataArray[i] = properties[i].metadata(); |
| 98 | valueArray[i] = properties[i].value(); |
| 99 | valueArray[i]->ref(); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | ImmutableStyleProperties::~ImmutableStyleProperties() |
| 104 | { |
| 105 | CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray()); |
| 106 | for (unsigned i = 0; i < m_arraySize; ++i) |
| 107 | valueArray[i]->deref(); |
| 108 | } |
| 109 | |
| 110 | MutableStyleProperties::MutableStyleProperties(const StyleProperties& other) |
| 111 | : StyleProperties(other.cssParserMode(), MutablePropertiesType) |
| 112 | { |
| 113 | ASSERT(other.type() != DeferredPropertiesType); |
| 114 | if (is<MutableStyleProperties>(other)) |
| 115 | m_propertyVector = downcast<MutableStyleProperties>(other).m_propertyVector; |
| 116 | else { |
| 117 | const auto& immutableOther = downcast<ImmutableStyleProperties>(other); |
| 118 | unsigned propertyCount = immutableOther.propertyCount(); |
| 119 | m_propertyVector.reserveInitialCapacity(propertyCount); |
| 120 | for (unsigned i = 0; i < propertyCount; ++i) |
| 121 | m_propertyVector.uncheckedAppend(immutableOther.propertyAt(i).toCSSProperty()); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | String StyleProperties::getPropertyValue(CSSPropertyID propertyID) const |
| 126 | { |
| 127 | RefPtr<CSSValue> value = getPropertyCSSValue(propertyID); |
| 128 | if (value) |
| 129 | return value->cssText(); |
| 130 | |
| 131 | const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID); |
| 132 | if (shorthand.length()) { |
| 133 | RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[0]); |
| 134 | if (!value || value->isPendingSubstitutionValue()) |
| 135 | return String(); |
| 136 | // FIXME: If all longhands are the same css-generic keyword(e.g. initial or inherit), |
| 137 | // then the shorthand should be serialized to that keyword. |
| 138 | // It seems to be needed to handle this in a single function commonly for all the shorthands, |
| 139 | // not in each of the shorthand serialization function. |
| 140 | // We could call that function here. |
| 141 | } |
| 142 | |
| 143 | // Shorthand and 4-values properties |
| 144 | switch (propertyID) { |
| 145 | case CSSPropertyAll: |
| 146 | return getCommonValue(allShorthand()); |
| 147 | case CSSPropertyAnimation: |
| 148 | return getLayeredShorthandValue(animationShorthand()); |
| 149 | case CSSPropertyBorderSpacing: |
| 150 | return borderSpacingValue(borderSpacingShorthand()); |
| 151 | case CSSPropertyBackgroundPosition: |
| 152 | return getLayeredShorthandValue(backgroundPositionShorthand()); |
| 153 | case CSSPropertyBackgroundRepeat: |
| 154 | return getLayeredShorthandValue(backgroundRepeatShorthand()); |
| 155 | case CSSPropertyBackground: |
| 156 | return getLayeredShorthandValue(backgroundShorthand()); |
| 157 | case CSSPropertyBorder: |
| 158 | return borderPropertyValue(borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand()); |
| 159 | case CSSPropertyBorderTop: |
| 160 | return getShorthandValue(borderTopShorthand()); |
| 161 | case CSSPropertyBorderRight: |
| 162 | return getShorthandValue(borderRightShorthand()); |
| 163 | case CSSPropertyBorderBottom: |
| 164 | return getShorthandValue(borderBottomShorthand()); |
| 165 | case CSSPropertyBorderLeft: |
| 166 | return getShorthandValue(borderLeftShorthand()); |
| 167 | case CSSPropertyBorderBlock: |
| 168 | return borderPropertyValue(borderBlockWidthShorthand(), borderBlockStyleShorthand(), borderBlockColorShorthand()); |
| 169 | case CSSPropertyBorderBlockColor: |
| 170 | return get2Values(borderBlockColorShorthand()); |
| 171 | case CSSPropertyBorderBlockStyle: |
| 172 | return get2Values(borderBlockStyleShorthand()); |
| 173 | case CSSPropertyBorderBlockWidth: |
| 174 | return get2Values(borderBlockWidthShorthand()); |
| 175 | case CSSPropertyBorderBlockStart: |
| 176 | return getShorthandValue(borderBlockStartShorthand()); |
| 177 | case CSSPropertyBorderBlockEnd: |
| 178 | return getShorthandValue(borderBlockEndShorthand()); |
| 179 | case CSSPropertyBorderInline: |
| 180 | return borderPropertyValue(borderInlineWidthShorthand(), borderInlineStyleShorthand(), borderInlineColorShorthand()); |
| 181 | case CSSPropertyBorderInlineColor: |
| 182 | return get2Values(borderInlineColorShorthand()); |
| 183 | case CSSPropertyBorderInlineStyle: |
| 184 | return get2Values(borderInlineStyleShorthand()); |
| 185 | case CSSPropertyBorderInlineWidth: |
| 186 | return get2Values(borderInlineWidthShorthand()); |
| 187 | case CSSPropertyBorderInlineStart: |
| 188 | return getShorthandValue(borderInlineStartShorthand()); |
| 189 | case CSSPropertyBorderInlineEnd: |
| 190 | return getShorthandValue(borderInlineEndShorthand()); |
| 191 | case CSSPropertyOutline: |
| 192 | return getShorthandValue(outlineShorthand()); |
| 193 | case CSSPropertyBorderColor: |
| 194 | return get4Values(borderColorShorthand()); |
| 195 | case CSSPropertyBorderWidth: |
| 196 | return get4Values(borderWidthShorthand()); |
| 197 | case CSSPropertyBorderStyle: |
| 198 | return get4Values(borderStyleShorthand()); |
| 199 | case CSSPropertyColumnRule: |
| 200 | return getShorthandValue(columnRuleShorthand()); |
| 201 | case CSSPropertyColumns: |
| 202 | return getShorthandValue(columnsShorthand()); |
| 203 | case CSSPropertyFlex: |
| 204 | return getShorthandValue(flexShorthand()); |
| 205 | case CSSPropertyFlexFlow: |
| 206 | return getShorthandValue(flexFlowShorthand()); |
| 207 | case CSSPropertyGridArea: |
| 208 | return getShorthandValue(gridAreaShorthand()); |
| 209 | case CSSPropertyGridTemplate: |
| 210 | return getShorthandValue(gridTemplateShorthand()); |
| 211 | case CSSPropertyGrid: |
| 212 | return getShorthandValue(gridShorthand()); |
| 213 | case CSSPropertyGridColumn: |
| 214 | return getShorthandValue(gridColumnShorthand()); |
| 215 | case CSSPropertyGridRow: |
| 216 | return getShorthandValue(gridRowShorthand()); |
| 217 | case CSSPropertyPageBreakAfter: |
| 218 | return pageBreakPropertyValue(pageBreakAfterShorthand()); |
| 219 | case CSSPropertyPageBreakBefore: |
| 220 | return pageBreakPropertyValue(pageBreakBeforeShorthand()); |
| 221 | case CSSPropertyPageBreakInside: |
| 222 | return pageBreakPropertyValue(pageBreakInsideShorthand()); |
| 223 | case CSSPropertyPlaceContent: |
| 224 | return getAlignmentShorthandValue(placeContentShorthand()); |
| 225 | case CSSPropertyPlaceItems: |
| 226 | return getAlignmentShorthandValue(placeItemsShorthand()); |
| 227 | case CSSPropertyPlaceSelf: |
| 228 | return getAlignmentShorthandValue(placeSelfShorthand()); |
| 229 | case CSSPropertyFont: |
| 230 | return fontValue(); |
| 231 | case CSSPropertyInset: |
| 232 | return get4Values(insetShorthand()); |
| 233 | case CSSPropertyInsetBlock: |
| 234 | return get2Values(insetBlockShorthand()); |
| 235 | case CSSPropertyInsetInline: |
| 236 | return get2Values(insetInlineShorthand()); |
| 237 | case CSSPropertyMargin: |
| 238 | return get4Values(marginShorthand()); |
| 239 | case CSSPropertyMarginBlock: |
| 240 | return get2Values(marginBlockShorthand()); |
| 241 | case CSSPropertyMarginInline: |
| 242 | return get2Values(marginInlineShorthand()); |
| 243 | case CSSPropertyWebkitMarginCollapse: |
| 244 | return getShorthandValue(webkitMarginCollapseShorthand()); |
| 245 | case CSSPropertyOverflow: |
| 246 | return getCommonValue(overflowShorthand()); |
| 247 | case CSSPropertyPadding: |
| 248 | return get4Values(paddingShorthand()); |
| 249 | case CSSPropertyPaddingBlock: |
| 250 | return get2Values(paddingBlockShorthand()); |
| 251 | case CSSPropertyPaddingInline: |
| 252 | return get2Values(paddingInlineShorthand()); |
| 253 | case CSSPropertyTransition: |
| 254 | return getLayeredShorthandValue(transitionShorthand()); |
| 255 | case CSSPropertyListStyle: |
| 256 | return getShorthandValue(listStyleShorthand()); |
| 257 | case CSSPropertyWebkitMarquee: |
| 258 | return getShorthandValue(webkitMarqueeShorthand()); |
| 259 | case CSSPropertyWebkitMaskPosition: |
| 260 | return getLayeredShorthandValue(webkitMaskPositionShorthand()); |
| 261 | case CSSPropertyWebkitMaskRepeat: |
| 262 | return getLayeredShorthandValue(webkitMaskRepeatShorthand()); |
| 263 | case CSSPropertyWebkitMask: |
| 264 | return getLayeredShorthandValue(webkitMaskShorthand()); |
| 265 | case CSSPropertyWebkitTextEmphasis: |
| 266 | return getShorthandValue(webkitTextEmphasisShorthand()); |
| 267 | case CSSPropertyWebkitTextStroke: |
| 268 | return getShorthandValue(webkitTextStrokeShorthand()); |
| 269 | case CSSPropertyPerspectiveOrigin: |
| 270 | return getShorthandValue(perspectiveOriginShorthand()); |
| 271 | case CSSPropertyTransformOrigin: |
| 272 | return getShorthandValue(transformOriginShorthand()); |
| 273 | case CSSPropertyMarker: { |
| 274 | RefPtr<CSSValue> value = getPropertyCSSValueInternal(CSSPropertyMarkerStart); |
| 275 | if (value) |
| 276 | return value->cssText(); |
| 277 | return String(); |
| 278 | } |
| 279 | case CSSPropertyBorderRadius: |
| 280 | return get4Values(borderRadiusShorthand()); |
| 281 | #if ENABLE(CSS_SCROLL_SNAP) |
| 282 | case CSSPropertyScrollSnapMargin: |
| 283 | return get4Values(scrollSnapMarginShorthand()); |
| 284 | case CSSPropertyScrollPadding: |
| 285 | return get4Values(scrollPaddingShorthand()); |
| 286 | #endif |
| 287 | default: |
| 288 | return String(); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | Optional<Color> StyleProperties::propertyAsColor(CSSPropertyID property) const |
| 293 | { |
| 294 | auto colorValue = getPropertyCSSValue(property); |
| 295 | if (!is<CSSPrimitiveValue>(colorValue)) |
| 296 | return WTF::nullopt; |
| 297 | |
| 298 | auto& primitiveColor = downcast<CSSPrimitiveValue>(*colorValue); |
| 299 | return primitiveColor.isRGBColor() ? primitiveColor.color() : CSSParser::parseColor(colorValue->cssText()); |
| 300 | } |
| 301 | |
| 302 | CSSValueID StyleProperties::propertyAsValueID(CSSPropertyID property) const |
| 303 | { |
| 304 | auto cssValue = getPropertyCSSValue(property); |
| 305 | return is<CSSPrimitiveValue>(cssValue) ? downcast<CSSPrimitiveValue>(*cssValue).valueID() : CSSValueInvalid; |
| 306 | } |
| 307 | |
| 308 | String StyleProperties::getCustomPropertyValue(const String& propertyName) const |
| 309 | { |
| 310 | RefPtr<CSSValue> value = getCustomPropertyCSSValue(propertyName); |
| 311 | if (value) |
| 312 | return value->cssText(); |
| 313 | return String(); |
| 314 | } |
| 315 | |
| 316 | String StyleProperties::borderSpacingValue(const StylePropertyShorthand& shorthand) const |
| 317 | { |
| 318 | RefPtr<CSSValue> horizontalValue = getPropertyCSSValueInternal(shorthand.properties()[0]); |
| 319 | RefPtr<CSSValue> verticalValue = getPropertyCSSValueInternal(shorthand.properties()[1]); |
| 320 | |
| 321 | // While standard border-spacing property does not allow specifying border-spacing-vertical without |
| 322 | // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>, |
| 323 | // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal. |
| 324 | if (!horizontalValue || !verticalValue) |
| 325 | return String(); |
| 326 | |
| 327 | String horizontalValueCSSText = horizontalValue->cssText(); |
| 328 | String verticalValueCSSText = verticalValue->cssText(); |
| 329 | if (horizontalValueCSSText == verticalValueCSSText) |
| 330 | return horizontalValueCSSText; |
| 331 | return horizontalValueCSSText + ' ' + verticalValueCSSText; |
| 332 | } |
| 333 | |
| 334 | void StyleProperties::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const |
| 335 | { |
| 336 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 337 | if (foundPropertyIndex == -1) |
| 338 | return; // All longhands must have at least implicit values if "font" is specified. |
| 339 | |
| 340 | if (propertyAt(foundPropertyIndex).isImplicit()) { |
| 341 | commonValue = String(); |
| 342 | return; |
| 343 | } |
| 344 | |
| 345 | char prefix = '\0'; |
| 346 | switch (propertyID) { |
| 347 | case CSSPropertyFontStyle: |
| 348 | break; // No prefix. |
| 349 | case CSSPropertyFontFamily: |
| 350 | case CSSPropertyFontVariantCaps: |
| 351 | case CSSPropertyFontWeight: |
| 352 | case CSSPropertyFontStretch: |
| 353 | prefix = ' '; |
| 354 | break; |
| 355 | case CSSPropertyLineHeight: |
| 356 | prefix = '/'; |
| 357 | break; |
| 358 | default: |
| 359 | ASSERT_NOT_REACHED(); |
| 360 | } |
| 361 | |
| 362 | if (prefix && !result.isEmpty()) |
| 363 | result.append(prefix); |
| 364 | String value = propertyAt(foundPropertyIndex).value()->cssText(); |
| 365 | result.append(value); |
| 366 | if (!commonValue.isNull() && commonValue != value) |
| 367 | commonValue = String(); |
| 368 | } |
| 369 | |
| 370 | String StyleProperties::fontValue() const |
| 371 | { |
| 372 | int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize); |
| 373 | int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily); |
| 374 | if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1) |
| 375 | return emptyString(); |
| 376 | |
| 377 | PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex); |
| 378 | PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex); |
| 379 | if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit()) |
| 380 | return emptyString(); |
| 381 | |
| 382 | String commonValue = fontSizeProperty.value()->cssText(); |
| 383 | StringBuilder result; |
| 384 | appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue); |
| 385 | appendFontLonghandValueIfExplicit(CSSPropertyFontVariantCaps, result, commonValue); |
| 386 | appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue); |
| 387 | appendFontLonghandValueIfExplicit(CSSPropertyFontStretch, result, commonValue); |
| 388 | if (!result.isEmpty()) |
| 389 | result.append(' '); |
| 390 | result.append(fontSizeProperty.value()->cssText()); |
| 391 | appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue); |
| 392 | if (!result.isEmpty()) |
| 393 | result.append(' '); |
| 394 | result.append(fontFamilyProperty.value()->cssText()); |
| 395 | if (isInitialOrInherit(commonValue)) |
| 396 | return commonValue; |
| 397 | return result.toString(); |
| 398 | } |
| 399 | |
| 400 | String StyleProperties::get2Values(const StylePropertyShorthand& shorthand) const |
| 401 | { |
| 402 | // Assume the properties are in the usual order start, end. |
| 403 | int startValueIndex = findPropertyIndex(shorthand.properties()[0]); |
| 404 | int endValueIndex = findPropertyIndex(shorthand.properties()[1]); |
| 405 | |
| 406 | if (startValueIndex == -1 || endValueIndex == -1) |
| 407 | return { }; |
| 408 | |
| 409 | auto start = propertyAt(startValueIndex); |
| 410 | auto end = propertyAt(endValueIndex); |
| 411 | |
| 412 | // All 2 properties must be specified. |
| 413 | if (!start.value() || !end.value()) |
| 414 | return { }; |
| 415 | |
| 416 | // Important flags must be the same |
| 417 | if (start.isImportant() != end.isImportant()) |
| 418 | return { }; |
| 419 | |
| 420 | if (start.isInherited() && end.isInherited()) |
| 421 | return getValueName(CSSValueInherit); |
| 422 | |
| 423 | if (start.value()->isInitialValue() || end.value()->isInitialValue()) { |
| 424 | if (start.value()->isInitialValue() && end.value()->isInitialValue() && !start.isImplicit()) |
| 425 | return getValueName(CSSValueInitial); |
| 426 | return { }; |
| 427 | } |
| 428 | |
| 429 | StringBuilder result; |
| 430 | result.append(start.value()->cssText()); |
| 431 | if (!start.value()->equals(*end.value())) { |
| 432 | result.append(' '); |
| 433 | result.append(end.value()->cssText()); |
| 434 | } |
| 435 | return result.toString(); |
| 436 | } |
| 437 | |
| 438 | String StyleProperties::get4Values(const StylePropertyShorthand& shorthand) const |
| 439 | { |
| 440 | // Assume the properties are in the usual order top, right, bottom, left. |
| 441 | int topValueIndex = findPropertyIndex(shorthand.properties()[0]); |
| 442 | int rightValueIndex = findPropertyIndex(shorthand.properties()[1]); |
| 443 | int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]); |
| 444 | int leftValueIndex = findPropertyIndex(shorthand.properties()[3]); |
| 445 | |
| 446 | if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1) |
| 447 | return String(); |
| 448 | |
| 449 | PropertyReference top = propertyAt(topValueIndex); |
| 450 | PropertyReference right = propertyAt(rightValueIndex); |
| 451 | PropertyReference bottom = propertyAt(bottomValueIndex); |
| 452 | PropertyReference left = propertyAt(leftValueIndex); |
| 453 | |
| 454 | // All 4 properties must be specified. |
| 455 | if (!top.value() || !right.value() || !bottom.value() || !left.value()) |
| 456 | return String(); |
| 457 | |
| 458 | // Important flags must be the same |
| 459 | if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant()) |
| 460 | return String(); |
| 461 | |
| 462 | if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited()) |
| 463 | return getValueName(CSSValueInherit); |
| 464 | |
| 465 | if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) { |
| 466 | if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) { |
| 467 | // All components are "initial" and "top" is not implicit. |
| 468 | return getValueName(CSSValueInitial); |
| 469 | } |
| 470 | return String(); |
| 471 | } |
| 472 | |
| 473 | bool showLeft = !right.value()->equals(*left.value()); |
| 474 | bool showBottom = !top.value()->equals(*bottom.value()) || showLeft; |
| 475 | bool showRight = !top.value()->equals(*right.value()) || showBottom; |
| 476 | |
| 477 | StringBuilder result; |
| 478 | result.append(top.value()->cssText()); |
| 479 | if (showRight) { |
| 480 | result.append(' '); |
| 481 | result.append(right.value()->cssText()); |
| 482 | } |
| 483 | if (showBottom) { |
| 484 | result.append(' '); |
| 485 | result.append(bottom.value()->cssText()); |
| 486 | } |
| 487 | if (showLeft) { |
| 488 | result.append(' '); |
| 489 | result.append(left.value()->cssText()); |
| 490 | } |
| 491 | return result.toString(); |
| 492 | } |
| 493 | |
| 494 | String StyleProperties::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const |
| 495 | { |
| 496 | StringBuilder result; |
| 497 | |
| 498 | const unsigned size = shorthand.length(); |
| 499 | // Begin by collecting the properties into an array. |
| 500 | Vector< RefPtr<CSSValue>> values(size); |
| 501 | size_t numLayers = 0; |
| 502 | |
| 503 | for (unsigned i = 0; i < size; ++i) { |
| 504 | values[i] = getPropertyCSSValueInternal(shorthand.properties()[i]); |
| 505 | if (!values[i]) { |
| 506 | // We don't have all longhand properties defined as required for the shorthand |
| 507 | // property and thus should not serialize to a shorthand value. See spec at |
| 508 | // http://www.w3.org/TR/cssom-1/#serialize-a-css-declaration-block. |
| 509 | return String(); |
| 510 | } |
| 511 | if (values[i]->isBaseValueList()) |
| 512 | numLayers = std::max(downcast<CSSValueList>(*values[i]).length(), numLayers); |
| 513 | else |
| 514 | numLayers = std::max<size_t>(1U, numLayers); |
| 515 | } |
| 516 | |
| 517 | String commonValue; |
| 518 | bool commonValueInitialized = false; |
| 519 | |
| 520 | // Now stitch the properties together. Implicit initial values are flagged as such and |
| 521 | // can safely be omitted. |
| 522 | for (size_t i = 0; i < numLayers; i++) { |
| 523 | StringBuilder layerResult; |
| 524 | bool useRepeatXShorthand = false; |
| 525 | bool useRepeatYShorthand = false; |
| 526 | bool useSingleWordShorthand = false; |
| 527 | bool foundPositionYCSSProperty = false; |
| 528 | for (unsigned j = 0; j < size; j++) { |
| 529 | RefPtr<CSSValue> value; |
| 530 | if (values[j]) { |
| 531 | if (values[j]->isBaseValueList()) |
| 532 | value = downcast<CSSValueList>(*values[j]).item(i); |
| 533 | else { |
| 534 | value = values[j]; |
| 535 | |
| 536 | // Color only belongs in the last layer. |
| 537 | if (shorthand.properties()[j] == CSSPropertyBackgroundColor) { |
| 538 | if (i != numLayers - 1) |
| 539 | value = nullptr; |
| 540 | } else if (i) // Other singletons only belong in the first layer. |
| 541 | value = nullptr; |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | // We need to report background-repeat as it was written in the CSS. If the property is implicit, |
| 546 | // then it was written with only one value. Here we figure out which value that was so we can |
| 547 | // report back correctly. |
| 548 | if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j])) |
| 549 | || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && isPropertyImplicit(shorthand.properties()[j]))) { |
| 550 | |
| 551 | // BUG 49055: make sure the value was not reset in the layer check just above. |
| 552 | if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value) |
| 553 | || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) { |
| 554 | RefPtr<CSSValue> yValue; |
| 555 | RefPtr<CSSValue> nextValue = values[j + 1]; |
| 556 | if (nextValue) { |
| 557 | if (is<CSSValueList>(*nextValue)) |
| 558 | yValue = downcast<CSSValueList>(*nextValue).itemWithoutBoundsCheck(i); |
| 559 | else |
| 560 | yValue = nextValue; |
| 561 | |
| 562 | if (!is<CSSPrimitiveValue>(*value) || !is<CSSPrimitiveValue>(*yValue)) |
| 563 | continue; |
| 564 | |
| 565 | CSSValueID xId = downcast<CSSPrimitiveValue>(*value).valueID(); |
| 566 | CSSValueID yId = downcast<CSSPrimitiveValue>(*yValue).valueID(); |
| 567 | if (xId != yId) { |
| 568 | if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) { |
| 569 | useRepeatXShorthand = true; |
| 570 | ++j; |
| 571 | } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) { |
| 572 | useRepeatYShorthand = true; |
| 573 | continue; |
| 574 | } |
| 575 | } else { |
| 576 | useSingleWordShorthand = true; |
| 577 | ++j; |
| 578 | } |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | String valueText; |
| 584 | if (value && !value->isImplicitInitialValue()) { |
| 585 | if (!layerResult.isEmpty()) |
| 586 | layerResult.append(' '); |
| 587 | if (foundPositionYCSSProperty |
| 588 | && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize)) |
| 589 | layerResult.appendLiteral("/ " ); |
| 590 | if (!foundPositionYCSSProperty |
| 591 | && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize)) |
| 592 | continue; |
| 593 | |
| 594 | if (useRepeatXShorthand) { |
| 595 | useRepeatXShorthand = false; |
| 596 | layerResult.append(getValueName(CSSValueRepeatX)); |
| 597 | } else if (useRepeatYShorthand) { |
| 598 | useRepeatYShorthand = false; |
| 599 | layerResult.append(getValueName(CSSValueRepeatY)); |
| 600 | } else { |
| 601 | if (useSingleWordShorthand) |
| 602 | useSingleWordShorthand = false; |
| 603 | valueText = value->cssText(); |
| 604 | layerResult.append(valueText); |
| 605 | } |
| 606 | |
| 607 | if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY |
| 608 | || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) { |
| 609 | foundPositionYCSSProperty = true; |
| 610 | |
| 611 | // background-position is a special case: if only the first offset is specified, |
| 612 | // the second one defaults to "center", not the same value. |
| 613 | if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit" ) |
| 614 | commonValue = String(); |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | if (!commonValueInitialized) { |
| 619 | commonValue = valueText; |
| 620 | commonValueInitialized = true; |
| 621 | } else if (!commonValue.isNull() && commonValue != valueText) |
| 622 | commonValue = String(); |
| 623 | } |
| 624 | |
| 625 | if (!layerResult.isEmpty()) { |
| 626 | if (!result.isEmpty()) |
| 627 | result.appendLiteral(", " ); |
| 628 | result.append(layerResult); |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | if (isInitialOrInherit(commonValue)) |
| 633 | return commonValue; |
| 634 | |
| 635 | if (result.isEmpty()) |
| 636 | return String(); |
| 637 | return result.toString(); |
| 638 | } |
| 639 | |
| 640 | String StyleProperties::getShorthandValue(const StylePropertyShorthand& shorthand) const |
| 641 | { |
| 642 | String commonValue; |
| 643 | StringBuilder result; |
| 644 | for (unsigned i = 0; i < shorthand.length(); ++i) { |
| 645 | if (!isPropertyImplicit(shorthand.properties()[i])) { |
| 646 | RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[i]); |
| 647 | if (!value) |
| 648 | return String(); |
| 649 | String valueText = value->cssText(); |
| 650 | if (!i) |
| 651 | commonValue = valueText; |
| 652 | else if (!commonValue.isNull() && commonValue != valueText) |
| 653 | commonValue = String(); |
| 654 | if (value->isInitialValue()) |
| 655 | continue; |
| 656 | if (!result.isEmpty()) |
| 657 | result.append(' '); |
| 658 | result.append(valueText); |
| 659 | } else |
| 660 | commonValue = String(); |
| 661 | } |
| 662 | if (isInitialOrInherit(commonValue)) |
| 663 | return commonValue; |
| 664 | if (result.isEmpty()) |
| 665 | return String(); |
| 666 | return result.toString(); |
| 667 | } |
| 668 | |
| 669 | // only returns a non-null value if all properties have the same, non-null value |
| 670 | String StyleProperties::getCommonValue(const StylePropertyShorthand& shorthand) const |
| 671 | { |
| 672 | String res; |
| 673 | bool lastPropertyWasImportant = false; |
| 674 | for (unsigned i = 0; i < shorthand.length(); ++i) { |
| 675 | RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[i]); |
| 676 | if (!value) |
| 677 | return String(); |
| 678 | // FIXME: CSSInitialValue::cssText should generate the right value. |
| 679 | String text = value->cssText(); |
| 680 | if (text.isNull()) |
| 681 | return String(); |
| 682 | if (res.isNull()) |
| 683 | res = text; |
| 684 | else if (res != text) |
| 685 | return String(); |
| 686 | |
| 687 | bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]); |
| 688 | if (i && lastPropertyWasImportant != currentPropertyIsImportant) |
| 689 | return String(); |
| 690 | lastPropertyWasImportant = currentPropertyIsImportant; |
| 691 | } |
| 692 | return res; |
| 693 | } |
| 694 | |
| 695 | String StyleProperties::getAlignmentShorthandValue(const StylePropertyShorthand& shorthand) const |
| 696 | { |
| 697 | String value = getCommonValue(shorthand); |
| 698 | if (value.isNull() || value.isEmpty()) |
| 699 | return getShorthandValue(shorthand); |
| 700 | return value; |
| 701 | } |
| 702 | |
| 703 | String StyleProperties::borderPropertyValue(const StylePropertyShorthand& width, const StylePropertyShorthand& style, const StylePropertyShorthand& color) const |
| 704 | { |
| 705 | const StylePropertyShorthand properties[3] = { width, style, color }; |
| 706 | String commonValue; |
| 707 | StringBuilder result; |
| 708 | for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) { |
| 709 | String value = getCommonValue(properties[i]); |
| 710 | if (value.isNull()) |
| 711 | return String(); |
| 712 | if (!i) |
| 713 | commonValue = value; |
| 714 | else if (commonValue != value) |
| 715 | commonValue = String(); |
| 716 | if (value == "initial" ) |
| 717 | continue; |
| 718 | if (!result.isEmpty()) |
| 719 | result.append(' '); |
| 720 | result.append(value); |
| 721 | } |
| 722 | if (isInitialOrInherit(commonValue)) |
| 723 | return commonValue; |
| 724 | return result.toString(); |
| 725 | } |
| 726 | |
| 727 | String StyleProperties::pageBreakPropertyValue(const StylePropertyShorthand& shorthand) const |
| 728 | { |
| 729 | RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[0]); |
| 730 | // FIXME: Remove this isGlobalKeyword check after we do this consistently for all shorthands in getPropertyValue. |
| 731 | if (value->isGlobalKeyword()) |
| 732 | return value->cssText(); |
| 733 | CSSValueID valueId = downcast<CSSPrimitiveValue>(*value).valueID(); |
| 734 | switch (valueId) { |
| 735 | case CSSValuePage: |
| 736 | return "always"_s ; |
| 737 | case CSSValueAuto: |
| 738 | case CSSValueAvoid: |
| 739 | case CSSValueLeft: |
| 740 | case CSSValueRight: |
| 741 | return value->cssText(); |
| 742 | default: |
| 743 | return String(); |
| 744 | } |
| 745 | } |
| 746 | |
| 747 | RefPtr<CSSValue> StyleProperties::getPropertyCSSValue(CSSPropertyID propertyID) const |
| 748 | { |
| 749 | return getPropertyCSSValueInternal(propertyID); |
| 750 | } |
| 751 | |
| 752 | RefPtr<CSSValue> StyleProperties::getPropertyCSSValueInternal(CSSPropertyID propertyID) const |
| 753 | { |
| 754 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 755 | if (foundPropertyIndex == -1) |
| 756 | return nullptr; |
| 757 | return propertyAt(foundPropertyIndex).value(); |
| 758 | } |
| 759 | |
| 760 | RefPtr<CSSValue> StyleProperties::getCustomPropertyCSSValue(const String& propertyName) const |
| 761 | { |
| 762 | int foundPropertyIndex = findCustomPropertyIndex(propertyName); |
| 763 | if (foundPropertyIndex == -1) |
| 764 | return nullptr; |
| 765 | return propertyAt(foundPropertyIndex).value(); |
| 766 | } |
| 767 | |
| 768 | bool MutableStyleProperties::removeShorthandProperty(CSSPropertyID propertyID) |
| 769 | { |
| 770 | StylePropertyShorthand shorthand = shorthandForProperty(propertyID); |
| 771 | if (!shorthand.length()) |
| 772 | return false; |
| 773 | |
| 774 | return removePropertiesInSet(shorthand.properties(), shorthand.length()); |
| 775 | } |
| 776 | |
| 777 | bool MutableStyleProperties::removeProperty(CSSPropertyID propertyID, String* returnText) |
| 778 | { |
| 779 | if (removeShorthandProperty(propertyID)) { |
| 780 | // FIXME: Return an equivalent shorthand when possible. |
| 781 | if (returnText) |
| 782 | *returnText = emptyString(); |
| 783 | return true; |
| 784 | } |
| 785 | |
| 786 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 787 | if (foundPropertyIndex == -1) { |
| 788 | if (returnText) |
| 789 | *returnText = emptyString(); |
| 790 | return false; |
| 791 | } |
| 792 | |
| 793 | if (returnText) |
| 794 | *returnText = propertyAt(foundPropertyIndex).value()->cssText(); |
| 795 | |
| 796 | // A more efficient removal strategy would involve marking entries as empty |
| 797 | // and sweeping them when the vector grows too big. |
| 798 | m_propertyVector.remove(foundPropertyIndex); |
| 799 | |
| 800 | return true; |
| 801 | } |
| 802 | |
| 803 | bool MutableStyleProperties::removeCustomProperty(const String& propertyName, String* returnText) |
| 804 | { |
| 805 | int foundPropertyIndex = findCustomPropertyIndex(propertyName); |
| 806 | if (foundPropertyIndex == -1) { |
| 807 | if (returnText) |
| 808 | *returnText = emptyString(); |
| 809 | return false; |
| 810 | } |
| 811 | |
| 812 | if (returnText) |
| 813 | *returnText = propertyAt(foundPropertyIndex).value()->cssText(); |
| 814 | |
| 815 | // A more efficient removal strategy would involve marking entries as empty |
| 816 | // and sweeping them when the vector grows too big. |
| 817 | m_propertyVector.remove(foundPropertyIndex); |
| 818 | |
| 819 | return true; |
| 820 | } |
| 821 | |
| 822 | bool StyleProperties::propertyIsImportant(CSSPropertyID propertyID) const |
| 823 | { |
| 824 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 825 | if (foundPropertyIndex != -1) |
| 826 | return propertyAt(foundPropertyIndex).isImportant(); |
| 827 | |
| 828 | StylePropertyShorthand shorthand = shorthandForProperty(propertyID); |
| 829 | if (!shorthand.length()) |
| 830 | return false; |
| 831 | |
| 832 | for (unsigned i = 0; i < shorthand.length(); ++i) { |
| 833 | if (!propertyIsImportant(shorthand.properties()[i])) |
| 834 | return false; |
| 835 | } |
| 836 | return true; |
| 837 | } |
| 838 | |
| 839 | bool StyleProperties::customPropertyIsImportant(const String& propertyName) const |
| 840 | { |
| 841 | int foundPropertyIndex = findCustomPropertyIndex(propertyName); |
| 842 | if (foundPropertyIndex != -1) |
| 843 | return propertyAt(foundPropertyIndex).isImportant(); |
| 844 | return false; |
| 845 | } |
| 846 | |
| 847 | String StyleProperties::getPropertyShorthand(CSSPropertyID propertyID) const |
| 848 | { |
| 849 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 850 | if (foundPropertyIndex == -1) |
| 851 | return String(); |
| 852 | return getPropertyNameString(propertyAt(foundPropertyIndex).shorthandID()); |
| 853 | } |
| 854 | |
| 855 | bool StyleProperties::isPropertyImplicit(CSSPropertyID propertyID) const |
| 856 | { |
| 857 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 858 | if (foundPropertyIndex == -1) |
| 859 | return false; |
| 860 | return propertyAt(foundPropertyIndex).isImplicit(); |
| 861 | } |
| 862 | |
| 863 | bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important, CSSParserContext parserContext) |
| 864 | { |
| 865 | if (!isEnabledCSSProperty(propertyID)) |
| 866 | return false; |
| 867 | |
| 868 | // Setting the value to an empty string just removes the property in both IE and Gecko. |
| 869 | // Setting it to null seems to produce less consistent results, but we treat it just the same. |
| 870 | if (value.isEmpty()) |
| 871 | return removeProperty(propertyID); |
| 872 | |
| 873 | parserContext.mode = cssParserMode(); |
| 874 | |
| 875 | // When replacing an existing property value, this moves the property to the end of the list. |
| 876 | // Firefox preserves the position, and MSIE moves the property to the beginning. |
| 877 | return CSSParser::parseValue(*this, propertyID, value, important, parserContext) == CSSParser::ParseResult::Changed; |
| 878 | } |
| 879 | |
| 880 | bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important) |
| 881 | { |
| 882 | CSSParserContext parserContext(cssParserMode()); |
| 883 | return setProperty(propertyID, value, important, parserContext); |
| 884 | } |
| 885 | |
| 886 | bool MutableStyleProperties::setCustomProperty(const Document* document, const String& propertyName, const String& value, bool important, CSSParserContext parserContext) |
| 887 | { |
| 888 | // Setting the value to an empty string just removes the property in both IE and Gecko. |
| 889 | // Setting it to null seems to produce less consistent results, but we treat it just the same. |
| 890 | if (value.isEmpty()) |
| 891 | return removeCustomProperty(propertyName); |
| 892 | |
| 893 | parserContext.mode = cssParserMode(); |
| 894 | |
| 895 | String syntax = "*" ; |
| 896 | auto* registered = document ? document->getCSSRegisteredCustomPropertySet().get(propertyName) : nullptr; |
| 897 | |
| 898 | if (registered) |
| 899 | syntax = registered->syntax; |
| 900 | |
| 901 | CSSTokenizer tokenizer(value); |
| 902 | if (!CSSPropertyParser::canParseTypedCustomPropertyValue(syntax, tokenizer.tokenRange(), parserContext)) |
| 903 | return false; |
| 904 | |
| 905 | // When replacing an existing property value, this moves the property to the end of the list. |
| 906 | // Firefox preserves the position, and MSIE moves the property to the beginning. |
| 907 | return CSSParser::parseCustomPropertyValue(*this, propertyName, value, important, parserContext) == CSSParser::ParseResult::Changed; |
| 908 | } |
| 909 | |
| 910 | void MutableStyleProperties::setProperty(CSSPropertyID propertyID, RefPtr<CSSValue>&& value, bool important) |
| 911 | { |
| 912 | StylePropertyShorthand shorthand = shorthandForProperty(propertyID); |
| 913 | if (!shorthand.length()) { |
| 914 | setProperty(CSSProperty(propertyID, WTFMove(value), important)); |
| 915 | return; |
| 916 | } |
| 917 | |
| 918 | removePropertiesInSet(shorthand.properties(), shorthand.length()); |
| 919 | |
| 920 | for (unsigned i = 0; i < shorthand.length(); ++i) |
| 921 | m_propertyVector.append(CSSProperty(shorthand.properties()[i], value.copyRef(), important)); |
| 922 | } |
| 923 | |
| 924 | bool MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot) |
| 925 | { |
| 926 | if (!removeShorthandProperty(property.id())) { |
| 927 | CSSProperty* toReplace = slot; |
| 928 | if (!slot) { |
| 929 | if (property.id() == CSSPropertyCustom) { |
| 930 | if (property.value()) |
| 931 | toReplace = findCustomCSSPropertyWithName(downcast<CSSCustomPropertyValue>(*property.value()).name()); |
| 932 | } else |
| 933 | toReplace = findCSSPropertyWithID(property.id()); |
| 934 | } |
| 935 | |
| 936 | if (toReplace) { |
| 937 | if (*toReplace == property) |
| 938 | return false; |
| 939 | |
| 940 | *toReplace = property; |
| 941 | return true; |
| 942 | } |
| 943 | } |
| 944 | |
| 945 | m_propertyVector.append(property); |
| 946 | return true; |
| 947 | } |
| 948 | |
| 949 | bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important) |
| 950 | { |
| 951 | return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important)); |
| 952 | } |
| 953 | |
| 954 | bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important) |
| 955 | { |
| 956 | return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important)); |
| 957 | } |
| 958 | |
| 959 | bool MutableStyleProperties::parseDeclaration(const String& styleDeclaration, CSSParserContext context) |
| 960 | { |
| 961 | auto oldProperties = WTFMove(m_propertyVector); |
| 962 | m_propertyVector.clear(); |
| 963 | |
| 964 | context.mode = cssParserMode(); |
| 965 | |
| 966 | CSSParser parser(context); |
| 967 | parser.parseDeclaration(*this, styleDeclaration); |
| 968 | |
| 969 | // We could do better. Just changing property order does not require style invalidation. |
| 970 | return oldProperties != m_propertyVector; |
| 971 | } |
| 972 | |
| 973 | bool MutableStyleProperties::addParsedProperties(const ParsedPropertyVector& properties) |
| 974 | { |
| 975 | bool anyChanged = false; |
| 976 | m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size()); |
| 977 | for (const auto& property : properties) { |
| 978 | if (addParsedProperty(property)) |
| 979 | anyChanged = true; |
| 980 | } |
| 981 | |
| 982 | return anyChanged; |
| 983 | } |
| 984 | |
| 985 | bool MutableStyleProperties::addParsedProperty(const CSSProperty& property) |
| 986 | { |
| 987 | if (property.id() == CSSPropertyCustom) { |
| 988 | if ((property.value() && !customPropertyIsImportant(downcast<CSSCustomPropertyValue>(*property.value()).name())) || property.isImportant()) |
| 989 | return setProperty(property); |
| 990 | return false; |
| 991 | } |
| 992 | return setProperty(property); |
| 993 | } |
| 994 | |
| 995 | String StyleProperties::asText() const |
| 996 | { |
| 997 | StringBuilder result; |
| 998 | |
| 999 | int positionXPropertyIndex = -1; |
| 1000 | int positionYPropertyIndex = -1; |
| 1001 | int repeatXPropertyIndex = -1; |
| 1002 | int repeatYPropertyIndex = -1; |
| 1003 | |
| 1004 | std::bitset<numCSSProperties> shorthandPropertyUsed; |
| 1005 | std::bitset<numCSSProperties> shorthandPropertyAppeared; |
| 1006 | |
| 1007 | unsigned size = propertyCount(); |
| 1008 | unsigned numDecls = 0; |
| 1009 | for (unsigned n = 0; n < size; ++n) { |
| 1010 | PropertyReference property = propertyAt(n); |
| 1011 | CSSPropertyID propertyID = property.id(); |
| 1012 | CSSPropertyID shorthandPropertyID = CSSPropertyInvalid; |
| 1013 | CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid; |
| 1014 | CSSPropertyID borderBlockFallbackShorthandProperty = CSSPropertyInvalid; |
| 1015 | CSSPropertyID borderInlineFallbackShorthandProperty = CSSPropertyInvalid; |
| 1016 | String value; |
| 1017 | auto serializeBorderShorthand = [&] (const CSSPropertyID borderProperty, const CSSPropertyID fallbackProperty) { |
| 1018 | // FIXME: Deal with cases where only some of border sides are specified. |
| 1019 | ASSERT(borderProperty - firstCSSProperty < static_cast<CSSPropertyID>(shorthandPropertyAppeared.size())); |
| 1020 | if (!shorthandPropertyAppeared[borderProperty - firstCSSProperty] && isEnabledCSSProperty(borderProperty)) { |
| 1021 | value = getPropertyValue(borderProperty); |
| 1022 | if (value.isNull()) |
| 1023 | shorthandPropertyAppeared.set(borderProperty - firstCSSProperty); |
| 1024 | else |
| 1025 | shorthandPropertyID = borderProperty; |
| 1026 | } else if (shorthandPropertyUsed[borderProperty - firstCSSProperty]) |
| 1027 | shorthandPropertyID = borderProperty; |
| 1028 | if (!shorthandPropertyID) |
| 1029 | shorthandPropertyID = fallbackProperty; |
| 1030 | }; |
| 1031 | |
| 1032 | if (property.value() && property.value()->isPendingSubstitutionValue()) { |
| 1033 | auto& substitutionValue = downcast<CSSPendingSubstitutionValue>(*property.value()); |
| 1034 | shorthandPropertyID = substitutionValue.shorthandPropertyId(); |
| 1035 | value = substitutionValue.shorthandValue()->cssText(); |
| 1036 | } else { |
| 1037 | switch (propertyID) { |
| 1038 | case CSSPropertyAnimationName: |
| 1039 | case CSSPropertyAnimationDuration: |
| 1040 | case CSSPropertyAnimationTimingFunction: |
| 1041 | case CSSPropertyAnimationDelay: |
| 1042 | case CSSPropertyAnimationIterationCount: |
| 1043 | case CSSPropertyAnimationDirection: |
| 1044 | case CSSPropertyAnimationFillMode: |
| 1045 | case CSSPropertyAnimationPlayState: |
| 1046 | shorthandPropertyID = CSSPropertyAnimation; |
| 1047 | break; |
| 1048 | case CSSPropertyBackgroundPositionX: |
| 1049 | positionXPropertyIndex = n; |
| 1050 | continue; |
| 1051 | case CSSPropertyBackgroundPositionY: |
| 1052 | positionYPropertyIndex = n; |
| 1053 | continue; |
| 1054 | case CSSPropertyBackgroundRepeatX: |
| 1055 | repeatXPropertyIndex = n; |
| 1056 | continue; |
| 1057 | case CSSPropertyBackgroundRepeatY: |
| 1058 | repeatYPropertyIndex = n; |
| 1059 | continue; |
| 1060 | case CSSPropertyBorderTopWidth: |
| 1061 | case CSSPropertyBorderRightWidth: |
| 1062 | case CSSPropertyBorderBottomWidth: |
| 1063 | case CSSPropertyBorderLeftWidth: |
| 1064 | if (!borderFallbackShorthandProperty) |
| 1065 | borderFallbackShorthandProperty = CSSPropertyBorderWidth; |
| 1066 | FALLTHROUGH; |
| 1067 | case CSSPropertyBorderTopStyle: |
| 1068 | case CSSPropertyBorderRightStyle: |
| 1069 | case CSSPropertyBorderBottomStyle: |
| 1070 | case CSSPropertyBorderLeftStyle: |
| 1071 | if (!borderFallbackShorthandProperty) |
| 1072 | borderFallbackShorthandProperty = CSSPropertyBorderStyle; |
| 1073 | FALLTHROUGH; |
| 1074 | case CSSPropertyBorderTopColor: |
| 1075 | case CSSPropertyBorderRightColor: |
| 1076 | case CSSPropertyBorderBottomColor: |
| 1077 | case CSSPropertyBorderLeftColor: |
| 1078 | if (!borderFallbackShorthandProperty) |
| 1079 | borderFallbackShorthandProperty = CSSPropertyBorderColor; |
| 1080 | serializeBorderShorthand(CSSPropertyBorder, borderFallbackShorthandProperty); |
| 1081 | break; |
| 1082 | case CSSPropertyBorderBlockStartWidth: |
| 1083 | case CSSPropertyBorderBlockEndWidth: |
| 1084 | if (!borderBlockFallbackShorthandProperty) |
| 1085 | borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockWidth; |
| 1086 | FALLTHROUGH; |
| 1087 | case CSSPropertyBorderBlockStartStyle: |
| 1088 | case CSSPropertyBorderBlockEndStyle: |
| 1089 | if (!borderBlockFallbackShorthandProperty) |
| 1090 | borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockStyle; |
| 1091 | FALLTHROUGH; |
| 1092 | case CSSPropertyBorderBlockStartColor: |
| 1093 | case CSSPropertyBorderBlockEndColor: |
| 1094 | if (!borderBlockFallbackShorthandProperty) |
| 1095 | borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockColor; |
| 1096 | serializeBorderShorthand(CSSPropertyBorderBlock, borderBlockFallbackShorthandProperty); |
| 1097 | break; |
| 1098 | case CSSPropertyBorderInlineStartWidth: |
| 1099 | case CSSPropertyBorderInlineEndWidth: |
| 1100 | if (!borderInlineFallbackShorthandProperty) |
| 1101 | borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineWidth; |
| 1102 | FALLTHROUGH; |
| 1103 | case CSSPropertyBorderInlineStartStyle: |
| 1104 | case CSSPropertyBorderInlineEndStyle: |
| 1105 | if (!borderInlineFallbackShorthandProperty) |
| 1106 | borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineStyle; |
| 1107 | FALLTHROUGH; |
| 1108 | case CSSPropertyBorderInlineStartColor: |
| 1109 | case CSSPropertyBorderInlineEndColor: |
| 1110 | if (!borderInlineFallbackShorthandProperty) |
| 1111 | borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineColor; |
| 1112 | serializeBorderShorthand(CSSPropertyBorderInline, borderInlineFallbackShorthandProperty); |
| 1113 | break; |
| 1114 | case CSSPropertyWebkitBorderHorizontalSpacing: |
| 1115 | case CSSPropertyWebkitBorderVerticalSpacing: |
| 1116 | shorthandPropertyID = CSSPropertyBorderSpacing; |
| 1117 | break; |
| 1118 | case CSSPropertyFontFamily: |
| 1119 | case CSSPropertyLineHeight: |
| 1120 | case CSSPropertyFontSize: |
| 1121 | case CSSPropertyFontStyle: |
| 1122 | case CSSPropertyFontVariantCaps: |
| 1123 | case CSSPropertyFontWeight: |
| 1124 | // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing. |
| 1125 | break; |
| 1126 | case CSSPropertyTop: |
| 1127 | case CSSPropertyRight: |
| 1128 | case CSSPropertyBottom: |
| 1129 | case CSSPropertyLeft: |
| 1130 | shorthandPropertyID = CSSPropertyInset; |
| 1131 | break; |
| 1132 | case CSSPropertyInsetBlockStart: |
| 1133 | case CSSPropertyInsetBlockEnd: |
| 1134 | shorthandPropertyID = CSSPropertyInsetBlock; |
| 1135 | break; |
| 1136 | case CSSPropertyInsetInlineStart: |
| 1137 | case CSSPropertyInsetInlineEnd: |
| 1138 | shorthandPropertyID = CSSPropertyInsetInline; |
| 1139 | break; |
| 1140 | case CSSPropertyListStyleType: |
| 1141 | case CSSPropertyListStylePosition: |
| 1142 | case CSSPropertyListStyleImage: |
| 1143 | shorthandPropertyID = CSSPropertyListStyle; |
| 1144 | break; |
| 1145 | case CSSPropertyMarginTop: |
| 1146 | case CSSPropertyMarginRight: |
| 1147 | case CSSPropertyMarginBottom: |
| 1148 | case CSSPropertyMarginLeft: |
| 1149 | shorthandPropertyID = CSSPropertyMargin; |
| 1150 | break; |
| 1151 | case CSSPropertyMarginBlockStart: |
| 1152 | case CSSPropertyMarginBlockEnd: |
| 1153 | shorthandPropertyID = CSSPropertyMarginBlock; |
| 1154 | break; |
| 1155 | case CSSPropertyMarginInlineStart: |
| 1156 | case CSSPropertyMarginInlineEnd: |
| 1157 | shorthandPropertyID = CSSPropertyMarginInline; |
| 1158 | break; |
| 1159 | case CSSPropertyOutlineWidth: |
| 1160 | case CSSPropertyOutlineStyle: |
| 1161 | case CSSPropertyOutlineColor: |
| 1162 | shorthandPropertyID = CSSPropertyOutline; |
| 1163 | break; |
| 1164 | case CSSPropertyOverflowX: |
| 1165 | case CSSPropertyOverflowY: |
| 1166 | shorthandPropertyID = CSSPropertyOverflow; |
| 1167 | break; |
| 1168 | case CSSPropertyPaddingTop: |
| 1169 | case CSSPropertyPaddingRight: |
| 1170 | case CSSPropertyPaddingBottom: |
| 1171 | case CSSPropertyPaddingLeft: |
| 1172 | shorthandPropertyID = CSSPropertyPadding; |
| 1173 | break; |
| 1174 | case CSSPropertyPaddingBlockStart: |
| 1175 | case CSSPropertyPaddingBlockEnd: |
| 1176 | shorthandPropertyID = CSSPropertyPaddingBlock; |
| 1177 | break; |
| 1178 | case CSSPropertyPaddingInlineStart: |
| 1179 | case CSSPropertyPaddingInlineEnd: |
| 1180 | shorthandPropertyID = CSSPropertyPaddingInline; |
| 1181 | break; |
| 1182 | #if ENABLE(CSS_SCROLL_SNAP) |
| 1183 | case CSSPropertyScrollPaddingTop: |
| 1184 | case CSSPropertyScrollPaddingRight: |
| 1185 | case CSSPropertyScrollPaddingBottom: |
| 1186 | case CSSPropertyScrollPaddingLeft: |
| 1187 | shorthandPropertyID = CSSPropertyScrollPadding; |
| 1188 | break; |
| 1189 | case CSSPropertyScrollSnapMarginTop: |
| 1190 | case CSSPropertyScrollSnapMarginRight: |
| 1191 | case CSSPropertyScrollSnapMarginBottom: |
| 1192 | case CSSPropertyScrollSnapMarginLeft: |
| 1193 | shorthandPropertyID = CSSPropertyScrollSnapMargin; |
| 1194 | break; |
| 1195 | #endif |
| 1196 | case CSSPropertyTransitionProperty: |
| 1197 | case CSSPropertyTransitionDuration: |
| 1198 | case CSSPropertyTransitionTimingFunction: |
| 1199 | case CSSPropertyTransitionDelay: |
| 1200 | shorthandPropertyID = CSSPropertyTransition; |
| 1201 | break; |
| 1202 | case CSSPropertyFlexDirection: |
| 1203 | case CSSPropertyFlexWrap: |
| 1204 | shorthandPropertyID = CSSPropertyFlexFlow; |
| 1205 | break; |
| 1206 | case CSSPropertyFlexBasis: |
| 1207 | case CSSPropertyFlexGrow: |
| 1208 | case CSSPropertyFlexShrink: |
| 1209 | shorthandPropertyID = CSSPropertyFlex; |
| 1210 | break; |
| 1211 | case CSSPropertyWebkitMaskPositionX: |
| 1212 | case CSSPropertyWebkitMaskPositionY: |
| 1213 | case CSSPropertyWebkitMaskRepeatX: |
| 1214 | case CSSPropertyWebkitMaskRepeatY: |
| 1215 | case CSSPropertyWebkitMaskImage: |
| 1216 | case CSSPropertyWebkitMaskRepeat: |
| 1217 | case CSSPropertyWebkitMaskPosition: |
| 1218 | case CSSPropertyWebkitMaskClip: |
| 1219 | case CSSPropertyWebkitMaskOrigin: |
| 1220 | shorthandPropertyID = CSSPropertyWebkitMask; |
| 1221 | break; |
| 1222 | case CSSPropertyPerspectiveOriginX: |
| 1223 | case CSSPropertyPerspectiveOriginY: |
| 1224 | shorthandPropertyID = CSSPropertyPerspectiveOrigin; |
| 1225 | break; |
| 1226 | case CSSPropertyTransformOriginX: |
| 1227 | case CSSPropertyTransformOriginY: |
| 1228 | case CSSPropertyTransformOriginZ: |
| 1229 | shorthandPropertyID = CSSPropertyTransformOrigin; |
| 1230 | break; |
| 1231 | default: |
| 1232 | break; |
| 1233 | } |
| 1234 | } |
| 1235 | |
| 1236 | unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty; |
| 1237 | if (shorthandPropertyID && isEnabledCSSProperty(shorthandPropertyID)) { |
| 1238 | ASSERT(shortPropertyIndex < shorthandPropertyUsed.size()); |
| 1239 | if (shorthandPropertyUsed[shortPropertyIndex]) |
| 1240 | continue; |
| 1241 | if (!shorthandPropertyAppeared[shortPropertyIndex] && value.isNull()) |
| 1242 | value = getPropertyValue(shorthandPropertyID); |
| 1243 | shorthandPropertyAppeared.set(shortPropertyIndex); |
| 1244 | } |
| 1245 | |
| 1246 | if (!value.isNull()) { |
| 1247 | propertyID = shorthandPropertyID; |
| 1248 | shorthandPropertyUsed.set(shortPropertyIndex); |
| 1249 | } else |
| 1250 | value = property.value()->cssText(); |
| 1251 | |
| 1252 | if (propertyID != CSSPropertyCustom && value == "initial" && !CSSProperty::isInheritedProperty(propertyID)) |
| 1253 | continue; |
| 1254 | |
| 1255 | if (numDecls++) |
| 1256 | result.append(' '); |
| 1257 | |
| 1258 | if (propertyID == CSSPropertyCustom) |
| 1259 | result.append(downcast<CSSCustomPropertyValue>(*property.value()).name()); |
| 1260 | else |
| 1261 | result.append(getPropertyName(propertyID)); |
| 1262 | |
| 1263 | result.appendLiteral(": " ); |
| 1264 | result.append(value); |
| 1265 | if (property.isImportant()) |
| 1266 | result.appendLiteral(" !important" ); |
| 1267 | result.append(';'); |
| 1268 | } |
| 1269 | |
| 1270 | // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output. |
| 1271 | // It is required because background-position-x/y are non-standard properties and WebKit generated output |
| 1272 | // would not work in Firefox (<rdar://problem/5143183>) |
| 1273 | // It would be a better solution if background-position was CSS_PAIR. |
| 1274 | if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) { |
| 1275 | PropertyReference positionXProperty = propertyAt(positionXPropertyIndex); |
| 1276 | PropertyReference positionYProperty = propertyAt(positionYPropertyIndex); |
| 1277 | |
| 1278 | if (numDecls++) |
| 1279 | result.append(' '); |
| 1280 | result.appendLiteral("background-position: " ); |
| 1281 | if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList()) |
| 1282 | result.append(getLayeredShorthandValue(backgroundPositionShorthand())); |
| 1283 | else { |
| 1284 | result.append(positionXProperty.value()->cssText()); |
| 1285 | result.append(' '); |
| 1286 | result.append(positionYProperty.value()->cssText()); |
| 1287 | } |
| 1288 | if (positionXProperty.isImportant()) |
| 1289 | result.appendLiteral(" !important" ); |
| 1290 | result.append(';'); |
| 1291 | } else { |
| 1292 | if (positionXPropertyIndex != -1) { |
| 1293 | if (numDecls++) |
| 1294 | result.append(' '); |
| 1295 | result.append(propertyAt(positionXPropertyIndex).cssText()); |
| 1296 | } |
| 1297 | if (positionYPropertyIndex != -1) { |
| 1298 | if (numDecls++) |
| 1299 | result.append(' '); |
| 1300 | result.append(propertyAt(positionYPropertyIndex).cssText()); |
| 1301 | } |
| 1302 | } |
| 1303 | |
| 1304 | // FIXME: We need to do the same for background-repeat. |
| 1305 | if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) { |
| 1306 | PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex); |
| 1307 | PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex); |
| 1308 | |
| 1309 | if (numDecls++) |
| 1310 | result.append(' '); |
| 1311 | result.appendLiteral("background-repeat: " ); |
| 1312 | if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList()) |
| 1313 | result.append(getLayeredShorthandValue(backgroundRepeatShorthand())); |
| 1314 | else { |
| 1315 | result.append(repeatXProperty.value()->cssText()); |
| 1316 | result.append(' '); |
| 1317 | result.append(repeatYProperty.value()->cssText()); |
| 1318 | } |
| 1319 | if (repeatXProperty.isImportant()) |
| 1320 | result.appendLiteral(" !important" ); |
| 1321 | result.append(';'); |
| 1322 | } else { |
| 1323 | if (repeatXPropertyIndex != -1) { |
| 1324 | if (numDecls++) |
| 1325 | result.append(' '); |
| 1326 | result.append(propertyAt(repeatXPropertyIndex).cssText()); |
| 1327 | } |
| 1328 | if (repeatYPropertyIndex != -1) { |
| 1329 | if (numDecls++) |
| 1330 | result.append(' '); |
| 1331 | result.append(propertyAt(repeatYPropertyIndex).cssText()); |
| 1332 | } |
| 1333 | } |
| 1334 | |
| 1335 | ASSERT(!numDecls ^ !result.isEmpty()); |
| 1336 | return result.toString(); |
| 1337 | } |
| 1338 | |
| 1339 | bool StyleProperties::hasCSSOMWrapper() const |
| 1340 | { |
| 1341 | return is<MutableStyleProperties>(*this) && downcast<MutableStyleProperties>(*this).m_cssomWrapper; |
| 1342 | } |
| 1343 | |
| 1344 | void MutableStyleProperties::mergeAndOverrideOnConflict(const StyleProperties& other) |
| 1345 | { |
| 1346 | unsigned size = other.propertyCount(); |
| 1347 | for (unsigned i = 0; i < size; ++i) |
| 1348 | addParsedProperty(other.propertyAt(i).toCSSProperty()); |
| 1349 | } |
| 1350 | |
| 1351 | bool StyleProperties::traverseSubresources(const WTF::Function<bool (const CachedResource&)>& handler) const |
| 1352 | { |
| 1353 | unsigned size = propertyCount(); |
| 1354 | for (unsigned i = 0; i < size; ++i) { |
| 1355 | if (propertyAt(i).value()->traverseSubresources(handler)) |
| 1356 | return true; |
| 1357 | } |
| 1358 | return false; |
| 1359 | } |
| 1360 | |
| 1361 | // This is the list of properties we want to copy in the copyBlockProperties() function. |
| 1362 | // It is the list of CSS properties that apply specially to block-level elements. |
| 1363 | static const CSSPropertyID blockProperties[] = { |
| 1364 | CSSPropertyOrphans, |
| 1365 | CSSPropertyOverflow, // This can be also be applied to replaced elements |
| 1366 | CSSPropertyWebkitAspectRatio, |
| 1367 | CSSPropertyColumnCount, |
| 1368 | CSSPropertyColumnGap, |
| 1369 | CSSPropertyRowGap, |
| 1370 | CSSPropertyColumnRuleColor, |
| 1371 | CSSPropertyColumnRuleStyle, |
| 1372 | CSSPropertyColumnRuleWidth, |
| 1373 | CSSPropertyWebkitColumnBreakBefore, |
| 1374 | CSSPropertyWebkitColumnBreakAfter, |
| 1375 | CSSPropertyWebkitColumnBreakInside, |
| 1376 | CSSPropertyColumnWidth, |
| 1377 | CSSPropertyPageBreakAfter, |
| 1378 | CSSPropertyPageBreakBefore, |
| 1379 | CSSPropertyPageBreakInside, |
| 1380 | CSSPropertyTextAlign, |
| 1381 | #if ENABLE(CSS3_TEXT) |
| 1382 | CSSPropertyWebkitTextAlignLast, |
| 1383 | CSSPropertyWebkitTextJustify, |
| 1384 | #endif // CSS3_TEXT |
| 1385 | CSSPropertyTextIndent, |
| 1386 | CSSPropertyWidows |
| 1387 | }; |
| 1388 | |
| 1389 | void MutableStyleProperties::clear() |
| 1390 | { |
| 1391 | m_propertyVector.clear(); |
| 1392 | } |
| 1393 | |
| 1394 | const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties); |
| 1395 | |
| 1396 | Ref<MutableStyleProperties> StyleProperties::copyBlockProperties() const |
| 1397 | { |
| 1398 | return copyPropertiesInSet(blockProperties, numBlockProperties); |
| 1399 | } |
| 1400 | |
| 1401 | void MutableStyleProperties::removeBlockProperties() |
| 1402 | { |
| 1403 | removePropertiesInSet(blockProperties, numBlockProperties); |
| 1404 | } |
| 1405 | |
| 1406 | bool MutableStyleProperties::removePropertiesInSet(const CSSPropertyID* set, unsigned length) |
| 1407 | { |
| 1408 | if (m_propertyVector.isEmpty()) |
| 1409 | return false; |
| 1410 | |
| 1411 | // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless. |
| 1412 | HashSet<CSSPropertyID> toRemove; |
| 1413 | for (unsigned i = 0; i < length; ++i) |
| 1414 | toRemove.add(set[i]); |
| 1415 | |
| 1416 | return m_propertyVector.removeAllMatching([&toRemove] (const CSSProperty& property) { |
| 1417 | return toRemove.contains(property.id()); |
| 1418 | }) > 0; |
| 1419 | } |
| 1420 | |
| 1421 | int ImmutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const |
| 1422 | { |
| 1423 | // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid |
| 1424 | // the compiler converting it to an int multiple times in the loop. |
| 1425 | uint16_t id = static_cast<uint16_t>(propertyID); |
| 1426 | for (int n = m_arraySize - 1 ; n >= 0; --n) { |
| 1427 | if (metadataArray()[n].m_propertyID == id) |
| 1428 | return n; |
| 1429 | } |
| 1430 | |
| 1431 | return -1; |
| 1432 | } |
| 1433 | |
| 1434 | int MutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const |
| 1435 | { |
| 1436 | // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid |
| 1437 | // the compiler converting it to an int multiple times in the loop. |
| 1438 | uint16_t id = static_cast<uint16_t>(propertyID); |
| 1439 | for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) { |
| 1440 | if (m_propertyVector.at(n).metadata().m_propertyID == id) |
| 1441 | return n; |
| 1442 | } |
| 1443 | |
| 1444 | return -1; |
| 1445 | } |
| 1446 | |
| 1447 | int ImmutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const |
| 1448 | { |
| 1449 | // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid |
| 1450 | // the compiler converting it to an int multiple times in the loop. |
| 1451 | for (int n = m_arraySize - 1 ; n >= 0; --n) { |
| 1452 | if (metadataArray()[n].m_propertyID == CSSPropertyCustom) { |
| 1453 | // We found a custom property. See if the name matches. |
| 1454 | if (!valueArray()[n]) |
| 1455 | continue; |
| 1456 | if (downcast<CSSCustomPropertyValue>(*valueArray()[n]).name() == propertyName) |
| 1457 | return n; |
| 1458 | } |
| 1459 | } |
| 1460 | |
| 1461 | return -1; |
| 1462 | } |
| 1463 | |
| 1464 | int MutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const |
| 1465 | { |
| 1466 | // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid |
| 1467 | // the compiler converting it to an int multiple times in the loop. |
| 1468 | for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) { |
| 1469 | if (m_propertyVector.at(n).metadata().m_propertyID == CSSPropertyCustom) { |
| 1470 | // We found a custom property. See if the name matches. |
| 1471 | if (!m_propertyVector.at(n).value()) |
| 1472 | continue; |
| 1473 | if (downcast<CSSCustomPropertyValue>(*m_propertyVector.at(n).value()).name() == propertyName) |
| 1474 | return n; |
| 1475 | } |
| 1476 | } |
| 1477 | |
| 1478 | return -1; |
| 1479 | } |
| 1480 | |
| 1481 | CSSProperty* MutableStyleProperties::findCSSPropertyWithID(CSSPropertyID propertyID) |
| 1482 | { |
| 1483 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 1484 | if (foundPropertyIndex == -1) |
| 1485 | return 0; |
| 1486 | return &m_propertyVector.at(foundPropertyIndex); |
| 1487 | } |
| 1488 | |
| 1489 | CSSProperty* MutableStyleProperties::findCustomCSSPropertyWithName(const String& propertyName) |
| 1490 | { |
| 1491 | int foundPropertyIndex = findCustomPropertyIndex(propertyName); |
| 1492 | if (foundPropertyIndex == -1) |
| 1493 | return 0; |
| 1494 | return &m_propertyVector.at(foundPropertyIndex); |
| 1495 | } |
| 1496 | |
| 1497 | bool StyleProperties::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const |
| 1498 | { |
| 1499 | int foundPropertyIndex = findPropertyIndex(propertyID); |
| 1500 | if (foundPropertyIndex == -1) |
| 1501 | return false; |
| 1502 | return propertyAt(foundPropertyIndex).value()->equals(*propertyValue); |
| 1503 | } |
| 1504 | |
| 1505 | Ref<MutableStyleProperties> StyleProperties::mutableCopy() const |
| 1506 | { |
| 1507 | return adoptRef(*new MutableStyleProperties(*this)); |
| 1508 | } |
| 1509 | |
| 1510 | Ref<MutableStyleProperties> StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const |
| 1511 | { |
| 1512 | Vector<CSSProperty, 256> list; |
| 1513 | list.reserveInitialCapacity(length); |
| 1514 | for (unsigned i = 0; i < length; ++i) { |
| 1515 | if (auto value = getPropertyCSSValueInternal(set[i])) |
| 1516 | list.uncheckedAppend(CSSProperty(set[i], WTFMove(value), false)); |
| 1517 | } |
| 1518 | return MutableStyleProperties::create(list.data(), list.size()); |
| 1519 | } |
| 1520 | |
| 1521 | PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration() |
| 1522 | { |
| 1523 | return m_cssomWrapper.get(); |
| 1524 | } |
| 1525 | |
| 1526 | CSSStyleDeclaration& MutableStyleProperties::ensureCSSStyleDeclaration() |
| 1527 | { |
| 1528 | if (m_cssomWrapper) { |
| 1529 | ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule()); |
| 1530 | ASSERT(!m_cssomWrapper->parentElement()); |
| 1531 | return *m_cssomWrapper; |
| 1532 | } |
| 1533 | m_cssomWrapper = std::make_unique<PropertySetCSSStyleDeclaration>(*this); |
| 1534 | return *m_cssomWrapper; |
| 1535 | } |
| 1536 | |
| 1537 | CSSStyleDeclaration& MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement& parentElement) |
| 1538 | { |
| 1539 | if (m_cssomWrapper) { |
| 1540 | ASSERT(m_cssomWrapper->parentElement() == &parentElement); |
| 1541 | return *m_cssomWrapper; |
| 1542 | } |
| 1543 | m_cssomWrapper = std::make_unique<InlineCSSStyleDeclaration>(*this, parentElement); |
| 1544 | return *m_cssomWrapper; |
| 1545 | } |
| 1546 | |
| 1547 | unsigned StyleProperties::averageSizeInBytes() |
| 1548 | { |
| 1549 | // Please update this if the storage scheme changes so that this longer reflects the actual size. |
| 1550 | return sizeForImmutableStylePropertiesWithPropertyCount(4); |
| 1551 | } |
| 1552 | |
| 1553 | // See the function above if you need to update this. |
| 1554 | struct SameSizeAsStyleProperties : public RefCounted<SameSizeAsStyleProperties> { |
| 1555 | unsigned bitfield; |
| 1556 | }; |
| 1557 | COMPILE_ASSERT(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), style_property_set_should_stay_small); |
| 1558 | |
| 1559 | #ifndef NDEBUG |
| 1560 | void StyleProperties::showStyle() |
| 1561 | { |
| 1562 | fprintf(stderr, "%s\n" , asText().ascii().data()); |
| 1563 | } |
| 1564 | #endif |
| 1565 | |
| 1566 | Ref<MutableStyleProperties> MutableStyleProperties::create(CSSParserMode cssParserMode) |
| 1567 | { |
| 1568 | return adoptRef(*new MutableStyleProperties(cssParserMode)); |
| 1569 | } |
| 1570 | |
| 1571 | Ref<MutableStyleProperties> MutableStyleProperties::create(const CSSProperty* properties, unsigned count) |
| 1572 | { |
| 1573 | return adoptRef(*new MutableStyleProperties(properties, count)); |
| 1574 | } |
| 1575 | |
| 1576 | String StyleProperties::PropertyReference::cssName() const |
| 1577 | { |
| 1578 | if (id() == CSSPropertyCustom) |
| 1579 | return downcast<CSSCustomPropertyValue>(*value()).name(); |
| 1580 | return getPropertyNameString(id()); |
| 1581 | } |
| 1582 | |
| 1583 | String StyleProperties::PropertyReference::cssText() const |
| 1584 | { |
| 1585 | StringBuilder result; |
| 1586 | result.append(cssName()); |
| 1587 | result.appendLiteral(": " ); |
| 1588 | result.append(m_value->cssText()); |
| 1589 | if (isImportant()) |
| 1590 | result.appendLiteral(" !important" ); |
| 1591 | result.append(';'); |
| 1592 | return result.toString(); |
| 1593 | } |
| 1594 | |
| 1595 | Ref<DeferredStyleProperties> DeferredStyleProperties::create(const CSSParserTokenRange& tokenRange, CSSDeferredParser& parser) |
| 1596 | { |
| 1597 | return adoptRef(*new DeferredStyleProperties(tokenRange, parser)); |
| 1598 | } |
| 1599 | |
| 1600 | DeferredStyleProperties::DeferredStyleProperties(const CSSParserTokenRange& range, CSSDeferredParser& parser) |
| 1601 | : StylePropertiesBase(parser.mode(), DeferredPropertiesType) |
| 1602 | , m_parser(parser) |
| 1603 | { |
| 1604 | size_t length = range.end() - range.begin(); |
| 1605 | m_tokens.reserveCapacity(length); |
| 1606 | m_tokens.append(range.begin(), length); |
| 1607 | } |
| 1608 | |
| 1609 | DeferredStyleProperties::~DeferredStyleProperties() = default; |
| 1610 | |
| 1611 | Ref<ImmutableStyleProperties> DeferredStyleProperties::parseDeferredProperties() |
| 1612 | { |
| 1613 | return m_parser->parseDeclaration(m_tokens); |
| 1614 | } |
| 1615 | |
| 1616 | } // namespace WebCore |
| 1617 | |