Bug 121020

Summary: Crash on shape-outside when using calc()
Product: WebKit Reporter: Dirk Schulze <krit>
Component: CSSAssignee: Hans Muller <giles_joplin>
Status: RESOLVED FIXED    
Severity: Normal CC: bjonesbe, buildbot, commit-queue, esprehn+autocc, giles_joplin, glenn, koivisto, macpherson, menard, rniwa
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 122036    
Attachments:
Description Flags
Crash test
none
Patch
buildbot: commit-queue-
Archive of layout-test-results from webkit-ews-14 for mac-mountainlion-wk2
none
Archive of layout-test-results from webkit-ews-07 for mac-mountainlion
none
Archive of layout-test-results from webkit-ews-03 for mac-mountainlion
none
Patch
krit: review-
Patch
krit: review+, krit: commit-queue-
Patch none

Description Dirk Schulze 2013-09-08 23:19:56 PDT
Created attachment 211010 [details]
Crash test

When a users specifies the shape-outside property with calc(), the browser crashes:

-webkit-shape-outside: circle(50%, 50%, calc(50% - 20px));

Stack:

1   0x10778a4d0 WTFCrash
2   0x109664cfd WebCore::floatValueForLength(WebCore::Length const&, float, WebCore::RenderView*)
3   0x109c08029 WebCore::Shape::createShape(WebCore::BasicShape const*, WebCore::LayoutSize const&, WebCore::WritingMode, WebCore::Length, WebCore::Length)
4   0x109c0b115 WebCore::ShapeInfo<WebCore::RenderBlock, &(WebCore::RenderStyle::resolvedShapeInside() const), &(WebCore::Shape::getIncludedIntervals(WebCore::LayoutUnit, WebCore::LayoutUnit, WTF::Vector<WebCore::LineSegment, 0ul, WTF::CrashOnOverflow>&) const)>::computedShape() const
5   0x1098712b1 WebCore::ShapeInsideInfo::computedShapeLogicalBoundingBox() const
6   0x10986d251 WebCore::ShapeInfo<WebCore::RenderBlock, &(WebCore::RenderStyle::resolvedShapeInside() const), &(WebCore::Shape::getIncludedIntervals(WebCore::LayoutUnit, WebCore::LayoutUnit, WTF::Vector<WebCore::LineSegment, 0ul, WTF::CrashOnOverflow>&) const)>::shapeLogicalTop() const
7   0x10987881b WebCore::RenderBlock::layoutRunsAndFloatsInRange(WebCore::LineLayoutState&, WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>&, WebCore::InlineIterator const&, WebCore::BidiStatus const&, unsigned int)

BasicShapeFunction.cpp:114 static Length convertToLength(...

does not have CalculatedConversion as possible type.
Comment 1 Dirk Schulze 2013-09-16 11:14:10 PDT
There was a report on chromium at http://code.google.com/p/chromium/issues/detail?id=277799 as well.
Comment 2 Hans Muller 2013-09-18 11:47:47 PDT
As Dirk pointed out, the fix for the immediate problem is trivial. Just add CalculatedConversion to the convertToLength() supportTypes template parameter in BasicShapeFunctions.cpp/convertToLength(). And similarly, this exposes a lacuna in CSSPrimitiveValue::CSSPrimitiveValue(const Length&)

window.getComputedStyle(document.getElementById("shape-inside"))["-webkit-shape-inside"]

[CRASH]

frame #6: 0x0000000104c9777f WebCore`WebCore::ComputedStyleExtractor::propertyValue(WebCore::CSSPropertyID, WebCore::EUpdateLayout) const + 47759 at CSSComputedStyleDeclaration.cpp:2847
   2844	                return cssValuePool().createIdentifierValue(CSSValueNone);
   2845	            }
   2846	            ASSERT(style->shapeInside()->type() == ShapeValue::Shape);
-> 2847	            return valueForBasicShape(style->shapeInside()->shape());
   2848	        case CSSPropertyWebkitShapeOutside:
   2849	            if (!style->shapeOutside())
   2850	                return cssValuePool().createIdentifierValue(CSSValueAuto);

The valueForBasicShape() implementation uses CSSValuePool.createValue() which uses the aforementioned CSSPrimitiveValue() constructor.
The constructor fails because it's currently unsupported for Lengths whose type is CALCULATED.

CSSPrimitiveValue::CSSPrimitiveValue(const Length& length)
{
...
        case Calculated:
        case Relative:
        case Undefined:
            ASSERT_NOT_REACHED();
            break;
}

This issue appears to have been addressed in Blink, see http://src.chromium.org/viewvc/blink?view=revision&revision=156683
Comment 3 Hans Muller 2013-09-26 17:32:19 PDT
Created attachment 212774 [details]
Patch

This change prevents a crash caused by specifying a CSS Shape geometry Length attribute with a calc() expression. It adds support for converting Lengths to CSSPrimitive Values, in large part by migrating Blink changes made to the calc classes since the split. Doing so required a few supporting changes in some related classes, notably CSSPrimitiveValue.
Comment 4 Build Bot 2013-09-26 18:22:17 PDT
Comment on attachment 212774 [details]
Patch

Attachment 212774 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.appspot.com/results/2629043

New failing tests:
css3/calc/cssom.html
Comment 5 Build Bot 2013-09-26 18:22:19 PDT
Created attachment 212778 [details]
Archive of layout-test-results from webkit-ews-14 for mac-mountainlion-wk2

The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews.
Bot: webkit-ews-14  Port: mac-mountainlion-wk2  Platform: Mac OS X 10.8.5
Comment 6 Build Bot 2013-09-26 19:02:08 PDT
Comment on attachment 212774 [details]
Patch

Attachment 212774 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.appspot.com/results/2632042

New failing tests:
css3/calc/cssom.html
Comment 7 Build Bot 2013-09-26 19:02:11 PDT
Created attachment 212785 [details]
Archive of layout-test-results from webkit-ews-07 for mac-mountainlion

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: webkit-ews-07  Port: mac-mountainlion  Platform: Mac OS X 10.8.5
Comment 8 Build Bot 2013-09-26 20:01:27 PDT
Comment on attachment 212774 [details]
Patch

Attachment 212774 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.appspot.com/results/2652011

New failing tests:
css3/calc/cssom.html
Comment 9 Build Bot 2013-09-26 20:01:30 PDT
Created attachment 212786 [details]
Archive of layout-test-results from webkit-ews-03 for mac-mountainlion

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: webkit-ews-03  Port: mac-mountainlion  Platform: Mac OS X 10.8.5
Comment 10 Hans Muller 2013-09-27 07:22:39 PDT
Created attachment 212805 [details]
Patch

Updated the expected results for /css3/calc/cssom.html. Fixed the LayoutTests ChangeLog entry.
Comment 11 Dirk Schulze 2013-09-27 09:06:39 PDT
Comment on attachment 212805 [details]
Patch

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

Thanks for all the work Hans! Great back porting patch. And thanks for cleaning up the source code as well. Just some snippets inside. Really just minor things but I would like to look over the patch again.

> LayoutTests/css3/calc/cssom-expected.txt:12
> -100px * (1 + 2 * 3 - 4 / 5) => calc(100px * ((1 + (2 * 3)) - (4 / 5)))
> -(100px) + 200px => calc(100px + 200px)
> +100px * (1 + 2 * 3 - 4 / 5) => calc(620px)
> +(100px) + 200px => calc(300px)

Do we have nested calc's as well? This is important if you animate from one calc function to a real value on CSS Shapes. So we need a test for that. I believe a small test calc(5px + calc(3px * 5%)) is sufficient enough.

> Source/WebCore/css/BasicShapeFunctions.cpp:41
> +static PassRefPtr<CSSPrimitiveValue> convertToCSSPrimitiveValue(const RenderStyle* style, const Length& length)

This function actually makes the calls longer. Compare the both:
 
pool.createValue(rectangle->x(), style);
convertToCSSPrimitiveValue(style, rectangle->x())

It doesn't look like it is worth it and you have to look at another place to see what is going on as well. I suggest removing this function again.

> Source/WebCore/css/BasicShapeFunctions.cpp:121
> +    return value->convertToLength<CalculatedConversion | FixedIntegerConversion | FixedFloatConversion | PercentConversion | ViewportPercentageConversion>(style, rootStyle, style->effectiveZoom());

I think CalculatedConversion is lined up after PercentConversion every where else. Just a snippet, but makes search and replace easier later when we refactor the code :D

> Source/WebCore/css/CSSCalculationValue.cpp:273
> +

One newline too many.

> Source/WebCore/css/CSSCalculationValue.cpp:292
> +        ASSERT_NOT_REACHED();

We already have the assertion in the switch, you'll never reach it.

> Source/WebCore/css/CSSCalculationValue.cpp:326
> +/* CalcNumber */        { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
> +/* CalcLength */        { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength },
> +/* CalcPercent */       { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength },
> +/* CalcPercentNumber */ { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
> +/* CalcPercentLength */ { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength },

Do you mind if you add these comments at the end of each line?

> Source/WebCore/css/CSSCalculationValue.cpp:362
> +    // Not testing for actual integer values.

I don't understand this comment.

> Source/WebCore/css/CSSCalculationValue.cpp:383
> +    static PassRefPtr<CSSCalcExpressionNode> createSimplified(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)

Wow! This function is massive! Do we have tests for special cases like calc(a + b / calc(d * calc(e + f))) ?

> Source/WebCore/css/CSSCalculationValue.cpp:748
> +        // FIXME(crbug.com/269320): Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.

Hmmm. I am not sure if we should leave that in WebKit source code. I think we have a WebKit bug for it already. Can you search it and replace the comment with the webkit bug please?

> Source/WebCore/css/CSSCalculationValue.cpp:795
> +    ASSERT_NOT_REACHED();

I am unsure if we should leave these assertions. But maybe we should if someone forgets to add an return. But then we should remove the assertion and return in case Undefined:. (Same with the other switches)
Comment 12 Hans Muller 2013-09-27 12:17:58 PDT
(In reply to comment #11)
> (From update of attachment 212805 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=212805&action=review
> 
> > LayoutTests/css3/calc/cssom-expected.txt:12
> > -100px * (1 + 2 * 3 - 4 / 5) => calc(100px * ((1 + (2 * 3)) - (4 / 5)))
> > -(100px) + 200px => calc(100px + 200px)
> > +100px * (1 + 2 * 3 - 4 / 5) => calc(620px)
> > +(100px) + 200px => calc(300px)
> 
> Do we have nested calc's as well? This is important if you animate from one calc function to a real value on CSS Shapes. So we need a test for that. I believe a small test calc(5px + calc(3px * 5%)) is sufficient enough.

We don't have nested calc's yet.  

> > Source/WebCore/css/CSSCalculationValue.cpp:292
> > +        ASSERT_NOT_REACHED();
> 
> We already have the assertion in the switch, you'll never reach it.

I agree however I think it's there to keep the Windows compiler from failing.  I think there was a discussion about this on webkit-dev recently although I haven't been able to find it.

> > Source/WebCore/css/CSSCalculationValue.cpp:362
> > +    // Not testing for actual integer values.
> 
> I don't understand this comment.

I think it's drawing a distinction between the calc() "resolved type" of the expression and the actual type of the result.  For example the result of 4/2 is not an integer per the spec. I don't think this part of the comment is useful (the spec reference below it is), so I've removed it.

> > Source/WebCore/css/CSSCalculationValue.cpp:383
> > +    static PassRefPtr<CSSCalcExpressionNode> createSimplified(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
> 
> Wow! This function is massive! Do we have tests for special cases like calc(a + b / calc(d * calc(e + f))) ?

Good point.  I'd forgotten to migrate the test added to check the new simplification code.

> 
> > Source/WebCore/css/CSSCalculationValue.cpp:748
> > +        // FIXME(crbug.com/269320): Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.
> 
> Hmmm. I am not sure if we should leave that in WebKit source code. I think we have a WebKit bug for it already. Can you search it and replace the comment with the webkit bug please?

I wasn't able to locate an existing bug for this.  I've removed the Blink bug reference for now.
Comment 13 Hans Muller 2013-09-27 12:28:40 PDT
Created attachment 212834 [details]
Patch

Made the suggested changes.
Comment 14 Bem Jones-Bey 2013-09-27 12:30:39 PDT
Comment on attachment 212805 [details]
Patch

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

>>> Source/WebCore/css/CSSCalculationValue.cpp:292
>>> +        ASSERT_NOT_REACHED();
>> 
>> We already have the assertion in the switch, you'll never reach it.
> 
> I agree however I think it's there to keep the Windows compiler from failing.  I think there was a discussion about this on webkit-dev recently although I haven't been able to find it.

It's Qt, actually. Here's the thread: https://lists.webkit.org/pipermail/webkit-dev/2013-September/025422.html
Comment 15 Dirk Schulze 2013-09-27 12:59:30 PDT
Comment on attachment 212834 [details]
Patch

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

Great patch. Just a snippet. r+

> Source/WebCore/css/CSSCalculationValue.cpp:746
> +        // FIXME: Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.

If this bug does not exist yet, please create it and add the link in the comment.
Comment 16 Hans Muller 2013-09-27 15:24:58 PDT
(In reply to comment #15)
> (From update of attachment 212834 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=212834&action=review
> 
> Great patch. Just a snippet. r+
> 
> > Source/WebCore/css/CSSCalculationValue.cpp:746
> > +        // FIXME: Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.
> 
> If this bug does not exist yet, please create it and add the link in the comment.

I created a WebKit version of the Bink bug, it's https://bugs.webkit.org/show_bug.cgi?id=122036
Comment 17 Hans Muller 2013-09-27 15:39:31 PDT
Created attachment 212846 [details]
Patch

Added the FIXME bug URL.
Comment 18 WebKit Commit Bot 2013-09-27 16:16:18 PDT
Comment on attachment 212846 [details]
Patch

Clearing flags on attachment: 212846

Committed r156586: <http://trac.webkit.org/changeset/156586>
Comment 19 WebKit Commit Bot 2013-09-27 16:16:25 PDT
All reviewed patches have been landed.  Closing bug.
Comment 20 Antti Koivisto 2013-11-07 01:32:47 PST
Comment on attachment 212846 [details]
Patch

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

> Source/WebCore/css/CSSValuePool.h:55
> +    PassRefPtr<CSSPrimitiveValue> createValue(const Length& value, const RenderStyle* style) { return CSSPrimitiveValue::create(value, style); }

A value with style? How does that make sense?
Comment 21 Hans Muller 2013-11-07 11:50:38 PST
(In reply to comment #20)
> (From update of attachment 212846 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=212846&action=review
> 
> > Source/WebCore/css/CSSValuePool.h:55
> > +    PassRefPtr<CSSPrimitiveValue> createValue(const Length& value, const RenderStyle* style) { return CSSPrimitiveValue::create(value, style); }
> 
> A value with style? How does that make sense?

To c(In reply to comment #20)
> (From update of attachment 212846 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=212846&action=review
> 
> > Source/WebCore/css/CSSValuePool.h:55
> > +    PassRefPtr<CSSPrimitiveValue> createValue(const Length& value, const RenderStyle* style) { return CSSPrimitiveValue::create(value, style); }
> 
> A value with style? How does that make sense?

It's really just there to provide access to the style's effectiveZoom, which is needed to compute the value of pixel type lengths. The CSSPrimitiveValue doesn't actually hang on to the style.