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