Bug 965860 - patch 4 - Console format string, r=khuey
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 27 Feb 2014 23:39:12 +0000
changeset 171460 39be88a5ce66f4e806515f607cc9b13352ad3870
parent 171459 428fa1a58cf365e5844c50bcb68c60f4fa2e0bd8
child 171461 200b26e5e847bd6af20b7e6a98bd03abbc3c68eb
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerskhuey
bugs965860
milestone30.0a1
Bug 965860 - patch 4 - Console format string, r=khuey
dom/base/Console.cpp
dom/base/Console.h
dom/tests/browser/browser_ConsoleAPITests.js
dom/tests/browser/test-console-api.html
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -614,17 +614,54 @@ Console::ProcessArguments(JSContext* aCx
     ++start;
 
     if (*start == '%') {
       output.Append(*start);
       ++start;
       continue;
     }
 
+    nsAutoString tmp;
+    tmp.Append('%');
+
+    int32_t integer = -1;
+    int32_t mantissa = -1;
+
+    // Let's parse %<number>.<number> for %d and %f
+    if (*start >= '0' && *start <= '9') {
+      integer = 0;
+
+      do {
+        integer = integer * 10 + *start - '0';
+        tmp.Append(*start);
+        ++start;
+      } while (*start >= '0' && *start <= '9');
+    }
+
+    if (*start == '.') {
+      tmp.Append(*start);
+      ++start;
+
+      // '.' must be followed by a number.
+      if (*start < '0' || *start > '9') {
+        output.Append(tmp);
+        continue;
+      }
+
+      mantissa = 0;
+
+      do {
+        mantissa = mantissa * 10 + *start - '0';
+        tmp.Append(*start);
+        ++start;
+      } while (*start >= '0' && *start <= '9');
+    }
+
     char ch = *start;
+    tmp.Append(ch);
     ++start;
 
     switch (ch) {
       case 'o':
       {
         if (!output.IsEmpty()) {
           JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx,
                                                              output.get(),
@@ -668,35 +705,39 @@ Console::ProcessArguments(JSContext* aCx
         if (index < aData.Length()) {
           JS::Rooted<JS::Value> value(aCx, aData[index++]);
 
           int32_t v;
           if (!JS::ToInt32(aCx, value, &v)) {
             return;
           }
 
-          output.AppendPrintf("%d", v);
+          nsCString format;
+          MakeFormatString(format, integer, mantissa, 'd');
+          output.AppendPrintf(format.get(), v);
         }
         break;
 
       case 'f':
         if (index < aData.Length()) {
           JS::Rooted<JS::Value> value(aCx, aData[index++]);
 
           double v;
           if (!JS::ToNumber(aCx, value, &v)) {
             return;
           }
 
-          output.AppendPrintf("%f", v);
+          nsCString format;
+          MakeFormatString(format, integer, mantissa, 'f');
+          output.AppendPrintf(format.get(), v);
         }
         break;
 
       default:
-        output.Append(ch);
+        output.Append(tmp);
         break;
     }
   }
 
   if (!output.IsEmpty()) {
     JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx, output.get(),
                                                        output.Length()));
     if (!str) {
@@ -708,16 +749,33 @@ Console::ProcessArguments(JSContext* aCx
 
   // The rest of the array, if unused by the format string.
   for (; index < aData.Length(); ++index) {
     aSequence.AppendElement(aData[index]);
   }
 }
 
 void
+Console::MakeFormatString(nsCString& aFormat, int32_t aInteger,
+                          int32_t aMantissa, char aCh)
+{
+  aFormat.Append("%");
+  if (aInteger >= 0) {
+    aFormat.AppendInt(aInteger);
+  }
+
+  if (aMantissa >= 0) {
+    aFormat.Append(".");
+    aFormat.AppendInt(aMantissa);
+  }
+
+  aFormat.Append(aCh);
+}
+
+void
 Console::ComposeGroupName(JSContext* aCx,
                           const nsTArray<JS::Heap<JS::Value>>& aData,
                           nsAString& aName)
 {
   for (uint32_t i = 0; i < aData.Length(); ++i) {
     if (i != 0) {
       aName.AppendASCII(" ");
     }
--- a/dom/base/Console.h
+++ b/dom/base/Console.h
@@ -132,16 +132,20 @@ private:
   // Example if the input is:
   //   "string: %s, integer: %d, object: %o, double: %d", 's', 1, window, 0.9
   // The output will be:
   //   [ "string: s, integer: 1, object: ", window, ", double: 0.9" ]
   void
   ProcessArguments(JSContext* aCx, const nsTArray<JS::Heap<JS::Value>>& aData,
                    Sequence<JS::Value>& aSequence);
 
+  void
+  MakeFormatString(nsCString& aFormat, int32_t aInteger, int32_t aMantissa,
+                   char aCh);
+
   // Stringify and Concat all the JS::Value in a single string using ' ' as
   // separator.
   void
   ComposeGroupName(JSContext* aCx, const nsTArray<JS::Heap<JS::Value>>& aData,
                    nsAString& aName);
 
   JS::Value
   StartTimer(JSContext* aCx, const JS::Value& aName,
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -161,20 +161,20 @@ function testConsoleGroup(aMessageObject
   if (aMessageObject.level == "groupEnd") {
     startTimeTest();
   }
 }
 
 function startTraceTest() {
   gLevel = "trace";
   gArgs = [
-    {filename: TEST_URI, lineNumber: 6, functionName: "window.foobar585956c", language: 2},
-    {filename: TEST_URI, lineNumber: 11, functionName: "foobar585956b", language: 2},
-    {filename: TEST_URI, lineNumber: 15, functionName: "foobar585956a", language: 2},
-    {filename: TEST_URI, lineNumber: 1, functionName: "onclick", language: 2}
+    {filename: TEST_URI, functionName: "window.foobar585956c", language: 2, lineNumber: 6},
+    {filename: TEST_URI, functionName: "foobar585956b", language: 2, lineNumber: 11},
+    {filename: TEST_URI, functionName: "foobar585956a", language: 2, lineNumber: 15},
+    {filename: TEST_URI, functionName: "onclick", language: 2, lineNumber: 1}
   ];
 
   let button = gWindow.document.getElementById("test-trace");
   ok(button, "found #test-trace button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
 
 function startLocationTest() {
@@ -185,17 +185,17 @@ function startLocationTest() {
     } catch (ex) {
       // XXX Bug 906593 - Exceptions in this function currently aren't
       // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   gLevel = "log";
   gArgs = [
-    {filename: TEST_URI, lineNumber: 19, functionName: "foobar646025", arguments: ["omg", "o", "d"]}
+    {filename: TEST_URI, functionName: "foobar646025", arguments: ["omg", "o", "d"], lineNumber: 19}
   ];
 
   let button = gWindow.document.getElementById("test-location");
   ok(button, "found #test-location button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
 
 function expect(level) {
@@ -208,25 +208,44 @@ function observeConsoleTest() {
   expect("log", "arg");
   win.console.log("arg");
   yield undefined;
 
   expect("info", "arg", "extra arg");
   win.console.info("arg", "extra arg");
   yield undefined;
 
-  // We don't currently support width and precision qualifiers, but we don't
-  // choke on them either.
-  expect("warn", "Lesson 1: PI is approximately equal to 3.14159");
+  expect("warn", "Lesson 1: PI is approximately equal to 3");
+  win.console.warn("Lesson %d: %s is approximately equal to %1.0f",
+                   1,
+                   "PI",
+                   3.14159);
+  yield undefined;
+
+  expect("warn", "Lesson 1: PI is approximately equal to 3.14");
   win.console.warn("Lesson %d: %s is approximately equal to %1.2f",
                    1,
                    "PI",
                    3.14159);
   yield undefined;
 
+  expect("warn", "Lesson 1: PI is approximately equal to 3.141590");
+  win.console.warn("Lesson %d: %s is approximately equal to %f",
+                   1,
+                   "PI",
+                   3.14159);
+  yield undefined;
+
+  expect("warn", "Lesson 1: PI is approximately equal to 3.1415900");
+  win.console.warn("Lesson %d: %s is approximately equal to %0.7f",
+                   1,
+                   "PI",
+                   3.14159);
+  yield undefined;
+
   expect("log", "%d, %s, %l");
   win.console.log("%d, %s, %l");
   yield undefined;
 
   expect("log", "%a %b %c");
   win.console.log("%a %b %c");
   yield undefined;
 
--- a/dom/tests/browser/test-console-api.html
+++ b/dom/tests/browser/test-console-api.html
@@ -46,17 +46,17 @@
 
       function testGroups() {
         console.groupCollapsed("a", "group");
         console.group("b", "group");
         console.groupEnd("b", "group");
       }
 
       function nativeCallback() {
-        new Promise(function(resolve, reject) { resolve(42); }).then(console.log);
+        new Promise(function(resolve, reject) { resolve(42); }).then(console.log.bind(console));
       }
     </script>
   </head>
   <body>
     <h1>Console API Test Page</h1>
     <button onclick="test();">Log stuff</button>
     <button id="test-trace" onclick="foobar585956a('omg');">Test trace</button>
     <button id="test-location" onclick="foobar646025('omg');">Test location</button>