3a158180f13dd846f1be770c8816312d2b74d8d4
[WebKit-https.git] / Source / WebCore / css / StyleBuilderCustom.h
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  * Copyright (C) 2014 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #ifndef StyleBuilderCustom_h
28 #define StyleBuilderCustom_h
29
30 #include "BasicShapeFunctions.h"
31 #include "CSSImageGeneratorValue.h"
32 #include "CSSImageSetValue.h"
33 #include "CSSImageValue.h"
34 #include "StyleResolver.h"
35
36 namespace WebCore {
37
38 // Note that we assume the CSS parser only allows valid CSSValue types.
39 namespace StyleBuilderFunctions {
40
41 inline void applyValueWebkitMarqueeIncrement(StyleResolver& styleResolver, CSSValue& value)
42 {
43     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
44     Length marqueeLength(Undefined);
45     switch (primitiveValue.getValueID()) {
46     case CSSValueSmall:
47         marqueeLength = Length(1, Fixed); // 1px.
48         break;
49     case CSSValueNormal:
50         marqueeLength = Length(6, Fixed); // 6px. The WinIE default.
51         break;
52     case CSSValueLarge:
53         marqueeLength = Length(36, Fixed); // 36px.
54         break;
55     case CSSValueInvalid:
56         marqueeLength = primitiveValue.convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f));
57         break;
58     default:
59         break;
60     }
61     if (!marqueeLength.isUndefined())
62         styleResolver.style()->setMarqueeIncrement(marqueeLength);
63 }
64
65 inline void applyValueDirection(StyleResolver& styleResolver, CSSValue& value)
66 {
67     styleResolver.style()->setDirection(downcast<CSSPrimitiveValue>(value));
68
69     Element* element = styleResolver.element();
70     if (element && styleResolver.element() == element->document().documentElement())
71         element->document().setDirectionSetOnDocumentElement(true);
72 }
73
74 static inline void resetEffectiveZoom(StyleResolver& styleResolver)
75 {
76     // Reset the zoom in effect. This allows the setZoom method to accurately compute a new zoom in effect.
77     styleResolver.setEffectiveZoom(styleResolver.parentStyle() ? styleResolver.parentStyle()->effectiveZoom() : RenderStyle::initialZoom());
78 }
79
80 inline void applyInitialZoom(StyleResolver& styleResolver)
81 {
82     resetEffectiveZoom(styleResolver);
83     styleResolver.setZoom(RenderStyle::initialZoom());
84 }
85
86 inline void applyInheritZoom(StyleResolver& styleResolver)
87 {
88     resetEffectiveZoom(styleResolver);
89     styleResolver.setZoom(styleResolver.parentStyle()->zoom());
90 }
91
92 inline void applyValueZoom(StyleResolver& styleResolver, CSSValue& value)
93 {
94     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
95
96     if (primitiveValue.getValueID() == CSSValueNormal) {
97         resetEffectiveZoom(styleResolver);
98         styleResolver.setZoom(RenderStyle::initialZoom());
99     } else if (primitiveValue.getValueID() == CSSValueReset) {
100         styleResolver.setEffectiveZoom(RenderStyle::initialZoom());
101         styleResolver.setZoom(RenderStyle::initialZoom());
102     } else if (primitiveValue.getValueID() == CSSValueDocument) {
103         float docZoom = styleResolver.rootElementStyle() ? styleResolver.rootElementStyle()->zoom() : RenderStyle::initialZoom();
104         styleResolver.setEffectiveZoom(docZoom);
105         styleResolver.setZoom(docZoom);
106     } else if (primitiveValue.isPercentage()) {
107         resetEffectiveZoom(styleResolver);
108         if (float percent = primitiveValue.getFloatValue())
109             styleResolver.setZoom(percent / 100.0f);
110     } else if (primitiveValue.isNumber()) {
111         resetEffectiveZoom(styleResolver);
112         if (float number = primitiveValue.getFloatValue())
113             styleResolver.setZoom(number);
114     }
115 }
116
117 #if ENABLE(CSS_SHAPES)
118 inline void applyValueWebkitShapeOutside(StyleResolver& styleResolver, CSSValue& value)
119 {
120     if (is<CSSPrimitiveValue>(value)) {
121         // FIXME: Shouldn't this be CSSValueNone?
122         // http://www.w3.org/TR/css-shapes/#shape-outside-property
123         if (downcast<CSSPrimitiveValue>(value).getValueID() == CSSValueAuto)
124             styleResolver.style()->setShapeOutside(nullptr);
125     } if (is<CSSImageValue>(value) || is<CSSImageGeneratorValue>(value) || is<CSSImageSetValue>(value)) {
126         RefPtr<ShapeValue> shape = ShapeValue::createImageValue(styleResolver.styleImage(CSSPropertyWebkitShapeOutside, value));
127         styleResolver.style()->setShapeOutside(shape.release());
128     } else if (is<CSSValueList>(value)) {
129         RefPtr<BasicShape> shape;
130         CSSBoxType referenceBox = BoxMissing;
131         for (auto& currentValue : downcast<CSSValueList>(value)) {
132             CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(currentValue.get());
133             if (primitiveValue.isShape())
134                 shape = basicShapeForValue(styleResolver.state().cssToLengthConversionData(), primitiveValue.getShapeValue());
135             else if (primitiveValue.getValueID() == CSSValueContentBox
136                 || primitiveValue.getValueID() == CSSValueBorderBox
137                 || primitiveValue.getValueID() == CSSValuePaddingBox
138                 || primitiveValue.getValueID() == CSSValueMarginBox)
139                 referenceBox = CSSBoxType(primitiveValue);
140             else
141                 return;
142         }
143
144         if (shape)
145             styleResolver.style()->setShapeOutside(ShapeValue::createShapeValue(shape.release(), referenceBox));
146         else if (referenceBox != BoxMissing)
147             styleResolver.style()->setShapeOutside(ShapeValue::createBoxShapeValue(referenceBox));
148     }
149 }
150 #endif // ENABLE(CSS_SHAPES)
151
152 static inline Length mmLength(double mm)
153 {
154     Ref<CSSPrimitiveValue> value(CSSPrimitiveValue::create(mm, CSSPrimitiveValue::CSS_MM));
155     return value.get().computeLength<Length>(CSSToLengthConversionData());
156 }
157 static inline Length inchLength(double inch)
158 {
159     Ref<CSSPrimitiveValue> value(CSSPrimitiveValue::create(inch, CSSPrimitiveValue::CSS_IN));
160     return value.get().computeLength<Length>(CSSToLengthConversionData());
161 }
162 static bool getPageSizeFromName(CSSPrimitiveValue* pageSizeName, CSSPrimitiveValue* pageOrientation, Length& width, Length& height)
163 {
164     static NeverDestroyed<Length> a5Width(mmLength(148));
165     static NeverDestroyed<Length> a5Height(mmLength(210));
166     static NeverDestroyed<Length> a4Width(mmLength(210));
167     static NeverDestroyed<Length> a4Height(mmLength(297));
168     static NeverDestroyed<Length> a3Width(mmLength(297));
169     static NeverDestroyed<Length> a3Height(mmLength(420));
170     static NeverDestroyed<Length> b5Width(mmLength(176));
171     static NeverDestroyed<Length> b5Height(mmLength(250));
172     static NeverDestroyed<Length> b4Width(mmLength(250));
173     static NeverDestroyed<Length> b4Height(mmLength(353));
174     static NeverDestroyed<Length> letterWidth(inchLength(8.5));
175     static NeverDestroyed<Length> letterHeight(inchLength(11));
176     static NeverDestroyed<Length> legalWidth(inchLength(8.5));
177     static NeverDestroyed<Length> legalHeight(inchLength(14));
178     static NeverDestroyed<Length> ledgerWidth(inchLength(11));
179     static NeverDestroyed<Length> ledgerHeight(inchLength(17));
180
181     if (!pageSizeName)
182         return false;
183
184     switch (pageSizeName->getValueID()) {
185     case CSSValueA5:
186         width = a5Width;
187         height = a5Height;
188         break;
189     case CSSValueA4:
190         width = a4Width;
191         height = a4Height;
192         break;
193     case CSSValueA3:
194         width = a3Width;
195         height = a3Height;
196         break;
197     case CSSValueB5:
198         width = b5Width;
199         height = b5Height;
200         break;
201     case CSSValueB4:
202         width = b4Width;
203         height = b4Height;
204         break;
205     case CSSValueLetter:
206         width = letterWidth;
207         height = letterHeight;
208         break;
209     case CSSValueLegal:
210         width = legalWidth;
211         height = legalHeight;
212         break;
213     case CSSValueLedger:
214         width = ledgerWidth;
215         height = ledgerHeight;
216         break;
217     default:
218         return false;
219     }
220
221     if (pageOrientation) {
222         switch (pageOrientation->getValueID()) {
223         case CSSValueLandscape:
224             std::swap(width, height);
225             break;
226         case CSSValuePortrait:
227             // Nothing to do.
228             break;
229         default:
230             return false;
231         }
232     }
233     return true;
234 }
235
236 inline void applyValueVerticalAlign(StyleResolver& styleResolver, CSSValue& value)
237 {
238     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
239     if (primitiveValue.getValueID())
240         styleResolver.style()->setVerticalAlign(primitiveValue);
241     else
242         styleResolver.style()->setVerticalAlignLength(primitiveValue.convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(styleResolver.state().cssToLengthConversionData()));
243 }
244
245 #if ENABLE(CSS_IMAGE_RESOLUTION)
246 inline void applyInheritImageResolution(StyleResolver& styleResolver)
247 {
248     styleResolver.style()->setImageResolutionSource(styleResolver.parentStyle()->imageResolutionSource());
249     styleResolver.style()->setImageResolutionSnap(styleResolver.parentStyle()->imageResolutionSnap());
250     styleResolver.style()->setImageResolution(styleResolver.parentStyle()->imageResolution());
251 }
252
253 inline void applyInitialImageResolution(StyleResolver& styleResolver)
254 {
255     styleResolver.style()->setImageResolutionSource(RenderStyle::initialImageResolutionSource());
256     styleResolver.style()->setImageResolutionSnap(RenderStyle::initialImageResolutionSnap());
257     styleResolver.style()->setImageResolution(RenderStyle::initialImageResolution());
258 }
259
260 inline void applyValueImageResolution(StyleResolver& styleResolver, CSSValue& value)
261 {
262     ImageResolutionSource source = RenderStyle::initialImageResolutionSource();
263     ImageResolutionSnap snap = RenderStyle::initialImageResolutionSnap();
264     double resolution = RenderStyle::initialImageResolution();
265     for (auto& item : downcast<CSSValueList>(value)) {
266         CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(item.get());
267         if (primitiveValue.getValueID() == CSSValueFromImage)
268             source = ImageResolutionFromImage;
269         else if (primitiveValue.getValueID() == CSSValueSnap)
270             snap = ImageResolutionSnapPixels;
271         else
272             resolution = primitiveValue.getDoubleValue(CSSPrimitiveValue::CSS_DPPX);
273     }
274     styleResolver.style()->setImageResolutionSource(source);
275     styleResolver.style()->setImageResolutionSnap(snap);
276     styleResolver.style()->setImageResolution(resolution);
277 }
278 #endif // ENABLE(CSS_IMAGE_RESOLUTION)
279
280 inline void applyInheritSize(StyleResolver&) { }
281 inline void applyInitialSize(StyleResolver&) { }
282 inline void applyValueSize(StyleResolver& styleResolver, CSSValue& value)
283 {
284     styleResolver.style()->resetPageSizeType();
285     Length width;
286     Length height;
287     PageSizeType pageSizeType = PAGE_SIZE_AUTO;
288     if (!is<CSSValueList>(value))
289         return;
290
291     auto& valueList = downcast<CSSValueList>(value);
292     switch (valueList.length()) {
293     case 2: {
294         CSSValue* firstValue = valueList.itemWithoutBoundsCheck(0);
295         CSSValue* secondValue = valueList.itemWithoutBoundsCheck(1);
296         // <length>{2} | <page-size> <orientation>
297         if (!is<CSSPrimitiveValue>(*firstValue) || !is<CSSPrimitiveValue>(*secondValue))
298             return;
299         auto& firstPrimitiveValue = downcast<CSSPrimitiveValue>(*firstValue);
300         auto& secondPrimitiveValue = downcast<CSSPrimitiveValue>(*secondValue);
301         if (firstPrimitiveValue.isLength()) {
302             // <length>{2}
303             if (!secondPrimitiveValue.isLength())
304                 return;
305             CSSToLengthConversionData conversionData = styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f);
306             width = firstPrimitiveValue.computeLength<Length>(conversionData);
307             height = secondPrimitiveValue.computeLength<Length>(conversionData);
308         } else {
309             // <page-size> <orientation>
310             // The value order is guaranteed. See CSSParser::parseSizeParameter.
311             if (!getPageSizeFromName(&firstPrimitiveValue, &secondPrimitiveValue, width, height))
312                 return;
313         }
314         pageSizeType = PAGE_SIZE_RESOLVED;
315         break;
316     }
317     case 1: {
318         CSSValue* value = valueList.itemWithoutBoundsCheck(0);
319         // <length> | auto | <page-size> | [ portrait | landscape]
320         if (!is<CSSPrimitiveValue>(*value))
321             return;
322         auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
323         if (primitiveValue.isLength()) {
324             // <length>
325             pageSizeType = PAGE_SIZE_RESOLVED;
326             width = height = primitiveValue.computeLength<Length>(styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f));
327         } else {
328             switch (primitiveValue.getValueID()) {
329             case 0:
330                 return;
331             case CSSValueAuto:
332                 pageSizeType = PAGE_SIZE_AUTO;
333                 break;
334             case CSSValuePortrait:
335                 pageSizeType = PAGE_SIZE_AUTO_PORTRAIT;
336                 break;
337             case CSSValueLandscape:
338                 pageSizeType = PAGE_SIZE_AUTO_LANDSCAPE;
339                 break;
340             default:
341                 // <page-size>
342                 pageSizeType = PAGE_SIZE_RESOLVED;
343                 if (!getPageSizeFromName(&primitiveValue, nullptr, width, height))
344                     return;
345             }
346         }
347         break;
348     }
349     default:
350         return;
351     }
352     styleResolver.style()->setPageSizeType(pageSizeType);
353     styleResolver.style()->setPageSize(LengthSize(width, height));
354 }
355
356 inline void applyInheritTextIndent(StyleResolver& styleResolver)
357 {
358     styleResolver.style()->setTextIndent(styleResolver.parentStyle()->textIndent());
359 #if ENABLE(CSS3_TEXT)
360     styleResolver.style()->setTextIndentLine(styleResolver.parentStyle()->textIndentLine());
361     styleResolver.style()->setTextIndentType(styleResolver.parentStyle()->textIndentType());
362 #endif
363 }
364
365 inline void applyInitialTextIndent(StyleResolver& styleResolver)
366 {
367     styleResolver.style()->setTextIndent(RenderStyle::initialTextIndent());
368 #if ENABLE(CSS3_TEXT)
369     styleResolver.style()->setTextIndentLine(RenderStyle::initialTextIndentLine());
370     styleResolver.style()->setTextIndentType(RenderStyle::initialTextIndentType());
371 #endif
372 }
373
374 inline void applyValueTextIndent(StyleResolver& styleResolver, CSSValue& value)
375 {
376     Length lengthOrPercentageValue;
377 #if ENABLE(CSS3_TEXT)
378     TextIndentLine textIndentLineValue = RenderStyle::initialTextIndentLine();
379     TextIndentType textIndentTypeValue = RenderStyle::initialTextIndentType();
380 #endif
381     for (auto& item : downcast<CSSValueList>(value)) {
382         auto& primitiveValue = downcast<CSSPrimitiveValue>(item.get());
383         if (!primitiveValue.getValueID())
384             lengthOrPercentageValue = primitiveValue.convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(styleResolver.state().cssToLengthConversionData());
385 #if ENABLE(CSS3_TEXT)
386         else if (primitiveValue.getValueID() == CSSValueWebkitEachLine)
387             textIndentLineValue = TextIndentEachLine;
388         else if (primitiveValue.getValueID() == CSSValueWebkitHanging)
389             textIndentTypeValue = TextIndentHanging;
390 #endif
391     }
392
393     ASSERT(!lengthOrPercentageValue.isUndefined());
394     styleResolver.style()->setTextIndent(lengthOrPercentageValue);
395 #if ENABLE(CSS3_TEXT)
396     styleResolver.style()->setTextIndentLine(textIndentLineValue);
397     styleResolver.style()->setTextIndentType(textIndentTypeValue);
398 #endif
399 }
400
401 enum BorderImageType { BorderImage, WebkitMaskBoxImage };
402 enum BorderImageModifierType { Outset, Repeat, Slice, Width };
403 template <BorderImageType type, BorderImageModifierType modifier>
404 class ApplyPropertyBorderImageModifier {
405 public:
406     static void applyInheritValue(StyleResolver& styleResolver)
407     {
408         NinePieceImage image(getValue(styleResolver.style()));
409         switch (modifier) {
410         case Outset:
411             image.copyOutsetFrom(getValue(styleResolver.parentStyle()));
412             break;
413         case Repeat:
414             image.copyRepeatFrom(getValue(styleResolver.parentStyle()));
415             break;
416         case Slice:
417             image.copyImageSlicesFrom(getValue(styleResolver.parentStyle()));
418             break;
419         case Width:
420             image.copyBorderSlicesFrom(getValue(styleResolver.parentStyle()));
421             break;
422         }
423         setValue(styleResolver.style(), image);
424     }
425
426     static void applyInitialValue(StyleResolver& styleResolver)
427     {
428         NinePieceImage image(getValue(styleResolver.style()));
429         switch (modifier) {
430         case Outset:
431             image.setOutset(LengthBox(0));
432             break;
433         case Repeat:
434             image.setHorizontalRule(StretchImageRule);
435             image.setVerticalRule(StretchImageRule);
436             break;
437         case Slice:
438             // Masks have a different initial value for slices. Preserve the value of 0 for backwards compatibility.
439             image.setImageSlices(type == BorderImage ? LengthBox(Length(100, Percent), Length(100, Percent), Length(100, Percent), Length(100, Percent)) : LengthBox());
440             image.setFill(false);
441             break;
442         case Width:
443             // Masks have a different initial value for widths. They use an 'auto' value rather than trying to fit to the border.
444             image.setBorderSlices(type == BorderImage ? LengthBox(Length(1, Relative), Length(1, Relative), Length(1, Relative), Length(1, Relative)) : LengthBox());
445             break;
446         }
447         setValue(styleResolver.style(), image);
448     }
449
450     static void applyValue(StyleResolver& styleResolver, CSSValue& value)
451     {
452         NinePieceImage image(getValue(styleResolver.style()));
453         switch (modifier) {
454         case Outset:
455             image.setOutset(styleResolver.styleMap()->mapNinePieceImageQuad(value));
456             break;
457         case Repeat:
458             styleResolver.styleMap()->mapNinePieceImageRepeat(value, image);
459             break;
460         case Slice:
461             styleResolver.styleMap()->mapNinePieceImageSlice(value, image);
462             break;
463         case Width:
464             image.setBorderSlices(styleResolver.styleMap()->mapNinePieceImageQuad(value));
465             break;
466         }
467         setValue(styleResolver.style(), image);
468     }
469
470 private:
471     static inline const NinePieceImage& getValue(RenderStyle* style)
472     {
473         return type == BorderImage ? style->borderImage() : style->maskBoxImage();
474     }
475
476     static inline void setValue(RenderStyle* style, const NinePieceImage& value)
477     {
478         return type == BorderImage ? style->setBorderImage(value) : style->setMaskBoxImage(value);
479     }
480 };
481
482 #define DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(type, modifier) \
483 inline void applyInherit##type##modifier(StyleResolver& styleResolver) \
484 { \
485     ApplyPropertyBorderImageModifier<type, modifier>::applyInheritValue(styleResolver); \
486 } \
487 inline void applyInitial##type##modifier(StyleResolver& styleResolver) \
488 { \
489     ApplyPropertyBorderImageModifier<type, modifier>::applyInitialValue(styleResolver); \
490 } \
491 inline void applyValue##type##modifier(StyleResolver& styleResolver, CSSValue& value) \
492 { \
493     ApplyPropertyBorderImageModifier<type, modifier>::applyValue(styleResolver, value); \
494 }
495
496 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Outset)
497 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Repeat)
498 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Slice)
499 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Width)
500 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(WebkitMaskBoxImage, Outset)
501 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(WebkitMaskBoxImage, Repeat)
502 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(WebkitMaskBoxImage, Slice)
503 DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(WebkitMaskBoxImage, Width)
504
505 } // namespace StyleBuilderFunctions
506
507 } // namespace WebCore
508
509 #endif // StyleBuilderCustom_h