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