RenderContainer.cpp [plain text]
#include "config.h"
#include "RenderContainer.h"
#include "AXObjectCache.h"
#include "Document.h"
#include "RenderCounter.h"
#include "RenderImageGeneratedContent.h"
#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderListItem.h"
#include "RenderTable.h"
#include "RenderTextFragment.h"
#include "RenderView.h"
#include "htmlediting.h"
namespace WebCore {
RenderContainer::RenderContainer(Node* node)
: RenderBox(node)
, m_firstChild(0)
, m_lastChild(0)
{
}
RenderContainer::~RenderContainer()
{
}
void RenderContainer::destroy()
{
destroyLeftoverChildren();
RenderBox::destroy();
}
void RenderContainer::destroyLeftoverChildren()
{
while (m_firstChild) {
if (m_firstChild->isListMarker() || (m_firstChild->style()->styleType() == RenderStyle::FIRST_LETTER && !m_firstChild->isText()))
m_firstChild->remove(); else {
if (m_firstChild->element())
m_firstChild->element()->setRenderer(0);
m_firstChild->destroy();
}
}
}
bool RenderContainer::canHaveChildren() const
{
return true;
}
static void updateListMarkerNumbers(RenderObject* child)
{
for (RenderObject* r = child; r; r = r->nextSibling())
if (r->isListItem())
static_cast<RenderListItem*>(r)->updateValue();
}
void RenderContainer::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
bool needsTable = false;
if (newChild->isListItem())
updateListMarkerNumbers(beforeChild ? beforeChild : m_lastChild);
else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP)
needsTable = !isTable();
else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION)
needsTable = !isTable();
else if (newChild->isTableSection())
needsTable = !isTable();
else if (newChild->isTableRow())
needsTable = !isTableSection();
else if (newChild->isTableCell()) {
needsTable = !isTableRow();
if (needsTable && isTableCell() && !m_firstChild && !newChild->isTableCell())
needsTable = false;
}
if (needsTable) {
RenderTable* table;
RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : m_lastChild;
if (afterChild && afterChild->isAnonymous() && afterChild->isTable())
table = static_cast<RenderTable*>(afterChild);
else {
table = new (renderArena()) RenderTable(document() );
RefPtr<RenderStyle> newStyle = RenderStyle::create();
newStyle->inheritFrom(style());
newStyle->setDisplay(TABLE);
table->setStyle(newStyle.release());
addChild(table, beforeChild);
}
table->addChild(newChild);
} else {
insertChildNode(newChild, beforeChild);
}
if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) {
RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText();
if (textToTransform)
toRenderText(newChild)->setText(textToTransform.release(), true);
}
}
RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild, bool fullRemove)
{
ASSERT(oldChild->parent() == this);
if (!documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) {
oldChild->setNeedsLayoutAndPrefWidthsRecalc();
oldChild->repaint();
}
if (oldChild->isBox())
toRenderBox(oldChild)->deleteLineBoxWrapper();
if (!documentBeingDestroyed() && fullRemove) {
RenderLayer* layer = 0;
if (m_style->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) {
layer = enclosingLayer();
layer->dirtyVisibleContentStatus();
}
if (oldChild->firstChild() || oldChild->hasLayer()) {
if (!layer) layer = enclosingLayer();
oldChild->removeLayers(layer);
}
if (oldChild->isListItem())
updateListMarkerNumbers(oldChild->nextSibling());
if (oldChild->isPositioned() && childrenInline())
dirtyLinesFromChangedChild(oldChild);
}
if (!documentBeingDestroyed() && oldChild->isSelectionBorder())
view()->clearSelection();
if (oldChild->previousSibling())
oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
if (oldChild->nextSibling())
oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
if (m_firstChild == oldChild)
m_firstChild = oldChild->nextSibling();
if (m_lastChild == oldChild)
m_lastChild = oldChild->previousSibling();
oldChild->setPreviousSibling(0);
oldChild->setNextSibling(0);
oldChild->setParent(0);
if (AXObjectCache::accessibilityEnabled())
document()->axObjectCache()->childrenChanged(this);
return oldChild;
}
void RenderContainer::removeChild(RenderObject* oldChild)
{
oldChild->removeFromObjectLists();
removeChildNode(oldChild);
}
RenderObject* RenderContainer::beforeAfterContainer(RenderStyle::PseudoId type)
{
if (type == RenderStyle::BEFORE) {
RenderObject* first = this;
do {
first = first->firstChild();
while (first && first->isListMarker())
first = first->nextSibling();
} while (first && first->isAnonymous() && first->style()->styleType() == RenderStyle::NOPSEUDO);
if (first && first->style()->styleType() != type)
return 0;
return first;
}
if (type == RenderStyle::AFTER) {
RenderObject* last = this;
do {
last = last->lastChild();
} while (last && last->isAnonymous() && last->style()->styleType() == RenderStyle::NOPSEUDO && !last->isListMarker());
if (last && last->style()->styleType() != type)
return 0;
return last;
}
ASSERT_NOT_REACHED();
return 0;
}
void RenderContainer::updateBeforeAfterContent(RenderStyle::PseudoId type)
{
if (parent() && parent()->createsAnonymousWrapper())
return;
updateBeforeAfterContentForContainer(type, this);
}
static RenderObject* findBeforeAfterParent(RenderObject* object)
{
if (!(object->isTable() || object->isTableSection() || object->isTableRow()))
return object;
RenderObject* beforeAfterParent = object;
while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage()))
beforeAfterParent = beforeAfterParent->firstChild();
return beforeAfterParent;
}
void RenderContainer::updateBeforeAfterContentForContainer(RenderStyle::PseudoId type, RenderContainer* styledObject)
{
ASSERT(document()->usesBeforeAfterRules());
if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER)
return;
RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type);
RenderObject* child = beforeAfterContainer(type);
bool oldContentPresent = child;
bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE;
if (newContentWanted && type == RenderStyle::BEFORE && isInlineContinuation())
newContentWanted = false;
if (newContentWanted && type == RenderStyle::AFTER && isRenderInline() && static_cast<RenderInline*>(this)->continuation())
newContentWanted = false;
if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) {
if (child && child->style()->styleType() == type) {
oldContentPresent = false;
child->destroy();
child = (type == RenderStyle::BEFORE) ? m_firstChild : m_lastChild;
}
}
if (!newContentWanted)
return;
if (isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE &&
!(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition))
pseudoElementStyle->setDisplay(INLINE);
if (oldContentPresent) {
if (child && child->style()->styleType() == type) {
child->setStyle(pseudoElementStyle);
RenderObject* beforeAfterParent = findBeforeAfterParent(child);
if (!beforeAfterParent)
return;
for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) {
if (genChild->isText())
genChild->setStyle(pseudoElementStyle);
else if (genChild->isImage()) {
RefPtr<RenderStyle> style = RenderStyle::create();
style->inheritFrom(pseudoElementStyle);
genChild->setStyle(style.release());
} else
ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_LETTER);
}
}
return; }
RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? firstChild() : 0;
RenderObject* generatedContentContainer = 0;
for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->m_next) {
RenderObject* renderer = 0;
switch (content->m_type) {
case CONTENT_NONE:
break;
case CONTENT_TEXT:
renderer = new (renderArena()) RenderTextFragment(document() , content->m_content.m_text);
renderer->setStyle(pseudoElementStyle);
break;
case CONTENT_OBJECT: {
RenderImageGeneratedContent* image = new (renderArena()) RenderImageGeneratedContent(document()); RefPtr<RenderStyle> style = RenderStyle::create();
style->inheritFrom(pseudoElementStyle);
image->setStyle(style.release());
if (StyleImage* styleImage = content->m_content.m_image)
image->setStyleImage(styleImage);
renderer = image;
break;
}
case CONTENT_COUNTER:
renderer = new (renderArena()) RenderCounter(document(), *content->m_content.m_counter);
renderer->setStyle(pseudoElementStyle);
break;
}
if (renderer) {
if (!generatedContentContainer) {
generatedContentContainer = RenderObject::createObject(document(), pseudoElementStyle);
generatedContentContainer->setStyle(pseudoElementStyle);
addChild(generatedContentContainer, insertBefore);
}
generatedContentContainer->addChild(renderer);
}
}
}
bool RenderContainer::isAfterContent(RenderObject* child) const
{
if (!child)
return false;
if (child->style()->styleType() != RenderStyle::AFTER)
return false;
if (child->isText() && !child->isBR())
return false;
return true;
}
static void invalidateCountersInContainer(RenderObject* container)
{
if (!container)
return;
container = findBeforeAfterParent(container);
if (!container)
return;
for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) {
if (content->isCounter())
static_cast<RenderCounter*>(content)->invalidate();
}
}
void RenderContainer::invalidateCounters()
{
if (documentBeingDestroyed())
return;
invalidateCountersInContainer(beforeAfterContainer(RenderStyle::BEFORE));
invalidateCountersInContainer(beforeAfterContainer(RenderStyle::AFTER));
}
void RenderContainer::appendChildNode(RenderObject* newChild, bool fullAppend)
{
ASSERT(newChild->parent() == 0);
ASSERT(!isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell()));
newChild->setParent(this);
RenderObject* lChild = m_lastChild;
if (lChild) {
newChild->setPreviousSibling(lChild);
lChild->setNextSibling(newChild);
} else
m_firstChild = newChild;
m_lastChild = newChild;
if (fullAppend) {
RenderLayer* layer = 0;
if (newChild->firstChild() || newChild->hasLayer()) {
layer = enclosingLayer();
newChild->addLayers(layer, newChild);
}
if (style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) {
if (!layer)
layer = enclosingLayer();
if (layer)
layer->setHasVisibleContent(true);
}
if (!newChild->isFloatingOrPositioned() && childrenInline())
dirtyLinesFromChangedChild(newChild);
}
newChild->setNeedsLayoutAndPrefWidthsRecalc(); if (!normalChildNeedsLayout())
setChildNeedsLayout(true);
if (AXObjectCache::accessibilityEnabled())
document()->axObjectCache()->childrenChanged(this);
}
void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool fullInsert)
{
if (!beforeChild) {
appendChildNode(child);
return;
}
ASSERT(!child->parent());
while (beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock())
beforeChild = beforeChild->parent();
ASSERT(beforeChild->parent() == this);
ASSERT(!isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell()));
if (beforeChild == m_firstChild)
m_firstChild = child;
RenderObject* prev = beforeChild->previousSibling();
child->setNextSibling(beforeChild);
beforeChild->setPreviousSibling(child);
if(prev) prev->setNextSibling(child);
child->setPreviousSibling(prev);
child->setParent(this);
if (fullInsert) {
RenderLayer* layer = 0;
if (child->firstChild() || child->hasLayer()) {
layer = enclosingLayer();
child->addLayers(layer, child);
}
if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) {
if (!layer)
layer = enclosingLayer();
if (layer)
layer->setHasVisibleContent(true);
}
if (!child->isFloating() && childrenInline())
dirtyLinesFromChangedChild(child);
}
child->setNeedsLayoutAndPrefWidthsRecalc();
if (!normalChildNeedsLayout())
setChildNeedsLayout(true);
if (AXObjectCache::accessibilityEnabled())
document()->axObjectCache()->childrenChanged(this);
}
void RenderContainer::layout()
{
ASSERT(needsLayout());
LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()));
RenderObject* child = m_firstChild;
while (child) {
child->layoutIfNeeded();
ASSERT(child->isRenderInline() || !child->needsLayout());
child = child->nextSibling();
}
statePusher.pop();
setNeedsLayout(false);
}
void RenderContainer::removeLeftoverAnonymousBlock(RenderBlock* child)
{
ASSERT(child->isAnonymousBlock());
ASSERT(!child->childrenInline());
if (child->continuation())
return;
RenderObject* firstAnChild = child->firstChild();
RenderObject* lastAnChild = child->lastChild();
if (firstAnChild) {
RenderObject* o = firstAnChild;
while(o) {
o->setParent(this);
o = o->nextSibling();
}
firstAnChild->setPreviousSibling(child->previousSibling());
lastAnChild->setNextSibling(child->nextSibling());
if (child->previousSibling())
child->previousSibling()->setNextSibling(firstAnChild);
if (child->nextSibling())
child->nextSibling()->setPreviousSibling(lastAnChild);
} else {
if (child->previousSibling())
child->previousSibling()->setNextSibling(child->nextSibling());
if (child->nextSibling())
child->nextSibling()->setPreviousSibling(child->previousSibling());
}
if (child == m_firstChild)
m_firstChild = firstAnChild;
if (child == m_lastChild)
m_lastChild = lastAnChild;
child->setParent(0);
child->setPreviousSibling(0);
child->setNextSibling(0);
if (!child->isText()) {
RenderContainer *c = static_cast<RenderContainer*>(child);
c->m_firstChild = 0;
c->m_next = 0;
}
child->destroy();
}
VisiblePosition RenderContainer::positionForCoordinates(int xPos, int yPos)
{
if (!m_firstChild)
return VisiblePosition(element(), 0, DOWNSTREAM);
if (isTable() && element()) {
int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft();
int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom();
if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) {
if (xPos <= right / 2)
return VisiblePosition(Position(element(), 0));
else
return VisiblePosition(Position(element(), maxDeepOffset(element())));
}
}
int minDist = INT_MAX;
RenderBox* closestRenderer = 0;
int newX = xPos;
int newY = yPos;
if (isTableRow()) {
newX += x();
newY += y();
}
for (RenderObject* renderObject = m_firstChild; renderObject; renderObject = renderObject->nextSibling()) {
if (!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow()
|| renderObject->style()->visibility() != VISIBLE)
continue;
if (!renderObject->isBox())
continue;
RenderBox* renderer = toRenderBox(renderObject);
int top = borderTop() + paddingTop() + (isTableRow() ? 0 : renderer->y());
int bottom = top + renderer->contentHeight();
int left = borderLeft() + paddingLeft() + (isTableRow() ? 0 : renderer->x());
int right = left + renderer->contentWidth();
if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) {
if (renderer->isTableRow())
return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y());
return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y());
}
IntPoint cmp;
if (xPos > right) {
if (yPos < top)
cmp = IntPoint(right, top);
else if (yPos > bottom)
cmp = IntPoint(right, bottom);
else
cmp = IntPoint(right, yPos);
} else if (xPos < left) {
if (yPos < top)
cmp = IntPoint(left, top);
else if (yPos > bottom)
cmp = IntPoint(left, bottom);
else
cmp = IntPoint(left, yPos);
} else {
if (yPos < top)
cmp = IntPoint(xPos, top);
else
cmp = IntPoint(xPos, bottom);
}
int x1minusx2 = cmp.x() - xPos;
int y1minusy2 = cmp.y() - yPos;
int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2;
if (dist < minDist) {
closestRenderer = renderer;
minDist = dist;
}
}
if (closestRenderer)
return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y());
return VisiblePosition(element(), 0, DOWNSTREAM);
}
void RenderContainer::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool)
{
if (!m_firstChild && (isInline() || isAnonymousBlock())) {
FloatPoint absPos = localToAbsolute(FloatPoint());
absoluteRects(rects, absPos.x(), absPos.y());
return;
}
if (!m_firstChild)
return;
unsigned offset = start;
for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) {
if (child->isText() || child->isInline() || child->isAnonymousBlock()) {
FloatPoint absPos = child->localToAbsolute(FloatPoint());
child->absoluteRects(rects, absPos.x(), absPos.y());
}
}
}
void RenderContainer::collectSelectionRects(Vector<SelectionRect>& rects, unsigned start, unsigned end)
{
Vector<FloatQuad> quads;
collectAbsoluteLineBoxQuads(quads, start, end, true);
for (unsigned i = 0; i < quads.size(); i++) {
rects.append(SelectionRect(quads[i].enclosingBoundingBox()));
}
}
void RenderContainer::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool )
{
if (!m_firstChild && (isInline() || isAnonymousBlock())) {
absoluteQuads(quads);
return;
}
if (!m_firstChild)
return;
unsigned offset = start;
for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) {
if (child->isText() || child->isInline() || child->isAnonymousBlock())
child->absoluteQuads(quads);
}
}
#undef DEBUG_LAYOUT
}