MathML: nested square root symbols have varying descenders
[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 "MathMLNames.h"
33
34 namespace WebCore {
35
36 using namespace MathMLNames;
37     
38 RenderMathMLUnderOver::RenderMathMLUnderOver(Element* element)
39     : RenderMathMLBlock(element)
40 {
41     // Determine what kind of under/over expression we have by element name
42     if (element->hasLocalName(MathMLNames::munderTag))
43         m_kind = Under;
44     else if (element->hasLocalName(MathMLNames::moverTag))
45         m_kind = Over;
46     else {
47         ASSERT(element->hasLocalName(MathMLNames::munderoverTag));
48         m_kind = UnderOver;
49     }
50 }
51
52 RenderBoxModelObject* RenderMathMLUnderOver::base() const
53 {
54     RenderObject* baseWrapper = firstChild();
55     if ((m_kind == Over || m_kind == UnderOver) && baseWrapper)
56         baseWrapper = baseWrapper->nextSibling();
57     if (!baseWrapper)
58         return 0;
59     RenderObject* base = baseWrapper->firstChild();
60     if (!base || !base->isBoxModelObject())
61         return 0;
62     return toRenderBoxModelObject(base);
63 }
64
65 void RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild)
66 {    
67     RenderMathMLBlock* row = createAnonymousMathMLBlock();
68     
69     // look through the children for rendered elements counting the blocks so we know what child
70     // we are adding
71     int blocks = 0;
72     RenderObject* current = this->firstChild();
73     while (current) {
74         blocks++;
75         current = current->nextSibling();
76     }
77     
78     switch (blocks) {
79     case 0:
80         // this is the base so just append it
81         RenderBlock::addChild(row, beforeChild);
82         break;
83     case 1:
84         // the under or over
85         row->style()->setTextAlign(CENTER);
86         if (m_kind == Over) {
87             // add the over as first
88             RenderBlock::addChild(row, firstChild());
89         } else {
90             // add the under as last
91             RenderBlock::addChild(row, beforeChild);
92         }
93         break;
94     case 2:
95         // the under or over
96         row->style()->setTextAlign(CENTER);
97         if (m_kind == UnderOver) {
98             // add the over as first
99             RenderBlock::addChild(row, firstChild());
100         } else {
101             // we really shouldn't get here as only munderover should have three children
102             RenderBlock::addChild(row, beforeChild);
103         }
104         break;
105     default:
106         // munderover shouldn't have more than three children. In theory we shouldn't 
107         // get here if the MathML is correctly formed, but that isn't a guarantee.
108         // We will treat this as another under element and they'll get something funky.
109         RenderBlock::addChild(row, beforeChild);
110     }
111     row->addChild(child);    
112 }
113
114 void RenderMathMLUnderOver::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
115 {
116     RenderMathMLBlock::styleDidChange(diff, oldStyle);
117     
118     RenderObject* base = this->base();
119     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
120         ASSERT(child->isAnonymous() && child->style()->refCount() == 1);
121         if (child->firstChild() != base)
122             child->style()->setTextAlign(CENTER);
123     }
124 }
125
126 RenderMathMLOperator* RenderMathMLUnderOver::unembellishedOperator()
127 {
128     RenderBoxModelObject* base = this->base();
129     if (!base || !base->isRenderMathMLBlock())
130         return 0;
131     return toRenderMathMLBlock(base)->unembellishedOperator();
132 }
133
134 inline int getOffsetHeight(RenderObject* obj) 
135 {
136     if (obj->isBoxModelObject()) {
137         RenderBoxModelObject* box = toRenderBoxModelObject(obj);
138         return box->pixelSnappedOffsetHeight();
139     }
140    
141     return 0;
142 }
143
144 LayoutUnit RenderMathMLUnderOver::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
145 {
146     RenderObject* current = firstChild();
147     if (!current || linePositionMode == PositionOfInteriorLineBoxes)
148         return RenderMathMLBlock::baselinePosition(baselineType, firstLine, direction, linePositionMode);
149
150     LayoutUnit baseline = direction == HorizontalLine ? marginTop() : marginRight();
151     switch (m_kind) {
152     case UnderOver:
153     case Over:
154         if (current->nextSibling()) {
155             baseline += getOffsetHeight(current);
156             current = current->nextSibling();
157         }
158         // fall through
159     case Under:
160         ASSERT(current->isRenderBlock());
161         baseline += toRenderBox(current)->firstLineBoxBaseline();
162     }
163
164     return baseline;
165 }
166
167 }
168
169 #endif // ENABLE(MATHML)