Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2000 Dirk Mueller (mueller (at) kde.org)
      5  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "core/rendering/RenderFieldset.h"
     26 
     27 #include "core/CSSPropertyNames.h"
     28 #include "core/HTMLNames.h"
     29 #include "core/html/HTMLLegendElement.h"
     30 #include "core/rendering/PaintInfo.h"
     31 #include "platform/graphics/GraphicsContextStateSaver.h"
     32 
     33 using std::min;
     34 using std::max;
     35 
     36 namespace WebCore {
     37 
     38 using namespace HTMLNames;
     39 
     40 RenderFieldset::RenderFieldset(Element* element)
     41     : RenderBlockFlow(element)
     42 {
     43 }
     44 
     45 void RenderFieldset::computePreferredLogicalWidths()
     46 {
     47     RenderBlockFlow::computePreferredLogicalWidths();
     48     if (RenderBox* legend = findLegend()) {
     49         int legendMinWidth = legend->minPreferredLogicalWidth();
     50 
     51         Length legendMarginLeft = legend->style()->marginLeft();
     52         Length legendMarginRight = legend->style()->marginLeft();
     53 
     54         if (legendMarginLeft.isFixed())
     55             legendMinWidth += legendMarginLeft.value();
     56 
     57         if (legendMarginRight.isFixed())
     58             legendMinWidth += legendMarginRight.value();
     59 
     60         m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, legendMinWidth + borderAndPaddingWidth());
     61     }
     62 }
     63 
     64 RenderObject* RenderFieldset::layoutSpecialExcludedChild(bool relayoutChildren, SubtreeLayoutScope&)
     65 {
     66     RenderBox* legend = findLegend();
     67     if (legend) {
     68         if (relayoutChildren)
     69             legend->setNeedsLayoutAndFullPaintInvalidation();
     70         legend->layoutIfNeeded();
     71 
     72         LayoutUnit logicalLeft;
     73         if (style()->isLeftToRightDirection()) {
     74             switch (legend->style()->textAlign()) {
     75             case CENTER:
     76                 logicalLeft = (logicalWidth() - logicalWidthForChild(legend)) / 2;
     77                 break;
     78             case RIGHT:
     79                 logicalLeft = logicalWidth() - borderEnd() - paddingEnd() - logicalWidthForChild(legend);
     80                 break;
     81             default:
     82                 logicalLeft = borderStart() + paddingStart() + marginStartForChild(legend);
     83                 break;
     84             }
     85         } else {
     86             switch (legend->style()->textAlign()) {
     87             case LEFT:
     88                 logicalLeft = borderStart() + paddingStart();
     89                 break;
     90             case CENTER: {
     91                 // Make sure that the extra pixel goes to the end side in RTL (since it went to the end side
     92                 // in LTR).
     93                 LayoutUnit centeredWidth = logicalWidth() - logicalWidthForChild(legend);
     94                 logicalLeft = centeredWidth - centeredWidth / 2;
     95                 break;
     96             }
     97             default:
     98                 logicalLeft = logicalWidth() - borderStart() - paddingStart() - marginStartForChild(legend) - logicalWidthForChild(legend);
     99                 break;
    100             }
    101         }
    102 
    103         setLogicalLeftForChild(legend, logicalLeft);
    104 
    105         LayoutUnit fieldsetBorderBefore = borderBefore();
    106         LayoutUnit legendLogicalHeight = logicalHeightForChild(legend);
    107 
    108         LayoutUnit legendLogicalTop;
    109         LayoutUnit collapsedLegendExtent;
    110         // FIXME: We need to account for the legend's margin before too.
    111         if (fieldsetBorderBefore > legendLogicalHeight) {
    112             // The <legend> is smaller than the associated fieldset before border
    113             // so the latter determines positioning of the <legend>. The sizing depends
    114             // on the legend's margins as we want to still follow the author's cues.
    115             // Firefox completely ignores the margins in this case which seems wrong.
    116             legendLogicalTop = (fieldsetBorderBefore - legendLogicalHeight) / 2;
    117             collapsedLegendExtent = max<LayoutUnit>(fieldsetBorderBefore, legendLogicalTop + legendLogicalHeight + marginAfterForChild(legend));
    118         } else
    119             collapsedLegendExtent = legendLogicalHeight + marginAfterForChild(legend);
    120 
    121         setLogicalTopForChild(legend, legendLogicalTop);
    122         setLogicalHeight(paddingBefore() + collapsedLegendExtent);
    123     }
    124     return legend;
    125 }
    126 
    127 RenderBox* RenderFieldset::findLegend(FindLegendOption option) const
    128 {
    129     for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
    130         if (option == IgnoreFloatingOrOutOfFlow && legend->isFloatingOrOutOfFlowPositioned())
    131             continue;
    132 
    133         if (isHTMLLegendElement(legend->node()))
    134             return toRenderBox(legend);
    135     }
    136     return 0;
    137 }
    138 
    139 void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    140 {
    141     if (!paintInfo.shouldPaintWithinRoot(this))
    142         return;
    143 
    144     LayoutRect paintRect(paintOffset, size());
    145     RenderBox* legend = findLegend();
    146     if (!legend)
    147         return RenderBlockFlow::paintBoxDecorations(paintInfo, paintOffset);
    148 
    149     // FIXME: We need to work with "rl" and "bt" block flow directions.  In those
    150     // cases the legend is embedded in the right and bottom borders respectively.
    151     // https://bugs.webkit.org/show_bug.cgi?id=47236
    152     if (style()->isHorizontalWritingMode()) {
    153         LayoutUnit yOff = (legend->y() > 0) ? LayoutUnit() : (legend->height() - borderTop()) / 2;
    154         paintRect.setHeight(paintRect.height() - yOff);
    155         paintRect.setY(paintRect.y() + yOff);
    156     } else {
    157         LayoutUnit xOff = (legend->x() > 0) ? LayoutUnit() : (legend->width() - borderLeft()) / 2;
    158         paintRect.setWidth(paintRect.width() - xOff);
    159         paintRect.setX(paintRect.x() + xOff);
    160     }
    161 
    162     if (!boxShadowShouldBeAppliedToBackground(determineBackgroundBleedAvoidance(paintInfo.context)))
    163         paintBoxShadow(paintInfo, paintRect, style(), Normal);
    164     paintFillLayers(paintInfo, resolveColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect);
    165     paintBoxShadow(paintInfo, paintRect, style(), Inset);
    166 
    167     if (!style()->hasBorder())
    168         return;
    169 
    170     // Create a clipping region around the legend and paint the border as normal
    171     GraphicsContext* graphicsContext = paintInfo.context;
    172     GraphicsContextStateSaver stateSaver(*graphicsContext);
    173 
    174     // FIXME: We need to work with "rl" and "bt" block flow directions.  In those
    175     // cases the legend is embedded in the right and bottom borders respectively.
    176     // https://bugs.webkit.org/show_bug.cgi?id=47236
    177     if (style()->isHorizontalWritingMode()) {
    178         LayoutUnit clipTop = paintRect.y();
    179         LayoutUnit clipHeight = max(static_cast<LayoutUnit>(style()->borderTopWidth()), legend->height() - ((legend->height() - borderTop()) / 2));
    180         graphicsContext->clipOut(pixelSnappedIntRect(paintRect.x() + legend->x(), clipTop, legend->width(), clipHeight));
    181     } else {
    182         LayoutUnit clipLeft = paintRect.x();
    183         LayoutUnit clipWidth = max(static_cast<LayoutUnit>(style()->borderLeftWidth()), legend->width());
    184         graphicsContext->clipOut(pixelSnappedIntRect(clipLeft, paintRect.y() + legend->y(), clipWidth, legend->height()));
    185     }
    186 
    187     paintBorder(paintInfo, paintRect, style());
    188 }
    189 
    190 void RenderFieldset::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    191 {
    192     if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
    193         return;
    194 
    195     LayoutRect paintRect = LayoutRect(paintOffset, size());
    196     RenderBox* legend = findLegend();
    197     if (!legend)
    198         return RenderBlockFlow::paintMask(paintInfo, paintOffset);
    199 
    200     // FIXME: We need to work with "rl" and "bt" block flow directions.  In those
    201     // cases the legend is embedded in the right and bottom borders respectively.
    202     // https://bugs.webkit.org/show_bug.cgi?id=47236
    203     if (style()->isHorizontalWritingMode()) {
    204         LayoutUnit yOff = (legend->y() > 0) ? LayoutUnit() : (legend->height() - borderTop()) / 2;
    205         paintRect.expand(0, -yOff);
    206         paintRect.move(0, yOff);
    207     } else {
    208         LayoutUnit xOff = (legend->x() > 0) ? LayoutUnit() : (legend->width() - borderLeft()) / 2;
    209         paintRect.expand(-xOff, 0);
    210         paintRect.move(xOff, 0);
    211     }
    212 
    213     paintMaskImages(paintInfo, paintRect);
    214 }
    215 
    216 } // namespace WebCore
    217