• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "core/rendering/shapes/ShapeOutsideInfo.h"
32 
33 #include "core/rendering/FloatingObjects.h"
34 #include "core/rendering/RenderBlockFlow.h"
35 #include "core/rendering/RenderBox.h"
36 #include "core/rendering/RenderImage.h"
37 #include "platform/LengthFunctions.h"
38 
39 namespace WebCore {
40 
referenceBox(const ShapeValue & shapeValue)41 CSSBoxType referenceBox(const ShapeValue& shapeValue)
42 {
43     if (shapeValue.cssBox() == BoxMissing)
44         return MarginBox;
45     return shapeValue.cssBox();
46 }
47 
setReferenceBoxLogicalSize(LayoutSize newReferenceBoxLogicalSize)48 void ShapeOutsideInfo::setReferenceBoxLogicalSize(LayoutSize newReferenceBoxLogicalSize)
49 {
50     bool isHorizontalWritingMode = m_renderer.containingBlock()->style()->isHorizontalWritingMode();
51     switch (referenceBox(*m_renderer.style()->shapeOutside())) {
52     case MarginBox:
53         if (isHorizontalWritingMode)
54             newReferenceBoxLogicalSize.expand(m_renderer.marginWidth(), m_renderer.marginHeight());
55         else
56             newReferenceBoxLogicalSize.expand(m_renderer.marginHeight(), m_renderer.marginWidth());
57         break;
58     case BorderBox:
59         break;
60     case PaddingBox:
61         if (isHorizontalWritingMode)
62             newReferenceBoxLogicalSize.shrink(m_renderer.borderWidth(), m_renderer.borderHeight());
63         else
64             newReferenceBoxLogicalSize.shrink(m_renderer.borderHeight(), m_renderer.borderWidth());
65         break;
66     case ContentBox:
67         if (isHorizontalWritingMode)
68             newReferenceBoxLogicalSize.shrink(m_renderer.borderAndPaddingWidth(), m_renderer.borderAndPaddingHeight());
69         else
70             newReferenceBoxLogicalSize.shrink(m_renderer.borderAndPaddingHeight(), m_renderer.borderAndPaddingWidth());
71         break;
72     case BoxMissing:
73         ASSERT_NOT_REACHED();
74         break;
75     }
76 
77     if (m_referenceBoxLogicalSize == newReferenceBoxLogicalSize)
78         return;
79     markShapeAsDirty();
80     m_referenceBoxLogicalSize = newReferenceBoxLogicalSize;
81 }
82 
checkShapeImageOrigin(Document & document,const StyleImage & styleImage)83 static bool checkShapeImageOrigin(Document& document, const StyleImage& styleImage)
84 {
85     if (styleImage.isGeneratedImage())
86         return true;
87 
88     ASSERT(styleImage.cachedImage());
89     ImageResource& imageResource = *(styleImage.cachedImage());
90     if (imageResource.isAccessAllowed(document.securityOrigin()))
91         return true;
92 
93     const KURL& url = imageResource.url();
94     String urlString = url.isNull() ? "''" : url.elidedString();
95     document.addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Unsafe attempt to load URL " + urlString + ".");
96 
97     return false;
98 }
99 
getShapeImageMarginRect(const RenderBox & renderBox,const LayoutSize & referenceBoxLogicalSize)100 static LayoutRect getShapeImageMarginRect(const RenderBox& renderBox, const LayoutSize& referenceBoxLogicalSize)
101 {
102     LayoutPoint marginBoxOrigin(-renderBox.marginLogicalLeft() - renderBox.borderAndPaddingLogicalLeft(), -renderBox.marginBefore() - renderBox.borderBefore() - renderBox.paddingBefore());
103     LayoutSize marginBoxSizeDelta(renderBox.marginLogicalWidth() + renderBox.borderAndPaddingLogicalWidth(), renderBox.marginLogicalHeight() + renderBox.borderAndPaddingLogicalHeight());
104     return LayoutRect(marginBoxOrigin, referenceBoxLogicalSize + marginBoxSizeDelta);
105 }
106 
createShapeForImage(StyleImage * styleImage,float shapeImageThreshold,WritingMode writingMode,float margin) const107 PassOwnPtr<Shape> ShapeOutsideInfo::createShapeForImage(StyleImage* styleImage, float shapeImageThreshold, WritingMode writingMode, float margin) const
108 {
109     const IntSize& imageSize = m_renderer.calculateImageIntrinsicDimensions(styleImage, roundedIntSize(m_referenceBoxLogicalSize), RenderImage::ScaleByEffectiveZoom);
110     styleImage->setContainerSizeForRenderer(&m_renderer, imageSize, m_renderer.style()->effectiveZoom());
111 
112     const LayoutRect& marginRect = getShapeImageMarginRect(m_renderer, m_referenceBoxLogicalSize);
113     const LayoutRect& imageRect = (m_renderer.isRenderImage())
114         ? toRenderImage(&m_renderer)->replacedContentRect()
115         : LayoutRect(LayoutPoint(), imageSize);
116 
117     ASSERT(!styleImage->isPendingImage());
118     RefPtr<Image> image = styleImage->image(const_cast<RenderBox*>(&m_renderer), imageSize);
119 
120     return Shape::createRasterShape(image.get(), shapeImageThreshold, imageRect, marginRect, writingMode, margin);
121 }
122 
computedShape() const123 const Shape& ShapeOutsideInfo::computedShape() const
124 {
125     if (Shape* shape = m_shape.get())
126         return *shape;
127 
128     const RenderStyle& style = *m_renderer.style();
129     ASSERT(m_renderer.containingBlock());
130     const RenderStyle& containingBlockStyle = *m_renderer.containingBlock()->style();
131 
132     WritingMode writingMode = containingBlockStyle.writingMode();
133     LayoutUnit maximumValue = m_renderer.containingBlock() ? m_renderer.containingBlock()->contentWidth() : LayoutUnit();
134     float margin = floatValueForLength(m_renderer.style()->shapeMargin(), maximumValue.toFloat());
135 
136     float shapeImageThreshold = style.shapeImageThreshold();
137     ASSERT(style.shapeOutside());
138     const ShapeValue& shapeValue = *style.shapeOutside();
139 
140     switch (shapeValue.type()) {
141     case ShapeValue::Shape:
142         ASSERT(shapeValue.shape());
143         m_shape = Shape::createShape(shapeValue.shape(), m_referenceBoxLogicalSize, writingMode, margin);
144         break;
145     case ShapeValue::Image:
146         ASSERT(shapeValue.isImageValid());
147         m_shape = createShapeForImage(shapeValue.image(), shapeImageThreshold, writingMode, margin);
148         break;
149     case ShapeValue::Box: {
150         const RoundedRect& shapeRect = style.getRoundedBorderFor(LayoutRect(LayoutPoint(), m_referenceBoxLogicalSize), m_renderer.view());
151         m_shape = Shape::createLayoutBoxShape(shapeRect, writingMode, margin);
152         break;
153     }
154     }
155 
156     ASSERT(m_shape);
157     return *m_shape;
158 }
159 
computeSegmentsForLine(LayoutUnit lineTop,LayoutUnit lineHeight) const160 SegmentList ShapeOutsideInfo::computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) const
161 {
162     ASSERT(lineHeight >= 0);
163     SegmentList segments;
164 
165     computedShape().getExcludedIntervals((lineTop - logicalTopOffset()), std::min(lineHeight, shapeLogicalBottom() - lineTop), segments);
166 
167     for (size_t i = 0; i < segments.size(); i++) {
168         segments[i].logicalLeft += logicalLeftOffset();
169         segments[i].logicalRight += logicalLeftOffset();
170     }
171 
172     return segments;
173 }
174 
borderBeforeInWritingMode(const RenderBox & renderer,WritingMode writingMode)175 inline LayoutUnit borderBeforeInWritingMode(const RenderBox& renderer, WritingMode writingMode)
176 {
177     switch (writingMode) {
178     case TopToBottomWritingMode: return renderer.borderTop();
179     case BottomToTopWritingMode: return renderer.borderBottom();
180     case LeftToRightWritingMode: return renderer.borderLeft();
181     case RightToLeftWritingMode: return renderer.borderRight();
182     }
183 
184     ASSERT_NOT_REACHED();
185     return renderer.borderBefore();
186 }
187 
borderAndPaddingBeforeInWritingMode(const RenderBox & renderer,WritingMode writingMode)188 inline LayoutUnit borderAndPaddingBeforeInWritingMode(const RenderBox& renderer, WritingMode writingMode)
189 {
190     switch (writingMode) {
191     case TopToBottomWritingMode: return renderer.borderTop() + renderer.paddingTop();
192     case BottomToTopWritingMode: return renderer.borderBottom() + renderer.paddingBottom();
193     case LeftToRightWritingMode: return renderer.borderLeft() + renderer.paddingLeft();
194     case RightToLeftWritingMode: return renderer.borderRight() + renderer.paddingRight();
195     }
196 
197     ASSERT_NOT_REACHED();
198     return renderer.borderAndPaddingBefore();
199 }
200 
logicalTopOffset() const201 LayoutUnit ShapeOutsideInfo::logicalTopOffset() const
202 {
203     switch (referenceBox(*m_renderer.style()->shapeOutside())) {
204     case MarginBox: return -m_renderer.marginBefore(m_renderer.containingBlock()->style());
205     case BorderBox: return LayoutUnit();
206     case PaddingBox: return borderBeforeInWritingMode(m_renderer, m_renderer.containingBlock()->style()->writingMode());
207     case ContentBox: return borderAndPaddingBeforeInWritingMode(m_renderer, m_renderer.containingBlock()->style()->writingMode());
208     case BoxMissing: break;
209     }
210 
211     ASSERT_NOT_REACHED();
212     return LayoutUnit();
213 }
214 
borderStartWithStyleForWritingMode(const RenderBox & renderer,const RenderStyle * style)215 inline LayoutUnit borderStartWithStyleForWritingMode(const RenderBox& renderer, const RenderStyle* style)
216 {
217     if (style->isHorizontalWritingMode()) {
218         if (style->isLeftToRightDirection())
219             return renderer.borderLeft();
220 
221         return renderer.borderRight();
222     }
223     if (style->isLeftToRightDirection())
224         return renderer.borderTop();
225 
226     return renderer.borderBottom();
227 }
228 
borderAndPaddingStartWithStyleForWritingMode(const RenderBox & renderer,const RenderStyle * style)229 inline LayoutUnit borderAndPaddingStartWithStyleForWritingMode(const RenderBox& renderer, const RenderStyle* style)
230 {
231     if (style->isHorizontalWritingMode()) {
232         if (style->isLeftToRightDirection())
233             return renderer.borderLeft() + renderer.paddingLeft();
234 
235         return renderer.borderRight() + renderer.paddingRight();
236     }
237     if (style->isLeftToRightDirection())
238         return renderer.borderTop() + renderer.paddingTop();
239 
240     return renderer.borderBottom() + renderer.paddingBottom();
241 }
242 
logicalLeftOffset() const243 LayoutUnit ShapeOutsideInfo::logicalLeftOffset() const
244 {
245     switch (referenceBox(*m_renderer.style()->shapeOutside())) {
246     case MarginBox: return -m_renderer.marginStart(m_renderer.containingBlock()->style());
247     case BorderBox: return LayoutUnit();
248     case PaddingBox: return borderStartWithStyleForWritingMode(m_renderer, m_renderer.containingBlock()->style());
249     case ContentBox: return borderAndPaddingStartWithStyleForWritingMode(m_renderer, m_renderer.containingBlock()->style());
250     case BoxMissing: break;
251     }
252 
253     ASSERT_NOT_REACHED();
254     return LayoutUnit();
255 }
256 
257 
isEnabledFor(const RenderBox & box)258 bool ShapeOutsideInfo::isEnabledFor(const RenderBox& box)
259 {
260     ShapeValue* shapeValue = box.style()->shapeOutside();
261     if (!box.isFloating() || !shapeValue)
262         return false;
263 
264     switch (shapeValue->type()) {
265     case ShapeValue::Shape:
266         return shapeValue->shape();
267     case ShapeValue::Image:
268         return shapeValue->isImageValid() && checkShapeImageOrigin(box.document(), *(shapeValue->image()));
269     case ShapeValue::Box:
270         return true;
271     }
272 
273     return false;
274 }
275 
updateDeltasForContainingBlockLine(const RenderBlockFlow & containingBlock,const FloatingObject & floatingObject,LayoutUnit lineTop,LayoutUnit lineHeight)276 void ShapeOutsideInfo::updateDeltasForContainingBlockLine(const RenderBlockFlow& containingBlock, const FloatingObject& floatingObject, LayoutUnit lineTop, LayoutUnit lineHeight)
277 {
278     LayoutUnit borderBoxTop = containingBlock.logicalTopForFloat(&floatingObject) + containingBlock.marginBeforeForChild(&m_renderer);
279     LayoutUnit borderBoxLineTop = lineTop - borderBoxTop;
280 
281     if (isShapeDirty() || m_borderBoxLineTop != borderBoxLineTop || m_lineHeight != lineHeight) {
282         m_borderBoxLineTop = borderBoxLineTop;
283         m_referenceBoxLineTop = borderBoxLineTop - logicalTopOffset();
284         m_lineHeight = lineHeight;
285 
286         LayoutUnit floatMarginBoxWidth = containingBlock.logicalWidthForFloat(&floatingObject);
287 
288         if (lineOverlapsShapeBounds()) {
289             SegmentList segments = computeSegmentsForLine(borderBoxLineTop, lineHeight);
290             if (segments.size()) {
291                 LayoutUnit logicalLeftMargin = containingBlock.style()->isLeftToRightDirection() ? containingBlock.marginStartForChild(&m_renderer) : containingBlock.marginEndForChild(&m_renderer);
292                 LayoutUnit rawLeftMarginBoxDelta = segments.first().logicalLeft + logicalLeftMargin;
293                 m_leftMarginBoxDelta = clampToLayoutUnit(rawLeftMarginBoxDelta, LayoutUnit(), floatMarginBoxWidth);
294 
295                 LayoutUnit logicalRightMargin = containingBlock.style()->isLeftToRightDirection() ? containingBlock.marginEndForChild(&m_renderer) : containingBlock.marginStartForChild(&m_renderer);
296                 LayoutUnit rawRightMarginBoxDelta = segments.last().logicalRight - containingBlock.logicalWidthForChild(&m_renderer) - logicalRightMargin;
297                 m_rightMarginBoxDelta = clampToLayoutUnit(rawRightMarginBoxDelta, -floatMarginBoxWidth, LayoutUnit());
298                 m_lineOverlapsShape = true;
299                 return;
300             }
301         }
302 
303         // Lines that do not overlap the shape should act as if the float
304         // wasn't there for layout purposes. So we set the deltas to remove the
305         // entire width of the float.
306         m_leftMarginBoxDelta = floatMarginBoxWidth;
307         m_rightMarginBoxDelta = -floatMarginBoxWidth;
308         m_lineOverlapsShape = false;
309     }
310 }
311 
computedShapePhysicalBoundingBox() const312 LayoutRect ShapeOutsideInfo::computedShapePhysicalBoundingBox() const
313 {
314     LayoutRect physicalBoundingBox = computedShape().shapeMarginLogicalBoundingBox();
315     physicalBoundingBox.setX(physicalBoundingBox.x() + logicalLeftOffset());
316 
317     if (m_renderer.style()->isFlippedBlocksWritingMode())
318         physicalBoundingBox.setY(m_renderer.logicalHeight() - physicalBoundingBox.maxY());
319     else
320         physicalBoundingBox.setY(physicalBoundingBox.y() + logicalTopOffset());
321 
322     if (!m_renderer.style()->isHorizontalWritingMode())
323         physicalBoundingBox = physicalBoundingBox.transposedRect();
324     else
325         physicalBoundingBox.setY(physicalBoundingBox.y() + logicalTopOffset());
326 
327     return physicalBoundingBox;
328 }
329 
shapeToRendererPoint(FloatPoint point) const330 FloatPoint ShapeOutsideInfo::shapeToRendererPoint(FloatPoint point) const
331 {
332     FloatPoint result = FloatPoint(point.x() + logicalLeftOffset(), point.y() + logicalTopOffset());
333     if (m_renderer.style()->isFlippedBlocksWritingMode())
334         result.setY(m_renderer.logicalHeight() - result.y());
335     if (!m_renderer.style()->isHorizontalWritingMode())
336         result = result.transposedPoint();
337     return result;
338 }
339 
shapeToRendererSize(FloatSize size) const340 FloatSize ShapeOutsideInfo::shapeToRendererSize(FloatSize size) const
341 {
342     if (!m_renderer.style()->isHorizontalWritingMode())
343         return size.transposedSize();
344     return size;
345 }
346 
347 }
348