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
createAnonymous(Document * document,PassRefPtr<NamedFlow> namedFlow)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
RenderNamedFlowThread(PassRefPtr<NamedFlow> namedFlow)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
~RenderNamedFlowThread()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
renderName() const73 const char* RenderNamedFlowThread::renderName() const
74 {
75 return "RenderNamedFlowThread";
76 }
77
clearContentNodes()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
updateWritingMode()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
nextRendererForNode(Node * node) const107 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
previousRendererForNode(Node * node) const123 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
addFlowChild(RenderObject * newChild)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
removeFlowChild(RenderObject * child)164 void RenderNamedFlowThread::removeFlowChild(RenderObject* child)
165 {
166 m_flowThreadChildList.remove(child);
167 }
168
dependsOn(RenderNamedFlowThread * otherRenderFlowThread) const169 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.
compareRenderRegions(const RenderRegion * firstRegion,const RenderRegion * secondRegion)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.
addRegionToList(RenderRegionList & regionList,RenderRegion * renderRegion)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
addRegionToNamedFlowThread(RenderRegion * renderRegion)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
addRegionToThread(RenderRegion * renderRegion)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
removeRegionFromThread(RenderRegion * renderRegion)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
regionChangedWritingMode(RenderRegion * region)318 void RenderNamedFlowThread::regionChangedWritingMode(RenderRegion* region)
319 {
320 if (m_regionList.first() == region)
321 updateWritingMode();
322 }
323
computeOversetStateForRegions(LayoutUnit oldClientAfterEdge)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
checkInvalidRegions()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
addDependencyOnFlowThread(RenderNamedFlowThread * otherFlowThread)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
removeDependencyOnFlowThread(RenderNamedFlowThread * otherFlowThread)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
pushDependencies(RenderNamedFlowThreadList & list)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.
registerNamedFlowContentNode(Node * contentNode)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
unregisterNamedFlowContentNode(Node * contentNode)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
flowThreadName() const477 const AtomicString& RenderNamedFlowThread::flowThreadName() const
478 {
479 return m_namedFlow->name();
480 }
481
isChildAllowed(RenderObject * child,RenderStyle * style) const482 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
dispatchRegionLayoutUpdateEvent()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
dispatchRegionOversetChangeEvent()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
regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread> *)513 void RenderNamedFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread>*)
514 {
515 ASSERT(m_namedFlow);
516
517 m_namedFlow->dispatchRegionLayoutUpdateEvent();
518 }
519
regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread> *)520 void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>*)
521 {
522 ASSERT(m_namedFlow);
523
524 m_namedFlow->dispatchRegionOversetChangeEvent();
525 }
526
setMarkForDestruction()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
resetMarkForDestruction()537 void RenderNamedFlowThread::resetMarkForDestruction()
538 {
539 if (m_namedFlow->flowState() == NamedFlow::FlowStateCreated)
540 return;
541
542 m_namedFlow->setRenderer(this);
543 }
544
isMarkedForDestruction() const545 bool RenderNamedFlowThread::isMarkedForDestruction() const
546 {
547 // Flow threads in the "NULL" state can be destroyed.
548 return m_namedFlow->flowState() == NamedFlow::FlowStateNull;
549 }
550
isContainedInNodes(Vector<Node * > others,Node * node)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
boxIntersectsRegion(LayoutUnit logicalTopForBox,LayoutUnit logicalBottomForBox,LayoutUnit logicalTopForRegion,LayoutUnit logicalBottomForRegion)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.
nextNodeInsideContentNode(const Node & currNode,const Node * contentNode)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
getRanges(Vector<RefPtr<Range>> & rangeObjects,const RenderRegion * region) const580 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