blob: fc464a2c14b165f338049af3c9128f3fe4ccc5ec [file] [log] [blame]
/**
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
* (C) 2005 Samuel Weinig (sam.weinig@gmail.com)
* Copyright (C) 2005 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "config.h"
#include "render_box.h"
#include "CachedImage.h"
#include "DocumentImpl.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HTMLElementImpl.h"
#include "RenderTableCell.h"
#include "htmlnames.h"
#include "render_arena.h"
#include "render_canvas.h"
#include "render_flexbox.h"
#include "render_line.h"
#include "render_replaced.h"
#include "render_theme.h"
#include <assert.h>
namespace WebCore {
using namespace HTMLNames;
#define TABLECELLMARGIN -0x4000
RenderBox::RenderBox(DOM::NodeImpl* node)
: RenderObject(node)
{
m_minWidth = -1;
m_maxWidth = -1;
m_overrideSize = -1;
m_width = m_height = 0;
m_x = 0;
m_y = 0;
m_marginTop = 0;
m_marginBottom = 0;
m_marginLeft = 0;
m_marginRight = 0;
m_staticX = 0;
m_staticY = 0;
m_layer = 0;
m_inlineBoxWrapper = 0;
}
void RenderBox::setStyle(RenderStyle *_style)
{
RenderObject::setStyle(_style);
// The root always paints its background/border.
if (isRoot())
setShouldPaintBackgroundOrBorder(true);
setInline(_style->isDisplayInlineType());
switch(_style->position())
{
case AbsolutePosition:
case FixedPosition:
setPositioned(true);
break;
default:
setPositioned(false);
if (_style->isFloating())
setFloating(true);
if (_style->position() == RelativePosition)
setRelPositioned(true);
}
// We also handle <body> and <html>, whose overflow applies to the viewport.
if (_style->overflow() != OVISIBLE && !isRoot() && (!isBody() || !document()->isHTMLDocument()) &&
(isRenderBlock() || isTableRow() || isTableSection()))
setHasOverflowClip();
if (requiresLayer()) {
if (!m_layer) {
m_layer = new (renderArena()) RenderLayer(this);
m_layer->insertOnlyThisLayer();
if (parent() && containingBlock())
m_layer->updateLayerPositions();
}
}
else if (m_layer && !isRoot() && !isCanvas()) {
assert(m_layer->parent());
RenderLayer *layer = m_layer;
m_layer = 0;
layer->removeOnlyThisLayer();
}
if (m_layer)
m_layer->styleChanged();
// Set the text color if we're the body.
if (isBody())
element()->getDocument()->setTextColor(_style->color());
if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintActionOutline))
static_cast<RenderCanvas*>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize());
}
RenderBox::~RenderBox()
{
}
void RenderBox::destroy()
{
// A lot of the code in this funtion is just pasted into
// RenderWidget::destroy. If anything in this function changes,
// be sure to fix RenderWidget::destroy() as well.
RenderLayer* layer = m_layer;
RenderArena* arena = renderArena();
// This must be done before we destroy the RenderObject.
if (layer)
layer->clearClipRect();
RenderObject::destroy();
if (layer)
layer->destroy(arena);
}
int RenderBox::contentWidth() const
{
int w = m_width - borderLeft() - borderRight();
w -= paddingLeft() + paddingRight();
if (includeScrollbarSize())
w -= m_layer->verticalScrollbarWidth();
return w;
}
int RenderBox::contentHeight() const
{
int h = m_height - borderTop() - borderBottom();
h -= paddingTop() + paddingBottom();
if (includeScrollbarSize())
h -= m_layer->horizontalScrollbarHeight();
return h;
}
int RenderBox::overrideWidth() const
{
return m_overrideSize == -1 ? m_width : m_overrideSize;
}
int RenderBox::overrideHeight() const
{
return m_overrideSize == -1 ? m_height : m_overrideSize;
}
void RenderBox::setPos( int xPos, int yPos )
{
if (xPos == m_x && yPos == m_y)
return; // Optimize for the case where we don't move at all.
m_x = xPos; m_y = yPos;
}
int RenderBox::width() const
{
return m_width;
}
int RenderBox::height() const
{
return m_height;
}
int RenderBox::calcBorderBoxWidth(int w) const
{
int toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight();
if (style()->boxSizing() == CONTENT_BOX)
return w + toAdd;
return kMax(w, toAdd);
}
int RenderBox::calcBorderBoxHeight(int h) const
{
int toAdd = borderTop() + borderBottom() + paddingTop() + paddingBottom();
if (style()->boxSizing() == CONTENT_BOX)
return h + toAdd;
return kMax(h, toAdd);
}
int RenderBox::calcContentBoxWidth(int w) const
{
if (style()->boxSizing() == BORDER_BOX)
w -= (borderLeft() + borderRight() + paddingLeft() + paddingRight());
return kMax(0, w);
}
int RenderBox::calcContentBoxHeight(int h) const
{
if (style()->boxSizing() == BORDER_BOX)
h -= (borderTop() + borderBottom() + paddingTop() + paddingBottom());
return kMax(0, h);
}
// Hit Testing
bool RenderBox::nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction action)
{
tx += m_x;
ty += m_y;
// Check kids first.
for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
// FIXME: We have to skip over inline flows, since they can show up inside table rows
// at the moment (a demoted inline <form> for example). If we ever implement a
// table-specific hit-test method (which we should do for performance reasons anyway),
// then we can remove this check.
if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action)) {
setInnerNode(info);
return true;
}
}
// Check our bounds next. For this purpose always assume that we can only be hit in the
// foreground phase (which is true for replaced elements like images).
if (action == HitTestForeground && IntRect(tx, ty, m_width, m_height).contains(x, y)) {
setInnerNode(info);
return true;
}
return false;
}
// --------------------- painting stuff -------------------------------
void RenderBox::paint(PaintInfo& i, int _tx, int _ty)
{
_tx += m_x;
_ty += m_y;
// default implementation. Just pass paint through to the children
for (RenderObject* child = firstChild(); child; child = child->nextSibling())
child->paint(i, _tx, _ty);
}
void RenderBox::paintRootBoxDecorations(PaintInfo& i, int _tx, int _ty)
{
const BackgroundLayer* bgLayer = style()->backgroundLayers();
Color bgColor = style()->backgroundColor();
if (document()->isHTMLDocument() && !style()->hasBackground()) {
// Locate the <body> element using the DOM. This is easier than trying
// to crawl around a render tree with potential :before/:after content and
// anonymous blocks created by inline <body> tags etc. We can locate the <body>
// render object very easily via the DOM.
HTMLElementImpl* body = document()->body();
RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0;
if (bodyObject) {
bgLayer = bodyObject->style()->backgroundLayers();
bgColor = bodyObject->style()->backgroundColor();
}
}
int w = width();
int h = height();
int rw, rh;
if (canvas()->view()) {
rw = canvas()->view()->contentsWidth();
rh = canvas()->view()->contentsHeight();
}
else {
rw = canvas()->width();
rh = canvas()->height();
}
int bx = _tx - marginLeft();
int by = _ty - marginTop();
int bw = kMax(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
int bh = kMax(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh);
// CSS2 14.2:
// " The background of the box generated by the root element covers the entire canvas."
// hence, paint the background even in the margin areas (unlike for every other element!)
// I just love these little inconsistencies .. :-( (Dirk)
int my = kMax(by, i.r.y());
paintBackgrounds(i.p, bgColor, bgLayer, my, i.r.height(), bx, by, bw, bh);
if (style()->hasBorder() && style()->display() != INLINE)
paintBorder( i.p, _tx, _ty, w, h, style() );
}
void RenderBox::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
{
if (!shouldPaintWithinRoot(i))
return;
if (isRoot())
return paintRootBoxDecorations(i, _tx, _ty);
int w = width();
int h = height() + borderTopExtra() + borderBottomExtra();
_ty -= borderTopExtra();
int my = kMax(_ty, i.r.y());
int mh;
if (_ty < i.r.y())
mh = kMax(0, h - (i.r.y() - _ty));
else
mh = kMin(i.r.height(), h);
// If we have a native theme appearance, paint that before painting our background.
// The theme will tell us whether or not we should also paint the CSS background.
if (!style()->hasAppearance() || theme()->paint(this, i, IntRect(_tx, _ty, w, h))) {
// The <body> only paints its background if the root element has defined a background
// independent of the body. Go through the DOM to get to the root element's render object,
// since the root could be inline and wrapped in an anonymous block.
if (!isBody() || !document()->isHTMLDocument() || document()->documentElement()->renderer()->style()->hasBackground())
paintBackgrounds(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
}
// The theme will tell us whether or not we should also paint the CSS border.
if ((!style()->hasAppearance() || theme()->paintBorder(this, i, IntRect(_tx, _ty, w, h))) && style()->hasBorder())
paintBorder(i.p, _tx, _ty, w, h, style());
}
void RenderBox::paintBackgrounds(GraphicsContext* p, const Color& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height)
{
if (!bgLayer) return;
paintBackgrounds(p, c, bgLayer->next(), clipy, cliph, _tx, _ty, w, height);
paintBackground(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height);
}
void RenderBox::paintBackground(GraphicsContext* p, const Color& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height)
{
paintBackgroundExtended(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height,
borderLeft(), borderRight(), paddingLeft(), paddingRight());
}
void RenderBox::paintBackgroundExtended(GraphicsContext* p, const Color& c, const BackgroundLayer* bgLayer, int clipy, int cliph,
int _tx, int _ty, int w, int h,
int bleft, int bright, int pleft, int pright)
{
bool clippedToBorderRadius = false;
if (style()->hasBorderRadius()) {
p->save();
p->addRoundedRectClip(IntRect(_tx, _ty, w, h),
style()->borderTopLeftRadius(), style()->borderTopRightRadius(),
style()->borderBottomLeftRadius(), style()->borderBottomRightRadius());
clippedToBorderRadius = true;
}
if (bgLayer->backgroundClip() != BGBORDER) {
// Clip to the padding or content boxes as necessary.
bool includePadding = bgLayer->backgroundClip() == BGCONTENT;
int x = _tx + bleft + (includePadding ? pleft : 0);
int y = _ty + borderTop() + (includePadding ? paddingTop() : 0);
int width = w - bleft - bright - (includePadding ? pleft + pright : 0);
int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0);
p->save();
p->addClip(IntRect(x, y, width, height));
}
CachedImage* bg = bgLayer->backgroundImage();
bool shouldPaintBackgroundImage = bg && bg->canRender();
Color bgColor = c;
// When this style flag is set, change existing background colors and images to a solid white background.
// If there's no bg color or image, leave it untouched to avoid affecting transparency.
// We don't try to avoid loading the background images, because this style flag is only set
// when printing, and at that point we've already loaded the background images anyway. (To avoid
// loading the background images we'd have to do this check when applying styles rather than
// while rendering.)
if (style()->forceBackgroundsToWhite()) {
// Note that we can't reuse this variable below because the bgColor might be changed
bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0;
if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
bgColor = Color::white;
shouldPaintBackgroundImage = false;
}
}
// Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
// no background in the child document should show the parent's background.
if (!bgLayer->next() && isRoot() && !(bgColor.isValid() && bgColor.alpha() > 0) && canvas()->view()) {
bool isTransparent;
DOM::NodeImpl* elt = document()->ownerElement();
if (elt) {
if (elt->hasTagName(frameTag))
isTransparent = false;
else {
// Locate the <body> element using the DOM. This is easier than trying
// to crawl around a render tree with potential :before/:after content and
// anonymous blocks created by inline <body> tags etc. We can locate the <body>
// render object very easily via the DOM.
HTMLElementImpl* body = document()->body();
isTransparent = !body || !body->hasLocalName(framesetTag); // Can't scroll a frameset document anyway.
}
} else
isTransparent = canvas()->view()->isTransparent();
if (isTransparent)
canvas()->view()->useSlowRepaints(); // The parent must show behind the child.
else
bgColor = Color::white;
}
// Paint the color first underneath all images.
if (!bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0) {
// If we have an alpha and we are painting the root element, go ahead and blend with white.
if (bgColor.alpha() < 0xFF && isRoot() && !canvas()->view()->isTransparent())
p->fillRect(_tx, clipy, w, cliph, Color(Color::white));
p->fillRect(_tx, clipy, w, cliph, bgColor);
}
// no progressive loading of the background image
if (shouldPaintBackgroundImage) {
int sx = 0;
int sy = 0;
int cw,ch;
int cx,cy;
// CSS2 chapter 14.2.1
if (bgLayer->backgroundAttachment()) {
// scroll
int hpab = 0, vpab = 0, left = 0, top = 0; // Init to 0 for background-origin of 'border'
if (bgLayer->backgroundOrigin() != BGBORDER) {
hpab += bleft + bright;
vpab += borderTop() + borderBottom();
left += bleft;
top += borderTop();
if (bgLayer->backgroundOrigin() == BGCONTENT) {
hpab += pleft + pright;
vpab += paddingTop() + paddingBottom();
left += pleft;
top += paddingTop();
}
}
int pw = w - hpab;
int ph = h - vpab;
int pixw = bg->imageSize().width();
int pixh = bg->imageSize().height();
EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
if( (bgr == NO_REPEAT || bgr == REPEAT_Y) && w > pixw ) {
cw = pixw;
int xPosition = bgLayer->backgroundXPosition().calcMinValue(pw - pixw);
if (xPosition >= 0)
cx = _tx + xPosition;
else {
cx = _tx;
if (pixw > 0) {
sx = -xPosition;
cw += xPosition;
}
}
cx += left;
} else {
// repeat over x or background is wider than box
cw = w;
cx = _tx;
if (pixw > 0) {
int xPosition = bgLayer->backgroundXPosition().calcMinValue(pw - pixw);
if ((xPosition > 0) && (bgr == NO_REPEAT)) {
cx += xPosition;
cw -= xPosition;
} else {
sx = pixw - (xPosition % pixw );
sx -= left % pixw;
}
}
}
if( (bgr == NO_REPEAT || bgr == REPEAT_X) && h > pixh ) {
ch = pixh;
int yPosition = bgLayer->backgroundYPosition().calcMinValue(ph - pixh);
if (yPosition >= 0)
cy = _ty + yPosition;
else {
cy = _ty;
if (pixh > 0) {
sy = -yPosition;
ch += yPosition;
}
}
cy += top;
} else {
// repeat over y or background is taller than box
ch = h;
cy = _ty;
if (pixh > 0) {
int yPosition = bgLayer->backgroundYPosition().calcMinValue(ph - pixh);
if ((yPosition > 0) && (bgr == NO_REPEAT)) {
cy += yPosition;
ch -= yPosition;
} else {
sy = pixh - (yPosition % pixh );
sy -= top % pixh;
}
}
}
}
else
{
//fixed
IntRect vr = viewRect();
int pw = vr.width();
int ph = vr.height();
int pixw = bg->imageSize().width();
int pixh = bg->imageSize().height();
EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
if( (bgr == NO_REPEAT || bgr == REPEAT_Y) && pw > pixw ) {
cw = pixw;
cx = vr.x() + bgLayer->backgroundXPosition().calcMinValue(pw - pixw);
} else {
cw = pw;
cx = vr.x();
if (pixw > 0)
sx = pixw - bgLayer->backgroundXPosition().calcMinValue(pw - pixw) % pixw;
}
if( (bgr == NO_REPEAT || bgr == REPEAT_X) && ph > pixh ) {
ch = pixh;
cy = vr.y() + bgLayer->backgroundYPosition().calcMinValue(ph - pixh);
} else {
ch = ph;
cy = vr.y();
if (pixh > 0)
sy = pixh - bgLayer->backgroundYPosition().calcMinValue(ph - pixh) % pixh;
}
IntRect b = intersection(IntRect(cx, cy, cw, ch), IntRect(_tx, _ty, w, h));
sx += b.x() - cx;
sy += b.y() - cy;
cx = b.x();
cy = b.y();
cw = b.width();
ch = b.height();
}
if (cw>0 && ch>0)
p->drawTiledImage(bg->image(), cx, cy, cw, ch, sx, sy);
}
if (bgLayer->backgroundClip() != BGBORDER)
p->restore(); // Undo the background clip
if (clippedToBorderRadius)
p->restore(); // Undo the border radius clip
}
void RenderBox::outlineBox(GraphicsContext* p, int _tx, int _ty, const char *color)
{
p->setPen(Pen(Color(color), 1, Pen::DotLine));
p->setBrush(WebCore::Brush::NoBrush);
p->drawRect(_tx, _ty, m_width, m_height);
}
IntRect RenderBox::getOverflowClipRect(int tx, int ty)
{
// XXX When overflow-clip (CSS3) is implemented, we'll obtain the property
// here.
int bl=borderLeft(),bt=borderTop(),bb=borderBottom(),br=borderRight();
int clipx = tx + bl;
int clipy = ty + bt;
int clipw = m_width - bl - br;
int cliph = m_height - bt - bb + borderTopExtra() + borderBottomExtra();
// Subtract out scrollbars if we have them.
if (m_layer) {
clipw -= m_layer->verticalScrollbarWidth();
cliph -= m_layer->horizontalScrollbarHeight();
}
return IntRect(clipx,clipy,clipw,cliph);
}
IntRect RenderBox::getClipRect(int tx, int ty)
{
int clipx = tx;
int clipy = ty;
int clipw = m_width;
int cliph = m_height;
if (!style()->clipLeft().isAuto()) {
int c = style()->clipLeft().calcValue(m_width);
clipx += c;
clipw -= c;
}
if (!style()->clipRight().isAuto()) {
int w = style()->clipRight().calcValue(m_width);
clipw -= m_width - w;
}
if (!style()->clipTop().isAuto()) {
int c = style()->clipTop().calcValue(m_height);
clipy += c;
cliph -= c;
}
if (!style()->clipBottom().isAuto()) {
int h = style()->clipBottom().calcValue(m_height);
cliph -= m_height - h;
}
return IntRect(clipx, clipy, clipw, cliph);
}
int RenderBox::containingBlockWidth() const
{
RenderBlock* cb = containingBlock();
if (!cb)
return 0;
if (usesLineWidth())
return cb->lineWidth(m_y);
else
return cb->contentWidth();
}
bool RenderBox::absolutePosition(int &xPos, int &yPos, bool f)
{
if (style()->position() == FixedPosition)
f = true;
RenderObject *o = container();
if (o && o->absolutePosition(xPos, yPos, f)) {
yPos += o->borderTopExtra();
if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isInlineFlow()) {
// When we have an enclosing relpositioned inline, we need to add in the offset of the first line
// box from the rest of the content, but only in the cases where we know we're positioned
// relative to the inline itself.
RenderFlow* flow = static_cast<RenderFlow*>(o);
int sx = 0;
int sy = 0;
if (flow->firstLineBox()) {
sx = flow->firstLineBox()->xPos();
sy = flow->firstLineBox()->yPos();
} else {
sx = flow->staticX();
sy = flow->staticY();
}
bool isInlineType = style()->isOriginalDisplayInlineType();
if (!hasStaticX())
xPos += sx;
// This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
// an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
// behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
// do.
if (hasStaticX() && !isInlineType)
// Avoid adding in the left border/padding of the containing block twice. Subtract it out.
xPos += sx - (containingBlock()->borderLeft() + containingBlock()->paddingLeft());
if (!hasStaticY())
yPos += sy;
}
if (o->hasOverflowClip())
o->layer()->subtractScrollOffset(xPos, yPos);
if (!isInline() || isReplaced()) {
xPos += m_x;
yPos += m_y;
}
if (isRelPositioned())
relativePositionOffset(xPos, yPos);
return true;
}
else {
xPos = yPos = 0;
return false;
}
}
void RenderBox::dirtyLineBoxes(bool fullLayout, bool)
{
if (m_inlineBoxWrapper) {
if (fullLayout) {
m_inlineBoxWrapper->destroy(renderArena());
m_inlineBoxWrapper = 0;
}
else
m_inlineBoxWrapper->dirtyLineBoxes();
}
}
void RenderBox::position(InlineBox* box, int from, int len, bool reverse, bool override)
{
if (isPositioned()) {
// Cache the x position only if we were an INLINE type originally.
bool wasInline = style()->isOriginalDisplayInlineType();
if (wasInline && hasStaticX()) {
// The value is cached in the xPos of the box. We only need this value if
// our object was inline originally, since otherwise it would have ended up underneath
// the inlines.
m_staticX = box->xPos();
}
else if (!wasInline && hasStaticY())
// Our object was a block originally, so we make our normal flow position be
// just below the line box (as though all the inlines that came before us got
// wrapped in an anonymous block, which is what would have happened had we been
// in flow). This value was cached in the yPos() of the box.
m_staticY = box->yPos();
// Nuke the box.
box->remove();
box->destroy(renderArena());
}
else if (isReplaced()) {
m_x = box->xPos();
m_y = box->yPos();
m_inlineBoxWrapper = box;
}
}
// For inline replaced elements, this function returns the inline box that owns us. Enables
// the replaced RenderObject to quickly determine what line it is contained on and to easily
// iterate over structures on the line.
InlineBox* RenderBox::inlineBoxWrapper() const
{
return m_inlineBoxWrapper;
}
void RenderBox::deleteLineBoxWrapper()
{
if (m_inlineBoxWrapper) {
if (!documentBeingDestroyed())
m_inlineBoxWrapper->remove();
m_inlineBoxWrapper->destroy(renderArena());
m_inlineBoxWrapper = 0;
}
}
void RenderBox::setInlineBoxWrapper(InlineBox* b)
{
m_inlineBoxWrapper = b;
}
IntRect RenderBox::getAbsoluteRepaintRect()
{
IntRect r(0, 0, overflowWidth(false), overflowHeight(false));
if (style()) {
if (style()->hasAppearance())
// The theme may wish to inflate the rect used when repainting.
theme()->adjustRepaintRect(this, r);
r.inflate(style()->outlineSize()); // FIXME: Technically the outline inflation could fit within the theme inflation.
}
computeAbsoluteRepaintRect(r);
return r;
}
void RenderBox::computeAbsoluteRepaintRect(IntRect& r, bool f)
{
int x = r.x() + m_x;
int y = r.y() + m_y;
// Apply the relative position offset when invalidating a rectangle. The layer
// is translated, but the render box isn't, so we need to do this to get the
// right dirty rect. Since this is called from RenderObject::setStyle, the relative position
// flag on the RenderObject has been cleared, so use the one on the style().
if (style()->position() == RelativePosition && m_layer)
m_layer->relativePositionOffset(x,y);
if (style()->position()==FixedPosition)
f = true;
RenderObject* o = container();
if (o) {
if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isInlineFlow()) {
// When we have an enclosing relpositioned inline, we need to add in the offset of the first line
// box from the rest of the content, but only in the cases where we know we're positioned
// relative to the inline itself.
RenderFlow* flow = static_cast<RenderFlow*>(o);
int sx = 0;
int sy = 0;
if (flow->firstLineBox()) {
sx = flow->firstLineBox()->xPos();
sy = flow->firstLineBox()->yPos();
} else {
sx = flow->staticX();
sy = flow->staticY();
}
bool isInlineType = style()->isOriginalDisplayInlineType();
if (!hasStaticX())
x += sx;
// This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
// an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
// behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
// do.
if (hasStaticX() && !isInlineType)
// Avoid adding in the left border/padding of the containing block twice. Subtract it out.
x += sx - (containingBlock()->borderLeft() + containingBlock()->paddingLeft());
if (!hasStaticY())
y += sy;
}
// <body> may not have overflow, since it might be applying its overflow value to the
// scrollbars.
if (o->hasOverflowClip()) {
// o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
// layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
// anyway if its size does change.
IntRect boxRect(0, 0, o->layer()->width(), o->layer()->height());
o->layer()->subtractScrollOffset(x,y); // For overflow:auto/scroll/hidden.
IntRect repaintRect(x, y, r.width(), r.height());
r = intersection(repaintRect, boxRect);
if (r.isEmpty())
return;
} else {
r.setX(x);
r.setY(y);
}
o->computeAbsoluteRepaintRect(r, f);
}
}
void RenderBox::repaintDuringLayoutIfMoved(int oldX, int oldY)
{
int newX = m_x;
int newY = m_y;
if (oldX != newX || oldY != newY) {
// The child moved. Invalidate the object's old and new positions. We have to do this
// since the object may not have gotten a layout.
m_x = oldX; m_y = oldY;
repaint();
repaintFloatingDescendants();
m_x = newX; m_y = newY;
repaint();
repaintFloatingDescendants();
}
}
void RenderBox::relativePositionOffset(int &tx, int &ty)
{
if(!style()->left().isAuto())
tx += style()->left().calcValue(containingBlockWidth());
else if(!style()->right().isAuto())
tx -= style()->right().calcValue(containingBlockWidth());
if(!style()->top().isAuto())
{
if (!style()->top().isPercent()
|| containingBlock()->style()->height().isFixed())
ty += style()->top().calcValue(containingBlockHeight());
}
else if(!style()->bottom().isAuto())
{
if (!style()->bottom().isPercent()
|| containingBlock()->style()->height().isFixed())
ty -= style()->bottom().calcValue(containingBlockHeight());
}
}
void RenderBox::calcWidth()
{
if (isPositioned())
calcAbsoluteHorizontal();
else
{
// The parent box is flexing us, so it has increased or decreased our width. Use the width
// from the style context.
if (m_overrideSize != -1 && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
&& parent()->isFlexingChildren()) {
m_width = m_overrideSize;
return;
}
bool inVerticalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL;
bool stretching = parent()->style()->boxAlign() == BSTRETCH;
bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable() &&
(!inVerticalBox || !stretching);
Length w;
if (treatAsReplaced)
w = Length( calcReplacedWidth(), Fixed );
else
w = style()->width();
Length ml = style()->marginLeft();
Length mr = style()->marginRight();
RenderBlock *cb = containingBlock();
int cw = containingBlockWidth();
if (cw<0) cw = 0;
m_marginLeft = 0;
m_marginRight = 0;
if (isInline() && !isInlineBlockOrInlineTable()) {
// just calculate margins
m_marginLeft = ml.calcMinValue(cw);
m_marginRight = mr.calcMinValue(cw);
if (treatAsReplaced) {
m_width = w.calcValue(cw) + borderLeft() + borderRight() + paddingLeft() + paddingRight();
m_width = kMax(m_width, m_minWidth);
}
return;
}
else {
LengthType widthType, minWidthType, maxWidthType;
if (treatAsReplaced) {
m_width = w.calcValue(cw) + borderLeft() + borderRight() + paddingLeft() + paddingRight();
widthType = w.type();
} else {
m_width = calcWidthUsing(Width, cw, widthType);
int minW = calcWidthUsing(MinWidth, cw, minWidthType);
int maxW = style()->maxWidth().value() == undefinedLength
? m_width : calcWidthUsing(MaxWidth, cw, maxWidthType);
if (m_width > maxW) {
m_width = maxW;
widthType = maxWidthType;
}
if (m_width < minW) {
m_width = minW;
widthType = minWidthType;
}
}
if (widthType == Auto) {
m_marginLeft = ml.calcMinValue(cw);
m_marginRight = mr.calcMinValue(cw);
}
else
{
calcHorizontalMargins(ml,mr,cw);
}
}
if (cw && cw != m_width + m_marginLeft + m_marginRight && !isFloating() && !isInline() &&
!cb->isFlexibleBox())
{
if (cb->style()->direction()==LTR)
m_marginRight = cw - m_width - m_marginLeft;
else
m_marginLeft = cw - m_width - m_marginRight;
}
}
}
int RenderBox::calcWidthUsing(WidthType widthType, int cw, LengthType& lengthType)
{
int width = m_width;
Length w;
if (widthType == Width)
w = style()->width();
else if (widthType == MinWidth)
w = style()->minWidth();
else
w = style()->maxWidth();
lengthType = w.type();
if (w.isIntrinsicOrAuto()) {
int marginLeft = style()->marginLeft().calcMinValue(cw);
int marginRight = style()->marginRight().calcMinValue(cw);
if (cw) width = cw - marginLeft - marginRight;
if (sizesToIntrinsicWidth(widthType)) {
width = kMax(width, m_minWidth);
width = kMin(width, m_maxWidth);
}
}
else
width = calcBorderBoxWidth(w.calcValue(cw));
return width;
}
bool RenderBox::sizesToIntrinsicWidth(WidthType widthType) const
{
// Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
// but they allow text to sit on the same line as the marquee.
if (isFloating() || (isCompact() && isInline()) ||
(isInlineBlockOrInlineTable() && !isHTMLMarquee()))
return true;
// This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both
// min-width and width. max-width is only clamped if it is also intrinsic.
Length width = widthType == MaxWidth ? style()->maxWidth() : style()->width();
if (width.type() == Intrinsic)
return true;
// Children of a horizontal marquee do not fill the container by default.
// FIXME: Need to deal with MAUTO value properly. It could be vertical.
if (parent()->style()->overflow() == OMARQUEE) {
EMarqueeDirection dir = parent()->style()->marqueeDirection();
if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
return true;
}
// Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
// that don't stretch their kids lay out their children at their intrinsic widths.
if (parent()->isFlexibleBox() &&
(parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
return true;
return false;
}
void RenderBox::calcHorizontalMargins(const Length& ml, const Length& mr, int cw)
{
if (isFloating() || isInline()) // Inline blocks/tables and floats don't have their margins increased.
{
m_marginLeft = ml.calcMinValue(cw);
m_marginRight = mr.calcMinValue(cw);
}
else
{
if ( (ml.isAuto() && mr.isAuto() && m_width<cw) ||
(!ml.isAuto() && !mr.isAuto() &&
containingBlock()->style()->textAlign() == KHTML_CENTER) )
{
m_marginLeft = (cw - m_width)/2;
if (m_marginLeft<0) m_marginLeft=0;
m_marginRight = cw - m_width - m_marginLeft;
}
else if ( (mr.isAuto() && m_width<cw) ||
(!ml.isAuto() && containingBlock()->style()->direction() == RTL &&
containingBlock()->style()->textAlign() == KHTML_LEFT))
{
m_marginLeft = ml.calcValue(cw);
m_marginRight = cw - m_width - m_marginLeft;
}
else if ( (ml.isAuto() && m_width<cw) ||
(!mr.isAuto() && containingBlock()->style()->direction() == LTR &&
containingBlock()->style()->textAlign() == KHTML_RIGHT))
{
m_marginRight = mr.calcValue(cw);
m_marginLeft = cw - m_width - m_marginRight;
}
else
{
// this makes auto margins 0 if we failed a m_width<cw test above (css2.1, 10.3.3)
m_marginLeft = ml.calcMinValue(cw);
m_marginRight = mr.calcMinValue(cw);
}
}
}
void RenderBox::calcHeight()
{
// Cell height is managed by the table and inline non-replaced elements do not support a height property.
if (isTableCell() || (isInline() && !isReplaced()))
return;
if (isPositioned())
calcAbsoluteVertical();
else
{
calcVerticalMargins();
// For tables, calculate margins only
if (isTable())
return;
Length h;
bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
bool stretching = parent()->style()->boxAlign() == BSTRETCH;
bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable() && (!inHorizontalBox || !stretching);
bool checkMinMaxHeight = false;
// The parent box is flexing us, so it has increased or decreased our height. We have to
// grab our cached flexible height.
if (m_overrideSize != -1 && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL
&& parent()->isFlexingChildren())
h = Length(m_overrideSize - borderTop() - borderBottom() - paddingTop() - paddingBottom(), Fixed);
else if (treatAsReplaced)
h = Length(calcReplacedHeight(), Fixed);
else {
h = style()->height();
checkMinMaxHeight = true;
}
// Block children of horizontal flexible boxes fill the height of the box.
if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
&& parent()->isStretchingChildren()) {
h = Length(parent()->contentHeight() - marginTop() - marginBottom() -
borderTop() - paddingTop() - borderBottom() - paddingBottom(), Fixed);
checkMinMaxHeight = false;
}
int height;
if (checkMinMaxHeight) {
height = calcHeightUsing(style()->height());
if (height == -1)
height = m_height;
int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset.
int maxH = style()->maxHeight().value() == undefinedLength ? height : calcHeightUsing(style()->maxHeight());
if (maxH == -1)
maxH = height;
height = kMin(maxH, height);
height = kMax(minH, height);
}
else
// The only times we don't check min/max height are when a fixed length has
// been given as an override. Just use that. The value has already been adjusted
// for box-sizing.
height = h.value() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
m_height = height;
}
// Unfurling marquees override with the furled height.
if (style()->overflow() == OMARQUEE && m_layer && m_layer->marquee() &&
m_layer->marquee()->isUnfurlMarquee() && !m_layer->marquee()->isHorizontal()) {
m_layer->marquee()->setEnd(m_height);
m_height = kMin(m_height, m_layer->marquee()->unfurlPos());
}
// WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
// <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
// is specified.
if (style()->htmlHacks() && style()->height().isAuto() &&
!isFloatingOrPositioned() && (isRoot() || isBody())) {
int margins = collapsedMarginTop() + collapsedMarginBottom();
int visHeight = canvas()->view()->visibleHeight();
if (isRoot())
m_height = kMax(m_height, visHeight - margins);
else
m_height = kMax(m_height, visHeight -
(margins + parent()->marginTop() + parent()->marginBottom() +
parent()->borderTop() + parent()->borderBottom() +
parent()->paddingTop() + parent()->paddingBottom()));
}
}
int RenderBox::calcHeightUsing(const Length& h)
{
int height = -1;
if (!h.isAuto()) {
if (h.isFixed())
height = h.value();
else if (h.isPercent())
height = calcPercentageHeight(h);
if (height != -1) {
height = calcBorderBoxHeight(height);
return height;
}
}
return height;
}
int RenderBox::calcPercentageHeight(const Length& height)
{
int result = -1;
bool includeBorderPadding = isTable();
RenderBlock* cb = containingBlock();
if (style()->htmlHacks()) {
// In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
// block that may have a specified height and then use it. In strict mode, this violates the
// specification, which states that percentage heights just revert to auto if the containing
// block has an auto height.
for ( ; !cb->isCanvas() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() &&
cb->style()->height().isAuto(); cb = cb->containingBlock());
}
// Table cells violate what the CSS spec says to do with heights. Basically we
// don't care if the cell specified a height or not. We just always make ourselves
// be a percentage of the cell's current content height.
if (cb->isTableCell()) {
result = cb->overrideSize();
if (result == -1) {
// Normally we would let the cell size intrinsically, but scrolling overflow has to be
// treated differently, since WinIE lets scrolled overflow regions shrink as needed.
// While we can't get all cases right, we can at least detect when the cell has a specified
// height or when the table has a specified height. In these cases we want to initially have
// no size and allow the flexing of the table or the cell to its specified height to cause us
// to grow to fill the space. This could end up being wrong in some cases, but it is
// preferable to the alternative (sizing intrinsically and making the row end up too big).
RenderTableCell* cell = static_cast<RenderTableCell*>(cb);
if (scrollsOverflow() &&
(!cell->style()->height().isAuto() || !cell->table()->style()->height().isAuto()))
return 0;
return -1;
}
includeBorderPadding = true;
}
// Otherwise we only use our percentage height if our containing block had a specified
// height.
else if (cb->style()->height().isFixed())
result = cb->calcContentBoxHeight(cb->style()->height().value());
else if (cb->style()->height().isPercent()) {
// We need to recur and compute the percentage height for our containing block.
result = cb->calcPercentageHeight(cb->style()->height());
if (result != -1)
result = cb->calcContentBoxHeight(result);
}
else if (cb->isCanvas() || (cb->isBody() && style()->htmlHacks())) {
// Don't allow this to affect the block' m_height member variable, since this
// can get called while the block is still laying out its kids.
int oldHeight = cb->height();
cb->calcHeight();
result = cb->contentHeight();
cb->setHeight(oldHeight);
} else if (cb->isRoot() && isPositioned()) {
// Match the positioned objects behavior, which is that positioned objects will fill their viewport
// always. Note we could only hit this case by recurring into calcPercentageHeight on a positioned containing block.
result = cb->calcContentBoxHeight(cb->availableHeight());
}
if (result != -1) {
result = height.calcValue(result);
if (includeBorderPadding) {
// It is necessary to use the border-box to match WinIE's broken
// box model. This is essential for sizing inside
// table cells using percentage heights.
result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom());
result = kMax(0, result);
}
}
return result;
}
int RenderBox::calcReplacedWidth() const
{
int width = calcReplacedWidthUsing(Width);
int minW = calcReplacedWidthUsing(MinWidth);
int maxW = style()->maxWidth().value() == undefinedLength ? width : calcReplacedWidthUsing(MaxWidth);
return kMax(minW, kMin(width, maxW));
}
int RenderBox::calcReplacedWidthUsing(WidthType widthType) const
{
Length w;
if (widthType == Width)
w = style()->width();
else if (widthType == MinWidth)
w = style()->minWidth();
else
w = style()->maxWidth();
switch (w.type()) {
case Fixed:
return calcContentBoxWidth(w.value());
case Percent: {
const int cw = containingBlockWidth();
if (cw > 0)
return calcContentBoxWidth(w.calcMinValue(cw));
}
// fall through
default:
return intrinsicWidth();
}
}
int RenderBox::calcReplacedHeight() const
{
int height = calcReplacedHeightUsing(Height);
int minH = calcReplacedHeightUsing(MinHeight);
int maxH = style()->maxHeight().value() == undefinedLength ? height : calcReplacedHeightUsing(MaxHeight);
return kMax(minH, kMin(height, maxH));
}
int RenderBox::calcReplacedHeightUsing(HeightType heightType) const
{
Length h;
if (heightType == Height)
h = style()->height();
else if (heightType == MinHeight)
h = style()->minHeight();
else
h = style()->maxHeight();
switch (h.type()) {
case Fixed:
return calcContentBoxHeight(h.value());
case Percent:
return calcContentBoxHeight(h.calcValue(containingBlock()->availableHeight()));
default:
return intrinsicHeight();
}
}
int RenderBox::availableHeight() const
{
return availableHeightUsing(style()->height());
}
int RenderBox::availableHeightUsing(const Length& h) const
{
if (h.isFixed())
return calcContentBoxHeight(h.value());
if (isCanvas())
return static_cast<const RenderCanvas*>(this)->view()->visibleHeight();
// We need to stop here, since we don't want to increase the height of the table
// artificially. We're going to rely on this cell getting expanded to some new
// height, and then when we lay out again we'll use the calculation below.
if (isTableCell() && (h.isAuto() || h.isPercent()))
return overrideSize() - (borderLeft() + borderRight() + paddingLeft() + paddingRight());
if (h.isPercent())
return calcContentBoxHeight(h.calcValue(containingBlock()->availableHeight()));
return containingBlock()->availableHeight();
}
void RenderBox::calcVerticalMargins()
{
if( isTableCell() ) {
// table margins are basically infinite
m_marginTop = TABLECELLMARGIN;
m_marginBottom = TABLECELLMARGIN;
return;
}
Length tm = style()->marginTop();
Length bm = style()->marginBottom();
// margins are calculated with respect to the _width_ of
// the containing block (8.3)
int cw = containingBlock()->contentWidth();
m_marginTop = tm.calcMinValue(cw);
m_marginBottom = bm.calcMinValue(cw);
}
void RenderBox::setStaticX(int staticX)
{
m_staticX = staticX;
}
void RenderBox::setStaticY(int staticY)
{
m_staticY = staticY;
}
void RenderBox::calcAbsoluteHorizontal()
{
const int AUTO = -666666;
int l, r, cw;
int pab = borderLeft()+ borderRight()+ paddingLeft()+ paddingRight();
l = r = AUTO;
// We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
RenderObject* cb = container();
cw = containingBlockWidth() + cb->paddingLeft() + cb->paddingRight();
if (!style()->left().isAuto())
l = style()->left().calcValue(cw);
if (!style()->right().isAuto())
r = style()->right().calcValue(cw);
int static_distance=0;
if ((parent()->style()->direction()==LTR && (l == AUTO && r == AUTO))
|| style()->left().isStatic())
{
static_distance = m_staticX - cb->borderLeft(); // Should already have been set through layout of the parent().
RenderObject* po = parent();
for (; po && po != cb; po = po->parent())
static_distance += po->xPos();
l = static_distance;
}
else if ((parent()->style()->direction()==RTL && (l==AUTO && r==AUTO ))
|| style()->right().isStatic())
{
RenderObject* po = parent();
static_distance = m_staticX + cw + cb->borderRight() - po->width(); // Should already have been set through layout of the parent().
while (po && po!=containingBlock()) {
static_distance -= po->xPos();
po=po->parent();
}
r = static_distance;
}
calcAbsoluteHorizontalValues(Width, cb, cw, pab, static_distance, l, r, m_width, m_marginLeft, m_marginRight, m_x);
// Avoid doing any work in the common case (where the values of min-width and max-width are their defaults).
int minW = m_width, minML, minMR, minX;
calcAbsoluteHorizontalValues(MinWidth, cb, cw, pab, static_distance, l, r, minW, minML, minMR, minX);
int maxW = m_width, maxML, maxMR, maxX;
if (style()->maxWidth().value() != undefinedLength)
calcAbsoluteHorizontalValues(MaxWidth, cb, cw, static_distance, pab, l, r, maxW, maxML, maxMR, maxX);
if (m_width > maxW) {
m_width = maxW;
m_marginLeft = maxML;
m_marginRight = maxMR;
m_x = maxX;
}
if (m_width < minW) {
m_width = minW;
m_marginLeft = minML;
m_marginRight = minMR;
m_x = minX;
}
}
void RenderBox::calcAbsoluteHorizontalValues(WidthType widthType, RenderObject* cb, int cw, int pab, int static_distance,
int l, int r, int& w, int& ml, int& mr, int& x)
{
const int AUTO = -666666;
w = ml = mr = AUTO;
if (!style()->marginLeft().isAuto())
ml = style()->marginLeft().calcValue(cw);
if (!style()->marginRight().isAuto())
mr = style()->marginRight().calcValue(cw);
Length width;
if (widthType == Width)
width = style()->width();
else if (widthType == MinWidth)
width = style()->minWidth();
else
width = style()->maxWidth();
if (!width.isIntrinsicOrAuto())
w = calcContentBoxWidth(width.calcValue(cw));
else if (isReplaced())
w = calcReplacedWidth();
if (l != AUTO && w != AUTO && r != AUTO) {
// left, width, right all given, play with margins
int ot = l + w + r + pab;
if (ml==AUTO && mr==AUTO) {
// both margins auto, solve for equality
ml = (cw - ot)/2;
mr = cw - ot - ml;
}
else if (ml==AUTO)
// solve for left margin
ml = cw - ot - mr;
else if (mr==AUTO)
// solve for right margin
mr = cw - ot - ml;
else {
// overconstrained, solve according to dir
if (style()->direction() == LTR)
r = cw - ( l + w + ml + mr + pab);
else
l = cw - ( r + w + ml + mr + pab);
}
}
else
{
// one or two of (left, width, right) missing, solve
// auto margins are ignored
if (ml==AUTO) ml = 0;
if (mr==AUTO) mr = 0;
//1. solve left & width.
if (l == AUTO && w == AUTO && r != AUTO) {
// From section 10.3.7 of the CSS2.1 specification.
// "The shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width)."
w = kMin(kMax(m_minWidth - pab, cw - (r + ml + mr + pab)), m_maxWidth - pab);
l = cw - (r + w + ml + mr + pab);
}
else
//2. solve left & right. use static positioning.
if (l == AUTO && w != AUTO && r == AUTO) {
if (style()->direction()==RTL) {
r = static_distance;
l = cw - (r + w + ml + mr + pab);
}
else {
l = static_distance;
r = cw - (l + w + ml + mr + pab);
}
} //3. solve width & right.
else if (l != AUTO && w == AUTO && r == AUTO) {
// From section 10.3.7 of the CSS2.1 specification.
// "The shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width)."
w = kMin(kMax(m_minWidth - pab, cw - (l + ml + mr + pab)), m_maxWidth - pab);
r = cw - (l + w + ml + mr + pab);
}
else
//4. solve left
if (l==AUTO && w!=AUTO && r!=AUTO)
l = cw - (r + w + ml + mr + pab);
else
//5. solve width
if (l!=AUTO && w==AUTO && r!=AUTO)
w = cw - (r + l + ml + mr + pab);
else
//6. solve right
if (l!=AUTO && w!=AUTO && r==AUTO)
r = cw - (l + w + ml + mr + pab);
}
w += pab;
x = l + ml + cb->borderLeft();
}
void RenderBox::calcAbsoluteVertical()
{
// css2 spec 10.6.4 & 10.6.5
// based on
// http://www.w3.org/Style/css2-updates/REC-CSS2-19980512-errata
// (actually updated 2000-10-24)
// that introduces static-position value for top, left & right
const int AUTO = -666666;
int t, b, ch;
t = b = AUTO;
int pab = borderTop()+borderBottom()+paddingTop()+paddingBottom();
// We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
RenderObject* cb = container();
if (cb->isRoot()) // Even in strict mode (where we don't grow the root to fill the viewport) other browsers
// position as though the root fills the viewport.
ch = cb->availableHeight();
else
ch = cb->height() - cb->borderTop() - cb->borderBottom();
if (!style()->top().isAuto())
t = style()->top().calcValue(ch);
if (!style()->bottom().isAuto())
b = style()->bottom().calcValue(ch);
int h, mt, mb, y;
calcAbsoluteVerticalValues(Height, cb, ch, pab, t, b, h, mt, mb, y);
// Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
int minH = h, minMT, minMB, minY;
calcAbsoluteVerticalValues(MinHeight, cb, ch, pab, t, b, minH, minMT, minMB, minY);
int maxH = h, maxMT, maxMB, maxY;
if (style()->maxHeight().value() != undefinedLength)
calcAbsoluteVerticalValues(MaxHeight, cb, ch, pab, t, b, maxH, maxMT, maxMB, maxY);
if (h > maxH) {
h = maxH;
mt = maxMT;
mb = maxMB;
y = maxY;
}
if (h < minH) {
h = minH;
mt = minMT;
mb = minMB;
y = minY;
}
// If our natural height exceeds the new height once we've set it, then we need to make sure to update
// overflow to track the spillout.
if (m_height > h)
setOverflowHeight(m_height);
// Set our final values.
m_height = h;
m_marginTop = mt;
m_marginBottom = mb;
m_y = y;
}
void RenderBox::calcAbsoluteVerticalValues(HeightType heightType, RenderObject* cb, int ch, int pab,
int t, int b, int& h, int& mt, int& mb, int& y)
{
const int AUTO = -666666;
h = mt = mb = AUTO;
if (!style()->marginTop().isAuto())
mt = style()->marginTop().calcValue(ch);
if (!style()->marginBottom().isAuto())
mb = style()->marginBottom().calcValue(ch);
Length height;
if (heightType == Height)
height = style()->height();
else if (heightType == MinHeight)
height = style()->minHeight();
else
height = style()->maxHeight();
int ourHeight = m_height;
if (isTable() && height.isAuto())
// Height is never unsolved for tables. "auto" means shrink to fit. Use our
// height instead.
h = ourHeight - pab;
else if (!height.isAuto())
{
h = calcContentBoxHeight(height.calcValue(ch));
if (ourHeight - pab > h)
ourHeight = h + pab;
}
else if (isReplaced())
h = calcReplacedHeight();
int static_top=0;
if ((t == AUTO && b == AUTO) || style()->top().isStatic()) {
// calc hypothetical location in the normal flow
// used for 1) top=static-position
// 2) top, bottom, height are all auto -> calc top -> 3.
// 3) precalc for case 2 below
static_top = m_staticY - cb->borderTop(); // Should already have been set through layout of the parent().
RenderObject* po = parent();
for (; po && po != cb; po = po->parent())
static_top += po->yPos();
if (h == AUTO || style()->top().isStatic())
t = static_top;
}
if (t != AUTO && h != AUTO && b != AUTO) {
// top, height, bottom all given, play with margins
int ot = h + t + b + pab;
if (mt == AUTO && mb == AUTO) {
// both margins auto, solve for equality
mt = (ch - ot)/2;
mb = ch - ot - mt;
}
else if (mt==AUTO)
// solve for top margin
mt = ch - ot - mb;
else if (mb==AUTO)
// solve for bottom margin
mb = ch - ot - mt;
else
// overconstrained, solve for bottom
b = ch - (h + t + mt + mb + pab);
}
else {
// one or two of (top, height, bottom) missing, solve
// auto margins are ignored
if (mt == AUTO)
mt = 0;
if (mb == AUTO)
mb = 0;
//1. solve top & height. use content height.
if (t == AUTO && h == AUTO && b != AUTO) {
h = ourHeight - pab;
t = ch - (h + b + mt + mb + pab);
}
else if (t == AUTO && h != AUTO && b == AUTO) //2. solve top & bottom. use static positioning.
{
t = static_top;
b = ch - (h + t + mt + mb + pab);
}
else if (t != AUTO && h == AUTO && b == AUTO) //3. solve height & bottom. use content height.
{
h = ourHeight - pab;
b = ch - (h + t + mt + mb + pab);
}
else
//4. solve top
if (t == AUTO && h != AUTO && b != AUTO)
t = ch - (h + b + mt + mb + pab);
else
//5. solve height
if (t != AUTO && h == AUTO && b != AUTO)
h = ch - (t + b + mt + mb + pab);
else
//6. solve bottom
if (t != AUTO && h != AUTO && b == AUTO)
b = ch - (h + t + mt + mb + pab);
}
if (ourHeight < h + pab) //content must still fit
ourHeight = h + pab;
if (hasOverflowClip() && ourHeight > h + pab)
ourHeight = h + pab;
// Do not allow the height to be negative. This can happen when someone specifies both top and bottom
// but the containing block height is less than top, e.g., top:20px, bottom:0, containing block height 16.
ourHeight = kMax(0, ourHeight);
h = ourHeight;
y = t + mt + cb->borderTop();
}
IntRect RenderBox::caretRect(int offset, EAffinity affinity, int *extraWidthToEndOfLine)
{
// FIXME: Is it OK to check only first child instead of picking
// right child based on offset? Is it OK to pass the same offset
// along to the child instead of offset 0 or whatever?
// propagate it downwards to its children, someone will feel responsible
RenderObject *child = firstChild();
if (child) {
IntRect result = child->caretRect(offset, affinity, extraWidthToEndOfLine);
// FIXME: in-band signalling!
if (result.isEmpty())
return result;
}
int _x, _y, height;
// if not, use the extents of this box
// offset 0 means left, offset 1 means right
_x = xPos() + (offset == 0 ? 0 : m_width);
InlineBox *box = inlineBoxWrapper();
if (box) {
height = box->root()->bottomOverflow() - box->root()->topOverflow();
_y = box->root()->topOverflow();
}
else {
_y = yPos();
height = m_height;
}
// If height of box is smaller than font height, use the latter one,
// otherwise the caret might become invisible.
//
// Also, if the box is not a replaced element, always use the font height.
// This prevents the "big caret" bug described in:
// <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
//
// FIXME: ignoring :first-line, missing good reason to take care of
int fontHeight = style()->font().height();
if (fontHeight > height || !isReplaced())
height = fontHeight;
int absx, absy;
RenderObject *cb = containingBlock();
if (cb && cb != this && cb->absolutePosition(absx, absy)) {
_x += absx;
_y += absy;
}
else {
// we don't know our absolute position, and there is no point returning
// just a relative one
return IntRect();
}
if (extraWidthToEndOfLine)
*extraWidthToEndOfLine = m_width - _x;
return IntRect(_x, _y, 1, height);
}
int RenderBox::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
{
return includeSelf ? m_height : 0;
}
int RenderBox::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
{
return includeSelf ? m_width : 0;
}
int RenderBox::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
{
return includeSelf ? 0 : m_width;
}
}