Home | History | Annotate | Download | only in rendering
      1 /**
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved.
      5  * Copyright (C) 2006 Andrew Wellington (proton (at) wiretapped.net)
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "core/rendering/RenderListItem.h"
     26 
     27 #include "HTMLNames.h"
     28 #include "core/dom/NodeTraversal.h"
     29 #include "core/html/HTMLOListElement.h"
     30 #include "core/rendering/RenderListMarker.h"
     31 #include "core/rendering/RenderView.h"
     32 #include "wtf/StdLibExtras.h"
     33 #include "wtf/text/StringBuilder.h"
     34 
     35 using namespace std;
     36 
     37 namespace WebCore {
     38 
     39 using namespace HTMLNames;
     40 
     41 RenderListItem::RenderListItem(Element* element)
     42     : RenderBlock(element)
     43     , m_marker(0)
     44     , m_hasExplicitValue(false)
     45     , m_isValueUpToDate(false)
     46     , m_notInList(false)
     47 {
     48     setInline(false);
     49 }
     50 
     51 void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     52 {
     53     RenderBlock::styleDidChange(diff, oldStyle);
     54 
     55     if (style()->listStyleType() != NoneListStyle
     56         || (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) {
     57         RefPtr<RenderStyle> newStyle = RenderStyle::create();
     58         // The marker always inherits from the list item, regardless of where it might end
     59         // up (e.g., in some deeply nested line box). See CSS3 spec.
     60         newStyle->inheritFrom(style());
     61         if (!m_marker)
     62             m_marker = RenderListMarker::createAnonymous(this);
     63         m_marker->setStyle(newStyle.release());
     64     } else if (m_marker) {
     65         m_marker->destroy();
     66         m_marker = 0;
     67     }
     68 }
     69 
     70 void RenderListItem::willBeDestroyed()
     71 {
     72     if (m_marker) {
     73         m_marker->destroy();
     74         m_marker = 0;
     75     }
     76     RenderBlock::willBeDestroyed();
     77 }
     78 
     79 void RenderListItem::insertedIntoTree()
     80 {
     81     RenderBlock::insertedIntoTree();
     82 
     83     updateListMarkerNumbers();
     84 }
     85 
     86 void RenderListItem::willBeRemovedFromTree()
     87 {
     88     RenderBlock::willBeRemovedFromTree();
     89 
     90     updateListMarkerNumbers();
     91 }
     92 
     93 static bool isList(const Node* node)
     94 {
     95     return (node->hasTagName(ulTag) || node->hasTagName(olTag));
     96 }
     97 
     98 // Returns the enclosing list with respect to the DOM order.
     99 static Node* enclosingList(const RenderListItem* listItem)
    100 {
    101     Node* listItemNode = listItem->node();
    102     Node* firstNode = 0;
    103     // We use parentNode because the enclosing list could be a ShadowRoot that's not Element.
    104     for (Node* parent = listItemNode->parentNode(); parent; parent = parent->parentNode()) {
    105         if (isList(parent))
    106             return parent;
    107         if (!firstNode)
    108             firstNode = parent;
    109     }
    110 
    111     // If there's no actual <ul> or <ol> list element, then the first found
    112     // node acts as our list for purposes of determining what other list items
    113     // should be numbered as part of the same list.
    114     return firstNode;
    115 }
    116 
    117 // Returns the next list item with respect to the DOM order.
    118 static RenderListItem* nextListItem(const Node* listNode, const RenderListItem* item = 0)
    119 {
    120     if (!listNode)
    121         return 0;
    122 
    123     const Node* current = item ? item->node() : listNode;
    124     current = ElementTraversal::nextIncludingPseudo(current, listNode);
    125 
    126     while (current) {
    127         if (isList(current)) {
    128             // We've found a nested, independent list: nothing to do here.
    129             current = ElementTraversal::nextIncludingPseudoSkippingChildren(current, listNode);
    130             continue;
    131         }
    132 
    133         RenderObject* renderer = current->renderer();
    134         if (renderer && renderer->isListItem())
    135             return toRenderListItem(renderer);
    136 
    137         // FIXME: Can this be optimized to skip the children of the elements without a renderer?
    138         current = ElementTraversal::nextIncludingPseudo(current, listNode);
    139     }
    140 
    141     return 0;
    142 }
    143 
    144 // Returns the previous list item with respect to the DOM order.
    145 static RenderListItem* previousListItem(const Node* listNode, const RenderListItem* item)
    146 {
    147     Node* current = item->node();
    148     for (current = ElementTraversal::previousIncludingPseudo(current, listNode); current; current = ElementTraversal::previousIncludingPseudo(current, listNode)) {
    149         RenderObject* renderer = current->renderer();
    150         if (!renderer || (renderer && !renderer->isListItem()))
    151             continue;
    152         Node* otherList = enclosingList(toRenderListItem(renderer));
    153         // This item is part of our current list, so it's what we're looking for.
    154         if (listNode == otherList)
    155             return toRenderListItem(renderer);
    156         // We found ourself inside another list; lets skip the rest of it.
    157         // Use nextIncludingPseudo() here because the other list itself may actually
    158         // be a list item itself. We need to examine it, so we do this to counteract
    159         // the previousIncludingPseudo() that will be done by the loop.
    160         if (otherList)
    161             current = ElementTraversal::nextIncludingPseudo(otherList);
    162     }
    163     return 0;
    164 }
    165 
    166 void RenderListItem::updateItemValuesForOrderedList(const HTMLOListElement* listNode)
    167 {
    168     ASSERT(listNode);
    169 
    170     for (RenderListItem* listItem = nextListItem(listNode); listItem; listItem = nextListItem(listNode, listItem))
    171         listItem->updateValue();
    172 }
    173 
    174 unsigned RenderListItem::itemCountForOrderedList(const HTMLOListElement* listNode)
    175 {
    176     ASSERT(listNode);
    177 
    178     unsigned itemCount = 0;
    179     for (RenderListItem* listItem = nextListItem(listNode); listItem; listItem = nextListItem(listNode, listItem))
    180         itemCount++;
    181 
    182     return itemCount;
    183 }
    184 
    185 inline int RenderListItem::calcValue() const
    186 {
    187     if (m_hasExplicitValue)
    188         return m_explicitValue;
    189 
    190     Node* list = enclosingList(this);
    191     HTMLOListElement* oListElement = (list && list->hasTagName(olTag)) ? static_cast<HTMLOListElement*>(list) : 0;
    192     int valueStep = 1;
    193     if (oListElement && oListElement->isReversed())
    194         valueStep = -1;
    195 
    196     // FIXME: This recurses to a possible depth of the length of the list.
    197     // That's not good -- we need to change this to an iterative algorithm.
    198     if (RenderListItem* previousItem = previousListItem(list, this))
    199         return previousItem->value() + valueStep;
    200 
    201     if (oListElement)
    202         return oListElement->start();
    203 
    204     return 1;
    205 }
    206 
    207 void RenderListItem::updateValueNow() const
    208 {
    209     m_value = calcValue();
    210     m_isValueUpToDate = true;
    211 }
    212 
    213 bool RenderListItem::isEmpty() const
    214 {
    215     return lastChild() == m_marker;
    216 }
    217 
    218 static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker)
    219 {
    220     RenderObject* firstChild = curr->firstChild();
    221     if (!firstChild)
    222         return 0;
    223 
    224     bool inQuirksMode = curr->document()->inQuirksMode();
    225     for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) {
    226         if (currChild == marker)
    227             continue;
    228 
    229         if (currChild->isInline() && (!currChild->isRenderInline() || curr->generatesLineBoxesForInlineChild(currChild)))
    230             return curr;
    231 
    232         if (currChild->isFloating() || currChild->isOutOfFlowPositioned())
    233             continue;
    234 
    235         if (currChild->isTable() || !currChild->isRenderBlock() || (currChild->isBox() && toRenderBox(currChild)->isWritingModeRoot()))
    236             break;
    237 
    238         if (curr->isListItem() && inQuirksMode && currChild->node() &&
    239             (currChild->node()->hasTagName(ulTag)|| currChild->node()->hasTagName(olTag)))
    240             break;
    241 
    242         RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild), marker);
    243         if (lineBox)
    244             return lineBox;
    245     }
    246 
    247     return 0;
    248 }
    249 
    250 void RenderListItem::updateValue()
    251 {
    252     if (!m_hasExplicitValue) {
    253         m_isValueUpToDate = false;
    254         if (m_marker)
    255             m_marker->setNeedsLayoutAndPrefWidthsRecalc();
    256     }
    257 }
    258 
    259 static RenderObject* firstNonMarkerChild(RenderObject* parent)
    260 {
    261     RenderObject* result = parent->firstChild();
    262     while (result && result->isListMarker())
    263         result = result->nextSibling();
    264     return result;
    265 }
    266 
    267 void RenderListItem::updateMarkerLocation()
    268 {
    269     // Sanity check the location of our marker.
    270     if (m_marker) {
    271         RenderObject* markerParent = m_marker->parent();
    272         RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker);
    273         if (!lineBoxParent) {
    274             // If the marker is currently contained inside an anonymous box,
    275             // then we are the only item in that anonymous box (since no line box
    276             // parent was found).  It's ok to just leave the marker where it is
    277             // in this case.
    278             if (markerParent && markerParent->isAnonymousBlock())
    279                 lineBoxParent = markerParent;
    280             else
    281                 lineBoxParent = this;
    282         }
    283 
    284         if (markerParent != lineBoxParent || m_marker->preferredLogicalWidthsDirty()) {
    285             // Removing and adding the marker can trigger repainting in
    286             // containers other than ourselves, so we need to disable LayoutState.
    287             LayoutStateDisabler layoutStateDisabler(view());
    288             updateFirstLetter();
    289             m_marker->remove();
    290             if (markerParent)
    291                 markerParent->dirtyLinesFromChangedChild(m_marker);
    292             if (!lineBoxParent)
    293                 lineBoxParent = this;
    294             lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent));
    295             m_marker->updateMarginsAndContent();
    296             // If markerParent is an anonymous block that has lost all its children, destroy it.
    297             if (markerParent && markerParent->isAnonymousBlock() && !markerParent->firstChild() && !toRenderBlock(markerParent)->continuation())
    298                 markerParent->destroy();
    299         }
    300     }
    301 }
    302 
    303 void RenderListItem::layout()
    304 {
    305     StackStats::LayoutCheckPoint layoutCheckPoint;
    306     ASSERT(needsLayout());
    307 
    308     updateMarkerLocation();
    309     RenderBlock::layout();
    310 }
    311 
    312 void RenderListItem::addOverflowFromChildren()
    313 {
    314     RenderBlock::addOverflowFromChildren();
    315     positionListMarker();
    316 }
    317 
    318 void RenderListItem::positionListMarker()
    319 {
    320     if (m_marker && m_marker->parent()->isBox() && !m_marker->isInside() && m_marker->inlineBoxWrapper()) {
    321         LayoutUnit markerOldLogicalLeft = m_marker->logicalLeft();
    322         LayoutUnit blockOffset = 0;
    323         LayoutUnit lineOffset = 0;
    324         for (RenderBox* o = m_marker->parentBox(); o != this; o = o->parentBox()) {
    325             blockOffset += o->logicalTop();
    326             lineOffset += o->logicalLeft();
    327         }
    328 
    329         bool adjustOverflow = false;
    330         LayoutUnit markerLogicalLeft;
    331         RootInlineBox* root = m_marker->inlineBoxWrapper()->root();
    332         bool hitSelfPaintingLayer = false;
    333 
    334         RootInlineBox* rootBox = m_marker->inlineBoxWrapper()->root();
    335         LayoutUnit lineTop = rootBox->lineTop();
    336         LayoutUnit lineBottom = rootBox->lineBottom();
    337 
    338         // FIXME: Need to account for relative positioning in the layout overflow.
    339         if (style()->isLeftToRightDirection()) {
    340             LayoutUnit leftLineOffset = logicalLeftOffsetForLine(blockOffset, logicalLeftOffsetForLine(blockOffset, false), false);
    341             markerLogicalLeft = leftLineOffset - lineOffset - paddingStart() - borderStart() + m_marker->marginStart();
    342             m_marker->inlineBoxWrapper()->adjustLineDirectionPosition(markerLogicalLeft - markerOldLogicalLeft);
    343             for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) {
    344                 LayoutRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect(lineTop, lineBottom);
    345                 LayoutRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect(lineTop, lineBottom);
    346                 if (markerLogicalLeft < newLogicalVisualOverflowRect.x() && !hitSelfPaintingLayer) {
    347                     newLogicalVisualOverflowRect.setWidth(newLogicalVisualOverflowRect.maxX() - markerLogicalLeft);
    348                     newLogicalVisualOverflowRect.setX(markerLogicalLeft);
    349                     if (box == root)
    350                         adjustOverflow = true;
    351                 }
    352                 if (markerLogicalLeft < newLogicalLayoutOverflowRect.x()) {
    353                     newLogicalLayoutOverflowRect.setWidth(newLogicalLayoutOverflowRect.maxX() - markerLogicalLeft);
    354                     newLogicalLayoutOverflowRect.setX(markerLogicalLeft);
    355                     if (box == root)
    356                         adjustOverflow = true;
    357                 }
    358                 box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect, lineTop, lineBottom);
    359                 if (box->boxModelObject()->hasSelfPaintingLayer())
    360                     hitSelfPaintingLayer = true;
    361             }
    362         } else {
    363             LayoutUnit rightLineOffset = logicalRightOffsetForLine(blockOffset, logicalRightOffsetForLine(blockOffset, false), false);
    364             markerLogicalLeft = rightLineOffset - lineOffset + paddingStart() + borderStart() + m_marker->marginEnd();
    365             m_marker->inlineBoxWrapper()->adjustLineDirectionPosition(markerLogicalLeft - markerOldLogicalLeft);
    366             for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) {
    367                 LayoutRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect(lineTop, lineBottom);
    368                 LayoutRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect(lineTop, lineBottom);
    369                 if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalVisualOverflowRect.maxX() && !hitSelfPaintingLayer) {
    370                     newLogicalVisualOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - newLogicalVisualOverflowRect.x());
    371                     if (box == root)
    372                         adjustOverflow = true;
    373                 }
    374                 if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalLayoutOverflowRect.maxX()) {
    375                     newLogicalLayoutOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - newLogicalLayoutOverflowRect.x());
    376                     if (box == root)
    377                         adjustOverflow = true;
    378                 }
    379                 box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect, lineTop, lineBottom);
    380 
    381                 if (box->boxModelObject()->hasSelfPaintingLayer())
    382                     hitSelfPaintingLayer = true;
    383             }
    384         }
    385 
    386         if (adjustOverflow) {
    387             LayoutRect markerRect(markerLogicalLeft + lineOffset, blockOffset, m_marker->width(), m_marker->height());
    388             if (!style()->isHorizontalWritingMode())
    389                 markerRect = markerRect.transposedRect();
    390             RenderBox* o = m_marker;
    391             bool propagateVisualOverflow = true;
    392             bool propagateLayoutOverflow = true;
    393             do {
    394                 o = o->parentBox();
    395                 if (o->isRenderBlock()) {
    396                     if (propagateVisualOverflow)
    397                         toRenderBlock(o)->addContentsVisualOverflow(markerRect);
    398                     if (propagateLayoutOverflow)
    399                         toRenderBlock(o)->addLayoutOverflow(markerRect);
    400                 }
    401                 if (o->hasOverflowClip()) {
    402                     propagateLayoutOverflow = false;
    403                     propagateVisualOverflow = false;
    404                 }
    405                 if (o->hasSelfPaintingLayer())
    406                     propagateVisualOverflow = false;
    407                 markerRect.moveBy(-o->location());
    408             } while (o != this && propagateVisualOverflow && propagateLayoutOverflow);
    409         }
    410     }
    411 }
    412 
    413 void RenderListItem::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    414 {
    415     if (!logicalHeight() && hasOverflowClip())
    416         return;
    417 
    418     RenderBlock::paint(paintInfo, paintOffset);
    419 }
    420 
    421 const String& RenderListItem::markerText() const
    422 {
    423     if (m_marker)
    424         return m_marker->text();
    425     return nullAtom.string();
    426 }
    427 
    428 String RenderListItem::markerTextWithSuffix() const
    429 {
    430     if (!m_marker)
    431         return String();
    432 
    433     // Append the suffix for the marker in the right place depending
    434     // on the direction of the text (right-to-left or left-to-right).
    435 
    436     const String& markerText = m_marker->text();
    437     const String markerSuffix = m_marker->suffix();
    438     StringBuilder result;
    439 
    440     if (!m_marker->style()->isLeftToRightDirection())
    441         result.append(markerSuffix);
    442 
    443     result.append(markerText);
    444 
    445     if (m_marker->style()->isLeftToRightDirection())
    446         result.append(markerSuffix);
    447 
    448     return result.toString();
    449 }
    450 
    451 void RenderListItem::explicitValueChanged()
    452 {
    453     if (m_marker)
    454         m_marker->setNeedsLayoutAndPrefWidthsRecalc();
    455     Node* listNode = enclosingList(this);
    456     for (RenderListItem* item = this; item; item = nextListItem(listNode, item))
    457         item->updateValue();
    458 }
    459 
    460 void RenderListItem::setExplicitValue(int value)
    461 {
    462     ASSERT(node());
    463 
    464     if (m_hasExplicitValue && m_explicitValue == value)
    465         return;
    466     m_explicitValue = value;
    467     m_value = value;
    468     m_hasExplicitValue = true;
    469     explicitValueChanged();
    470 }
    471 
    472 void RenderListItem::clearExplicitValue()
    473 {
    474     ASSERT(node());
    475 
    476     if (!m_hasExplicitValue)
    477         return;
    478     m_hasExplicitValue = false;
    479     m_isValueUpToDate = false;
    480     explicitValueChanged();
    481 }
    482 
    483 static RenderListItem* previousOrNextItem(bool isListReversed, Node* list, RenderListItem* item)
    484 {
    485     return isListReversed ? previousListItem(list, item) : nextListItem(list, item);
    486 }
    487 
    488 void RenderListItem::updateListMarkerNumbers()
    489 {
    490     Node* listNode = enclosingList(this);
    491     // The list node can be the shadow root which has no renderer.
    492     ASSERT(listNode);
    493     if (!listNode)
    494         return;
    495 
    496     bool isListReversed = false;
    497     HTMLOListElement* oListElement = (listNode && listNode->hasTagName(olTag)) ? static_cast<HTMLOListElement*>(listNode) : 0;
    498     if (oListElement) {
    499         oListElement->itemCountChanged();
    500         isListReversed = oListElement->isReversed();
    501     }
    502     for (RenderListItem* item = previousOrNextItem(isListReversed, listNode, this); item; item = previousOrNextItem(isListReversed, listNode, item)) {
    503         if (!item->m_isValueUpToDate) {
    504             // If an item has been marked for update before, we can safely
    505             // assume that all the following ones have too.
    506             // This gives us the opportunity to stop here and avoid
    507             // marking the same nodes again.
    508             break;
    509         }
    510         item->updateValue();
    511     }
    512 }
    513 
    514 } // namespace WebCore
    515