Roll out r156930 and r156937, they caused 34 assertion failures on bots.
authorap@apple.com <ap@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Oct 2013 06:32:05 +0000 (06:32 +0000)
committerap@apple.com <ap@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Oct 2013 06:32:05 +0000 (06:32 +0000)
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@156947 268f45cc-cd09-0410-ab3c-d52691b4dbfc

27 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/mathml/presentation/mo-minus-expected.html [deleted file]
LayoutTests/mathml/presentation/mo-minus.html [deleted file]
LayoutTests/mathml/presentation/mo-stacked-glyphs-expected.html [deleted file]
LayoutTests/mathml/presentation/mo-stacked-glyphs.html [deleted file]
LayoutTests/mathml/presentation/mo-stretchy-vertical-bar-expected-mismatch.html [deleted file]
LayoutTests/mathml/presentation/mo-stretchy-vertical-bar.html [deleted file]
LayoutTests/mathml/very-large-stretchy-operators-expected.txt [deleted file]
LayoutTests/mathml/very-large-stretchy-operators.html [deleted file]
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 be97c73..2ffb364 100644 (file)
@@ -1,3 +1,20 @@
+2013-10-04  Alexey Proskuryakov  <ap@apple.com>
+
+        Roll out r156930 and r156937, they caused 34 assertion failures on bots.
+
+        * TestExpectations:
+        * mathml/presentation/mo-minus-expected.html: Removed.
+        * mathml/presentation/mo-minus.html: Removed.
+        * mathml/presentation/mo-stacked-glyphs-expected.html: Removed.
+        * mathml/presentation/mo-stacked-glyphs.html: Removed.
+        * mathml/presentation/mo-stretchy-vertical-bar-expected-mismatch.html: Removed.
+        * mathml/presentation/mo-stretchy-vertical-bar.html: Removed.
+        * mathml/very-large-stretchy-operators-expected.txt: Removed.
+        * mathml/very-large-stretchy-operators.html: Removed.
+        * platform/gtk/TestExpectations:
+        * platform/mac/TestExpectations:
+        * platform/win/TestExpectations:
+
 2013-10-04  Mark Lam  <mark.lam@apple.com>
 
         Change ScriptDebugServer to use DebuggerCallFrame instead of JavaScriptCallFrame.
index 486f01d..c36c89c 100644 (file)
@@ -19,8 +19,6 @@ 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
deleted file mode 100644 (file)
index f392ff8..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<!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
deleted file mode 100644 (file)
index f698209..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<!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
deleted file mode 100644 (file)
index d9f2a64..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<!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
deleted file mode 100644 (file)
index a93e725..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<!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
deleted file mode 100644 (file)
index e9e6c0e..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<!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
deleted file mode 100644 (file)
index 349ab1e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<!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
deleted file mode 100644 (file)
index af6e4f7..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-This test should not timeout.
-
-
diff --git a/LayoutTests/mathml/very-large-stretchy-operators.html b/LayoutTests/mathml/very-large-stretchy-operators.html
deleted file mode 100644 (file)
index 6b0ec2b..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-<!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 9637fb0..6c1664c 100644 (file)
@@ -1343,6 +1343,7 @@ 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 bde464e..8403298 100644 (file)
@@ -1273,6 +1273,7 @@ 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 53dfc77..e593819 100644 (file)
@@ -2703,6 +2703,7 @@ 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 50f50a1..d628d02 100644 (file)
@@ -1,3 +1,21 @@
+2013-10-04  Alexey Proskuryakov  <ap@apple.com>
+
+        Roll out r156930 and r156937, they caused 34 assertion failures on bots.
+
+        * css/mathml.css:
+        * mathml/MathMLTextElement.cpp:
+        * mathml/MathMLTextElement.h:
+        * rendering/RenderFlexibleBox.h:
+        * rendering/mathml/RenderMathMLBlock.cpp:
+        * rendering/mathml/RenderMathMLBlock.h:
+        * rendering/mathml/RenderMathMLFenced.cpp:
+        * rendering/mathml/RenderMathMLOperator.cpp:
+        * rendering/mathml/RenderMathMLOperator.h:
+        * rendering/mathml/RenderMathMLRoot.cpp:
+        * rendering/mathml/RenderMathMLRoot.h:
+        * rendering/mathml/RenderMathMLRow.cpp:
+        * rendering/mathml/RenderMathMLRow.h:
+
 2013-10-04  Anders Carlsson  <andersca@apple.com>
 
         FramePolicyFunction should be an std::function
index 90983d7..f3a2b40 100644 (file)
@@ -2,6 +2,7 @@
 
 math {
     -webkit-line-box-contain: glyphs replaced;
+    line-height: 0;
     text-indent: 0;
 }
 mtext {
index 6465fc4..c8b7fe0 100644 (file)
@@ -41,7 +41,6 @@ using namespace MathMLNames;
 inline MathMLTextElement::MathMLTextElement(const QualifiedName& tagName, Document& document)
     : MathMLElement(tagName, document)
 {
-    setHasCustomStyleResolveCallbacks();
 }
 
 PassRefPtr<MathMLTextElement> MathMLTextElement::create(const QualifiedName& tagName, Document& document)
@@ -49,20 +48,6 @@ 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 90e2bc9..7138ed4 100644 (file)
@@ -35,13 +35,11 @@ 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 2feac2e..5ea9c0b 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;
+    virtual void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect) OVERRIDE FINAL;
 
     bool isHorizontalFlow() const;
 
index ba91c29..144d074 100644 (file)
@@ -46,6 +46,7 @@ using namespace MathMLNames;
 RenderMathMLBlock::RenderMathMLBlock(Element* container)
     : RenderFlexibleBox(container)
     , m_ignoreInAccessibilityTree(false)
+    , m_preferredLogicalHeight(preferredLogicalHeightUnset)
 {
 }
 
@@ -54,6 +55,13 @@ 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);
@@ -63,6 +71,62 @@ 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 60d8872..23b568d 100644 (file)
@@ -67,6 +67,17 @@ 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)
@@ -82,6 +93,15 @@ 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 1fcc53f..1e4aeb2 100644 (file)
@@ -97,12 +97,9 @@ RenderMathMLOperator* RenderMathMLFenced::createMathMLOperator(UChar uChar, Rend
 
 void RenderMathMLFenced::makeFences()
 {
-    RenderMathMLOperator* openFence = createMathMLOperator(m_open, RenderMathMLOperator::Fence);
-    RenderMathMLRow::addChild(openFence, firstChild());
+    RenderMathMLRow::addChild(createMathMLOperator(m_open, RenderMathMLOperator::Fence), firstChild());
     m_closeFenceRenderer = createMathMLOperator(m_close, RenderMathMLOperator::Fence);
     RenderMathMLRow::addChild(m_closeFenceRenderer);
-    openFence->updateFromElement();
-    m_closeFenceRenderer->updateFromElement();
 }
 
 void RenderMathMLFenced::addChild(RenderObject* child, RenderObject* beforeChild)
@@ -164,13 +161,8 @@ 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) {
-                ASSERT(child->isRenderMathMLBlock());
-                RenderMathMLBlock* block = toRenderMathMLBlock(child);
-                ASSERT(block->isRenderMathMLOperator());
-                toRenderMathMLOperator(block)->updateFromElement();
+            if (isFence)
                 child->style()->setMarginStart(Length(gFenceMarginEms * style()->fontSize(), Fixed));
-            }
         }
     }
 }
index 9936539..1e64ac6 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * 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)
 {
 }
 
@@ -76,7 +53,6 @@ RenderMathMLOperator::RenderMathMLOperator(Element* element, UChar operatorChar)
     , m_stretchHeight(0)
     , m_operator(convertHyphenMinusToMinusSign(operatorChar))
     , m_operatorType(Default)
-    , m_stretchyCharacter(0)
 {
 }
 
@@ -87,74 +63,71 @@ 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;
-    updateStyle();
+    
+    updateFromElement();
 }
 
 void RenderMathMLOperator::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
 {
     RenderMathMLBlock::styleDidChange(diff, oldStyle);
-    updateFromElement();
-}
-
-FloatRect RenderMathMLOperator::glyphBoundsForCharacter(UChar character)
-{
-    GlyphData data = style()->font().glyphDataForCharacter(character, false);
-    return data.fontData->boundsForGlyph(data.glyph);
-}
-
-float RenderMathMLOperator::glyphHeightForCharacter(UChar character)
-{
-    return glyphBoundsForCharacter(character).height();
-}
-
-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);
+    
+    if (firstChild())
+        updateFromElement();
 }
 
-void RenderMathMLOperator::computePreferredLogicalWidths()
+void RenderMathMLOperator::computePreferredLogicalWidths() 
 {
     ASSERT(preferredLogicalWidthsDirty());
 
-    UChar stretchedCharacter;
-    bool allowStretching = shouldAllowStretching(stretchedCharacter);
-    if (!allowStretching) {
-        RenderMathMLBlock::computePreferredLogicalWidths();
-        return;
-    }
+#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();
 
-    float maximumGlyphWidth = advanceForCharacter(stretchedCharacter);
-    for (unsigned index = 0; index < WTF_ARRAY_LENGTH(stretchyCharacters); ++index) {
-        if (stretchyCharacters[index].character != stretchedCharacter)
-            continue;
+    RenderMathMLBlock::computePreferredLogicalWidths();
+}
 
-        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;
-    }
+// 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
+};
 
-    m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = maximumGlyphWidth;
+// Note glyphHeightForCharacter truncates its result to an int.
+int RenderMathMLOperator::glyphHeightForCharacter(UChar character)
+{
+    GlyphData data = style()->font().glyphDataForCharacter(character, false);
+    FloatRect glyphBounds = data.fontData->boundsForGlyph(data.glyph);
+    return glyphBounds.height();
 }
 
 // FIXME: It's cleaner to only call updateFromElement when an attribute has changed. The body of
@@ -172,236 +145,183 @@ void RenderMathMLOperator::updateFromElement()
     // renderer to 0, so we need to restore it.
     element()->setRenderer(savedRenderer);
     
-    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)
-{
+    // 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.
     Element* mo = element();
-    if (equalIgnoringCase(mo->getAttribute(MathMLNames::stretchyAttr), "false"))
-        return false;
-
-    if (m_operator) {
-        stretchedCharacter = m_operator;
-        return true;
+    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];
+        }
     }
 
-    // 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];
+    // 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;
+            }
+        }
     }
+    
+    // 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;
+    }
+    
+    // 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());
+        }
 
-    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;
+        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;
+            }
+        }
+        createGlyph(partsData->bottomGlyph, bottomGlyphHeight, -1);
     }
-
-    // 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;
+    
+    setNeedsLayoutAndPrefWidthsRecalc();
 }
 
-void RenderMathMLOperator::updateStyle()
+PassRefPtr<RenderStyle> RenderMathMLOperator::createStackableStyle(int maxHeightForRenderer)
 {
-    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);
 
-    if (m_isStretched)
-        style()->setHeight(Length(expandedStretchHeight(), Fixed));
-    else
-        style()->setHeight(Length());
-
-    toRenderElement(firstChild())->setPseudoStyle(newStyle.release());
-}
-
-int RenderMathMLOperator::firstLineBoxBaseline() const
-{
-    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(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;
-    }
-
-    // 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;
-}
-
-void RenderMathMLOperator::fillWithExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
-{
-    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;
-    }
+    return newStyle.release();
 }
 
-void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
+RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int maxHeightForRenderer, int charRelative)
 {
-    RenderMathMLBlock::paint(info, paintOffset);
-
-    if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground)
-        return;
-
-    if (!m_isStretched && !m_stretchyCharacter) {
-        RenderMathMLBlock::paint(info, paintOffset);
-        return;
+    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;
     }
-
-    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());
+    
+    RenderText* text = RenderText::createAnonymous(document(), String(&glyph, 1));
+    parent->addChild(text);
+    return container;
 }
 
-void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
+int RenderMathMLOperator::firstLineBoxBaseline() const
 {
-    if (m_isStretched)
-        return;
-    RenderMathMLBlock::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
+    if (m_isStacked)
+        return m_stretchHeight * 2 / 3 - (m_stretchHeight - static_cast<int>(m_stretchHeight / gOperatorExpansion)) / 2;    
+    return RenderMathMLBlock::firstLineBoxBaseline();
 }
     
 }
index 2c3d5c2..747f070 100644 (file)
@@ -42,57 +42,32 @@ 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; }
-    void updateStyle();
-
-    void paint(PaintInfo&, const LayoutPoint&);
-
-    struct StretchyCharacter {
-        UChar character;
-        UChar topGlyph;
-        UChar extensionGlyph;
-        UChar bottomGlyph;
-        UChar middleGlyph;
-    };
-
+    
+protected:
+    virtual void computePreferredLogicalWidths() OVERRIDE;
+    PassRefPtr<RenderStyle> createStackableStyle(int maxHeightForRenderer);
+    RenderBlock* createGlyph(UChar glyph, int maxHeightForRenderer, int charRelative);
+    
 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);
-
-    bool shouldAllowStretching(UChar& characterForStretching);
-    StretchyCharacter* findAcceptableStretchyCharacter(UChar);
-
-    FloatRect glyphBoundsForCharacter(UChar);
-    float glyphHeightForCharacter(UChar);
-    float advanceForCharacter(UChar);
 
-    enum CharacterPaintTrimming {
-        TrimTop,
-        TrimBottom,
-        TrimTopAndBottom,
-    };
+    int glyphHeightForCharacter(UChar);
 
-    LayoutRect paintCharacter(PaintInfo&, UChar, const LayoutPoint& origin, CharacterPaintTrimming);
-    void fillWithExtensionGlyph(PaintInfo&, const LayoutPoint& from, const LayoutPoint& to);
+    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE;
 
     int m_stretchHeight;
-    bool m_isStretched;
-
+    bool m_isStacked;
     UChar m_operator;
     OperatorType m_operatorType;
-    StretchyCharacter* m_stretchyCharacter;
 };
 
 inline RenderMathMLOperator* toRenderMathMLOperator(RenderMathMLBlock* block)
index 6bf589c..109f22c 100644 (file)
@@ -173,25 +173,29 @@ void RenderMathMLRoot::addChild(RenderObject* newChild, RenderObject* beforeChil
         toRenderElement(firstChild())->addChild(newChild, beforeChild && beforeChild->parent() == firstChild() ? beforeChild : 0);
 }
 
-RenderBox* RenderMathMLRoot::index() const
+RenderBoxModelObject* RenderMathMLRoot::index() const
 {
     if (!firstChild())
         return 0;
     RenderObject* index = firstChild()->nextSibling();
-    if (!index || !index->isBox())
+    if (!index || !index->isBoxModelObject())
         return 0;
-    return toRenderBox(index);
+    return toRenderBoxModelObject(index);
 }
 
-void RenderMathMLRoot::layout()
+void RenderMathMLRoot::computePreferredLogicalWidths()
 {
-    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();
+    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();
+    
     int frontWidth = lroundf(gFrontWidthEms * style()->fontSize());
     
     // Base height above which the shape of the root changes
@@ -208,10 +212,10 @@ void RenderMathMLRoot::layout()
     int rootPad = lroundf(gSpaceAboveEms * style()->fontSize());
     m_intrinsicPaddingBefore = rootPad;
     m_indexTop = 0;
-    if (RenderBox* index = this->index()) {
+    if (RenderBoxModelObject* index = this->index()) {
         m_intrinsicPaddingStart = roundToInt(index->maxPreferredLogicalWidth()) + m_overbarLeftPointShift;
-
-        int indexHeight = roundToInt(index->logicalHeight());
+        
+        int indexHeight = roundToInt(preferredLogicalHeightAfterSizing(index));
         int partDipHeight = lroundf((1 - gRootRadicalDipLeftPointYPos) * baseHeight);
         int rootExtraTop = partDipHeight + indexHeight - (baseHeight + rootPad);
         if (rootExtraTop > 0)
@@ -221,16 +225,26 @@ void RenderMathMLRoot::layout()
     } else
         m_intrinsicPaddingStart = frontWidth;
 
-    // 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);
+    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 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();
-
-    if (RenderBox* index = this->index())
-        index->setLogicalTop(m_indexTop);
+    
+    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);
 }
 
 void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
index ca7d479..f8ecb96 100644 (file)
@@ -57,8 +57,10 @@ 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).
-    RenderBox* index() const;
+    RenderBoxModelObject* index() const;
 
     int m_intrinsicPaddingBefore;
     int m_intrinsicPaddingAfter;
index 91f3712..25fb75b 100644 (file)
@@ -51,16 +51,25 @@ RenderMathMLRow* RenderMathMLRow::createAnonymousWithParentRenderer(const Render
     return newMRow;
 }
 
-void RenderMathMLRow::layout()
+void RenderMathMLRow::computePreferredLogicalWidths()
 {
+    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()) {
-        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 (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)));
     }
     if (!stretchLogicalHeight)
         stretchLogicalHeight = style()->fontSize();
@@ -68,13 +77,26 @@ void RenderMathMLRow::layout()
     // Set the sizes of (possibly embellished) stretchy operator children.
     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
         if (child->isRenderMathMLBlock()) {
-            if (RenderMathMLOperator* renderMo = toRenderMathMLBlock(child)->unembellishedOperator()) {
-                if (renderMo->stretchHeight() != stretchLogicalHeight)
-                    renderMo->stretchToHeight(stretchLogicalHeight);
-            }
+            RenderMathMLOperator* renderMo = toRenderMathMLBlock(child)->unembellishedOperator();
+            if (renderMo)
+                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 33494c3..9c1a1dc 100644 (file)
@@ -41,6 +41,9 @@ 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: