1/* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * * Redistributions of source code must retain the above copyright 5 * notice, this list of conditions and the following disclaimer. 6 * * Redistributions in binary form must reproduce the above 7 * copyright notice, this list of conditions and the following disclaimer 8 * in the documentation and/or other materials provided with the 9 * distribution. 10 * * Neither the name of Google Inc. nor the names of its 11 * contributors may be used to endorse or promote products derived from 12 * this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "core/css/resolver/StyleBuilderConverter.h" 29 30#include "core/css/CSSFunctionValue.h" 31#include "core/css/CSSGridLineNamesValue.h" 32#include "core/css/CSSPrimitiveValueMappings.h" 33#include "core/css/CSSReflectValue.h" 34#include "core/css/CSSShadowValue.h" 35#include "core/css/Pair.h" 36#include "core/svg/SVGURIReference.h" 37 38namespace WebCore { 39 40namespace { 41 42static GridLength convertGridTrackBreadth(const StyleResolverState& state, CSSPrimitiveValue* primitiveValue) 43{ 44 if (primitiveValue->getValueID() == CSSValueMinContent) 45 return Length(MinContent); 46 47 if (primitiveValue->getValueID() == CSSValueMaxContent) 48 return Length(MaxContent); 49 50 // Fractional unit. 51 if (primitiveValue->isFlex()) 52 return GridLength(primitiveValue->getDoubleValue()); 53 54 return primitiveValue->convertToLength<FixedConversion | PercentConversion | AutoConversion>(state.cssToLengthConversionData()); 55} 56 57} // namespace 58 59PassRefPtr<StyleReflection> StyleBuilderConverter::convertBoxReflect(StyleResolverState& state, CSSValue* value) 60{ 61 if (value->isPrimitiveValue()) { 62 ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueNone); 63 return RenderStyle::initialBoxReflect(); 64 } 65 66 CSSReflectValue* reflectValue = toCSSReflectValue(value); 67 RefPtr<StyleReflection> reflection = StyleReflection::create(); 68 reflection->setDirection(*reflectValue->direction()); 69 if (reflectValue->offset()) 70 reflection->setOffset(reflectValue->offset()->convertToLength<FixedConversion | PercentConversion>(state.cssToLengthConversionData())); 71 NinePieceImage mask; 72 mask.setMaskDefaults(); 73 state.styleMap().mapNinePieceImage(state.style(), CSSPropertyWebkitBoxReflect, reflectValue->mask(), mask); 74 reflection->setMask(mask); 75 76 return reflection.release(); 77} 78 79AtomicString StyleBuilderConverter::convertFragmentIdentifier(StyleResolverState& state, CSSValue* value) 80{ 81 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 82 if (primitiveValue->isURI()) 83 return SVGURIReference::fragmentIdentifierFromIRIString(primitiveValue->getStringValue(), state.element()->treeScope()); 84 return nullAtom; 85} 86 87EGlyphOrientation StyleBuilderConverter::convertGlyphOrientation(StyleResolverState&, CSSValue* value) 88{ 89 if (!value->isPrimitiveValue()) 90 return GO_0DEG; 91 92 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 93 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_DEG) 94 return GO_0DEG; 95 96 float angle = fabsf(fmodf(primitiveValue->getFloatValue(), 360.0f)); 97 98 if (angle <= 45.0f || angle > 315.0f) 99 return GO_0DEG; 100 if (angle > 45.0f && angle <= 135.0f) 101 return GO_90DEG; 102 if (angle > 135.0f && angle <= 225.0f) 103 return GO_180DEG; 104 return GO_270DEG; 105} 106 107GridPosition StyleBuilderConverter::convertGridPosition(StyleResolverState&, CSSValue* value) 108{ 109 // We accept the specification's grammar: 110 // 'auto' | [ <integer> || <custom-ident> ] | [ span && [ <integer> || <custom-ident> ] ] | <custom-ident> 111 112 GridPosition position; 113 114 if (value->isPrimitiveValue()) { 115 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 116 // We translate <custom-ident> to <string> during parsing as it 117 // makes handling it more simple. 118 if (primitiveValue->isString()) { 119 position.setNamedGridArea(primitiveValue->getStringValue()); 120 return position; 121 } 122 123 ASSERT(primitiveValue->getValueID() == CSSValueAuto); 124 return position; 125 } 126 127 CSSValueList* values = toCSSValueList(value); 128 ASSERT(values->length()); 129 130 bool isSpanPosition = false; 131 // The specification makes the <integer> optional, in which case it default to '1'. 132 int gridLineNumber = 1; 133 String gridLineName; 134 135 CSSValueListIterator it = values; 136 CSSPrimitiveValue* currentValue = toCSSPrimitiveValue(it.value()); 137 if (currentValue->getValueID() == CSSValueSpan) { 138 isSpanPosition = true; 139 it.advance(); 140 currentValue = it.hasMore() ? toCSSPrimitiveValue(it.value()) : 0; 141 } 142 143 if (currentValue && currentValue->isNumber()) { 144 gridLineNumber = currentValue->getIntValue(); 145 it.advance(); 146 currentValue = it.hasMore() ? toCSSPrimitiveValue(it.value()) : 0; 147 } 148 149 if (currentValue && currentValue->isString()) { 150 gridLineName = currentValue->getStringValue(); 151 it.advance(); 152 } 153 154 ASSERT(!it.hasMore()); 155 if (isSpanPosition) 156 position.setSpanPosition(gridLineNumber, gridLineName); 157 else 158 position.setExplicitPosition(gridLineNumber, gridLineName); 159 160 return position; 161} 162 163GridTrackSize StyleBuilderConverter::convertGridTrackSize(StyleResolverState& state, CSSValue* value) 164{ 165 if (value->isPrimitiveValue()) 166 return GridTrackSize(convertGridTrackBreadth(state, toCSSPrimitiveValue(value))); 167 168 CSSFunctionValue* minmaxFunction = toCSSFunctionValue(value); 169 CSSValueList* arguments = minmaxFunction->arguments(); 170 ASSERT_WITH_SECURITY_IMPLICATION(arguments->length() == 2); 171 GridLength minTrackBreadth(convertGridTrackBreadth(state, toCSSPrimitiveValue(arguments->itemWithoutBoundsCheck(0)))); 172 GridLength maxTrackBreadth(convertGridTrackBreadth(state, toCSSPrimitiveValue(arguments->itemWithoutBoundsCheck(1)))); 173 return GridTrackSize(minTrackBreadth, maxTrackBreadth); 174} 175 176bool StyleBuilderConverter::convertGridTrackList(CSSValue* value, Vector<GridTrackSize>& trackSizes, NamedGridLinesMap& namedGridLines, OrderedNamedGridLines& orderedNamedGridLines, StyleResolverState& state) 177{ 178 // Handle 'none'. 179 if (value->isPrimitiveValue()) { 180 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 181 return primitiveValue->getValueID() == CSSValueNone; 182 } 183 184 if (!value->isValueList()) 185 return false; 186 187 size_t currentNamedGridLine = 0; 188 for (CSSValueListIterator i = value; i.hasMore(); i.advance()) { 189 CSSValue* currValue = i.value(); 190 if (currValue->isGridLineNamesValue()) { 191 CSSGridLineNamesValue* lineNamesValue = toCSSGridLineNamesValue(currValue); 192 for (CSSValueListIterator j = lineNamesValue; j.hasMore(); j.advance()) { 193 String namedGridLine = toCSSPrimitiveValue(j.value())->getStringValue(); 194 NamedGridLinesMap::AddResult result = namedGridLines.add(namedGridLine, Vector<size_t>()); 195 result.storedValue->value.append(currentNamedGridLine); 196 OrderedNamedGridLines::AddResult orderedInsertionResult = orderedNamedGridLines.add(currentNamedGridLine, Vector<String>()); 197 orderedInsertionResult.storedValue->value.append(namedGridLine); 198 } 199 continue; 200 } 201 202 ++currentNamedGridLine; 203 trackSizes.append(convertGridTrackSize(state, currValue)); 204 } 205 206 // The parser should have rejected any <track-list> without any <track-size> as 207 // this is not conformant to the syntax. 208 ASSERT(!trackSizes.isEmpty()); 209 return true; 210} 211 212void StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(const NamedGridAreaMap& namedGridAreas, NamedGridLinesMap& namedGridLines, GridTrackSizingDirection direction) 213{ 214 NamedGridAreaMap::const_iterator end = namedGridAreas.end(); 215 for (NamedGridAreaMap::const_iterator it = namedGridAreas.begin(); it != end; ++it) { 216 GridSpan areaSpan = direction == ForRows ? it->value.rows : it->value.columns; 217 { 218 NamedGridLinesMap::AddResult startResult = namedGridLines.add(it->key + "-start", Vector<size_t>()); 219 startResult.storedValue->value.append(areaSpan.resolvedInitialPosition.toInt()); 220 std::sort(startResult.storedValue->value.begin(), startResult.storedValue->value.end()); 221 } 222 { 223 NamedGridLinesMap::AddResult endResult = namedGridLines.add(it->key + "-end", Vector<size_t>()); 224 endResult.storedValue->value.append(areaSpan.resolvedFinalPosition.toInt() + 1); 225 std::sort(endResult.storedValue->value.begin(), endResult.storedValue->value.end()); 226 } 227 } 228} 229 230Length StyleBuilderConverter::convertLength(StyleResolverState& state, CSSValue* value) 231{ 232 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 233 Length result = primitiveValue->convertToLength<FixedConversion | PercentConversion>(state.cssToLengthConversionData()); 234 result.setQuirk(primitiveValue->isQuirkValue()); 235 return result; 236} 237 238Length StyleBuilderConverter::convertLengthOrAuto(StyleResolverState& state, CSSValue* value) 239{ 240 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 241 Length result = primitiveValue->convertToLength<FixedConversion | PercentConversion | AutoConversion>(state.cssToLengthConversionData()); 242 result.setQuirk(primitiveValue->isQuirkValue()); 243 return result; 244} 245 246Length StyleBuilderConverter::convertLengthSizing(StyleResolverState& state, CSSValue* value) 247{ 248 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 249 switch (primitiveValue->getValueID()) { 250 case CSSValueInvalid: 251 return convertLength(state, value); 252 case CSSValueIntrinsic: 253 return Length(Intrinsic); 254 case CSSValueMinIntrinsic: 255 return Length(MinIntrinsic); 256 case CSSValueWebkitMinContent: 257 return Length(MinContent); 258 case CSSValueWebkitMaxContent: 259 return Length(MaxContent); 260 case CSSValueWebkitFillAvailable: 261 return Length(FillAvailable); 262 case CSSValueWebkitFitContent: 263 return Length(FitContent); 264 case CSSValueAuto: 265 return Length(Auto); 266 default: 267 ASSERT_NOT_REACHED(); 268 return Length(); 269 } 270} 271 272Length StyleBuilderConverter::convertLengthMaxSizing(StyleResolverState& state, CSSValue* value) 273{ 274 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 275 if (primitiveValue->getValueID() == CSSValueNone) 276 return Length(Undefined); 277 return convertLengthSizing(state, value); 278} 279 280LengthPoint StyleBuilderConverter::convertLengthPoint(StyleResolverState& state, CSSValue* value) 281{ 282 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 283 Pair* pair = primitiveValue->getPairValue(); 284 Length x = pair->first()->convertToLength<FixedConversion | PercentConversion>(state.cssToLengthConversionData()); 285 Length y = pair->second()->convertToLength<FixedConversion | PercentConversion>(state.cssToLengthConversionData()); 286 return LengthPoint(x, y); 287} 288 289LineBoxContain StyleBuilderConverter::convertLineBoxContain(StyleResolverState&, CSSValue* value) 290{ 291 if (value->isPrimitiveValue()) { 292 ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueNone); 293 return LineBoxContainNone; 294 } 295 296 return toCSSLineBoxContainValue(value)->value(); 297} 298 299float StyleBuilderConverter::convertNumberOrPercentage(StyleResolverState& state, CSSValue* value) 300{ 301 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 302 ASSERT(primitiveValue->isNumber() || primitiveValue->isPercentage()); 303 if (primitiveValue->isNumber()) 304 return primitiveValue->getFloatValue(); 305 return primitiveValue->getFloatValue() / 100.0f; 306} 307 308EPaintOrder StyleBuilderConverter::convertPaintOrder(StyleResolverState&, CSSValue* cssPaintOrder) 309{ 310 if (cssPaintOrder->isValueList()) { 311 int paintOrder = 0; 312 CSSValueListInspector iter(cssPaintOrder); 313 for (size_t i = 0; i < iter.length(); i++) { 314 CSSPrimitiveValue* value = toCSSPrimitiveValue(iter.item(i)); 315 316 EPaintOrderType paintOrderType = PT_NONE; 317 switch (value->getValueID()) { 318 case CSSValueFill: 319 paintOrderType = PT_FILL; 320 break; 321 case CSSValueStroke: 322 paintOrderType = PT_STROKE; 323 break; 324 case CSSValueMarkers: 325 paintOrderType = PT_MARKERS; 326 break; 327 default: 328 ASSERT_NOT_REACHED(); 329 break; 330 } 331 332 paintOrder |= (paintOrderType << kPaintOrderBitwidth*i); 333 } 334 return (EPaintOrder)paintOrder; 335 } 336 337 return PO_NORMAL; 338} 339 340PassRefPtr<QuotesData> StyleBuilderConverter::convertQuotes(StyleResolverState&, CSSValue* value) 341{ 342 if (value->isValueList()) { 343 CSSValueList* list = toCSSValueList(value); 344 RefPtr<QuotesData> quotes = QuotesData::create(); 345 for (size_t i = 0; i < list->length(); i += 2) { 346 CSSValue* first = list->itemWithoutBoundsCheck(i); 347 // item() returns null if out of bounds so this is safe. 348 CSSValue* second = list->item(i + 1); 349 if (!second) 350 continue; 351 String startQuote = toCSSPrimitiveValue(first)->getStringValue(); 352 String endQuote = toCSSPrimitiveValue(second)->getStringValue(); 353 quotes->addPair(std::make_pair(startQuote, endQuote)); 354 } 355 return quotes.release(); 356 } 357 // FIXME: We should assert we're a primitive value with valueID = CSSValueNone 358 return QuotesData::create(); 359} 360 361LengthSize StyleBuilderConverter::convertRadius(StyleResolverState& state, CSSValue* value) 362{ 363 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 364 Pair* pair = primitiveValue->getPairValue(); 365 Length radiusWidth = pair->first()->convertToLength<FixedConversion | PercentConversion>(state.cssToLengthConversionData()); 366 Length radiusHeight = pair->second()->convertToLength<FixedConversion | PercentConversion>(state.cssToLengthConversionData()); 367 float width = radiusWidth.value(); 368 float height = radiusHeight.value(); 369 ASSERT(width >= 0 && height >= 0); 370 if (width <= 0 || height <= 0) 371 return LengthSize(Length(0, Fixed), Length(0, Fixed)); 372 return LengthSize(radiusWidth, radiusHeight); 373} 374 375PassRefPtr<ShadowList> StyleBuilderConverter::convertShadow(StyleResolverState& state, CSSValue* value) 376{ 377 if (value->isPrimitiveValue()) { 378 ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueNone); 379 return PassRefPtr<ShadowList>(); 380 } 381 382 const CSSValueList* valueList = toCSSValueList(value); 383 size_t shadowCount = valueList->length(); 384 ShadowDataVector shadows; 385 for (size_t i = 0; i < shadowCount; ++i) { 386 const CSSShadowValue* item = toCSSShadowValue(valueList->item(i)); 387 float x = item->x->computeLength<float>(state.cssToLengthConversionData()); 388 float y = item->y->computeLength<float>(state.cssToLengthConversionData()); 389 float blur = item->blur ? item->blur->computeLength<float>(state.cssToLengthConversionData()) : 0; 390 float spread = item->spread ? item->spread->computeLength<float>(state.cssToLengthConversionData()) : 0; 391 ShadowStyle shadowStyle = item->style && item->style->getValueID() == CSSValueInset ? Inset : Normal; 392 Color color; 393 if (item->color) 394 color = state.document().textLinkColors().colorFromPrimitiveValue(item->color.get(), state.style()->color()); 395 else 396 color = state.style()->color(); 397 shadows.append(ShadowData(FloatPoint(x, y), blur, spread, shadowStyle, color)); 398 } 399 return ShadowList::adopt(shadows); 400} 401 402float StyleBuilderConverter::convertSpacing(StyleResolverState& state, CSSValue* value) 403{ 404 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 405 if (primitiveValue->getValueID() == CSSValueNormal) 406 return 0; 407 return primitiveValue->computeLength<float>(state.cssToLengthConversionData()); 408} 409 410PassRefPtr<SVGLengthList> StyleBuilderConverter::convertStrokeDasharray(StyleResolverState&, CSSValue* value) 411{ 412 if (!value->isValueList()) { 413 return SVGRenderStyle::initialStrokeDashArray(); 414 } 415 416 CSSValueList* dashes = toCSSValueList(value); 417 418 RefPtr<SVGLengthList> array = SVGLengthList::create(); 419 size_t length = dashes->length(); 420 for (size_t i = 0; i < length; ++i) { 421 CSSValue* currValue = dashes->itemWithoutBoundsCheck(i); 422 if (!currValue->isPrimitiveValue()) 423 continue; 424 425 CSSPrimitiveValue* dash = toCSSPrimitiveValue(dashes->itemWithoutBoundsCheck(i)); 426 array->append(SVGLength::fromCSSPrimitiveValue(dash)); 427 } 428 429 return array.release(); 430} 431 432Color StyleBuilderConverter::convertSVGColor(StyleResolverState& state, CSSValue* value) 433{ 434 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 435 if (primitiveValue->isRGBColor()) 436 return primitiveValue->getRGBA32Value(); 437 ASSERT(primitiveValue->getValueID() == CSSValueCurrentcolor); 438 return state.style()->color(); 439} 440 441PassRefPtr<SVGLength> StyleBuilderConverter::convertSVGLength(StyleResolverState&, CSSValue* value) 442{ 443 return SVGLength::fromCSSPrimitiveValue(toCSSPrimitiveValue(value)); 444} 445 446float StyleBuilderConverter::convertTextStrokeWidth(StyleResolverState& state, CSSValue* value) 447{ 448 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 449 if (primitiveValue->getValueID()) { 450 float multiplier = convertLineWidth<float>(state, value); 451 return CSSPrimitiveValue::create(multiplier / 48, CSSPrimitiveValue::CSS_EMS)->computeLength<float>(state.cssToLengthConversionData()); 452 } 453 return primitiveValue->computeLength<float>(state.cssToLengthConversionData()); 454} 455 456} // namespace WebCore 457