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