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/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