Bug 968923 - part 4 - hook up use counters to WebIDL bindings; r=bz
authorCameron McCormack <cam@mcc.id.au>
Thu, 05 Feb 2015 12:53:01 -0500
changeset 258246 f58b8e4e584353ddcb733f9c163e88cf17947c4a
parent 258245 d0d513d053427bd517e73f500cd5c5733efa4da8
child 258247 185b4c796b1c2432e3f801d2b5a20a95c391608f
push id29249
push userryanvm@gmail.com
push dateWed, 19 Aug 2015 11:17:27 +0000
treeherdermozilla-central@706b23a03d1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs968923
milestone43.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 968923 - part 4 - hook up use counters to WebIDL bindings; r=bz
dom/base/UseCounter.h
dom/base/usecounters.py
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/parser/WebIDL.py
--- a/dom/base/UseCounter.h
+++ b/dom/base/UseCounter.h
@@ -9,17 +9,18 @@
 
 namespace mozilla {
 
 enum UseCounter : int16_t {
   eUseCounter_UNKNOWN = -1,
 #define USE_COUNTER_DOM_METHOD(interface_, name_) \
     eUseCounter_##interface_##_##name_,
 #define USE_COUNTER_DOM_ATTRIBUTE(interface_, name_) \
-    eUseCounter_##interface_##_##name_,
+    eUseCounter_##interface_##_##name_##_getter, \
+    eUseCounter_##interface_##_##name_##_setter,
 #define USE_COUNTER_CSS_PROPERTY(name_, id_) \
     eUseCounter_property_##id_,
 #include "mozilla/dom/UseCounterList.h"
 #undef USE_COUNTER_DOM_METHOD
 #undef USE_COUNTER_DOM_ATTRIBUTE
 #undef USE_COUNTER_CSS_PROPERTY
   eUseCounter_Count
 };
--- a/dom/base/usecounters.py
+++ b/dom/base/usecounters.py
@@ -65,14 +65,16 @@ def generate_histograms(filename):
             append_counter('USE_COUNTER_%s_DOCUMENT' % name, 'Whether a document %s' % desc)
             append_counter('USE_COUNTER_%s_PAGE' % name, 'Whether a page %s' % desc)
 
         if counter['type'] == 'method':
             method = '%s.%s' % (counter['interface_name'], counter['method_name'])
             append_counters(method.replace('.', '_').upper(), 'called %s' % method)
         elif counter['type'] == 'attribute':
             attr = '%s.%s' % (counter['interface_name'], counter['attribute_name'])
-            append_counters(attr.replace('.', '_').upper(), 'got or set %s' % attr)
+            counter_name = attr.replace('.', '_').upper()
+            append_counters('%s_getter' % counter_name, 'got %s' % attr)
+            append_counters('%s_setter' % counter_name, 'set %s' % attr)
         elif counter['type'] == 'property':
             prop = counter['property_name']
             append_counters('PROPERTY_%s' % prop.replace('-', '_').upper(), "used the '%s' property" % prop)
 
     return items
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -9,48 +9,51 @@
 #include <algorithm>
 #include <stdarg.h>
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
+#include "mozilla/UseCounter.h"
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIXPConnect.h"
 #include "nsUTF8Utils.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "prprf.h"
+#include "nsGlobalWindow.h"
 
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMErrorBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "WorkerPrivate.h"
 #include "nsDOMClassInfo.h"
 #include "ipc/ErrorIPCUtils.h"
+#include "mozilla/UseCounter.h"
 
 namespace mozilla {
 namespace dom {
 
 JSErrorFormatString ErrorFormatString[] = {
 #define MSG_DEF(_name, _argc, _exn, _str) \
   { _str, _argc, _exn },
 #include "mozilla/dom/Errors.msg"
@@ -3045,10 +3048,20 @@ AssertReflectorHasGivenProto(JSContext* 
   ok = JS_WrapObject(aCx, &givenProto);
   MOZ_ASSERT(ok);
   MOZ_ASSERT(givenProto == reflectorProto,
              "How are we supposed to change the proto now?");
 }
 } // namespace binding_detail
 #endif // DEBUG
 
+void
+SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
+                             UseCounter aUseCounter)
+{
+  nsGlobalWindow* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
+  if (win && win->GetDocument()) {
+    win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter);
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -37,16 +37,19 @@
 #include "nsIVariant.h"
 
 #include "nsWrapperCacheInlines.h"
 
 class nsIJSID;
 class nsPIDOMWindow;
 
 namespace mozilla {
+
+enum UseCounter : int16_t;
+
 namespace dom {
 template<typename DataType> class MozMap;
 
 nsresult
 UnwrapArgImpl(JS::Handle<JSObject*> src, const nsIID& iid, void** ppArg);
 
 /** Convert a jsval to an XPCOM pointer. */
 template <class Interface>
@@ -3312,12 +3315,16 @@ bool GetSetlikeBackingObject(JSContext* 
                              bool* aBackingObjCreated);
 
 // Get the desired prototype object for an object construction from the given
 // CallArgs.  Null is returned if the default prototype should be used.
 bool
 GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
                 JS::MutableHandle<JSObject*> aDesiredProto);
 
+void
+SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
+                             UseCounter aUseCounter);
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_BindingUtils_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6831,17 +6831,18 @@ class CGPerSignatureCall(CGThing):
     """
     # XXXbz For now each entry in the argument list is either an
     # IDLArgument or a FakeArgument, but longer-term we may want to
     # have ways of flagging things like JSContext* or optional_argc in
     # there.
 
     def __init__(self, returnType, arguments, nativeMethodName, static,
                  descriptor, idlNode, argConversionStartsAt=0, getter=False,
-                 setter=False, isConstructor=False, resultVar=None):
+                 setter=False, isConstructor=False, useCounterName=None,
+                 resultVar=None):
         assert idlNode.isMethod() == (not getter and not setter)
         assert idlNode.isAttr() == (getter or setter)
         # Constructors are always static
         assert not isConstructor or static
 
         CGThing.__init__(self)
         self.returnType = returnType
         self.descriptor = descriptor
@@ -7042,16 +7043,22 @@ class CGPerSignatureCall(CGThing):
                                                               idlNode.maplikeOrSetlike,
                                                               idlNode.identifier.name))
         else:
             cgThings.append(CGCallGenerator(
                 self.getErrorReport() if self.isFallible() else None,
                 self.getArguments(), argsPre, returnType,
                 self.extendedAttributes, descriptor, nativeMethodName,
                 static, argsPost=argsPost, resultVar=resultVar))
+
+        if useCounterName:
+            # Generate a telemetry call for when [UseCounter] is used.
+            code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName
+            cgThings.append(CGGeneric(code))
+
         self.cgRoot = CGList(cgThings)
 
     def getArguments(self):
         return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)]
 
     def isFallible(self):
         return 'infallible' not in self.extendedAttributes
 
@@ -7206,31 +7213,37 @@ class CGMethodCall(CGThing):
 
         if isConstructor:
             assert constructorName is not None
             methodName = constructorName
         else:
             methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name)
         argDesc = "argument %d of " + methodName
 
+        if method.getExtendedAttribute("UseCounter"):
+            useCounterName = methodName.replace(".", "_")
+        else:
+            useCounterName = None
+
         def requiredArgCount(signature):
             arguments = signature[1]
             if len(arguments) == 0:
                 return 0
             requiredArgs = len(arguments)
             while requiredArgs and arguments[requiredArgs-1].optional:
                 requiredArgs -= 1
             return requiredArgs
 
         def getPerSignatureCall(signature, argConversionStartsAt=0):
             return CGPerSignatureCall(signature[0], signature[1],
                                       nativeMethodName, static, descriptor,
                                       method,
                                       argConversionStartsAt=argConversionStartsAt,
-                                      isConstructor=isConstructor)
+                                      isConstructor=isConstructor,
+                                      useCounterName=useCounterName)
 
         def filteredSignatures(signatures, descriptor):
             def typeExposedInWorkers(type):
                 return (not type.isGeckoInterface() or
                         type.inner.isExposedInAnyWorker())
             if descriptor.workers:
                 # Filter out the signatures that should not be exposed in a
                 # worker.  The IDL parser enforces the return value being
@@ -7625,19 +7638,24 @@ class CGMethodCall(CGThing):
 
 
 class CGGetterCall(CGPerSignatureCall):
     """
     A class to generate a native object getter call for a particular IDL
     getter.
     """
     def __init__(self, returnType, nativeMethodName, descriptor, attr):
+        if attr.getExtendedAttribute("UseCounter"):
+            useCounterName = "%s_%s_getter" % (descriptor.interface.identifier.name,
+                                               attr.identifier.name)
+        else:
+            useCounterName = None
         CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
                                     attr.isStatic(), descriptor, attr,
-                                    getter=True)
+                                    getter=True, useCounterName=useCounterName)
 
 
 class FakeIdentifier():
     def __init__(self, name):
         self.name = name
 
 
 class FakeArgument():
@@ -7674,20 +7692,25 @@ class FakeArgument():
 
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
     setter.
     """
     def __init__(self, argType, nativeMethodName, descriptor, attr):
+        if attr.getExtendedAttribute("UseCounter"):
+            useCounterName = "%s_%s_setter" % (descriptor.interface.identifier.name,
+                                               attr.identifier.name)
+        else:
+            useCounterName = None
         CGPerSignatureCall.__init__(self, None,
                                     [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
                                     nativeMethodName, attr.isStatic(),
-                                    descriptor, attr, setter=True)
+                                    descriptor, attr, setter=True, useCounterName=useCounterName)
 
     def wrap_return_value(self):
         attr = self.idlNode
         if self.descriptor.wrapperCache and attr.slotIndex is not None:
             if attr.getExtendedAttribute("StoreInSlot"):
                 args = "cx, self"
             else:
                 args = "self"
@@ -12850,16 +12873,23 @@ class CGBindingRoot(CGThing):
         bindingHeaders["mozilla/dom/BindingDeclarations.h"] = (
             not hasCode and enums)
 
         bindingHeaders["WrapperFactory.h"] = descriptors
         bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
         bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries  # AutoJSAPI
         bindingHeaders["xpcpublic.h"] = dictionaries  # xpc::UnprivilegedJunkScope
 
+        # For things that have [UseCounter]
+        def descriptorRequiresTelemetry(desc):
+            iface = desc.interface
+            return any(m.getExtendedAttribute("UseCounter") for m in iface.members)
+        bindingHeaders["mozilla/UseCounter.h"] = any(
+            descriptorRequiresTelemetry(d) for d in descriptors)
+
         cgthings.extend(traverseMethods)
         cgthings.extend(unlinkMethods)
 
         # Do codegen for all the dictionaries.  We have to be a bit careful
         # here, because we have to generate these in order from least derived
         # to most derived so that class inheritance works out.  We also have to
         # generate members before the dictionary that contains them.
 
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -4021,16 +4021,21 @@ class IDLAttribute(IDLInterfaceMember):
                 raise WebIDLError("[DependsOn] takes an identifier",
                                   [attr.location])
             if (attr.value() != "Everything" and attr.value() != "DOMState" and
                 not self.readonly):
                 raise WebIDLError("[DependsOn=%s] only allowed on "
                                   "readonly attributes" % attr.value(),
                                   [attr.location, self.location])
             self._setDependsOn(attr.value())
+        elif identifier == "UseCounter":
+            if self.stringifier:
+                raise WebIDLError("[UseCounter] must not be used on a "
+                                  "stringifier attribute",
+                                  [attr.location, self.location])
         elif (identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "SetterThrows" or
               identifier == "Throws" or
               identifier == "GetterThrows" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
               identifier == "Frozen" or
@@ -4426,16 +4431,25 @@ class IDLMethod(IDLInterfaceMember, IDLS
 
     def isMaplikeOrSetlikeMethod(self):
         """
         True if this method was generated as part of a
         maplike/setlike/etc interface (e.g. has/get methods)
         """
         return self.maplikeOrSetlike is not None
 
+    def isSpecial(self):
+        return (self.isGetter() or
+                self.isSetter() or
+                self.isCreator() or
+                self.isDeleter() or
+                self.isLegacycaller() or
+                self.isStringifier() or
+                self.isJsonifier())
+
     def hasOverloads(self):
         return self._hasOverloads
 
     def isIdentifierLess(self):
         """
         True if the method name started with __, and if the method is not a
         maplike/setlike method. Interfaces with maplike/setlike will generate
         methods starting with __ for chrome only backing object access in JS
@@ -4707,16 +4721,21 @@ class IDLMethod(IDLInterfaceMember, IDLS
                 raise WebIDLError("[DependsOn] takes an identifier",
                                   [attr.location])
             self._setDependsOn(attr.value())
         elif identifier == "Alias":
             if not attr.hasValue():
                 raise WebIDLError("[Alias] takes an identifier or string",
                                   [attr.location])
             self._addAlias(attr.value())
+        elif identifier == "UseCounter":
+            if self.isSpecial():
+                raise WebIDLError("[UseCounter] must not be used on a special "
+                                  "operation",
+                                  [attr.location, self.location])
         elif (identifier == "Throws" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "AvailableIn" or