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