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