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