Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2000 Dirk Mueller (mueller (at) kde.org)
      4  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  *
     21  */
     22 
     23 #include "config.h"
     24 #include "RenderReplaced.h"
     25 
     26 #include "GraphicsContext.h"
     27 #include "RenderBlock.h"
     28 #include "RenderLayer.h"
     29 #include "RenderTheme.h"
     30 #include "RenderView.h"
     31 #include "VisiblePosition.h"
     32 
     33 using namespace std;
     34 
     35 namespace WebCore {
     36 
     37 const int cDefaultWidth = 300;
     38 const int cDefaultHeight = 150;
     39 
     40 RenderReplaced::RenderReplaced(Node* node)
     41     : RenderBox(node)
     42     , m_intrinsicSize(cDefaultWidth, cDefaultHeight)
     43     , m_hasIntrinsicSize(false)
     44 {
     45     setReplaced(true);
     46 }
     47 
     48 RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize)
     49     : RenderBox(node)
     50     , m_intrinsicSize(intrinsicSize)
     51     , m_hasIntrinsicSize(true)
     52 {
     53     setReplaced(true);
     54 }
     55 
     56 RenderReplaced::~RenderReplaced()
     57 {
     58 }
     59 
     60 void RenderReplaced::destroy()
     61 {
     62     if (!documentBeingDestroyed() && parent())
     63         parent()->dirtyLinesFromChangedChild(this);
     64 
     65     RenderBox::destroy();
     66 }
     67 
     68 void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     69 {
     70     RenderBox::styleDidChange(diff, oldStyle);
     71 
     72     bool hadStyle = (oldStyle != 0);
     73     float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
     74     if (style() && style()->effectiveZoom() != oldZoom)
     75         intrinsicSizeChanged();
     76 }
     77 
     78 void RenderReplaced::layout()
     79 {
     80     ASSERT(needsLayout());
     81 
     82     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
     83 
     84     setHeight(minimumReplacedHeight());
     85 
     86     computeLogicalWidth();
     87     computeLogicalHeight();
     88 
     89     m_overflow.clear();
     90     addShadowOverflow();
     91     updateLayerTransform();
     92 
     93     repainter.repaintAfterLayout();
     94     setNeedsLayout(false);
     95 }
     96 
     97 void RenderReplaced::intrinsicSizeChanged()
     98 {
     99     int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom());
    100     int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom());
    101     m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
    102     setNeedsLayoutAndPrefWidthsRecalc();
    103 }
    104 
    105 void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
    106 {
    107     if (!shouldPaint(paintInfo, tx, ty))
    108         return;
    109 
    110     tx += x();
    111     ty += y();
    112 
    113     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
    114         paintBoxDecorations(paintInfo, tx, ty);
    115 
    116     if (paintInfo.phase == PaintPhaseMask) {
    117         paintMask(paintInfo, tx, ty);
    118         return;
    119     }
    120 
    121     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
    122         paintOutline(paintInfo.context, tx, ty, width(), height());
    123 
    124     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
    125         return;
    126 
    127     if (!paintInfo.shouldPaintWithinRoot(this))
    128         return;
    129 
    130     bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();
    131     if (paintInfo.phase == PaintPhaseSelection) {
    132         if (selectionState() == SelectionNone)
    133             return;
    134         drawSelectionTint = false;
    135     }
    136 
    137     bool completelyClippedOut = false;
    138     if (style()->hasBorderRadius()) {
    139         IntRect borderRect = IntRect(tx, ty, width(), height());
    140 
    141         if (borderRect.isEmpty())
    142             completelyClippedOut = true;
    143         else {
    144             // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
    145             paintInfo.context->save();
    146             paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(borderRect));
    147         }
    148     }
    149 
    150     if (!completelyClippedOut) {
    151         paintReplaced(paintInfo, tx, ty);
    152 
    153         if (style()->hasBorderRadius())
    154             paintInfo.context->restore();
    155     }
    156 
    157     // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
    158     // surrounding content.
    159     if (drawSelectionTint) {
    160         IntRect selectionPaintingRect = localSelectionRect();
    161         selectionPaintingRect.move(tx, ty);
    162         paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace());
    163     }
    164 }
    165 
    166 bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
    167 {
    168     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
    169             && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
    170         return false;
    171 
    172     if (!paintInfo.shouldPaintWithinRoot(this))
    173         return false;
    174 
    175     // if we're invisible or haven't received a layout yet, then just bail.
    176     if (style()->visibility() != VISIBLE)
    177         return false;
    178 
    179     int currentTX = tx + x();
    180     int currentTY = ty + y();
    181 
    182     // Early exit if the element touches the edges.
    183     int top = currentTY + minYVisualOverflow();
    184     int bottom = currentTY + maxYVisualOverflow();
    185     if (isSelected() && m_inlineBoxWrapper) {
    186         int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
    187         int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
    188         top = min(selTop, top);
    189         bottom = max(selBottom, bottom);
    190     }
    191 
    192     int os = 2 * maximalOutlineSize(paintInfo.phase);
    193     if (currentTX + minXVisualOverflow() >= paintInfo.rect.maxX() + os || currentTX + maxXVisualOverflow() <= paintInfo.rect.x() - os)
    194         return false;
    195     if (top >= paintInfo.rect.maxY() + os || bottom <= paintInfo.rect.y() - os)
    196         return false;
    197 
    198     return true;
    199 }
    200 
    201 static inline bool lengthIsSpecified(Length length)
    202 {
    203     LengthType lengthType = length.type();
    204     return lengthType == Fixed || lengthType == Percent;
    205 }
    206 
    207 int RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) const
    208 {
    209     int logicalWidth;
    210     if (lengthIsSpecified(style()->width()))
    211         logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth());
    212     else if (m_hasIntrinsicSize)
    213         logicalWidth = calcAspectRatioLogicalWidth();
    214     else
    215         logicalWidth = intrinsicLogicalWidth();
    216 
    217     int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
    218     int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
    219 
    220     return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
    221 }
    222 
    223 int RenderReplaced::computeReplacedLogicalHeight() const
    224 {
    225     int logicalHeight;
    226     if (lengthIsSpecified(style()->logicalHeight()))
    227         logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight());
    228     else if (m_hasIntrinsicSize)
    229         logicalHeight = calcAspectRatioLogicalHeight();
    230     else
    231         logicalHeight = intrinsicLogicalHeight();
    232 
    233     int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
    234     int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
    235 
    236     return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
    237 }
    238 
    239 int RenderReplaced::calcAspectRatioLogicalWidth() const
    240 {
    241     int intrinsicWidth = intrinsicLogicalWidth();
    242     int intrinsicHeight = intrinsicLogicalHeight();
    243     if (!intrinsicHeight)
    244         return 0;
    245     return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight;
    246 }
    247 
    248 int RenderReplaced::calcAspectRatioLogicalHeight() const
    249 {
    250     int intrinsicWidth = intrinsicLogicalWidth();
    251     int intrinsicHeight = intrinsicLogicalHeight();
    252     if (!intrinsicWidth)
    253         return 0;
    254     return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth;
    255 }
    256 
    257 void RenderReplaced::computePreferredLogicalWidths()
    258 {
    259     ASSERT(preferredLogicalWidthsDirty());
    260 
    261     int borderAndPadding = borderAndPaddingWidth();
    262     m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(false) + borderAndPadding;
    263 
    264     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
    265         m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0));
    266 
    267     if (style()->width().isPercent() || style()->height().isPercent()
    268         || style()->maxWidth().isPercent() || style()->maxHeight().isPercent()
    269         || style()->minWidth().isPercent() || style()->minHeight().isPercent())
    270         m_minPreferredLogicalWidth = 0;
    271     else
    272         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
    273 
    274     setPreferredLogicalWidthsDirty(false);
    275 }
    276 
    277 unsigned RenderReplaced::caretMaxRenderedOffset() const
    278 {
    279     return 1;
    280 }
    281 
    282 VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point)
    283 {
    284     InlineBox* box = inlineBoxWrapper();
    285     if (!box)
    286         return createVisiblePosition(0, DOWNSTREAM);
    287 
    288     // FIXME: This code is buggy if the replaced element is relative positioned.
    289 
    290     RootInlineBox* root = box->root();
    291 
    292     int top = root->selectionTop();
    293     int bottom = root->selectionBottom();
    294 
    295     int blockDirectionPosition = box->isHorizontal() ? point.y() + y() : point.x() + x();
    296     int lineDirectionPosition = box->isHorizontal() ? point.x() + x() : point.y() + y();
    297 
    298     if (blockDirectionPosition < top)
    299         return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above
    300 
    301     if (blockDirectionPosition >= bottom)
    302         return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below
    303 
    304     if (node()) {
    305         if (lineDirectionPosition <= box->logicalLeft() + (box->logicalWidth() / 2))
    306             return createVisiblePosition(0, DOWNSTREAM);
    307         return createVisiblePosition(1, DOWNSTREAM);
    308     }
    309 
    310     return RenderBox::positionForPoint(point);
    311 }
    312 
    313 IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
    314 {
    315     ASSERT(!needsLayout());
    316 
    317     if (!isSelected())
    318         return IntRect();
    319 
    320     IntRect rect = localSelectionRect();
    321     if (clipToVisibleContent)
    322         computeRectForRepaint(repaintContainer, rect);
    323     else
    324         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
    325 
    326     return rect;
    327 }
    328 
    329 IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
    330 {
    331     if (checkWhetherSelected && !isSelected())
    332         return IntRect();
    333 
    334     if (!m_inlineBoxWrapper)
    335         // We're a block-level replaced element.  Just return our own dimensions.
    336         return IntRect(0, 0, width(), height());
    337 
    338     RootInlineBox* root = m_inlineBoxWrapper->root();
    339     int newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
    340     if (root->block()->style()->isHorizontalWritingMode())
    341         return IntRect(0, newLogicalTop, width(), root->selectionHeight());
    342     return IntRect(newLogicalTop, 0, root->selectionHeight(), height());
    343 }
    344 
    345 void RenderReplaced::setSelectionState(SelectionState s)
    346 {
    347     RenderBox::setSelectionState(s); // The selection state for our containing block hierarchy is updated by the base class call.
    348     if (m_inlineBoxWrapper) {
    349         RootInlineBox* line = m_inlineBoxWrapper->root();
    350         if (line)
    351             line->setHasSelectedChildren(isSelected());
    352     }
    353 }
    354 
    355 bool RenderReplaced::isSelected() const
    356 {
    357     SelectionState s = selectionState();
    358     if (s == SelectionNone)
    359         return false;
    360     if (s == SelectionInside)
    361         return true;
    362 
    363     int selectionStart, selectionEnd;
    364     selectionStartEnd(selectionStart, selectionEnd);
    365     if (s == SelectionStart)
    366         return selectionStart == 0;
    367 
    368     int end = node()->hasChildNodes() ? node()->childNodeCount() : 1;
    369     if (s == SelectionEnd)
    370         return selectionEnd == end;
    371     if (s == SelectionBoth)
    372         return selectionStart == 0 && selectionEnd == end;
    373 
    374     ASSERT(0);
    375     return false;
    376 }
    377 
    378 IntSize RenderReplaced::intrinsicSize() const
    379 {
    380     return m_intrinsicSize;
    381 }
    382 
    383 void RenderReplaced::setIntrinsicSize(const IntSize& size)
    384 {
    385     ASSERT(m_hasIntrinsicSize);
    386     m_intrinsicSize = size;
    387 }
    388 
    389 IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
    390 {
    391     if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
    392         return IntRect();
    393 
    394     // The selectionRect can project outside of the overflowRect, so take their union
    395     // for repainting to avoid selection painting glitches.
    396     IntRect r = unionRect(localSelectionRect(false), visualOverflowRect());
    397 
    398     RenderView* v = view();
    399     if (v) {
    400         // FIXME: layoutDelta needs to be applied in parts before/after transforms and
    401         // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
    402         r.move(v->layoutDelta());
    403     }
    404 
    405     if (style()) {
    406         if (style()->hasAppearance())
    407             // The theme may wish to inflate the rect used when repainting.
    408             theme()->adjustRepaintRect(this, r);
    409         if (v)
    410             r.inflate(style()->outlineSize());
    411     }
    412     computeRectForRepaint(repaintContainer, r);
    413     return r;
    414 }
    415 
    416 }
    417