[MathML] Remove RenderTree modification during layout and refactor the StretchyOp...
authormrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Oct 2013 00:08:08 +0000 (00:08 +0000)
committermrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Oct 2013 00:08:08 +0000 (00:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=121416

Source/WebCore:

Reviewed by David Hyatt.

Tests: mathml/presentation/mo-minus.html
       mathml/presentation/mo-stacked-glyphs.html
       mathml/presentation/mo-stretchy-vertical-bar.html
       mathml/very-large-stretchy-operators.html

MathML stretched operators by both modifying the width of the operator
and adding children to the operator node in the render tree.

Instead we make the operator width equal to the widest glyph possible that we use
to represent the operator.  Additionally instead of rendering stretchy glyphs via
stacked operator pieces in separate render tree nodes, keep only one node for the
glyph, and use a custom paint method to paint the stacked representation.

With this patch, rendering seems roughly equivalent on Mac and markedly better
on Linux.

* css/mathml.css:
Not sure what this line-height:0 was here for, but it caused bugs with the new code

* mathml/MathMLTextElement.cpp:
(WebCore::MathMLTextElement::attach):
(WebCore):
(WebCore::MathMLTextElement::childrenChanged):
Need to update the anonymous render tree below <mo> elements when their
children change or when the renderer is first attached.

* mathml/MathMLTextElement.h:
(MathMLTextElement):
* rendering/mathml/RenderMathMLBlock.cpp:
(WebCore::RenderMathMLBlock::RenderMathMLBlock):
* rendering/mathml/RenderMathMLBlock.h:
Now that the preferred width doesn't depend on the height, we don't
need to override computePreferredLogicalWidths at all.

* rendering/mathml/RenderMathMLFenced.cpp:
(WebCore::RenderMathMLFenced::makeFences):
(WebCore::RenderMathMLFenced::styleDidChange):
Need to update the anonymous renderers for the anonymous RenderMathMLOperators.

* rendering/mathml/RenderMathMLOperator.cpp:
(WebCore::RenderMathMLOperator::expandedStretchHeight):
Store the non-expanded stretch height so that we can detect when the height
actually changed.

(WebCore::RenderMathMLOperator::stretchToHeight):
Only update the style on the anonymous render tree since this is called
from RenderMathMLRow::layout

(WebCore::RenderMathMLOperator::styleDidChange):
(WebCore::RenderMathMLOperator::glyphBoundsForCharacter): A helper to get glyph boundaries.
(WebCore::RenderMathMLOperator::glyphHeightForCharacter): Ditto for glyph width.
(WebCore::RenderMathMLOperator::advanceForCharacter): The advance is different from the width,
and we want the width of the operator to be the advance instead of the tight bounding width.
(WebCore::RenderMathMLOperator::computePreferredLogicalWidths): Use the max of all possible glyphs
we can use to render this operator.
(WebCore::RenderMathMLOperator::updateFromElement): We add a child for rendering the non-scaled
version of the glyph.
(WebCore::RenderMathMLOperator::firstCharacterForStretching): Helper to figure out what character
is the character used for stretching operations.
(WebCore::RenderMathMLOperator::findAcceptableStretchyCharacter): Helper to find an acceptable set
of glyph pieces for stretching characters.
(WebCore::RenderMathMLOperator::updateStyle): Resize the operator to the appropriate height and hide
the child if we are using the stretchy style.
(WebCore::RenderMathMLOperator::firstLineBoxBaseline):
(WebCore::RenderMathMLOperator::paintCharacter): Helper to paint a single character component of the
stretchy glyph.
(WebCore::RenderMathMLOperator::fillWithExtensionGlyph): Helper to paint the extender glue between
features of the stretchy glyph.
(WebCore::RenderMathMLOperator::paint): Properly paint stretchy glyphs.
* rendering/mathml/RenderMathMLOperator.h: Update method declarations.

LayoutTests:

Patch by Frédéric Wang <fred.wang@free.fr> on 2013-10-04
Reviewed by David Hyatt.

Add some reftests for stretchy operators.

* TestExpectations: The remaining MathML pixel tests are broken by this patch.
* LayoutTests/platform/gtk/TestExpectations:
* LayoutTests/platform/mac/TestExpectations:
* LayoutTests/platform/win/TestExpectations:
* mathml/presentation/mo-minus.html: Added.
* mathml/presentation/mo-stacked-glyphs-expected.html: Added.
* mathml/presentation/mo-stacked-glyphs.html: Added.
* mathml/presentation/mo-stretchy-vertical-bar-expected-mismatch.html: Added.
* mathml/presentation/mo-stretchy-vertical-bar.html: Added.
* mathml/very-large-stretchy-operators-expected.txt: Added.
* mathml/very-large-stretchy-operators.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@156930 268f45cc-cd09-0410-ab3c-d52691b4dbfc

27 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/mathml/presentation/mo-minus-expected.html [new file with mode: 0644]
LayoutTests/mathml/presentation/mo-minus.html [new file with mode: 0644]
LayoutTests/mathml/presentation/mo-stacked-glyphs-expected.html [new file with mode: 0644]
LayoutTests/mathml/presentation/mo-stacked-glyphs.html [new file with mode: 0644]
LayoutTests/mathml/presentation/mo-stretchy-vertical-bar-expected-mismatch.html [new file with mode: 0644]
LayoutTests/mathml/presentation/mo-stretchy-vertical-bar.html [new file with mode: 0644]
LayoutTests/mathml/very-large-stretchy-operators-expected.txt [new file with mode: 0644]
LayoutTests/mathml/very-large-stretchy-operators.html [new file with mode: 0644]
LayoutTests/platform/gtk/TestExpectations
LayoutTests/platform/mac/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/css/mathml.css
Source/WebCore/mathml/MathMLTextElement.cpp
Source/WebCore/mathml/MathMLTextElement.h
Source/WebCore/rendering/RenderFlexibleBox.h
Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp
Source/WebCore/rendering/mathml/RenderMathMLBlock.h
Source/WebCore/rendering/mathml/RenderMathMLFenced.cpp
Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp
Source/WebCore/rendering/mathml/RenderMathMLOperator.h
Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp
Source/WebCore/rendering/mathml/RenderMathMLRoot.h
Source/WebCore/rendering/mathml/RenderMathMLRow.cpp
Source/WebCore/rendering/mathml/RenderMathMLRow.h

index 6ae60daf213ae2eabd8487a0418d0f8527a10891..b3510f3d663890d02f78bfb5268900cdebbcf610 100644 (file)
@@ -1,3 +1,24 @@
+2013-10-04  Frédéric Wang  <fred.wang@free.fr>
+
+        [MathML] Remove RenderTree modification during layout and refactor the StretchyOp code
+        https://bugs.webkit.org/show_bug.cgi?id=121416
+
+        Reviewed by David Hyatt.
+
+        Add some reftests for stretchy operators.
+
+        * TestExpectations: The remaining MathML pixel tests are broken by this patch.
+        * LayoutTests/platform/gtk/TestExpectations:
+        * LayoutTests/platform/mac/TestExpectations:
+        * LayoutTests/platform/win/TestExpectations:
+        * mathml/presentation/mo-minus.html: Added.
+        * mathml/presentation/mo-stacked-glyphs-expected.html: Added.
+        * mathml/presentation/mo-stacked-glyphs.html: Added.
+        * mathml/presentation/mo-stretchy-vertical-bar-expected-mismatch.html: Added.
+        * mathml/presentation/mo-stretchy-vertical-bar.html: Added.
+        * mathml/very-large-stretchy-operators-expected.txt: Added.
+        * mathml/very-large-stretchy-operators.html: Added.
+
 2013-10-04  Oliver Hunt  <oliver@apple.com>
 
         Update all the tests, and include new file.
index c36c89c3a5d1cc4280bc8bc26f6c770d05e0ba5d..486f01d93ae676b2f4d301744cd6fdfa1bc6e8b8 100644 (file)
@@ -19,6 +19,8 @@ webkit.org/b/76280 media/W3C/video/networkState/networkState_during_progress.htm
 # MathML pixel tests
 webkit.org/b/99618  mathml/presentation/roots.xhtml [ Failure ]
 webkit.org/b/99618  mathml/presentation/mo-stretch.html [ Failure ]
+webkit.org/b/57700  mathml/presentation/row.xhtml [ Failure ]
+webkit.org/b/57700  mathml/presentation/mo.xhtml [ Failure ]
 
 # These conformace tests are no longer in sync with the latest specification
 # and expect compareDocumentPosition() to return:
diff --git a/LayoutTests/mathml/presentation/mo-minus-expected.html b/LayoutTests/mathml/presentation/mo-minus-expected.html
new file mode 100644 (file)
index 0000000..f392ff8
--- /dev/null
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+  <head>
+    <title>mo-minus</title>
+  </head>
+  <body>
+
+    <math><mi>a</mi><mo>&#x2212;</mo><mi>a</mi></math>
+
+  </body>
+</html>
diff --git a/LayoutTests/mathml/presentation/mo-minus.html b/LayoutTests/mathml/presentation/mo-minus.html
new file mode 100644 (file)
index 0000000..f698209
--- /dev/null
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+  <head>
+    <title>mo-minus</title>
+  </head>
+  <body>
+
+    <!-- This hyphen minus should be displayed a minus sign U+2212 -->
+    <math><mi>a</mi><mo>-</mo><mi>a</mi></math>
+
+  </body>
+</html>
diff --git a/LayoutTests/mathml/presentation/mo-stacked-glyphs-expected.html b/LayoutTests/mathml/presentation/mo-stacked-glyphs-expected.html
new file mode 100644 (file)
index 0000000..d9f2a64
--- /dev/null
@@ -0,0 +1,46 @@
+<!doctype html>
+<html>
+  <head>
+    <title>mo stacked glyphs</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+
+    <div style="position: absolute; top: 25px; left: 0; opacity: 0.5;">
+      <math>
+        <mrow>
+          <mo>(</mo>
+          <mo>)</mo>
+          <mo>{</mo>
+          <mo>}</mo>
+          <mo>[</mo>
+          <mo>]</mo>
+          <mo>|</mo>
+          <mo stretchy="true">&#x222b;</mo>
+          <mspace height="200px" depth="200px"/>
+        </mrow>
+      </math>
+    </div>
+
+    <div style="position: absolute; top: 25px; left: 0; opacity: 0.5;">
+      <math>
+        <mrow>
+          <mo>(</mo>
+          <mo>)</mo>
+          <mo>{</mo>
+          <mo>}</mo>
+          <mo>[</mo>
+          <mo>]</mo>
+          <mo>|</mo>
+          <mo stretchy="true">&#x222b;</mo>
+          <mspace height="200px" depth="200px"/>
+        </mrow>
+      </math>
+    </div>
+
+    <div style="position: absolute; top: 0px; left: 0; width: 100%; height: 150px; background: red;"></div>
+    <div style="position: absolute; top: 200px; left: 0; width: 100%; height: 150px; background: red;"></div>
+    <div style="position: absolute; top: 400px; left: 0; width: 100%; height: 150px; background: red;"></div>
+
+  </body>
+</html>
diff --git a/LayoutTests/mathml/presentation/mo-stacked-glyphs.html b/LayoutTests/mathml/presentation/mo-stacked-glyphs.html
new file mode 100644 (file)
index 0000000..a93e725
--- /dev/null
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+  <head>
+    <title>mo stacked glyphs</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+
+    <!-- Test some stretchy operators requiring stacked glyphs at large sizes. -->
+    <div style="position: absolute; top: 25px; left: 0; opacity: 0.5;">
+      <math>
+        <mrow>
+          <mo>(</mo>
+          <mo>)</mo>
+          <mo>{</mo>
+          <mo>}</mo>
+          <mo>[</mo>
+          <mo>]</mo>
+          <mo>|</mo>
+          <mo stretchy="true">&#x222b;</mo>
+          <mspace height="200px" depth="200px"/>
+        </mrow>
+      </math>
+    </div>
+
+    <!-- Same operators, but shifted vertically by one pixel. -->
+    <div style="position: absolute; top: 26px; left: 0; opacity: 0.5;">
+      <math>
+        <mrow>
+          <mo>(</mo>
+          <mo>)</mo>
+          <mo>{</mo>
+          <mo>}</mo>
+          <mo>[</mo>
+          <mo>]</mo>
+          <mo>|</mo>
+          <mo stretchy="true">&#x222b;</mo>
+          <mspace height="200px" depth="200px"/>
+        </mrow>
+      </math>
+    </div>
+    
+    <!-- Some horizontal bands to hide the top/middle/bottom parts of the
+         operators and only show the straight lines. Note that the two previous
+         <div> elements have opacity=0.5 and are only shifted vertically by
+         one pixel. Hence the overlapped <div>'s should look like one <div> of
+         opacity=1, provided there are no gaps between the stacked glyphs and
+         that these glyphs are perfectly aligned. -->
+        <div style="position: absolute; top: 0px; left: 0; width: 100%; height: 150px; background: red;"></div>
+        <div style="position: absolute; top: 200px; left: 0; width: 100%; height: 150px; background: red;"></div>
+        <div style="position: absolute; top: 400px; left: 0; width: 100%; height: 150px; background: red;"></div>
+
+  </body>
+</html>
diff --git a/LayoutTests/mathml/presentation/mo-stretchy-vertical-bar-expected-mismatch.html b/LayoutTests/mathml/presentation/mo-stretchy-vertical-bar-expected-mismatch.html
new file mode 100644 (file)
index 0000000..e9e6c0e
--- /dev/null
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+  <head>
+    <title>mo stretchy vertical bar</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+
+    <div style="position: absolute; top: 0px; left: 0;">
+      <math>
+        <mrow>
+          <mspace height="190px" depth="190px" width="200px" mathbackground="red"/>
+          <mspace height="200px" depth="200px"/>
+        </mrow>
+      </math>
+    </div>
+  </body>
+</html>
diff --git a/LayoutTests/mathml/presentation/mo-stretchy-vertical-bar.html b/LayoutTests/mathml/presentation/mo-stretchy-vertical-bar.html
new file mode 100644 (file)
index 0000000..349ab1e
--- /dev/null
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+  <head>
+    <title>mo stretchy vertical bar</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+
+    <!-- This verifies that the vertical bar stretches to an arbitrary size -->
+    <div style="position: absolute; top: 0px; left: 0;">
+      <math>
+        <mrow>
+          <mo>|</mo>
+          <mspace height="200px" depth="200px"/>
+        </mrow>
+      </math>
+    </div>
+
+    <!-- Red rectangle that should only partially cover the bar. -->
+    <div style="position: absolute; top: 0px; left: 0;">
+      <math>
+        <mrow>
+          <mspace height="190px" depth="190px" width="200px" mathbackground="red"/>
+          <mspace height="200px" depth="200px"/>
+        </mrow>
+      </math>
+    </div>
+  </body>
+</html>
diff --git a/LayoutTests/mathml/very-large-stretchy-operators-expected.txt b/LayoutTests/mathml/very-large-stretchy-operators-expected.txt
new file mode 100644 (file)
index 0000000..af6e4f7
--- /dev/null
@@ -0,0 +1,3 @@
+This test should not timeout.
+
+
diff --git a/LayoutTests/mathml/very-large-stretchy-operators.html b/LayoutTests/mathml/very-large-stretchy-operators.html
new file mode 100644 (file)
index 0000000..6b0ec2b
--- /dev/null
@@ -0,0 +1,94 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Large Stretchy Operator</title>
+    <meta charset="utf-8"/>
+    <style>
+      span { display: inline-block; width: 10px; background: red; }
+    </style>
+    <script>
+      if (window.testRunner)
+        testRunner.dumpAsText();
+    </script>
+  </head>
+  <body>
+
+    <p>This test should not timeout.</p>
+
+    <math>
+      <mfenced>
+        <mrow>
+          <mo>[</mo>
+          <mfenced>
+            <mrow>
+              <mo>[</mo>
+              <mfenced>
+                <mrow>
+                  <mo>[</mo>
+                  <mfenced>
+                    <mrow>
+                      <mo>[</mo>
+                      <mfenced>
+                        <mrow>
+                          <mo>[</mo>
+                          <mfenced>
+                            <mrow>
+                              <mo>[</mo>
+                              <mfenced>
+                                <mrow>
+                                  <mo>[</mo>
+                                  <mfenced>
+                                    <mrow>
+                                      <mo>[</mo>
+                                      <mfenced>
+                                        <mrow>
+                                          <mo>[</mo>
+                                          <mfenced>
+                                            <mrow>
+                                              <mo>[</mo>
+                                              <mtext><span height="2500px"></span></mtext>
+                                              <mo>]</mo>
+                                            </mrow>
+                                            <mtext><span style="height: 5000px"></span></mtext>
+                                          </mfenced>
+                                          <mo>]</mo>
+                                        </mrow>
+                                        <mtext><span style="height: 20000px"></span></mtext>
+                                      </mfenced>
+                                      <mo>]</mo>
+                                    </mrow>
+                                    <mtext><span style="height: 40000px"></span></mtext>
+                                  </mfenced>
+                                  <mo>]</mo>
+                                </mrow>
+                                <mtext><span style="height: 80000px"></span></mtext>
+                              </mfenced>
+                              <mo>]</mo>
+                            </mrow>
+                            <mtext><span style="height: 160000px"></span></mtext>
+                          </mfenced>
+                          <mo>]</mo>
+                        </mrow>
+                        <mtext><span style="height: 320000px"></span></mtext>
+                      </mfenced>
+                      <mo>]</mo>
+                    </mrow>
+                    <mtext><span style="height: 640000px"></span></mtext>
+                  </mfenced>
+                  <mo>]</mo>
+                </mrow>
+                <mtext><span style="height: 1280000px"></span></mtext>
+              </mfenced>
+              <mo>]</mo>
+            </mrow>
+            <mtext><span style="height: 2560000px"></span></mtext>
+          </mfenced>
+          <mtext><span style="height: 5120000px"></span></mtext>
+          <mo>]</mo>
+        </mrow>
+        <mtext><span style="height: 10240000px"></span></mtext>
+      </mfenced>
+    </math>
+
+  </body>
+</html>
index 6c1664c15615433616277b11a0a388001ff9194b..9637fb0a40fb2f52d6e12b035f07c788fab8009a 100644 (file)
@@ -1343,7 +1343,6 @@ webkit.org/b/118595 fast/regions/autoheight-correct-region-for-lines-2.html [ Im
 
 # MathML tests failing after MathML pixel tests were replaced with ref tests
 webkit.org/b/118904 mathml/presentation/bug95404.html [ ImageOnlyFailure ]
-webkit.org/b/119004 mathml/presentation/fenced-mi.html [ ImageOnlyFailure ]
 
 webkit.org/b/119730 fast/dom/Window/open-zero-size-as-default.html [ Failure ]
 
index 8403298e6e92f9e4db15f1c3f9cc2539db304b18..bde464ec9bad24e3cf43ea06b208a3cde0a5c851 100644 (file)
@@ -1273,7 +1273,6 @@ webkit.org/b/118269 compositing/geometry/fixed-position-flipped-writing-mode.htm
 webkit.org/b/118479 fast/repaint/fixed-right-in-page-scale.html [ Pass Crash ]
 
 webkit.org/b/118900 mathml/presentation/mspace-units.html [ ImageOnlyFailure ]
-webkit.org/b/118856 mathml/presentation/fenced-mi.html [ ImageOnlyFailure ]
 
 # Plug-in blocking callback doesn't exist in WebKit1, so skip and re-enable for WebKit2.
 plugins/unavailable-plugin-indicator-obscurity.html
index e5938197668d6dd5bb8f2ee78bfc5b25dab66c0a..53dfc774597a0a37bd045480e55ddb7f9462d8b3 100644 (file)
@@ -2703,7 +2703,6 @@ webkit.org/b/117200 mathml/presentation/mo-stretch.html
 webkit.org/b/117200 mathml/presentation/roots.xhtml
 webkit.org/b/117200 mathml/presentation/row.xhtml
 webkit.org/b/119002 mathml/presentation/bug97990.html
-webkit.org/b/118856 mathml/presentation/fenced-mi.html
 webkit.org/b/118900 mathml/presentation/mspace-units.html
 webkit.org/b/119002 mathml/presentation/scripts-subsup.html
 
index b95b4647aecedc840376e8c21be304dffc7d6896..06d00ef800768635a6b85064aba0f474d2d11ddb 100644 (file)
@@ -1,3 +1,81 @@
+2013-10-04  Ojan Vafai  <ojan@chromium.org> and Martin Robinson  <mrobinson@igalia.com>
+
+        [MathML] Remove RenderTree modification during layout and refactor the StretchyOp code
+        https://bugs.webkit.org/show_bug.cgi?id=121416
+
+        Reviewed by David Hyatt.
+
+        Tests: mathml/presentation/mo-minus.html
+               mathml/presentation/mo-stacked-glyphs.html
+               mathml/presentation/mo-stretchy-vertical-bar.html
+               mathml/very-large-stretchy-operators.html
+
+        MathML stretched operators by both modifying the width of the operator
+        and adding children to the operator node in the render tree.
+
+        Instead we make the operator width equal to the widest glyph possible that we use
+        to represent the operator.  Additionally instead of rendering stretchy glyphs via
+        stacked operator pieces in separate render tree nodes, keep only one node for the
+        glyph, and use a custom paint method to paint the stacked representation.
+
+        With this patch, rendering seems roughly equivalent on Mac and markedly better
+        on Linux.
+
+        * css/mathml.css:
+        Not sure what this line-height:0 was here for, but it caused bugs with the new code
+
+        * mathml/MathMLTextElement.cpp:
+        (WebCore::MathMLTextElement::attach):
+        (WebCore):
+        (WebCore::MathMLTextElement::childrenChanged):
+        Need to update the anonymous render tree below <mo> elements when their
+        children change or when the renderer is first attached.
+
+        * mathml/MathMLTextElement.h:
+        (MathMLTextElement):
+        * rendering/mathml/RenderMathMLBlock.cpp:
+        (WebCore::RenderMathMLBlock::RenderMathMLBlock):
+        * rendering/mathml/RenderMathMLBlock.h:
+        Now that the preferred width doesn't depend on the height, we don't
+        need to override computePreferredLogicalWidths at all.
+
+        * rendering/mathml/RenderMathMLFenced.cpp:
+        (WebCore::RenderMathMLFenced::makeFences):
+        (WebCore::RenderMathMLFenced::styleDidChange):
+        Need to update the anonymous renderers for the anonymous RenderMathMLOperators.
+
+        * rendering/mathml/RenderMathMLOperator.cpp:
+        (WebCore::RenderMathMLOperator::expandedStretchHeight):
+        Store the non-expanded stretch height so that we can detect when the height
+        actually changed.
+
+        (WebCore::RenderMathMLOperator::stretchToHeight):
+        Only update the style on the anonymous render tree since this is called
+        from RenderMathMLRow::layout
+
+        (WebCore::RenderMathMLOperator::styleDidChange):
+        (WebCore::RenderMathMLOperator::glyphBoundsForCharacter): A helper to get glyph boundaries.
+        (WebCore::RenderMathMLOperator::glyphHeightForCharacter): Ditto for glyph width.
+        (WebCore::RenderMathMLOperator::advanceForCharacter): The advance is different from the width,
+        and we want the width of the operator to be the advance instead of the tight bounding width.
+        (WebCore::RenderMathMLOperator::computePreferredLogicalWidths): Use the max of all possible glyphs
+        we can use to render this operator.
+        (WebCore::RenderMathMLOperator::updateFromElement): We add a child for rendering the non-scaled
+        version of the glyph.
+        (WebCore::RenderMathMLOperator::firstCharacterForStretching): Helper to figure out what character
+        is the character used for stretching operations.
+        (WebCore::RenderMathMLOperator::findAcceptableStretchyCharacter): Helper to find an acceptable set
+        of glyph pieces for stretching characters.
+        (WebCore::RenderMathMLOperator::updateStyle): Resize the operator to the appropriate height and hide
+        the child if we are using the stretchy style.
+        (WebCore::RenderMathMLOperator::firstLineBoxBaseline):
+        (WebCore::RenderMathMLOperator::paintCharacter): Helper to paint a single character component of the
+        stretchy glyph.
+        (WebCore::RenderMathMLOperator::fillWithExtensionGlyph): Helper to paint the extender glue between
+        features of the stretchy glyph.
+        (WebCore::RenderMathMLOperator::paint): Properly paint stretchy glyphs.
+        * rendering/mathml/RenderMathMLOperator.h: Update method declarations.
+
 2013-10-04  Ryosuke Niwa  <rniwa@webkit.org>
 
         Build fix after r156925. It collided with r156903.
index f3a2b40b098fbd10570ee2f75f011e4c15d9c77b..90983d75786ac49efe90dc0624ea33480836c468 100644 (file)
@@ -2,7 +2,6 @@
 
 math {
     -webkit-line-box-contain: glyphs replaced;
-    line-height: 0;
     text-indent: 0;
 }
 mtext {
index c8b7fe0f1d8291aa224a38e0167ce4b5b0e3679c..6465fc4a880c5deee74195cc646219a93182b17e 100644 (file)
@@ -41,6 +41,7 @@ using namespace MathMLNames;
 inline MathMLTextElement::MathMLTextElement(const QualifiedName& tagName, Document& document)
     : MathMLElement(tagName, document)
 {
+    setHasCustomStyleResolveCallbacks();
 }
 
 PassRefPtr<MathMLTextElement> MathMLTextElement::create(const QualifiedName& tagName, Document& document)
@@ -48,6 +49,20 @@ PassRefPtr<MathMLTextElement> MathMLTextElement::create(const QualifiedName& tag
     return adoptRef(new MathMLTextElement(tagName, document));
 }
 
+void MathMLTextElement::didAttachRenderers()
+{
+    MathMLElement::didAttachRenderers();
+    if (renderer())
+        renderer()->updateFromElement();
+}
+
+void MathMLTextElement::childrenChanged(const ChildChange& change)
+{
+    MathMLElement::childrenChanged(change);
+    if (renderer())
+        renderer()->updateFromElement();
+}
+
 RenderElement* MathMLTextElement::createRenderer(RenderArena& arena, RenderStyle& style)
 {
     if (hasLocalName(MathMLNames::moTag))
index 7138ed4cba08f47a204bc0cc9c0dfbd3c0ce114c..90e2bc91d296b7c649129f4b736019628806cd76 100644 (file)
@@ -35,11 +35,13 @@ namespace WebCore {
 class MathMLTextElement : public MathMLElement {
 public:
     static PassRefPtr<MathMLTextElement> create(const QualifiedName& tagName, Document&);
+    virtual void didAttachRenderers() OVERRIDE;
 
 private:
     MathMLTextElement(const QualifiedName& tagName, Document&);
 
     virtual RenderElement* createRenderer(RenderArena&, RenderStyle&);
+    virtual void childrenChanged(const ChildChange&) OVERRIDE;
 };
     
 }
index 5ea9c0ba0c0252326241615a28e771b1130d1f5b..2feac2e4624d02d23bdf5ae52c76e47b9de6a7b5 100644 (file)
@@ -53,7 +53,7 @@ public:
     virtual int firstLineBoxBaseline() const OVERRIDE;
     virtual int inlineBlockBaseline(LineDirectionMode) const OVERRIDE;
 
-    virtual void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect) OVERRIDE FINAL;
+    virtual void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect) OVERRIDE;
 
     bool isHorizontalFlow() const;
 
index 144d0747841cb379be652953bc3d7e8cb3903e36..ba91c29487774244e0400de9641e8a8e28a881b4 100644 (file)
@@ -46,7 +46,6 @@ using namespace MathMLNames;
 RenderMathMLBlock::RenderMathMLBlock(Element* container)
     : RenderFlexibleBox(container)
     , m_ignoreInAccessibilityTree(false)
-    , m_preferredLogicalHeight(preferredLogicalHeightUnset)
 {
 }
 
@@ -55,13 +54,6 @@ bool RenderMathMLBlock::isChildAllowed(RenderObject* child, RenderStyle*) const
     return child->node() && child->node()->nodeType() == Node::ELEMENT_NODE;
 }
 
-void RenderMathMLBlock::computePreferredLogicalWidths()
-{
-    ASSERT(preferredLogicalWidthsDirty());
-    m_preferredLogicalHeight = preferredLogicalHeightUnset;
-    RenderFlexibleBox::computePreferredLogicalWidths();
-}
-
 RenderMathMLBlock* RenderMathMLBlock::createAnonymousMathMLBlock(EDisplay display)
 {
     RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), display);
@@ -71,62 +63,6 @@ RenderMathMLBlock* RenderMathMLBlock::createAnonymousMathMLBlock(EDisplay displa
     return newBlock;
 }
 
-// An arbitrary large value, like RenderBlock.cpp BLOCK_MAX_WIDTH or FixedTableLayout.cpp TABLE_MAX_WIDTH.
-static const int cLargeLogicalWidth = 15000;
-
-void RenderMathMLBlock::computeChildrenPreferredLogicalHeights()
-{
-    ASSERT(needsLayout());
-
-    // This is ugly, but disable fragmentation when computing the preferred heights.
-    FragmentationDisabler fragmentationDisabler(this);
-
-    // Ensure a full repaint will happen after layout finishes.
-    setNeedsLayout(MarkOnlyThis);
-
-    bool hadLayoutState = view().layoutState();
-    if (!hadLayoutState)
-        view().pushLayoutState(this);
-
-    {
-        LayoutStateDisabler layoutStateDisabler(&view());
-        
-        LayoutUnit oldAvailableLogicalWidth = availableLogicalWidth();
-        setLogicalWidth(cLargeLogicalWidth);
-        
-        for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
-            if (!child->isBox())
-                continue;
-            
-            // Because our width changed, |child| may need layout.
-            if (child->maxPreferredLogicalWidth() > oldAvailableLogicalWidth)
-                child->setNeedsLayout(MarkOnlyThis);
-            
-            RenderMathMLBlock* childMathMLBlock = child->isRenderMathMLBlock() ? toRenderMathMLBlock(child) : 0;
-            if (childMathMLBlock && !childMathMLBlock->isPreferredLogicalHeightDirty())
-                continue;
-            // Layout our child to compute its preferred logical height.
-            child->layoutIfNeeded();
-            if (childMathMLBlock)
-                childMathMLBlock->setPreferredLogicalHeight(childMathMLBlock->logicalHeight());
-        }
-    }
-    if (!hadLayoutState)
-        view().popLayoutState(this);
-}
-
-LayoutUnit RenderMathMLBlock::preferredLogicalHeightAfterSizing(RenderObject* child)
-{
-    if (child->isRenderMathMLBlock())
-        return toRenderMathMLBlock(child)->preferredLogicalHeight();
-    if (child->isBox()) {
-        ASSERT(!child->needsLayout());
-        return toRenderBox(child)->logicalHeight();
-    }
-    // This currently ignores -webkit-line-box-contain:
-    return child->style()->fontSize();
-}
-
 int RenderMathMLBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
 {
     // mathml.css sets math { -webkit-line-box-contain: glyphs replaced; line-height: 0; }, so when linePositionMode == PositionOfInteriorLineBoxes we want to
index 23b568d39cf146da59469887b3841c6507d8370f..60d88728f37db71967169f3c6c37c6b67b66d077 100644 (file)
@@ -67,17 +67,6 @@ public:
     // https://bugs.webkit.org/show_bug.cgi?id=78617.
     virtual RenderMathMLOperator* unembellishedOperator() { return 0; }
     
-    // A MathML element's preferred logical widths often depend on its children's preferred heights, not just their widths.
-    // This is due to operator stretching and other layout fine tuning. We define an element's preferred height to be its
-    // actual height after layout inside a very wide parent.
-    bool isPreferredLogicalHeightDirty() const { return preferredLogicalWidthsDirty() || m_preferredLogicalHeight < 0; }
-    // The caller must ensure !isPreferredLogicalHeightDirty().
-    LayoutUnit preferredLogicalHeight() const { ASSERT(!isPreferredLogicalHeightDirty()); return m_preferredLogicalHeight; }
-    static const int preferredLogicalHeightUnset = -1;
-    void setPreferredLogicalHeight(LayoutUnit logicalHeight) { m_preferredLogicalHeight = logicalHeight; }
-    // computePreferredLogicalWidths() in derived classes must ensure m_preferredLogicalHeight is set to < 0 or its correct value.
-    virtual void computePreferredLogicalWidths() OVERRIDE;
-    
     virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const OVERRIDE;
     
 #if ENABLE(DEBUG_MATH_LAYOUT)
@@ -93,15 +82,6 @@ public:
 private:
     virtual const char* renderName() const OVERRIDE;
     bool m_ignoreInAccessibilityTree;
-    
-protected:
-    // Set our logical width to a large value, and compute our children's preferred logical heights.
-    void computeChildrenPreferredLogicalHeights();
-    // This can only be called after children have been sized by computeChildrenPreferredLogicalHeights().
-    static LayoutUnit preferredLogicalHeightAfterSizing(RenderObject* child);
-    
-    // m_preferredLogicalHeight is dirty if it's < 0 or preferredLogicalWidthsDirty().
-    LayoutUnit m_preferredLogicalHeight;
 };
 
 inline RenderMathMLBlock* toRenderMathMLBlock(RenderObject* object)
index 1e4aeb2cf549729dc27ee30f832e3bbc94a477df..1fcc53fbd5c395efcd9985df9d0ab1c1558e2b97 100644 (file)
@@ -97,9 +97,12 @@ RenderMathMLOperator* RenderMathMLFenced::createMathMLOperator(UChar uChar, Rend
 
 void RenderMathMLFenced::makeFences()
 {
-    RenderMathMLRow::addChild(createMathMLOperator(m_open, RenderMathMLOperator::Fence), firstChild());
+    RenderMathMLOperator* openFence = createMathMLOperator(m_open, RenderMathMLOperator::Fence);
+    RenderMathMLRow::addChild(openFence, firstChild());
     m_closeFenceRenderer = createMathMLOperator(m_close, RenderMathMLOperator::Fence);
     RenderMathMLRow::addChild(m_closeFenceRenderer);
+    openFence->updateFromElement();
+    m_closeFenceRenderer->updateFromElement();
 }
 
 void RenderMathMLFenced::addChild(RenderObject* child, RenderObject* beforeChild)
@@ -161,8 +164,13 @@ void RenderMathMLFenced::styleDidChange(StyleDifference diff, const RenderStyle*
             child->style()->inheritFrom(style());
             bool isFence = child == firstChild() || child == lastChild();
             child->style()->setMarginEnd(Length((isFence ? gFenceMarginEms : gSeparatorMarginEndEms) * style()->fontSize(), Fixed));
-            if (isFence)
+            if (isFence) {
+                ASSERT(child->isRenderMathMLBlock());
+                RenderMathMLBlock* block = toRenderMathMLBlock(child);
+                ASSERT(block->isRenderMathMLOperator());
+                toRenderMathMLOperator(block)->updateFromElement();
                 child->style()->setMarginStart(Length(gFenceMarginEms * style()->fontSize(), Fixed));
+            }
         }
     }
 }
index 1e64ac691cf8a6bdfc1b0934b1b4553c74c3780f..808bfdd453784114c533bee6cc0a4ef0eca389ce 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved.
  * Copyright (C) 2010 François Sausset (sausset@gmail.com). All rights reserved.
+ * Copyright (C) 2013 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "FontCache.h"
 #include "FontSelector.h"
 #include "MathMLNames.h"
+#include "PaintInfo.h"
 #include "RenderBlockFlow.h"
 #include "RenderText.h"
+#include "ScaleTransformOperation.h"
+#include "TransformOperations.h"
+#include <wtf/MathExtras.h>
 
 namespace WebCore {
     
 using namespace MathMLNames;
 
+// FIXME: The OpenType MATH table contains information that should override this table (http://wkbug/122297).
+static RenderMathMLOperator::StretchyCharacter stretchyCharacters[13] = {
+    { 0x28  , 0x239b, 0x239c, 0x239d, 0x0    }, // left parenthesis
+    { 0x29  , 0x239e, 0x239f, 0x23a0, 0x0    }, // right parenthesis
+    { 0x5b  , 0x23a1, 0x23a2, 0x23a3, 0x0    }, // left square bracket
+    { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0    }, // left ceiling
+    { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0    }, // left floor
+    { 0x5d  , 0x23a4, 0x23a5, 0x23a6, 0x0    }, // right square bracket
+    { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0    }, // right ceiling
+    { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0    }, // right floor
+    { 0x7b  , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
+    { 0x7c  , 0x23aa, 0x23aa, 0x23aa, 0x0    }, // vertical bar
+    { 0x2016, 0x2016, 0x2016, 0x2016, 0x0    }, // double vertical line
+    { 0x7d  , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
+    { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0    } // integral sign
+};
+
 RenderMathMLOperator::RenderMathMLOperator(Element* element)
     : RenderMathMLBlock(element)
     , m_stretchHeight(0)
     , m_operator(0)
     , m_operatorType(Default)
+    , m_stretchyCharacter(0)
 {
 }
 
@@ -53,6 +76,7 @@ RenderMathMLOperator::RenderMathMLOperator(Element* element, UChar operatorChar)
     , m_stretchHeight(0)
     , m_operator(convertHyphenMinusToMinusSign(operatorChar))
     , m_operatorType(Default)
+    , m_stretchyCharacter(0)
 {
 }
 
@@ -63,71 +87,74 @@ bool RenderMathMLOperator::isChildAllowed(RenderObject*, RenderStyle*) const
 
 static const float gOperatorExpansion = 1.2f;
 
+float RenderMathMLOperator::expandedStretchHeight() const
+{
+    return m_stretchHeight * gOperatorExpansion;
+}
+
 void RenderMathMLOperator::stretchToHeight(int height)
 {
-    height *= gOperatorExpansion;
     if (m_stretchHeight == height)
         return;
+
     m_stretchHeight = height;
-    
-    updateFromElement();
+    updateStyle();
 }
 
 void RenderMathMLOperator::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
 {
     RenderMathMLBlock::styleDidChange(diff, oldStyle);
-    
-    if (firstChild())
-        updateFromElement();
+    updateFromElement();
 }
 
-void RenderMathMLOperator::computePreferredLogicalWidths() 
+FloatRect RenderMathMLOperator::glyphBoundsForCharacter(UChar character)
 {
-    ASSERT(preferredLogicalWidthsDirty());
-
-#ifndef NDEBUG
-    // FIXME: Remove this once mathml stops modifying the render tree here.
-    SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false);
-#endif
-    
-    // Check for an uninitialized operator.
-    if (!firstChild())
-        updateFromElement();
+    GlyphData data = style()->font().glyphDataForCharacter(character, false);
+    return data.fontData->boundsForGlyph(data.glyph);
+}
 
-    RenderMathMLBlock::computePreferredLogicalWidths();
+float RenderMathMLOperator::glyphHeightForCharacter(UChar character)
+{
+    return glyphBoundsForCharacter(character).height();
 }
 
-// This is a table of stretchy characters.
-// FIXME: Should this be read from the unicode characteristics somehow?
-// table:  stretchy operator, top char, extension char, bottom char, middle char
-static struct StretchyCharacter {
-    UChar character;
-    UChar topGlyph;
-    UChar extensionGlyph;
-    UChar bottomGlyph;
-    UChar middleGlyph;
-} stretchyCharacters[13] = {
-    { 0x28  , 0x239b, 0x239c, 0x239d, 0x0    }, // left parenthesis
-    { 0x29  , 0x239e, 0x239f, 0x23a0, 0x0    }, // right parenthesis
-    { 0x5b  , 0x23a1, 0x23a2, 0x23a3, 0x0    }, // left square bracket
-    { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0    }, // left ceiling
-    { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0    }, // left floor
-    { 0x5d  , 0x23a4, 0x23a5, 0x23a6, 0x0    }, // right square bracket
-    { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0    }, // right ceiling
-    { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0    }, // right floor
-    { 0x7b  , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
-    { 0x7c  , 0x23aa, 0x23aa, 0x23aa, 0x0    }, // vertical bar
-    { 0x2016, 0x2016, 0x2016, 0x2016, 0x0    }, // double vertical line
-    { 0x7d  , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
-    { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0    } // integral sign
-};
+float RenderMathMLOperator::advanceForCharacter(UChar character)
+{
+    // Hyphen minus is always replaced by the minus sign in rendered text.
+    GlyphData data = style()->font().glyphDataForCharacter(convertHyphenMinusToMinusSign(character), false);
+    return data.fontData->widthForGlyph(data.glyph);
+}
 
-// Note glyphHeightForCharacter truncates its result to an int.
-int RenderMathMLOperator::glyphHeightForCharacter(UChar character)
+void RenderMathMLOperator::computePreferredLogicalWidths()
 {
-    GlyphData data = style()->font().glyphDataForCharacter(character, false);
-    FloatRect glyphBounds = data.fontData->boundsForGlyph(data.glyph);
-    return glyphBounds.height();
+    ASSERT(preferredLogicalWidthsDirty());
+
+    UChar stretchedCharacter;
+    bool allowStretching = shouldAllowStretching(stretchedCharacter);
+    if (!allowStretching) {
+        RenderMathMLBlock::computePreferredLogicalWidths();
+        return;
+    }
+
+    float maximumGlyphWidth = advanceForCharacter(stretchedCharacter);
+    for (unsigned index = 0; index < WTF_ARRAY_LENGTH(stretchyCharacters); ++index) {
+        if (stretchyCharacters[index].character != stretchedCharacter)
+            continue;
+
+        StretchyCharacter& character = stretchyCharacters[index];
+        if (character.topGlyph)
+            maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.topGlyph));
+        if (character.extensionGlyph)
+            maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.extensionGlyph));
+        if (character.bottomGlyph)
+            maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.bottomGlyph));
+        if (character.middleGlyph)
+            maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.middleGlyph));
+        m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = maximumGlyphWidth;
+        return;
+    }
+
+    m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = maximumGlyphWidth;
 }
 
 // FIXME: It's cleaner to only call updateFromElement when an attribute has changed. The body of
@@ -145,183 +172,236 @@ void RenderMathMLOperator::updateFromElement()
     // renderer to 0, so we need to restore it.
     element()->setRenderer(savedRenderer);
     
-    // If the operator is fixed, it will be contained in m_operator
-    UChar firstChar = m_operator;
-    
-    // This boolean indicates whether stretching is disabled via the markup.
-    bool stretchDisabled = false;
-    
-    // We may need the element later if we can't stretch.
+    RefPtr<RenderStyle> newStyle = RenderStyle::create();
+    newStyle->inheritFrom(style());
+    newStyle->setDisplay(FLEX);
+
+    RenderMathMLBlock* container = new (renderArena()) RenderMathMLBlock(element());
+    // This container doesn't offer any useful information to accessibility.
+    container->setIgnoreInAccessibilityTree(true);
+    container->setStyle(newStyle.release());
+
+    addChild(container);
+    RenderText* text = 0;
+    if (m_operator)
+        text = RenderText::createAnonymous(document(), String(&m_operator, 1));
+    else
+        text = RenderText::createAnonymous(document(), element()->textContent().replace(hyphenMinus, minusSign).impl());
+
+    // If we can't figure out the text, leave it blank.
+    if (text)
+        container->addChild(text);
+
+    updateStyle();
+    setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+bool RenderMathMLOperator::shouldAllowStretching(UChar& stretchedCharacter)
+{
     Element* mo = element();
-    AtomicString stretchyAttr = mo->getAttribute(MathMLNames::stretchyAttr);
-    stretchDisabled = equalIgnoringCase(stretchyAttr, "false");
-    
-    // If stretching isn't disabled, get the character from the text content.
-    if (!stretchDisabled && !firstChar) {
-        String opText = mo->textContent();
-        for (unsigned i = 0; !firstChar && i < opText.length(); i++) {
-            if (!isSpaceOrNewline(opText[i]))
-                firstChar = opText[i];
-        }
-    }
+    if (equalIgnoringCase(mo->getAttribute(MathMLNames::stretchyAttr), "false"))
+        return false;
 
-    // The 'index' holds the stretchable character's glyph information
-    int index = -1;
-    
-    // isStretchy indicates whether the character is streatchable via a number of factors.
-    bool isStretchy = false;
-    
-    // Check for a stretchable character.
-    if (!stretchDisabled && firstChar) {
-        const int maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
-        for (index++; index < maxIndex; index++) {
-            if (stretchyCharacters[index].character == firstChar) {
-                isStretchy = true;
-                break;
-            }
-        }
+    if (m_operator) {
+        stretchedCharacter = m_operator;
+        return true;
     }
-    
-    // We only stack glyphs if the stretch height is larger than a minimum size.
-    bool shouldStack = isStretchy && m_stretchHeight > style()->fontSize();
-    struct StretchyCharacter* partsData = 0;
-    int topGlyphHeight = 0;
-    int extensionGlyphHeight = 0;
-    int bottomGlyphHeight = 0;
-    int middleGlyphHeight = 0;
-    if (shouldStack) {
-        partsData = &stretchyCharacters[index];
-        
-        FontCachePurgePreventer fontCachePurgePreventer;
-        
-        topGlyphHeight = glyphHeightForCharacter(partsData->topGlyph);
-        extensionGlyphHeight = glyphHeightForCharacter(partsData->extensionGlyph) - 1;
-        bottomGlyphHeight = glyphHeightForCharacter(partsData->bottomGlyph);
-        if (partsData->middleGlyph)
-            middleGlyphHeight = glyphHeightForCharacter(partsData->middleGlyph) - 1;
-        shouldStack = m_stretchHeight >= topGlyphHeight + middleGlyphHeight + bottomGlyphHeight && extensionGlyphHeight > 0;
+
+    // FIXME: This does not handle surrogate pairs (http://wkbug.com/122296/).
+    String opText = mo->textContent();
+    stretchedCharacter = 0;
+    for (unsigned i = 0; i < opText.length(); ++i) {
+        // If there's more than one non-whitespace character in this node, then don't even try to stretch it.
+        if (stretchedCharacter && !isSpaceOrNewline(opText[i]))
+            return false;
+
+        if (!isSpaceOrNewline(opText[i]))
+            stretchedCharacter = opText[i];
     }
-    
-    // Either stretch is disabled or we don't have a stretchable character over the minimum height
-    if (stretchDisabled || !shouldStack) {
-        m_isStacked = false;
-        RenderBlock* container = new (renderArena()) RenderMathMLBlock(element());
-        // This container doesn't offer any useful information to accessibility.
-        toRenderMathMLBlock(container)->setIgnoreInAccessibilityTree(true);
-        
-        RefPtr<RenderStyle> newStyle = RenderStyle::create();
-        newStyle->inheritFrom(style());
-        newStyle->setDisplay(FLEX);
-        
-        // Check for a stretchable character that is under the minimum height.
-        if (!stretchDisabled && isStretchy && m_stretchHeight > style()->fontSize()) {
-            FontDescription desc = style()->fontDescription();
-            desc.setIsAbsoluteSize(true);
-            desc.setSpecifiedSize(m_stretchHeight);
-            desc.setComputedSize(m_stretchHeight);
-            newStyle->setFontDescription(desc);
-            newStyle->font().update(style()->font().fontSelector());
-        }
 
-        container->setStyle(newStyle.release());
-        addChild(container);
-        
-        // Build the text of the operator.
-        RenderText* text = 0;
-        if (m_operator) 
-            text = RenderText::createAnonymous(document(), String(&m_operator, 1));
-        else
-            text = RenderText::createAnonymous(document(), element()->textContent().replace(hyphenMinus, minusSign).impl());
-        // If we can't figure out the text, leave it blank.
-        if (text) {
-            container->addChild(text);
-        }
-    } else {
-        // Build stretchable characters as a stack of glyphs.
-        m_isStacked = true;
-        
-        // To avoid gaps, we position glyphs after the top glyph upward by 1px. We also truncate
-        // glyph heights to ints, and then reduce all but the top & bottom such heights by 1px.
-        
-        int remaining = m_stretchHeight - topGlyphHeight - bottomGlyphHeight;
-        createGlyph(partsData->topGlyph, topGlyphHeight, 0);
-        if (partsData->middleGlyph) {
-            // We have a middle glyph (e.g. a curly bracket) that requires special processing.
-            remaining -= middleGlyphHeight;
-            int half = (remaining + 1) / 2;
-            remaining -= half;
-            while (remaining > 0) {
-                int height = std::min<int>(remaining, extensionGlyphHeight);
-                createGlyph(partsData->extensionGlyph, height, -1);
-                remaining -= height;
-            }
-            
-            // The middle glyph in the stack.
-            createGlyph(partsData->middleGlyph, middleGlyphHeight, -1);
-            
-            remaining = half;
-            while (remaining > 0) {
-                int height = std::min<int>(remaining, extensionGlyphHeight);
-                createGlyph(partsData->extensionGlyph, height, -1);
-                remaining -= height;
-            }
-        } else {
-            // We do not have a middle glyph and so we just extend from the top to the bottom glyph.
-            while (remaining > 0) {
-                int height = std::min<int>(remaining, extensionGlyphHeight);
-                createGlyph(partsData->extensionGlyph, height, -1);
-                remaining -= height;
-            }
+    return stretchedCharacter;
+}
+
+// FIXME: We should also look at alternate characters defined in the OpenType MATH table (http://wkbug/122297).
+RenderMathMLOperator::StretchyCharacter* RenderMathMLOperator::findAcceptableStretchyCharacter(UChar character)
+{
+    StretchyCharacter* stretchyCharacter = 0;
+    const int maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
+    for (int index = 0; index < maxIndex; ++index) {
+        if (stretchyCharacters[index].character == character) {
+            stretchyCharacter = &stretchyCharacters[index];
+            break;
         }
-        createGlyph(partsData->bottomGlyph, bottomGlyphHeight, -1);
     }
-    
-    setNeedsLayoutAndPrefWidthsRecalc();
+
+    // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
+    if (!stretchyCharacter)
+        return 0;
+
+    float height = glyphHeightForCharacter(stretchyCharacter->topGlyph) + glyphHeightForCharacter(stretchyCharacter->bottomGlyph);
+    if (stretchyCharacter->middleGlyph)
+        height += glyphHeightForCharacter(stretchyCharacter->middleGlyph);
+
+    if (height > expandedStretchHeight())
+        return 0;
+
+    return stretchyCharacter;
 }
 
-PassRefPtr<RenderStyle> RenderMathMLOperator::createStackableStyle(int maxHeightForRenderer)
+void RenderMathMLOperator::updateStyle()
 {
+    ASSERT(firstChild());
+    if (!firstChild())
+        return;
+
+    UChar stretchedCharacter;
+    bool allowStretching = shouldAllowStretching(stretchedCharacter);
+
+    float stretchedCharacterHeight = style()->fontMetrics().floatHeight();
+    m_isStretched = allowStretching && expandedStretchHeight() > stretchedCharacterHeight;
+
+    // Sometimes we cannot stretch an operator properly, so in that case, we should just use the original size.
+    m_stretchyCharacter = m_isStretched ? findAcceptableStretchyCharacter(stretchedCharacter) : 0;
+    if (!m_stretchyCharacter)
+        m_isStretched = false;
+
     RefPtr<RenderStyle> newStyle = RenderStyle::create();
     newStyle->inheritFrom(style());
     newStyle->setDisplay(FLEX);
-    
-    newStyle->setMaxHeight(Length(maxHeightForRenderer, Fixed));
-    
-    newStyle->setOverflowY(OHIDDEN);
-    newStyle->setOverflowX(OHIDDEN);
 
-    return newStyle.release();
+    if (m_isStretched)
+        style()->setHeight(Length(expandedStretchHeight(), Fixed));
+    else
+        style()->setHeight(Length());
+
+    toRenderElement(firstChild())->setPseudoStyle(newStyle.release());
 }
 
-RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int maxHeightForRenderer, int charRelative)
+int RenderMathMLOperator::firstLineBoxBaseline() const
 {
-    RenderBlock* container = new (renderArena()) RenderMathMLBlock(element());
-    toRenderMathMLBlock(container)->setIgnoreInAccessibilityTree(true);
-    container->setStyle(createStackableStyle(maxHeightForRenderer));
-    addChild(container);
-    RenderBlock* parent = container;
-    if (charRelative) {
-        RenderBlock* charBlock = new (renderArena()) RenderBlockFlow(element());
-        RefPtr<RenderStyle> charStyle = RenderStyle::create();
-        charStyle->inheritFrom(container->style());
-        charStyle->setDisplay(INLINE_BLOCK);
-        charStyle->setTop(Length(charRelative, Fixed));
-        charStyle->setPosition(RelativePosition);
-        charBlock->setStyle(charStyle);
-        parent->addChild(charBlock);
-        parent = charBlock;
+    if (m_isStretched)
+        return expandedStretchHeight() * 2 / 3 - (expandedStretchHeight() - m_stretchHeight) / 2;
+    return RenderMathMLBlock::firstLineBoxBaseline();
+}
+
+LayoutRect RenderMathMLOperator::paintCharacter(PaintInfo& info, UChar character, const LayoutPoint& origin, CharacterPaintTrimming trim)
+{
+    GlyphData data = style()->font().glyphDataForCharacter(character, false);
+    FloatRect glyphBounds = data.fontData->boundsForGlyph(data.glyph);
+
+    LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
+    glyphPaintRect.setY(origin.y() + glyphBounds.y());
+
+    // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries
+    // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less
+    // than full coverage. These edge pixels can introduce small seams between connected glyphs
+    FloatRect clipBounds = info.rect;
+    switch (trim) {
+    case TrimTop:
+        glyphPaintRect.shiftYEdgeTo(ceil(glyphPaintRect.y()) + 1);
+        clipBounds.shiftYEdgeTo(glyphPaintRect.y());
+        break;
+    case TrimBottom:
+        glyphPaintRect.shiftMaxYEdgeTo(floor(glyphPaintRect.maxY()) - 1);
+        clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
+        break;
+    case TrimTopAndBottom:
+        glyphPaintRect.shiftYEdgeTo(ceil(glyphPaintRect.y() + 1));
+        glyphPaintRect.shiftMaxYEdgeTo(floor(glyphPaintRect.maxY()) - 1);
+        clipBounds.shiftYEdgeTo(glyphPaintRect.y());
+        clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
+        break;
     }
-    
-    RenderText* text = RenderText::createAnonymous(document(), String(&glyph, 1));
-    parent->addChild(text);
-    return container;
+
+    // Clipping the enclosing IntRect avoids any potential issues at joined edges.
+    GraphicsContextStateSaver stateSaver(*info.context);
+    info.context->clip(clipBounds);
+
+    info.context->drawText(style()->font(), TextRun(&character, 1), origin);
+
+    return glyphPaintRect;
 }
 
-int RenderMathMLOperator::firstLineBoxBaseline() const
+void RenderMathMLOperator::fillWithExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
 {
-    if (m_isStacked)
-        return m_stretchHeight * 2 / 3 - (m_stretchHeight - static_cast<int>(m_stretchHeight / gOperatorExpansion)) / 2;    
-    return RenderMathMLBlock::firstLineBoxBaseline();
+    ASSERT(m_stretchyCharacter);
+    ASSERT(m_stretchyCharacter->extensionGlyph);
+    ASSERT(from.y() < to.y());
+
+    GraphicsContextStateSaver stateSaver(*info.context);
+
+    FloatRect glyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->extensionGlyph);
+
+    // Clipping the extender region here allows us to draw the bottom extender glyph into the
+    // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
+    IntRect clipBounds = info.rect;
+    clipBounds.shiftYEdgeTo(from.y());
+    clipBounds.shiftMaxYEdgeTo(to.y());
+    info.context->clip(clipBounds);
+
+    // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels.
+    float offsetToGlyphTop = glyphBounds.y() + 2;
+    LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop);
+    FloatRect lastPaintedGlyphRect(from, FloatSize());
+
+    while (lastPaintedGlyphRect.maxY() < to.y()) {
+        lastPaintedGlyphRect = paintCharacter(info, m_stretchyCharacter->extensionGlyph, glyphOrigin, TrimTopAndBottom);
+        glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
+
+        // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
+        // with trimming. In that case we just draw nothing.
+        if (lastPaintedGlyphRect.isEmpty())
+            break;
+    }
+}
+
+void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
+{
+    RenderMathMLBlock::paint(info, paintOffset);
+
+    if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground)
+        return;
+
+    if (!m_isStretched && !m_stretchyCharacter) {
+        RenderMathMLBlock::paint(info, paintOffset);
+        return;
+    }
+
+    GraphicsContextStateSaver stateSaver(*info.context);
+    info.context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace());
+
+    ASSERT(m_stretchyCharacter->topGlyph);
+    ASSERT(m_stretchyCharacter->bottomGlyph);
+
+    // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
+    LayoutPoint operatorTopLeft = ceiledIntPoint(paintOffset + location());
+    FloatRect topGlyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->topGlyph);
+    LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y());
+    LayoutRect topGlyphPaintRect = paintCharacter(info, m_stretchyCharacter->topGlyph, topGlyphOrigin, TrimBottom);
+
+    FloatRect bottomGlyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->bottomGlyph);
+    LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + offsetHeight() - (bottomGlyphBounds.height() + bottomGlyphBounds.y()));
+    LayoutRect bottomGlyphPaintRect = paintCharacter(info, m_stretchyCharacter->bottomGlyph, bottomGlyphOrigin, TrimTop);
+
+    if (m_stretchyCharacter->middleGlyph) {
+        // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height  toward the bottom glyph.
+        FloatRect middleGlyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->middleGlyph);
+        LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y() + y());
+        middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0));
+        middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0));
+
+        LayoutRect middleGlyphPaintRect = paintCharacter(info, m_stretchyCharacter->middleGlyph, middleGlyphOrigin, TrimTopAndBottom);
+        fillWithExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
+        fillWithExtensionGlyph(info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
+    } else
+        fillWithExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
+}
+
+void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
+{
+    if (m_isStretched)
+        return;
+    RenderMathMLBlock::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
 }
     
 }
index 747f070d915fd2a9637edf341a24586513320bf6..2c3d5c255d8bacc36767a8a4f2cd2585e7d301ae 100644 (file)
@@ -42,32 +42,57 @@ public:
     
     virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
     virtual void updateFromElement() OVERRIDE;
+    virtual void computePreferredLogicalWidths();
     
     virtual RenderMathMLOperator* unembellishedOperator() OVERRIDE { return this; }
     void stretchToHeight(int pixelHeight);
+    int stretchHeight() { return m_stretchHeight; }
+    float expandedStretchHeight() const;
     
     virtual int firstLineBoxBaseline() const OVERRIDE;
     
     enum OperatorType { Default, Separator, Fence };
     void setOperatorType(OperatorType type) { m_operatorType = type; }
     OperatorType operatorType() const { return m_operatorType; }
-    
-protected:
-    virtual void computePreferredLogicalWidths() OVERRIDE;
-    PassRefPtr<RenderStyle> createStackableStyle(int maxHeightForRenderer);
-    RenderBlock* createGlyph(UChar glyph, int maxHeightForRenderer, int charRelative);
-    
+    void updateStyle();
+
+    void paint(PaintInfo&, const LayoutPoint&);
+
+    struct StretchyCharacter {
+        UChar character;
+        UChar topGlyph;
+        UChar extensionGlyph;
+        UChar bottomGlyph;
+        UChar middleGlyph;
+    };
+
 private:
     virtual const char* renderName() const { return isAnonymous() ? "RenderMathMLOperator (anonymous)" : "RenderMathMLOperator"; }
+    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE;
+    virtual void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect);
 
-    int glyphHeightForCharacter(UChar);
+    bool shouldAllowStretching(UChar& characterForStretching);
+    StretchyCharacter* findAcceptableStretchyCharacter(UChar);
 
-    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE;
+    FloatRect glyphBoundsForCharacter(UChar);
+    float glyphHeightForCharacter(UChar);
+    float advanceForCharacter(UChar);
+
+    enum CharacterPaintTrimming {
+        TrimTop,
+        TrimBottom,
+        TrimTopAndBottom,
+    };
+
+    LayoutRect paintCharacter(PaintInfo&, UChar, const LayoutPoint& origin, CharacterPaintTrimming);
+    void fillWithExtensionGlyph(PaintInfo&, const LayoutPoint& from, const LayoutPoint& to);
 
     int m_stretchHeight;
-    bool m_isStacked;
+    bool m_isStretched;
+
     UChar m_operator;
     OperatorType m_operatorType;
+    StretchyCharacter* m_stretchyCharacter;
 };
 
 inline RenderMathMLOperator* toRenderMathMLOperator(RenderMathMLBlock* block)
index 109f22c7b56c6d6bfee3faf195f8d139e79dcbb1..6bf589c65170dd4cd127b03cb5c46dfa51b25c4a 100644 (file)
@@ -173,29 +173,25 @@ void RenderMathMLRoot::addChild(RenderObject* newChild, RenderObject* beforeChil
         toRenderElement(firstChild())->addChild(newChild, beforeChild && beforeChild->parent() == firstChild() ? beforeChild : 0);
 }
 
-RenderBoxModelObject* RenderMathMLRoot::index() const
+RenderBox* RenderMathMLRoot::index() const
 {
     if (!firstChild())
         return 0;
     RenderObject* index = firstChild()->nextSibling();
-    if (!index || !index->isBoxModelObject())
+    if (!index || !index->isBox())
         return 0;
-    return toRenderBoxModelObject(index);
+    return toRenderBox(index);
 }
 
-void RenderMathMLRoot::computePreferredLogicalWidths()
+void RenderMathMLRoot::layout()
 {
-    ASSERT(preferredLogicalWidthsDirty() && needsLayout());
-    
-#ifndef NDEBUG
-    // FIXME: Remove this once mathml stops modifying the render tree here.
-    SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false);
-#endif
-    
-    computeChildrenPreferredLogicalHeights();
-    
-    int baseHeight = firstChild() ? roundToInt(preferredLogicalHeightAfterSizing(firstChild())) : style()->fontSize();
-    
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (!child->isBox())
+            continue;
+        child->layoutIfNeeded();
+    }
+
+    int baseHeight = firstChild() && firstChild()->isBox() ? roundToInt(toRenderBox(firstChild())->logicalHeight()) : style()->fontSize();
     int frontWidth = lroundf(gFrontWidthEms * style()->fontSize());
     
     // Base height above which the shape of the root changes
@@ -212,10 +208,10 @@ void RenderMathMLRoot::computePreferredLogicalWidths()
     int rootPad = lroundf(gSpaceAboveEms * style()->fontSize());
     m_intrinsicPaddingBefore = rootPad;
     m_indexTop = 0;
-    if (RenderBoxModelObject* index = this->index()) {
+    if (RenderBox* index = this->index()) {
         m_intrinsicPaddingStart = roundToInt(index->maxPreferredLogicalWidth()) + m_overbarLeftPointShift;
-        
-        int indexHeight = roundToInt(preferredLogicalHeightAfterSizing(index));
+
+        int indexHeight = roundToInt(index->logicalHeight());
         int partDipHeight = lroundf((1 - gRootRadicalDipLeftPointYPos) * baseHeight);
         int rootExtraTop = partDipHeight + indexHeight - (baseHeight + rootPad);
         if (rootExtraTop > 0)
@@ -225,26 +221,16 @@ void RenderMathMLRoot::computePreferredLogicalWidths()
     } else
         m_intrinsicPaddingStart = frontWidth;
 
-    RenderMathMLBlock::computePreferredLogicalWidths();
-    
-    // Shrink our logical width to its probable value now without triggering unnecessary relayout of our children.
-    ASSERT(needsLayout() && logicalWidth() >= maxPreferredLogicalWidth());
-    setLogicalWidth(maxPreferredLogicalWidth());
-}
+    // FIXME: We should rewrite RenderMathMLRoot to rewrite -webkit-flex-order to get rid of the need
+    // for intrinsic padding. See https://bugs.webkit.org/show_bug.cgi?id=107151#c2.
+    // FIXME: We should make it so that the preferred width of RenderMathMLRoots doesn't change during layout.
+    // Technically, we currently only need to set the dirty bit here if one of the member variables above changes.
+    setPreferredLogicalWidthsDirty(true);
 
-void RenderMathMLRoot::layout()
-{
-    // Our computePreferredLogicalWidths() may change our logical width and then layout our children, which
-    // RenderBlock::layout()'s relayoutChildren logic isn't expecting.
-    if (preferredLogicalWidthsDirty())
-        computePreferredLogicalWidths();
-    
     RenderMathMLBlock::layout();
-    
-    RenderBoxModelObject* index = this->index();
-    // If |index|, it should be a RenderBlock here, unless the user has overriden its { position: absolute }.
-    if (index && index->isBox())
-        toRenderBox(index)->setLogicalTop(m_indexTop);
+
+    if (RenderBox* index = this->index())
+        index->setLogicalTop(m_indexTop);
 }
 
 void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
index f8ecb9607e280c4b8ea4b1e184dae3d0bd3e73f2..ca7d4794785ce345db6858a93800b64173773f3b 100644 (file)
@@ -57,10 +57,8 @@ private:
     virtual bool isRenderMathMLRoot() const { return true; }
     virtual const char* renderName() const { return "RenderMathMLRoot"; }
     
-    virtual void computePreferredLogicalWidths() OVERRIDE;
-    
     // This may return 0 for a non-MathML index (which won't occur in valid MathML).
-    RenderBoxModelObject* index() const;
+    RenderBox* index() const;
 
     int m_intrinsicPaddingBefore;
     int m_intrinsicPaddingAfter;
index 25fb75b7c5a1da07b6728cda1c7f2ec42850dc40..91f371249783b4dddbc7d7896e2f2316ca7eebfc 100644 (file)
@@ -51,25 +51,16 @@ RenderMathMLRow* RenderMathMLRow::createAnonymousWithParentRenderer(const Render
     return newMRow;
 }
 
-void RenderMathMLRow::computePreferredLogicalWidths()
+void RenderMathMLRow::layout()
 {
-    ASSERT(preferredLogicalWidthsDirty() && needsLayout());
-
-#ifndef NDEBUG
-    // FIXME: Remove this once mathml stops modifying the render tree here.
-    SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false);
-#endif
-
-    computeChildrenPreferredLogicalHeights();
     int stretchLogicalHeight = 0;
     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
-        if (child->isRenderMathMLBlock()) {
-            RenderMathMLOperator* renderMo = toRenderMathMLBlock(child)->unembellishedOperator();
-            // FIXME: Only skip renderMo if it is stretchy.
-            if (renderMo)
-                continue;
-        }
-        stretchLogicalHeight = max<int>(stretchLogicalHeight, roundToInt(preferredLogicalHeightAfterSizing(child)));
+        child->layoutIfNeeded();
+        // FIXME: Only skip renderMo if it is stretchy.
+        if (child->isRenderMathMLBlock() && toRenderMathMLBlock(child)->unembellishedOperator())
+            continue;
+        if (child->isBox())
+            stretchLogicalHeight = max<int>(stretchLogicalHeight, roundToInt(toRenderBox(child)->logicalHeight()));
     }
     if (!stretchLogicalHeight)
         stretchLogicalHeight = style()->fontSize();
@@ -77,26 +68,13 @@ void RenderMathMLRow::computePreferredLogicalWidths()
     // Set the sizes of (possibly embellished) stretchy operator children.
     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
         if (child->isRenderMathMLBlock()) {
-            RenderMathMLOperator* renderMo = toRenderMathMLBlock(child)->unembellishedOperator();
-            if (renderMo)
-                renderMo->stretchToHeight(stretchLogicalHeight);
+            if (RenderMathMLOperator* renderMo = toRenderMathMLBlock(child)->unembellishedOperator()) {
+                if (renderMo->stretchHeight() != stretchLogicalHeight)
+                    renderMo->stretchToHeight(stretchLogicalHeight);
+            }
         }
     }
 
-    RenderMathMLBlock::computePreferredLogicalWidths();
-    
-    // Shrink our logical width to its probable value now without triggering unnecessary relayout of our children.
-    ASSERT(needsLayout() && logicalWidth() >= maxPreferredLogicalWidth());
-    setLogicalWidth(maxPreferredLogicalWidth());
-}
-
-void RenderMathMLRow::layout()
-{
-    // Our computePreferredLogicalWidths() may change our logical width and then layout our children, which
-    // RenderBlock::layout()'s relayoutChildren logic isn't expecting.
-    if (preferredLogicalWidthsDirty())
-        computePreferredLogicalWidths();
-    
     RenderMathMLBlock::layout();
 }
 
index 9c1a1dc351b143d1949f80a826d927520d24ce67..33494c3580c012ec919ffeb72a17e1ab2025fff8 100644 (file)
@@ -41,9 +41,6 @@ public:
     virtual bool isRenderMathMLRow() const { return true; }
     
 protected:
-    // This also sets our stretchy embellished operator children to their correct sizes.
-    virtual void computePreferredLogicalWidths() OVERRIDE;
-    
     virtual void layout();
 
 private: