MathML internals for bug 52444 fix - type checking, PassRefPtr
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLUnderOver.cpp
1 /*
2  * Copyright (C) 2009 Alex Milowski (alex@milowski.com). 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY 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
28 #if ENABLE(MATHML)
29
30 #include "RenderMathMLUnderOver.h"
31
32 #include "FontSelector.h"
33 #include "MathMLNames.h"
34
35 namespace WebCore {
36
37 using namespace MathMLNames;
38     
39 static const double gOverSpacingAdjustment = 0.5;
40     
41 RenderMathMLUnderOver::RenderMathMLUnderOver(Element* element)
42     : RenderMathMLBlock(element)
43 {
44     // Determine what kind of under/over expression we have by element name
45     
46     if (element->hasLocalName(MathMLNames::munderTag))
47         m_kind = Under;
48     else if (element->hasLocalName(MathMLNames::moverTag))
49         m_kind = Over;
50     else if (element->hasLocalName(MathMLNames::munderoverTag))
51         m_kind = UnderOver;
52     else 
53         m_kind = Under;
54     
55 }
56
57 void RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild)
58 {    
59     RenderMathMLBlock* row = new (renderArena()) RenderMathMLBlock(node());
60     RefPtr<RenderStyle> rowStyle = makeBlockStyle();
61     row->setStyle(rowStyle.release());
62     row->setIsAnonymous(true);
63     
64     // look through the children for rendered elements counting the blocks so we know what child
65     // we are adding
66     int blocks = 0;
67     RenderObject* current = this->firstChild();
68     while (current) {
69         blocks++;
70         current = current->nextSibling();
71     }
72     
73     switch (blocks) {
74     case 0:
75         // this is the base so just append it
76         RenderBlock::addChild(row, beforeChild);
77         break;
78     case 1:
79         // the under or over
80         // FIXME: text-align: center does not work
81         row->style()->setTextAlign(CENTER);
82         if (m_kind == Over) {
83             // add the over as first
84             RenderBlock::addChild(row, firstChild());
85         } else {
86             // add the under as last
87             RenderBlock::addChild(row, beforeChild);
88         }
89         break;
90     case 2:
91         // the under or over
92         // FIXME: text-align: center does not work
93         row->style()->setTextAlign(CENTER);
94         if (m_kind == UnderOver) {
95             // add the over as first
96             RenderBlock::addChild(row, firstChild());
97         } else {
98             // we really shouldn't get here as only munderover should have three children
99             RenderBlock::addChild(row, beforeChild);
100         }
101         break;
102     default:
103         // munderover shouldn't have more than three children. In theory we shouldn't 
104         // get here if the MathML is correctly formed, but that isn't a guarantee.
105         // We will treat this as another under element and they'll get something funky.
106         RenderBlock::addChild(row, beforeChild);
107     }
108     row->addChild(child);    
109 }
110
111 inline int getOffsetHeight(RenderObject* obj) 
112 {
113     if (obj->isBoxModelObject()) {
114         RenderBoxModelObject* box = toRenderBoxModelObject(obj);
115         return box->offsetHeight();
116     }
117    
118     return 0;
119 }
120
121 void RenderMathMLUnderOver::stretchToHeight(int height)
122 {
123
124     RenderObject* base = firstChild();
125     if (!base)
126         return;
127         
128     // For over or underover, the base is the sibling of the first child
129     if (m_kind != Under) 
130         base = base->nextSibling();
131         
132     if (!base)
133         return;
134         
135     // use the child of the row which is the actual base
136     base = base->firstChild();
137     
138     if (base && base->isRenderMathMLBlock()) {
139         RenderMathMLBlock* block = toRenderMathMLBlock(base);
140         block->stretchToHeight(height);
141         setNeedsLayout(true);
142     }
143 }
144
145 void RenderMathMLUnderOver::layout() 
146 {
147     RenderBlock::layout();
148     RenderObject* over = 0;
149     RenderObject* base = 0;
150     switch (m_kind) {
151     case Over:
152         // We need to calculate the baseline over the over versus the start of the base and 
153         // adjust the placement of the base.
154         over = firstChild();
155         if (over) {
156             // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
157             // FIXME: bases that ascend higher than the line box intrude into the over
158             if (!over->firstChild() || !over->firstChild()->isBoxModelObject())
159                 break;
160             
161             LayoutUnit overSpacing = static_cast<LayoutUnit>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
162             
163             // base row wrapper
164             base = over->nextSibling();
165             if (base) {
166                 if (overSpacing > 0) 
167                     base->style()->setMarginTop(Length(-overSpacing, Fixed));
168                 else 
169                     base->style()->setMarginTop(Length(0, Fixed));
170             }
171             
172         }
173         break;
174     case Under:
175         // FIXME: Non-ascending glyphs in the under should be moved closer to the base
176
177         // We need to calculate the baseline of the base versus the start of the under block and
178         // adjust the placement of the under block.
179         
180         // base row wrapper
181         base = firstChild();
182         if (base) {
183             LayoutUnit baseHeight = getOffsetHeight(base);
184             // actual base
185             base = base->firstChild();
186             if (!base || !base->isBoxModelObject())
187                 break;
188             
189             // FIXME: We need to look at the space between a single maximum height of
190             //        the line boxes and the baseline and squeeze them together
191             LayoutUnit underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
192             
193             // adjust the base's intrusion into the under
194             RenderObject* under = lastChild();
195             if (under && underSpacing > 0)
196                 under->style()->setMarginTop(Length(-underSpacing, Fixed));
197         }
198         break;
199     case UnderOver:
200         // FIXME: Non-descending glyphs in the over should be moved closer to the base
201         // FIXME: Non-ascending glyphs in the under should be moved closer to the base
202         
203         // We need to calculate the baseline of the over versus the start of the base and 
204         // adjust the placement of the base.
205         
206         over = firstChild();
207         if (over) {
208             // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
209             // FIXME: bases that ascend higher than the line box intrude into the over
210             if (!over->firstChild() || !over->firstChild()->isBoxModelObject())
211                 break;
212             LayoutUnit overSpacing = static_cast<LayoutUnit>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
213             
214             // base row wrapper
215             base = over->nextSibling();
216             
217             if (base) {
218                 if (overSpacing > 0)
219                     base->style()->setMarginTop(Length(-overSpacing, Fixed));
220                 
221                 // We need to calculate the baseline of the base versus the start of the under block and
222                 // adjust the placement of the under block.
223                 
224                 LayoutUnit baseHeight = getOffsetHeight(base);
225                 // actual base
226                 base = base->firstChild();
227                 if (!base || !base->isBoxModelObject())
228                     break;
229
230                 // FIXME: We need to look at the space between a single maximum height of
231                 //        the line boxes and the baseline and squeeze them together
232                 LayoutUnit underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
233                 
234                 RenderObject* under = lastChild();
235                 if (under && under->firstChild() && under->firstChild()->isRenderInline() && underSpacing > 0)
236                     under->style()->setMarginTop(Length(-underSpacing, Fixed));
237                 
238             }
239         }
240         break;
241     }
242     setNeedsLayout(true);
243     RenderBlock::layout();
244 }
245
246 LayoutUnit RenderMathMLUnderOver::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
247 {
248     RenderObject* current = firstChild();
249     if (!current || linePositionMode == PositionOfInteriorLineBoxes)
250         return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
251
252     LayoutUnit baseline = 0;
253     switch (m_kind) {
254     case UnderOver:
255     case Over:
256         baseline += getOffsetHeight(current);
257         current = current->nextSibling();
258         if (current) {
259             // actual base
260             RenderObject* base = current->firstChild();
261             if (!base || !base->isBoxModelObject())
262                 break;
263             baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, firstLine, HorizontalLine, linePositionMode);
264             // added the negative top margin
265             baseline += current->style()->marginTop().value();
266         }
267         break;
268     case Under:
269         RenderObject* base = current->firstChild();
270         if (base && base->isBoxModelObject())
271             baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
272     }
273
274     return baseline;
275 }
276
277
278 int RenderMathMLUnderOver::nonOperatorHeight() const 
279 {
280     int nonOperators = 0;
281     for (RenderObject* current = firstChild(); current; current = current->nextSibling()) {
282         if (current->firstChild() && current->firstChild()->isRenderMathMLBlock()) {
283             RenderMathMLBlock* block = toRenderMathMLBlock(current->firstChild());
284             if (!block->isRenderMathMLOperator()) 
285                 nonOperators += getOffsetHeight(current);
286         } else
287             nonOperators += getOffsetHeight(current);
288     }
289     return nonOperators;
290 }
291
292 }
293
294 #endif // ENABLE(MATHML)