diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 5afc5981bf88..f5d02dbe2f53 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -2125,3 +2125,31 @@ addExternalIface('nsISessionStoreRestoreData', headerFile='nsISessionStoreRestoreData.h', notflattened=True) addExternalIface('nsIScreen', nativeType='nsIScreen', headerFile='nsIScreen.h', notflattened=True) + +# The TemplatedAttributes dictionary has the interface name where the template +# should be generated as the key. The values are lists of dictionaries, where +# each dictionary corresponds to one template. The dictionary contains: +# +# template the template's name +# getter the name for the native getter to call +# setter the name for the native setter to call +# argument a tuple for the additional argument that should be passed to the +# native getter and setter, containing the type for the argument +# and a name for the argument. The value will be supplied by the +# [BindingTemplate] extended attribute. +# attrName a string which in the generated C++ code would yield a +# |const char*| that contains the attribute's name + +TemplatedAttributes = { + +'CSS2Properties': [ + { + 'template': 'CSS2Property', + 'getter': 'GetPropertyValue', + 'setter': 'SetPropertyValue', + 'argument': ('nsCSSPropertyID', 'id'), + 'attrName': 'nsCSSProps::PropertyIDLName(id)', + }, +], + +} diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 501741450c94..ad30d021ab26 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1927,7 +1927,7 @@ class CGAbstractMethod(CGThing): prologue += indent( fill( """ - BindingCallContext ${cxname}(cx_, "${label}"); + BindingCallContext ${cxname}(cx_, ${label}); """, cxname=cxname, label=error_reporting_label, @@ -8989,6 +8989,14 @@ class CGPerSignatureCall(CGThing): dontSetSlot should be set to True if the value should not be cached in a slot (even if the attribute is marked as StoreInSlot or Cached in the WebIDL). + + errorReportingLabel can contain a custom label to use for error reporting. + It will be inserted as is in the code, so if it needs to be a literal + string in C++ it should be quoted. + + additionalArgsPre contains additional arguments that are added after the + arguments that CGPerSignatureCall itself adds (JSContext, global, …), and + before the actual arguments. """ # XXXbz For now each entry in the argument list is either an @@ -9013,6 +9021,8 @@ class CGPerSignatureCall(CGThing): objectName="obj", dontSetSlot=False, extendedAttributes=None, + errorReportingLabel=None, + additionalArgsPre=[], ): assert idlNode.isMethod() == (not getter and not setter) assert idlNode.isAttr() == (getter or setter) @@ -9322,14 +9332,17 @@ class CGPerSignatureCall(CGThing): assert setter cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode)) else: - context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor) - if getter: - context = context + " getter" - elif setter: - context = context + " setter" - # Callee expects a quoted string for the context if - # there's a context. - context = '"%s"' % context + if errorReportingLabel is None: + context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor) + if getter: + context = context + " getter" + elif setter: + context = context + " setter" + # Callee expects a quoted string for the context if + # there's a context. + context = '"%s"' % context + else: + context = errorReportingLabel if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"): [ @@ -9346,7 +9359,7 @@ class CGPerSignatureCall(CGThing): needsCallerType(idlNode), isChromeOnly(idlNode), args, - argsPre, + argsPre + additionalArgsPre, returnType, self.extendedAttributes, descriptor, @@ -10225,6 +10238,8 @@ class CGGetterCall(CGPerSignatureCall): nativeMethodName, descriptor, attr, + errorReportingLabel=None, + argsPre=[], dontSetSlot=False, extendedAttributes=None, ): @@ -10249,6 +10264,8 @@ class CGGetterCall(CGPerSignatureCall): useCounterName=useCounterName, dontSetSlot=dontSetSlot, extendedAttributes=extendedAttributes, + errorReportingLabel=errorReportingLabel, + additionalArgsPre=argsPre, ) @@ -10285,7 +10302,15 @@ class CGSetterCall(CGPerSignatureCall): setter. """ - def __init__(self, argType, nativeMethodName, descriptor, attr): + def __init__( + self, + argType, + nativeMethodName, + descriptor, + attr, + errorReportingLabel=None, + argsPre=[], + ): if attr.getExtendedAttribute("UseCounter"): useCounterName = "%s_%s_setter" % ( descriptor.interface.identifier.name, @@ -10305,6 +10330,8 @@ class CGSetterCall(CGPerSignatureCall): attr, setter=True, useCounterName=useCounterName, + errorReportingLabel=errorReportingLabel, + additionalArgsPre=argsPre, ) def wrap_return_value(self): @@ -10586,7 +10613,7 @@ class CGSpecializedMethod(CGAbstractStaticMethod): descriptor, idlMethod ): return None - return GetLabelForErrorReporting(descriptor, idlMethod, isConstructor) + return '"%s"' % GetLabelForErrorReporting(descriptor, idlMethod, isConstructor) def error_reporting_label(self): return CGSpecializedMethod.error_reporting_label_helper( @@ -10596,7 +10623,9 @@ class CGSpecializedMethod(CGAbstractStaticMethod): @staticmethod def makeNativeName(descriptor, method): if method.underlyingAttr: - return CGSpecializedGetter.makeNativeName(descriptor, method.underlyingAttr) + return CGSpecializedGetterCommon.makeNativeName( + descriptor, method.underlyingAttr + ) name = method.identifier.name return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic())) @@ -10989,21 +11018,25 @@ class CGStaticMethod(CGAbstractStaticBindingMethod): ) -class CGSpecializedGetter(CGAbstractStaticMethod): +class CGSpecializedGetterCommon(CGAbstractStaticMethod): """ A class for generating the code for a specialized attribute getter that the JIT can call with lower overhead. """ - def __init__(self, descriptor, attr): - self.attr = attr - name = "get_" + IDLToCIdentifier(attr.identifier.name) - args = [ - Argument("JSContext*", "cx"), - Argument("JS::Handle", "obj"), - Argument("void*", "void_self"), - Argument("JSJitGetterCallArgs", "args"), - ] + def __init__( + self, + descriptor, + name, + nativeName, + attr, + args, + errorReportingLabel=None, + additionalArg=None, + ): + self.nativeName = nativeName + self.errorReportingLabel = errorReportingLabel + self.additionalArgs = [] if additionalArg is None else [additionalArg] # StoreInSlot attributes have their getters called from Wrap(). We # really hope they can't run script, and don't want to annotate Wrap() # methods as doing that anyway, so let's not annotate them as @@ -11013,7 +11046,7 @@ class CGSpecializedGetter(CGAbstractStaticMethod): descriptor, name, "bool", - args, + args + self.additionalArgs, canRunScript=not attr.getExtendedAttribute("StoreInSlot"), ) @@ -11041,7 +11074,13 @@ class CGSpecializedGetter(CGAbstractStaticMethod): # backing object from the slot, this requires its own generator. return prefix + getObservableArrayGetterBody(self.descriptor, self.attr) - nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr) + if self.nativeName is None: + nativeName = CGSpecializedGetterCommon.makeNativeName( + self.descriptor, self.attr + ) + else: + nativeName = self.nativeName + type = self.attr.type if self.attr.getExtendedAttribute("CrossOriginReadable"): remoteType = type @@ -11074,6 +11113,8 @@ class CGSpecializedGetter(CGAbstractStaticMethod): nativeName, self.descriptor, self.attr, + self.errorReportingLabel, + argsPre=[a.name for a in self.additionalArgs], dontSetSlot=True, extendedAttributes=extendedAttributes, ).define(), @@ -11144,21 +11185,30 @@ class CGSpecializedGetter(CGAbstractStaticMethod): ) return ( - prefix + CGGetterCall(type, nativeName, self.descriptor, self.attr).define() + prefix + + CGGetterCall( + type, + nativeName, + self.descriptor, + self.attr, + self.errorReportingLabel, + argsPre=[a.name for a in self.additionalArgs], + ).define() ) - def auto_profiler_label(self): + def auto_profiler_label(self, profilerLabel=None): + if profilerLabel is None: + profilerLabel = '"' + self.attr.identifier.name + '"' interface_name = self.descriptor.interface.identifier.name - attr_name = self.attr.identifier.name return fill( """ AUTO_PROFILER_LABEL_DYNAMIC_FAST( - "${interface_name}", "${attr_name}", DOM, cx, + "${interface_name}", ${attr_name}, DOM, cx, uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) | uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); """, interface_name=interface_name, - attr_name=attr_name, + attr_name=profilerLabel, ) def error_reporting_label(self): @@ -11177,6 +11227,112 @@ class CGSpecializedGetter(CGAbstractStaticMethod): return nativeName +class CGSpecializedGetter(CGSpecializedGetterCommon): + """ + A class for generating the code for a specialized attribute getter + that the JIT can call with lower overhead. + """ + + def __init__(self, descriptor, attr): + self.attr = attr + name = "get_" + IDLToCIdentifier(attr.identifier.name) + args = [ + Argument("JSContext*", "cx"), + Argument("JS::Handle", "obj"), + Argument("void*", "void_self"), + Argument("JSJitGetterCallArgs", "args"), + ] + CGSpecializedGetterCommon.__init__(self, descriptor, name, None, attr, args) + + +class CGTemplateForSpecializedGetter(CGSpecializedGetterCommon): + """ + A class for generating the code for a specialized attribute getter + that can be used as the common getter that templated attribute + getters can forward to. + """ + + def __init__(self, descriptor, template): + self.attr = template.attr + self.attrNameString = template.attrNameString + args = [ + Argument("JSContext*", "cx"), + Argument("JS::Handle", "obj"), + Argument("void*", "void_self"), + Argument("JSJitGetterCallArgs", "args"), + ] + errorDescription = ( + 'ErrorDescriptionFor{ "%s", attrName }' + % descriptor.interface.identifier.name + ) + CGSpecializedGetterCommon.__init__( + self, + descriptor, + template.getter, + template.getter, + self.attr, + args, + errorReportingLabel=errorDescription, + additionalArg=Argument(template.argument.type, template.argument.name), + ) + + def auto_profiler_label(self): + return ( + fill( + """ + const char* attrName = ${attrNameString}; + """, + attrNameString=self.attrNameString, + ) + + CGSpecializedGetterCommon.auto_profiler_label(self, "attrName") + ) + + +class CGSpecializedTemplatedGetter(CGAbstractStaticMethod): + """ + A class for generating the code for a specialized templated attribute + getter that forwards to a common template getter. + """ + + def __init__(self, descriptor, attr, template, additionalArg): + self.attr = attr + self.template = template + self.additionalArg = additionalArg + name = "get_" + IDLToCIdentifier(attr.identifier.name) + args = [ + Argument("JSContext*", "cx"), + Argument("JS::Handle", "obj"), + Argument("void*", "void_self"), + Argument("JSJitGetterCallArgs", "args"), + ] + assert not attr.getExtendedAttribute("StoreInSlot") + CGAbstractStaticMethod.__init__( + self, + descriptor, + name, + "bool", + args, + canRunScript=True, + ) + + def definition_body(self): + if self.additionalArg is None: + additionalArg = self.attr.identifier.name + else: + additionalArg = self.additionalArg + + return fill( + """ + return ${namespace}::${getter}(cx, obj, void_self, args, ${additionalArg}); + """, + namespace=toBindingNamespace( + self.template.descriptor.interface.identifier.name + ), + getter=self.template.getter, + additionalArg=additionalArg, + ) + + class CGGetterPromiseWrapper(CGAbstractStaticMethod): """ A class for generating a wrapper around another getter that will @@ -11220,7 +11376,9 @@ class CGStaticGetter(CGAbstractStaticBindingMethod): CGAbstractStaticBindingMethod.__init__(self, descriptor, name) def generate_code(self): - nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr) + nativeName = CGSpecializedGetterCommon.makeNativeName( + self.descriptor, self.attr + ) return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr) def auto_profiler_label(self): @@ -11242,29 +11400,44 @@ class CGStaticGetter(CGAbstractStaticBindingMethod): return None -class CGSpecializedSetter(CGAbstractStaticMethod): +class CGSpecializedSetterCommon(CGAbstractStaticMethod): """ A class for generating the code for a specialized attribute setter that the JIT can call with lower overhead. """ - def __init__(self, descriptor, attr): - self.attr = attr - name = "set_" + IDLToCIdentifier(attr.identifier.name) - args = [ - Argument("JSContext*", "cx"), - Argument("JS::Handle", "obj"), - Argument("void*", "void_self"), - Argument("JSJitSetterCallArgs", "args"), - ] + def __init__( + self, + descriptor, + name, + nativeName, + attr, + args, + errorReportingLabel=None, + additionalArg=None, + ): + self.nativeName = nativeName + self.errorReportingLabel = errorReportingLabel + self.additionalArgs = [] if additionalArg is None else [additionalArg] CGAbstractStaticMethod.__init__( - self, descriptor, name, "bool", args, canRunScript=True + self, + descriptor, + name, + "bool", + args + self.additionalArgs, + canRunScript=True, ) def definition_body(self): - nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr) type = self.attr.type - call = CGSetterCall(type, nativeName, self.descriptor, self.attr).define() + call = CGSetterCall( + type, + self.nativeName, + self.descriptor, + self.attr, + self.errorReportingLabel, + [a.name for a in self.additionalArgs], + ).define() prefix = "" if self.attr.getExtendedAttribute("CrossOriginWritable"): if type.isGeckoInterface() and not type.unroll().inner.isExternal(): @@ -11298,18 +11471,19 @@ class CGSpecializedSetter(CGAbstractStaticMethod): call=call, ) - def auto_profiler_label(self): + def auto_profiler_label(self, profilerLabel=None): interface_name = self.descriptor.interface.identifier.name - attr_name = self.attr.identifier.name + if profilerLabel is None: + profilerLabel = '"' + self.attr.identifier.name + '"' return fill( """ AUTO_PROFILER_LABEL_DYNAMIC_FAST( - "${interface_name}", "${attr_name}", DOM, cx, + "${interface_name}", ${attr_name}, DOM, cx, uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) | uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); """, interface_name=interface_name, - attr_name=attr_name, + attr_name=profilerLabel, ) @staticmethod @@ -11320,14 +11494,19 @@ class CGSpecializedSetter(CGAbstractStaticMethod): attr.type, descriptor, allowTreatNonCallableAsNull=True ): return None - return ( + return '"%s"' % ( GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter" ) def error_reporting_label(self): - return CGSpecializedSetter.error_reporting_label_helper( + errorReportingLabel = CGSpecializedSetterCommon.error_reporting_label_helper( self.descriptor, self.attr ) + if errorReportingLabel is None: + return None + if self.errorReportingLabel: + return self.errorReportingLabel + return errorReportingLabel @staticmethod def makeNativeName(descriptor, attr): @@ -11335,6 +11514,114 @@ class CGSpecializedSetter(CGAbstractStaticMethod): return "Set" + MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic())) +class CGSpecializedSetter(CGSpecializedSetterCommon): + """ + A class for generating the code for a specialized attribute setter + that the JIT can call with lower overhead. + """ + + def __init__(self, descriptor, attr): + self.attr = attr + name = "set_" + IDLToCIdentifier(attr.identifier.name) + args = [ + Argument("JSContext*", "cx"), + Argument("JS::Handle", "obj"), + Argument("void*", "void_self"), + Argument("JSJitSetterCallArgs", "args"), + ] + CGSpecializedSetterCommon.__init__( + self, + descriptor, + name, + CGSpecializedSetterCommon.makeNativeName(descriptor, attr), + attr, + args, + ) + + +class CGTemplateForSpecializedSetter(CGSpecializedSetterCommon): + """ + A class for generating the code for a specialized attribute setter + that can be used as the common setter that templated attribute + setters can forward to. + """ + + def __init__(self, descriptor, template): + self.attr = template.attr + self.attrNameString = template.attrNameString + args = [ + Argument("JSContext*", "cx"), + Argument("JS::Handle", "obj"), + Argument("void*", "void_self"), + Argument("JSJitSetterCallArgs", "args"), + ] + errorDescription = ( + 'ErrorDescriptionFor{ "%s", attrName }' + % descriptor.interface.identifier.name + ) + CGSpecializedSetterCommon.__init__( + self, + descriptor, + template.setter, + template.setter, + self.attr, + args, + errorReportingLabel=errorDescription, + additionalArg=Argument(template.argument.type, template.argument.name), + ) + + def auto_profiler_label(self): + return ( + fill( + """ + const char* attrName = ${attrNameString}; + """, + attrNameString=self.attrNameString, + ) + + CGSpecializedSetterCommon.auto_profiler_label(self, "attrName") + ) + + +class CGSpecializedTemplatedSetter(CGAbstractStaticMethod): + """ + A class for generating the code for a specialized templated attribute + setter that forwards to a common template setter. + """ + + def __init__(self, descriptor, attr, template, additionalArg): + self.attr = attr + self.template = template + self.additionalArg = additionalArg + name = "set_" + IDLToCIdentifier(attr.identifier.name) + args = [ + Argument("JSContext*", "cx"), + Argument("JS::Handle", "obj"), + Argument("void*", "void_self"), + Argument("JSJitSetterCallArgs", "args"), + ] + CGAbstractStaticMethod.__init__( + self, descriptor, name, "bool", args, canRunScript=True + ) + + def definition_body(self): + additionalArgs = [] + if self.additionalArg is None: + additionalArgs.append(self.attr.identifier.name) + else: + additionalArgs.append(self.additionalArg) + + return fill( + """ + return ${namespace}::${setter}(cx, obj, void_self, args, ${additionalArgs}); + """, + namespace=toBindingNamespace( + self.template.descriptor.interface.identifier.name + ), + setter=self.template.setter, + additionalArgs=", ".join(additionalArgs), + ) + + class CGStaticSetter(CGAbstractStaticBindingMethod): """ A class for generating the C++ code for an IDL static attribute setter. @@ -11346,7 +11633,9 @@ class CGStaticSetter(CGAbstractStaticBindingMethod): CGAbstractStaticBindingMethod.__init__(self, descriptor, name) def generate_code(self): - nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr) + nativeName = CGSpecializedSetterCommon.makeNativeName( + self.descriptor, self.attr + ) checkForArg = CGGeneric( fill( """ @@ -11375,7 +11664,7 @@ class CGStaticSetter(CGAbstractStaticBindingMethod): ) def error_reporting_label(self): - return CGSpecializedSetter.error_reporting_label_helper( + return CGSpecializedSetterCommon.error_reporting_label_helper( self.descriptor, self.attr ) @@ -11416,7 +11705,7 @@ class CGSpecializedForwardingSetter(CGSpecializedSetter): def error_reporting_label(self): # We always need to be able to throw. - return ( + return '"%s"' % ( GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False) + " setter" ) @@ -14825,7 +15114,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod): if error_label: cxDecl = fill( """ - BindingCallContext cx(cx_, "${error_label}"); + BindingCallContext cx(cx_, ${error_label}); """, error_label=error_label, ) @@ -14877,7 +15166,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod): if error_label: set += fill( """ - BindingCallContext cx(cx_, "${error_label}"); + BindingCallContext cx(cx_, ${error_label}); """, error_label=error_label, ) @@ -15638,7 +15927,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod): if error_label: cxDecl = fill( """ - BindingCallContext cx(cx_, "${error_label}"); + BindingCallContext cx(cx_, ${error_label}); """, error_label=error_label, ) @@ -15671,7 +15960,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod): if error_label: cxDecl = fill( """ - BindingCallContext cx(cx_, "${error_label}"); + BindingCallContext cx(cx_, ${error_label}); """, error_label=error_label, ) @@ -16305,7 +16594,7 @@ def memberProperties(m, descriptor): class CGDescriptor(CGThing): - def __init__(self, descriptor): + def __init__(self, descriptor, attributeTemplates): CGThing.__init__(self) assert ( @@ -16365,10 +16654,23 @@ class CGDescriptor(CGThing): defaultToJSONMethod = None needCrossOriginPropertyArrays = False unscopableNames = list() + for n in descriptor.interface.legacyFactoryFunctions: cgThings.append( CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n)) ) + + if descriptor.attributeTemplates is not None: + for template in descriptor.attributeTemplates: + if template.getter is not None: + cgThings.append( + CGTemplateForSpecializedGetter(descriptor, template) + ) + if template.setter is not None: + cgThings.append( + CGTemplateForSpecializedSetter(descriptor, template) + ) + for m in descriptor.interface.members: if m.isMethod() and m.identifier.name == "QueryInterface": continue @@ -16417,7 +16719,28 @@ class CGDescriptor(CGThing): assert descriptor.interface.hasInterfaceObject() cgThings.append(CGStaticGetter(descriptor, m)) elif descriptor.interface.hasInterfacePrototypeObject(): - specializedGetter = CGSpecializedGetter(descriptor, m) + template = m.getExtendedAttribute("BindingTemplate") + if template is not None: + templateName = template[0][0] + additionalArg = template[0][1] + if not (m.type.isPrimitive() or m.type.isString()): + raise TypeError( + "We only support primitives or strings on templated attributes. " + "Attribute '%s' on interface '%s' has type '%s' but tries to " + "use template '%s'" + % ( + m.identifier.name, + descriptor.interface.identifier.name, + m.type, + templateName, + ) + ) + template = attributeTemplates.get(templateName) + specializedGetter = CGSpecializedTemplatedGetter( + descriptor, m, template, additionalArg + ) + else: + specializedGetter = CGSpecializedGetter(descriptor, m) cgThings.append(specializedGetter) if m.type.isPromise(): cgThings.append( @@ -16430,7 +16753,21 @@ class CGDescriptor(CGThing): assert descriptor.interface.hasInterfaceObject() cgThings.append(CGStaticSetter(descriptor, m)) elif descriptor.interface.hasInterfacePrototypeObject(): - cgThings.append(CGSpecializedSetter(descriptor, m)) + template = m.getExtendedAttribute("BindingTemplate") + if template is not None: + if isinstance(template[0], list): + templateName = template[0][0] + additionalArg = template[0][1] + else: + templateName = template[0] + additionalArg = None + template = attributeTemplates.get(templateName) + specializedSetter = CGSpecializedTemplatedSetter( + descriptor, m, template, additionalArg + ) + else: + specializedSetter = CGSpecializedSetter(descriptor, m) + cgThings.append(specializedSetter) if props.isCrossOriginSetter: needCrossOriginPropertyArrays = True elif m.getExtendedAttribute("PutForwards"): @@ -18708,7 +19045,9 @@ class CGBindingRoot(CGThing): cgthings.append(CGNamespace("binding_detail", CGFastCallback(t))) # Do codegen for all the descriptors - cgthings.extend([CGDescriptor(x) for x in descriptors]) + cgthings.extend( + [CGDescriptor(x, config.attributeTemplates) for x in descriptors] + ) # Do codegen for all the callback interfaces. cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors]) @@ -19324,7 +19663,7 @@ class CGExampleGetter(CGNativeMember): self, descriptor, attr, - CGSpecializedGetter.makeNativeName(descriptor, attr), + CGSpecializedGetterCommon.makeNativeName(descriptor, attr), (attr.type, []), descriptor.getExtendedAttributes(attr, getter=True), ) @@ -19349,7 +19688,7 @@ class CGExampleSetter(CGNativeMember): self, descriptor, attr, - CGSpecializedSetter.makeNativeName(descriptor, attr), + CGSpecializedSetterCommon.makeNativeName(descriptor, attr), ( BuiltinTypes[IDLBuiltinType.Types.undefined], [FakeArgument(attr.type)], @@ -19472,7 +19811,7 @@ class CGBindingImplClass(CGClass): m for m in iface.members if m.isAttr() - and CGSpecializedGetter.makeNativeName(descriptor, m) == "Length" + and CGSpecializedGetterCommon.makeNativeName(descriptor, m) == "Length" ) if not haveLengthAttr: self.methodDecls.append( @@ -20023,7 +20362,7 @@ class CGJSImplGetter(CGJSImplMember): self, descriptor, attr, - CGSpecializedGetter.makeNativeName(descriptor, attr), + CGSpecializedGetterCommon.makeNativeName(descriptor, attr), (attr.type, []), descriptor.getExtendedAttributes(attr, getter=True), passJSBitsAsNeeded=False, @@ -20048,7 +20387,7 @@ class CGJSImplSetter(CGJSImplMember): self, descriptor, attr, - CGSpecializedSetter.makeNativeName(descriptor, attr), + CGSpecializedSetterCommon.makeNativeName(descriptor, attr), ( BuiltinTypes[IDLBuiltinType.Types.undefined], [FakeArgument(attr.type)], @@ -23592,7 +23931,7 @@ class CGEventGetter(CGNativeMember): self, descriptor, attr, - CGSpecializedGetter.makeNativeName(descriptor, attr), + CGSpecializedGetterCommon.makeNativeName(descriptor, attr), (attr.type, []), ea, resultNotAddRefed=not attr.type.isSequence(), @@ -23932,7 +24271,7 @@ class CGEventClass(CGBindingImplClass): # either. extraMethods.append( ClassMethod( - CGSpecializedGetter.makeNativeName(descriptor, m), + CGSpecializedGetterCommon.makeNativeName(descriptor, m), "void", [Argument("JS::MutableHandle", "aRetVal")], const=True, diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 1bfdcdd1002c..2e48e0ee0399 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -52,6 +52,31 @@ class Configuration(DescriptorProvider): exec(io.open(filename, encoding="utf-8").read(), glbl) config = glbl["DOMInterfaces"] + class IDLAttrGetterOrSetterTemplate: + def __init__(self, template, getter, setter, argument, attrName): + class TemplateAdditionalArg: + def __init__(self, type, name, value=None): + self.type = type + self.name = name + self.value = value + + self.descriptor = None + self.usedInOtherInterfaces = False + self.getter = getter + self.setter = setter + self.argument = TemplateAdditionalArg(*argument) + self.attrNameString = attrName + self.attr = None + + self.attributeTemplates = dict() + attributeTemplatesByInterface = dict() + for interface, templates in glbl["TemplatedAttributes"].items(): + for template in templates: + name = template.get("template") + t = IDLAttrGetterOrSetterTemplate(**template) + self.attributeTemplates[name] = t + attributeTemplatesByInterface.setdefault(interface, list()).append(t) + webRoots = tuple(map(os.path.normpath, webRoots)) def isInWebIDLRoot(path): @@ -137,7 +162,12 @@ class Configuration(DescriptorProvider): entry = config.get(iface.identifier.name, {}) assert not isinstance(entry, list) - desc = Descriptor(self, iface, entry) + desc = Descriptor( + self, + iface, + entry, + attributeTemplatesByInterface.get(iface.identifier.name), + ) self.descriptors.append(desc) # Setting up descriptorsByName while iterating through interfaces # means we can get the nativeType of iterable interfaces without @@ -274,6 +304,183 @@ class Configuration(DescriptorProvider): offsets = accumulate(map(lambda n: len(n) + 1, names), initial=0) self.namesStringOffsets = list(zip(names, offsets)) + allTemplatedAttributes = ( + (m, d) + for d in self.descriptors + if not d.interface.isExternal() + for m in d.interface.members + if m.isAttr() and m.getExtendedAttribute("BindingTemplate") is not None + ) + # attributesPerTemplate will have the template names as keys, and a + # list of tuples as values. Every tuple contains an IDLAttribute and a + # descriptor. + attributesPerTemplate = dict() + for m, d in allTemplatedAttributes: + t = m.getExtendedAttribute("BindingTemplate") + if isinstance(t[0], list): + t = t[0] + l = attributesPerTemplate.setdefault(t[0], list()) + # We want the readonly attributes last, because we use the first + # attribute in the list as the canonical attribute for the + # template, and if there are any writable attributes the + # template should have support for that. + if not m.readonly: + l.insert(0, (m, d)) + else: + l.append((m, d)) + + for name, attributes in attributesPerTemplate.items(): + # We use the first attribute to generate a canonical implementation + # of getter and setter. + firstAttribute, firstDescriptor = attributes[0] + template = self.attributeTemplates.get(name) + if template is None: + raise TypeError( + "Unknown BindingTemplate with name %s for %s on %s" + % ( + name, + firstAttribute.identifier.name, + firstDescriptor.interface.identifier.name, + ) + ) + + # This mimics a real IDL attribute for templated bindings. + class TemplateIDLAttribute: + def __init__(self, attr): + assert attr.isAttr() + assert not attr.isMaplikeOrSetlikeAttr() + assert not attr.slotIndices + + self.identifier = attr.identifier + self.type = attr.type + self.extendedAttributes = attr.getExtendedAttributes() + self.slotIndices = None + + def getExtendedAttribute(self, name): + return self.extendedAttributes.get(name) + + def isAttr(self): + return True + + def isMaplikeOrSetlikeAttr(self): + return False + + def isMethod(self): + return False + + def isStatic(self): + return False + + template.attr = TemplateIDLAttribute(firstAttribute) + + def filterExtendedAttributes(extendedAttributes): + # These are the extended attributes that we allow to have + # different values among all atributes that use the same + # template. + ignoredAttributes = { + "BindingTemplate", + "BindingAlias", + "Pure", + "Pref", + "Func", + "Throws", + "GetterThrows", + "SetterThrows", + } + return dict( + filter( + lambda i: i[0] not in ignoredAttributes, + extendedAttributes.items(), + ) + ) + + firstExtAttrs = filterExtendedAttributes( + firstAttribute.getExtendedAttributes() + ) + + for a, d in attributes: + # We want to make sure all getters or setters grouped by a + # template have the same WebIDL signatures, so make sure + # their types are the same. + if template.attr.type != a.type: + raise TypeError( + "%s on %s and %s on %s have different type, but they're using the same template %s." + % ( + firstAttribute.identifier.name, + firstDescriptor.interface.identifier.name, + a.identifier.name, + d.interface.identifier.name, + name, + ) + ) + + extAttrs = filterExtendedAttributes(a.getExtendedAttributes()) + if template.attr.extendedAttributes != extAttrs: + for k in extAttrs.keys() - firstExtAttrs.keys(): + raise TypeError( + "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s." + % ( + a.identifier.name, + d.interface.identifier.name, + k, + firstAttribute.identifier.name, + firstDescriptor.interface.identifier.name, + name, + ) + ) + for k in firstExtAttrs.keys() - extAttrs.keys(): + raise TypeError( + "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s." + % ( + firstAttribute.identifier.name, + firstDescriptor.interface.identifier.name, + k, + a.identifier.name, + d.interface.identifier.name, + name, + ) + ) + for (k, v) in firstExtAttrs.items(): + if extAttrs[k] != v: + raise TypeError( + "%s on %s and %s on %s have different values for extended attribute %s, but they're using the same template %s." + % ( + firstAttribute.identifier.name, + firstDescriptor.interface.identifier.name, + a.identifier.name, + d.interface.identifier.name, + k, + name, + ) + ) + + def sameThrows(getter=False, setter=False): + extAttrs1 = firstDescriptor.getExtendedAttributes( + firstAttribute, getter=getter, setter=setter + ) + extAttrs2 = d.getExtendedAttributes(a, getter=getter, setter=setter) + return ("needsErrorResult" in extAttrs1) == ( + "needsErrorResult" in extAttrs2 + ) + + if not sameThrows(getter=True) or ( + not a.readonly and not sameThrows(setter=True) + ): + raise TypeError( + "%s on %s and %s on %s have different annotations about throwing, but they're using the same template %s." + % ( + firstAttribute.identifier.name, + firstDescriptor.interface.identifier.name, + a.identifier.name, + d.interface.identifier.name, + name, + ) + ) + + for name, template in self.attributeTemplates.items(): + if template.attr is None: + raise TypeError("Template %s is unused, please remove it." % name) + def getInterface(self, ifname): return self.interfaces[ifname] @@ -427,10 +634,14 @@ class Descriptor(DescriptorProvider): Represents a single descriptor for an interface. See Bindings.conf. """ - def __init__(self, config, interface, desc): + def __init__(self, config, interface, desc, attributeTemplates): DescriptorProvider.__init__(self) self.config = config self.interface = interface + self.attributeTemplates = attributeTemplates + if self.attributeTemplates is not None: + for t in self.attributeTemplates: + t.descriptor = self self.wantsXrays = not interface.isExternal() and interface.isExposedInWindow() diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h index 8b2f7e6164c7..d2003c1d9bee 100644 --- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -464,6 +464,11 @@ class TErrorResult { // hopefully it's all temporary until we sort out the EME bits. friend class dom::Promise; + // Implementation of MaybeSetPendingException for the case when we're a + // failure result. See documentation of MaybeSetPendingException for the + // "context" argument. + void SetPendingException(JSContext* cx, const char* context); + private: #ifdef DEBUG enum UnionState { @@ -567,11 +572,6 @@ class TErrorResult { // touching the union anymore. void ClearUnionData(); - // Implementation of MaybeSetPendingException for the case when we're a - // failure result. See documentation of MaybeSetPendingException for the - // "context" argument. - void SetPendingException(JSContext* cx, const char* context); - // Methods for setting various specific kinds of pending exceptions. See // documentation of MaybeSetPendingException for the "context" argument. void SetPendingExceptionWithMessage(JSContext* cx, const char* context); @@ -828,13 +828,47 @@ class CopyableErrorResult inline ErrorResult::ErrorResult(CopyableErrorResult&& aRHS) : ErrorResult(reinterpret_cast(aRHS)) {} -namespace dom { -namespace binding_detail { +namespace dom::binding_detail { + +enum class ErrorFor { + getter, + setter, +}; + +template +struct ErrorDescriptionFor { + const char* mInterface; + const char* mMember; +}; + class FastErrorResult : public mozilla::binding_danger::TErrorResult< mozilla::binding_danger::JustAssertCleanupPolicy> { + public: + using TErrorResult::MaybeSetPendingException; + + template + [[nodiscard]] bool MaybeSetPendingException( + JSContext* aCx, const ErrorDescriptionFor& aDescription) { + WouldReportJSException(); + if (!Failed()) { + return false; + } + + nsAutoCString description(aDescription.mInterface); + description.Append('.'); + description.Append(aDescription.mMember); + if constexpr (ErrorType == ErrorFor::getter) { + description.AppendLiteral(" getter"); + } else { + static_assert(ErrorType == ErrorFor::setter); + description.AppendLiteral(" setter"); + } + SetPendingException(aCx, description.get()); + return true; + } }; -} // namespace binding_detail -} // namespace dom + +} // namespace dom::binding_detail // We want an OOMReporter class that has the following properties: // diff --git a/dom/bindings/GenerateCSS2PropertiesWebIDL.py b/dom/bindings/GenerateCSS2PropertiesWebIDL.py index e7bd1be46e25..29926134bc1c 100644 --- a/dom/bindings/GenerateCSS2PropertiesWebIDL.py +++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py @@ -35,9 +35,13 @@ def generate(output, idlFilename, dataFile): # We already added this as a BindingAlias for the original prop. continue + propId = p.prop_id + else: + propId = p.id # Unfortunately, even some of the getters here are fallible # (e.g. on nsComputedDOMStyle). extendedAttrs = [ + "BindingTemplate=(CSS2Property, eCSSProperty_%s)" % propId, "CEReactions", "Throws", "SetterNeedsSubjectPrincipal=NonSystem", diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index cf6240c213ba..18f5c76e7054 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -5725,6 +5725,7 @@ class IDLAttribute(IDLInterfaceMember): or identifier == "ReturnValueNeedsContainsHack" or identifier == "BinaryName" or identifier == "NonEnumerable" + or identifier == "BindingTemplate" ): # Known attributes that we don't need to do anything with here pass @@ -5735,6 +5736,9 @@ class IDLAttribute(IDLInterfaceMember): ) IDLInterfaceMember.handleExtendedAttribute(self, attr) + def getExtendedAttributes(self): + return self._extendedAttrDict + def resolve(self, parentScope): assert isinstance(parentScope, IDLScope) self.type.resolveType(parentScope) diff --git a/dom/docs/webIdlBindings/index.md b/dom/docs/webIdlBindings/index.md index 186a87f06884..2585f3e6115c 100644 --- a/dom/docs/webIdlBindings/index.md +++ b/dom/docs/webIdlBindings/index.md @@ -1390,6 +1390,25 @@ implementing `MyInterface`. Multiple `[BindingAlias]` extended attributes can be used on a single attribute. +### `[BindingTemplate=(name, value)]` + +This extended attribute can be specified on an attribute, and causes the getter +and setter for this attribute to forward to a common generated implementation, +shared with all other attributes that have a `[BindingTemplate]` with the same +value for the `name` argument. The `TemplatedAttributes` dictionary in +Bindings.conf needs to contain a definition for the template with the name +`name`. The `value` will be passed as an argument when calling the common +generated implementation. + +This is aimed at very specialized use cases where an interface has a +large number of attributes that all have the same type, and for which we have a +native implementation that's common to all these attributes, and typically uses +some id based on the attribute's name in the implementation. All the attributes +that use the same template need to mostly have the same extended attributes, +except form a small number that are allowed to differ (`[BindingTemplate]`, +`[BindingAlias]`, `[Pure]`, [`Pref`] and [`Func`], and the annotations for +whether the getter and setter throws exceptions). + ### `[ChromeOnly]` This extended attribute can be specified on any method, attribute, or diff --git a/layout/style/nsDOMCSSDeclaration.h b/layout/style/nsDOMCSSDeclaration.h index 956984a13e38..a1cc7b473c5f 100644 --- a/layout/style/nsDOMCSSDeclaration.h +++ b/layout/style/nsDOMCSSDeclaration.h @@ -65,6 +65,10 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration { */ virtual nsresult GetPropertyValue(const nsCSSPropertyID aPropID, nsACString& aValue); + void GetPropertyValue(const nsCSSPropertyID aPropID, nsACString& aValue, + mozilla::ErrorResult& aRv) { + aRv = GetPropertyValue(aPropID, aValue); + } /** * Method analogous to CSSStyleDeclaration::SetProperty. This @@ -93,32 +97,6 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration { uint32_t Length() override; // WebIDL interface for CSS2Properties -#define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) publicname_ -#define CSS_PROP(id_, method_) \ - void Get##method_(nsACString& aValue, mozilla::ErrorResult& rv) { \ - rv = GetPropertyValue(eCSSProperty_##id_, aValue); \ - } \ - \ - void Set##method_(const nsACString& aValue, nsIPrincipal* aSubjectPrincipal, \ - mozilla::ErrorResult& aRv) { \ - SetPropertyValue(eCSSProperty_##id_, aValue, aSubjectPrincipal, aRv); \ - } - -#define CSS_PROP_LIST_EXCLUDE_INTERNAL -#define CSS_PROP_LIST_EXCLUDE_NOT_IN_STYLE -#define CSS_PROP_LONGHAND(name_, id_, method_, ...) CSS_PROP(id_, method_) -#define CSS_PROP_SHORTHAND(name_, id_, method_, ...) CSS_PROP(id_, method_) -#define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, ...) \ - CSS_PROP(id_, method_) -#include "mozilla/ServoCSSPropList.h" -#undef CSS_PROP_ALIAS -#undef CSS_PROP_SHORTHAND -#undef CSS_PROP_LONGHAND -#undef CSS_PROP_LIST_EXCLUDE_INTERNAL -#undef CSS_PROP_LIST_EXCLUDE_NOT_IN_STYLE -#undef CSS_PROP -#undef CSS_PROP_PUBLIC_OR_PRIVATE - virtual void IndexedGetter(uint32_t aIndex, bool& aFound, nsACString& aPropName) override;