2009-01-31 David Hyatt <hyatt@apple.com>
[WebKit-https.git] / WebCore / rendering / RenderContainer.cpp
1 /**
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2000 Dirk Mueller (mueller@kde.org)
5  *           (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
6  * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
7  * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25
26 #include "config.h"
27 #include "RenderContainer.h"
28
29 #include "AXObjectCache.h"
30 #include "Document.h"
31 #include "RenderCounter.h"
32 #include "RenderImageGeneratedContent.h"
33 #include "RenderInline.h"
34 #include "RenderLayer.h"
35 #include "RenderListItem.h"
36 #include "RenderTable.h"
37 #include "RenderTextFragment.h"
38 #include "RenderView.h"
39 #include "htmlediting.h"
40
41 namespace WebCore {
42
43 RenderContainer::RenderContainer(Node* node)
44     : RenderBox(node)
45 {
46 }
47
48 RenderContainer::~RenderContainer()
49 {
50 }
51
52 static void updateListMarkerNumbers(RenderObject* child)
53 {
54     for (RenderObject* r = child; r; r = r->nextSibling())
55         if (r->isListItem())
56             static_cast<RenderListItem*>(r)->updateValue();
57 }
58
59 void RenderContainer::addChild(RenderObject* newChild, RenderObject* beforeChild)
60 {
61     bool needsTable = false;
62
63     if (newChild->isListItem())
64         updateListMarkerNumbers(beforeChild ? beforeChild : children()->lastChild());
65     else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP)
66         needsTable = !isTable();
67     else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION)
68         needsTable = !isTable();
69     else if (newChild->isTableSection())
70         needsTable = !isTable();
71     else if (newChild->isTableRow())
72         needsTable = !isTableSection();
73     else if (newChild->isTableCell()) {
74         needsTable = !isTableRow();
75         // I'm not 100% sure this is the best way to fix this, but without this
76         // change we recurse infinitely when trying to render the CSS2 test page:
77         // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html.
78         // See Radar 2925291.
79         if (needsTable && isTableCell() && !children()->firstChild() && !newChild->isTableCell())
80             needsTable = false;
81     }
82
83     if (needsTable) {
84         RenderTable* table;
85         RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children()->lastChild();
86         if (afterChild && afterChild->isAnonymous() && afterChild->isTable())
87             table = static_cast<RenderTable*>(afterChild);
88         else {
89             table = new (renderArena()) RenderTable(document() /* is anonymous */);
90             RefPtr<RenderStyle> newStyle = RenderStyle::create();
91             newStyle->inheritFrom(style());
92             newStyle->setDisplay(TABLE);
93             table->setStyle(newStyle.release());
94             addChild(table, beforeChild);
95         }
96         table->addChild(newChild);
97     } else {
98         // just add it...
99         insertChildNode(newChild, beforeChild);
100     }
101     
102     if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) {
103         RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText();
104         if (textToTransform)
105             toRenderText(newChild)->setText(textToTransform.release(), true);
106     }
107 }
108
109 RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild, bool fullRemove)
110 {
111     ASSERT(oldChild->parent() == this);
112
113     // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or
114     // that a positioned child got yanked).  We also repaint, so that the area exposed when the child
115     // disappears gets repainted properly.
116     if (!documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) {
117         oldChild->setNeedsLayoutAndPrefWidthsRecalc();
118         oldChild->repaint();
119     }
120         
121     // If we have a line box wrapper, delete it.
122     oldChild->deleteLineBoxWrapper();
123
124     if (!documentBeingDestroyed() && fullRemove) {
125         // if we remove visible child from an invisible parent, we don't know the layer visibility any more
126         RenderLayer* layer = 0;
127         if (m_style->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) {
128             layer = enclosingLayer();
129             layer->dirtyVisibleContentStatus();
130         }
131
132          // Keep our layer hierarchy updated.
133         if (oldChild->firstChild() || oldChild->hasLayer()) {
134             if (!layer) layer = enclosingLayer();            
135             oldChild->removeLayers(layer);
136         }
137         
138         // renumber ordered lists
139         if (oldChild->isListItem())
140             updateListMarkerNumbers(oldChild->nextSibling());
141         
142         if (oldChild->isPositioned() && childrenInline())
143             dirtyLinesFromChangedChild(oldChild);
144     }
145     
146     // If oldChild is the start or end of the selection, then clear the selection to
147     // avoid problems of invalid pointers.
148     // FIXME: The SelectionController should be responsible for this when it
149     // is notified of DOM mutations.
150     if (!documentBeingDestroyed() && oldChild->isSelectionBorder())
151         view()->clearSelection();
152
153     // remove the child
154     if (oldChild->previousSibling())
155         oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
156     if (oldChild->nextSibling())
157         oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
158
159     if (children()->firstChild() == oldChild)
160         children()->setFirstChild(oldChild->nextSibling());
161     if (children()->lastChild() == oldChild)
162         children()->setLastChild(oldChild->previousSibling());
163
164     oldChild->setPreviousSibling(0);
165     oldChild->setNextSibling(0);
166     oldChild->setParent(0);
167
168     if (AXObjectCache::accessibilityEnabled())
169         document()->axObjectCache()->childrenChanged(this);
170
171     return oldChild;
172 }
173
174 void RenderContainer::removeChild(RenderObject* oldChild)
175 {
176     // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode
177     // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on
178     // layout anyway).
179     oldChild->removeFromObjectLists();
180     
181     removeChildNode(oldChild);
182 }
183
184 RenderObject* RenderContainer::beforeAfterContainer(RenderStyle::PseudoId type)
185 {
186     if (type == RenderStyle::BEFORE) {
187         RenderObject* first = this;
188         do {
189             // Skip list markers.
190             first = first->firstChild();
191             while (first && first->isListMarker())
192                 first = first->nextSibling();
193         } while (first && first->isAnonymous() && first->style()->styleType() == RenderStyle::NOPSEUDO);
194         if (first && first->style()->styleType() != type)
195             return 0;
196         return first;
197     }
198     if (type == RenderStyle::AFTER) {
199         RenderObject* last = this;
200         do {
201             last = last->lastChild();
202         } while (last && last->isAnonymous() && last->style()->styleType() == RenderStyle::NOPSEUDO && !last->isListMarker());
203         if (last && last->style()->styleType() != type)
204             return 0;
205         return last;
206     }
207
208     ASSERT_NOT_REACHED();
209     return 0;
210 }
211
212 void RenderContainer::updateBeforeAfterContent(RenderStyle::PseudoId type)
213 {
214     // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it.
215     if (parent() && parent()->createsAnonymousWrapper())
216         return;
217     updateBeforeAfterContentForContainer(type, this);
218 }
219
220 static RenderObject* findBeforeAfterParent(RenderObject* object)
221 {
222     // Only table parts need to search for the :before or :after parent
223     if (!(object->isTable() || object->isTableSection() || object->isTableRow()))
224         return object;
225
226     RenderObject* beforeAfterParent = object;
227     while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage()))
228         beforeAfterParent = beforeAfterParent->firstChild();
229     return beforeAfterParent;
230 }
231
232 void RenderContainer::updateBeforeAfterContentForContainer(RenderStyle::PseudoId type, RenderContainer* styledObject)
233 {
234     // Double check that the document did in fact use generated content rules.  Otherwise we should not have been called.
235     ASSERT(document()->usesBeforeAfterRules());
236
237     // In CSS2, before/after pseudo-content cannot nest.  Check this first.
238     if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER)
239         return;
240     
241     RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type);
242     RenderObject* child = beforeAfterContainer(type);
243
244     // Whether or not we currently have generated content attached.
245     bool oldContentPresent = child;
246
247     // Whether or not we now want generated content.  
248     bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE;
249
250     // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate
251     // :after content and not :before content.
252     if (newContentWanted && type == RenderStyle::BEFORE && isRenderInline() && toRenderInline(this)->isInlineContinuation())
253         newContentWanted = false;
254
255     // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object,
256     // then we don't generate the :after content.
257     if (newContentWanted && type == RenderStyle::AFTER && isRenderInline() && toRenderInline(this)->continuation())
258         newContentWanted = false;
259     
260     // If we don't want generated content any longer, or if we have generated content, but it's no longer
261     // identical to the new content data we want to build render objects for, then we nuke all
262     // of the old generated content.
263     if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) {
264         // Nuke the child. 
265         if (child && child->style()->styleType() == type) {
266             oldContentPresent = false;
267             child->destroy();
268             child = (type == RenderStyle::BEFORE) ? children()->firstChild() : children()->lastChild();
269         }
270     }
271
272     // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we
273     // have no generated content and can now return.
274     if (!newContentWanted)
275         return;
276
277     if (isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE &&
278         !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition))
279         // According to the CSS2 spec (the end of section 12.1), the only allowed
280         // display values for the pseudo style are NONE and INLINE for inline flows.
281         // FIXME: CSS2.1 lifted this restriction, but block display types will crash.
282         // For now we at least relax the restriction to allow all inline types like inline-block
283         // and inline-table.
284         pseudoElementStyle->setDisplay(INLINE);
285
286     if (oldContentPresent) {
287         if (child && child->style()->styleType() == type) {
288             // We have generated content present still.  We want to walk this content and update our
289             // style information with the new pseudo-element style.
290             child->setStyle(pseudoElementStyle);
291
292             RenderObject* beforeAfterParent = findBeforeAfterParent(child);
293             if (!beforeAfterParent)
294                 return;
295
296             // Note that if we ever support additional types of generated content (which should be way off
297             // in the future), this code will need to be patched.
298             for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) {
299                 if (genChild->isText())
300                     // Generated text content is a child whose style also needs to be set to the pseudo-element style.
301                     genChild->setStyle(pseudoElementStyle);
302                 else if (genChild->isImage()) {
303                     // Images get an empty style that inherits from the pseudo.
304                     RefPtr<RenderStyle> style = RenderStyle::create();
305                     style->inheritFrom(pseudoElementStyle);
306                     genChild->setStyle(style.release());
307                 } else
308                     // Must be a first-letter container. updateFirstLetter() will take care of it.
309                     ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_LETTER);
310             }
311         }
312         return; // We've updated the generated content. That's all we needed to do.
313     }
314     
315     RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? children()->firstChild() : 0;
316
317     // Generated content consists of a single container that houses multiple children (specified
318     // by the content property).  This generated content container gets the pseudo-element style set on it.
319     RenderObject* generatedContentContainer = 0;
320     
321     // Walk our list of generated content and create render objects for each.
322     for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->m_next) {
323         RenderObject* renderer = 0;
324         switch (content->m_type) {
325             case CONTENT_NONE:
326                 break;
327             case CONTENT_TEXT:
328                 renderer = new (renderArena()) RenderTextFragment(document() /* anonymous object */, content->m_content.m_text);
329                 renderer->setStyle(pseudoElementStyle);
330                 break;
331             case CONTENT_OBJECT: {
332                 RenderImageGeneratedContent* image = new (renderArena()) RenderImageGeneratedContent(document()); // anonymous object
333                 RefPtr<RenderStyle> style = RenderStyle::create();
334                 style->inheritFrom(pseudoElementStyle);
335                 image->setStyle(style.release());
336                 if (StyleImage* styleImage = content->m_content.m_image)
337                     image->setStyleImage(styleImage);
338                 renderer = image;
339                 break;
340             }
341             case CONTENT_COUNTER:
342                 renderer = new (renderArena()) RenderCounter(document(), *content->m_content.m_counter);
343                 renderer->setStyle(pseudoElementStyle);
344                 break;
345         }
346
347         if (renderer) {
348             if (!generatedContentContainer) {
349                 // Make a generated box that might be any display type now that we are able to drill down into children
350                 // to find the original content properly.
351                 generatedContentContainer = RenderObject::createObject(document(), pseudoElementStyle);
352                 generatedContentContainer->setStyle(pseudoElementStyle);
353                 addChild(generatedContentContainer, insertBefore);
354             }
355             generatedContentContainer->addChild(renderer);
356         }
357     }
358 }
359
360 bool RenderContainer::isAfterContent(RenderObject* child) const
361 {
362     if (!child)
363         return false;
364     if (child->style()->styleType() != RenderStyle::AFTER)
365         return false;
366     // Text nodes don't have their own styles, so ignore the style on a text node.
367     if (child->isText() && !child->isBR())
368         return false;
369     return true;
370 }
371
372 static void invalidateCountersInContainer(RenderObject* container)
373 {
374     if (!container)
375         return;
376     container = findBeforeAfterParent(container);
377     if (!container)
378         return;
379     for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) {
380         if (content->isCounter())
381             static_cast<RenderCounter*>(content)->invalidate();
382     }
383 }
384
385 void RenderContainer::invalidateCounters()
386 {
387     if (documentBeingDestroyed())
388         return;
389
390     invalidateCountersInContainer(beforeAfterContainer(RenderStyle::BEFORE));
391     invalidateCountersInContainer(beforeAfterContainer(RenderStyle::AFTER));
392 }
393
394 void RenderContainer::appendChildNode(RenderObject* newChild, bool fullAppend)
395 {
396     ASSERT(newChild->parent() == 0);
397     ASSERT(!isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell()));
398
399     newChild->setParent(this);
400     RenderObject* lChild = children()->lastChild();
401
402     if (lChild) {
403         newChild->setPreviousSibling(lChild);
404         lChild->setNextSibling(newChild);
405     } else
406         children()->setFirstChild(newChild);
407
408     children()->setLastChild(newChild);
409     
410     if (fullAppend) {
411         // Keep our layer hierarchy updated.  Optimize for the common case where we don't have any children
412         // and don't have a layer attached to ourselves.
413         RenderLayer* layer = 0;
414         if (newChild->firstChild() || newChild->hasLayer()) {
415             layer = enclosingLayer();
416             newChild->addLayers(layer, newChild);
417         }
418
419         // if the new child is visible but this object was not, tell the layer it has some visible content
420         // that needs to be drawn and layer visibility optimization can't be used
421         if (style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) {
422             if (!layer)
423                 layer = enclosingLayer();
424             if (layer)
425                 layer->setHasVisibleContent(true);
426         }
427         
428         if (!newChild->isFloatingOrPositioned() && childrenInline())
429             dirtyLinesFromChangedChild(newChild);
430     }
431
432     newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy.
433     if (!normalChildNeedsLayout())
434         setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
435     
436     if (AXObjectCache::accessibilityEnabled())
437         document()->axObjectCache()->childrenChanged(this);
438 }
439
440 void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool fullInsert)
441 {
442     if (!beforeChild) {
443         appendChildNode(child);
444         return;
445     }
446
447     ASSERT(!child->parent());
448     while (beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock())
449         beforeChild = beforeChild->parent();
450     ASSERT(beforeChild->parent() == this);
451
452     ASSERT(!isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell()));
453
454     if (beforeChild == children()->firstChild())
455         children()->setFirstChild(child);
456
457     RenderObject* prev = beforeChild->previousSibling();
458     child->setNextSibling(beforeChild);
459     beforeChild->setPreviousSibling(child);
460     if(prev) prev->setNextSibling(child);
461     child->setPreviousSibling(prev);
462
463     child->setParent(this);
464     
465     if (fullInsert) {
466         // Keep our layer hierarchy updated.  Optimize for the common case where we don't have any children
467         // and don't have a layer attached to ourselves.
468         RenderLayer* layer = 0;
469         if (child->firstChild() || child->hasLayer()) {
470             layer = enclosingLayer();
471             child->addLayers(layer, child);
472         }
473
474         // if the new child is visible but this object was not, tell the layer it has some visible content
475         // that needs to be drawn and layer visibility optimization can't be used
476         if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) {
477             if (!layer)
478                 layer = enclosingLayer();
479             if (layer)
480                 layer->setHasVisibleContent(true);
481         }
482
483         
484         if (!child->isFloating() && childrenInline())
485             dirtyLinesFromChangedChild(child);
486     }
487
488     child->setNeedsLayoutAndPrefWidthsRecalc();
489     if (!normalChildNeedsLayout())
490         setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
491     
492     if (AXObjectCache::accessibilityEnabled())
493         document()->axObjectCache()->childrenChanged(this);
494 }
495
496 VisiblePosition RenderContainer::positionForCoordinates(int xPos, int yPos)
497 {
498     // no children...return this render object's element, if there is one, and offset 0
499     if (!children()->firstChild())
500         return VisiblePosition(element(), 0, DOWNSTREAM);
501         
502     if (isTable() && element()) {
503         int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft();
504         int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom();
505         
506         if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) {
507             if (xPos <= right / 2)
508                 return VisiblePosition(Position(element(), 0));
509             else
510                 return VisiblePosition(Position(element(), maxDeepOffset(element())));
511         }
512     }
513
514     // Pass off to the closest child.
515     int minDist = INT_MAX;
516     RenderBox* closestRenderer = 0;
517     int newX = xPos;
518     int newY = yPos;
519     if (isTableRow()) {
520         newX += x();
521         newY += y();
522     }
523     for (RenderObject* renderObject = children()->firstChild(); renderObject; renderObject = renderObject->nextSibling()) {
524         if (!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() 
525             || renderObject->style()->visibility() != VISIBLE)
526             continue;
527         
528         if (!renderObject->isBox())
529             continue;
530         
531         RenderBox* renderer = toRenderBox(renderObject);
532
533         int top = borderTop() + paddingTop() + (isTableRow() ? 0 : renderer->y());
534         int bottom = top + renderer->contentHeight();
535         int left = borderLeft() + paddingLeft() + (isTableRow() ? 0 : renderer->x());
536         int right = left + renderer->contentWidth();
537         
538         if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) {
539             if (renderer->isTableRow())
540                 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y());
541             return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y());
542         }
543
544         // Find the distance from (x, y) to the box.  Split the space around the box into 8 pieces
545         // and use a different compare depending on which piece (x, y) is in.
546         IntPoint cmp;
547         if (xPos > right) {
548             if (yPos < top)
549                 cmp = IntPoint(right, top);
550             else if (yPos > bottom)
551                 cmp = IntPoint(right, bottom);
552             else
553                 cmp = IntPoint(right, yPos);
554         } else if (xPos < left) {
555             if (yPos < top)
556                 cmp = IntPoint(left, top);
557             else if (yPos > bottom)
558                 cmp = IntPoint(left, bottom);
559             else
560                 cmp = IntPoint(left, yPos);
561         } else {
562             if (yPos < top)
563                 cmp = IntPoint(xPos, top);
564             else
565                 cmp = IntPoint(xPos, bottom);
566         }
567         
568         int x1minusx2 = cmp.x() - xPos;
569         int y1minusy2 = cmp.y() - yPos;
570         
571         int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2;
572         if (dist < minDist) {
573             closestRenderer = renderer;
574             minDist = dist;
575         }
576     }
577     
578     if (closestRenderer)
579         return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y());
580     
581     return VisiblePosition(element(), 0, DOWNSTREAM);
582 }
583
584 void RenderContainer::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool)
585 {
586     if (!children()->firstChild() && (isInline() || isAnonymousBlock())) {
587         FloatPoint absPos = localToAbsolute(FloatPoint());
588         absoluteRects(rects, absPos.x(), absPos.y());
589         return;
590     }
591
592     if (!children()->firstChild())
593         return;
594
595     unsigned offset = start;
596     for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) {
597         if (child->isText() || child->isInline() || child->isAnonymousBlock()) {
598             FloatPoint absPos = child->localToAbsolute(FloatPoint());
599             child->absoluteRects(rects, absPos.x(), absPos.y());
600         }
601     }
602 }
603
604 void RenderContainer::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool /*useSelectionHeight*/)
605 {
606     if (!children()->firstChild() && (isInline() || isAnonymousBlock())) {
607         absoluteQuads(quads);
608         return;
609     }
610
611     if (!children()->firstChild())
612         return;
613
614     unsigned offset = start;
615     for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) {
616         if (child->isText() || child->isInline() || child->isAnonymousBlock())
617             child->absoluteQuads(quads);
618     }
619 }
620
621 #undef DEBUG_LAYOUT
622
623 } // namespace WebCore