Bug 1009645 - Implement [FeatureDetectible] extended attribute and hook it up to navigator.getFeature. r=bz
authorReuben Morais <reuben.morais@gmail.com>
Mon, 14 Jul 2014 20:08:45 -0300
changeset 215820 3f6a695b80a442b17243a1d352180950e4dff86a
parent 215819 2b340d6bf8ff3aac8fe386e589eaa273f80f2fcc
child 215821 f9bb67be4abd5156551587e4ea5e28f89528e8a7
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1009645
milestone33.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 1009645 - Implement [FeatureDetectible] extended attribute and hook it up to navigator.getFeature. r=bz
dom/base/Navigator.cpp
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/mozwebidlcodegen/__init__.py
dom/bindings/parser/WebIDL.py
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -101,16 +101,18 @@
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 #if defined(XP_LINUX)
 #include "mozilla/Hal.h"
 #endif
 #include "mozilla/dom/ContentChild.h"
 
+#include "mozilla/dom/FeatureList.h"
+
 namespace mozilla {
 namespace dom {
 
 static bool sDoNotTrackEnabled = false;
 static uint32_t sDoNotTrackValue = 1;
 static bool sVibratorEnabled   = false;
 static uint32_t sMaxVibrateMS  = 0;
 static uint32_t sMaxVibrateListLen = 0;
@@ -1529,16 +1531,28 @@ Navigator::GetFeature(const nsAString& a
   nsAutoCString feature = NS_ConvertUTF16toUTF8(aName);
   for (uint32_t i = 0; i < MOZ_ARRAY_LENGTH(manifestFeatures); i++) {
     if (feature.Equals(manifestFeatures[i])) {
       p->MaybeResolve(true);
       return p.forget();
     }
   }
 
+  NS_NAMED_LITERAL_STRING(apiWindowPrefix, "api.window.");
+  if (StringBeginsWith(aName, apiWindowPrefix)) {
+    const nsAString& featureName = Substring(aName, apiWindowPrefix.Length(), aName.Length()-apiWindowPrefix.Length());
+    printf_stderr("apiWindowPrefix.Length(): %d, aName.Length(): %d\n", apiWindowPrefix.Length(), aName.Length());
+    if (IsFeatureDetectible(featureName)) {
+      p->MaybeResolve(true);
+    } else {
+      p->MaybeResolve(JS::UndefinedHandleValue);
+    }
+    return p.forget();
+  }
+
   // resolve with <undefined> because the feature name is not supported
   p->MaybeResolve(JS::UndefinedHandleValue);
 
   return p.forget();
 }
 
 
 PowerManager*
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -13662,16 +13662,46 @@ class GlobalGenRoots():
         curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr)
 
         # Add include guards.
         curr = CGIncludeGuard('UnionConversions', curr)
 
         # Done.
         return curr
 
+    @staticmethod
+    def FeatureList(config):
+        things = set()
+        for d in config.getDescriptors():
+            if not d.interface.isExternal() and d.featureDetectibleThings is not None:
+                things.update(d.featureDetectibleThings)
+        things = CGList((CGGeneric(declare='"%s",' % t) for t in sorted(things)), joiner="\n")
+        things.append(CGGeneric(declare="nullptr"))
+        things = CGWrapper(CGIndenter(things), pre="static const char* const FeatureList[] = {\n",
+                                               post="\n};\n")
+
+        helper = CGWrapper(CGIndenter(things), pre="bool IsFeatureDetectible(const nsAString& aFeature) {\n",
+                                               post=dedent("""
+              const char* const* feature = FeatureList;
+              while (*feature) {
+                if (aFeature.EqualsASCII(*feature)) {
+                  return true;
+                }
+                ++feature;
+              }
+
+              return false;
+            }
+        """))
+
+        curr = CGNamespace.build(['mozilla', 'dom'], helper)
+        curr = CGHeaders([], [], [], [], ["nsString.h"], [], 'FeatureList', curr)
+        curr = CGIncludeGuard('FeatureList', curr)
+
+        return curr
 
 # Code generator for simple events
 class CGEventGetter(CGNativeMember):
     def __init__(self, descriptor, attr):
         ea = descriptor.getExtendedAttributes(attr, getter=True)
         if not attr.type.isSequence():
             ea.append('resultNotAddRefed')
         CGNativeMember.__init__(self, descriptor, attr,
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -440,16 +440,28 @@ class Descriptor(DescriptorProvider):
 
             self.checkPermissionsIndex = addPermissions(self.interface)
             self.checkPermissionsIndicesForMembers = dict()
             for m in self.interface.members:
                 permissionsIndex = addPermissions(m)
                 if permissionsIndex is not None:
                     self.checkPermissionsIndicesForMembers[m.identifier.name] = permissionsIndex
 
+            self.featureDetectibleThings = set()
+            if self.interface.getExtendedAttribute("FeatureDetectible") is not None:
+                if self.interface.getNavigatorProperty():
+                    self.featureDetectibleThings.add("Navigator.%s" % self.interface.getNavigatorProperty())
+                else:
+                    assert(self.interface.ctor() is not None)
+                    self.featureDetectibleThings.add(self.interface.identifier.name)
+
+            for m in self.interface.members:
+                if m.getExtendedAttribute("FeatureDetectible") is not None:
+                    self.featureDetectibleThings.add("%s.%s" % (self.interface.identifier.name, m.identifier.name))
+
         # Build the prototype chain.
         self.prototypeChain = []
         parent = interface
         while parent:
             self.prototypeChain.insert(0, parent.identifier.name)
             parent = parent.parent
         config.maxProtoChainLength = max(config.maxProtoChainLength,
                                          len(self.prototypeChain))
--- a/dom/bindings/mozwebidlcodegen/__init__.py
+++ b/dom/bindings/mozwebidlcodegen/__init__.py
@@ -122,16 +122,17 @@ class WebIDLCodegenManager(LoggingMixin)
     """Manages all code generation around WebIDL.
 
     To facilitate testing, this object is meant to be generic and reusable.
     Paths, etc should be parameters and not hardcoded.
     """
 
     # Global parser derived declaration files.
     GLOBAL_DECLARE_FILES = {
+        'FeatureList.h',
         'GeneratedAtomList.h',
         'PrototypeList.h',
         'RegisterBindings.h',
         'UnionConversions.h',
         'UnionTypes.h',
     }
 
     # Global parser derived definition files.
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -563,16 +563,26 @@ class IDLInterface(IDLObjectWithScope):
         if parent and isinstance(parent, IDLExternalInterface):
             raise WebIDLError("%s inherits from %s which does not have "
                               "a definition" %
                               (self.identifier.name,
                                self.parent.identifier.name),
                               [self.location])
         assert not parent or isinstance(parent, IDLInterface)
 
+        if self.getExtendedAttribute("FeatureDetectible"):
+            if self.getExtendedAttribute("NoInterfaceObject"):
+                raise WebIDLError("[FeatureDetectible] not allowed on interface "
+                                  " with [NoInterfaceObject]",
+                                  [self.location])
+            if self.getExtendedAttribute("Pref"):
+                raise WebIDLError("[FeatureDetectible] must not be specified "
+                                  "in combination with [Pref]",
+                                  [self.location])
+
         self.parent = parent
 
         assert iter(self.members)
 
         if self.parent:
             self.parent.finish(scope)
 
             self.parent._hasChildInterfaces = True
@@ -1036,17 +1046,18 @@ class IDLInterface(IDLObjectWithScope):
                 if not attr.noArguments():
                     raise WebIDLError("[Global] must take no arguments",
                                       [attr.location])
                 self._isOnGlobalProtoChain = True
             elif (identifier == "NeedNewResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
                   identifier == "Unforgeable" or
-                  identifier == "LegacyEventInit"):
+                  identifier == "LegacyEventInit" or
+                  identifier == "FeatureDetectible"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
             elif (identifier == "Pref" or
                   identifier == "JSImplementation" or
                   identifier == "HeaderFile" or
                   identifier == "NavigatorProperty" or
@@ -2885,16 +2896,28 @@ class IDLAttribute(IDLInterfaceMember):
         if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
             raise WebIDLError("An attribute with [PutForwards] must have an "
                               "interface type as its type", [self.location])
 
         if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
             raise WebIDLError("An attribute with [SameObject] must have an "
                               "interface type as its type", [self.location])
 
+        if self.getExtendedAttribute("FeatureDetectible"):
+            if not (self.getExtendedAttribute("Func") or
+                    self.getExtendedAttribute("AvailableIn") or
+                    self.getExtendedAttribute("CheckPermissions")):
+                raise WebIDLError("[%s] is only allowed in combination with [Func], "
+                                  "[AvailableIn] or [CheckPermissions]" % identifier,
+                                  [attr.location, self.location])
+            if self.getExtendedAttribute("Pref"):
+                raise WebIDLError("[FeatureDetectible] must not be specified "
+                                  "in combination with [Pref]",
+                                  [self.location])
+
     def validate(self):
         if ((self.getExtendedAttribute("Cached") or
              self.getExtendedAttribute("StoreInSlot")) and
             not self.getExtendedAttribute("Constant") and
             not self.getExtendedAttribute("Pure")):
             raise WebIDLError("Cached attributes and attributes stored in "
                               "slots must be constant or pure, since the "
                               "getter won't always be called.",
@@ -3003,16 +3026,23 @@ class IDLAttribute(IDLInterfaceMember):
             if self.isStatic():
                 raise WebIDLError("[%s] is only allowed on non-static "
                                   "attributes" % identifier,
                                   [attr.location, self.location])
             if self.getExtendedAttribute("LenientThis"):
                 raise WebIDLError("[LenientThis] is not allowed in combination "
                                   "with [%s]" % identifier,
                                   [attr.location, self.location])
+        elif identifier == "FeatureDetectible":
+            if not (self.getExtendedAttribute("Func") or
+                    self.getExtendedAttribute("AvailableIn") or
+                    self.getExtendedAttribute("CheckPermissions")):
+                raise WebIDLError("[%s] is only allowed in combination with [Func], "
+                                  "[AvailableIn] or [CheckPermissions]" % identifier,
+                                  [attr.location, self.location])
         elif (identifier == "Pref" or
               identifier == "SetterThrows" or
               identifier == "Pure" or
               identifier == "Throws" or
               identifier == "GetterThrows" or
               identifier == "ChromeOnly" or
               identifier == "SameObject" or
               identifier == "Constant" or
@@ -3418,16 +3448,28 @@ class IDLMethod(IDLInterfaceMember, IDLS
 
         return self
 
     def signatures(self):
         return [(overload.returnType, overload.arguments) for overload in
                 self._overloads]
 
     def finish(self, scope):
+        if self.getExtendedAttribute("FeatureDetectible"):
+            if not (self.getExtendedAttribute("Func") or
+                    self.getExtendedAttribute("AvailableIn") or
+                    self.getExtendedAttribute("CheckPermissions")):
+                raise WebIDLError("[FeatureDetectible] is only allowed in combination "
+                                  "with [Func], [AvailableIn] or [CheckPermissions]",
+                                  [self.location])
+            if self.getExtendedAttribute("Pref"):
+                raise WebIDLError("[FeatureDetectible] must not be specified "
+                                      "in combination with [Pref]",
+                                      [self.location])
+
         overloadWithPromiseReturnType = None
         overloadWithoutPromiseReturnType = None
         for overload in self._overloads:
             variadicArgument = None
 
             arguments = overload.arguments
             for (idx, argument) in enumerate(arguments):
                 if not argument.isComplete():
@@ -3588,17 +3630,18 @@ class IDLMethod(IDLInterfaceMember, IDLS
                 raise WebIDLError("[LenientFloat] used on a non-void method",
                                   [attr.location, self.location])
             if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
                 raise WebIDLError("[LenientFloat] used on an operation with no "
                                   "restricted float type arguments",
                                   [attr.location, self.location])
         elif (identifier == "Pure" or
               identifier == "CrossOriginCallable" or
-              identifier == "WebGLHandlesContextLoss"):
+              identifier == "WebGLHandlesContextLoss" or
+              identifier == "FeatureDetectible"):
             # Known no-argument attributes.
             if not attr.noArguments():
                 raise WebIDLError("[%s] must take no arguments" % identifier,
                                   [attr.location])
         elif (identifier == "Throws" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "Pref" or