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