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