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