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