Remove CSS_SHAPES feature definition. This should always be on.
[WebKit-https.git] / Source / WebCore / rendering / shapes / ShapeOutsideInfo.cpp
1 /*
2  * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include "config.h"
31
32 #include "ShapeOutsideInfo.h"
33
34 #include "BoxShape.h"
35 #include "FloatingObjects.h"
36 #include "LengthFunctions.h"
37 #include "RenderBlockFlow.h"
38 #include "RenderBox.h"
39 #include "RenderImage.h"
40 #include "RenderRegion.h"
41
42 namespace WebCore {
43
44 LayoutRect ShapeOutsideInfo::computedShapePhysicalBoundingBox() const
45 {
46     LayoutRect physicalBoundingBox = computedShape().shapeMarginLogicalBoundingBox();
47     physicalBoundingBox.setX(physicalBoundingBox.x() + logicalLeftOffset());
48     physicalBoundingBox.setY(physicalBoundingBox.y() + logicalTopOffset());
49     if (m_renderer.style().isFlippedBlocksWritingMode())
50         physicalBoundingBox.setY(m_renderer.logicalHeight() - physicalBoundingBox.maxY());
51     if (!m_renderer.style().isHorizontalWritingMode())
52         physicalBoundingBox = physicalBoundingBox.transposedRect();
53     return physicalBoundingBox;
54 }
55
56 FloatPoint ShapeOutsideInfo::shapeToRendererPoint(const FloatPoint& point) const
57 {
58     FloatPoint result = FloatPoint(point.x() + logicalLeftOffset(), point.y() + logicalTopOffset());
59     if (m_renderer.style().isFlippedBlocksWritingMode())
60         result.setY(m_renderer.logicalHeight() - result.y());
61     if (!m_renderer.style().isHorizontalWritingMode())
62         result = result.transposedPoint();
63     return result;
64 }
65
66 FloatSize ShapeOutsideInfo::shapeToRendererSize(const FloatSize& size) const
67 {
68     if (!m_renderer.style().isHorizontalWritingMode())
69         return size.transposedSize();
70     return size;
71 }
72
73 static inline CSSBoxType referenceBox(const ShapeValue& shapeValue)
74 {
75     if (shapeValue.cssBox() == BoxMissing) {
76         if (shapeValue.type() == ShapeValue::Type::Image)
77             return ContentBox;
78         return MarginBox;
79     }
80     return shapeValue.cssBox();
81 }
82
83 void ShapeOutsideInfo::setReferenceBoxLogicalSize(LayoutSize newReferenceBoxLogicalSize)
84 {
85     bool isHorizontalWritingMode = m_renderer.containingBlock()->style().isHorizontalWritingMode();
86     switch (referenceBox(*m_renderer.style().shapeOutside())) {
87     case MarginBox:
88         if (isHorizontalWritingMode)
89             newReferenceBoxLogicalSize.expand(m_renderer.horizontalMarginExtent(), m_renderer.verticalMarginExtent());
90         else
91             newReferenceBoxLogicalSize.expand(m_renderer.verticalMarginExtent(), m_renderer.horizontalMarginExtent());
92         break;
93     case BorderBox:
94         break;
95     case PaddingBox:
96         if (isHorizontalWritingMode)
97             newReferenceBoxLogicalSize.shrink(m_renderer.horizontalBorderExtent(), m_renderer.verticalBorderExtent());
98         else
99             newReferenceBoxLogicalSize.shrink(m_renderer.verticalBorderExtent(), m_renderer.horizontalBorderExtent());
100         break;
101     case ContentBox:
102         if (isHorizontalWritingMode)
103             newReferenceBoxLogicalSize.shrink(m_renderer.horizontalBorderAndPaddingExtent(), m_renderer.verticalBorderAndPaddingExtent());
104         else
105             newReferenceBoxLogicalSize.shrink(m_renderer.verticalBorderAndPaddingExtent(), m_renderer.horizontalBorderAndPaddingExtent());
106         break;
107     case Fill:
108     case Stroke:
109     case ViewBox:
110     case BoxMissing:
111         ASSERT_NOT_REACHED();
112         break;
113     }
114
115     if (m_referenceBoxLogicalSize == newReferenceBoxLogicalSize)
116         return;
117     markShapeAsDirty();
118     m_referenceBoxLogicalSize = newReferenceBoxLogicalSize;
119 }
120
121 static inline bool checkShapeImageOrigin(Document& document, const StyleImage& styleImage)
122 {
123     if (styleImage.isGeneratedImage())
124         return true;
125
126     ASSERT(styleImage.cachedImage());
127     CachedImage& cachedImage = *(styleImage.cachedImage());
128     if (cachedImage.isOriginClean(document.securityOrigin()))
129         return true;
130
131     const URL& url = cachedImage.url();
132     String urlString = url.isNull() ? "''" : url.stringCenterEllipsizedToLength();
133     document.addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Unsafe attempt to load URL " + urlString + ".");
134
135     return false;
136 }
137
138 static LayoutRect getShapeImageMarginRect(const RenderBox& renderBox, const LayoutSize& referenceBoxLogicalSize)
139 {
140     LayoutPoint marginBoxOrigin(-renderBox.marginLogicalLeft() - renderBox.borderAndPaddingLogicalLeft(), -renderBox.marginBefore() - renderBox.borderBefore() - renderBox.paddingBefore());
141     LayoutSize marginBoxSizeDelta(renderBox.marginLogicalWidth() + renderBox.borderAndPaddingLogicalWidth(), renderBox.marginLogicalHeight() + renderBox.borderAndPaddingLogicalHeight());
142     LayoutSize marginRectSize(referenceBoxLogicalSize + marginBoxSizeDelta);
143     marginRectSize.clampNegativeToZero();
144     return LayoutRect(marginBoxOrigin, marginRectSize);
145 }
146
147 std::unique_ptr<Shape> ShapeOutsideInfo::createShapeForImage(StyleImage* styleImage, float shapeImageThreshold, WritingMode writingMode, float margin) const
148 {
149     LayoutSize imageSize = m_renderer.calculateImageIntrinsicDimensions(styleImage, m_referenceBoxLogicalSize, RenderImage::ScaleByEffectiveZoom);
150     styleImage->setContainerSizeForRenderer(&m_renderer, imageSize, m_renderer.style().effectiveZoom());
151
152     const LayoutRect& marginRect = getShapeImageMarginRect(m_renderer, m_referenceBoxLogicalSize);
153     const LayoutRect& imageRect = is<RenderImage>(m_renderer)
154         ? downcast<RenderImage>(m_renderer).replacedContentRect(m_renderer.intrinsicSize())
155         : LayoutRect(LayoutPoint(), imageSize);
156
157     ASSERT(!styleImage->isPending());
158     RefPtr<Image> image = styleImage->image(const_cast<RenderBox*>(&m_renderer), imageSize);
159     return Shape::createRasterShape(image.get(), shapeImageThreshold, imageRect, marginRect, writingMode, margin);
160 }
161
162 const Shape& ShapeOutsideInfo::computedShape() const
163 {
164     if (Shape* shape = m_shape.get())
165         return *shape;
166
167     const RenderStyle& style = m_renderer.style();
168     ASSERT(m_renderer.containingBlock());
169     const RenderStyle& containingBlockStyle = m_renderer.containingBlock()->style();
170
171     WritingMode writingMode = containingBlockStyle.writingMode();
172     float margin = floatValueForLength(m_renderer.style().shapeMargin(), m_renderer.containingBlock() ? m_renderer.containingBlock()->contentWidth() : LayoutUnit());
173     float shapeImageThreshold = style.shapeImageThreshold();
174     const ShapeValue& shapeValue = *style.shapeOutside();
175
176     switch (shapeValue.type()) {
177     case ShapeValue::Type::Shape:
178         ASSERT(shapeValue.shape());
179         m_shape = Shape::createShape(*shapeValue.shape(), m_referenceBoxLogicalSize, writingMode, margin);
180         break;
181     case ShapeValue::Type::Image:
182         ASSERT(shapeValue.isImageValid());
183         m_shape = createShapeForImage(shapeValue.image(), shapeImageThreshold, writingMode, margin);
184         break;
185     case ShapeValue::Type::Box: {
186         RoundedRect shapeRect = computeRoundedRectForBoxShape(referenceBox(shapeValue), m_renderer);
187         if (!containingBlockStyle.isHorizontalWritingMode())
188             shapeRect = shapeRect.transposedRect();
189         m_shape = Shape::createBoxShape(shapeRect, writingMode, margin);
190         break;
191     }
192     }
193
194     ASSERT(m_shape);
195     return *m_shape;
196 }
197
198 static inline LayoutUnit borderBeforeInWritingMode(const RenderBox& renderer, WritingMode writingMode)
199 {
200     switch (writingMode) {
201     case TopToBottomWritingMode: return renderer.borderTop();
202     case BottomToTopWritingMode: return renderer.borderBottom();
203     case LeftToRightWritingMode: return renderer.borderLeft();
204     case RightToLeftWritingMode: return renderer.borderRight();
205     }
206
207     ASSERT_NOT_REACHED();
208     return renderer.borderBefore();
209 }
210
211 static inline LayoutUnit borderAndPaddingBeforeInWritingMode(const RenderBox& renderer, WritingMode writingMode)
212 {
213     switch (writingMode) {
214     case TopToBottomWritingMode: return renderer.borderTop() + renderer.paddingTop();
215     case BottomToTopWritingMode: return renderer.borderBottom() + renderer.paddingBottom();
216     case LeftToRightWritingMode: return renderer.borderLeft() + renderer.paddingLeft();
217     case RightToLeftWritingMode: return renderer.borderRight() + renderer.paddingRight();
218     }
219
220     ASSERT_NOT_REACHED();
221     return renderer.borderAndPaddingBefore();
222 }
223
224 LayoutUnit ShapeOutsideInfo::logicalTopOffset() const
225 {
226     switch (referenceBox(*m_renderer.style().shapeOutside())) {
227     case MarginBox: return -m_renderer.marginBefore(&m_renderer.containingBlock()->style());
228     case BorderBox: return LayoutUnit();
229     case PaddingBox: return borderBeforeInWritingMode(m_renderer, m_renderer.containingBlock()->style().writingMode());
230     case ContentBox: return borderAndPaddingBeforeInWritingMode(m_renderer, m_renderer.containingBlock()->style().writingMode());
231     case Fill: break;
232     case Stroke: break;
233     case ViewBox: break;
234     case BoxMissing: break;
235     }
236     
237     ASSERT_NOT_REACHED();
238     return LayoutUnit();
239 }
240
241 static inline LayoutUnit borderStartWithStyleForWritingMode(const RenderBox& renderer, const RenderStyle& style)
242 {
243     if (style.isHorizontalWritingMode()) {
244         if (style.isLeftToRightDirection())
245             return renderer.borderLeft();
246         
247         return renderer.borderRight();
248     }
249     if (style.isLeftToRightDirection())
250         return renderer.borderTop();
251     
252     return renderer.borderBottom();
253 }
254
255 static inline LayoutUnit borderAndPaddingStartWithStyleForWritingMode(const RenderBox& renderer, const RenderStyle& style)
256 {
257     if (style.isHorizontalWritingMode()) {
258         if (style.isLeftToRightDirection())
259             return renderer.borderLeft() + renderer.paddingLeft();
260         
261         return renderer.borderRight() + renderer.paddingRight();
262     }
263     if (style.isLeftToRightDirection())
264         return renderer.borderTop() + renderer.paddingTop();
265     
266     return renderer.borderBottom() + renderer.paddingBottom();
267 }
268
269 LayoutUnit ShapeOutsideInfo::logicalLeftOffset() const
270 {
271     if (m_renderer.isRenderRegion())
272         return LayoutUnit();
273     
274     switch (referenceBox(*m_renderer.style().shapeOutside())) {
275     case MarginBox: return -m_renderer.marginStart(&m_renderer.containingBlock()->style());
276     case BorderBox: return LayoutUnit();
277     case PaddingBox: return borderStartWithStyleForWritingMode(m_renderer, m_renderer.containingBlock()->style());
278     case ContentBox: return borderAndPaddingStartWithStyleForWritingMode(m_renderer, m_renderer.containingBlock()->style());
279     case Fill: break;
280     case Stroke: break;
281     case ViewBox: break;
282     case BoxMissing: break;
283     }
284
285     ASSERT_NOT_REACHED();
286     return LayoutUnit();
287 }
288
289 bool ShapeOutsideInfo::isEnabledFor(const RenderBox& box)
290 {
291     ShapeValue* shapeValue = box.style().shapeOutside();
292     if (!box.isFloating() || !shapeValue)
293         return false;
294
295     switch (shapeValue->type()) {
296     case ShapeValue::Type::Shape: return shapeValue->shape();
297     case ShapeValue::Type::Image: return shapeValue->isImageValid() && checkShapeImageOrigin(box.document(), *(shapeValue->image()));
298     case ShapeValue::Type::Box: return true;
299     }
300
301     ASSERT_NOT_REACHED();
302     return false;
303 }
304
305 ShapeOutsideDeltas ShapeOutsideInfo::computeDeltasForContainingBlockLine(const RenderBlockFlow& containingBlock, const FloatingObject& floatingObject, LayoutUnit lineTop, LayoutUnit lineHeight)
306 {
307     ASSERT(lineHeight >= 0);
308     LayoutUnit borderBoxTop = containingBlock.logicalTopForFloat(floatingObject) + containingBlock.marginBeforeForChild(m_renderer);
309     LayoutUnit borderBoxLineTop = lineTop - borderBoxTop;
310
311     if (isShapeDirty() || !m_shapeOutsideDeltas.isForLine(borderBoxLineTop, lineHeight)) {
312         LayoutUnit referenceBoxLineTop = borderBoxLineTop - logicalTopOffset();
313         LayoutUnit floatMarginBoxWidth = std::max<LayoutUnit>(LayoutUnit(), containingBlock.logicalWidthForFloat(floatingObject));
314
315         if (computedShape().lineOverlapsShapeMarginBounds(referenceBoxLineTop, lineHeight)) {
316             LineSegment segment = computedShape().getExcludedInterval((borderBoxLineTop - logicalTopOffset()), std::min(lineHeight, shapeLogicalBottom() - borderBoxLineTop));
317             if (segment.isValid) {
318                 LayoutUnit logicalLeftMargin = containingBlock.style().isLeftToRightDirection() ? containingBlock.marginStartForChild(m_renderer) : containingBlock.marginEndForChild(m_renderer);
319                 LayoutUnit rawLeftMarginBoxDelta = segment.logicalLeft + logicalLeftOffset() + logicalLeftMargin;
320                 LayoutUnit leftMarginBoxDelta = clampTo<LayoutUnit>(rawLeftMarginBoxDelta, LayoutUnit(), floatMarginBoxWidth);
321
322                 LayoutUnit logicalRightMargin = containingBlock.style().isLeftToRightDirection() ? containingBlock.marginEndForChild(m_renderer) : containingBlock.marginStartForChild(m_renderer);
323                 LayoutUnit rawRightMarginBoxDelta = segment.logicalRight + logicalLeftOffset() - containingBlock.logicalWidthForChild(m_renderer) - logicalRightMargin;
324                 LayoutUnit rightMarginBoxDelta = clampTo<LayoutUnit>(rawRightMarginBoxDelta, -floatMarginBoxWidth, LayoutUnit());
325
326                 m_shapeOutsideDeltas = ShapeOutsideDeltas(leftMarginBoxDelta, rightMarginBoxDelta, true, borderBoxLineTop, lineHeight);
327                 return m_shapeOutsideDeltas;
328             }
329         }
330
331         // Lines that do not overlap the shape should act as if the float
332         // wasn't there for layout purposes. So we set the deltas to remove the
333         // entire width of the float
334         m_shapeOutsideDeltas = ShapeOutsideDeltas(floatMarginBoxWidth, -floatMarginBoxWidth, false, borderBoxLineTop, lineHeight);
335     }
336
337     return m_shapeOutsideDeltas;
338 }
339
340 }