de491be27b71cc9a27b1f956571cc182801518c3
[WebKit-https.git] / WebCore / khtml / rendering / render_container.cpp
1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2000 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2003 Apple Computer, Inc.
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., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  *
24  */
25
26 //#define DEBUG_LAYOUT
27
28 #include "render_container.h"
29 #include "render_table.h"
30 #include "render_text.h"
31 #include "render_image.h"
32 #include "render_canvas.h"
33 #include "xml/dom_docimpl.h"
34 #include "xml/dom_position.h"
35
36 #include <kdebug.h>
37 #include <assert.h>
38
39 #if APPLE_CHANGES
40 // For accessibility
41 #include "KWQAccObjectCache.h" 
42 #endif
43
44 using DOM::Position;
45 using namespace khtml;
46
47 RenderContainer::RenderContainer(DOM::NodeImpl* node)
48     : RenderObject(node)
49 {
50     m_first = 0;
51     m_last = 0;
52 }
53
54
55 RenderContainer::~RenderContainer()
56 {
57 }
58
59 void RenderContainer::detach()
60 {
61     if (continuation())
62         continuation()->detach();
63     
64     RenderObject* next;
65     for(RenderObject* n = m_first; n; n = next ) {
66         n->removeFromObjectLists();
67         n->setParent(0);
68         next = n->nextSibling();
69         n->detach();
70     }
71     m_first = 0;
72     m_last = 0;
73
74     RenderObject::detach();
75 }
76
77 bool RenderContainer::canHaveChildren() const
78 {
79     return true;
80 }
81
82 void RenderContainer::addChild(RenderObject *newChild, RenderObject *beforeChild)
83 {
84 #ifdef DEBUG_LAYOUT
85     kdDebug( 6040 ) << this << ": " <<  renderName() << "(RenderObject)::addChild( " << newChild << ": " <<
86         newChild->renderName() << ", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
87 #endif
88
89     bool needsTable = false;
90
91     if(!newChild->isText() && !newChild->isReplaced()) {
92         switch(newChild->style()->display()) {
93         case INLINE:
94         case BLOCK:
95         case INLINE_BLOCK:
96         case LIST_ITEM:
97         case RUN_IN:
98         case COMPACT:
99         case BOX:
100         case INLINE_BOX:
101         case TABLE:
102         case INLINE_TABLE:
103         case TABLE_COLUMN:
104             break;
105         case TABLE_COLUMN_GROUP:
106         case TABLE_CAPTION:
107         case TABLE_ROW_GROUP:
108         case TABLE_HEADER_GROUP:
109         case TABLE_FOOTER_GROUP:
110
111             //kdDebug( 6040 ) << "adding section" << endl;
112             if ( !isTable() )
113                 needsTable = true;
114             break;
115         case TABLE_ROW:
116             //kdDebug( 6040 ) << "adding row" << endl;
117             if ( !isTableSection() )
118                 needsTable = true;
119             break;
120         case TABLE_CELL:
121             //kdDebug( 6040 ) << "adding cell" << endl;
122             if ( !isTableRow() )
123                 needsTable = true;
124 #if APPLE_CHANGES
125             // I'm not 100% sure this is the best way to fix this, but without this
126             // change we recurse infinitely when trying to render the CSS2 test page:
127             // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html.
128             // See Radar 2925291.
129             if ( isTableCell() && !firstChild() && !newChild->isTableCell() )
130                 needsTable = false;
131 #endif
132             break;
133         case NONE:
134             kdDebug( 6000 ) << "error in RenderObject::addChild()!!!!" << endl;
135             break;
136         }
137     }
138
139     if ( needsTable ) {
140         RenderTable *table;
141         if( !beforeChild )
142             beforeChild = lastChild();
143         if( beforeChild && beforeChild->isAnonymous() && beforeChild->isTable() )
144             table = static_cast<RenderTable *>(beforeChild);
145         else {
146             //kdDebug( 6040 ) << "creating anonymous table" << endl;
147             table = new (renderArena()) RenderTable(document() /* is anonymous */);
148             RenderStyle *newStyle = new (renderArena()) RenderStyle();
149             newStyle->inheritFrom(style());
150             newStyle->setDisplay(TABLE);
151             table->setStyle(newStyle);
152             addChild(table, beforeChild);
153         }
154         table->addChild(newChild);
155     } else {
156         // just add it...
157         insertChildNode(newChild, beforeChild);
158     }
159 }
160
161 RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild)
162 {
163     KHTMLAssert(oldChild->parent() == this);
164
165     // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or
166     // that a positioned child got yanked).  We also repaint, so that the area exposed when the child
167     // disappears gets repainted properly.
168     if (!documentBeingDestroyed()) {
169         oldChild->setNeedsLayoutAndMinMaxRecalc();
170         oldChild->repaint();
171         
172         // Keep our layer hierarchy updated.
173         oldChild->removeLayers(enclosingLayer());
174     
175         // if oldChild is the start or end of the selection, then clear the selection to
176         // avoid problems of invalid pointers
177     
178         // ### This is not the "proper" solution... ideally the selection should be maintained
179         // based on DOM Nodes and a Range, which gets adjusted appropriately when nodes are
180         // deleted/inserted near etc. But this at least prevents crashes caused when the start
181         // or end of the selection is deleted and then accessed when the user next selects
182         // something.
183     
184         if (oldChild->isSelectionBorder()) {
185             RenderObject *root = oldChild;
186             while (root && root->parent())
187                 root = root->parent();
188             if (root->isCanvas()) {
189                 static_cast<RenderCanvas*>(root)->clearSelection();
190             }
191         }
192     }
193     
194     // remove the child
195     if (oldChild->previousSibling())
196         oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
197     if (oldChild->nextSibling())
198         oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
199
200     if (m_first == oldChild)
201         m_first = oldChild->nextSibling();
202     if (m_last == oldChild)
203         m_last = oldChild->previousSibling();
204
205     oldChild->setPreviousSibling(0);
206     oldChild->setNextSibling(0);
207     oldChild->setParent(0);
208
209 #if APPLE_CHANGES
210     KWQAccObjectCache* cache = document()->getExistingAccObjectCache();
211     if (cache)
212         cache->childrenChanged(this);
213 #endif
214     
215     return oldChild;
216 }
217
218 void RenderContainer::removeChild(RenderObject *oldChild)
219 {
220     removeChildNode(oldChild);
221 }
222
223 void RenderContainer::updatePseudoChild(RenderStyle::PseudoId type, RenderObject* child)
224 {
225     // In CSS2, before/after pseudo-content cannot nest.  Check this first.
226     if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER)
227         return;
228     
229     RenderStyle* pseudo = getPseudoStyle(type);
230
231     // Whether or not we currently have generated content attached.
232     bool oldContentPresent = child && (child->style()->styleType() == type);
233
234     // Whether or not we now want generated content.  
235     bool newContentWanted = pseudo && pseudo->display() != NONE;
236
237     // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate
238     // :after content and not :before content.
239     if (type == RenderStyle::BEFORE && isInlineContinuation())
240         newContentWanted = false;
241
242     // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object,
243     // then we don't generate the :after content.
244     if (type == RenderStyle::AFTER && isRenderInline() && continuation())
245         newContentWanted = false;
246     
247     // If we don't want generated content any longer, or if we have generated content, but it's no longer
248     // identical to the new content data we want to build render objects for, then we nuke all
249     // of the old generated content.
250     if (!newContentWanted ||
251         (oldContentPresent && !child->style()->contentDataEquivalent(pseudo))) {
252         // Nuke the child. 
253         if (child && child->style()->styleType() == type) {
254             oldContentPresent = false;
255             removeChild(child);
256             child = (type == RenderStyle::BEFORE) ? firstChild() : lastChild();
257         }
258     }
259
260     // If we have no pseudo-style or if the pseudo's display type is NONE, then we
261     // have no generated content and can now return.
262     if (!newContentWanted)
263         return;
264
265     if (isInlineFlow() && pseudo->display() != INLINE)
266         // According to the CSS2 spec (the end of section 12.1), the only allowed
267         // display values for the pseudo style are NONE and INLINE.  Since we already
268         // determined that the pseudo is not display NONE, any display other than
269         // inline should be mutated to INLINE.
270         pseudo->setDisplay(INLINE);
271     
272     if (oldContentPresent) {
273         if (child && child->style()->styleType() == type) {
274             // We have generated content present still.  We want to walk this content and update our
275             // style information with the new pseudo style.
276             child->setStyle(pseudo);
277
278             // Note that if we ever support additional types of generated content (which should be way off
279             // in the future), this code will need to be patched.
280             for (RenderObject* genChild = child->firstChild(); genChild; genChild = genChild->nextSibling()) {
281                 if (genChild->isText())
282                     // Generated text content is a child whose style also needs to be set to the pseudo
283                     // style.
284                     genChild->setStyle(pseudo);
285                 else {
286                     // Images get an empty style that inherits from the pseudo.
287                     RenderStyle* style = new (renderArena()) RenderStyle();
288                     style->inheritFrom(pseudo);
289                     genChild->setStyle(style);
290                 }
291             }
292         }
293         return; // We've updated the generated content. That's all we needed to do.
294     }
295     
296     RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? child : 0;
297
298     // Generated content consists of a single container that houses multiple children (specified
299     // by the content property).  This pseudo container gets the pseudo style set on it.
300     RenderObject* pseudoContainer = 0;
301     
302     // Now walk our list of generated content and create render objects for every type
303     // we encounter.
304     for (ContentData* contentData = pseudo->contentData();
305          contentData; contentData = contentData->_nextContent) {
306         if (!pseudoContainer)
307             pseudoContainer = RenderFlow::createAnonymousFlow(document(), pseudo); /* anonymous box */
308         
309         if (contentData->contentType() == CONTENT_TEXT)
310         {
311             RenderText* t = new (renderArena()) RenderTextFragment(document() /*anonymous object */, contentData->contentText());
312             t->setStyle(pseudo);
313             pseudoContainer->addChild(t);
314         }
315         else if (contentData->contentType() == CONTENT_OBJECT)
316         {
317             RenderImage* img = new (renderArena()) RenderImage(document()); /* Anonymous object */
318             RenderStyle* style = new (renderArena()) RenderStyle();
319             style->inheritFrom(pseudo);
320             img->setStyle(style);
321             img->setContentObject(contentData->contentObject());
322             pseudoContainer->addChild(img);
323         }
324     }
325
326     if (pseudoContainer) {
327         // Add the pseudo after we've installed all our content, so that addChild will be able to find the text
328         // inside the inline for e.g., first-letter styling.
329         addChild(pseudoContainer, insertBefore);
330     }
331 }
332
333
334 void RenderContainer::appendChildNode(RenderObject* newChild)
335 {
336     KHTMLAssert(newChild->parent() == 0);
337
338     newChild->setParent(this);
339     RenderObject* lChild = lastChild();
340
341     if(lChild)
342     {
343         newChild->setPreviousSibling(lChild);
344         lChild->setNextSibling(newChild);
345     }
346     else
347         setFirstChild(newChild);
348
349     setLastChild(newChild);
350     
351     // Keep our layer hierarchy updated.  Optimize for the common case where we don't have any children
352     // and don't have a layer attached to ourselves.
353     if (newChild->firstChild() || newChild->layer()) {
354         RenderLayer* layer = enclosingLayer();
355         newChild->addLayers(layer, newChild);
356     }
357     
358     newChild->setNeedsLayoutAndMinMaxRecalc(); // Goes up the containing block hierarchy.
359     if (!normalChildNeedsLayout())
360         setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
361     
362     if (!newChild->isFloatingOrPositioned() && childrenInline())
363         dirtyLinesFromChangedChild(newChild);
364     
365 #if APPLE_CHANGES
366     KWQAccObjectCache* cache = document()->getExistingAccObjectCache();
367     if (cache)
368         cache->childrenChanged(this);
369 #endif
370 }
371
372 void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild)
373 {
374     if(!beforeChild) {
375         appendChildNode(child);
376         return;
377     }
378
379     KHTMLAssert(!child->parent());
380     while ( beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock() )
381         beforeChild = beforeChild->parent();
382     KHTMLAssert(beforeChild->parent() == this);
383
384     if(beforeChild == firstChild())
385         setFirstChild(child);
386
387     RenderObject* prev = beforeChild->previousSibling();
388     child->setNextSibling(beforeChild);
389     beforeChild->setPreviousSibling(child);
390     if(prev) prev->setNextSibling(child);
391     child->setPreviousSibling(prev);
392
393     child->setParent(this);
394     
395     // Keep our layer hierarchy updated.
396     RenderLayer* layer = enclosingLayer();
397     child->addLayers(layer, child);
398
399     child->setNeedsLayoutAndMinMaxRecalc();
400     if (!normalChildNeedsLayout())
401         setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
402     
403     if (!child->isFloatingOrPositioned() && childrenInline())
404         dirtyLinesFromChangedChild(child);
405     
406 #if APPLE_CHANGES
407     KWQAccObjectCache* cache = document()->getExistingAccObjectCache();
408     if (cache)
409         cache->childrenChanged(this);
410 #endif    
411 }
412
413
414 void RenderContainer::layout()
415 {
416     KHTMLAssert( needsLayout() );
417     KHTMLAssert( minMaxKnown() );
418
419     RenderObject *child = firstChild();
420     while( child ) {
421         child->layoutIfNeeded();
422         child = child->nextSibling();
423     }
424     setNeedsLayout(false);
425 }
426
427 void RenderContainer::removeLeftoverAnonymousBoxes()
428 {
429     // we have to go over all child nodes and remove anonymous boxes, that do _not_
430     // have inline children to keep the tree flat
431     RenderObject *child = firstChild();
432     while( child ) {
433         RenderObject *next = child->nextSibling();
434         
435         if ( child->isRenderBlock() && child->isAnonymousBlock() && !child->continuation() && !child->childrenInline() && !child->isTableCell() ) {
436             RenderObject *firstAnChild = child->firstChild();
437             RenderObject *lastAnChild = child->lastChild();
438             if ( firstAnChild ) {
439                 RenderObject *o = firstAnChild;
440                 while( o ) {
441                     o->setParent( this );
442                     o = o->nextSibling();
443                 }
444                 firstAnChild->setPreviousSibling( child->previousSibling() );
445                 lastAnChild->setNextSibling( child->nextSibling() );
446                 if ( child->previousSibling() )
447                     child->previousSibling()->setNextSibling( firstAnChild );
448                 if ( child->nextSibling() )
449                     child->nextSibling()->setPreviousSibling( lastAnChild );
450             } else {
451                 if ( child->previousSibling() )
452                     child->previousSibling()->setNextSibling( child->nextSibling() );
453                 if ( child->nextSibling() )
454                     child->nextSibling()->setPreviousSibling( child->previousSibling() );
455                 
456             }
457             if ( child == firstChild() )
458                 m_first = firstAnChild;
459             if ( child == lastChild() )
460                 m_last = lastAnChild;
461             child->setParent( 0 );
462             child->setPreviousSibling( 0 );
463             child->setNextSibling( 0 );
464             if ( !child->isText() ) {
465                 RenderContainer *c = static_cast<RenderContainer *>(child);
466                 c->m_first = 0;
467                 c->m_next = 0;
468             }
469             child->detach();
470         }
471         child = next;
472     }
473     if ( parent() )
474         parent()->removeLeftoverAnonymousBoxes();
475 }
476
477 Position RenderContainer::positionForCoordinates(int _x, int _y)
478 {
479     // no children...return this render object's element, if there is one, and offset 0
480     if (!firstChild())
481         return Position(element(), 0);
482
483     // look for the geometrically-closest child and pass off to that child
484     int min = INT_MAX;
485     RenderObject *closestRenderer = 0;
486     for (RenderObject *renderer = firstChild(); renderer; renderer = renderer->nextSibling()) {
487         if (!renderer->firstChild() && !renderer->isInline() && !renderer->isBlockFlow())
488             continue;
489
490         int absx, absy;
491         renderer->absolutePosition(absx, absy);
492         
493         int top = absy + borderTop() + paddingTop();
494         int bottom = top + renderer->contentHeight();
495         int left = absx + borderLeft() + paddingLeft();
496         int right = left + renderer->contentWidth();
497         
498         int cmp;
499         cmp = abs(_y - top);    if (cmp < min) { closestRenderer = renderer; min = cmp; }
500         cmp = abs(_y - bottom); if (cmp < min) { closestRenderer = renderer; min = cmp; }
501         cmp = abs(_x - left);   if (cmp < min) { closestRenderer = renderer; min = cmp; }
502         cmp = abs(_x - right);  if (cmp < min) { closestRenderer = renderer; min = cmp; }
503     }
504     
505     if (closestRenderer)
506         return closestRenderer->positionForCoordinates(_x, _y);
507     
508     return Position(element(), 0);
509 }
510     
511 #undef DEBUG_LAYOUT