1dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block/*
2dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
3dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block *
4dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * Redistribution and use in source and binary forms, with or without
5dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * modification, are permitted provided that the following conditions
6dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * are met:
7dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * 1. Redistributions of source code must retain the above copyright
8dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block *    notice, this list of conditions and the following disclaimer.
9dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * 2. Redistributions in binary form must reproduce the above copyright
10dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block *    notice, this list of conditions and the following disclaimer in the
11dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block *    documentation and/or other materials provided with the distribution.
12dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block *
13dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block */
25dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
26dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "config.h"
27dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
28dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#if ENABLE(MATHML)
29dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
30dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "RenderMathMLUnderOver.h"
31dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
32dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "FontSelector.h"
33dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "MathMLNames.h"
34dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
35dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blocknamespace WebCore {
36dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
37dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockusing namespace MathMLNames;
38dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
39dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockstatic const double gOverSpacingAdjustment = 0.5;
40dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
41dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve BlockRenderMathMLUnderOver::RenderMathMLUnderOver(Node* expression)
42dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    : RenderMathMLBlock(expression)
43dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{
44dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    Element* element = static_cast<Element*>(expression);
45dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    // Determine what kind of under/over expression we have by element name
46dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
47dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    if (element->hasLocalName(MathMLNames::munderTag))
48dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        m_kind = Under;
49dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    else if (element->hasLocalName(MathMLNames::moverTag))
50dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        m_kind = Over;
51dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    else if (element->hasLocalName(MathMLNames::munderoverTag))
52dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        m_kind = UnderOver;
53dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    else
54dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        m_kind = Under;
55dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
56dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
57dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
58dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockvoid RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild)
59dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{
60dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RenderMathMLBlock* row = new (renderArena()) RenderMathMLBlock(node());
61dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RefPtr<RenderStyle> rowStyle = makeBlockStyle();
62dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    row->setStyle(rowStyle.release());
63dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
64dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    // look through the children for rendered elements counting the blocks so we know what child
65dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    // we are adding
66dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    int blocks = 0;
67dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RenderObject* current = this->firstChild();
68dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    while (current) {
69dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        blocks++;
70dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        current = current->nextSibling();
71dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    }
72dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
73dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    switch (blocks) {
74dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case 0:
75dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // this is the base so just append it
76dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        RenderBlock::addChild(row, beforeChild);
77dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        break;
78dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case 1:
79dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // the under or over
80dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // FIXME: text-align: center does not work
81dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        row->style()->setTextAlign(CENTER);
82dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if (m_kind == Over) {
83dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // add the over as first
84dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            RenderBlock::addChild(row, firstChild());
85dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        } else {
86dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // add the under as last
87dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            RenderBlock::addChild(row, beforeChild);
88dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        }
89dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        break;
90dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case 2:
91dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // the under or over
92dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // FIXME: text-align: center does not work
93dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        row->style()->setTextAlign(CENTER);
94dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if (m_kind == UnderOver) {
95dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // add the over as first
96dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            RenderBlock::addChild(row, firstChild());
97dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        } else {
98dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // we really shouldn't get here as only munderover should have three children
99dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            RenderBlock::addChild(row, beforeChild);
100dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        }
101dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        break;
102dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    default:
103dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // munderover shouldn't have more than three children.  In theory we shouldn't
104dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // get here if the MathML is correctly formed, but that isn't a guarantee.
105dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // We will treat this as another under element and they'll get something funky.
106dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        RenderBlock::addChild(row, beforeChild);
107dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    }
108dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    row->addChild(child);
109dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
110dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
111dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockinline int getOffsetHeight(RenderObject* obj)
112dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{
113dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    if (obj->isBoxModelObject()) {
114dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        RenderBoxModelObject* box = toRenderBoxModelObject(obj);
115dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return box->offsetHeight();
116dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    }
117dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
118dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    return 0;
119dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
120dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
121e14391e94c850b8bd03680c23b38978db68687a8John Reckvoid RenderMathMLUnderOver::stretchToHeight(int height)
122dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{
123dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
124dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RenderObject* base = firstChild();
125dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    if (!base)
126dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return;
127dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
128dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    // For over or underover, the base is the sibling of the first child
129dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    if (m_kind != Under)
130dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        base = base->nextSibling();
131dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
132dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    if (!base)
133dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return;
134dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
135dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    // use the child of the row which is the actual base
136dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    base = base->firstChild();
137dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
138dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    if (base && base->isRenderMathMLBlock()) {
139dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        RenderMathMLBlock* block = toRenderMathMLBlock(base);
140dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        block->stretchToHeight(height);
141e14391e94c850b8bd03680c23b38978db68687a8John Reck        setNeedsLayout(true);
142dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    }
143dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
144dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
145dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockvoid RenderMathMLUnderOver::layout()
146dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{
147dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RenderBlock::layout();
148dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RenderObject* over = 0;
149dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RenderObject* base = 0;
150dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    switch (m_kind) {
151dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case Over:
152dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // We need to calculate the baseline over the over versus the start of the base and
153dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // adjust the placement of the base.
154dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        over = firstChild();
155dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if (over) {
156dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
157dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // FIXME: bases that ascend higher than the line box intrude into the over
158a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch            if (!over->firstChild()->isBoxModelObject())
159a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                break;
160a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
1616b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner            int overSpacing = static_cast<int>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
162dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
163dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // base row wrapper
164dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            base = over->nextSibling();
165dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if (base) {
166dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                if (overSpacing > 0)
167dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                    base->style()->setMarginTop(Length(-overSpacing, Fixed));
168dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                else
169dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                    base->style()->setMarginTop(Length(0, Fixed));
170dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            }
171dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
172dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        }
173dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        break;
174dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case Under:
175dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // FIXME: Non-ascending glyphs in the under should be moved closer to the base
176dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
177dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // We need to calculate the baseline of the base versus the start of the under block and
178dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // adjust the placement of the under block.
179dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
180dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // base row wrapper
181dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        base = firstChild();
182dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if (base) {
183dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            int baseHeight = getOffsetHeight(base);
184dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // actual base
185dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            base = base->firstChild();
186a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch            if (!base->isBoxModelObject())
187a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                break;
188a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
189dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // FIXME: We need to look at the space between a single maximum height of
190dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            //        the line boxes and the baseline and squeeze them together
1916b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner            int underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
192dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
193dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // adjust the base's intrusion into the under
194dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            RenderObject* under = lastChild();
195dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if (under && underSpacing > 0)
196dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                under->style()->setMarginTop(Length(-underSpacing, Fixed));
197dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        }
198dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        break;
199dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case UnderOver:
200dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // FIXME: Non-descending glyphs in the over should be moved closer to the base
201dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // FIXME: Non-ascending glyphs in the under should be moved closer to the base
202dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
203dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // We need to calculate the baseline of the over versus the start of the base and
204dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        // adjust the placement of the base.
205dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
206dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        over = firstChild();
207dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if (over) {
208dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
209dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // FIXME: bases that ascend higher than the line box intrude into the over
210a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch            if (!over->firstChild()->isBoxModelObject())
211a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                break;
2126b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner            int overSpacing = static_cast<int>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
213dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
214dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // base row wrapper
215dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            base = over->nextSibling();
216dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
217dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if (base) {
218dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                if (overSpacing > 0)
219dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                    base->style()->setMarginTop(Length(-overSpacing, Fixed));
220dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
221dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                // We need to calculate the baseline of the base versus the start of the under block and
222dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                // adjust the placement of the under block.
223dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
224dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                int baseHeight = getOffsetHeight(base);
225dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                // actual base
226dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                base = base->firstChild();
227a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                if (!base->isBoxModelObject())
228a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                    break;
229a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
230dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                // FIXME: We need to look at the space between a single maximum height of
231dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                //        the line boxes and the baseline and squeeze them together
2326b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner                int underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
233dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
234dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                RenderObject* under = lastChild();
235dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                if (under && under->firstChild()->isRenderInline() && underSpacing > 0)
236dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                    under->style()->setMarginTop(Length(-underSpacing, Fixed));
237dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
238dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            }
239dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        }
240dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        break;
241dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    }
242e14391e94c850b8bd03680c23b38978db68687a8John Reck    setNeedsLayout(true);
243dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    RenderBlock::layout();
244dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
245dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
2466b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brennerint RenderMathMLUnderOver::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
247dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{
248e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block    RenderObject* current = firstChild();
249e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block    if (!current)
2506b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
251e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block
252dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    int baseline = 0;
253dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    switch (m_kind) {
254dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case UnderOver:
255dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case Over:
256dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        baseline += getOffsetHeight(current);
257dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        current = current->nextSibling();
258dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if (current) {
259dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // actual base
260dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            RenderObject* base = current->firstChild();
261a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch            if (!base || !base->isBoxModelObject())
262e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block                break;
2636b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner            baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, firstLine, HorizontalLine, linePositionMode);
264dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            // added the negative top margin
265dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            baseline += current->style()->marginTop().value();
266dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        }
267dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        break;
268dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    case Under:
269e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block        RenderObject* base = current->firstChild();
270a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        if (base && base->isBoxModelObject())
2716b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner            baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
272dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    }
273e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block
274e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block    // FIXME: Where is the extra 2-3px adjusted for zoom coming from?
275e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block    float zoomFactor = style()->effectiveZoom();
276e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block    baseline += static_cast<int>((zoomFactor > 1.25 ? 2 : 3) * zoomFactor);
277dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    return baseline;
278dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
279dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
280dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
281dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockint RenderMathMLUnderOver::nonOperatorHeight() const
282dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{
283e14391e94c850b8bd03680c23b38978db68687a8John Reck    int nonOperators = 0;
284e14391e94c850b8bd03680c23b38978db68687a8John Reck    for (RenderObject* current = firstChild(); current; current = current->nextSibling()) {
285e14391e94c850b8bd03680c23b38978db68687a8John Reck        if (current->firstChild()->isRenderMathMLBlock()) {
286e14391e94c850b8bd03680c23b38978db68687a8John Reck            RenderMathMLBlock* block = toRenderMathMLBlock(current->firstChild());
287e14391e94c850b8bd03680c23b38978db68687a8John Reck            if (!block->isRenderMathMLOperator())
288e14391e94c850b8bd03680c23b38978db68687a8John Reck                nonOperators += getOffsetHeight(current);
289e14391e94c850b8bd03680c23b38978db68687a8John Reck        } else {
290e14391e94c850b8bd03680c23b38978db68687a8John Reck            nonOperators += getOffsetHeight(current);
291e14391e94c850b8bd03680c23b38978db68687a8John Reck        }
292e14391e94c850b8bd03680c23b38978db68687a8John Reck    }
293e14391e94c850b8bd03680c23b38978db68687a8John Reck    return nonOperators;
294dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
295dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
296dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
297dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
298dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
299dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#endif // ENABLE(MATHML)
300