1189e4b434118330ba555a363b943d3abd7323b1
[WebKit-https.git] / WebCore / rendering / RenderObjectChildList.cpp
1 /*
2  * Copyright (C) 2009 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 COMPUTER, 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 INTERRUPTION) 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 "RenderObjectChildList.h"
28
29 #include "RenderBlock.h"
30 #include "RenderCounter.h"
31 #include "RenderImageGeneratedContent.h"
32 #include "RenderInline.h"
33 #include "RenderTextFragment.h"
34
35 namespace WebCore {
36
37 void RenderObjectChildList::destroyLeftoverChildren()
38 {
39     while (firstChild()) {
40         if (firstChild()->isListMarker() || (firstChild()->style()->styleType() == RenderStyle::FIRST_LETTER && !firstChild()->isText()))
41             firstChild()->remove();  // List markers are owned by their enclosing list and so don't get destroyed by this container. Similarly, first letters are destroyed by their remaining text fragment.
42         else {
43             // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields.
44             if (firstChild()->element())
45                 firstChild()->element()->setRenderer(0);
46             firstChild()->destroy();
47         }
48     }
49 }
50
51 static RenderObject* beforeAfterContainer(RenderObject* container, RenderStyle::PseudoId type)
52 {
53     if (type == RenderStyle::BEFORE) {
54         RenderObject* first = container;
55         do {
56             // Skip list markers.
57             first = first->firstChild();
58             while (first && first->isListMarker())
59                 first = first->nextSibling();
60         } while (first && first->isAnonymous() && first->style()->styleType() == RenderStyle::NOPSEUDO);
61         if (first && first->style()->styleType() != type)
62             return 0;
63         return first;
64     }
65     if (type == RenderStyle::AFTER) {
66         RenderObject* last = container;
67         do {
68             last = last->lastChild();
69         } while (last && last->isAnonymous() && last->style()->styleType() == RenderStyle::NOPSEUDO && !last->isListMarker());
70         if (last && last->style()->styleType() != type)
71             return 0;
72         return last;
73     }
74
75     ASSERT_NOT_REACHED();
76     return 0;
77 }
78
79 static RenderObject* findBeforeAfterParent(RenderObject* object)
80 {
81     // Only table parts need to search for the :before or :after parent
82     if (!(object->isTable() || object->isTableSection() || object->isTableRow()))
83         return object;
84
85     RenderObject* beforeAfterParent = object;
86     while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage()))
87         beforeAfterParent = beforeAfterParent->firstChild();
88     return beforeAfterParent;
89 }
90
91 static void invalidateCountersInContainer(RenderObject* container)
92 {
93     if (!container)
94         return;
95     container = findBeforeAfterParent(container);
96     if (!container)
97         return;
98     for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) {
99         if (content->isCounter())
100             static_cast<RenderCounter*>(content)->invalidate();
101     }
102 }
103
104 void RenderObjectChildList::invalidateCounters(RenderObject* owner)
105 {
106     ASSERT(!owner->documentBeingDestroyed());
107     invalidateCountersInContainer(beforeAfterContainer(owner, RenderStyle::BEFORE));
108     invalidateCountersInContainer(beforeAfterContainer(owner, RenderStyle::AFTER));
109 }
110
111 void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, RenderStyle::PseudoId type, RenderObject* styledObject)
112 {
113     // Double check that the document did in fact use generated content rules.  Otherwise we should not have been called.
114     ASSERT(owner->document()->usesBeforeAfterRules());
115
116     // In CSS2, before/after pseudo-content cannot nest.  Check this first.
117     if (owner->style()->styleType() == RenderStyle::BEFORE || owner->style()->styleType() == RenderStyle::AFTER)
118         return;
119     
120     if (!styledObject)
121         styledObject = owner;
122
123     RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type);
124     RenderObject* child = beforeAfterContainer(owner, type);
125
126     // Whether or not we currently have generated content attached.
127     bool oldContentPresent = child;
128
129     // Whether or not we now want generated content.  
130     bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE;
131
132     // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate
133     // :after content and not :before content.
134     if (newContentWanted && type == RenderStyle::BEFORE && owner->isRenderInline() && toRenderInline(owner)->isInlineContinuation())
135         newContentWanted = false;
136
137     // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object,
138     // then we don't generate the :after content.
139     if (newContentWanted && type == RenderStyle::AFTER && owner->isRenderInline() && toRenderInline(owner)->continuation())
140         newContentWanted = false;
141     
142     // If we don't want generated content any longer, or if we have generated content, but it's no longer
143     // identical to the new content data we want to build render objects for, then we nuke all
144     // of the old generated content.
145     if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) {
146         // Nuke the child. 
147         if (child && child->style()->styleType() == type) {
148             oldContentPresent = false;
149             child->destroy();
150             child = (type == RenderStyle::BEFORE) ? owner->virtualChildren()->firstChild() : owner->virtualChildren()->lastChild();
151         }
152     }
153
154     // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we
155     // have no generated content and can now return.
156     if (!newContentWanted)
157         return;
158
159     if (owner->isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE &&
160         !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition))
161         // According to the CSS2 spec (the end of section 12.1), the only allowed
162         // display values for the pseudo style are NONE and INLINE for inline flows.
163         // FIXME: CSS2.1 lifted this restriction, but block display types will crash.
164         // For now we at least relax the restriction to allow all inline types like inline-block
165         // and inline-table.
166         pseudoElementStyle->setDisplay(INLINE);
167
168     if (oldContentPresent) {
169         if (child && child->style()->styleType() == type) {
170             // We have generated content present still.  We want to walk this content and update our
171             // style information with the new pseudo-element style.
172             child->setStyle(pseudoElementStyle);
173
174             RenderObject* beforeAfterParent = findBeforeAfterParent(child);
175             if (!beforeAfterParent)
176                 return;
177
178             // Note that if we ever support additional types of generated content (which should be way off
179             // in the future), this code will need to be patched.
180             for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) {
181                 if (genChild->isText())
182                     // Generated text content is a child whose style also needs to be set to the pseudo-element style.
183                     genChild->setStyle(pseudoElementStyle);
184                 else if (genChild->isImage()) {
185                     // Images get an empty style that inherits from the pseudo.
186                     RefPtr<RenderStyle> style = RenderStyle::create();
187                     style->inheritFrom(pseudoElementStyle);
188                     genChild->setStyle(style.release());
189                 } else
190                     // Must be a first-letter container. updateFirstLetter() will take care of it.
191                     ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_LETTER);
192             }
193         }
194         return; // We've updated the generated content. That's all we needed to do.
195     }
196     
197     RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? owner->virtualChildren()->firstChild() : 0;
198
199     // Generated content consists of a single container that houses multiple children (specified
200     // by the content property).  This generated content container gets the pseudo-element style set on it.
201     RenderObject* generatedContentContainer = 0;
202     
203     // Walk our list of generated content and create render objects for each.
204     for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->m_next) {
205         RenderObject* renderer = 0;
206         switch (content->m_type) {
207             case CONTENT_NONE:
208                 break;
209             case CONTENT_TEXT:
210                 renderer = new (owner->renderArena()) RenderTextFragment(owner->document() /* anonymous object */, content->m_content.m_text);
211                 renderer->setStyle(pseudoElementStyle);
212                 break;
213             case CONTENT_OBJECT: {
214                 RenderImageGeneratedContent* image = new (owner->renderArena()) RenderImageGeneratedContent(owner->document()); // anonymous object
215                 RefPtr<RenderStyle> style = RenderStyle::create();
216                 style->inheritFrom(pseudoElementStyle);
217                 image->setStyle(style.release());
218                 if (StyleImage* styleImage = content->m_content.m_image)
219                     image->setStyleImage(styleImage);
220                 renderer = image;
221                 break;
222             }
223             case CONTENT_COUNTER:
224                 renderer = new (owner->renderArena()) RenderCounter(owner->document(), *content->m_content.m_counter);
225                 renderer->setStyle(pseudoElementStyle);
226                 break;
227         }
228
229         if (renderer) {
230             if (!generatedContentContainer) {
231                 // Make a generated box that might be any display type now that we are able to drill down into children
232                 // to find the original content properly.
233                 generatedContentContainer = RenderObject::createObject(owner->document(), pseudoElementStyle);
234                 generatedContentContainer->setStyle(pseudoElementStyle);
235                 owner->addChild(generatedContentContainer, insertBefore);
236             }
237             generatedContentContainer->addChild(renderer);
238         }
239     }
240 }
241
242 }