Bug 965860 - patch 6 - Console.count(), r=khuey
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 27 Feb 2014 23:39:24 +0000
changeset 171462 4d27a920dbf9a44dd3e011bb129a8707c1eceaf2
parent 171461 200b26e5e847bd6af20b7e6a98bd03abbc3c68eb
child 171463 ec85e0c7c0608971110eb31375dd3e1e094accbf
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerskhuey
bugs965860
milestone30.0a1
Bug 965860 - patch 6 - Console.count(), r=khuey
dom/base/Console.cpp
dom/base/Console.h
dom/webidl/Console.webidl
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -20,16 +20,19 @@
 #include "nsILoadContext.h"
 #include "nsIServiceManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIWebNavigation.h"
 
 // The maximum allowed number of concurrent timers per page.
 #define MAX_PAGE_TIMERS 10000
 
+// The maximum allowed number of concurrent counters per page.
+#define MAX_PAGE_COUNTERS 10000
+
 // The maximum stacktrace depth when populating the stacktrace array used for
 // console.trace().
 #define DEFAULT_MAX_STACKTRACE_DEPTH 200
 
 // The console API methods are async and their action is executed later. This
 // delay tells how much later.
 #define CALL_DELAY 15 // milliseconds
 
@@ -327,16 +330,18 @@ void
 Console::Assert(JSContext* aCx, bool aCondition,
                 const Sequence<JS::Value>& aData)
 {
   if (!aCondition) {
     Method(aCx, MethodAssert, NS_LITERAL_STRING("assert"), aData);
   }
 }
 
+METHOD(Count, "count")
+
 void
 Console::__noSuchMethod__()
 {
   // Nothing to do.
 }
 
 // Queue a call to a console method. See the CALL_DELAY constant.
 void
@@ -561,16 +566,20 @@ Console::ProcessCallData(ConsoleCallData
   else if (aData.mMethodName == MethodTime && !aData.mArguments.IsEmpty()) {
     event.mTimer = StartTimer(cx, aData.mArguments[0], aData.mMonotonicTimer);
   }
 
   else if (aData.mMethodName == MethodTimeEnd && !aData.mArguments.IsEmpty()) {
     event.mTimer = StopTimer(cx, aData.mArguments[0], aData.mMonotonicTimer);
   }
 
+  else if (aData.mMethodName == MethodCount) {
+    event.mCounter = IncreaseCounter(cx, frame, aData.mArguments);
+  }
+
   JS::Rooted<JS::Value> eventValue(cx);
   if (!event.ToObject(cx, JS::NullPtr(), &eventValue)) {
     Throw(cx, NS_ERROR_FAILURE);
     return;
   }
 
   JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
   MOZ_ASSERT(eventObj);
@@ -924,10 +933,65 @@ void
 Console::ArgumentsToValueList(const nsTArray<JS::Heap<JS::Value>>& aData,
                               Sequence<JS::Value>& aSequence)
 {
   for (uint32_t i = 0; i < aData.Length(); ++i) {
     aSequence.AppendElement(aData[i]);
   }
 }
 
+JS::Value
+Console::IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
+                          const nsTArray<JS::Heap<JS::Value>>& aArguments)
+{
+  ClearException ce(aCx);
+
+  nsAutoString key;
+  nsAutoString label;
+
+  if (!aArguments.IsEmpty()) {
+    JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
+    JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
+
+    nsDependentJSString string;
+    if (jsString && string.init(aCx, jsString)) {
+      label = string;
+      key = string;
+    }
+  }
+
+  if (key.IsEmpty()) {
+    key.Append(aFrame.mFilename);
+    key.Append(NS_LITERAL_STRING(":"));
+    key.AppendInt(aFrame.mLineNumber);
+  }
+
+  uint32_t count = 0;
+  if (!mCounterRegistry.Get(key, &count)) {
+    if (mCounterRegistry.Count() >= MAX_PAGE_COUNTERS) {
+      RootedDictionary<ConsoleCounterError> error(aCx);
+
+      JS::Rooted<JS::Value> value(aCx);
+      if (!error.ToObject(aCx, JS::NullPtr(), &value)) {
+        return JS::UndefinedValue();
+      }
+
+      return value;
+    }
+  }
+
+  ++count;
+  mCounterRegistry.Put(key, count);
+
+  RootedDictionary<ConsoleCounter> data(aCx);
+  data.mLabel = label;
+  data.mCount = count;
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!data.ToObject(aCx, JS::NullPtr(), &value)) {
+    return JS::UndefinedValue();
+  }
+
+  return value;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Console.h
+++ b/dom/base/Console.h
@@ -17,16 +17,17 @@
 #include "nsWrapperCache.h"
 
 class nsIConsoleAPIStorage;
 
 namespace mozilla {
 namespace dom {
 
 class ConsoleCallData;
+class ConsoleStackEntry;
 
 class Console MOZ_FINAL : public nsITimerCallback
                         , public nsIObserver
                         , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console,
@@ -92,16 +93,19 @@ public:
   void
   ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData,
              ErrorResult& aRv);
 
   void
   Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
 
   void
+  Count(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
   __noSuchMethod__();
 
 private:
   enum MethodName
   {
     MethodLog,
     MethodInfo,
     MethodWarn,
@@ -110,17 +114,18 @@ private:
     MethodDebug,
     MethodTrace,
     MethodDir,
     MethodGroup,
     MethodGroupCollapsed,
     MethodGroupEnd,
     MethodTime,
     MethodTimeEnd,
-    MethodAssert
+    MethodAssert,
+    MethodCount
   };
 
   void
   Method(JSContext* aCx, MethodName aName, const nsAString& aString,
          const Sequence<JS::Value>& aData);
 
   void
   ProcessCallData(ConsoleCallData& aData);
@@ -164,22 +169,27 @@ private:
   ArgumentsToValueList(const nsTArray<JS::Heap<JS::Value>>& aData,
                        Sequence<JS::Value>& aSequence);
 
   void
   ProfileMethod(JSContext* aCx, const nsAString& aAction,
                 const Sequence<JS::Value>& aData,
                 ErrorResult& aRv);
 
+  JS::Value
+  IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
+                   const nsTArray<JS::Heap<JS::Value>>& aArguments);
+
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsITimer> mTimer;
   nsCOMPtr<nsIConsoleAPIStorage> mStorage;
 
   nsTArray<ConsoleCallData> mQueuedCalls;
   nsDataHashtable<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry;
+  nsDataHashtable<nsStringHashKey, uint32_t> mCounterRegistry;
 
   uint64_t mOuterID;
   uint64_t mInnerID;
 
   friend class ConsoleCallData;
 };
 
 } // dom namespace
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -22,16 +22,18 @@ interface Console {
 
   [Throws]
   void profile(any... data);
 
   [Throws]
   void profileEnd(any... data);
 
   void assert(boolean condition, any... data);
+  void count(any... data);
+
   void ___noSuchMethod__();
 };
 
 // This is used to propagate console events to the observers.
 dictionary ConsoleEvent {
   (unsigned long or DOMString) ID;
   (unsigned long or DOMString) innerID;
   DOMString level = "";
@@ -39,16 +41,17 @@ dictionary ConsoleEvent {
   unsigned long lineNumber = 0;
   DOMString functionName = "";
   double timeStamp = 0;
   sequence<any> arguments;
   boolean private = false;
   sequence<ConsoleStackEntry> stacktrace;
   DOMString groupName = "";
   any timer = null;
+  any counter = null;
 };
 
 // Event for profile operations
 dictionary ConsoleProfileEvent {
   DOMString action = "";
   sequence<any> arguments;
 };
 
@@ -68,8 +71,17 @@ dictionary ConsoleTimerStart {
 dictionary ConsoleTimerEnd {
   DOMString name = "";
   double duration = 0;
 };
 
 dictionary ConsoleTimerError {
   DOMString error = "maxTimersExceeded";
 };
+
+dictionary ConsoleCounter {
+  DOMString label = "";
+  unsigned long count = 0;
+};
+
+dictionary ConsoleCounterError {
+  DOMString error = "maxCountersExceeded";
+};