Bug 1375829 part 5. Switch from using "jsonifier" syntax to the spec's "[Default] toJSON" syntax. r=qdot
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 17 May 2018 23:43:59 -0400
changeset 418799 1f3dbe030f57
parent 418798 23c967ee8611
child 418800 973e3a16f6dc
push id34013
push userdluca@mozilla.com
push date2018-05-18 09:56 +0000
treeherdermozilla-central@11ee70f24ea5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs1375829
milestone62.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 1375829 part 5. Switch from using "jsonifier" syntax to the spec's "[Default] toJSON" syntax. r=qdot
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_cereactions.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestExampleGen.webidl
dom/bindings/test/TestJSImplGen.webidl
dom/webidl/MediaDeviceInfo.webidl
dom/webidl/PaymentAddress.webidl
dom/webidl/PaymentResponse.webidl
dom/webidl/Performance.webidl
dom/webidl/PerformanceEntry.webidl
dom/webidl/PerformanceNavigation.webidl
dom/webidl/PerformanceNavigationTiming.webidl
dom/webidl/PerformanceResourceTiming.webidl
dom/webidl/PerformanceServerTiming.webidl
dom/webidl/PerformanceTiming.webidl
dom/webidl/RTCIceCandidate.webidl
dom/webidl/RTCSessionDescription.webidl
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1070,25 +1070,25 @@ class CGHeaders(CGWrapper):
                 if iface.hasInterfaceObject():
                     parent = iface.parent
                     while parent and not parent.hasInterfaceObject():
                         parent = parent.parent
                     if parent:
                         ancestors.append(parent)
         interfaceDeps.extend(ancestors)
 
-        # Include parent interface headers needed for jsonifier code.
+        # Include parent interface headers needed for default toJSON code.
         jsonInterfaceParents = []
         for desc in descriptors:
-            if not desc.operations['Jsonifier']:
+            if not desc.hasDefaultToJSON:
                 continue
             parent = desc.interface.parent
             while parent:
                 parentDesc = desc.getDescriptor(parent.identifier.name)
-                if parentDesc.operations['Jsonifier']:
+                if parentDesc.hasDefaultToJSON:
                     jsonInterfaceParents.append(parentDesc.interface)
                 parent = parent.parent
         interfaceDeps.extend(jsonInterfaceParents)
 
         bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
 
         # Grab all the implementation declaration files we need.
         implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude())
@@ -2406,30 +2406,16 @@ class MethodDefiner(PropertyDefiner):
                     "length": 0,
                     "flags": "JSPROP_ENUMERATE",
                     "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
                 }
                 if isChromeOnly(stringifier):
                     self.chrome.append(toStringDesc)
                 else:
                     self.regular.append(toStringDesc)
-            jsonifier = descriptor.operations['Jsonifier']
-            if (jsonifier and
-                unforgeable == MemberIsUnforgeable(jsonifier, descriptor)):
-                toJSONDesc = {
-                    "name": "toJSON",
-                    "nativeName": jsonifier.identifier.name,
-                    "length": 0,
-                    "flags": "JSPROP_ENUMERATE",
-                    "condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
-                }
-                if isChromeOnly(jsonifier):
-                    self.chrome.append(toJSONDesc)
-                else:
-                    self.regular.append(toJSONDesc)
             if (unforgeable and
                 descriptor.interface.getExtendedAttribute("Unforgeable")):
                 # Synthesize our valueOf method
                 self.regular.append({
                     "name": 'valueOf',
                     "selfHostedName": "Object_valueOf",
                     "methodInfo": False,
                     "length": 0,
@@ -2873,53 +2859,53 @@ class CGNativeProperties(CGList):
 
     def declare(self):
         return ""
 
     def define(self):
         return CGList.define(self)
 
 
-class CGJsonifyAttributesMethod(CGAbstractMethod):
-    """
-    Generate the JsonifyAttributes method for an interface descriptor
-    """
-    def __init__(self, descriptor, jsonifierMethod):
+class CGCollectJSONAttributesMethod(CGAbstractMethod):
+    """
+    Generate the CollectJSONAttributes method for an interface descriptor
+    """
+    def __init__(self, descriptor, toJSONMethod):
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('%s*' % descriptor.nativeType, 'self'),
                 Argument('JS::Rooted<JSObject*>&', 'aResult')]
-        CGAbstractMethod.__init__(self, descriptor, 'JsonifyAttributes',
+        CGAbstractMethod.__init__(self, descriptor, 'CollectJSONAttributes',
                                   'bool', args, canRunScript=True)
-        self.jsonifierMethod = jsonifierMethod
+        self.toJSONMethod = toJSONMethod
 
     def definition_body(self):
         ret = ''
         interface = self.descriptor.interface
-        jsonifierCondition = PropertyDefiner.getControllingCondition(self.jsonifierMethod,
-                                                                     self.descriptor)
+        toJSONCondition = PropertyDefiner.getControllingCondition(self.toJSONMethod,
+                                                                  self.descriptor)
         for m in interface.members:
             if m.isAttr() and not m.isStatic() and m.type.isJSONType():
                 getAndDefine = fill(
                     """
                     JS::Rooted<JS::Value> temp(aCx);
                     if (!get_${name}(aCx, obj, self, JSJitGetterCallArgs(&temp))) {
                       return false;
                     }
                     if (!JS_DefineProperty(aCx, aResult, "${name}", temp, JSPROP_ENUMERATE)) {
                       return false;
                     }
                     """,
                     name=IDLToCIdentifier(m.identifier.name))
                 # Make sure we don't include things which are supposed to be
                 # disabled.  Things that either don't have disablers or whose
-                # disablers match the disablers for our jsonifier method can't
+                # disablers match the disablers for our toJSON method can't
                 # possibly be disabled, but other things might be.
                 condition = PropertyDefiner.getControllingCondition(m, self.descriptor)
-                if condition.hasDisablers() and condition != jsonifierCondition:
+                if condition.hasDisablers() and condition != toJSONCondition:
                     ret += fill(
                         """
                         // This is unfortunately a linear scan through sAttributes, but we
                         // only do it for things which _might_ be disabled, which should
                         // help keep the performance problems down.
                         if (IsGetterEnabled(aCx, obj, (JSJitGetterOp)get_${name}, sAttributes)) {
                           $*{getAndDefine}
                         }
@@ -8636,42 +8622,42 @@ class CGMethodPromiseWrapper(CGAbstractS
             methodName=self.method.name,
             args=", ".join(arg.name for arg in self.args))
 
     @staticmethod
     def makeName(methodName):
         return methodName + "_promiseWrapper"
 
 
-class CGJsonifierMethod(CGSpecializedMethod):
+class CGDefaultToJSONMethod(CGSpecializedMethod):
     def __init__(self, descriptor, method):
-        assert method.isJsonifier()
+        assert method.isDefaultToJSON()
         CGSpecializedMethod.__init__(self, descriptor, method)
 
     def definition_body(self):
         ret = dedent("""
             JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx));
             if (!result) {
               return false;
             }
             """)
 
         jsonDescriptors = [self.descriptor]
         interface = self.descriptor.interface.parent
         while interface:
             descriptor = self.descriptor.getDescriptor(interface.identifier.name)
-            if descriptor.operations['Jsonifier']:
+            if descriptor.hasDefaultToJSON:
                 jsonDescriptors.append(descriptor)
             interface = interface.parent
 
         # Iterate the array in reverse: oldest ancestor first
         for descriptor in jsonDescriptors[::-1]:
             ret += fill(
                 """
-                if (!${parentclass}::JsonifyAttributes(cx, obj, self, result)) {
+                if (!${parentclass}::CollectJSONAttributes(cx, obj, self, result)) {
                   return false;
                 }
                 """,
                 parentclass=toBindingNamespace(descriptor.name)
                 )
         ret += ('args.rval().setObject(*result);\n'
                 'return true;\n')
         return ret
@@ -12259,25 +12245,22 @@ def stripTrailingWhitespace(text):
     return '\n'.join(line.rstrip() for line in lines) + tail
 
 
 class MemberProperties:
     def __init__(self):
         self.isCrossOriginMethod = False
         self.isCrossOriginGetter = False
         self.isCrossOriginSetter = False
-        self.isJsonifier = False
 
 
 def memberProperties(m, descriptor):
     props = MemberProperties()
     if m.isMethod():
-        if m == descriptor.operations['Jsonifier']:
-            props.isJsonifier = True
-        elif (not m.isIdentifierLess() or m == descriptor.operations['Stringifier']):
+        if (not m.isIdentifierLess() or m == descriptor.operations['Stringifier']):
             if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
                 if m.getExtendedAttribute("CrossOriginCallable"):
                     props.isCrossOriginMethod = True
     elif m.isAttr():
         if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
             if m.getExtendedAttribute("CrossOriginReadable"):
                 props.isCrossOriginGetter = True
         if not m.readonly:
@@ -12307,34 +12290,34 @@ class CGDescriptor(CGThing):
         cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
                                   descriptor.nativeType))
         parent = descriptor.interface.parent
         if parent:
             cgThings.append(CGGeneric("static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
                                       "              \"Can't inherit from an interface with a different ownership model.\");\n" %
                                       toBindingNamespace(descriptor.parentPrototypeName)))
 
-        jsonifierMethod = None
+        defaultToJSONMethod = None
         crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
         unscopableNames = list()
         for n in descriptor.interface.namedConstructors:
             cgThings.append(CGClassConstructor(descriptor, n,
                                                NamedConstructorName(n)))
         for m in descriptor.interface.members:
             if m.isMethod() and m.identifier.name == 'QueryInterface':
                 continue
 
             props = memberProperties(m, descriptor)
 
             if m.isMethod():
                 if m.getExtendedAttribute("Unscopable"):
                     assert not m.isStatic()
                     unscopableNames.append(m.identifier.name)
-                if props.isJsonifier:
-                    jsonifierMethod = m
+                if m.isDefaultToJSON():
+                    defaultToJSONMethod = m
                 elif not m.isIdentifierLess() or m == descriptor.operations['Stringifier']:
                     if m.isStatic():
                         assert descriptor.interface.hasInterfaceObject()
                         cgThings.append(CGStaticMethod(descriptor, m))
                         if m.returnsPromise():
                             cgThings.append(CGStaticMethodJitinfo(m))
                     elif descriptor.interface.hasInterfacePrototypeObject():
                         specializedMethod = CGSpecializedMethod(descriptor, m)
@@ -12385,19 +12368,19 @@ class CGDescriptor(CGThing):
                 elif m.getExtendedAttribute("LenientSetter"):
                     cgThings.append(CGSpecializedLenientSetter(descriptor, m))
                 if (not m.isStatic() and
                     descriptor.interface.hasInterfacePrototypeObject()):
                     cgThings.append(CGMemberJITInfo(descriptor, m))
             if m.isConst() and m.type.isPrimitive():
                 cgThings.append(CGConstDefinition(m))
 
-        if jsonifierMethod:
-            cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
-            cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
+        if defaultToJSONMethod:
+            cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod))
+            cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod))
         if descriptor.interface.isNavigatorProperty():
             cgThings.append(CGConstructNavigatorObject(descriptor))
 
         if descriptor.concrete and not descriptor.proxy:
             if wantsAddProperty(descriptor):
                 cgThings.append(CGAddPropertyHook(descriptor))
 
             # Always have a finalize hook, regardless of whether the class
@@ -12411,20 +12394,20 @@ class CGDescriptor(CGThing):
         if descriptor.interface.isJSImplemented():
             for m in clearableCachedAttrs(descriptor):
                 cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m))
 
         properties = PropertyArrays(descriptor)
         cgThings.append(CGGeneric(define=str(properties)))
         cgThings.append(CGNativeProperties(descriptor, properties))
 
-        if jsonifierMethod:
+        if defaultToJSONMethod:
             # Now that we know about our property arrays, we can
-            # output our "jsonify attributes" method, which uses those.
-            cgThings.append(CGJsonifyAttributesMethod(descriptor, jsonifierMethod))
+            # output our "collect attribute values" method, which uses those.
+            cgThings.append(CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod))
 
         if descriptor.interface.hasInterfaceObject():
             cgThings.append(CGClassConstructor(descriptor,
                                                descriptor.interface.ctor()))
             cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
             cgThings.append(CGNamedConstructors(descriptor))
 
         cgThings.append(CGLegacyCallHook(descriptor))
@@ -14717,19 +14700,16 @@ class CGBindingImplClass(CGClass):
                     return
             if name == "LegacyCaller":
                 if op.isIdentifierLess():
                     # XXXbz I wish we were consistent about our renaming here.
                     name = "LegacyCall"
                 else:
                     # We already added this method
                     return
-            if name == "Jsonifier":
-                # We already added this method
-                return
             self.methodDecls.append(
                 CGNativeMember(descriptor, op,
                                name,
                                (returnType, args),
                                descriptor.getExtendedAttributes(op)))
         # Sort things by name so we get stable ordering in the output.
         ops = descriptor.operations.items()
         ops.sort(key=lambda x: x[0])
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -412,37 +412,38 @@ class Descriptor(DescriptorProvider):
             'IndexedGetter': None,
             'IndexedSetter': None,
             'IndexedDeleter': None,
             'NamedGetter': None,
             'NamedSetter': None,
             'NamedDeleter': None,
             'Stringifier': None,
             'LegacyCaller': None,
-            'Jsonifier': None
             }
 
-        # Stringifiers and jsonifiers need to be set up whether an interface is
+        self.hasDefaultToJSON = False
+
+        # Stringifiers need to be set up whether an interface is
         # concrete or not, because they're actually prototype methods and hence
         # can apply to instances of descendant interfaces.  Legacy callers and
         # named/indexed operations only need to be set up on concrete
         # interfaces, since they affect the JSClass we end up using, not the
         # prototype object.
         def addOperation(operation, m):
             if not self.operations[operation]:
                 self.operations[operation] = m
 
         # Since stringifiers go on the prototype, we only need to worry
         # about our own stringifier, not those of our ancestor interfaces.
         if not self.interface.isExternal():
             for m in self.interface.members:
                 if m.isMethod() and m.isStringifier():
                     addOperation('Stringifier', m)
-                if m.isMethod() and m.isJsonifier():
-                    addOperation('Jsonifier', m)
+                if m.isMethod() and m.isDefaultToJSON():
+                    self.hasDefaultToJSON = True
 
         if self.concrete:
             self.proxy = False
             iface = self.interface
             for m in iface.members:
                 # Don't worry about inheriting legacycallers either: in
                 # practice these are on most-derived prototypes.
                 if m.isMethod() and m.isLegacycaller():
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1012,20 +1012,19 @@ class IDLInterfaceOrNamespace(IDLObjectW
             # spec would otherwise require us to synthesize and is
             # missing the ones we plan to synthesize.
             if not any(m.isMethod() and m.isStringifier() for m in self.members):
                 raise WebIDLError("Unforgeable interface %s does not have a "
                                   "stringifier" % self.identifier.name,
                                   [self.location])
 
             for m in self.members:
-                if ((m.isMethod() and m.isJsonifier()) or
-                    m.identifier.name == "toJSON"):
+                if m.identifier.name == "toJSON":
                     raise WebIDLError("Unforgeable interface %s has a "
-                                      "jsonifier so we won't be able to add "
+                                      "toJSON so we won't be able to add "
                                       "one ourselves" % self.identifier.name,
                                       [self.location, m.location])
 
                 if m.identifier.name == "valueOf" and not m.isStatic():
                     raise WebIDLError("Unforgeable interface %s has a valueOf "
                                       "member so we won't be able to add one "
                                       "ourselves" % self.identifier.name,
                                       [self.location, m.location])
@@ -1110,25 +1109,22 @@ class IDLInterfaceOrNamespace(IDLObjectW
             if member.isGetter():
                 memberType = "getters"
             elif member.isSetter():
                 memberType = "setters"
             elif member.isDeleter():
                 memberType = "deleters"
             elif member.isStringifier():
                 memberType = "stringifiers"
-            elif member.isJsonifier():
-                memberType = "jsonifiers"
             elif member.isLegacycaller():
                 memberType = "legacycallers"
             else:
                 continue
 
-            if (memberType != "stringifiers" and memberType != "legacycallers" and
-                memberType != "jsonifiers"):
+            if (memberType != "stringifiers" and memberType != "legacycallers"):
                 if member.isNamed():
                     memberType = "named " + memberType
                 else:
                     assert member.isIndexed()
                     memberType = "indexed " + memberType
 
             if memberType in specialMembersSeen:
                 raise WebIDLError("Multiple " + memberType + " on %s" % (self),
@@ -2813,17 +2809,17 @@ class IDLWrapperType(IDLType):
         return isinstance(self.inner, IDLEnum)
 
     def isJSONType(self):
         if self.isInterface():
             if self.inner.isExternal():
                 return False
             iface = self.inner
             while iface:
-                if any(m.isMethod() and m.isJsonifier() for m in self.inner.members):
+                if any(m.isMethod() and m.isToJSON() for m in self.inner.members):
                     return True
                 iface = iface.parent
             return False
         elif self.isEnum():
             return True
         elif self.isDictionary():
             return all(m.type.isJSONType() for m in self.inner.members)
         else:
@@ -4666,17 +4662,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
         'Neither',
         'Named',
         'Indexed'
     )
 
     def __init__(self, location, identifier, returnType, arguments,
                  static=False, getter=False, setter=False,
                  deleter=False, specialType=NamedOrIndexed.Neither,
-                 legacycaller=False, stringifier=False, jsonifier=False,
+                 legacycaller=False, stringifier=False,
                  maplikeOrSetlikeOrIterable=None, htmlConstructor=False):
         # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Method)
 
         self._hasOverloads = False
 
         assert isinstance(returnType, IDLType)
@@ -4691,18 +4687,16 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert isinstance(setter, bool)
         self._setter = setter
         assert isinstance(deleter, bool)
         self._deleter = deleter
         assert isinstance(legacycaller, bool)
         self._legacycaller = legacycaller
         assert isinstance(stringifier, bool)
         self._stringifier = stringifier
-        assert isinstance(jsonifier, bool)
-        self._jsonifier = jsonifier
         assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
         self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
         assert isinstance(htmlConstructor, bool)
         # The identifier of a HTMLConstructor must be 'constructor'.
         assert not htmlConstructor or identifier.name == "constructor"
         self._htmlConstructor = htmlConstructor
         self._specialType = specialType
         self._unforgeable = False
@@ -4740,22 +4734,16 @@ class IDLMethod(IDLInterfaceMember, IDLS
             assert not arguments[1].optional and not arguments[1].variadic
 
         if self._stringifier:
             assert len(self._overloads) == 1
             overload = self._overloads[0]
             assert len(overload.arguments) == 0
             assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
 
-        if self._jsonifier:
-            assert len(self._overloads) == 1
-            overload = self._overloads[0]
-            assert len(overload.arguments) == 0
-            assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
-
     def isStatic(self):
         return self._static
 
     def forceStatic(self):
         self._static = True
 
     def isGetter(self):
         return self._getter
@@ -4777,33 +4765,35 @@ class IDLMethod(IDLInterfaceMember, IDLS
         return self._specialType == IDLMethod.NamedOrIndexed.Indexed
 
     def isLegacycaller(self):
         return self._legacycaller
 
     def isStringifier(self):
         return self._stringifier
 
-    def isJsonifier(self):
-        return self._jsonifier
+    def isToJSON(self):
+        return self.identifier.name == "toJSON"
+
+    def isDefaultToJSON(self):
+        return self.isToJSON() and self.getExtendedAttribute("Default")
 
     def isMaplikeOrSetlikeOrIterableMethod(self):
         """
         True if this method was generated as part of a
         maplike/setlike/etc interface (e.g. has/get methods)
         """
         return self.maplikeOrSetlikeOrIterable is not None
 
     def isSpecial(self):
         return (self.isGetter() or
                 self.isSetter() or
                 self.isDeleter() or
                 self.isLegacycaller() or
-                self.isStringifier() or
-                self.isJsonifier())
+                self.isStringifier())
 
     def isHTMLConstructor(self):
         return self._htmlConstructor
 
     def hasOverloads(self):
         return self._hasOverloads
 
     def isIdentifierLess(self):
@@ -4849,18 +4839,16 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert not self.isGetter()
         assert not method.isGetter()
         assert not self.isSetter()
         assert not method.isSetter()
         assert not self.isDeleter()
         assert not method.isDeleter()
         assert not self.isStringifier()
         assert not method.isStringifier()
-        assert not self.isJsonifier()
-        assert not method.isJsonifier()
         assert not self.isHTMLConstructor()
         assert not method.isHTMLConstructor()
 
         return self
 
     def signatures(self):
         return [(overload.returnType, overload.arguments) for overload in
                 self._overloads]
@@ -5119,16 +5107,24 @@ class IDLMethod(IDLInterfaceMember, IDLS
             if not attr.noArguments():
                 raise WebIDLError("[CEReactions] must take no arguments",
                                   [attr.location])
 
             if self.isSpecial() and not self.isSetter() and not self.isDeleter():
                 raise WebIDLError("[CEReactions] is only allowed on operation, "
                                   "attribute, setter, and deleter",
                                   [attr.location, self.location])
+        elif identifier == "Default":
+            if not attr.noArguments():
+                raise WebIDLError("[Default] must take no arguments",
+                                  [attr.location])
+
+            if not self.isToJSON():
+                raise WebIDLError("[Default] is only allowed on toJSON operations",
+                                  [attr.location, self.location])
         elif (identifier == "Throws" or
               identifier == "CanOOM" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "SecureContext" or
@@ -5306,17 +5302,16 @@ class Tokenizer(object):
         "typedef": "TYPEDEF",
         "implements": "IMPLEMENTS",
         "const": "CONST",
         "null": "NULL",
         "true": "TRUE",
         "false": "FALSE",
         "serializer": "SERIALIZER",
         "stringifier": "STRINGIFIER",
-        "jsonifier": "JSONIFIER",
         "unrestricted": "UNRESTRICTED",
         "attribute": "ATTRIBUTE",
         "readonly": "READONLY",
         "inherit": "INHERIT",
         "static": "STATIC",
         "getter": "GETTER",
         "setter": "SETTER",
         "deleter": "DELETER",
@@ -6137,29 +6132,16 @@ class Parser(Tokenizer):
                                              allowDoubleUnderscore=True)
         method = IDLMethod(self.getLocation(p, 1),
                            identifier,
                            returnType=BuiltinTypes[IDLBuiltinType.Types.domstring],
                            arguments=[],
                            stringifier=True)
         p[0] = method
 
-    def p_Jsonifier(self, p):
-        """
-            Operation : JSONIFIER SEMICOLON
-        """
-        identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
-                                             "__jsonifier", allowDoubleUnderscore=True)
-        method = IDLMethod(self.getLocation(p, 1),
-                           identifier,
-                           returnType=BuiltinTypes[IDLBuiltinType.Types.object],
-                           arguments=[],
-                           jsonifier=True)
-        p[0] = method
-
     def p_QualifierStatic(self, p):
         """
             Qualifier : STATIC
         """
         p[0] = [IDLInterfaceMember.Special.Static]
 
     def p_QualifierStringifier(self, p):
         """
@@ -6303,17 +6285,16 @@ class Parser(Tokenizer):
                          | MAPLIKE
                          | PARTIAL
                          | REQUIRED
                          | SERIALIZER
                          | SETLIKE
                          | SETTER
                          | STATIC
                          | STRINGIFIER
-                         | JSONIFIER
                          | TYPEDEF
                          | UNRESTRICTED
                          | NAMESPACE
         """
         p[0] = p[1]
 
     def p_AttributeName(self, p):
         """
@@ -6455,17 +6436,16 @@ class Parser(Tokenizer):
                   | OCTET
                   | OPTIONAL
                   | SEQUENCE
                   | RECORD
                   | SETTER
                   | SHORT
                   | STATIC
                   | STRINGIFIER
-                  | JSONIFIER
                   | TRUE
                   | TYPEDEF
                   | UNSIGNED
                   | VOID
         """
         pass
 
     def p_OtherOrComma(self, p):
--- a/dom/bindings/parser/tests/test_cereactions.py
+++ b/dom/bindings/parser/tests/test_cereactions.py
@@ -126,22 +126,8 @@ def WebIDLTest(parser, harness):
         """)
         results = parser.finish()
     except:
         threw = True
 
     harness.ok(threw,
                "Should have thrown for [CEReactions] used on a stringifier")
 
-    parser = parser.reset()
-    threw = False
-    try:
-        parser.parse("""
-            interface Foo {
-              [CEReactions] jsonifier;
-            };
-        """)
-
-        results = parser.finish()
-    except:
-        threw = True
-
-    harness.ok(threw, "Should have thrown for [CEReactions] used on a jsonifier")
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -940,22 +940,22 @@ public:
   uint32_t UnforgeableAttr2();
   uint32_t UnforgeableMethod();
   uint32_t UnforgeableMethod2();
   void Stringify(nsString&);
   void PassRenamedInterface(nsRenamedInterface&);
   TestInterface* PutForwardsAttr();
   TestInterface* PutForwardsAttr2();
   TestInterface* PutForwardsAttr3();
-  void GetJsonifierShouldSkipThis(JSContext*, JS::MutableHandle<JS::Value>);
-  void SetJsonifierShouldSkipThis(JSContext*, JS::Rooted<JS::Value>&);
-  TestParentInterface* JsonifierShouldSkipThis2();
-  void SetJsonifierShouldSkipThis2(TestParentInterface&);
-  TestCallbackInterface* JsonifierShouldSkipThis3();
-  void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
+  void GetToJSONShouldSkipThis(JSContext*, JS::MutableHandle<JS::Value>);
+  void SetToJSONShouldSkipThis(JSContext*, JS::Rooted<JS::Value>&);
+  TestParentInterface* ToJSONShouldSkipThis2();
+  void SetToJSONShouldSkipThis2(TestParentInterface&);
+  TestCallbackInterface* ToJSONShouldSkipThis3();
+  void SetToJSONShouldSkipThis3(TestCallbackInterface&);
   void ThrowingMethod(ErrorResult& aRv);
   bool GetThrowingAttr(ErrorResult& aRv) const;
   void SetThrowingAttr(bool arg, ErrorResult& aRv);
   bool GetThrowingGetterAttr(ErrorResult& aRv) const;
   void SetThrowingGetterAttr(bool arg);
   bool ThrowingSetterAttr() const;
   void SetThrowingSetterAttr(bool arg, ErrorResult& aRv);
   void CanOOMMethod(OOMReporter& aRv);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -958,20 +958,20 @@ interface TestInterface {
   [CEReactions] void ceReactionsMethodOverload(DOMString bar);
   [CEReactions] attribute boolean ceReactionsAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
 
-  attribute any jsonifierShouldSkipThis;
-  attribute TestParentInterface jsonifierShouldSkipThis2;
-  attribute TestCallbackInterface jsonifierShouldSkipThis3;
-  jsonifier;
+  attribute any toJSONShouldSkipThis;
+  attribute TestParentInterface toJSONShouldSkipThis2;
+  attribute TestCallbackInterface toJSONShouldSkipThis3;
+  [Default] object toJSON();
 
   attribute byte dashed-attribute;
   void dashed-method();
 
   // [NonEnumerable] tests
   [NonEnumerable]
   attribute boolean nonEnumerableAttr;
   [NonEnumerable]
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -791,20 +791,20 @@ interface TestExampleInterface {
   [CEReactions] void ceReactionsMethodOverload();
   [CEReactions] void ceReactionsMethodOverload(DOMString bar);
   [CEReactions] attribute boolean ceReactionsAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
-  attribute any jsonifierShouldSkipThis;
-  attribute TestParentInterface jsonifierShouldSkipThis2;
-  attribute TestCallbackInterface jsonifierShouldSkipThis3;
-  jsonifier;
+  attribute any toJSONShouldSkipThis;
+  attribute TestParentInterface toJSONShouldSkipThis2;
+  attribute TestCallbackInterface toJSONShouldSkipThis3;
+  [Default] object toJSON();
 
   attribute byte dashed-attribute;
   void dashed-method();
 
   // [NonEnumerable] tests
   [NonEnumerable]
   attribute boolean nonEnumerableAttr;
   [NonEnumerable]
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -809,20 +809,20 @@ interface TestJSImplInterface {
   // now, because we always pass in the caller principal anyway.
   //  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
   //  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   // legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
-  attribute any jsonifierShouldSkipThis;
-  attribute TestParentInterface jsonifierShouldSkipThis2;
-  attribute TestCallbackInterface jsonifierShouldSkipThis3;
-  jsonifier;
+  attribute any toJSONShouldSkipThis;
+  attribute TestParentInterface toJSONShouldSkipThis2;
+  attribute TestCallbackInterface toJSONShouldSkipThis3;
+  [Default] object toJSON();
 
   attribute byte dashed-attribute;
   void dashed-method();
 
   // [NonEnumerable] tests
   [NonEnumerable]
   attribute boolean nonEnumerableAttr;
   [NonEnumerable]
--- a/dom/webidl/MediaDeviceInfo.webidl
+++ b/dom/webidl/MediaDeviceInfo.webidl
@@ -15,10 +15,10 @@ enum MediaDeviceKind {
 
 [Func="Navigator::HasUserMediaSupport"]
 interface MediaDeviceInfo {
   readonly attribute DOMString       deviceId;
   readonly attribute MediaDeviceKind kind;
   readonly attribute DOMString       label;
   readonly attribute DOMString       groupId;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/PaymentAddress.webidl
+++ b/dom/webidl/PaymentAddress.webidl
@@ -5,19 +5,17 @@
  *
  * The origin of this WebIDL file is
  *   https://www.w3.org/TR/payment-request/#paymentaddress-interface
  */
 
 [SecureContext,
  Func="mozilla::dom::PaymentRequest::PrefEnabled"]
 interface PaymentAddress {
-  // TODO: Use serializer once available. (Bug 863402)
-  // serializer = {attribute};
-  jsonifier;
+  [Default] object toJSON();
 
   readonly attribute DOMString              country;
   // TODO: Use FrozenArray once available. (Bug 1236777)
   // readonly attribute FrozenArray<DOMString> addressLine;
   [Frozen, Cached, Pure]
   readonly attribute sequence<DOMString>    addressLine;
   readonly attribute DOMString              region;
   readonly attribute DOMString              city;
--- a/dom/webidl/PaymentResponse.webidl
+++ b/dom/webidl/PaymentResponse.webidl
@@ -11,19 +11,17 @@ enum PaymentComplete {
   "success",
   "fail",
   "unknown"
 };
 
 [SecureContext,
  Func="mozilla::dom::PaymentRequest::PrefEnabled"]
 interface PaymentResponse {
-  // TODO: Use serializer once available. (Bug 863402)
-  // serializer = {attribute};
-  jsonifier;
+  [Default] object toJSON();
 
   readonly attribute DOMString       requestId;
   readonly attribute DOMString       methodName;
   readonly attribute object          details;
   readonly attribute PaymentAddress? shippingAddress;
   readonly attribute DOMString?      shippingOption;
   readonly attribute DOMString?      payerName;
   readonly attribute DOMString?      payerEmail;
--- a/dom/webidl/Performance.webidl
+++ b/dom/webidl/Performance.webidl
@@ -24,17 +24,17 @@ interface Performance : EventTarget {
 
 [Exposed=Window]
 partial interface Performance {
   [Constant]
   readonly attribute PerformanceTiming timing;
   [Constant]
   readonly attribute PerformanceNavigation navigation;
 
-  jsonifier;
+  [Default] object toJSON();
 };
 
 // http://www.w3.org/TR/performance-timeline/#sec-window.performance-attribute
 [Exposed=(Window,Worker)]
 partial interface Performance {
   PerformanceEntryList getEntries();
   PerformanceEntryList getEntriesByType(DOMString entryType);
   PerformanceEntryList getEntriesByName(DOMString name, optional DOMString
--- a/dom/webidl/PerformanceEntry.webidl
+++ b/dom/webidl/PerformanceEntry.webidl
@@ -13,10 +13,10 @@
 [Exposed=(Window,Worker)]
 interface PerformanceEntry
 {
   readonly attribute DOMString name;
   readonly attribute DOMString entryType;
   readonly attribute DOMHighResTimeStamp startTime;
   readonly attribute DOMHighResTimeStamp duration;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/PerformanceNavigation.webidl
+++ b/dom/webidl/PerformanceNavigation.webidl
@@ -14,10 +14,10 @@ interface PerformanceNavigation {
   const unsigned short TYPE_NAVIGATE = 0;
   const unsigned short TYPE_RELOAD = 1;
   const unsigned short TYPE_BACK_FORWARD = 2;
   const unsigned short TYPE_RESERVED = 255;
 
   readonly attribute unsigned short type;
   readonly attribute unsigned short redirectCount;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/PerformanceNavigationTiming.webidl
+++ b/dom/webidl/PerformanceNavigationTiming.webidl
@@ -24,10 +24,10 @@ interface PerformanceNavigationTiming : 
   readonly        attribute DOMHighResTimeStamp domContentLoadedEventStart;
   readonly        attribute DOMHighResTimeStamp domContentLoadedEventEnd;
   readonly        attribute DOMHighResTimeStamp domComplete;
   readonly        attribute DOMHighResTimeStamp loadEventStart;
   readonly        attribute DOMHighResTimeStamp loadEventEnd;
   readonly        attribute NavigationType      type;
   readonly        attribute unsigned short      redirectCount;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/PerformanceResourceTiming.webidl
+++ b/dom/webidl/PerformanceResourceTiming.webidl
@@ -49,10 +49,10 @@ interface PerformanceResourceTiming : Pe
   [NeedsSubjectPrincipal]
   readonly attribute unsigned long long decodedBodySize;
 
   // TODO: Use FrozenArray once available. (Bug 1236777)
   // readonly attribute FrozenArray<PerformanceServerTiming> serverTiming;
   [SecureContext, Frozen, Cached, Pure, NeedsSubjectPrincipal]
   readonly attribute sequence<PerformanceServerTiming> serverTiming;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/PerformanceServerTiming.webidl
+++ b/dom/webidl/PerformanceServerTiming.webidl
@@ -11,10 +11,10 @@
  */
 
 [SecureContext,Exposed=(Window,Worker)]
 interface PerformanceServerTiming {
   readonly attribute DOMString           name;
   readonly attribute DOMHighResTimeStamp duration;
   readonly attribute DOMString           description;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/PerformanceTiming.webidl
+++ b/dom/webidl/PerformanceTiming.webidl
@@ -40,10 +40,10 @@ interface PerformanceTiming {
   readonly attribute unsigned long long timeToNonBlankPaint;
 
   // This is a Mozilla proprietary extension and not part of the
   // performance/navigation timing specification. It marks the
   // completion of the first presentation flush after DOMContentLoaded.
   [Pref="dom.performance.time_to_dom_content_flushed.enabled"]
   readonly attribute unsigned long long timeToDOMContentFlushed;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/RTCIceCandidate.webidl
+++ b/dom/webidl/RTCIceCandidate.webidl
@@ -16,10 +16,10 @@ dictionary RTCIceCandidateInit {
 [Pref="media.peerconnection.enabled",
  JSImplementation="@mozilla.org/dom/rtcicecandidate;1",
  Constructor(RTCIceCandidateInit candidateInitDict)]
 interface RTCIceCandidate {
   attribute DOMString       candidate;
   attribute DOMString?      sdpMid;
   attribute unsigned short? sdpMLineIndex;
 
-  jsonifier;
+  [Default] object toJSON();
 };
--- a/dom/webidl/RTCSessionDescription.webidl
+++ b/dom/webidl/RTCSessionDescription.webidl
@@ -22,10 +22,10 @@ dictionary RTCSessionDescriptionInit {
 [Pref="media.peerconnection.enabled",
  JSImplementation="@mozilla.org/dom/rtcsessiondescription;1",
  Constructor(optional RTCSessionDescriptionInit descriptionInitDict)]
 interface RTCSessionDescription {
   // These should be readonly, but writing causes deprecation warnings for a bit
   attribute RTCSdpType type;
   attribute DOMString sdp;
 
-  jsonifier;
+  [Default] object toJSON();
 };