1 /* 2 * Copyright (C) 2012 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "core/rendering/RenderNamedFlowThread.h" 28 29 #include "RuntimeEnabledFeatures.h" 30 #include "bindings/v8/ExceptionStatePlaceholder.h" 31 #include "core/dom/NamedFlow.h" 32 #include "core/dom/NodeRenderingTraversal.h" 33 #include "core/dom/NodeTraversal.h" 34 #include "core/dom/Position.h" 35 #include "core/dom/Range.h" 36 #include "core/dom/Text.h" 37 #include "core/inspector/InspectorInstrumentation.h" 38 #include "core/rendering/FlowThreadController.h" 39 #include "core/rendering/InlineTextBox.h" 40 #include "core/rendering/RenderInline.h" 41 #include "core/rendering/RenderRegion.h" 42 #include "core/rendering/RenderText.h" 43 #include "core/rendering/RenderView.h" 44 45 namespace WebCore { 46 47 RenderNamedFlowThread* RenderNamedFlowThread::createAnonymous(Document* document, PassRefPtr<NamedFlow> namedFlow) 48 { 49 ASSERT(RuntimeEnabledFeatures::cssRegionsEnabled()); 50 RenderNamedFlowThread* renderer = new RenderNamedFlowThread(namedFlow); 51 renderer->setDocumentForAnonymous(document); 52 return renderer; 53 } 54 55 RenderNamedFlowThread::RenderNamedFlowThread(PassRefPtr<NamedFlow> namedFlow) 56 : m_overset(true) 57 , m_namedFlow(namedFlow) 58 , m_regionLayoutUpdateEventTimer(this, &RenderNamedFlowThread::regionLayoutUpdateEventTimerFired) 59 , m_regionOversetChangeEventTimer(this, &RenderNamedFlowThread::regionOversetChangeEventTimerFired) 60 { 61 } 62 63 RenderNamedFlowThread::~RenderNamedFlowThread() 64 { 65 // The flow thread can be destroyed without unregistering the content nodes if the document is destroyed. 66 // This can lead to problems because the nodes are still marked as belonging to a flow thread. 67 clearContentNodes(); 68 69 // Also leave the NamedFlow object in a consistent state by calling mark for destruction. 70 setMarkForDestruction(); 71 } 72 73 const char* RenderNamedFlowThread::renderName() const 74 { 75 return "RenderNamedFlowThread"; 76 } 77 78 void RenderNamedFlowThread::clearContentNodes() 79 { 80 for (NamedFlowContentNodes::iterator it = m_contentNodes.begin(); it != m_contentNodes.end(); ++it) { 81 Node* contentNode = *it; 82 83 ASSERT(contentNode && contentNode->isElementNode()); 84 ASSERT(contentNode->inNamedFlow()); 85 ASSERT(contentNode->document() == document()); 86 87 contentNode->clearInNamedFlow(); 88 } 89 90 m_contentNodes.clear(); 91 } 92 93 void RenderNamedFlowThread::updateWritingMode() 94 { 95 if (RenderRegion* firstRegion = m_regionList.first()) { 96 if (style()->writingMode() != firstRegion->style()->writingMode()) { 97 // The first region defines the principal writing mode for the entire flow. 98 RefPtr<RenderStyle> newStyle = RenderStyle::clone(style()); 99 newStyle->setWritingMode(firstRegion->style()->writingMode()); 100 setStyle(newStyle); 101 } 102 } 103 } 104 105 RenderObject* RenderNamedFlowThread::nextRendererForNode(Node* node) const 106 { 107 FlowThreadChildList::const_iterator it = m_flowThreadChildList.begin(); 108 FlowThreadChildList::const_iterator end = m_flowThreadChildList.end(); 109 110 for (; it != end; ++it) { 111 RenderObject* child = *it; 112 ASSERT(child->node()); 113 unsigned short position = node->compareDocumentPosition(child->node()); 114 if (position & Node::DOCUMENT_POSITION_FOLLOWING) 115 return child; 116 } 117 118 return 0; 119 } 120 121 RenderObject* RenderNamedFlowThread::previousRendererForNode(Node* node) const 122 { 123 if (m_flowThreadChildList.isEmpty()) 124 return 0; 125 126 FlowThreadChildList::const_iterator begin = m_flowThreadChildList.begin(); 127 FlowThreadChildList::const_iterator end = m_flowThreadChildList.end(); 128 FlowThreadChildList::const_iterator it = end; 129 130 do { 131 --it; 132 RenderObject* child = *it; 133 ASSERT(child->node()); 134 unsigned short position = node->compareDocumentPosition(child->node()); 135 if (position & Node::DOCUMENT_POSITION_PRECEDING) 136 return child; 137 } while (it != begin); 138 139 return 0; 140 } 141 142 void RenderNamedFlowThread::addFlowChild(RenderObject* newChild) 143 { 144 // The child list is used to sort the flow thread's children render objects 145 // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM. 146 147 Node* childNode = newChild->node(); 148 149 // Do not add anonymous objects. 150 if (!childNode) 151 return; 152 153 ASSERT(childNode->isElementNode()); 154 155 RenderObject* beforeChild = nextRendererForNode(childNode); 156 if (beforeChild) 157 m_flowThreadChildList.insertBefore(beforeChild, newChild); 158 else 159 m_flowThreadChildList.add(newChild); 160 } 161 162 void RenderNamedFlowThread::removeFlowChild(RenderObject* child) 163 { 164 m_flowThreadChildList.remove(child); 165 } 166 167 bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const 168 { 169 if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread)) 170 return true; 171 172 // Recursively traverse the m_layoutBeforeThreadsSet. 173 RenderNamedFlowThreadCountedSet::const_iterator iterator = m_layoutBeforeThreadsSet.begin(); 174 RenderNamedFlowThreadCountedSet::const_iterator end = m_layoutBeforeThreadsSet.end(); 175 for (; iterator != end; ++iterator) { 176 const RenderNamedFlowThread* beforeFlowThread = (*iterator).key; 177 if (beforeFlowThread->dependsOn(otherRenderFlowThread)) 178 return true; 179 } 180 181 return false; 182 } 183 184 // Compare two regions to determine in which one the content should flow first. 185 // The function returns true if the first passed region is "less" than the second passed region. 186 // If the first region appears before second region in DOM, 187 // the first region is "less" than the second region. 188 // If the first region is "less" than the second region, the first region receives content before second region. 189 static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion) 190 { 191 ASSERT(firstRegion); 192 ASSERT(secondRegion); 193 194 ASSERT(firstRegion->generatingNode()); 195 ASSERT(secondRegion->generatingNode()); 196 197 // If the regions belong to different nodes, compare their position in the DOM. 198 if (firstRegion->generatingNode() != secondRegion->generatingNode()) { 199 unsigned short position = firstRegion->generatingNode()->compareDocumentPosition(secondRegion->generatingNode()); 200 201 // If the second region is contained in the first one, the first region is "less" if it's :before. 202 if (position & Node::DOCUMENT_POSITION_CONTAINED_BY) { 203 ASSERT(secondRegion->style()->styleType() == NOPSEUDO); 204 return firstRegion->style()->styleType() == BEFORE; 205 } 206 207 // If the second region contains the first region, the first region is "less" if the second is :after. 208 if (position & Node::DOCUMENT_POSITION_CONTAINS) { 209 ASSERT(firstRegion->style()->styleType() == NOPSEUDO); 210 return secondRegion->style()->styleType() == AFTER; 211 } 212 213 return (position & Node::DOCUMENT_POSITION_FOLLOWING); 214 } 215 216 // FIXME: Currently it's not possible for an element to be both a region and have pseudo-children. The case is covered anyway. 217 switch (firstRegion->style()->styleType()) { 218 case BEFORE: 219 // The second region can be the node or the after pseudo-element (before is smaller than any of those). 220 return true; 221 case AFTER: 222 // The second region can be the node or the before pseudo-element (after is greater than any of those). 223 return false; 224 case NOPSEUDO: 225 // The second region can either be the before or the after pseudo-element (the node is only smaller than the after pseudo-element). 226 return firstRegion->style()->styleType() == AFTER; 227 default: 228 break; 229 } 230 231 ASSERT_NOT_REACHED(); 232 return true; 233 } 234 235 // This helper function adds a region to a list preserving the order property of the list. 236 static void addRegionToList(RenderRegionList& regionList, RenderRegion* renderRegion) 237 { 238 if (regionList.isEmpty()) 239 regionList.add(renderRegion); 240 else { 241 // Find the first region "greater" than renderRegion. 242 RenderRegionList::iterator it = regionList.begin(); 243 while (it != regionList.end() && !compareRenderRegions(renderRegion, *it)) 244 ++it; 245 regionList.insertBefore(it, renderRegion); 246 } 247 } 248 249 void RenderNamedFlowThread::addRegionToNamedFlowThread(RenderRegion* renderRegion) 250 { 251 ASSERT(renderRegion); 252 ASSERT(!renderRegion->isValid()); 253 254 if (renderRegion->parentNamedFlowThread()) 255 addDependencyOnFlowThread(renderRegion->parentNamedFlowThread()); 256 257 renderRegion->setIsValid(true); 258 addRegionToList(m_regionList, renderRegion); 259 260 if (m_regionList.first() == renderRegion) 261 updateWritingMode(); 262 } 263 264 void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion) 265 { 266 ASSERT(renderRegion); 267 ASSERT(!renderRegion->isValid()); 268 269 resetMarkForDestruction(); 270 271 if (renderRegion->parentNamedFlowThread() && renderRegion->parentNamedFlowThread()->dependsOn(this)) { 272 // The order of invalid regions is irrelevant. 273 m_invalidRegionList.add(renderRegion); 274 // Register ourself to get a notification when the state changes. 275 renderRegion->parentNamedFlowThread()->m_observerThreadsSet.add(this); 276 return; 277 } 278 279 addRegionToNamedFlowThread(renderRegion); 280 281 invalidateRegions(); 282 } 283 284 void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion) 285 { 286 ASSERT(renderRegion); 287 288 if (renderRegion->parentNamedFlowThread()) { 289 if (!renderRegion->isValid()) { 290 ASSERT(m_invalidRegionList.contains(renderRegion)); 291 m_invalidRegionList.remove(renderRegion); 292 renderRegion->parentNamedFlowThread()->m_observerThreadsSet.remove(this); 293 // No need to invalidate the regions rectangles. The removed region 294 // was not taken into account. Just return here. 295 return; 296 } 297 removeDependencyOnFlowThread(renderRegion->parentNamedFlowThread()); 298 } 299 300 ASSERT(m_regionList.contains(renderRegion)); 301 bool wasFirst = m_regionList.first() == renderRegion; 302 m_regionList.remove(renderRegion); 303 304 if (canBeDestroyed()) 305 setMarkForDestruction(); 306 307 // After removing all the regions in the flow the following layout needs to dispatch the regionLayoutUpdate event 308 if (m_regionList.isEmpty()) 309 setDispatchRegionLayoutUpdateEvent(true); 310 else if (wasFirst) 311 updateWritingMode(); 312 313 invalidateRegions(); 314 } 315 316 void RenderNamedFlowThread::regionChangedWritingMode(RenderRegion* region) 317 { 318 if (m_regionList.first() == region) 319 updateWritingMode(); 320 } 321 322 void RenderNamedFlowThread::computeOversetStateForRegions(LayoutUnit oldClientAfterEdge) 323 { 324 LayoutUnit height = oldClientAfterEdge; 325 326 // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread) 327 // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow 328 // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height). 329 // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region) 330 if (hasRenderOverflow() 331 && ( (isHorizontalWritingMode() && visualOverflowRect().maxY() > clientBoxRect().maxY()) 332 || (!isHorizontalWritingMode() && visualOverflowRect().maxX() > clientBoxRect().maxX()))) 333 height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX(); 334 335 RenderRegion* lastReg = lastRegion(); 336 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 337 RenderRegion* region = *iter; 338 LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x()); 339 LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().maxY() : region->flowThreadPortionRect().maxX()); 340 RegionOversetState previousState = region->regionOversetState(); 341 RegionOversetState state = RegionFit; 342 if (flowMin <= 0) 343 state = RegionEmpty; 344 if (flowMax > 0 && region == lastReg) 345 state = RegionOverset; 346 region->setRegionOversetState(state); 347 // determine whether the NamedFlow object should dispatch a regionLayoutUpdate event 348 // FIXME: currently it cannot determine whether a region whose regionOverset state remained either "fit" or "overset" has actually 349 // changed, so it just assumes that the NamedFlow should dispatch the event 350 if (previousState != state 351 || state == RegionFit 352 || state == RegionOverset) 353 setDispatchRegionLayoutUpdateEvent(true); 354 355 if (previousState != state) 356 setDispatchRegionOversetChangeEvent(true); 357 } 358 359 // If the number of regions has changed since we last computed the overset property, schedule the regionOversetChange event. 360 if (previousRegionCountChanged()) { 361 setDispatchRegionOversetChangeEvent(true); 362 updatePreviousRegionCount(); 363 } 364 365 // With the regions overflow state computed we can also set the overset flag for the named flow. 366 // If there are no valid regions in the chain, overset is true. 367 m_overset = lastReg ? lastReg->regionOversetState() == RegionOverset : true; 368 } 369 370 void RenderNamedFlowThread::checkInvalidRegions() 371 { 372 Vector<RenderRegion*> newValidRegions; 373 for (RenderRegionList::iterator iter = m_invalidRegionList.begin(); iter != m_invalidRegionList.end(); ++iter) { 374 RenderRegion* region = *iter; 375 // The only reason a region would be invalid is because it has a parent flow thread. 376 ASSERT(!region->isValid() && region->parentNamedFlowThread()); 377 if (region->parentNamedFlowThread()->dependsOn(this)) 378 continue; 379 380 newValidRegions.append(region); 381 } 382 383 for (Vector<RenderRegion*>::iterator iter = newValidRegions.begin(); iter != newValidRegions.end(); ++iter) { 384 RenderRegion* region = *iter; 385 m_invalidRegionList.remove(region); 386 region->parentNamedFlowThread()->m_observerThreadsSet.remove(this); 387 addRegionToNamedFlowThread(region); 388 } 389 390 if (!newValidRegions.isEmpty()) 391 invalidateRegions(); 392 393 if (m_observerThreadsSet.isEmpty()) 394 return; 395 396 // Notify all the flow threads that were dependent on this flow. 397 398 // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions. 399 Vector<RenderNamedFlowThread*> observers; 400 copyToVector(m_observerThreadsSet, observers); 401 402 for (size_t i = 0; i < observers.size(); ++i) { 403 RenderNamedFlowThread* flowThread = observers.at(i); 404 flowThread->checkInvalidRegions(); 405 } 406 } 407 408 void RenderNamedFlowThread::addDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread) 409 { 410 RenderNamedFlowThreadCountedSet::AddResult result = m_layoutBeforeThreadsSet.add(otherFlowThread); 411 if (result.isNewEntry) { 412 // This is the first time we see this dependency. Make sure we recalculate all the dependencies. 413 view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true); 414 } 415 } 416 417 void RenderNamedFlowThread::removeDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread) 418 { 419 bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread); 420 if (removed) { 421 checkInvalidRegions(); 422 view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true); 423 } 424 } 425 426 void RenderNamedFlowThread::pushDependencies(RenderNamedFlowThreadList& list) 427 { 428 for (RenderNamedFlowThreadCountedSet::iterator iter = m_layoutBeforeThreadsSet.begin(); iter != m_layoutBeforeThreadsSet.end(); ++iter) { 429 RenderNamedFlowThread* flowThread = (*iter).key; 430 if (list.contains(flowThread)) 431 continue; 432 flowThread->pushDependencies(list); 433 list.add(flowThread); 434 } 435 } 436 437 // The content nodes list contains those nodes with -webkit-flow-into: flow. 438 // An element with display:none should also be listed among those nodes. 439 // The list of nodes is ordered. 440 void RenderNamedFlowThread::registerNamedFlowContentNode(Node* contentNode) 441 { 442 ASSERT(contentNode && contentNode->isElementNode()); 443 ASSERT(contentNode->document() == document()); 444 445 contentNode->setInNamedFlow(); 446 447 resetMarkForDestruction(); 448 449 // Find the first content node following the new content node. 450 for (NamedFlowContentNodes::iterator it = m_contentNodes.begin(); it != m_contentNodes.end(); ++it) { 451 Node* node = *it; 452 unsigned short position = contentNode->compareDocumentPosition(node); 453 if (position & Node::DOCUMENT_POSITION_FOLLOWING) { 454 m_contentNodes.insertBefore(node, contentNode); 455 return; 456 } 457 } 458 m_contentNodes.add(contentNode); 459 } 460 461 void RenderNamedFlowThread::unregisterNamedFlowContentNode(Node* contentNode) 462 { 463 ASSERT(contentNode && contentNode->isElementNode()); 464 ASSERT(m_contentNodes.contains(contentNode)); 465 ASSERT(contentNode->inNamedFlow()); 466 ASSERT(contentNode->document() == document()); 467 468 contentNode->clearInNamedFlow(); 469 m_contentNodes.remove(contentNode); 470 471 if (canBeDestroyed()) 472 setMarkForDestruction(); 473 } 474 475 const AtomicString& RenderNamedFlowThread::flowThreadName() const 476 { 477 return m_namedFlow->name(); 478 } 479 480 bool RenderNamedFlowThread::isChildAllowed(RenderObject* child, RenderStyle* style) const 481 { 482 if (!child->node()) 483 return true; 484 485 ASSERT(child->node()->isElementNode()); 486 Node* originalParent = NodeRenderingTraversal::parent(child->node()); 487 if (!originalParent || !originalParent->renderer()) 488 return true; 489 490 return originalParent->renderer()->isChildAllowed(child, style); 491 } 492 493 void RenderNamedFlowThread::dispatchRegionLayoutUpdateEvent() 494 { 495 RenderFlowThread::dispatchRegionLayoutUpdateEvent(); 496 InspectorInstrumentation::didUpdateRegionLayout(document(), m_namedFlow.get()); 497 498 if (!m_regionLayoutUpdateEventTimer.isActive() && m_namedFlow->hasEventListeners()) 499 m_regionLayoutUpdateEventTimer.startOneShot(0); 500 } 501 502 void RenderNamedFlowThread::dispatchRegionOversetChangeEvent() 503 { 504 RenderFlowThread::dispatchRegionOversetChangeEvent(); 505 InspectorInstrumentation::didChangeRegionOverset(document(), m_namedFlow.get()); 506 507 if (!m_regionOversetChangeEventTimer.isActive() && m_namedFlow->hasEventListeners()) 508 m_regionOversetChangeEventTimer.startOneShot(0); 509 } 510 511 void RenderNamedFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread>*) 512 { 513 ASSERT(m_namedFlow); 514 515 m_namedFlow->dispatchRegionLayoutUpdateEvent(); 516 } 517 518 void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>*) 519 { 520 ASSERT(m_namedFlow); 521 522 m_namedFlow->dispatchRegionOversetChangeEvent(); 523 } 524 525 void RenderNamedFlowThread::setMarkForDestruction() 526 { 527 if (m_namedFlow->flowState() == NamedFlow::FlowStateNull) 528 return; 529 530 m_namedFlow->setRenderer(0); 531 // After this call ends, the renderer can be safely destroyed. 532 // The NamedFlow object may outlive its renderer if it's referenced from a script and may be reatached to one if the named flow is recreated in the stylesheet. 533 } 534 535 void RenderNamedFlowThread::resetMarkForDestruction() 536 { 537 if (m_namedFlow->flowState() == NamedFlow::FlowStateCreated) 538 return; 539 540 m_namedFlow->setRenderer(this); 541 } 542 543 bool RenderNamedFlowThread::isMarkedForDestruction() const 544 { 545 // Flow threads in the "NULL" state can be destroyed. 546 return m_namedFlow->flowState() == NamedFlow::FlowStateNull; 547 } 548 549 static bool isContainedInNodes(Vector<Node*> others, Node* node) 550 { 551 for (size_t i = 0; i < others.size(); i++) { 552 Node* other = others.at(i); 553 if (other->contains(node)) 554 return true; 555 } 556 return false; 557 } 558 559 static bool boxIntersectsRegion(LayoutUnit logicalTopForBox, LayoutUnit logicalBottomForBox, LayoutUnit logicalTopForRegion, LayoutUnit logicalBottomForRegion) 560 { 561 bool regionIsEmpty = logicalBottomForRegion != LayoutUnit::max() && logicalTopForRegion != LayoutUnit::min() 562 && (logicalBottomForRegion - logicalTopForRegion) <= 0; 563 return (logicalBottomForBox - logicalTopForBox) > 0 564 && !regionIsEmpty 565 && logicalTopForBox < logicalBottomForRegion && logicalTopForRegion < logicalBottomForBox; 566 } 567 568 void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, const RenderRegion* region) const 569 { 570 LayoutUnit logicalTopForRegion; 571 LayoutUnit logicalBottomForRegion; 572 573 // extend the first region top to contain everything up to its logical height 574 if (region->isFirstRegion()) 575 logicalTopForRegion = LayoutUnit::min(); 576 else 577 logicalTopForRegion = region->logicalTopForFlowThreadContent(); 578 579 // extend the last region to contain everything above its y() 580 if (region->isLastRegion()) 581 logicalBottomForRegion = LayoutUnit::max(); 582 else 583 logicalBottomForRegion = region->logicalBottomForFlowThreadContent(); 584 585 Vector<Node*> nodes; 586 // eliminate the contentNodes that are descendants of other contentNodes 587 for (NamedFlowContentNodes::const_iterator it = contentNodes().begin(); it != contentNodes().end(); ++it) { 588 Node* node = *it; 589 if (!isContainedInNodes(nodes, node)) 590 nodes.append(node); 591 } 592 593 for (size_t i = 0; i < nodes.size(); i++) { 594 Node* contentNode = nodes.at(i); 595 if (!contentNode->renderer()) 596 continue; 597 598 RefPtr<Range> range = Range::create(contentNode->document()); 599 bool foundStartPosition = false; 600 bool startsAboveRegion = true; 601 bool endsBelowRegion = true; 602 bool skipOverOutsideNodes = false; 603 Node* lastEndNode = 0; 604 605 for (Node* node = contentNode; node; node = NodeTraversal::next(node, contentNode)) { 606 RenderObject* renderer = node->renderer(); 607 if (!renderer) 608 continue; 609 610 LayoutRect boundingBox; 611 if (renderer->isRenderInline()) 612 boundingBox = toRenderInline(renderer)->linesBoundingBox(); 613 else if (renderer->isText()) 614 boundingBox = toRenderText(renderer)->linesBoundingBox(); 615 else { 616 boundingBox = toRenderBox(renderer)->frameRect(); 617 if (toRenderBox(renderer)->isRelPositioned()) 618 boundingBox.move(toRenderBox(renderer)->relativePositionLogicalOffset()); 619 } 620 621 LayoutUnit offsetTop = renderer->containingBlock()->offsetFromLogicalTopOfFirstPage(); 622 const LayoutPoint logicalOffsetFromTop(isHorizontalWritingMode() ? LayoutUnit() : offsetTop, 623 isHorizontalWritingMode() ? offsetTop : LayoutUnit()); 624 625 boundingBox.moveBy(logicalOffsetFromTop); 626 627 LayoutUnit logicalTopForRenderer = region->logicalTopOfFlowThreadContentRect(boundingBox); 628 LayoutUnit logicalBottomForRenderer = region->logicalBottomOfFlowThreadContentRect(boundingBox); 629 630 // if the bounding box of the current element doesn't intersect the region box 631 // close the current range only if the start element began inside the region, 632 // otherwise just move the start position after this node and keep skipping them until we found a proper start position. 633 if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) { 634 if (foundStartPosition) { 635 if (!startsAboveRegion) { 636 if (range->intersectsNode(node, IGNORE_EXCEPTION)) 637 range->setEndBefore(node, IGNORE_EXCEPTION); 638 rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION)); 639 range = Range::create(contentNode->document()); 640 startsAboveRegion = true; 641 } else 642 skipOverOutsideNodes = true; 643 } 644 if (skipOverOutsideNodes) 645 range->setStartAfter(node, IGNORE_EXCEPTION); 646 foundStartPosition = false; 647 continue; 648 } 649 650 // start position 651 if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) { 652 if (renderer->isText()) { // Text crosses region top 653 // for Text elements, just find the last textbox that is contained inside the region and use its start() offset as start position 654 RenderText* textRenderer = toRenderText(renderer); 655 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { 656 if (offsetTop + box->logicalBottom() < logicalTopForRegion) 657 continue; 658 range->setStart(Position(toText(node), box->start())); 659 startsAboveRegion = false; 660 break; 661 } 662 } else { // node crosses region top 663 // for all elements, except Text, just set the start position to be before their children 664 startsAboveRegion = true; 665 range->setStart(Position(node, Position::PositionIsBeforeChildren)); 666 } 667 } else { // node starts inside region 668 // for elements that start inside the region, set the start position to be before them. If we found one, we will just skip the others until 669 // the range is closed. 670 if (startsAboveRegion) { 671 startsAboveRegion = false; 672 range->setStartBefore(node, IGNORE_EXCEPTION); 673 } 674 } 675 skipOverOutsideNodes = false; 676 foundStartPosition = true; 677 678 // end position 679 if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) { 680 // for Text elements, just find just find the last textbox that is contained inside the region and use its start()+len() offset as end position 681 if (renderer->isText()) { // Text crosses region bottom 682 RenderText* textRenderer = toRenderText(renderer); 683 InlineTextBox* lastBox = 0; 684 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { 685 if ((offsetTop + box->logicalTop()) < logicalBottomForRegion) { 686 lastBox = box; 687 continue; 688 } 689 ASSERT(lastBox); 690 if (lastBox) 691 range->setEnd(Position(toText(node), lastBox->start() + lastBox->len())); 692 break; 693 } 694 endsBelowRegion = false; 695 lastEndNode = node; 696 } else { // node crosses region bottom 697 // for all elements, except Text, just set the start position to be after their children 698 range->setEnd(Position(node, Position::PositionIsAfterChildren)); 699 endsBelowRegion = true; 700 lastEndNode = node; 701 } 702 } else { // node ends inside region 703 // for elements that ends inside the region, set the end position to be after them 704 // allow this end position to be changed only by other elements that are not descendants of the current end node 705 if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) { 706 range->setEndAfter(node, IGNORE_EXCEPTION); 707 endsBelowRegion = false; 708 lastEndNode = node; 709 } 710 } 711 } 712 if (foundStartPosition || skipOverOutsideNodes) 713 rangeObjects.append(range); 714 } 715 } 716 717 } 718