[MathML] Remove RenderTree modification during layout and refactor the StretchyOp...
authormrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Oct 2013 00:36:07 +0000 (00:36 +0000)
committermrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Oct 2013 00:36:07 +0000 (00:36 +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::computeLogicalHeight): Return the stretched operator height, if appropriate.
(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@157070 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 4bfc9e4..03b7120 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-07  Eric Carlson  <eric.carlson@apple.com>
 
         [MediaStream API] update MediaStreamTrack object to match spec
index c36c89c..486f01d 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 6c1664c..9637fb0 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 e58a594..523a7db 100644 (file)
@@ -1272,7 +1272,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 e593819..53dfc77 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 18b606e..b802321 100644 (file)
@@ -1,3 +1,82 @@
+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::computeLogicalHeight): Return the stretched operator height, if appropriate.
+        (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-07  Eric Carlson  <eric.carlson@apple.com>
 
         [MediaStream API] update MediaStreamTrack object to match spec
index f3a2b40..90983d7 100644 (file)
@@ -2,7 +2,6 @@
 
 math {
     -webkit-line-box-contain: glyphs replaced;
-    line-height: 0;
     text-indent: 0;
 }
 mtext {
index c8b7fe0..6465fc4 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 7138ed4..90e2bc9 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 25dbc51..c7f4476 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 144d074..ba91c29 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 c581128..1aaf81d 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 1e4aeb2..1fcc53f 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 1e64ac6..9ef854a 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,232 @@ 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()
 {
-    RefPtr<RenderStyle> newStyle = RenderStyle::create();
-    newStyle->inheritFrom(style());
-    newStyle->setDisplay(FLEX);
-    
-    newStyle->setMaxHeight(Length(maxHeightForRenderer, Fixed));
-    
-    newStyle->setOverflowY(OHIDDEN);
-    newStyle->setOverflowX(OHIDDEN);
+    ASSERT(firstChild());
+    if (!firstChild())
+        return;
+
+    UChar stretchedCharacter;
+    bool allowStretching = shouldAllowStretching(stretchedCharacter);
 
-    return newStyle.release();
+    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;
 }
 
-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();
+}
+
+void RenderMathMLOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
+{
+    if (m_isStretched)
+        logicalHeight = expandedStretchHeight();
+    RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
+}
+
+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(ceilf(glyphPaintRect.y()) + 1);
+        clipBounds.shiftYEdgeTo(glyphPaintRect.y());
+        break;
+    case TrimBottom:
+        glyphPaintRect.shiftMaxYEdgeTo(floorf(glyphPaintRect.maxY()) - 1);
+        clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
+        break;
+    case TrimTopAndBottom:
+        glyphPaintRect.shiftYEdgeTo(ceilf(glyphPaintRect.y() + 1));
+        glyphPaintRect.shiftMaxYEdgeTo(floorf(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 f23759d..cda0f72 100644 (file)
@@ -42,32 +42,58 @@ public:
     
     virtual bool isChildAllowed(RenderObject*, RenderStyle*) const OVERRIDE;
     virtual void updateFromElement() OVERRIDE;
+    virtual void computePreferredLogicalWidths() OVERRIDE;
+    virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE;
     
     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 OVERRIDE { 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 109f22c..6bf589c 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 f8ecb96..ca7d479 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 25fb75b..91f3712 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 9c1a1dc..33494c3 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: