forked from mirrors/gecko-dev
Bug 1803752 - Make CSS2Properties getters and setters use a common generated implementation. r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D181106
This commit is contained in:
parent
4a6f4b7aa1
commit
36003fd793
8 changed files with 722 additions and 105 deletions
|
|
@ -2125,3 +2125,31 @@ addExternalIface('nsISessionStoreRestoreData',
|
||||||
headerFile='nsISessionStoreRestoreData.h', notflattened=True)
|
headerFile='nsISessionStoreRestoreData.h', notflattened=True)
|
||||||
addExternalIface('nsIScreen', nativeType='nsIScreen',
|
addExternalIface('nsIScreen', nativeType='nsIScreen',
|
||||||
headerFile='nsIScreen.h', notflattened=True)
|
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)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1927,7 +1927,7 @@ class CGAbstractMethod(CGThing):
|
||||||
prologue += indent(
|
prologue += indent(
|
||||||
fill(
|
fill(
|
||||||
"""
|
"""
|
||||||
BindingCallContext ${cxname}(cx_, "${label}");
|
BindingCallContext ${cxname}(cx_, ${label});
|
||||||
""",
|
""",
|
||||||
cxname=cxname,
|
cxname=cxname,
|
||||||
label=error_reporting_label,
|
label=error_reporting_label,
|
||||||
|
|
@ -8991,6 +8991,14 @@ class CGPerSignatureCall(CGThing):
|
||||||
dontSetSlot should be set to True if the value should not be cached in a
|
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
|
slot (even if the attribute is marked as StoreInSlot or Cached in the
|
||||||
WebIDL).
|
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
|
# XXXbz For now each entry in the argument list is either an
|
||||||
|
|
@ -9015,6 +9023,8 @@ class CGPerSignatureCall(CGThing):
|
||||||
objectName="obj",
|
objectName="obj",
|
||||||
dontSetSlot=False,
|
dontSetSlot=False,
|
||||||
extendedAttributes=None,
|
extendedAttributes=None,
|
||||||
|
errorReportingLabel=None,
|
||||||
|
additionalArgsPre=[],
|
||||||
):
|
):
|
||||||
assert idlNode.isMethod() == (not getter and not setter)
|
assert idlNode.isMethod() == (not getter and not setter)
|
||||||
assert idlNode.isAttr() == (getter or setter)
|
assert idlNode.isAttr() == (getter or setter)
|
||||||
|
|
@ -9324,6 +9334,7 @@ class CGPerSignatureCall(CGThing):
|
||||||
assert setter
|
assert setter
|
||||||
cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode))
|
cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode))
|
||||||
else:
|
else:
|
||||||
|
if errorReportingLabel is None:
|
||||||
context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
|
context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
|
||||||
if getter:
|
if getter:
|
||||||
context = context + " getter"
|
context = context + " getter"
|
||||||
|
|
@ -9332,6 +9343,8 @@ class CGPerSignatureCall(CGThing):
|
||||||
# Callee expects a quoted string for the context if
|
# Callee expects a quoted string for the context if
|
||||||
# there's a context.
|
# there's a context.
|
||||||
context = '"%s"' % context
|
context = '"%s"' % context
|
||||||
|
else:
|
||||||
|
context = errorReportingLabel
|
||||||
|
|
||||||
if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"):
|
if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"):
|
||||||
[
|
[
|
||||||
|
|
@ -9348,7 +9361,7 @@ class CGPerSignatureCall(CGThing):
|
||||||
needsCallerType(idlNode),
|
needsCallerType(idlNode),
|
||||||
isChromeOnly(idlNode),
|
isChromeOnly(idlNode),
|
||||||
args,
|
args,
|
||||||
argsPre,
|
argsPre + additionalArgsPre,
|
||||||
returnType,
|
returnType,
|
||||||
self.extendedAttributes,
|
self.extendedAttributes,
|
||||||
descriptor,
|
descriptor,
|
||||||
|
|
@ -10227,6 +10240,8 @@ class CGGetterCall(CGPerSignatureCall):
|
||||||
nativeMethodName,
|
nativeMethodName,
|
||||||
descriptor,
|
descriptor,
|
||||||
attr,
|
attr,
|
||||||
|
errorReportingLabel=None,
|
||||||
|
argsPre=[],
|
||||||
dontSetSlot=False,
|
dontSetSlot=False,
|
||||||
extendedAttributes=None,
|
extendedAttributes=None,
|
||||||
):
|
):
|
||||||
|
|
@ -10251,6 +10266,8 @@ class CGGetterCall(CGPerSignatureCall):
|
||||||
useCounterName=useCounterName,
|
useCounterName=useCounterName,
|
||||||
dontSetSlot=dontSetSlot,
|
dontSetSlot=dontSetSlot,
|
||||||
extendedAttributes=extendedAttributes,
|
extendedAttributes=extendedAttributes,
|
||||||
|
errorReportingLabel=errorReportingLabel,
|
||||||
|
additionalArgsPre=argsPre,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10287,7 +10304,15 @@ class CGSetterCall(CGPerSignatureCall):
|
||||||
setter.
|
setter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, argType, nativeMethodName, descriptor, attr):
|
def __init__(
|
||||||
|
self,
|
||||||
|
argType,
|
||||||
|
nativeMethodName,
|
||||||
|
descriptor,
|
||||||
|
attr,
|
||||||
|
errorReportingLabel=None,
|
||||||
|
argsPre=[],
|
||||||
|
):
|
||||||
if attr.getExtendedAttribute("UseCounter"):
|
if attr.getExtendedAttribute("UseCounter"):
|
||||||
useCounterName = "%s_%s_setter" % (
|
useCounterName = "%s_%s_setter" % (
|
||||||
descriptor.interface.identifier.name,
|
descriptor.interface.identifier.name,
|
||||||
|
|
@ -10307,6 +10332,8 @@ class CGSetterCall(CGPerSignatureCall):
|
||||||
attr,
|
attr,
|
||||||
setter=True,
|
setter=True,
|
||||||
useCounterName=useCounterName,
|
useCounterName=useCounterName,
|
||||||
|
errorReportingLabel=errorReportingLabel,
|
||||||
|
additionalArgsPre=argsPre,
|
||||||
)
|
)
|
||||||
|
|
||||||
def wrap_return_value(self):
|
def wrap_return_value(self):
|
||||||
|
|
@ -10588,7 +10615,7 @@ class CGSpecializedMethod(CGAbstractStaticMethod):
|
||||||
descriptor, idlMethod
|
descriptor, idlMethod
|
||||||
):
|
):
|
||||||
return None
|
return None
|
||||||
return GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
|
return '"%s"' % GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
|
||||||
|
|
||||||
def error_reporting_label(self):
|
def error_reporting_label(self):
|
||||||
return CGSpecializedMethod.error_reporting_label_helper(
|
return CGSpecializedMethod.error_reporting_label_helper(
|
||||||
|
|
@ -10598,7 +10625,9 @@ class CGSpecializedMethod(CGAbstractStaticMethod):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def makeNativeName(descriptor, method):
|
def makeNativeName(descriptor, method):
|
||||||
if method.underlyingAttr:
|
if method.underlyingAttr:
|
||||||
return CGSpecializedGetter.makeNativeName(descriptor, method.underlyingAttr)
|
return CGSpecializedGetterCommon.makeNativeName(
|
||||||
|
descriptor, method.underlyingAttr
|
||||||
|
)
|
||||||
name = method.identifier.name
|
name = method.identifier.name
|
||||||
return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic()))
|
return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic()))
|
||||||
|
|
||||||
|
|
@ -10991,21 +11020,25 @@ class CGStaticMethod(CGAbstractStaticBindingMethod):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CGSpecializedGetter(CGAbstractStaticMethod):
|
class CGSpecializedGetterCommon(CGAbstractStaticMethod):
|
||||||
"""
|
"""
|
||||||
A class for generating the code for a specialized attribute getter
|
A class for generating the code for a specialized attribute getter
|
||||||
that the JIT can call with lower overhead.
|
that the JIT can call with lower overhead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, descriptor, attr):
|
def __init__(
|
||||||
self.attr = attr
|
self,
|
||||||
name = "get_" + IDLToCIdentifier(attr.identifier.name)
|
descriptor,
|
||||||
args = [
|
name,
|
||||||
Argument("JSContext*", "cx"),
|
nativeName,
|
||||||
Argument("JS::Handle<JSObject*>", "obj"),
|
attr,
|
||||||
Argument("void*", "void_self"),
|
args,
|
||||||
Argument("JSJitGetterCallArgs", "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
|
# StoreInSlot attributes have their getters called from Wrap(). We
|
||||||
# really hope they can't run script, and don't want to annotate Wrap()
|
# 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
|
# methods as doing that anyway, so let's not annotate them as
|
||||||
|
|
@ -11015,7 +11048,7 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
||||||
descriptor,
|
descriptor,
|
||||||
name,
|
name,
|
||||||
"bool",
|
"bool",
|
||||||
args,
|
args + self.additionalArgs,
|
||||||
canRunScript=not attr.getExtendedAttribute("StoreInSlot"),
|
canRunScript=not attr.getExtendedAttribute("StoreInSlot"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -11043,7 +11076,13 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
||||||
# backing object from the slot, this requires its own generator.
|
# backing object from the slot, this requires its own generator.
|
||||||
return prefix + getObservableArrayGetterBody(self.descriptor, self.attr)
|
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
|
type = self.attr.type
|
||||||
if self.attr.getExtendedAttribute("CrossOriginReadable"):
|
if self.attr.getExtendedAttribute("CrossOriginReadable"):
|
||||||
remoteType = type
|
remoteType = type
|
||||||
|
|
@ -11076,6 +11115,8 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
||||||
nativeName,
|
nativeName,
|
||||||
self.descriptor,
|
self.descriptor,
|
||||||
self.attr,
|
self.attr,
|
||||||
|
self.errorReportingLabel,
|
||||||
|
argsPre=[a.name for a in self.additionalArgs],
|
||||||
dontSetSlot=True,
|
dontSetSlot=True,
|
||||||
extendedAttributes=extendedAttributes,
|
extendedAttributes=extendedAttributes,
|
||||||
).define(),
|
).define(),
|
||||||
|
|
@ -11146,21 +11187,30 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
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
|
interface_name = self.descriptor.interface.identifier.name
|
||||||
attr_name = self.attr.identifier.name
|
|
||||||
return fill(
|
return fill(
|
||||||
"""
|
"""
|
||||||
AUTO_PROFILER_LABEL_DYNAMIC_FAST(
|
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::STRING_TEMPLATE_GETTER) |
|
||||||
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
|
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
|
||||||
""",
|
""",
|
||||||
interface_name=interface_name,
|
interface_name=interface_name,
|
||||||
attr_name=attr_name,
|
attr_name=profilerLabel,
|
||||||
)
|
)
|
||||||
|
|
||||||
def error_reporting_label(self):
|
def error_reporting_label(self):
|
||||||
|
|
@ -11179,6 +11229,112 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
||||||
return nativeName
|
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<JSObject*>", "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<JSObject*>", "obj"),
|
||||||
|
Argument("void*", "void_self"),
|
||||||
|
Argument("JSJitGetterCallArgs", "args"),
|
||||||
|
]
|
||||||
|
errorDescription = (
|
||||||
|
'ErrorDescriptionFor<ErrorFor::getter>{ "%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<JSObject*>", "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):
|
class CGGetterPromiseWrapper(CGAbstractStaticMethod):
|
||||||
"""
|
"""
|
||||||
A class for generating a wrapper around another getter that will
|
A class for generating a wrapper around another getter that will
|
||||||
|
|
@ -11222,7 +11378,9 @@ class CGStaticGetter(CGAbstractStaticBindingMethod):
|
||||||
CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
|
CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
|
||||||
|
|
||||||
def generate_code(self):
|
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)
|
return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
|
||||||
|
|
||||||
def auto_profiler_label(self):
|
def auto_profiler_label(self):
|
||||||
|
|
@ -11244,29 +11402,44 @@ class CGStaticGetter(CGAbstractStaticBindingMethod):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class CGSpecializedSetter(CGAbstractStaticMethod):
|
class CGSpecializedSetterCommon(CGAbstractStaticMethod):
|
||||||
"""
|
"""
|
||||||
A class for generating the code for a specialized attribute setter
|
A class for generating the code for a specialized attribute setter
|
||||||
that the JIT can call with lower overhead.
|
that the JIT can call with lower overhead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, descriptor, attr):
|
def __init__(
|
||||||
self.attr = attr
|
self,
|
||||||
name = "set_" + IDLToCIdentifier(attr.identifier.name)
|
descriptor,
|
||||||
args = [
|
name,
|
||||||
Argument("JSContext*", "cx"),
|
nativeName,
|
||||||
Argument("JS::Handle<JSObject*>", "obj"),
|
attr,
|
||||||
Argument("void*", "void_self"),
|
args,
|
||||||
Argument("JSJitSetterCallArgs", "args"),
|
errorReportingLabel=None,
|
||||||
]
|
additionalArg=None,
|
||||||
|
):
|
||||||
|
self.nativeName = nativeName
|
||||||
|
self.errorReportingLabel = errorReportingLabel
|
||||||
|
self.additionalArgs = [] if additionalArg is None else [additionalArg]
|
||||||
CGAbstractStaticMethod.__init__(
|
CGAbstractStaticMethod.__init__(
|
||||||
self, descriptor, name, "bool", args, canRunScript=True
|
self,
|
||||||
|
descriptor,
|
||||||
|
name,
|
||||||
|
"bool",
|
||||||
|
args + self.additionalArgs,
|
||||||
|
canRunScript=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def definition_body(self):
|
def definition_body(self):
|
||||||
nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr)
|
|
||||||
type = self.attr.type
|
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 = ""
|
prefix = ""
|
||||||
if self.attr.getExtendedAttribute("CrossOriginWritable"):
|
if self.attr.getExtendedAttribute("CrossOriginWritable"):
|
||||||
if type.isGeckoInterface() and not type.unroll().inner.isExternal():
|
if type.isGeckoInterface() and not type.unroll().inner.isExternal():
|
||||||
|
|
@ -11300,18 +11473,19 @@ class CGSpecializedSetter(CGAbstractStaticMethod):
|
||||||
call=call,
|
call=call,
|
||||||
)
|
)
|
||||||
|
|
||||||
def auto_profiler_label(self):
|
def auto_profiler_label(self, profilerLabel=None):
|
||||||
interface_name = self.descriptor.interface.identifier.name
|
interface_name = self.descriptor.interface.identifier.name
|
||||||
attr_name = self.attr.identifier.name
|
if profilerLabel is None:
|
||||||
|
profilerLabel = '"' + self.attr.identifier.name + '"'
|
||||||
return fill(
|
return fill(
|
||||||
"""
|
"""
|
||||||
AUTO_PROFILER_LABEL_DYNAMIC_FAST(
|
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::STRING_TEMPLATE_SETTER) |
|
||||||
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
|
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
|
||||||
""",
|
""",
|
||||||
interface_name=interface_name,
|
interface_name=interface_name,
|
||||||
attr_name=attr_name,
|
attr_name=profilerLabel,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -11322,14 +11496,19 @@ class CGSpecializedSetter(CGAbstractStaticMethod):
|
||||||
attr.type, descriptor, allowTreatNonCallableAsNull=True
|
attr.type, descriptor, allowTreatNonCallableAsNull=True
|
||||||
):
|
):
|
||||||
return None
|
return None
|
||||||
return (
|
return '"%s"' % (
|
||||||
GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter"
|
GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter"
|
||||||
)
|
)
|
||||||
|
|
||||||
def error_reporting_label(self):
|
def error_reporting_label(self):
|
||||||
return CGSpecializedSetter.error_reporting_label_helper(
|
errorReportingLabel = CGSpecializedSetterCommon.error_reporting_label_helper(
|
||||||
self.descriptor, self.attr
|
self.descriptor, self.attr
|
||||||
)
|
)
|
||||||
|
if errorReportingLabel is None:
|
||||||
|
return None
|
||||||
|
if self.errorReportingLabel:
|
||||||
|
return self.errorReportingLabel
|
||||||
|
return errorReportingLabel
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def makeNativeName(descriptor, attr):
|
def makeNativeName(descriptor, attr):
|
||||||
|
|
@ -11337,6 +11516,114 @@ class CGSpecializedSetter(CGAbstractStaticMethod):
|
||||||
return "Set" + MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
|
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<JSObject*>", "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<JSObject*>", "obj"),
|
||||||
|
Argument("void*", "void_self"),
|
||||||
|
Argument("JSJitSetterCallArgs", "args"),
|
||||||
|
]
|
||||||
|
errorDescription = (
|
||||||
|
'ErrorDescriptionFor<ErrorFor::setter>{ "%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<JSObject*>", "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):
|
class CGStaticSetter(CGAbstractStaticBindingMethod):
|
||||||
"""
|
"""
|
||||||
A class for generating the C++ code for an IDL static attribute setter.
|
A class for generating the C++ code for an IDL static attribute setter.
|
||||||
|
|
@ -11348,7 +11635,9 @@ class CGStaticSetter(CGAbstractStaticBindingMethod):
|
||||||
CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
|
CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
|
||||||
|
|
||||||
def generate_code(self):
|
def generate_code(self):
|
||||||
nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr)
|
nativeName = CGSpecializedSetterCommon.makeNativeName(
|
||||||
|
self.descriptor, self.attr
|
||||||
|
)
|
||||||
checkForArg = CGGeneric(
|
checkForArg = CGGeneric(
|
||||||
fill(
|
fill(
|
||||||
"""
|
"""
|
||||||
|
|
@ -11377,7 +11666,7 @@ class CGStaticSetter(CGAbstractStaticBindingMethod):
|
||||||
)
|
)
|
||||||
|
|
||||||
def error_reporting_label(self):
|
def error_reporting_label(self):
|
||||||
return CGSpecializedSetter.error_reporting_label_helper(
|
return CGSpecializedSetterCommon.error_reporting_label_helper(
|
||||||
self.descriptor, self.attr
|
self.descriptor, self.attr
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -11418,7 +11707,7 @@ class CGSpecializedForwardingSetter(CGSpecializedSetter):
|
||||||
|
|
||||||
def error_reporting_label(self):
|
def error_reporting_label(self):
|
||||||
# We always need to be able to throw.
|
# We always need to be able to throw.
|
||||||
return (
|
return '"%s"' % (
|
||||||
GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False)
|
GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False)
|
||||||
+ " setter"
|
+ " setter"
|
||||||
)
|
)
|
||||||
|
|
@ -14833,7 +15122,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
|
||||||
if error_label:
|
if error_label:
|
||||||
cxDecl = fill(
|
cxDecl = fill(
|
||||||
"""
|
"""
|
||||||
BindingCallContext cx(cx_, "${error_label}");
|
BindingCallContext cx(cx_, ${error_label});
|
||||||
""",
|
""",
|
||||||
error_label=error_label,
|
error_label=error_label,
|
||||||
)
|
)
|
||||||
|
|
@ -14885,7 +15174,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
|
||||||
if error_label:
|
if error_label:
|
||||||
set += fill(
|
set += fill(
|
||||||
"""
|
"""
|
||||||
BindingCallContext cx(cx_, "${error_label}");
|
BindingCallContext cx(cx_, ${error_label});
|
||||||
""",
|
""",
|
||||||
error_label=error_label,
|
error_label=error_label,
|
||||||
)
|
)
|
||||||
|
|
@ -15646,7 +15935,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod):
|
||||||
if error_label:
|
if error_label:
|
||||||
cxDecl = fill(
|
cxDecl = fill(
|
||||||
"""
|
"""
|
||||||
BindingCallContext cx(cx_, "${error_label}");
|
BindingCallContext cx(cx_, ${error_label});
|
||||||
""",
|
""",
|
||||||
error_label=error_label,
|
error_label=error_label,
|
||||||
)
|
)
|
||||||
|
|
@ -15679,7 +15968,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod):
|
||||||
if error_label:
|
if error_label:
|
||||||
cxDecl = fill(
|
cxDecl = fill(
|
||||||
"""
|
"""
|
||||||
BindingCallContext cx(cx_, "${error_label}");
|
BindingCallContext cx(cx_, ${error_label});
|
||||||
""",
|
""",
|
||||||
error_label=error_label,
|
error_label=error_label,
|
||||||
)
|
)
|
||||||
|
|
@ -16313,7 +16602,7 @@ def memberProperties(m, descriptor):
|
||||||
|
|
||||||
|
|
||||||
class CGDescriptor(CGThing):
|
class CGDescriptor(CGThing):
|
||||||
def __init__(self, descriptor):
|
def __init__(self, descriptor, attributeTemplates):
|
||||||
CGThing.__init__(self)
|
CGThing.__init__(self)
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
|
|
@ -16373,10 +16662,23 @@ class CGDescriptor(CGThing):
|
||||||
defaultToJSONMethod = None
|
defaultToJSONMethod = None
|
||||||
needCrossOriginPropertyArrays = False
|
needCrossOriginPropertyArrays = False
|
||||||
unscopableNames = list()
|
unscopableNames = list()
|
||||||
|
|
||||||
for n in descriptor.interface.legacyFactoryFunctions:
|
for n in descriptor.interface.legacyFactoryFunctions:
|
||||||
cgThings.append(
|
cgThings.append(
|
||||||
CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n))
|
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:
|
for m in descriptor.interface.members:
|
||||||
if m.isMethod() and m.identifier.name == "QueryInterface":
|
if m.isMethod() and m.identifier.name == "QueryInterface":
|
||||||
continue
|
continue
|
||||||
|
|
@ -16425,6 +16727,27 @@ class CGDescriptor(CGThing):
|
||||||
assert descriptor.interface.hasInterfaceObject()
|
assert descriptor.interface.hasInterfaceObject()
|
||||||
cgThings.append(CGStaticGetter(descriptor, m))
|
cgThings.append(CGStaticGetter(descriptor, m))
|
||||||
elif descriptor.interface.hasInterfacePrototypeObject():
|
elif descriptor.interface.hasInterfacePrototypeObject():
|
||||||
|
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)
|
specializedGetter = CGSpecializedGetter(descriptor, m)
|
||||||
cgThings.append(specializedGetter)
|
cgThings.append(specializedGetter)
|
||||||
if m.type.isPromise():
|
if m.type.isPromise():
|
||||||
|
|
@ -16438,7 +16761,21 @@ class CGDescriptor(CGThing):
|
||||||
assert descriptor.interface.hasInterfaceObject()
|
assert descriptor.interface.hasInterfaceObject()
|
||||||
cgThings.append(CGStaticSetter(descriptor, m))
|
cgThings.append(CGStaticSetter(descriptor, m))
|
||||||
elif descriptor.interface.hasInterfacePrototypeObject():
|
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:
|
if props.isCrossOriginSetter:
|
||||||
needCrossOriginPropertyArrays = True
|
needCrossOriginPropertyArrays = True
|
||||||
elif m.getExtendedAttribute("PutForwards"):
|
elif m.getExtendedAttribute("PutForwards"):
|
||||||
|
|
@ -18716,7 +19053,9 @@ class CGBindingRoot(CGThing):
|
||||||
cgthings.append(CGNamespace("binding_detail", CGFastCallback(t)))
|
cgthings.append(CGNamespace("binding_detail", CGFastCallback(t)))
|
||||||
|
|
||||||
# Do codegen for all the descriptors
|
# 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.
|
# Do codegen for all the callback interfaces.
|
||||||
cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
|
cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
|
||||||
|
|
@ -19332,7 +19671,7 @@ class CGExampleGetter(CGNativeMember):
|
||||||
self,
|
self,
|
||||||
descriptor,
|
descriptor,
|
||||||
attr,
|
attr,
|
||||||
CGSpecializedGetter.makeNativeName(descriptor, attr),
|
CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
|
||||||
(attr.type, []),
|
(attr.type, []),
|
||||||
descriptor.getExtendedAttributes(attr, getter=True),
|
descriptor.getExtendedAttributes(attr, getter=True),
|
||||||
)
|
)
|
||||||
|
|
@ -19357,7 +19696,7 @@ class CGExampleSetter(CGNativeMember):
|
||||||
self,
|
self,
|
||||||
descriptor,
|
descriptor,
|
||||||
attr,
|
attr,
|
||||||
CGSpecializedSetter.makeNativeName(descriptor, attr),
|
CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
|
||||||
(
|
(
|
||||||
BuiltinTypes[IDLBuiltinType.Types.undefined],
|
BuiltinTypes[IDLBuiltinType.Types.undefined],
|
||||||
[FakeArgument(attr.type)],
|
[FakeArgument(attr.type)],
|
||||||
|
|
@ -19480,7 +19819,7 @@ class CGBindingImplClass(CGClass):
|
||||||
m
|
m
|
||||||
for m in iface.members
|
for m in iface.members
|
||||||
if m.isAttr()
|
if m.isAttr()
|
||||||
and CGSpecializedGetter.makeNativeName(descriptor, m) == "Length"
|
and CGSpecializedGetterCommon.makeNativeName(descriptor, m) == "Length"
|
||||||
)
|
)
|
||||||
if not haveLengthAttr:
|
if not haveLengthAttr:
|
||||||
self.methodDecls.append(
|
self.methodDecls.append(
|
||||||
|
|
@ -20031,7 +20370,7 @@ class CGJSImplGetter(CGJSImplMember):
|
||||||
self,
|
self,
|
||||||
descriptor,
|
descriptor,
|
||||||
attr,
|
attr,
|
||||||
CGSpecializedGetter.makeNativeName(descriptor, attr),
|
CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
|
||||||
(attr.type, []),
|
(attr.type, []),
|
||||||
descriptor.getExtendedAttributes(attr, getter=True),
|
descriptor.getExtendedAttributes(attr, getter=True),
|
||||||
passJSBitsAsNeeded=False,
|
passJSBitsAsNeeded=False,
|
||||||
|
|
@ -20056,7 +20395,7 @@ class CGJSImplSetter(CGJSImplMember):
|
||||||
self,
|
self,
|
||||||
descriptor,
|
descriptor,
|
||||||
attr,
|
attr,
|
||||||
CGSpecializedSetter.makeNativeName(descriptor, attr),
|
CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
|
||||||
(
|
(
|
||||||
BuiltinTypes[IDLBuiltinType.Types.undefined],
|
BuiltinTypes[IDLBuiltinType.Types.undefined],
|
||||||
[FakeArgument(attr.type)],
|
[FakeArgument(attr.type)],
|
||||||
|
|
@ -23600,7 +23939,7 @@ class CGEventGetter(CGNativeMember):
|
||||||
self,
|
self,
|
||||||
descriptor,
|
descriptor,
|
||||||
attr,
|
attr,
|
||||||
CGSpecializedGetter.makeNativeName(descriptor, attr),
|
CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
|
||||||
(attr.type, []),
|
(attr.type, []),
|
||||||
ea,
|
ea,
|
||||||
resultNotAddRefed=not attr.type.isSequence(),
|
resultNotAddRefed=not attr.type.isSequence(),
|
||||||
|
|
@ -23940,7 +24279,7 @@ class CGEventClass(CGBindingImplClass):
|
||||||
# either.
|
# either.
|
||||||
extraMethods.append(
|
extraMethods.append(
|
||||||
ClassMethod(
|
ClassMethod(
|
||||||
CGSpecializedGetter.makeNativeName(descriptor, m),
|
CGSpecializedGetterCommon.makeNativeName(descriptor, m),
|
||||||
"void",
|
"void",
|
||||||
[Argument("JS::MutableHandle<JS::Value>", "aRetVal")],
|
[Argument("JS::MutableHandle<JS::Value>", "aRetVal")],
|
||||||
const=True,
|
const=True,
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,31 @@ class Configuration(DescriptorProvider):
|
||||||
exec(io.open(filename, encoding="utf-8").read(), glbl)
|
exec(io.open(filename, encoding="utf-8").read(), glbl)
|
||||||
config = glbl["DOMInterfaces"]
|
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))
|
webRoots = tuple(map(os.path.normpath, webRoots))
|
||||||
|
|
||||||
def isInWebIDLRoot(path):
|
def isInWebIDLRoot(path):
|
||||||
|
|
@ -137,7 +162,12 @@ class Configuration(DescriptorProvider):
|
||||||
entry = config.get(iface.identifier.name, {})
|
entry = config.get(iface.identifier.name, {})
|
||||||
assert not isinstance(entry, list)
|
assert not isinstance(entry, list)
|
||||||
|
|
||||||
desc = Descriptor(self, iface, entry)
|
desc = Descriptor(
|
||||||
|
self,
|
||||||
|
iface,
|
||||||
|
entry,
|
||||||
|
attributeTemplatesByInterface.get(iface.identifier.name),
|
||||||
|
)
|
||||||
self.descriptors.append(desc)
|
self.descriptors.append(desc)
|
||||||
# Setting up descriptorsByName while iterating through interfaces
|
# Setting up descriptorsByName while iterating through interfaces
|
||||||
# means we can get the nativeType of iterable interfaces without
|
# 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)
|
offsets = accumulate(map(lambda n: len(n) + 1, names), initial=0)
|
||||||
self.namesStringOffsets = list(zip(names, offsets))
|
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:
|
||||||
|
print("Template %s is unused, please remove it." % name)
|
||||||
|
|
||||||
def getInterface(self, ifname):
|
def getInterface(self, ifname):
|
||||||
return self.interfaces[ifname]
|
return self.interfaces[ifname]
|
||||||
|
|
||||||
|
|
@ -427,10 +634,14 @@ class Descriptor(DescriptorProvider):
|
||||||
Represents a single descriptor for an interface. See Bindings.conf.
|
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)
|
DescriptorProvider.__init__(self)
|
||||||
self.config = config
|
self.config = config
|
||||||
self.interface = interface
|
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()
|
self.wantsXrays = not interface.isExternal() and interface.isExposedInWindow()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -464,6 +464,11 @@ class TErrorResult {
|
||||||
// hopefully it's all temporary until we sort out the EME bits.
|
// hopefully it's all temporary until we sort out the EME bits.
|
||||||
friend class dom::Promise;
|
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:
|
private:
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
enum UnionState {
|
enum UnionState {
|
||||||
|
|
@ -567,11 +572,6 @@ class TErrorResult {
|
||||||
// touching the union anymore.
|
// touching the union anymore.
|
||||||
void ClearUnionData();
|
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
|
// Methods for setting various specific kinds of pending exceptions. See
|
||||||
// documentation of MaybeSetPendingException for the "context" argument.
|
// documentation of MaybeSetPendingException for the "context" argument.
|
||||||
void SetPendingExceptionWithMessage(JSContext* cx, const char* context);
|
void SetPendingExceptionWithMessage(JSContext* cx, const char* context);
|
||||||
|
|
@ -828,13 +828,47 @@ class CopyableErrorResult
|
||||||
inline ErrorResult::ErrorResult(CopyableErrorResult&& aRHS)
|
inline ErrorResult::ErrorResult(CopyableErrorResult&& aRHS)
|
||||||
: ErrorResult(reinterpret_cast<ErrorResult&&>(aRHS)) {}
|
: ErrorResult(reinterpret_cast<ErrorResult&&>(aRHS)) {}
|
||||||
|
|
||||||
namespace dom {
|
namespace dom::binding_detail {
|
||||||
namespace binding_detail {
|
|
||||||
|
enum class ErrorFor {
|
||||||
|
getter,
|
||||||
|
setter,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <ErrorFor ErrorType>
|
||||||
|
struct ErrorDescriptionFor {
|
||||||
|
const char* mInterface;
|
||||||
|
const char* mMember;
|
||||||
|
};
|
||||||
|
|
||||||
class FastErrorResult : public mozilla::binding_danger::TErrorResult<
|
class FastErrorResult : public mozilla::binding_danger::TErrorResult<
|
||||||
mozilla::binding_danger::JustAssertCleanupPolicy> {
|
mozilla::binding_danger::JustAssertCleanupPolicy> {
|
||||||
|
public:
|
||||||
|
using TErrorResult::MaybeSetPendingException;
|
||||||
|
|
||||||
|
template <ErrorFor ErrorType>
|
||||||
|
[[nodiscard]] bool MaybeSetPendingException(
|
||||||
|
JSContext* aCx, const ErrorDescriptionFor<ErrorType>& 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:
|
// We want an OOMReporter class that has the following properties:
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,13 @@ def generate(output, idlFilename, dataFile):
|
||||||
# We already added this as a BindingAlias for the original prop.
|
# We already added this as a BindingAlias for the original prop.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
propId = p.prop_id
|
||||||
|
else:
|
||||||
|
propId = p.id
|
||||||
# Unfortunately, even some of the getters here are fallible
|
# Unfortunately, even some of the getters here are fallible
|
||||||
# (e.g. on nsComputedDOMStyle).
|
# (e.g. on nsComputedDOMStyle).
|
||||||
extendedAttrs = [
|
extendedAttrs = [
|
||||||
|
"BindingTemplate=(CSS2Property, eCSSProperty_%s)" % propId,
|
||||||
"CEReactions",
|
"CEReactions",
|
||||||
"Throws",
|
"Throws",
|
||||||
"SetterNeedsSubjectPrincipal=NonSystem",
|
"SetterNeedsSubjectPrincipal=NonSystem",
|
||||||
|
|
|
||||||
|
|
@ -5725,6 +5725,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||||
or identifier == "ReturnValueNeedsContainsHack"
|
or identifier == "ReturnValueNeedsContainsHack"
|
||||||
or identifier == "BinaryName"
|
or identifier == "BinaryName"
|
||||||
or identifier == "NonEnumerable"
|
or identifier == "NonEnumerable"
|
||||||
|
or identifier == "BindingTemplate"
|
||||||
):
|
):
|
||||||
# Known attributes that we don't need to do anything with here
|
# Known attributes that we don't need to do anything with here
|
||||||
pass
|
pass
|
||||||
|
|
@ -5735,6 +5736,9 @@ class IDLAttribute(IDLInterfaceMember):
|
||||||
)
|
)
|
||||||
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
||||||
|
|
||||||
|
def getExtendedAttributes(self):
|
||||||
|
return self._extendedAttrDict
|
||||||
|
|
||||||
def resolve(self, parentScope):
|
def resolve(self, parentScope):
|
||||||
assert isinstance(parentScope, IDLScope)
|
assert isinstance(parentScope, IDLScope)
|
||||||
self.type.resolveType(parentScope)
|
self.type.resolveType(parentScope)
|
||||||
|
|
|
||||||
|
|
@ -1390,6 +1390,25 @@ implementing `MyInterface`.
|
||||||
Multiple `[BindingAlias]` extended attributes can be used on a single
|
Multiple `[BindingAlias]` extended attributes can be used on a single
|
||||||
attribute.
|
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]`
|
### `[ChromeOnly]`
|
||||||
|
|
||||||
This extended attribute can be specified on any method, attribute, or
|
This extended attribute can be specified on any method, attribute, or
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,10 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration {
|
||||||
*/
|
*/
|
||||||
virtual nsresult GetPropertyValue(const nsCSSPropertyID aPropID,
|
virtual nsresult GetPropertyValue(const nsCSSPropertyID aPropID,
|
||||||
nsACString& aValue);
|
nsACString& aValue);
|
||||||
|
void GetPropertyValue(const nsCSSPropertyID aPropID, nsACString& aValue,
|
||||||
|
mozilla::ErrorResult& aRv) {
|
||||||
|
aRv = GetPropertyValue(aPropID, aValue);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method analogous to CSSStyleDeclaration::SetProperty. This
|
* Method analogous to CSSStyleDeclaration::SetProperty. This
|
||||||
|
|
@ -93,32 +97,6 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration {
|
||||||
uint32_t Length() override;
|
uint32_t Length() override;
|
||||||
|
|
||||||
// WebIDL interface for CSS2Properties
|
// 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,
|
virtual void IndexedGetter(uint32_t aIndex, bool& aFound,
|
||||||
nsACString& aPropName) override;
|
nsACString& aPropName) override;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue