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