Bug 153208

Summary: Refactor RenderMathMLRow layout functions to avoid using flexbox
Product: WebKit Reporter: Alejandro G. Castro <alex>
Component: MathMLAssignee: Frédéric Wang (:fredw) <fred.wang>
Status: RESOLVED FIXED    
Severity: Enhancement CC: cfleizach, commit-queue, darin, dbarton, esprehn+autocc, fred.wang, glenn, hyatt, jfernandez, kondapallykalyan, mmaxfield, mrobinson, rego, rniwa, WebkitBugTracker, zalan
Priority: P2    
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 153742, 153991, 155019, 155792, 157943    
Attachments:
Description Flags
Patch
none
testcase embellished operator
none
Patch
none
testcase Arabic characters
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch mrobinson: review+

Description Alejandro G. Castro 2016-01-18 10:02:01 PST
This is the first patch in the set to remove the flexbox dependency of the MathML layout without changing the render tree structure. We have started with the row for no particular reason. This patch will add some new calculation functions that will be used in this and other patches of the set.
Comment 1 Alejandro G. Castro 2016-01-18 10:14:49 PST
For more information about the rationale of this change check the following thread in the mailing list:

https://lists.webkit.org/pipermail/webkit-dev/2015-December/027840.html
Comment 2 Alejandro G. Castro 2016-01-18 10:32:39 PST
Created attachment 269230 [details]
Patch
Comment 3 Frédéric Wang (:fredw) 2016-01-22 05:01:28 PST
Comment on attachment 269230 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=269230&action=review

> Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:152
> +            downcast<RenderMathMLOperator>(child)->stretchTo(stretchHeightAboveBaseline, stretchDepthBelowBaseline);

I don't understand this. The previous code checked that child was a RenderMathMLBlock and then tried to get the unembellished RenderMathMLOperator.
Comment 4 Martin Robinson 2016-01-26 07:28:56 PST
(In reply to comment #3)
> Comment on attachment 269230 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=269230&action=review
> 
> > Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:152
> > +            downcast<RenderMathMLOperator>(child)->stretchTo(stretchHeightAboveBaseline, stretchDepthBelowBaseline);
> 
> I don't understand this. The previous code checked that child was a
> RenderMathMLBlock and then tried to get the unembellished
> RenderMathMLOperator.

Is the patch ready to review or does this question block it?
Comment 5 Frédéric Wang (:fredw) 2016-01-28 01:27:40 PST
Created attachment 270103 [details]
testcase embellished operator

The change breaks stretching embellished operators, I'm surprised that it is not tested yet. If we want to preserve the current behavior, we probably want something like

if (is<RenderMathMLBlock>(child)) {
  if (auto renderOperator = downcast<RenderMathMLBlock>(child)->unembellishedOperator())
    renderOperator->stretchTo(stretchHeightAboveBaseline, stretchDepthBelowBaseline);
}

But actually, we really only want to stretch operators that are "stretchy" and have direction "vertical", so an optimization is:

if (is<RenderMathMLBlock>(child)) {
  auto renderOperator = downcast<RenderMathMLBlock>(child)->unembellishedOperator();
  if (renderOperator && renderOperator->hasOperatorFlag(MathMLOperatorDictionary::Stretchy) && renderOperator->isVertical())
    renderOperator->stretchTo(stretchHeightAboveBaseline, stretchDepthBelowBaseline);
}
Comment 6 Alejandro G. Castro 2016-01-28 07:22:20 PST
(In reply to comment #5)
> Created attachment 270103 [details]
> testcase embellished operator
> 
> The change breaks stretching embellished operators, I'm surprised that it is
> not tested yet. If we want to preserve the current behavior, we probably
> want something like
> 
> if (is<RenderMathMLBlock>(child)) {
>   if (auto renderOperator =
> downcast<RenderMathMLBlock>(child)->unembellishedOperator())
>     renderOperator->stretchTo(stretchHeightAboveBaseline,
> stretchDepthBelowBaseline);
> }
> 
> But actually, we really only want to stretch operators that are "stretchy"
> and have direction "vertical", so an optimization is:
> 
> if (is<RenderMathMLBlock>(child)) {
>   auto renderOperator =
> downcast<RenderMathMLBlock>(child)->unembellishedOperator();
>   if (renderOperator &&
> renderOperator->hasOperatorFlag(MathMLOperatorDictionary::Stretchy) &&
> renderOperator->isVertical())
>     renderOperator->stretchTo(stretchHeightAboveBaseline,
> stretchDepthBelowBaseline);
> }

Thanks for the comment, I agree with you, I'll replace the code and upload a new patch.
Comment 7 Martin Robinson 2016-01-28 07:26:51 PST
Comment on attachment 269230 [details]
Patch

Removing patch from review queue for the moment.
Comment 8 Alejandro G. Castro 2016-01-29 04:06:49 PST
(In reply to comment #5)
> Created attachment 270103 [details]
> testcase embellished operator
> 
> The change breaks stretching embellished operators, I'm surprised that it is
> not tested yet. If we want to preserve the current behavior, we probably
> want something like
> 
> if (is<RenderMathMLBlock>(child)) {
>   if (auto renderOperator =
> downcast<RenderMathMLBlock>(child)->unembellishedOperator())
>     renderOperator->stretchTo(stretchHeightAboveBaseline,
> stretchDepthBelowBaseline);
> }
> 
> But actually, we really only want to stretch operators that are "stretchy"
> and have direction "vertical", so an optimization is:
> 
> if (is<RenderMathMLBlock>(child)) {
>   auto renderOperator =
> downcast<RenderMathMLBlock>(child)->unembellishedOperator();
>   if (renderOperator &&
> renderOperator->hasOperatorFlag(MathMLOperatorDictionary::Stretchy) &&
> renderOperator->isVertical())
>     renderOperator->stretchTo(stretchHeightAboveBaseline,
> stretchDepthBelowBaseline);
> }

Just tested the change does not work. The rationale is correct and without the anonymous and the flexbox we should be able to get the code to look like you say but not with this first patch I'm afraid, that is what makes all this tree structure so complex. I've used your initial proposal and changed the patch, I'll upload again a rebased version.

Thanks for the comments.
Comment 9 Alejandro G. Castro 2016-01-29 04:39:47 PST
Created attachment 270200 [details]
Patch
Comment 10 Frédéric Wang (:fredw) 2016-02-08 01:54:43 PST
Created attachment 270850 [details]
testcase Arabic characters

At the moment, the MathML code does not make any distinction between logical and ink vertical metrics and so the user agent stylesheet contains the rule "math { -webkit-line-box-contain: glyphs replaced; }" to force the use of ink bounds. This seems to work well in general but fails for Arabic characters. At least on WebKitGTK the computed height is 0 for me and so in the attached testcase, the background is not drawn at all for Arabic characters.

With the refactoring done in this bug, the glyph height is really used to determine the logical height of MathML elements and so Arabic characters do not appear at all. I'm not sure that's something we should care for now, but we should think about that in the future. See bug 151592 for more discussions.
Comment 11 Frédéric Wang (:fredw) 2016-02-09 12:08:14 PST
Comment on attachment 270200 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=270200&action=review

> Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:333
> +

there is an extra space here

> Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:340
> +

here too
Comment 12 Frédéric Wang (:fredw) 2016-02-09 13:15:21 PST
Comment on attachment 270200 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=270200&action=review

> Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:165
> +        LayoutUnit ascent = WebCore::marginBoxAscentForChild(*child);

Is the WebCore:: prefix really needed? Javier & Rego removed it in a follow-up commit.

> Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:190
> +        LayoutUnit ascent = WebCore::marginBoxAscentForChild(*child);

ditto
Comment 13 Alejandro G. Castro 2016-02-12 03:29:53 PST
Created attachment 271160 [details]
Patch
Comment 14 Alejandro G. Castro 2016-02-12 03:30:41 PST
(In reply to comment #12)
> Comment on attachment 270200 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=270200&action=review
> 
> > Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:165
> > +        LayoutUnit ascent = WebCore::marginBoxAscentForChild(*child);
> 
> Is the WebCore:: prefix really needed? Javier & Rego removed it in a
> follow-up commit.
> 
> > Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:190
> > +        LayoutUnit ascent = WebCore::marginBoxAscentForChild(*child);
> 
> ditto

Thanks for the comments, I've just uploaded a new version with your proposals.
Comment 15 Alejandro G. Castro 2016-02-12 03:47:48 PST
(In reply to comment #14)
> > > Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:190
> > > +        LayoutUnit ascent = WebCore::marginBoxAscentForChild(*child);
> > 
> > ditto
> 
> Thanks for the comments, I've just uploaded a new version with your
> proposals.

I was wrong, I did not test your proposal properly, we need them because we want to use the local version and not the one in flexbox, which is private. I'll review it properly.
Comment 16 Alejandro G. Castro 2016-02-12 03:57:06 PST
Created attachment 271162 [details]
Patch
Comment 17 Martin Robinson 2016-02-12 09:17:51 PST
Comment on attachment 271162 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=271162&action=review

Looks good. Before landing, do you mind having people more familiar with flex box to double-check the bits from flex box.

> Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:105
> +    // FIXME: What about the RenderBlocks?

If this FIXME is no longer relevant, it should probably just be removed.

> Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:187
> +        centerBlockOffset = - centerBlockOffset;

Nit: = - should be =-
Comment 18 Said Abou-Hallawa 2016-02-12 12:22:47 PST
Comment on attachment 271162 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=271162&action=review

> Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:356
> +        return style().writingMode() == TopToBottomWritingMode || style().writingMode() == LeftToRightWritingMode;

I do not understand this part. Why does the the writing mode solely decides the flow direction for vertical flow?

> Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:400
> +}

Similar code exists for borders, margins and paddings in RenderStyle and RenderBox. I wish we can have one place which does this confusing calculations.

> Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:415
> +    return marginTop();

If you add the following function to RenderBox: const LayoutBoxExtent& margins() const { return m_marginBox; }

This function can be written like this:

return child.margins().before(transformedWritingMode());

> Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:431
> +    return marginBottom();

If you add the following function to RenderBox: const LayoutBoxExtent& margins() const { return m_marginBox; }

This function can be written like this:

return child.margins().after(transformedWritingMode());

> Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:449
> +    return TopToBottomWritingMode;

I do not understand this part. Why does the text direction affect the writing mode transformation for horizontal flow?
Comment 19 Alejandro G. Castro 2016-02-15 03:46:15 PST
Thanks for the review, I'm adding hyatt to check all the rationale of the patches to check if it is ok to proceed.

FYI main idea with these initial set of patches is to remove the flexbox dependency, which means it is not a very clean code until we refactor without the flexbox. The other option would be a huge patch with all the refactors and rebases of the dump render tree tests. It seemed to us this iterative plan makes it easy to divide the work and smaller patches to review, because the amount of code controlling the old flexbox logic added in RenderMathMLBlock is not big and very well identified to try to remove it in the future.

(In reply to comment #17)
> Comment on attachment 271162 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=271162&action=review
> 
> Looks good. Before landing, do you mind having people more familiar with
> flex box to double-check the bits from flex box.
> 

Added hyatt to check if he can validate the code. The only part from flexbox are the functions added to RenderMathMLBlock, we tried to minimize that part.

> > Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:105
> > +    // FIXME: What about the RenderBlocks?
> 
> If this FIXME is no longer relevant, it should probably just be removed.
> 

Yep, I'll do it.

> > Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:187
> > +        centerBlockOffset = - centerBlockOffset;
> 
> Nit: = - should be =-

Yep.

Thanks again.
Comment 20 Alejandro G. Castro 2016-02-15 04:31:39 PST
(In reply to comment #18)
> Comment on attachment 271162 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=271162&action=review
> 
> > Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:356
> > +        return style().writingMode() == TopToBottomWritingMode || style().writingMode() == LeftToRightWritingMode;
> 
> I do not understand this part. Why does the the writing mode solely decides
> the flow direction for vertical flow?
> 

Good point, we are basically duplicating what flexbox is currently doing for these cases. If I understand correctly the reason it just uses the writing mode for the column flow is because it tries to get the inline base direction, to get the start and end distances, but I'm not an expert on that code so take my comment with a grain of salt, some references:

https://www.w3.org/TR/css-flexbox-1/#valdef-flex-direction-column
https://drafts.csswg.org/css-writing-modes-3/#text-flow

> > Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:400
> > +}
> 
> Similar code exists for borders, margins and paddings in RenderStyle and
> RenderBox. I wish we can have one place which does this confusing
> calculations.
> 

Great point, I'll try to check if we can use those.

> > Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:415
> > +    return marginTop();
> 
> If you add the following function to RenderBox: const LayoutBoxExtent&
> margins() const { return m_marginBox; }
> 
> This function can be written like this:
> 
> return child.margins().before(transformedWritingMode());
> 

I'll do it thanks.

> > Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:431
> > +    return marginBottom();
> 
> If you add the following function to RenderBox: const LayoutBoxExtent&
> margins() const { return m_marginBox; }
> 
> This function can be written like this:
> 
> return child.margins().after(transformedWritingMode());
> 
> > Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp:449
> > +    return TopToBottomWritingMode;
> 
> I do not understand this part. Why does the text direction affect the
> writing mode transformation for horizontal flow?

In this case we are also doing what flexbox is currently doing, we basically want to keep it like it was until we can replace the code with more specific code for MathML. Anyway, I'll try to check if we can reduce the amount of code using your suggestions and upload the patch.

Thanks again :).
Comment 21 Frédéric Wang (:fredw) 2016-03-02 00:50:19 PST
I'm going to upload a new patch with Martin's nits addressed.

Regarding Said's feedback, here are my two cents:

> I do not understand this part. Why does the the writing mode solely decides the flow direction for vertical flow?
> I do not understand this part. Why does the text direction affect the writing mode transformation for horizontal flow?

As Alejandro said, we are basically duplicating flexbox behavior as our idea was to do a smooth transition before a complete refactoring. One of the issue with the current MathML code is that on the one hand flexbox rules are more complex than what is really needed for MathML and on the other hand MathML layout does not always follow layout invariants. So it's often hard to understand the actual rendering behavior and thus to work on MathML & review patches. We plan to submit our simplification in future patches so that all will be much more understandable (in particular, only horizontal RTL and LTR will matter). For now, we have to trust that the flexbox implementation is correct...

> Similar code exists for borders, margins and paddings in RenderStyle and RenderBox. I wish we can have one place which does this confusing calculations.

Yes, that's bad. However, it's now exactly the same code (it uses "flow" functions) so I'm not sure they would produce the same behavior as flexbox. I suspect we may be able to do that at the current status of our MathML development branch, since we do not care about flow stuff anymore.

> If you add the following function to RenderBox: const LayoutBoxExtent& margins() const { return m_marginBox; }
> If you add the following function to RenderBox: const LayoutBoxExtent& margins() const { return m_marginBox; }

We can certainly do that to simplify the code. However, that would be at the price of adding some code to RenderBox/RenderStyle for MathML's own sake and someone already complained on the webkit-dev mailing list that we should avoid that as much as possible. Again, the plan is to remove/rewrite these functions in the future so I guess it's acceptable to have this temporary functions, given that we will significantly reduce the code size in follow-up patches.
Comment 22 Frédéric Wang (:fredw) 2016-03-02 00:51:37 PST
(In reply to comment #21)
> it's now exactly

*not*
Comment 23 Frédéric Wang (:fredw) 2016-03-03 03:17:27 PST
Created attachment 272750 [details]
Patch
Comment 24 Frédéric Wang (:fredw) 2016-03-03 03:19:05 PST
I uploaded a new version of the patch which includes future refactoring/simplification/cleanup from the MathMLLayout branch. The advantage is that we no longer need to add all these flexbox-like functions that are unrelated to math layout (although I suspect they will be needed later in the other bugs opened by Alex). The disadvantages are that:

1) There are (maybe?) more changes to review in one go.
2) This needs some minor test adjustments. More specifically, the one stretching selection of horizontal stretchy operators has rendering changes that are not important. And I disabled one fraction reftest for now, which has a small 1px size difference (for some reason it only pass after the complete refactoring of RenderMathMLFraction from the MathMLLayout branch).
Comment 25 Frédéric Wang (:fredw) 2016-03-11 01:22:38 PST
Created attachment 273700 [details]
Patch

I'm uploading updated version of the MathML refactoring patches to solve conflicts after bug 154388 and bug 155005 ; and to do other minor changes.
Comment 26 Frédéric Wang (:fredw) 2016-03-31 08:16:40 PDT
Created attachment 275283 [details]
Patch
Comment 27 Martin Robinson 2016-04-01 08:44:06 PDT
Comment on attachment 275283 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=275283&action=review

Looks good with a couple nits.

> Source/WebCore/ChangeLog:17
> +        This is the first patch in the set to remove the flexbox
> +        dependency of the MathML layout without changing the render tree
> +        structure. We have done some temporary changes to allow overriding of
> +        layoutBlock and to implement paintChildren, but this will be remove in a
> +        follow-up patch. We also implement firstLineBaseline,
> +        computePreferredLogicalWidths and layoutBlock of RenderMathMLRow without
> +        using any flexbox functions. We adjust a bit the MathML CSS to take into
> +        account these changes. Finally, we delete the unused helper function to
> +        create anonymous RenderMathMLRow.
> +

Perhaps update this to reflect the most recent version of this patch?

> Source/WebCore/rendering/mathml/RenderMathMLRow.cpp:115
> +    LayoutUnit horizontalOffset = borderAndPaddingStart();

Let's move this declarations down to right before they are first used (line 142).
Comment 28 Frédéric Wang (:fredw) 2016-04-04 01:25:41 PDT
Committed r198998: <http://trac.webkit.org/changeset/198998>