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::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     61 {
     62     RenderBox::styleDidChange(diff, oldStyle);
     63 
     64     bool hadStyle = (oldStyle != 0);
     65     float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
     66     if (style() && style()->effectiveZoom() != oldZoom)
     67         intrinsicSizeChanged();
     68 }
     69 
     70 void RenderReplaced::layout()
     71 {
     72     ASSERT(needsLayout());
     73 
     74     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
     75 
     76     setHeight(minimumReplacedHeight());
     77 
     78     computeLogicalWidth();
     79     computeLogicalHeight();
     80 
     81     m_overflow.clear();
     82     addShadowOverflow();
     83     updateLayerTransform();
     84 
     85     repainter.repaintAfterLayout();
     86     setNeedsLayout(false);
     87 }
     88 
     89 void RenderReplaced::intrinsicSizeChanged()
     90 {
     91     int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom());
     92     int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom());
     93     m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
     94     setNeedsLayoutAndPrefWidthsRecalc();
     95 }
     96 
     97 void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
     98 {
     99     if (!shouldPaint(paintInfo, tx, ty))
    100         return;
    101 
    102     tx += x();
    103     ty += y();
    104 
    105     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
    106         paintBoxDecorations(paintInfo, tx, ty);
    107 
    108     if (paintInfo.phase == PaintPhaseMask) {
    109         paintMask(paintInfo, tx, ty);
    110         return;
    111     }
    112 
    113     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
    114         paintOutline(paintInfo.context, tx, ty, width(), height());
    115 
    116     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
    117         return;
    118 
    119     if (!paintInfo.shouldPaintWithinRoot(this))
    120         return;
    121 
    122     bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();
    123     if (paintInfo.phase == PaintPhaseSelection) {
    124         if (selectionState() == SelectionNone)
    125             return;
    126         drawSelectionTint = false;
    127     }
    128 
    129     bool completelyClippedOut = false;
    130     if (style()->hasBorderRadius()) {
    131         IntRect borderRect = IntRect(tx, ty, width(), height());
    132 
    133         if (borderRect.isEmpty())
    134             completelyClippedOut = true;
    135         else {
    136             // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
    137             paintInfo.context->save();
    138             paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(borderRect));
    139         }
    140     }
    141 
    142     if (!completelyClippedOut) {
    143         paintReplaced(paintInfo, tx, ty);
    144 
    145         if (style()->hasBorderRadius())
    146             paintInfo.context->restore();
    147     }
    148 
    149     // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
    150     // surrounding content.
    151     if (drawSelectionTint) {
    152         IntRect selectionPaintingRect = localSelectionRect();
    153         selectionPaintingRect.move(tx, ty);
    154         paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace());
    155     }
    156 }
    157 
    158 bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
    159 {
    160     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
    161             && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
    162         return false;
    163 
    164     if (!paintInfo.shouldPaintWithinRoot(this))
    165         return false;
    166 
    167     // if we're invisible or haven't received a layout yet, then just bail.
    168     if (style()->visibility() != VISIBLE)
    169         return false;
    170 
    171     int currentTX = tx + x();
    172     int currentTY = ty + y();
    173 
    174     // Early exit if the element touches the edges.
    175     int top = currentTY + minYVisualOverflow();
    176     int bottom = currentTY + maxYVisualOverflow();
    177     if (isSelected() && m_inlineBoxWrapper) {
    178         int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
    179         int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
    180         top = min(selTop, top);
    181         bottom = max(selBottom, bottom);
    182     }
    183 
    184     int os = 2 * maximalOutlineSize(paintInfo.phase);
    185     if (currentTX + minXVisualOverflow() >= paintInfo.rect.maxX() + os || currentTX + maxXVisualOverflow() <= paintInfo.rect.x() - os)
    186         return false;
    187     if (top >= paintInfo.rect.maxY() + os || bottom <= paintInfo.rect.y() - os)
    188         return false;
    189 
    190     return true;
    191 }
    192 
    193 static inline bool lengthIsSpecified(Length length)
    194 {
    195     LengthType lengthType = length.type();
    196     return lengthType == Fixed || lengthType == Percent;
    197 }
    198 
    199 int RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) const
    200 {
    201     int logicalWidth;
    202     if (lengthIsSpecified(style()->width()))
    203         logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth());
    204     else if (m_hasIntrinsicSize)
    205         logicalWidth = calcAspectRatioLogicalWidth();
    206     else
    207         logicalWidth = intrinsicLogicalWidth();
    208 
    209     int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
    210     int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
    211 
    212     return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
    213 }
    214 
    215 int RenderReplaced::computeReplacedLogicalHeight() const
    216 {
    217     int logicalHeight;
    218     if (lengthIsSpecified(style()->logicalHeight()))
    219         logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight());
    220     else if (m_hasIntrinsicSize)
    221         logicalHeight = calcAspectRatioLogicalHeight();
    222     else
    223         logicalHeight = intrinsicLogicalHeight();
    224 
    225     int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
    226     int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
    227 
    228     return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
    229 }
    230 
    231 int RenderReplaced::calcAspectRatioLogicalWidth() const
    232 {
    233     int intrinsicWidth = intrinsicLogicalWidth();
    234     int intrinsicHeight = intrinsicLogicalHeight();
    235     if (!intrinsicHeight)
    236         return 0;
    237     return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight;
    238 }
    239 
    240 int RenderReplaced::calcAspectRatioLogicalHeight() const
    241 {
    242     int intrinsicWidth = intrinsicLogicalWidth();
    243     int intrinsicHeight = intrinsicLogicalHeight();
    244     if (!intrinsicWidth)
    245         return 0;
    246     return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth;
    247 }
    248 
    249 void RenderReplaced::computePreferredLogicalWidths()
    250 {
    251     ASSERT(preferredLogicalWidthsDirty());
    252 
    253     int borderAndPadding = borderAndPaddingWidth();
    254     m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(false) + borderAndPadding;
    255 
    256     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
    257         m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0));
    258 
    259     if (style()->width().isPercent() || style()->height().isPercent()
    260         || style()->maxWidth().isPercent() || style()->maxHeight().isPercent()
    261         || style()->minWidth().isPercent() || style()->minHeight().isPercent())
    262         m_minPreferredLogicalWidth = 0;
    263     else
    264         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
    265 
    266     setPreferredLogicalWidthsDirty(false);
    267 }
    268 
    269 unsigned RenderReplaced::caretMaxRenderedOffset() const
    270 {
    271     return 1;
    272 }
    273 
    274 VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point)
    275 {
    276     InlineBox* box = inlineBoxWrapper();
    277     if (!box)
    278         return createVisiblePosition(0, DOWNSTREAM);
    279 
    280     // FIXME: This code is buggy if the replaced element is relative positioned.
    281 
    282     RootInlineBox* root = box->root();
    283 
    284     int top = root->selectionTop();
    285     int bottom = root->selectionBottom();
    286 
    287     int blockDirectionPosition = box->isHorizontal() ? point.y() + y() : point.x() + x();
    288     int lineDirectionPosition = box->isHorizontal() ? point.x() + x() : point.y() + y();
    289 
    290     if (blockDirectionPosition < top)
    291         return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above
    292 
    293     if (blockDirectionPosition >= bottom)
    294         return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below
    295 
    296     if (node()) {
    297         if (lineDirectionPosition <= box->logicalLeft() + (box->logicalWidth() / 2))
    298             return createVisiblePosition(0, DOWNSTREAM);
    299         return createVisiblePosition(1, DOWNSTREAM);
    300     }
    301 
    302     return RenderBox::positionForPoint(point);
    303 }
    304 
    305 IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
    306 {
    307     ASSERT(!needsLayout());
    308 
    309     if (!isSelected())
    310         return IntRect();
    311 
    312     IntRect rect = localSelectionRect();
    313     if (clipToVisibleContent)
    314         computeRectForRepaint(repaintContainer, rect);
    315     else
    316         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
    317 
    318     return rect;
    319 }
    320 
    321 IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
    322 {
    323     if (checkWhetherSelected && !isSelected())
    324         return IntRect();
    325 
    326     if (!m_inlineBoxWrapper)
    327         // We're a block-level replaced element.  Just return our own dimensions.
    328         return IntRect(0, 0, width(), height());
    329 
    330     RootInlineBox* root = m_inlineBoxWrapper->root();
    331     int newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
    332     if (root->block()->style()->isHorizontalWritingMode())
    333         return IntRect(0, newLogicalTop, width(), root->selectionHeight());
    334     return IntRect(newLogicalTop, 0, root->selectionHeight(), height());
    335 }
    336 
    337 void RenderReplaced::setSelectionState(SelectionState s)
    338 {
    339     RenderBox::setSelectionState(s); // The selection state for our containing block hierarchy is updated by the base class call.
    340     if (m_inlineBoxWrapper) {
    341         RootInlineBox* line = m_inlineBoxWrapper->root();
    342         if (line)
    343             line->setHasSelectedChildren(isSelected());
    344     }
    345 }
    346 
    347 bool RenderReplaced::isSelected() const
    348 {
    349     SelectionState s = selectionState();
    350     if (s == SelectionNone)
    351         return false;
    352     if (s == SelectionInside)
    353         return true;
    354 
    355     int selectionStart, selectionEnd;
    356     selectionStartEnd(selectionStart, selectionEnd);
    357     if (s == SelectionStart)
    358         return selectionStart == 0;
    359 
    360     int end = node()->hasChildNodes() ? node()->childNodeCount() : 1;
    361     if (s == SelectionEnd)
    362         return selectionEnd == end;
    363     if (s == SelectionBoth)
    364         return selectionStart == 0 && selectionEnd == end;
    365 
    366     ASSERT(0);
    367     return false;
    368 }
    369 
    370 IntSize RenderReplaced::intrinsicSize() const
    371 {
    372     return m_intrinsicSize;
    373 }
    374 
    375 void RenderReplaced::setIntrinsicSize(const IntSize& size)
    376 {
    377     ASSERT(m_hasIntrinsicSize);
    378     m_intrinsicSize = size;
    379 }
    380 
    381 IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
    382 {
    383     if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
    384         return IntRect();
    385 
    386     // The selectionRect can project outside of the overflowRect, so take their union
    387     // for repainting to avoid selection painting glitches.
    388     IntRect r = unionRect(localSelectionRect(false), visualOverflowRect());
    389 
    390     RenderView* v = view();
    391     if (v) {
    392         // FIXME: layoutDelta needs to be applied in parts before/after transforms and
    393         // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
    394         r.move(v->layoutDelta());
    395     }
    396 
    397     if (style()) {
    398         if (style()->hasAppearance())
    399             // The theme may wish to inflate the rect used when repainting.
    400             theme()->adjustRepaintRect(this, r);
    401         if (v)
    402             r.inflate(style()->outlineSize());
    403     }
    404     computeRectForRepaint(repaintContainer, r);
    405     return r;
    406 }
    407 
    408 }
    409