2d5b1ccba0312fd855143cd0a1d177dc97c98b8a
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLBlock.cpp
1 /*
2  * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
3  * Copyright (C) 2012 David Barton (dbarton@mathscribe.com). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #if ENABLE(MATHML)
30
31 #include "RenderMathMLBlock.h"
32
33 #include "GraphicsContext.h"
34 #include "MathMLNames.h"
35 #include "RenderView.h"
36
37 #if ENABLE(DEBUG_MATH_LAYOUT)
38 #include "PaintInfo.h"
39 #endif
40
41 namespace WebCore {
42     
43 using namespace MathMLNames;
44     
45 RenderMathMLBlock::RenderMathMLBlock(Element* container)
46     : RenderFlexibleBox(container)
47     , m_ignoreInAccessibilityTree(false)
48     , m_preferredLogicalHeight(preferredLogicalHeightUnset)
49 {
50 }
51
52 bool RenderMathMLBlock::isChildAllowed(RenderObject* child, RenderStyle*) const
53 {
54     return child->node() && child->node()->nodeType() == Node::ELEMENT_NODE;
55 }
56
57 void RenderMathMLBlock::computePreferredLogicalWidths()
58 {
59     ASSERT(preferredLogicalWidthsDirty());
60     m_preferredLogicalHeight = preferredLogicalHeightUnset;
61     RenderFlexibleBox::computePreferredLogicalWidths();
62 }
63
64 RenderMathMLBlock* RenderMathMLBlock::createAnonymousMathMLBlock(EDisplay display)
65 {
66     RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), display);
67     RenderMathMLBlock* newBlock = new (renderArena()) RenderMathMLBlock(0);
68     newBlock->setDocumentForAnonymous(document());
69     newBlock->setStyle(newStyle.release());
70     return newBlock;
71 }
72
73 // An arbitrary large value, like RenderBlock.cpp BLOCK_MAX_WIDTH or FixedTableLayout.cpp TABLE_MAX_WIDTH.
74 static const int cLargeLogicalWidth = 15000;
75
76 void RenderMathMLBlock::computeChildrenPreferredLogicalHeights()
77 {
78     ASSERT(needsLayout());
79     
80     // Ensure a full repaint will happen after layout finishes.
81     setNeedsLayout(true, MarkOnlyThis);
82     
83     RenderView* renderView = view();
84     bool hadLayoutState = renderView->layoutState();
85     if (!hadLayoutState)
86         renderView->pushLayoutState(this);
87     {
88         LayoutStateDisabler layoutStateDisabler(renderView);
89         
90         LayoutUnit oldAvailableLogicalWidth = availableLogicalWidth();
91         setLogicalWidth(cLargeLogicalWidth);
92         
93         for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
94             if (!child->isBox())
95                 continue;
96             
97             // Because our width changed, |child| may need layout.
98             if (child->maxPreferredLogicalWidth() > oldAvailableLogicalWidth)
99                 child->setNeedsLayout(true, MarkOnlyThis);
100             
101             RenderMathMLBlock* childMathMLBlock = child->isRenderMathMLBlock() ? toRenderMathMLBlock(child) : 0;
102             if (childMathMLBlock && !childMathMLBlock->isPreferredLogicalHeightDirty())
103                 continue;
104             // Layout our child to compute its preferred logical height.
105             child->layoutIfNeeded();
106             if (childMathMLBlock)
107                 childMathMLBlock->setPreferredLogicalHeight(childMathMLBlock->logicalHeight());
108         }
109     }
110     if (!hadLayoutState)
111         renderView->popLayoutState(this);
112 }
113
114 LayoutUnit RenderMathMLBlock::preferredLogicalHeightAfterSizing(RenderObject* child)
115 {
116     if (child->isRenderMathMLBlock())
117         return toRenderMathMLBlock(child)->preferredLogicalHeight();
118     if (child->isBox()) {
119         ASSERT(!child->needsLayout());
120         return toRenderBox(child)->logicalHeight();
121     }
122     // This currently ignores -webkit-line-box-contain:
123     return child->style()->fontSize();
124 }
125
126 int RenderMathMLBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
127 {
128     // mathml.css sets math { -webkit-line-box-contain: glyphs replaced; line-height: 0; }, so when linePositionMode == PositionOfInteriorLineBoxes we want to
129     // return 0 here to match our line-height. This matters when RootInlineBox::ascentAndDescentForBox is called on a RootInlineBox for an inline-block.
130     if (linePositionMode == PositionOfInteriorLineBoxes)
131         return 0;
132     
133     LayoutUnit baseline = firstLineBoxBaseline(); // FIXME: This may be unnecessary after flex baselines are implemented (https://bugs.webkit.org/show_bug.cgi?id=96188).
134     if (baseline != -1)
135         return baseline;
136     
137     return RenderFlexibleBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
138 }
139
140 const char* RenderMathMLBlock::renderName() const
141 {
142     EDisplay display = style()->display();
143     if (display == FLEX)
144         return isAnonymous() ? "RenderMathMLBlock (anonymous, flex)" : "RenderMathMLBlock (flex)";
145     if (display == INLINE_FLEX)
146         return isAnonymous() ? "RenderMathMLBlock (anonymous, inline-flex)" : "RenderMathMLBlock (inline-flex)";
147     // |display| should be one of the above.
148     ASSERT_NOT_REACHED();
149     return isAnonymous() ? "RenderMathMLBlock (anonymous)" : "RenderMathMLBlock";
150 }
151
152 #if ENABLE(DEBUG_MATH_LAYOUT)
153 void RenderMathMLBlock::paint(PaintInfo& info, const LayoutPoint& paintOffset)
154 {
155     RenderFlexibleBox::paint(info, paintOffset);
156     
157     if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground)
158         return;
159
160     IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location());
161
162     GraphicsContextStateSaver stateSaver(*info.context);
163     
164     info.context->setStrokeThickness(1.0f);
165     info.context->setStrokeStyle(SolidStroke);
166     info.context->setStrokeColor(Color(0, 0, 255), ColorSpaceSRGB);
167     
168     info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y()));
169     info.context->drawLine(IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y()), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()));
170     info.context->drawLine(IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()));
171     info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()));
172     
173     int topStart = paddingTop();
174     
175     info.context->setStrokeColor(Color(0, 255, 0), ColorSpaceSRGB);
176     
177     info.context->drawLine(IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + topStart), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + topStart));
178     
179     int baseline = roundToInt(baselinePosition(AlphabeticBaseline, true, HorizontalLine));
180     
181     info.context->setStrokeColor(Color(255, 0, 0), ColorSpaceSRGB);
182     
183     info.context->drawLine(IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + baseline), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + baseline));
184 }
185 #endif // ENABLE(DEBUG_MATH_LAYOUT)
186
187 int RenderMathMLTable::firstLineBoxBaseline() const
188 {
189     // In legal MathML, we'll have a MathML parent. That RenderFlexibleBox parent will use our firstLineBoxBaseline() for baseline alignment, per
190     // http://dev.w3.org/csswg/css3-flexbox/#flex-baselines. We want to vertically center an <mtable>, such as a matrix. Essentially the whole <mtable> element fits on a
191     // single line, whose baseline gives this centering. This is different than RenderTable::firstLineBoxBaseline, which returns the baseline of the first row of a <table>.
192     return (logicalHeight() + style()->fontMetrics().xHeight()) / 2;
193 }
194
195 }    
196
197 #endif