Index: third_party/WebKit/WebCore/rendering/RenderObjectChildList.cpp |
=================================================================== |
--- third_party/WebKit/WebCore/rendering/RenderObjectChildList.cpp (revision 9118) |
+++ third_party/WebKit/WebCore/rendering/RenderObjectChildList.cpp (working copy) |
@@ -25,10 +25,29 @@ |
#include "config.h" |
#include "RenderObjectChildList.h" |
-#include "RenderObject.h" |
+#include "AXObjectCache.h" |
+#include "RenderBlock.h" |
+#include "RenderCounter.h" |
+#include "RenderImageGeneratedContent.h" |
+#include "RenderInline.h" |
+#include "RenderLayer.h" |
+#include "RenderListItem.h" |
+#include "RenderStyle.h" |
+#include "RenderTextFragment.h" |
+#include "RenderView.h" |
+ |
namespace WebCore { |
+static void updateListMarkerNumbers(RenderObject* child) |
+{ |
+ for (RenderObject* r = child; r; r = r->nextSibling()) { |
+ if (r->isListItem()) |
+ static_cast<RenderListItem*>(r)->updateValue(); |
+ } |
+} |
+ |
+ |
void RenderObjectChildList::destroyLeftoverChildren() |
{ |
while (firstChild()) { |
@@ -43,4 +62,364 @@ |
} |
} |
+RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, RenderObject* oldChild, bool fullRemove) |
+{ |
+ ASSERT(oldChild->parent() == owner); |
+ |
+ // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or |
+ // that a positioned child got yanked). We also repaint, so that the area exposed when the child |
+ // disappears gets repainted properly. |
+ if (!owner->documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) { |
+ oldChild->setNeedsLayoutAndPrefWidthsRecalc(); |
+ oldChild->repaint(); |
+ } |
+ |
+ // If we have a line box wrapper, delete it. |
+ oldChild->deleteLineBoxWrapper(); |
+ |
+ if (!owner->documentBeingDestroyed() && fullRemove) { |
+ // if we remove visible child from an invisible parent, we don't know the layer visibility any more |
+ RenderLayer* layer = 0; |
+ if (owner->style()->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) { |
+ layer = owner->enclosingLayer(); |
+ layer->dirtyVisibleContentStatus(); |
+ } |
+ |
+ // Keep our layer hierarchy updated. |
+ if (oldChild->firstChild() || oldChild->hasLayer()) { |
+ if (!layer) |
+ layer = owner->enclosingLayer(); |
+ oldChild->removeLayers(layer); |
+ } |
+ |
+ // renumber ordered lists |
+ if (oldChild->isListItem()) |
+ updateListMarkerNumbers(oldChild->nextSibling()); |
+ |
+ if (oldChild->isPositioned() && owner->childrenInline()) |
+ owner->dirtyLinesFromChangedChild(oldChild); |
+ } |
+ |
+ // If oldChild is the start or end of the selection, then clear the selection to |
+ // avoid problems of invalid pointers. |
+ // FIXME: The SelectionController should be responsible for this when it |
+ // is notified of DOM mutations. |
+ if (!owner->documentBeingDestroyed() && oldChild->isSelectionBorder()) |
+ owner->view()->clearSelection(); |
+ |
+ // remove the child |
+ if (oldChild->previousSibling()) |
+ oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); |
+ if (oldChild->nextSibling()) |
+ oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); |
+ |
+ if (firstChild() == oldChild) |
+ setFirstChild(oldChild->nextSibling()); |
+ if (lastChild() == oldChild) |
+ setLastChild(oldChild->previousSibling()); |
+ |
+ oldChild->setPreviousSibling(0); |
+ oldChild->setNextSibling(0); |
+ oldChild->setParent(0); |
+ |
+ if (AXObjectCache::accessibilityEnabled()) |
+ owner->document()->axObjectCache()->childrenChanged(owner); |
+ |
+ return oldChild; |
} |
+ |
+void RenderObjectChildList::appendChildNode(RenderObject* owner, RenderObject* newChild, bool fullAppend) |
+{ |
+ ASSERT(newChild->parent() == 0); |
+ ASSERT(!owner->isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell())); |
+ |
+ newChild->setParent(owner); |
+ RenderObject* lChild = lastChild(); |
+ |
+ if (lChild) { |
+ newChild->setPreviousSibling(lChild); |
+ lChild->setNextSibling(newChild); |
+ } else |
+ setFirstChild(newChild); |
+ |
+ setLastChild(newChild); |
+ |
+ if (fullAppend) { |
+ // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children |
+ // and don't have a layer attached to ourselves. |
+ RenderLayer* layer = 0; |
+ if (newChild->firstChild() || newChild->hasLayer()) { |
+ layer = owner->enclosingLayer(); |
+ newChild->addLayers(layer, newChild); |
+ } |
+ |
+ // if the new child is visible but this object was not, tell the layer it has some visible content |
+ // that needs to be drawn and layer visibility optimization can't be used |
+ if (owner->style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) { |
+ if (!layer) |
+ layer = owner->enclosingLayer(); |
+ if (layer) |
+ layer->setHasVisibleContent(true); |
+ } |
+ |
+ if (!newChild->isFloatingOrPositioned() && owner->childrenInline()) |
+ owner->dirtyLinesFromChangedChild(newChild); |
+ } |
+ |
+ newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. |
+ if (!owner->normalChildNeedsLayout()) |
+ owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. |
+ |
+ if (AXObjectCache::accessibilityEnabled()) |
+ owner->document()->axObjectCache()->childrenChanged(owner); |
+} |
+ |
+void RenderObjectChildList::insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* beforeChild, bool fullInsert) |
+{ |
+ if (!beforeChild) { |
+ appendChildNode(owner, child); |
+ return; |
+ } |
+ |
+ ASSERT(!child->parent()); |
+ while (beforeChild->parent() != owner && beforeChild->parent()->isAnonymousBlock()) |
+ beforeChild = beforeChild->parent(); |
+ ASSERT(beforeChild->parent() == owner); |
+ |
+ ASSERT(!owner->isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell())); |
+ |
+ if (beforeChild == firstChild()) |
+ setFirstChild(child); |
+ |
+ RenderObject* prev = beforeChild->previousSibling(); |
+ child->setNextSibling(beforeChild); |
+ beforeChild->setPreviousSibling(child); |
+ if (prev) |
+ prev->setNextSibling(child); |
+ child->setPreviousSibling(prev); |
+ |
+ child->setParent(owner); |
+ |
+ if (fullInsert) { |
+ // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children |
+ // and don't have a layer attached to ourselves. |
+ RenderLayer* layer = 0; |
+ if (child->firstChild() || child->hasLayer()) { |
+ layer = owner->enclosingLayer(); |
+ child->addLayers(layer, child); |
+ } |
+ |
+ // if the new child is visible but this object was not, tell the layer it has some visible content |
+ // that needs to be drawn and layer visibility optimization can't be used |
+ if (owner->style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) { |
+ if (!layer) |
+ layer = owner->enclosingLayer(); |
+ if (layer) |
+ layer->setHasVisibleContent(true); |
+ } |
+ |
+ |
+ if (!child->isFloating() && owner->childrenInline()) |
+ owner->dirtyLinesFromChangedChild(child); |
+ } |
+ |
+ child->setNeedsLayoutAndPrefWidthsRecalc(); |
+ if (!owner->normalChildNeedsLayout()) |
+ owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. |
+ |
+ if (AXObjectCache::accessibilityEnabled()) |
+ owner->document()->axObjectCache()->childrenChanged(owner); |
+} |
+ |
+static RenderObject* beforeAfterContainer(RenderObject* container, RenderStyle::PseudoId type) |
+{ |
+ if (type == RenderStyle::BEFORE) { |
+ RenderObject* first = container; |
+ do { |
+ // Skip list markers. |
+ 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 = container; |
+ 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; |
+} |
+ |
+static RenderObject* findBeforeAfterParent(RenderObject* object) |
+{ |
+ // Only table parts need to search for the :before or :after parent |
+ if (!(object->isTable() || object->isTableSection() || object->isTableRow())) |
+ return object; |
+ |
+ RenderObject* beforeAfterParent = object; |
+ while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage())) |
+ beforeAfterParent = beforeAfterParent->firstChild(); |
+ return beforeAfterParent; |
+} |
+ |
+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 RenderObjectChildList::invalidateCounters(RenderObject* owner) |
+{ |
+ ASSERT(!owner->documentBeingDestroyed()); |
+ invalidateCountersInContainer(beforeAfterContainer(owner, RenderStyle::BEFORE)); |
+ invalidateCountersInContainer(beforeAfterContainer(owner, RenderStyle::AFTER)); |
+} |
+ |
+void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, RenderStyle::PseudoId type, RenderObject* styledObject) |
+{ |
+ // Double check that the document did in fact use generated content rules. Otherwise we should not have been called. |
+ ASSERT(owner->document()->usesBeforeAfterRules()); |
+ |
+ // In CSS2, before/after pseudo-content cannot nest. Check this first. |
+ if (owner->style()->styleType() == RenderStyle::BEFORE || owner->style()->styleType() == RenderStyle::AFTER) |
+ return; |
+ |
+ if (!styledObject) |
+ styledObject = owner; |
+ |
+ RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type); |
+ RenderObject* child = beforeAfterContainer(owner, type); |
+ |
+ // Whether or not we currently have generated content attached. |
+ bool oldContentPresent = child; |
+ |
+ // Whether or not we now want generated content. |
+ bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE; |
+ |
+ // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate |
+ // :after content and not :before content. |
+ if (newContentWanted && type == RenderStyle::BEFORE && owner->isRenderInline() && toRenderInline(owner)->isInlineContinuation()) |
+ newContentWanted = false; |
+ |
+ // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object, |
+ // then we don't generate the :after content. |
+ if (newContentWanted && type == RenderStyle::AFTER && owner->isRenderInline() && toRenderInline(owner)->continuation()) |
+ newContentWanted = false; |
+ |
+ // If we don't want generated content any longer, or if we have generated content, but it's no longer |
+ // identical to the new content data we want to build render objects for, then we nuke all |
+ // of the old generated content. |
+ if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) { |
+ // Nuke the child. |
+ if (child && child->style()->styleType() == type) { |
+ oldContentPresent = false; |
+ child->destroy(); |
+ child = (type == RenderStyle::BEFORE) ? owner->virtualChildren()->firstChild() : owner->virtualChildren()->lastChild(); |
+ } |
+ } |
+ |
+ // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we |
+ // have no generated content and can now return. |
+ if (!newContentWanted) |
+ return; |
+ |
+ if (owner->isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE && |
+ !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition)) |
+ // According to the CSS2 spec (the end of section 12.1), the only allowed |
+ // display values for the pseudo style are NONE and INLINE for inline flows. |
+ // FIXME: CSS2.1 lifted this restriction, but block display types will crash. |
+ // For now we at least relax the restriction to allow all inline types like inline-block |
+ // and inline-table. |
+ pseudoElementStyle->setDisplay(INLINE); |
+ |
+ if (oldContentPresent) { |
+ if (child && child->style()->styleType() == type) { |
+ // We have generated content present still. We want to walk this content and update our |
+ // style information with the new pseudo-element style. |
+ child->setStyle(pseudoElementStyle); |
+ |
+ RenderObject* beforeAfterParent = findBeforeAfterParent(child); |
+ if (!beforeAfterParent) |
+ return; |
+ |
+ // Note that if we ever support additional types of generated content (which should be way off |
+ // in the future), this code will need to be patched. |
+ for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) { |
+ if (genChild->isText()) |
+ // Generated text content is a child whose style also needs to be set to the pseudo-element style. |
+ genChild->setStyle(pseudoElementStyle); |
+ else if (genChild->isImage()) { |
+ // Images get an empty style that inherits from the pseudo. |
+ RefPtr<RenderStyle> style = RenderStyle::create(); |
+ style->inheritFrom(pseudoElementStyle); |
+ genChild->setStyle(style.release()); |
+ } else |
+ // Must be a first-letter container. updateFirstLetter() will take care of it. |
+ ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_LETTER); |
+ } |
+ } |
+ return; // We've updated the generated content. That's all we needed to do. |
+ } |
+ |
+ RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? owner->virtualChildren()->firstChild() : 0; |
+ |
+ // Generated content consists of a single container that houses multiple children (specified |
+ // by the content property). This generated content container gets the pseudo-element style set on it. |
+ RenderObject* generatedContentContainer = 0; |
+ |
+ // Walk our list of generated content and create render objects for each. |
+ 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 (owner->renderArena()) RenderTextFragment(owner->document() /* anonymous object */, content->m_content.m_text); |
+ renderer->setStyle(pseudoElementStyle); |
+ break; |
+ case CONTENT_OBJECT: { |
+ RenderImageGeneratedContent* image = new (owner->renderArena()) RenderImageGeneratedContent(owner->document()); // anonymous object |
+ 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 (owner->renderArena()) RenderCounter(owner->document(), *content->m_content.m_counter); |
+ renderer->setStyle(pseudoElementStyle); |
+ break; |
+ } |
+ |
+ if (renderer) { |
+ if (!generatedContentContainer) { |
+ // Make a generated box that might be any display type now that we are able to drill down into children |
+ // to find the original content properly. |
+ generatedContentContainer = RenderObject::createObject(owner->document(), pseudoElementStyle); |
+ generatedContentContainer->setStyle(pseudoElementStyle); |
+ owner->addChild(generatedContentContainer, insertBefore); |
+ } |
+ generatedContentContainer->addChild(renderer); |
+ } |
+ } |
+} |
+ |
+} // namespace WebCore |