RenderListItem.cpp [plain text]
#include "config.h"
#include "RenderListItem.h"
#include "CSSFontSelector.h"
#include "ElementTraversal.h"
#include "HTMLNames.h"
#include "HTMLOListElement.h"
#include "HTMLUListElement.h"
#include "InlineElementBox.h"
#include "PseudoElement.h"
#include "RenderTreeBuilder.h"
#include "RenderView.h"
#include "StyleInheritedData.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/StackStats.h>
#include <wtf/StdLibExtras.h>
namespace WebCore {
using namespace HTMLNames;
WTF_MAKE_ISO_ALLOCATED_IMPL(RenderListItem);
RenderListItem::RenderListItem(Element& element, RenderStyle&& style)
: RenderBlockFlow(element, WTFMove(style))
{
setInline(false);
}
RenderListItem::~RenderListItem()
{
ASSERT(!m_marker);
}
RenderStyle RenderListItem::computeMarkerStyle() const
{
RenderStyle parentStyle = RenderStyle::clone(style());
auto fontDescription = style().fontDescription();
fontDescription.setVariantNumericSpacing(FontVariantNumericSpacing::TabularNumbers);
parentStyle.setFontDescription(WTFMove(fontDescription));
parentStyle.fontCascade().update(&document().fontSelector());
if (auto markerStyle = getCachedPseudoStyle(PseudoId::Marker, &parentStyle))
return RenderStyle::clone(*markerStyle);
auto markerStyle = RenderStyle::create();
markerStyle.inheritFrom(parentStyle);
return markerStyle;
}
void RenderListItem::insertedIntoTree()
{
RenderBlockFlow::insertedIntoTree();
updateListMarkerNumbers();
}
void RenderListItem::willBeRemovedFromTree()
{
RenderBlockFlow::willBeRemovedFromTree();
updateListMarkerNumbers();
}
bool isHTMLListElement(const Node& node)
{
return is<HTMLUListElement>(node) || is<HTMLOListElement>(node);
}
static Element* enclosingList(const RenderListItem& listItem)
{
auto& element = listItem.element();
auto* parent = is<PseudoElement>(element) ? downcast<PseudoElement>(element).hostElement() : element.parentElement();
for (auto* ancestor = parent; ancestor; ancestor = ancestor->parentElement()) {
if (isHTMLListElement(*ancestor))
return ancestor;
}
return parent;
}
static RenderListItem* nextListItemHelper(const Element& list, const Element& element)
{
auto* current = &element;
auto advance = [&] {
current = ElementTraversal::nextIncludingPseudo(*current, &list);
};
advance();
while (current) {
auto* renderer = current->renderer();
if (!is<RenderListItem>(renderer)) {
advance();
continue;
}
auto& item = downcast<RenderListItem>(*renderer);
auto* otherList = enclosingList(item);
if (!otherList) {
advance();
continue;
}
if (&list == otherList)
return &item;
current = ElementTraversal::nextIncludingPseudoSkippingChildren(*current, &list);
}
return nullptr;
}
static inline RenderListItem* nextListItem(const Element& list, const RenderListItem& item)
{
return nextListItemHelper(list, item.element());
}
static inline RenderListItem* firstListItem(const Element& list)
{
return nextListItemHelper(list, list);
}
static RenderListItem* previousListItem(const Element& list, const RenderListItem& item)
{
auto* current = &item.element();
auto advance = [&] {
current = ElementTraversal::previousIncludingPseudo(*current, &list);
};
advance();
while (current) {
auto* renderer = current->renderer();
if (!is<RenderListItem>(renderer)) {
advance();
continue;
}
auto& item = downcast<RenderListItem>(*renderer);
auto* otherList = enclosingList(item);
if (!otherList) {
advance();
continue;
}
if (&list == otherList)
return &item;
current = otherList;
}
return nullptr;
}
void RenderListItem::updateItemValuesForOrderedList(const HTMLOListElement& list)
{
for (auto* listItem = firstListItem(list); listItem; listItem = nextListItem(list, *listItem))
listItem->updateValue();
}
unsigned RenderListItem::itemCountForOrderedList(const HTMLOListElement& list)
{
unsigned itemCount = 0;
for (auto* listItem = firstListItem(list); listItem; listItem = nextListItem(list, *listItem))
++itemCount;
return itemCount;
}
void RenderListItem::updateValueNow() const
{
auto* list = enclosingList(*this);
auto* orderedList = is<HTMLOListElement>(list) ? downcast<HTMLOListElement>(list) : nullptr;
auto* startItem = this;
if (list) {
auto* item = this;
while ((item = previousListItem(*list, *item))) {
startItem = item;
if (item->m_value)
break;
}
}
auto& startValue = startItem->m_value;
if (!startValue)
startValue = orderedList ? orderedList->start() : 1;
int value = *startValue;
int increment = (orderedList && orderedList->isReversed()) ? -1 : 1;
for (auto* item = startItem; item != this; ) {
item = nextListItem(*list, *item);
item->m_value = (value += increment);
}
}
void RenderListItem::updateValue()
{
if (!m_valueWasSetExplicitly) {
m_value = WTF::nullopt;
if (m_marker)
m_marker->setNeedsLayoutAndPrefWidthsRecalc();
}
}
void RenderListItem::layout()
{
StackStats::LayoutCheckPoint layoutCheckPoint;
ASSERT(needsLayout());
RenderBlockFlow::layout();
}
void RenderListItem::addOverflowFromChildren()
{
if (m_marker)
m_marker->addOverflowFromListMarker();
RenderBlockFlow::addOverflowFromChildren();
}
void RenderListItem::computePreferredLogicalWidths()
{
if (m_marker && m_marker->preferredLogicalWidthsDirty())
m_marker->updateMarginsAndContent();
RenderBlockFlow::computePreferredLogicalWidths();
}
void RenderListItem::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (!logicalHeight() && hasOverflowClip())
return;
RenderBlockFlow::paint(paintInfo, paintOffset);
}
const String& RenderListItem::markerText() const
{
if (m_marker)
return m_marker->text();
return nullAtom().string();
}
String RenderListItem::markerTextWithSuffix() const
{
if (!m_marker)
return String();
if (m_marker->style().isLeftToRightDirection())
return m_marker->text() + m_marker->suffix();
return m_marker->suffix() + m_marker->text();
}
void RenderListItem::explicitValueChanged()
{
if (m_marker)
m_marker->setNeedsLayoutAndPrefWidthsRecalc();
updateValue();
auto* list = enclosingList(*this);
if (!list)
return;
auto* item = this;
while ((item = nextListItem(*list, *item)))
item->updateValue();
}
void RenderListItem::setExplicitValue(Optional<int> value)
{
if (!value) {
if (!m_valueWasSetExplicitly)
return;
} else {
if (m_valueWasSetExplicitly && m_value == value)
return;
}
m_valueWasSetExplicitly = value.hasValue();
m_value = value;
explicitValueChanged();
}
void RenderListItem::updateListMarkerNumbers()
{
auto* list = enclosingList(*this);
if (!list)
return;
bool isInReversedOrderedList = false;
if (is<HTMLOListElement>(*list)) {
auto& orderedList = downcast<HTMLOListElement>(*list);
orderedList.itemCountChanged();
isInReversedOrderedList = orderedList.isReversed();
}
auto* item = this;
auto subsequentListItem = isInReversedOrderedList ? previousListItem : nextListItem;
while ((item = subsequentListItem(*list, *item)) && item->m_value)
item->updateValue();
}
bool RenderListItem::isInReversedOrderedList() const
{
auto* list = enclosingList(*this);
return is<HTMLOListElement>(list) && downcast<HTMLOListElement>(*list).isReversed();
}
}