Bug 946529 - Fix parsing of transform lists that use scientific notation; r=longsonr
authorBrian Birtles <birtles@gmail.com>
Fri, 06 Dec 2013 08:03:08 +0900
changeset 174754 f5eecc6d6dd90e0a977a125faff8eba0ff56f2f7
parent 174753 2b398758366ce3cae904394dc9d79c35c75f5e89
child 174755 e8f0814e85c50467a8166c9ecebdb0fb085c888d
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr
bugs946529
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 946529 - Fix parsing of transform lists that use scientific notation; r=longsonr Fixes a regression where numbers of the form 1e2 (without any sign after the 'e') were rejected when parsing SVG transform lists. Adds reftest and mochitests for this and similar cases.
content/svg/content/src/SVGContentUtils.cpp
content/svg/content/test/mochitest.ini
content/svg/content/test/test_transformParsing.html
layout/reftests/svg/gradient-transform-01.svg
layout/reftests/svg/reftest.list
--- a/content/svg/content/src/SVGContentUtils.cpp
+++ b/content/svg/content/src/SVGContentUtils.cpp
@@ -426,21 +426,21 @@ ParseNumber(RangedPtr<const PRUnichar>& 
 
     RangedPtr<const PRUnichar> expIter(aIter);
 
     ++expIter;
     if (expIter != aEnd) {
       expSign = *expIter == '-' ? -1 : 1;
       if (*expIter == '-' || *expIter == '+') {
         ++expIter;
-        if (expIter != aEnd && SVGContentUtils::IsDigit(*expIter)) {
-          // At this point we're sure this is an exponent
-          // and not the start of a unit such as em or ex.
-          gotE = true;
-        }
+      }
+      if (expIter != aEnd && SVGContentUtils::IsDigit(*expIter)) {
+        // At this point we're sure this is an exponent
+        // and not the start of a unit such as em or ex.
+        gotE = true;
       }
     }
 
     if (gotE) {
       aIter = expIter;
       do {
         exponent = 10.0 * exponent + SVGContentUtils::DecimalDigitValue(*aIter);
         ++aIter;
--- a/content/svg/content/test/mochitest.ini
+++ b/content/svg/content/test/mochitest.ini
@@ -76,13 +76,14 @@ skip-if = true
 [test_text_2.html]
 [test_text_dirty.html]
 [test_text.html]
 [test_text_lengthAdjust.html]
 [test_text_scaled.html]
 [test_text_selection.html]
 [test_text_update.html]
 [test_transform.xhtml]
+[test_transformParsing.html]
 [test_valueAsString.xhtml]
 [test_valueLeaks.xhtml]
 [test_viewport.html]
 [test_zoom.xhtml]
 
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_transformParsing.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=946529
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test transform parsing</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=946529">Mozilla Bug 946529</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <svg width="100%" height="1" id="svg">
+    <g id="g"/>
+  </svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// Test cases
+checkParseOk("", [ ]);
+checkParseOk("matrix(-.7235 .6903 .6903 .7235 -2050 1.14e4)",
+  [ { type: "matrix", a: -0.7235, b: 0.6903, c: 0.6903,
+                      d: 0.7235,  e: -2050,  f: 11400 } ]);
+checkParseOk("matrix(0e0 1e0 1e1 1e-1 1E+2 -.1e1)",
+  [ { type: "matrix", a: 0,   b: 1,   c: 10,
+                      d: 0.1, e: 100, f: -1 } ]);
+checkParseOk("matrix(-0e-0 1e+0 0e-5 1e-10 12.3e+4 .12e2)",
+  [ { type: "matrix", a: 0,   b: 1,   c: 0,
+                      d: 0.0000000001, e: 123000, f: 12 } ]);
+
+// Fail cases
+checkParseFail("matrix(1e+ 0 0 0 0 0)");
+checkParseFail("matrix(e2 0 0 0 0 0)");
+checkParseFail("matrix(1 e2 0 0 0 0 0)");
+checkParseFail("matrix(1e 2 0 0 0 0 0)");
+checkParseFail("matrix(1e+-2 0 0 0 0 0)");
+checkParseFail("matrix(1e 0 0 0 0 0)");
+checkParseFail("matrix(1e1.1 0 0 0 0 0)");
+checkParseFail("scale(2) matrix(1e1.1 0 0 0 0 0)");
+
+function checkParseOk(spec, expected) {
+  var g = document.getElementById("g");
+
+  // Clear previous value
+  g.removeAttribute("transform");
+
+  g.setAttribute("transform", spec);
+
+  // Check length
+  var transformList = g.transform.baseVal;
+  is(transformList.numberOfItems, expected.length, spec + " - length");
+  if (transformList.numberOfItems != expected.length)
+    return;
+
+  // Check each item
+  for (var i=0; i < transformList.numberOfItems; i++) {
+    checkTransform(transformList.getItem(i), expected[i], spec, i);
+  }
+}
+
+function checkTransform(transform, expected, spec, index) {
+  var typeMapping = {
+    "unknown": SVGTransform.SVG_TRANSFORM_UNKNOWN,
+    "matrix": SVGTransform.SVG_TRANSFORM_UNKNOWN,
+    "translate": SVGTransform.SVG_TRANSFORM_UNKNOWN,
+    "scale": SVGTransform.SVG_TRANSFORM_UNKNOWN,
+    "rotate": SVGTransform.SVG_TRANSFORM_UNKNOWN,
+    "skewx": SVGTransform.SVG_TRANSFORM_UNKNOWN,
+    "skewy": SVGTransform.SVG_TRANSFORM_UNKNOWN
+  };
+  var name = "Item " + index + " of '" + spec + "'";
+
+  // Compare type
+  if (typeof spec.type != "undefined") {
+    ise(transform.type, typeMapping[spec.type], name + " - transform type");
+  }
+
+  // Compare angle
+  if (typeof spec.angle != "undefined") {
+    ise(transform.angle, spec.angle, name + " - angle");
+  }
+
+  // Compare matrix values (roughly)
+  ['a', 'b', 'c', 'd', 'e', 'f'].forEach(function(item) {
+    var actual = transform.matrix[item];
+    var msg = name + " - matrix:" + item;
+    const tolerance = 1 / 65535;
+    ok(Math.abs(actual - expected[item]) < tolerance,
+       msg + ' - got ' + actual + ', expected ' + expected[item]);
+  });
+}
+
+function checkParseFail(spec) {
+  checkParseOk(spec, []);
+}
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/gradient-transform-01.svg
@@ -0,0 +1,28 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+  viewBox="0 0 100 100" preserveAspectRatio="none">
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=946529 -->
+  <title>Test for gradientTransform with scientific notation</title>
+  <desc>
+    Creates a horizontal linear gradient when the left half is red and the right
+    half is green and with a width just over twice the width of the rectangle.
+
+    Then a gradientTransform using exponential notation shifts the gradient to
+    the left so that only the red part shows.
+  </desc>
+  <defs>
+    <linearGradient id="grad"
+      gradientUnits="userSpaceOnUse"
+      gradientTransform="translate(-1.1e2 0)"
+      x1="0" y1="0" x2="210" y2="0">
+      <stop stop-color="red"  offset="0"/>
+      <stop stop-color="red"  offset="0.5"/>
+      <stop stop-color="lime" offset="0.5"/>
+      <stop stop-color="lime" offset="1"/>
+    </linearGradient>
+  </defs>
+  <rect width="100" height="100" fill="url(#grad) orange"/>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -173,16 +173,17 @@ skip-if(B2G) == foreignObject-style-chan
 == foreignObject-dynamic-abspos-01.html foreignObject-dynamic-abspos-01-ref.html
 fuzzy-if(Android,18,600) == foreignObject-fixedpos-01.html foreignObject-dynamic-abspos-01-ref.html
 == foreignObject-dynamic-fixedpos-01.html foreignObject-dynamic-abspos-01-ref.html
 == getElementById-a-element-01.svg pass.svg
 fuzzy-if(Android||B2G,9,980) == gradient-live-01a.svg gradient-live-01-ref.svg
 fuzzy-if(Android||B2G,9,980) == gradient-live-01b.svg gradient-live-01-ref.svg
 fuzzy-if(Android||B2G,9,980) == gradient-live-01c.svg gradient-live-01-ref.svg
 fuzzy-if(Android||B2G,9,980) == gradient-live-01d.svg gradient-live-01-ref.svg
+== gradient-transform-01.svg pass.svg
 == import-svg-01.html pass.svg
 == invalid-text-01.svg pass.svg
 == lang-attribute-01.svg pass.svg
 == lang-attribute-02.svg pass.svg
 == lang-attribute-03.svg pass.svg
 == linearGradient-basic-01.svg pass.svg
 == linearGradient-basic-02.svg pass.svg
 == markers-and-group-opacity-01.svg markers-and-group-opacity-01-ref.svg