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 41 CSSBoxType referenceBox(const ShapeValue& shapeValue) 42 { 43 if (shapeValue.cssBox() == BoxMissing) 44 return MarginBox; 45 return shapeValue.cssBox(); 46 } 47 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 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 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 107 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 123 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 160 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 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 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 201 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 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 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 243 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 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 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 312 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 330 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 340 FloatSize ShapeOutsideInfo::shapeToRendererSize(FloatSize size) const 341 { 342 if (!m_renderer.style()->isHorizontalWritingMode()) 343 return size.transposedSize(); 344 return size; 345 } 346 347 } 348