Support transform-box to switch sizing box in SVG
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 May 2017 19:19:24 +0000 (19:19 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 May 2017 19:19:24 +0000 (19:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=145783

Reviewed by Dean Jackson.

Source/WebCore:

Add support for the CSS "transform-box" property, as described at
<https://drafts.csswg.org/css-transforms/#transform-box>.

This changes the behavior of percentage values in transform-origin in SVG.
When these were added in r110532, percentage values in transform-origin were made
relative to the bounding box, but absolute values relative to the view box.
<https://github.com/w3c/csswg-drafts/issues/895> has concluded that this behavior
is confusing. The new behavior is that, for SVG elements, both absolute and
percentage values are relative to the reference box, which is specified by the
new transform-box property.

The initial value for transform-box is border-box, with the svg.css UA stylesheet
supplying a default of view-box for the relevant SVG elements per
<https://www.w3.org/TR/SVG2/styling.html#UAStyleSheet>.

For non-SVG elements, the used value is always border-box, so there is no change
in behavior.

Tests: fast/css/transform-box-parsing.html
       svg/transforms/svg-transform-box.html

* css/CSSComputedStyleDeclaration.cpp:
(WebCore::ComputedStyleExtractor::propertyValue):
* css/CSSPrimitiveValueMappings.h:
(WebCore::CSSPrimitiveValue::CSSPrimitiveValue):
(WebCore::CSSPrimitiveValue::operator TransformBox):
* css/CSSProperties.json:
* css/CSSValueKeywords.in:
* css/parser/CSSPropertyParser.cpp:
(WebCore::CSSPropertyParser::parseSingleValue):
* css/svg.css:
(*:not(svg),):
(*): Deleted.
(html|* > svg): Deleted.
* page/animation/AnimationBase.cpp:
(WebCore::AnimationBase::computeTransformedExtentViaTransformList):
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::applyTransform): The transformOriginX().isPercent() tests
were added to support the weird "% values are relative to bounding box" in SVG. Now
it's up to the caller to pass a non-zero origin when that matters, and
SVGGraphicsElement::animatedLocalTransform() is the only caller that does so.
* rendering/style/RenderStyle.h:
(WebCore::RenderStyle::hasTransform):
(WebCore::RenderStyle::transformBox):
(WebCore::RenderStyle::setTransformBox):
(WebCore::RenderStyle::initialTransformBox):
* rendering/style/RenderStyleConstants.h:
* rendering/style/StyleTransformData.cpp:
(WebCore::StyleTransformData::StyleTransformData):
(WebCore::StyleTransformData::operator==):
* rendering/style/StyleTransformData.h:
* svg/SVGGraphicsElement.cpp:
(WebCore::SVGGraphicsElement::animatedLocalTransform): Consult the transform-box
style to compute the reference box as the bounding box, or the view box.

Source/WebInspectorUI:

Add transform-box to the list of autocompletions.

* UserInterface/External/CodeMirror/css.js:

LayoutTests:

Modify tests that relied on the old "% values relative to the bounding box" behavior,
and new tests for parsing and rendering with transform-box.

* fast/css/transform-box-parsing.html: Added.
* svg/transforms/change-transform-origin-css.xhtml:
* svg/transforms/change-transform-origin-presentation-attribute.xhtml:
* svg/transforms/percent-transform-values.xhtml:
* svg/transforms/svg-transform-box-expected.html: Added.
* svg/transforms/svg-transform-box.html: Added.
* svg/transforms/transform-origin-css-property.xhtml:
* transforms/svg-vs-css.xhtml:

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

26 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/transform-box-parsing-expected.txt [new file with mode: 0644]
LayoutTests/fast/css/transform-box-parsing.html [new file with mode: 0644]
LayoutTests/svg/transforms/change-transform-origin-css.xhtml
LayoutTests/svg/transforms/change-transform-origin-presentation-attribute.xhtml
LayoutTests/svg/transforms/percent-transform-values.xhtml
LayoutTests/svg/transforms/svg-transform-box-expected.html [new file with mode: 0644]
LayoutTests/svg/transforms/svg-transform-box.html [new file with mode: 0644]
LayoutTests/svg/transforms/transform-origin-css-property.xhtml
LayoutTests/transforms/svg-vs-css.xhtml
Source/WebCore/ChangeLog
Source/WebCore/css/CSSComputedStyleDeclaration.cpp
Source/WebCore/css/CSSPrimitiveValueMappings.h
Source/WebCore/css/CSSProperties.json
Source/WebCore/css/CSSValueKeywords.in
Source/WebCore/css/parser/CSSPropertyParser.cpp
Source/WebCore/css/svg.css
Source/WebCore/page/animation/AnimationBase.cpp
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/RenderStyleConstants.h
Source/WebCore/rendering/style/StyleTransformData.cpp
Source/WebCore/rendering/style/StyleTransformData.h
Source/WebCore/svg/SVGGraphicsElement.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/External/CodeMirror/css.js

index 7bd0aef..8f401f0 100644 (file)
@@ -1,3 +1,22 @@
+2017-05-22  Simon Fraser  <simon.fraser@apple.com>
+
+        Support transform-box to switch sizing box in SVG
+        https://bugs.webkit.org/show_bug.cgi?id=145783
+
+        Reviewed by Dean Jackson.
+
+        Modify tests that relied on the old "% values relative to the bounding box" behavior,
+        and new tests for parsing and rendering with transform-box.
+
+        * fast/css/transform-box-parsing.html: Added.
+        * svg/transforms/change-transform-origin-css.xhtml:
+        * svg/transforms/change-transform-origin-presentation-attribute.xhtml:
+        * svg/transforms/percent-transform-values.xhtml:
+        * svg/transforms/svg-transform-box-expected.html: Added.
+        * svg/transforms/svg-transform-box.html: Added.
+        * svg/transforms/transform-origin-css-property.xhtml:
+        * transforms/svg-vs-css.xhtml:
+
 2017-05-22  Antti Koivisto  <antti@apple.com>
 
         Crash in WebCore::StyleRuleKeyframes::findKeyframeIndex
diff --git a/LayoutTests/fast/css/transform-box-parsing-expected.txt b/LayoutTests/fast/css/transform-box-parsing-expected.txt
new file mode 100644 (file)
index 0000000..795ff69
--- /dev/null
@@ -0,0 +1,28 @@
+This tests checks parsing of the 'transform-box' property
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS testPropertyValue("transform-box: border-box", "transform-box") is "border-box"
+PASS testPropertyValue("transform-box: fill-box", "transform-box") is "fill-box"
+PASS testPropertyValue("transform-box: view-box", "transform-box") is "view-box"
+
+PASS testComputedStyle("", "transform-box") is "border-box"
+PASS testComputedStyle("transform-box: fill-box", "transform-box") is "fill-box"
+PASS testComputedStyle("transform-box: view-box", "transform-box") is "view-box"
+
+PASS testSVGComputedStyle("", "transform-box") is "view-box"
+PASS testSVGComputedStyle("transform-box: fill-box", "transform-box") is "fill-box"
+PASS testSVGComputedStyle("transform-box: border-box", "transform-box") is "border-box"
+
+PASS testComputedStyleOnElementWithId("foreignObject") is "view-box"
+
+Test default value on elements with CSS layout boxes
+PASS testComputedStyleOnElementWithId("svgRoot") is "border-box"
+PASS testComputedStyleOnElementWithId("nested-svg") is "border-box"
+PASS testComputedStyleOnElementWithId("inside-foreign-object") is "border-box"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/css/transform-box-parsing.html b/LayoutTests/fast/css/transform-box-parsing.html
new file mode 100644 (file)
index 0000000..52918b5
--- /dev/null
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+    <svg id="svgRoot" xmlns="http://www.w3.org/2000/svg">
+        <foreignObject id="foreignObject">
+            <html>
+                <div id="inside-foreign-object"></div>
+            </html>
+        </foreignObject>
+        <foreignObject>
+            <svg id="nested-svg" xmlns="http://www.w3.org/2000/svg" />
+        </foreignObject>
+    </svg>
+    <script>
+        description("This tests checks parsing of the 'transform-box' property");
+
+        function testPropertyValue(declaration, property)
+        {
+            var div = document.createElement("div");
+            div.setAttribute("style", declaration);
+            document.body.appendChild(div);
+
+            var result = div.style.getPropertyValue(property);
+            document.body.removeChild(div);
+            return result;
+        }
+
+        function testComputedStyle(declaration, property)
+        {
+            var div = document.createElement("div");
+            div.setAttribute("style", declaration);
+            document.body.appendChild(div);
+
+            var result = window.getComputedStyle(div).getPropertyValue(property);
+            document.body.removeChild(div);
+            return result;
+        }
+
+        const svgNS = "http://www.w3.org/2000/svg";
+
+        function testSVGComputedStyle(declaration, property)
+        {
+            var svgRoot = document.getElementById('svgRoot');
+
+            var rect = document.createElementNS(svgNS, "rect");
+            rect.setAttribute("style", declaration);
+            svgRoot.appendChild(rect);
+
+            var result = window.getComputedStyle(rect).getPropertyValue(property);
+            svgRoot.removeChild(rect);
+            return result;
+        }
+        
+        function testComputedStyleOnElementWithId(elementId)
+        {
+            return window.getComputedStyle(document.getElementById(elementId)).getPropertyValue("transform-box");
+        }
+
+        shouldBeEqualToString('testPropertyValue("transform-box: border-box", "transform-box")', 'border-box');
+        shouldBeEqualToString('testPropertyValue("transform-box: fill-box", "transform-box")', 'fill-box');
+        shouldBeEqualToString('testPropertyValue("transform-box: view-box", "transform-box")', 'view-box');
+
+        debug('');
+        shouldBeEqualToString('testComputedStyle("", "transform-box")', 'border-box');
+        shouldBeEqualToString('testComputedStyle("transform-box: fill-box", "transform-box")', 'fill-box');
+        shouldBeEqualToString('testComputedStyle("transform-box: view-box", "transform-box")', 'view-box');
+
+        debug('');
+        shouldBeEqualToString('testSVGComputedStyle("", "transform-box")', 'view-box');
+        shouldBeEqualToString('testSVGComputedStyle("transform-box: fill-box", "transform-box")', 'fill-box');
+        shouldBeEqualToString('testSVGComputedStyle("transform-box: border-box", "transform-box")', 'border-box');
+
+        debug('');
+        shouldBeEqualToString('testComputedStyleOnElementWithId("foreignObject")', 'view-box');
+
+        debug('');
+        debug('Test default value on elements with CSS layout boxes');
+        shouldBeEqualToString('testComputedStyleOnElementWithId("svgRoot")', 'border-box');
+        shouldBeEqualToString('testComputedStyleOnElementWithId("nested-svg")', 'border-box');
+        shouldBeEqualToString('testComputedStyleOnElementWithId("inside-foreign-object")', 'border-box');
+
+        debug('');
+    </script>
+
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 38f94ce..58adb0d 100644 (file)
@@ -9,11 +9,11 @@ The green 100x100 rectangle should appear at 25,25.
 
 <body>
   <svg xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: 0px; left: 0px;">
-      <rect id="rect" width="50px" height="50px" x="50" y="50" fill="green" style="-webkit-transform: scale(2, 2);"/>
+      <rect id="rect" width="50px" height="50px" x="50" y="50" fill="green" style="transform: scale(2, 2); transform-box: fill-box;"/>
   </svg>
 
   <script><![CDATA[
-      document.getElementById('rect').style.webkitTransformOrigin = "50% 50%";
+      document.getElementById('rect').style.transformOrigin = "50% 50%";
   ]]></script>
 </body>
 </html>
index c03ee2b..5ce8f70 100644 (file)
@@ -8,7 +8,7 @@ The green 100x100 rectangle should appear at 25,25.
 
 <body>
   <svg xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: 0px; left: 0px;">
-      <rect id="rect" width="50px" height="50px" x="50" y="50" fill="green" transform-origin="0 0" style="-webkit-transform: scale(2, 2);"/>
+      <rect id="rect" width="50px" height="50px" x="50" y="50" fill="green" transform-origin="0 0" style="transform: scale(2, 2); transform-box: fill-box;"/>
   </svg>
 
   <script><![CDATA[
index 5e2648b..f0a16b9 100644 (file)
@@ -1,6 +1,6 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <body>
-  <p>Test for bug <a href="">79068</a> - SVG should support transform-origin and relative values
+  <p>Test for bug <a href="http://webkit.org/b/79068">79068</a> - SVG should support transform-origin and relative values
   Verify that CSS transform translate values can be specified with percentages.
   </p>
   You should see a series of "PASS" messages and one 80x60 green rectangle.   The green rectangle obscures two
@@ -8,9 +8,9 @@
   <p>
   </p>
   <svg style="position:absolute; left:0px; top:0px;" xmlns="http://www.w3.org/2000/svg">
-    <rect id="r1" x="10" y="200" width="80" height="60" fill="red" style="-webkit-transform: translate(50%)"/>
-    <rect id="r2" x="90" y="260" width="80" height="60" fill="red" style="-webkit-transform: translate(-50%, -100%)"/>
-    <rect id="r3" x="10" y="200" width="80" height="60" fill="green" style="-webkit-transform: translate(50%, 0%)"/>
+    <rect id="r1" x="10" y="200" width="80" height="60" fill="red" style="transform-box: fill-box; transform: translate(50%)"/>
+    <rect id="r2" x="90" y="260" width="80" height="60" fill="red" style="transform-box: fill-box; transform: translate(-50%, -100%)"/>
+    <rect id="r3" x="10" y="200" width="80" height="60" fill="green" style="transform-box: fill-box; transform: translate(50%, 0%)"/>
   </svg>
   <pre id="log"></pre>
 </body>
diff --git a/LayoutTests/svg/transforms/svg-transform-box-expected.html b/LayoutTests/svg/transforms/svg-transform-box-expected.html
new file mode 100644 (file)
index 0000000..90c617f
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style>
+    svg {
+        border: 1px solid black;
+    }
+  </style>
+</head>
+
+<body>
+
+    <div class="container">
+        <p>You should see no red boxes below</p>
+        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 600 400" style=" width: 600px; height: 400px;">
+            <!-- transform-box: view-box -->
+            <rect x="20" y="10" width="100" height="50" fill="green" />
+            <rect x="480" y="40" width="50" height="50" fill="green" />
+            <rect x="120" y="120" width="50" height="50" fill="green" />
+            <rect x="20" y="200" width="100" height="50" fill="green" />
+            <rect x="480" y="260" width="50" height="50" fill="green" />
+            <rect x="120" y="320" width="50" height="50" fill="green" />
+        </svg>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/svg/transforms/svg-transform-box.html b/LayoutTests/svg/transforms/svg-transform-box.html
new file mode 100644 (file)
index 0000000..2506f53
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style>
+    svg {
+        border: 1px solid black;
+    }
+  </style>
+</head>
+
+<body>
+
+    <div class="container">
+        <p>You should see no red boxes below</p>
+        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 600 400" style=" width: 600px; height: 400px;">
+            <!-- transform-box: view-box -->
+            <rect x="20" y="10" width="100" height="50" fill="red" />
+            <rect x="20" y="5" width="50" height="25" fill="green" style="transform: scale(2, 2); transform-origin: 20px 0; transform-box: view-box;" />
+
+            <rect x="480" y="40" width="50" height="50" fill="red" />
+            <rect x="0" y="0" width="50" height="50" fill="green" style="transform: translate(80%, 10%); transform-box: view-box;" />
+
+            <rect x="120" y="120" width="50" height="50" fill="red" />
+            <rect x="180" y="10" width="50" height="50" fill="green" style="transform: rotate(90deg); transform-origin: 120px 60px; transform-box: view-box;" />
+            <rect x="180" y="10" width="50" height="50" fill="green" style="transform: rotate(90deg); transform-origin: 20% 15%; transform-box: view-box;" />
+
+            <!-- transform-box: fill-box -->
+            <rect x="20" y="200" width="100" height="50" fill="red" />
+            <rect x="20" y="200" width="50" height="25" fill="green" style="transform: scale(2, 2); transform-box: fill-box;" />
+
+            <rect x="480" y="260" width="50" height="50" fill="red" />
+            <rect x="430" y="310" width="50" height="50" fill="green" style="transform: translate(100%, -100%); transform-box: fill-box;" />
+
+            <rect x="120" y="320" width="50" height="50" fill="red" />
+            <rect x="70" y="270" width="50" height="50" fill="green" style="transform: rotate(180deg); transform-origin: 50px 50px; transform-box: fill-box;" />
+            <rect x="95" y="345" width="50" height="50" fill="green" style="transform: rotate(90deg); transform-origin: 100% 50%; transform-box: fill-box;" />
+        </svg>
+    </div>
+
+</body>
+</html>
index 205baef..9aac74f 100644 (file)
@@ -10,7 +10,7 @@
 
 #transformOriginRect {
     fill: green;
-    -webkit-transform: rotate(90deg);
+    transform: rotate(90deg);
 }
 </style>
 
     function addTransformOriginRect(x, y, s)
     {
        var rect = document.createElementNS(svgNS, "rect");
-       var transformOrigin = (typeof(s) == "string") ? s :  (x + s[0]) + " " + (y + s[1]);
+       var transformOrigin = (typeof(s) == "string") ? s :  (x + s[0]) + "px " + (y + s[1]) + "px";
        rect.setAttribute("id", "transformOriginRect");
        rect.setAttribute("x", x);
        rect.setAttribute("y", y);
        rect.setAttribute("width", 30);
        rect.setAttribute("height", 30);
-       rect.setAttribute("style", "-webkit-transform-origin: " + transformOrigin + ";");
+       rect.setAttribute("style", "transform-box: view-box; transform-origin: " + transformOrigin + ";");
        document.getElementById("svgRoot").appendChild(rect);
     }
 
index 69f01c9..573159e 100644 (file)
@@ -97,9 +97,9 @@
     <div class="container">
       <svg xmlns="http://www.w3.org/2000/svg" version="1.1"  
           viewBox="0 0 200 200" style="width:200px; height:200px;">
-          <g id="group1" x="0" y="0" width="60" height="60" transform="translate(75, 25)">
+          <g id="group1" transform="translate(75, 25)">
             <rect x="0" y="0" width="60" height="60" stroke="black" stroke-width="1px" stroke-dasharray="1 1" fill="none" />
-            <g id="group2" x="0" y="0" width="60" height="60" transform="scale(2)" >
+            <g id="group2" transform="scale(2)">
               <rect x="0" y="0" width="60" height="60" stroke="black" stroke-dasharray="1 1" stroke-width="1px" fill="none" />
               <rect id="group3" x="0" y="0" width="60" height="60" stroke="blue" fill="none" transform="rotate(45)" />
             </g>
index f9e7b6f..8400ea0 100644 (file)
@@ -1,3 +1,65 @@
+2017-05-22  Simon Fraser  <simon.fraser@apple.com>
+
+        Support transform-box to switch sizing box in SVG
+        https://bugs.webkit.org/show_bug.cgi?id=145783
+
+        Reviewed by Dean Jackson.
+
+        Add support for the CSS "transform-box" property, as described at
+        <https://drafts.csswg.org/css-transforms/#transform-box>.
+        
+        This changes the behavior of percentage values in transform-origin in SVG.
+        When these were added in r110532, percentage values in transform-origin were made
+        relative to the bounding box, but absolute values relative to the view box.
+        <https://github.com/w3c/csswg-drafts/issues/895> has concluded that this behavior
+        is confusing. The new behavior is that, for SVG elements, both absolute and
+        percentage values are relative to the reference box, which is specified by the
+        new transform-box property.
+
+        The initial value for transform-box is border-box, with the svg.css UA stylesheet
+        supplying a default of view-box for the relevant SVG elements per
+        <https://www.w3.org/TR/SVG2/styling.html#UAStyleSheet>.
+
+        For non-SVG elements, the used value is always border-box, so there is no change
+        in behavior.
+
+        Tests: fast/css/transform-box-parsing.html
+               svg/transforms/svg-transform-box.html
+
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::ComputedStyleExtractor::propertyValue):
+        * css/CSSPrimitiveValueMappings.h:
+        (WebCore::CSSPrimitiveValue::CSSPrimitiveValue):
+        (WebCore::CSSPrimitiveValue::operator TransformBox):
+        * css/CSSProperties.json:
+        * css/CSSValueKeywords.in:
+        * css/parser/CSSPropertyParser.cpp:
+        (WebCore::CSSPropertyParser::parseSingleValue):
+        * css/svg.css:
+        (*:not(svg),):
+        (*): Deleted.
+        (html|* > svg): Deleted.
+        * page/animation/AnimationBase.cpp:
+        (WebCore::AnimationBase::computeTransformedExtentViaTransformList):
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::applyTransform): The transformOriginX().isPercent() tests
+        were added to support the weird "% values are relative to bounding box" in SVG. Now
+        it's up to the caller to pass a non-zero origin when that matters, and
+        SVGGraphicsElement::animatedLocalTransform() is the only caller that does so.
+        * rendering/style/RenderStyle.h:
+        (WebCore::RenderStyle::hasTransform):
+        (WebCore::RenderStyle::transformBox):
+        (WebCore::RenderStyle::setTransformBox):
+        (WebCore::RenderStyle::initialTransformBox):
+        * rendering/style/RenderStyleConstants.h:
+        * rendering/style/StyleTransformData.cpp:
+        (WebCore::StyleTransformData::StyleTransformData):
+        (WebCore::StyleTransformData::operator==):
+        * rendering/style/StyleTransformData.h:
+        * svg/SVGGraphicsElement.cpp:
+        (WebCore::SVGGraphicsElement::animatedLocalTransform): Consult the transform-box
+        style to compute the reference box as the bounding box, or the view box.
+
 2017-05-22  Chris Dumez  <cdumez@apple.com>
 
         Add support for [LegacyWindowAlias] IDL extended attribute
index 75e1b5d..0460e51 100644 (file)
@@ -231,6 +231,7 @@ static const CSSPropertyID computedProperties[] = {
     CSSPropertyTextTransform,
     CSSPropertyTop,
     CSSPropertyTransform,
+    CSSPropertyTransformBox,
     CSSPropertyTransformOrigin,
     CSSPropertyTransformStyle,
     CSSPropertyTransitionDelay,
@@ -3682,6 +3683,8 @@ RefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propertyID,
             return cssValuePool.createValue(style->speak());
         case CSSPropertyTransform:
             return computedTransform(renderer, *style);
+        case CSSPropertyTransformBox:
+            return CSSPrimitiveValue::create(style->transformBox());
         case CSSPropertyTransformOrigin: {
             auto list = CSSValueList::createSpaceSeparated();
             if (renderer) {
index 9e491fa..6a4366d 100644 (file)
@@ -4414,6 +4414,42 @@ template<> inline CSSPrimitiveValue::operator ETransformStyle3D() const
     return TransformStyle3DFlat;
 }
 
+template<> inline CSSPrimitiveValue::CSSPrimitiveValue(TransformBox box)
+    : CSSValue(PrimitiveClass)
+{
+    m_primitiveUnitType = CSS_VALUE_ID;
+    switch (box) {
+    case TransformBox::BorderBox:
+        m_value.valueID = CSSValueBorderBox;
+        break;
+    case TransformBox::FillBox:
+        m_value.valueID = CSSValueFillBox;
+        break;
+    case TransformBox::ViewBox:
+        m_value.valueID = CSSValueViewBox;
+        break;
+    }
+}
+
+template<> inline CSSPrimitiveValue::operator TransformBox() const
+{
+    ASSERT(isValueID());
+
+    switch (m_value.valueID) {
+    case CSSValueBorderBox:
+        return TransformBox::BorderBox;
+    case CSSValueFillBox:
+        return TransformBox::FillBox;
+    case CSSValueViewBox:
+        return TransformBox::ViewBox;
+    default:
+        break;
+    }
+
+    ASSERT_NOT_REACHED();
+    return TransformBox::BorderBox;
+}
+
 template<> inline CSSPrimitiveValue::CSSPrimitiveValue(ColumnAxis e)
     : CSSValue(PrimitiveClass)
 {
index 1abcb50..57c3a3f 100644 (file)
                 "url": "https://www.w3.org/TR/css-transforms-1/#transform-property"
             }
         },
+        "transform-box": {
+            "values" : [
+                "border-box",
+                "fill-box",
+                "view-box"
+            ],
+            "codegen-properties": {
+            },
+            "specification": {
+                "category": "css-transforms",
+                "url": "https://www.w3.org/TR/css-transforms/#propdef-transform-box"
+            }
+        },
         "transform-origin": {
             "codegen-properties": {
                 "aliases": [
index 2e9a622..c45e0ea 100644 (file)
@@ -1247,6 +1247,11 @@ translateY
 translateZ
 translate3d
 
+// transform-box
+// border-box
+// view-box
+fill-box
+
 // motion path
 path
 
index 3e08726..b9af41e 100644 (file)
@@ -4071,6 +4071,8 @@ RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSS
         return consumeLineWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
     case CSSPropertyTransform:
         return consumeTransform(m_range, m_context.mode);
+    case CSSPropertyTransformBox:
+        return consumeIdent<CSSValueBorderBox, CSSValueViewBox, CSSValueFillBox>(m_range);
     case CSSPropertyTransformOriginX:
     case CSSPropertyPerspectiveOriginX:
         return consumePositionX(m_range, m_context.mode);
index 1cf8881..53537e8 100644 (file)
@@ -72,12 +72,8 @@ text, tspan, tref {
     outline: auto 5px -webkit-focus-ring-color
 }
 
-/* CSS transform specification: "transform-origin 0 0 for SVG elements without associated CSS layout box, 50% 50% for all other elements". */
-* {
-    -webkit-transform-origin: 0 0;
-}
-
-html|* > svg {
-    -webkit-transform-origin: 50% 50%;
+*:not(svg),
+*:not(foreignObject) > svg {
+    transform-origin: 0 0;
+    transform-box: view-box;
 }
index 5e93f83..4cf5e29 100644 (file)
@@ -802,13 +802,10 @@ bool AnimationBase::computeTransformedExtentViaTransformList(const FloatRect& re
     
     bool applyTransformOrigin = containsRotation(style.transform().operations()) || style.transform().affectedByTransformOrigin();
     if (applyTransformOrigin) {
-        float offsetX = style.transformOriginX().isPercent() ? rendererBox.x() : 0;
-        float offsetY = style.transformOriginY().isPercent() ? rendererBox.y() : 0;
-
-        transformOrigin.setX(floatValueForLength(style.transformOriginX(), rendererBox.width()) + offsetX);
-        transformOrigin.setY(floatValueForLength(style.transformOriginY(), rendererBox.height()) + offsetY);
+        transformOrigin.setX(rendererBox.x() + floatValueForLength(style.transformOriginX(), rendererBox.width()));
+        transformOrigin.setY(rendererBox.y() + floatValueForLength(style.transformOriginY(), rendererBox.height()));
         // Ignore transformOriginZ because we'll bail if we encounter any 3D transforms.
-        
+
         floatBounds.moveBy(-transformOrigin);
     }
 
index 3f48034..da17065 100644 (file)
@@ -1130,24 +1130,20 @@ void RenderStyle::applyTransform(TransformationMatrix& transform, const FloatRec
 {
     auto& operations = m_rareNonInheritedData->transform->operations.operations();
     bool applyTransformOrigin = requireTransformOrigin(operations, applyOrigin);
-
-    float offsetX = transformOriginX().isPercent() ? boundingBox.x() : 0;
-    float offsetY = transformOriginY().isPercent() ? boundingBox.y() : 0;
-
+    
+    FloatPoint3D originTranslate;
     if (applyTransformOrigin) {
-        transform.translate3d(floatValueForLength(transformOriginX(), boundingBox.width()) + offsetX,
-                              floatValueForLength(transformOriginY(), boundingBox.height()) + offsetY,
-                              transformOriginZ());
+        originTranslate.setX(boundingBox.x() + floatValueForLength(transformOriginX(), boundingBox.width()));
+        originTranslate.setY(boundingBox.y() + floatValueForLength(transformOriginY(), boundingBox.height()));
+        originTranslate.setZ(transformOriginZ());
+        transform.translate3d(originTranslate.x(), originTranslate.y(), originTranslate.z());
     }
 
     for (auto& operation : operations)
         operation->apply(transform, boundingBox.size());
 
-    if (applyTransformOrigin) {
-        transform.translate3d(-floatValueForLength(transformOriginX(), boundingBox.width()) - offsetX,
-                              -floatValueForLength(transformOriginY(), boundingBox.height()) - offsetY,
-                              -transformOriginZ());
-    }
+    if (applyTransformOrigin)
+        transform.translate3d(-originTranslate.x(), -originTranslate.y(), -originTranslate.z());
 }
 
 void RenderStyle::setPageScaleTransform(float scale)
index e4657b1..e80f958 100644 (file)
@@ -608,10 +608,11 @@ public:
     ColumnSpan columnSpan() const { return static_cast<ColumnSpan>(m_rareNonInheritedData->multiCol->columnSpan); }
 
     const TransformOperations& transform() const { return m_rareNonInheritedData->transform->operations; }
+    bool hasTransform() const { return !m_rareNonInheritedData->transform->operations.operations().isEmpty(); }
     const Length& transformOriginX() const { return m_rareNonInheritedData->transform->x; }
     const Length& transformOriginY() const { return m_rareNonInheritedData->transform->y; }
     float transformOriginZ() const { return m_rareNonInheritedData->transform->z; }
-    bool hasTransform() const { return !m_rareNonInheritedData->transform->operations.operations().isEmpty(); }
+    TransformBox transformBox() const { return m_rareNonInheritedData->transform->transformBox; }
 
     TextEmphasisFill textEmphasisFill() const { return static_cast<TextEmphasisFill>(m_rareInheritedData->textEmphasisFill); }
     TextEmphasisMark textEmphasisMark() const;
@@ -1135,10 +1136,13 @@ public:
     void resetColumnRule() { SET_NESTED_VAR(m_rareNonInheritedData, multiCol, rule, BorderValue()); }
     void setColumnSpan(ColumnSpan columnSpan) { SET_NESTED_VAR(m_rareNonInheritedData, multiCol, columnSpan, columnSpan); }
     void inheritColumnPropertiesFrom(const RenderStyle& parent) { m_rareNonInheritedData.access().multiCol = parent.m_rareNonInheritedData->multiCol; }
+
     void setTransform(const TransformOperations& ops) { SET_NESTED_VAR(m_rareNonInheritedData, transform, operations, ops); }
     void setTransformOriginX(Length&& length) { SET_NESTED_VAR(m_rareNonInheritedData, transform, x, WTFMove(length)); }
     void setTransformOriginY(Length&& length) { SET_NESTED_VAR(m_rareNonInheritedData, transform, y, WTFMove(length)); }
     void setTransformOriginZ(float f) { SET_NESTED_VAR(m_rareNonInheritedData, transform, z, f); }
+    void setTransformBox(TransformBox box) { SET_NESTED_VAR(m_rareNonInheritedData, transform, transformBox, box); }
+
     void setSpeak(ESpeak s) { SET_VAR(m_rareInheritedData, speak, s); }
     void setTextCombine(TextCombine v) { SET_VAR(m_rareNonInheritedData, textCombine, v); }
     void setTextDecorationColor(const Color& c) { SET_VAR(m_rareNonInheritedData, textDecorationColor, c); }
@@ -1532,6 +1536,7 @@ public:
     static const TransformOperations& initialTransform() { static NeverDestroyed<TransformOperations> ops; return ops; }
     static Length initialTransformOriginX() { return Length(50.0f, Percent); }
     static Length initialTransformOriginY() { return Length(50.0f, Percent); }
+    static TransformBox initialTransformBox() { return TransformBox::BorderBox; }
     static EPointerEvents initialPointerEvents() { return PE_AUTO; }
     static float initialTransformOriginZ() { return 0; }
     static ETransformStyle3D initialTransformStyle3D() { return TransformStyle3DFlat; }
index e9df190..0d4c075 100644 (file)
@@ -583,7 +583,13 @@ enum ETransformStyle3D {
 enum EBackfaceVisibility {
     BackfaceVisibilityVisible, BackfaceVisibilityHidden
 };
-    
+
+enum class TransformBox {
+    BorderBox,
+    FillBox,
+    ViewBox
+};
+
 enum ELineClampType { LineClampLineCount, LineClampPercentage };
 
 enum Hyphens { HyphensNone, HyphensManual, HyphensAuto };
index eaf7a6a..eba3222 100644 (file)
@@ -31,6 +31,7 @@ StyleTransformData::StyleTransformData()
     , x(RenderStyle::initialTransformOriginX())
     , y(RenderStyle::initialTransformOriginY())
     , z(RenderStyle::initialTransformOriginZ())
+    , transformBox(RenderStyle::initialTransformBox())
 {
 }
 
@@ -40,6 +41,7 @@ inline StyleTransformData::StyleTransformData(const StyleTransformData& other)
     , x(other.x)
     , y(other.y)
     , z(other.z)
+    , transformBox(other.transformBox)
 {
 }
 
@@ -50,7 +52,7 @@ Ref<StyleTransformData> StyleTransformData::copy() const
 
 bool StyleTransformData::operator==(const StyleTransformData& other) const
 {
-    return x == other.x && y == other.y && z == other.z && operations == other.operations;
+    return x == other.x && y == other.y && z == other.z && transformBox == other.transformBox && operations == other.operations;
 }
 
 } // namespace WebCore
index 6b46a35..a36c9fa 100644 (file)
@@ -25,6 +25,7 @@
 #pragma once
 
 #include "Length.h"
+#include "RenderStyleConstants.h"
 #include "TransformOperations.h"
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
@@ -48,6 +49,7 @@ public:
     Length x;
     Length y;
     float z;
+    TransformBox transformBox;
 
 private:
     StyleTransformData();
index f5f9293..907b520 100644 (file)
@@ -27,6 +27,7 @@
 #include "SVGNames.h"
 #include "SVGPathData.h"
 #include "SVGRect.h"
+#include "SVGSVGElement.h"
 #include "SVGStringList.h"
 #include <wtf/NeverDestroyed.h>
 
@@ -79,10 +80,26 @@ AffineTransform SVGGraphicsElement::animatedLocalTransform() const
 
     // If CSS property was set, use that, otherwise fallback to attribute (if set).
     if (style && style->hasTransform()) {
+        
+        FloatRect boundingBox;
+        switch (style->transformBox()) {
+        case TransformBox::FillBox:
+            boundingBox = renderer()->objectBoundingBox();
+            break;
+        case TransformBox::BorderBox:
+            // For SVG elements without an associated CSS layout box, the used value for border-box is view-box.
+        case TransformBox::ViewBox:
+            if (auto *viewportElement = nearestViewportElement()) {
+                if (is<SVGSVGElement>(*viewportElement))
+                    boundingBox = downcast<SVGSVGElement>(*viewportElement).viewBox();
+            }
+            break;
+        }
+        
         // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath.
         // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/
         TransformationMatrix transform;
-        style->applyTransform(transform, renderer()->objectBoundingBox());
+        style->applyTransform(transform, boundingBox);
 
         // Flatten any 3D transform.
         matrix = transform.toAffineTransform();
index 0345a65..3c6f3c3 100644 (file)
@@ -1,3 +1,14 @@
+2017-05-22  Simon Fraser  <simon.fraser@apple.com>
+
+        Support transform-box to switch sizing box in SVG
+        https://bugs.webkit.org/show_bug.cgi?id=145783
+
+        Reviewed by Dean Jackson.
+
+        Add transform-box to the list of autocompletions.
+
+        * UserInterface/External/CodeMirror/css.js:
+
 2017-05-22  Brian Burg  <bburg@apple.com>
 
         Web Inspector: RTL: In Timelines > JavaScript & Events, digits for sample count are not localized
index a1d5a38..0e65c21 100644 (file)
@@ -519,7 +519,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "text-emphasis-position", "text-emphasis-style", "text-height",
     "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
     "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
-    "text-wrap", "top", "transform", "transform-origin", "transform-style",
+    "text-wrap", "top", "transform", "transform-box", "transform-origin", "transform-style",
     "transition", "transition-delay", "transition-duration",
     "transition-property", "transition-timing-function", "unicode-bidi",
     "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",