Commit 259fe836 authored by abucur@adobe.com's avatar abucur@adobe.com

Use DOM ordering for list counts

https://bugs.webkit.org/show_bug.cgi?id=110352

Reviewed by Elliott Sprehn.

Source/WebCore:

Currently the list items ordering is made by traversing the render tree. This gives bad results for:
- list items flown inside regions because they are not rendered as descendants of their DOM list ancestors.
- list items matched to insertion points inside a shadow tree. The insertion point may be a child of a
list so the numbering gets broken.

To implement correct DOM ordering I've taken into account the fact that pseudo-elements can have display: list-item
so they should be included when traversing the DOM tree. I've added helper methods on the NodeTraversal
namespace that should allow easily traversing the tree including the pseudo-elements (e.g. firstChildWithPseudo
for an element with pseudo-before will return the before PseudoElement). Using these helper methods I've implemented
pre-order traversal methods aware of the pseudo-elements.
An effect of this change is how the list items inside shadow tree update their counting. With the patch, if there's
no <ol> or <ul> element on the ancestor chain of a <li> element to the shadow root, the list node will be considered the
first parent of the <li> or the shadow root if there is no ancestor.
The RenderListItem class now makes use of this new method of traversal, replacing the one based on the render tree.
To simplify the CSS counters implementation, I've changed the traversal functions inside RenderCounter to also make use
of this method. The CSS counters and the list items now have the same traversal algorithm.

In later patches I'll add tests that should cover the regions and shadow DOM use cases.
Tests bug for shadow DOM: https://bugs.webkit.org/show_bug.cgi?id=113193
Tests bug for regions: https://bugs.webkit.org/show_bug.cgi?id=103975

Tests: no new tests is this patch; added the correct expectations for fast/lists/positioned-count-crash.html
and fast/dom/shadow/shadow-and-list-elements.html

* dom/Node.cpp:
(WebCore::Node::pseudoAwarePreviousSibling):
(WebCore):
(WebCore::Node::pseudoAwareNextSibling):
(WebCore::Node::pseudoAwareFirstChild):
(WebCore::Node::pseudoAwareLastChild):
* dom/Node.h:
(Node):
* dom/NodeTraversal.cpp:
(WebCore):
(WebCore::NodeTraversal::previousIncludingPseudo):
(NodeTraversal):
(WebCore::NodeTraversal::nextIncludingPseudo):
(WebCore::NodeTraversal::nextIncludingPseudoSkippingChildren):
* dom/NodeTraversal.h:
(ElementTraversal):
(NodeTraversal):
(WebCore::ElementTraversal::previousIncludingPseudo):
(WebCore::ElementTraversal::nextIncludingPseudo):
(WebCore::ElementTraversal::nextIncludingPseudoSkippingChildren):
(WebCore::ElementTraversal::pseudoAwarePreviousSibling):
* html/HTMLLIElement.cpp:
(WebCore::HTMLLIElement::attach):
* html/HTMLOListElement.cpp:
(WebCore::HTMLOListElement::updateItemValues):
(WebCore::HTMLOListElement::recalculateItemCount):
* rendering/RenderCounter.cpp:
(WebCore::previousInPreOrder):
(WebCore::previousSiblingOrParent):
(WebCore::parentElement):
(WebCore::nextInPreOrder):
* rendering/RenderListItem.cpp:
(WebCore):
(WebCore::enclosingList):
(WebCore::RenderListItem::nextListItem):
(WebCore::previousListItem):
(WebCore::RenderListItem::calcValue):
(WebCore::RenderListItem::explicitValueChanged):
(WebCore::previousOrNextItem):
(WebCore::RenderListItem::updateListMarkerNumbers):
* rendering/RenderListItem.h:
(RenderListItem):

LayoutTests:

The fast/dom/shadow/shadow-and-list-elements-expected.html has changed because the list items
inside the shadow tree no longer see the root <ol> element.
The test fast/lists/positioned-count-crash.html has the correct rendering after changing
the list counting to be in DOM order.

* fast/dom/shadow/shadow-and-list-elements-expected.html:
* fast/lists/positioned-count-crash-expected.txt:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@148026 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 58ad2803
2013-04-09 Andrei Bucur <abucur@adobe.com>
Use DOM ordering for list counts
https://bugs.webkit.org/show_bug.cgi?id=110352
Reviewed by Elliott Sprehn.
The fast/dom/shadow/shadow-and-list-elements-expected.html has changed because the list items
inside the shadow tree no longer see the root <ol> element.
The test fast/lists/positioned-count-crash.html has the correct rendering after changing
the list counting to be in DOM order.
* fast/dom/shadow/shadow-and-list-elements-expected.html:
* fast/lists/positioned-count-crash-expected.txt:
2013-04-09 Rafael Brandao <rafael.lobo@openbossa.org>
[CoordinatedGraphics] serviceScriptedAnimations expects time in seconds
......@@ -22,18 +22,23 @@
// <li>C</li>
// </ol>
var hostEquivalent = document.getElementById("hostEquivalent");
var shadowListRoot = document.createElement("ol");
shadowListRoot.style.paddingLeft = "0px";
hostEquivalent.appendChild(shadowListRoot);
var childX = document.createElement("li");
childX.innerHTML = "X";
hostEquivalent.appendChild(childX);
childX.style.listStylePosition = "inside";
shadowListRoot.appendChild(childX);
var childUl = document.createElement("ul");
childUl.innerHTML = "B";
hostEquivalent.appendChild(childUl);
shadowListRoot.appendChild(childUl);
var childY = document.createElement("li");
childY.innerHTML = "Y";
hostEquivalent.appendChild(childY);
childY.style.listStylePosition = "inside";
shadowListRoot.appendChild(childY);
</script>
</body>
</html>
......@@ -2,5 +2,5 @@ II
For manual test: If you see no crash and "II II", it means this test PASS.
FAIL list marker should be II. Was I.
PASS list marker is II.
2013-04-09 Andrei Bucur <abucur@adobe.com>
Use DOM ordering for list counts
https://bugs.webkit.org/show_bug.cgi?id=110352
Reviewed by Elliott Sprehn.
Currently the list items ordering is made by traversing the render tree. This gives bad results for:
- list items flown inside regions because they are not rendered as descendants of their DOM list ancestors.
- list items matched to insertion points inside a shadow tree. The insertion point may be a child of a
list so the numbering gets broken.
To implement correct DOM ordering I've taken into account the fact that pseudo-elements can have display: list-item
so they should be included when traversing the DOM tree. I've added helper methods on the NodeTraversal
namespace that should allow easily traversing the tree including the pseudo-elements (e.g. firstChildWithPseudo
for an element with pseudo-before will return the before PseudoElement). Using these helper methods I've implemented
pre-order traversal methods aware of the pseudo-elements.
An effect of this change is how the list items inside shadow tree update their counting. With the patch, if there's
no <ol> or <ul> element on the ancestor chain of a <li> element to the shadow root, the list node will be considered the
first parent of the <li> or the shadow root if there is no ancestor.
The RenderListItem class now makes use of this new method of traversal, replacing the one based on the render tree.
To simplify the CSS counters implementation, I've changed the traversal functions inside RenderCounter to also make use
of this method. The CSS counters and the list items now have the same traversal algorithm.
In later patches I'll add tests that should cover the regions and shadow DOM use cases.
Tests bug for shadow DOM: https://bugs.webkit.org/show_bug.cgi?id=113193
Tests bug for regions: https://bugs.webkit.org/show_bug.cgi?id=103975
Tests: no new tests is this patch; added the correct expectations for fast/lists/positioned-count-crash.html
and fast/dom/shadow/shadow-and-list-elements.html
* dom/Node.cpp:
(WebCore::Node::pseudoAwarePreviousSibling):
(WebCore):
(WebCore::Node::pseudoAwareNextSibling):
(WebCore::Node::pseudoAwareFirstChild):
(WebCore::Node::pseudoAwareLastChild):
* dom/Node.h:
(Node):
* dom/NodeTraversal.cpp:
(WebCore):
(WebCore::NodeTraversal::previousIncludingPseudo):
(NodeTraversal):
(WebCore::NodeTraversal::nextIncludingPseudo):
(WebCore::NodeTraversal::nextIncludingPseudoSkippingChildren):
* dom/NodeTraversal.h:
(ElementTraversal):
(NodeTraversal):
(WebCore::ElementTraversal::previousIncludingPseudo):
(WebCore::ElementTraversal::nextIncludingPseudo):
(WebCore::ElementTraversal::nextIncludingPseudoSkippingChildren):
(WebCore::ElementTraversal::pseudoAwarePreviousSibling):
* html/HTMLLIElement.cpp:
(WebCore::HTMLLIElement::attach):
* html/HTMLOListElement.cpp:
(WebCore::HTMLOListElement::updateItemValues):
(WebCore::HTMLOListElement::recalculateItemCount):
* rendering/RenderCounter.cpp:
(WebCore::previousInPreOrder):
(WebCore::previousSiblingOrParent):
(WebCore::parentElement):
(WebCore::nextInPreOrder):
* rendering/RenderListItem.cpp:
(WebCore):
(WebCore::enclosingList):
(WebCore::RenderListItem::nextListItem):
(WebCore::previousListItem):
(WebCore::RenderListItem::calcValue):
(WebCore::RenderListItem::explicitValueChanged):
(WebCore::previousOrNextItem):
(WebCore::RenderListItem::updateListMarkerNumbers):
* rendering/RenderListItem.h:
(RenderListItem):
2013-04-09 Bruno de Oliveira Abinader <bruno.abinader@basyskom.com>
[Texmap] TextureMapperLayer refactor for readiblity
......@@ -1132,6 +1132,62 @@ void Node::detach()
#endif
}
Node* Node::pseudoAwarePreviousSibling() const
{
if (parentElement() && !previousSibling()) {
Element* parent = parentElement();
if (isAfterPseudoElement() && parent->lastChild())
return parent->lastChild();
if (!isBeforePseudoElement())
return parent->pseudoElement(BEFORE);
}
return previousSibling();
}
Node* Node::pseudoAwareNextSibling() const
{
if (parentElement() && !nextSibling()) {
Element* parent = parentElement();
if (isBeforePseudoElement() && parent->firstChild())
return parent->firstChild();
if (!isAfterPseudoElement())
return parent->pseudoElement(AFTER);
}
return nextSibling();
}
Node* Node::pseudoAwareFirstChild() const
{
if (isElementNode()) {
const Element* currentElement = toElement(this);
Node* first = currentElement->pseudoElement(BEFORE);
if (first)
return first;
first = currentElement->firstChild();
if (!first)
first = currentElement->pseudoElement(AFTER);
return first;
}
return firstChild();
}
Node* Node::pseudoAwareLastChild() const
{
if (isElementNode()) {
const Element* currentElement = toElement(this);
Node* last = currentElement->pseudoElement(AFTER);
if (last)
return last;
last = currentElement->lastChild();
if (!last)
last = currentElement->pseudoElement(BEFORE);
return last;
}
return lastChild();
}
// FIXME: This code is used by editing. Seems like it could move over there and not pollute Node.
Node *Node::previousNodeConsideringAtomicNodes() const
{
......
......@@ -193,6 +193,10 @@ public:
Node* lastChild() const;
bool hasAttributes() const;
NamedNodeMap* attributes() const;
Node* pseudoAwareNextSibling() const;
Node* pseudoAwarePreviousSibling() const;
Node* pseudoAwareFirstChild() const;
Node* pseudoAwareLastChild() const;
virtual KURL baseURI() const;
......
......@@ -26,10 +26,59 @@
#include "NodeTraversal.h"
#include "ContainerNode.h"
#include "PseudoElement.h"
namespace WebCore {
namespace NodeTraversal {
Node* previousIncludingPseudo(const Node* current, const Node* stayWithin)
{
Node* previous;
if (current == stayWithin)
return 0;
if ((previous = current->pseudoAwarePreviousSibling())) {
while (previous->pseudoAwareLastChild())
previous = previous->pseudoAwareLastChild();
return previous;
}
return current->parentNode();
}
Node* nextIncludingPseudo(const Node* current, const Node* stayWithin)
{
Node* next;
if ((next = current->pseudoAwareFirstChild()))
return next;
if (current == stayWithin)
return 0;
if ((next = current->pseudoAwareNextSibling()))
return next;
for (current = current->parentNode(); current; current = current->parentNode()) {
if (current == stayWithin)
return 0;
if ((next = current->pseudoAwareNextSibling()))
return next;
}
return 0;
}
Node* nextIncludingPseudoSkippingChildren(const Node* current, const Node* stayWithin)
{
Node* next;
if (current == stayWithin)
return 0;
if ((next = current->pseudoAwareNextSibling()))
return next;
for (current = current->parentNode(); current; current = current->parentNode()) {
if (current == stayWithin)
return 0;
if ((next = current->pseudoAwareNextSibling()))
return next;
}
return 0;
}
Node* nextAncestorSibling(const Node* current)
{
ASSERT(!current->nextSibling());
......
......@@ -34,17 +34,27 @@ namespace ElementTraversal {
// First element child of the node.
Element* firstWithin(const Node*);
Element* firstWithin(const ContainerNode*);
// Pre-order traversal skipping non-element nodes.
Element* next(const Node*);
Element* next(const Node*, const Node* stayWithin);
Element* next(const ContainerNode*);
Element* next(const ContainerNode*, const Node* stayWithin);
// Like next, but skips children.
Element* nextSkippingChildren(const Node*);
Element* nextSkippingChildren(const Node*, const Node* stayWithin);
Element* nextSkippingChildren(const ContainerNode*);
Element* nextSkippingChildren(const ContainerNode*, const Node* stayWithin);
// Pre-order traversal including the pseudo-elements.
Element* previousIncludingPseudo(const Node*, const Node* = 0);
Element* nextIncludingPseudo(const Node*, const Node* = 0);
Element* nextIncludingPseudoSkippingChildren(const Node*, const Node* = 0);
// Utility function to traverse only the element and pseudo-element siblings of a node.
Element* pseudoAwarePreviousSibling(const Node*);
}
namespace NodeTraversal {
......@@ -77,6 +87,11 @@ Node* nextPostOrder(const Node*, const Node* stayWithin = 0);
Node* previousPostOrder(const Node*, const Node* stayWithin = 0);
Node* previousSkippingChildrenPostOrder(const Node*, const Node* stayWithin = 0);
// Pre-order traversal including the pseudo-elements.
Node* previousIncludingPseudo(const Node*, const Node* = 0);
Node* nextIncludingPseudo(const Node*, const Node* = 0);
Node* nextIncludingPseudoSkippingChildren(const Node*, const Node* = 0);
}
namespace ElementTraversal {
......@@ -135,6 +150,39 @@ inline Element* traverseNextElementSkippingChildrenTemplate(NodeType* current, c
}
inline Element* nextSkippingChildren(const ContainerNode* current, const Node* stayWithin) { return traverseNextElementSkippingChildrenTemplate(current, stayWithin); }
inline Element* nextSkippingChildren(const Node* current, const Node* stayWithin) { return traverseNextElementSkippingChildrenTemplate(current, stayWithin); }
inline Element* previousIncludingPseudo(const Node* current, const Node* stayWithin)
{
Node* node = NodeTraversal::previousIncludingPseudo(current, stayWithin);
while (node && !node->isElementNode())
node = NodeTraversal::previousIncludingPseudo(node, stayWithin);
return toElement(node);
}
inline Element* nextIncludingPseudo(const Node* current, const Node* stayWithin)
{
Node* node = NodeTraversal::nextIncludingPseudo(current, stayWithin);
while (node && !node->isElementNode())
node = NodeTraversal::nextIncludingPseudo(node, stayWithin);
return toElement(node);
}
inline Element* nextIncludingPseudoSkippingChildren(const Node* current, const Node* stayWithin)
{
Node* node = NodeTraversal::nextIncludingPseudoSkippingChildren(current, stayWithin);
while (node && !node->isElementNode())
node = NodeTraversal::nextIncludingPseudoSkippingChildren(node, stayWithin);
return toElement(node);
}
inline Element* pseudoAwarePreviousSibling(const Node* current)
{
Node* node = current->pseudoAwarePreviousSibling();
while (node && !node->isElementNode())
node = node->pseudoAwarePreviousSibling();
return toElement(node);
}
}
namespace NodeTraversal {
......
......@@ -92,23 +92,23 @@ void HTMLLIElement::attach()
HTMLElement::attach();
if (renderer() && renderer()->isListItem()) {
RenderListItem* render = toRenderListItem(renderer());
RenderListItem* listItemRenderer = toRenderListItem(renderer());
// Find the enclosing list node.
Node* listNode = 0;
EventPathWalker walker(this);
Element* listNode = 0;
Element* current = this;
while (!listNode) {
walker.moveToParent();
if (!walker.node())
current = current->parentElement();
if (!current)
break;
if (walker.node()->hasTagName(ulTag) || walker.node()->hasTagName(olTag))
listNode = walker.node();
if (current->hasTagName(ulTag) || current->hasTagName(olTag))
listNode = current;
}
// If we are not in a list, tell the renderer so it can position us inside.
// We don't want to change our style to say "inside" since that would affect nested nodes.
if (!listNode)
render->setNotInList(true);
listItemRenderer->setNotInList(true);
parseValue(fastGetAttribute(valueAttr));
}
......
......@@ -106,7 +106,7 @@ void HTMLOListElement::setStart(int start)
void HTMLOListElement::updateItemValues()
{
for (RenderListItem* listItem = RenderListItem::nextListItem(renderer()); listItem; listItem = RenderListItem::nextListItem(renderer(), listItem))
for (RenderListItem* listItem = RenderListItem::nextListItem(this); listItem; listItem = RenderListItem::nextListItem(this, listItem))
listItem->updateValue();
}
......@@ -114,7 +114,7 @@ void HTMLOListElement::recalculateItemCount()
{
m_itemCount = 0;
for (RenderListItem* listItem = RenderListItem::nextListItem(renderer()); listItem; listItem = RenderListItem::nextListItem(renderer(), listItem))
for (RenderListItem* listItem = RenderListItem::nextListItem(this); listItem; listItem = RenderListItem::nextListItem(this, listItem))
m_itemCount++;
m_shouldRecalculateItemCount = false;
......
......@@ -57,94 +57,30 @@ static CounterMaps& counterMaps()
// including pseudo elements as defined in CSS 2.1.
static RenderObject* previousInPreOrder(const RenderObject* object)
{
Element* parent;
Element* sibling;
switch (object->style()->styleType()) {
case NOPSEUDO:
ASSERT(!object->isAnonymous());
parent = toElement(object->node());
sibling = parent->previousElementSibling();
parent = parent->parentElement();
break;
case BEFORE:
return object->generatingNode()->renderer(); // It is always the generating node's renderer
case AFTER:
parent = toElement(object->generatingNode());
sibling = parent->lastElementChild();
break;
default:
ASSERT_NOT_REACHED();
return 0;
}
while (sibling) {
if (RenderObject* renderer = sibling->renderer()) {
if (RenderObject* after = sibling->pseudoElementRenderer(AFTER))
return after;
parent = sibling;
sibling = sibling->lastElementChild();
if (!sibling) {
if (RenderObject* before = toElement(renderer->node())->pseudoElementRenderer(BEFORE))
return before;
return renderer;
}
} else
sibling = sibling->previousElementSibling();
}
if (!parent)
return 0;
if (RenderObject* before = parent->pseudoElementRenderer(BEFORE))
return before;
return parent->renderer();
Element* self = toElement(object->node());
Element* previous = ElementTraversal::previousIncludingPseudo(self);
while (previous && !previous->renderer())
previous = ElementTraversal::previousIncludingPseudo(previous);
return previous ? previous->renderer() : 0;
}
// This function processes the renderer tree in the order of the DOM tree
// including pseudo elements as defined in CSS 2.1.
static RenderObject* previousSiblingOrParent(const RenderObject* object)
{
Element* parent;
Element* sibling;
switch (object->style()->styleType()) {
case NOPSEUDO:
ASSERT(!object->isAnonymous());
parent = toElement(object->node());
sibling = parent->previousElementSibling();
parent = parent->parentElement();
break;
case BEFORE:
return object->generatingNode()->renderer(); // It is always the generating node's renderer
case AFTER:
parent = toElement(object->generatingNode());
sibling = parent->lastElementChild();
break;
default:
ASSERT_NOT_REACHED();
return 0;
}
while (sibling) {
if (RenderObject* renderer = sibling->renderer()) // This skips invisible nodes
return renderer;
sibling = sibling->previousElementSibling();
}
if (!parent)
return 0;
if (RenderObject* before = parent->pseudoElementRenderer(BEFORE))
return before;
return parent->renderer();
Element* self = toElement(object->node());
Element* previous = ElementTraversal::pseudoAwarePreviousSibling(self);
while (previous && !previous->renderer())
previous = ElementTraversal::pseudoAwarePreviousSibling(previous);
if (previous)
return previous->renderer();
previous = self->parentElement();
return previous ? previous->renderer() : 0;
}
static Element* parentElement(RenderObject* object)
static inline Element* parentElement(RenderObject* object)
{
switch (object->style()->styleType()) {
case NOPSEUDO:
ASSERT(!object->isAnonymous());
return toElement(object->node())->parentElement();
case BEFORE:
case AFTER:
return toElement(object->generatingNode());
default:
ASSERT_NOT_REACHED();
return 0;
}
return toElement(object->node())->parentElement();
}
static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObject* second)
......@@ -156,44 +92,11 @@ static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObjec
// including pseudo elements as defined in CSS 2.1.
static RenderObject* nextInPreOrder(const RenderObject* object, const Element* stayWithin, bool skipDescendants = false)
{
Element* self;
Element* child;
self = toElement(object->generatingNode());
if (skipDescendants)
goto nextsibling;
switch (object->style()->styleType()) {
case NOPSEUDO:
ASSERT(!object->isAnonymous());
if (RenderObject* before = self->pseudoElementRenderer(BEFORE))
return before;
break;
case BEFORE:
break;
case AFTER:
goto nextsibling;
default:
ASSERT_NOT_REACHED();
return 0;
}
child = ElementTraversal::firstWithin(self);
while (true) {
while (child) {
if (RenderObject* renderer = child->renderer())
return renderer;
child = ElementTraversal::nextSkippingChildren(child, self);
}
if (RenderObject* after = self->pseudoElementRenderer(AFTER))
return after;
nextsibling:
if (self == stayWithin)
return 0;
child = ElementTraversal::nextSkippingChildren(self);
self = self->parentElement();
if (!self) {
ASSERT(!child); // We can only reach this if we are searching beyond the root element
return 0; // which cannot have siblings
}
}
Element* self = toElement(object->node());
Element* next = skipDescendants ? ElementTraversal::nextIncludingPseudoSkippingChildren(self, stayWithin) : ElementTraversal::nextIncludingPseudo(self, stayWithin);
while (next && !next->renderer())
next = skipDescendants ? ElementTraversal::nextIncludingPseudoSkippingChildren(next, stayWithin) : ElementTraversal::nextIncludingPseudo(next, stayWithin);
return next ? next->renderer() : 0;
}
static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value)
......
......@@ -27,6 +27,7 @@
#include "CachedImage.h"
#include "HTMLNames.h"
#include "HTMLOListElement.h"
#include "NodeTraversal.h"
#include "RenderListMarker.h"
#include "RenderView.h"
#include "StyleInheritedData.h"
......@@ -96,18 +97,17 @@ static bool isList(Node* node)
return (node->hasTagName(ulTag) || node->hasTagName(olTag));
}
// Returns the enclosing list with respect to the DOM order.
static Node* enclosingList(const RenderListItem* listItem)
{
Node* listItemNode = listItem->node();
Node* firstNode = 0;
for (const RenderObject* renderer = listItem->parent(); renderer; renderer = renderer->parent()) {
Node* node = renderer->node();
if (node) {
if (isList(node))
return node;
if (!firstNode)
firstNode = node;
}
// We use parentNode because the enclosing list could be a ShadowRoot that's not Element.
for (Node* parent = listItemNode->parentNode(); parent; parent = parent->parentNode()) {
if (isList(parent))
return parent;
if (!firstNode)
firstNode = parent;
}
// If there's no actual <ul> or <ol> list element, then the first found
......@@ -116,42 +116,51 @@ static Node* enclosingList(const RenderListItem* listItem)
return firstNode;
}
RenderListItem* RenderListItem::nextListItem(RenderObject* list, const RenderListItem* item)
// Returns the next list item with respect to the DOM order.
RenderListItem* RenderListItem::nextListItem(Node* listNode, const RenderListItem* item)
{
if (!list)
if (!listNode)
return 0;
RenderObject* renderer = item ? item->nextInPreOrder(list) : list->nextInPreOrder(list);
while (renderer) {
if (renderer->node() && isList(renderer->node())) {
Node* current = item ? item->node() : listNode;
current = ElementTraversal::nextIncludingPseudo(current, listNode);
while (current) {
if (isList(current)) {
// We've found a nested, independent list: nothing to do here.
renderer = renderer->nextInPreOrderAfterChildren(list);
current = ElementTraversal::nextIncludingPseudoSkippingChildren(current, listNode);
continue;
}
if (renderer->isListItem())