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 {
     44     setReplaced(true);
     45 }
     46 
     47 RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize)
     48     : RenderBox(node)
     49     , m_intrinsicSize(intrinsicSize)
     50 {
     51     setReplaced(true);
     52 }
     53 
     54 RenderReplaced::~RenderReplaced()
     55 {
     56 }
     57 
     58 void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     59 {
     60     RenderBox::styleDidChange(diff, oldStyle);
     61 
     62     bool hadStyle = (oldStyle != 0);
     63     float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
     64     if (style() && style()->effectiveZoom() != oldZoom)
     65         intrinsicSizeChanged();
     66 }
     67 
     68 void RenderReplaced::layout()
     69 {
     70     ASSERT(needsLayout());
     71 
     72     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
     73 
     74     setHeight(minimumReplacedHeight());
     75 
     76     calcWidth();
     77     calcHeight();
     78 
     79     m_overflow.clear();
     80     addShadowOverflow();
     81 
     82     repainter.repaintAfterLayout();
     83 
     84     setNeedsLayout(false);
     85 }
     86 
     87 void RenderReplaced::intrinsicSizeChanged()
     88 {
     89     int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom());
     90     int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom());
     91     m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
     92     setNeedsLayoutAndPrefWidthsRecalc();
     93 }
     94 
     95 void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
     96 {
     97     if (!shouldPaint(paintInfo, tx, ty))
     98         return;
     99 
    100     tx += x();
    101     ty += y();
    102 
    103     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
    104         paintBoxDecorations(paintInfo, tx, ty);
    105 
    106     if (paintInfo.phase == PaintPhaseMask) {
    107         paintMask(paintInfo, tx, ty);
    108         return;
    109     }
    110 
    111     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
    112         paintOutline(paintInfo.context, tx, ty, width(), height(), style());
    113 
    114     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
    115         return;
    116 
    117     if (!shouldPaintWithinRoot(paintInfo))
    118         return;
    119 
    120     bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();
    121     if (paintInfo.phase == PaintPhaseSelection) {
    122         if (selectionState() == SelectionNone)
    123             return;
    124         drawSelectionTint = false;
    125     }
    126 
    127     bool completelyClippedOut = false;
    128     if (style()->hasBorderRadius()) {
    129         IntRect borderRect = IntRect(tx, ty, width(), height());
    130 
    131         if (borderRect.isEmpty())
    132             completelyClippedOut = true;
    133         else {
    134             // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
    135             paintInfo.context->save();
    136 
    137             IntSize topLeft, topRight, bottomLeft, bottomRight;
    138             style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
    139 
    140             paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
    141         }
    142     }
    143 
    144     if (!completelyClippedOut) {
    145         paintReplaced(paintInfo, tx, ty);
    146 
    147         if (style()->hasBorderRadius())
    148             paintInfo.context->restore();
    149     }
    150 
    151     // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
    152     // surrounding content.
    153     if (drawSelectionTint) {
    154         IntRect selectionPaintingRect = localSelectionRect();
    155         selectionPaintingRect.move(tx, ty);
    156         paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace());
    157     }
    158 }
    159 
    160 bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
    161 {
    162     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
    163             && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
    164         return false;
    165 
    166     if (!shouldPaintWithinRoot(paintInfo))
    167         return false;
    168 
    169     // if we're invisible or haven't received a layout yet, then just bail.
    170     if (style()->visibility() != VISIBLE)
    171         return false;
    172 
    173     int currentTX = tx + x();
    174     int currentTY = ty + y();
    175 
    176     // Early exit if the element touches the edges.
    177     int top = currentTY + topVisibleOverflow();
    178     int bottom = currentTY + bottomVisibleOverflow();
    179     if (isSelected() && m_inlineBoxWrapper) {
    180         int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
    181         int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
    182         top = min(selTop, top);
    183         bottom = max(selBottom, bottom);
    184     }
    185 
    186     int os = 2 * maximalOutlineSize(paintInfo.phase);
    187     if (currentTX + leftVisibleOverflow() >= paintInfo.rect.right() + os || currentTX + rightVisibleOverflow() <= paintInfo.rect.x() - os)
    188         return false;
    189     if (top >= paintInfo.rect.bottom() + os || bottom <= paintInfo.rect.y() - os)
    190         return false;
    191 
    192     return true;
    193 }
    194 
    195 void RenderReplaced::calcPrefWidths()
    196 {
    197     ASSERT(prefWidthsDirty());
    198 
    199     int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight();
    200     int width = calcReplacedWidth(false) + paddingAndBorders;
    201 
    202     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
    203         width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0));
    204 
    205     if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) {
    206         m_minPrefWidth = 0;
    207         m_maxPrefWidth = width;
    208     } else
    209         m_minPrefWidth = m_maxPrefWidth = width;
    210 
    211     setPrefWidthsDirty(false);
    212 }
    213 
    214 int RenderReplaced::lineHeight(bool, bool) const
    215 {
    216     return height() + marginTop() + marginBottom();
    217 }
    218 
    219 int RenderReplaced::baselinePosition(bool, bool) const
    220 {
    221     return height() + marginTop() + marginBottom();
    222 }
    223 
    224 unsigned RenderReplaced::caretMaxRenderedOffset() const
    225 {
    226     return 1;
    227 }
    228 
    229 VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point)
    230 {
    231     InlineBox* box = inlineBoxWrapper();
    232     if (!box)
    233         return createVisiblePosition(0, DOWNSTREAM);
    234 
    235     // FIXME: This code is buggy if the replaced element is relative positioned.
    236 
    237     RootInlineBox* root = box->root();
    238 
    239     int top = root->lineTop();
    240     int bottom = root->nextRootBox() ? root->nextRootBox()->lineTop() : root->lineBottom();
    241 
    242     if (point.y() + y() < top)
    243         return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above
    244 
    245     if (point.y() + y() >= bottom)
    246         return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below
    247 
    248     if (node()) {
    249         if (point.x() <= width() / 2)
    250             return createVisiblePosition(0, DOWNSTREAM);
    251         return createVisiblePosition(1, DOWNSTREAM);
    252     }
    253 
    254     return RenderBox::positionForPoint(point);
    255 }
    256 
    257 IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
    258 {
    259     ASSERT(!needsLayout());
    260 
    261     if (!isSelected())
    262         return IntRect();
    263 
    264     IntRect rect = localSelectionRect();
    265     if (clipToVisibleContent)
    266         computeRectForRepaint(repaintContainer, rect);
    267     else
    268         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
    269 
    270     return rect;
    271 }
    272 
    273 IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
    274 {
    275     if (checkWhetherSelected && !isSelected())
    276         return IntRect();
    277 
    278     if (!m_inlineBoxWrapper)
    279         // We're a block-level replaced element.  Just return our own dimensions.
    280         return IntRect(0, 0, width(), height());
    281 
    282     RenderBlock* cb =  containingBlock();
    283     if (!cb)
    284         return IntRect();
    285 
    286     RootInlineBox* root = m_inlineBoxWrapper->root();
    287     return IntRect(0, root->selectionTop() - y(), width(), root->selectionHeight());
    288 }
    289 
    290 void RenderReplaced::setSelectionState(SelectionState s)
    291 {
    292     RenderBox::setSelectionState(s);
    293     if (m_inlineBoxWrapper) {
    294         RootInlineBox* line = m_inlineBoxWrapper->root();
    295         if (line)
    296             line->setHasSelectedChildren(isSelected());
    297     }
    298 
    299     containingBlock()->setSelectionState(s);
    300 }
    301 
    302 bool RenderReplaced::isSelected() const
    303 {
    304     SelectionState s = selectionState();
    305     if (s == SelectionNone)
    306         return false;
    307     if (s == SelectionInside)
    308         return true;
    309 
    310     int selectionStart, selectionEnd;
    311     selectionStartEnd(selectionStart, selectionEnd);
    312     if (s == SelectionStart)
    313         return selectionStart == 0;
    314 
    315     int end = node()->hasChildNodes() ? node()->childNodeCount() : 1;
    316     if (s == SelectionEnd)
    317         return selectionEnd == end;
    318     if (s == SelectionBoth)
    319         return selectionStart == 0 && selectionEnd == end;
    320 
    321     ASSERT(0);
    322     return false;
    323 }
    324 
    325 IntSize RenderReplaced::intrinsicSize() const
    326 {
    327     return m_intrinsicSize;
    328 }
    329 
    330 void RenderReplaced::setIntrinsicSize(const IntSize& size)
    331 {
    332     m_intrinsicSize = size;
    333 }
    334 
    335 IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
    336 {
    337     if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
    338         return IntRect();
    339 
    340     // The selectionRect can project outside of the overflowRect, so take their union
    341     // for repainting to avoid selection painting glitches.
    342     IntRect r = unionRect(localSelectionRect(false), visibleOverflowRect());
    343 
    344     RenderView* v = view();
    345     if (v) {
    346         // FIXME: layoutDelta needs to be applied in parts before/after transforms and
    347         // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
    348         r.move(v->layoutDelta());
    349     }
    350 
    351     if (style()) {
    352         if (style()->hasAppearance())
    353             // The theme may wish to inflate the rect used when repainting.
    354             theme()->adjustRepaintRect(this, r);
    355         if (v)
    356             r.inflate(style()->outlineSize());
    357     }
    358     computeRectForRepaint(repaintContainer, r);
    359     return r;
    360 }
    361 
    362 }
    363