mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
Bug 1891784 - Support HTML reflected attributes returning FrozenArray. r=edgar
Differential Revision: https://phabricator.services.mozilla.com/D207754
This commit is contained in:
parent
2a61918bb1
commit
a91991024d
@ -9644,8 +9644,10 @@ class CGPerSignatureCall(CGThing):
|
||||
# already-preserved wrapper.
|
||||
if (
|
||||
self.idlNode.getExtendedAttribute("Cached")
|
||||
and self.descriptor.wrapperCache
|
||||
):
|
||||
or self.idlNode.getExtendedAttribute(
|
||||
"ReflectedHTMLAttributeReturningFrozenArray"
|
||||
)
|
||||
) and self.descriptor.wrapperCache:
|
||||
preserveWrapper = dedent(
|
||||
"""
|
||||
PreserveWrapper(self);
|
||||
@ -10254,7 +10256,9 @@ class CGGetterCall(CGPerSignatureCall):
|
||||
argsPre=[],
|
||||
dontSetSlot=False,
|
||||
extendedAttributes=None,
|
||||
preConversionCode=None,
|
||||
):
|
||||
self.preConversionCode = preConversionCode
|
||||
if attr.getExtendedAttribute("UseCounter"):
|
||||
useCounterName = "%s_%s_getter" % (
|
||||
descriptor.interface.identifier.name,
|
||||
@ -10280,6 +10284,12 @@ class CGGetterCall(CGPerSignatureCall):
|
||||
additionalArgsPre=argsPre,
|
||||
)
|
||||
|
||||
def wrap_return_value(self):
|
||||
wrap = CGPerSignatureCall.wrap_return_value(self)
|
||||
if self.preConversionCode is not None:
|
||||
wrap = self.preConversionCode + wrap
|
||||
return wrap
|
||||
|
||||
|
||||
class FakeIdentifier:
|
||||
def __init__(self, name):
|
||||
@ -11134,6 +11144,8 @@ class CGSpecializedGetterCommon(CGAbstractStaticMethod):
|
||||
+ prefix
|
||||
)
|
||||
|
||||
argsPre = [a.name for a in self.additionalArgs]
|
||||
maybeReturnCachedVal = None
|
||||
if self.attr.slotIndices is not None:
|
||||
# We're going to store this return value in a slot on some object,
|
||||
# to cache it. The question is, which object? For dictionary and
|
||||
@ -11178,23 +11190,52 @@ class CGSpecializedGetterCommon(CGAbstractStaticMethod):
|
||||
slotIndex=memberReservedSlot(self.attr, self.descriptor),
|
||||
)
|
||||
|
||||
prefix += fill(
|
||||
"""
|
||||
MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage)) > slotIndex);
|
||||
{
|
||||
// Scope for cachedVal
|
||||
JS::Value cachedVal = JS::GetReservedSlot(slotStorage, slotIndex);
|
||||
if (!cachedVal.isUndefined()) {
|
||||
args.rval().set(cachedVal);
|
||||
// The cached value is in the compartment of slotStorage,
|
||||
// so wrap into the caller compartment as needed.
|
||||
return ${maybeWrap}(cx, args.rval());
|
||||
}
|
||||
}
|
||||
if self.attr.getExtendedAttribute(
|
||||
"ReflectedHTMLAttributeReturningFrozenArray"
|
||||
):
|
||||
argsPre.append("hasCachedValue ? &useCachedValue : nullptr")
|
||||
prefix += dedent(
|
||||
"""
|
||||
MOZ_ASSERT(slotIndex < JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage)));
|
||||
JS::Rooted<JS::Value> cachedVal(cx, JS::GetReservedSlot(slotStorage, slotIndex));
|
||||
bool hasCachedValue = !cachedVal.isUndefined();
|
||||
bool useCachedValue = false;
|
||||
"""
|
||||
)
|
||||
maybeReturnCachedVal = fill(
|
||||
"""
|
||||
MOZ_ASSERT_IF(useCachedValue, hasCachedValue);
|
||||
if (hasCachedValue && useCachedValue) {
|
||||
args.rval().set(cachedVal);
|
||||
// The cached value is in the compartment of slotStorage,
|
||||
// so wrap into the caller compartment as needed.
|
||||
return ${maybeWrap}(cx, args.rval());
|
||||
}
|
||||
|
||||
""",
|
||||
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
|
||||
)
|
||||
${clearCachedValue}(self);
|
||||
|
||||
""",
|
||||
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
|
||||
clearCachedValue=MakeClearCachedValueNativeName(self.attr),
|
||||
)
|
||||
else:
|
||||
prefix += fill(
|
||||
"""
|
||||
MOZ_ASSERT(slotIndex < JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage)));
|
||||
{
|
||||
// Scope for cachedVal
|
||||
JS::Value cachedVal = JS::GetReservedSlot(slotStorage, slotIndex);
|
||||
if (!cachedVal.isUndefined()) {
|
||||
args.rval().set(cachedVal);
|
||||
// The cached value is in the compartment of slotStorage,
|
||||
// so wrap into the caller compartment as needed.
|
||||
return ${maybeWrap}(cx, args.rval());
|
||||
}
|
||||
}
|
||||
|
||||
""",
|
||||
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
|
||||
)
|
||||
|
||||
return (
|
||||
prefix
|
||||
@ -11204,7 +11245,8 @@ class CGSpecializedGetterCommon(CGAbstractStaticMethod):
|
||||
self.descriptor,
|
||||
self.attr,
|
||||
self.errorReportingLabel,
|
||||
argsPre=[a.name for a in self.additionalArgs],
|
||||
argsPre=argsPre,
|
||||
preConversionCode=maybeReturnCachedVal,
|
||||
).define()
|
||||
)
|
||||
|
||||
@ -11855,11 +11897,11 @@ class CGMemberJITInfo(CGThing):
|
||||
isAlwaysInSlot=toStringBool(alwaysInSlot),
|
||||
isLazilyCachedInSlot=toStringBool(lazilyInSlot),
|
||||
isTypedMethod=toStringBool(isTypedMethod),
|
||||
slotIndex=slotIndex,
|
||||
slotIndex="0" if slotIndex is None else slotIndex,
|
||||
)
|
||||
return initializer.rstrip()
|
||||
|
||||
if alwaysInSlot or lazilyInSlot:
|
||||
if slotIndex is not None:
|
||||
slotAssert = fill(
|
||||
"""
|
||||
static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
|
||||
@ -11950,16 +11992,24 @@ class CGMemberJITInfo(CGThing):
|
||||
assert (
|
||||
isAlwaysInSlot
|
||||
or self.member.getExtendedAttribute("Cached")
|
||||
or self.member.getExtendedAttribute(
|
||||
"ReflectedHTMLAttributeReturningFrozenArray"
|
||||
)
|
||||
or self.member.type.isObservableArray()
|
||||
)
|
||||
isLazilyCachedInSlot = not isAlwaysInSlot
|
||||
isLazilyCachedInSlot = (
|
||||
not isAlwaysInSlot
|
||||
and not self.member.getExtendedAttribute(
|
||||
"ReflectedHTMLAttributeReturningFrozenArray"
|
||||
)
|
||||
)
|
||||
slotIndex = memberReservedSlot(self.member, self.descriptor)
|
||||
# We'll statically assert that this is not too big in
|
||||
# CGUpdateMemberSlotsMethod, in the case when
|
||||
# isAlwaysInSlot is true.
|
||||
else:
|
||||
isLazilyCachedInSlot = False
|
||||
slotIndex = "0"
|
||||
slotIndex = None
|
||||
|
||||
result = self.defineJitInfo(
|
||||
getterinfo,
|
||||
@ -12000,7 +12050,7 @@ class CGMemberJITInfo(CGThing):
|
||||
"AliasEverything",
|
||||
False,
|
||||
False,
|
||||
"0",
|
||||
None,
|
||||
[BuiltinTypes[IDLBuiltinType.Types.undefined]],
|
||||
None,
|
||||
)
|
||||
@ -12071,7 +12121,7 @@ class CGMemberJITInfo(CGThing):
|
||||
aliasSet,
|
||||
False,
|
||||
False,
|
||||
"0",
|
||||
None,
|
||||
[s[0] for s in sigs],
|
||||
args,
|
||||
)
|
||||
@ -19830,6 +19880,14 @@ class CGExampleGetter(CGNativeMember):
|
||||
descriptor.getExtendedAttributes(attr, getter=True),
|
||||
)
|
||||
|
||||
def getArgs(self, returnType, argList):
|
||||
args = CGNativeMember.getArgs(self, returnType, argList)
|
||||
if self.member.getExtendedAttribute(
|
||||
"ReflectedHTMLAttributeReturningFrozenArray"
|
||||
):
|
||||
args.insert(0, Argument("bool*", "aUseCachedValue"))
|
||||
return args
|
||||
|
||||
def declare(self, cgClass):
|
||||
assert self.member.isAttr()
|
||||
# We skip declaring ourselves if this is a maplike/setlike attr (in
|
||||
|
@ -145,6 +145,7 @@ if CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]:
|
||||
"test/TestInterfaceObservableArray.h",
|
||||
"test/TestInterfaceSetlike.h",
|
||||
"test/TestInterfaceSetlikeNode.h",
|
||||
"test/TestReflectedHTMLAttribute.h",
|
||||
"test/TestTrialInterface.h",
|
||||
"test/WrapperCachedNonISupportsTestInterface.h",
|
||||
]
|
||||
@ -164,6 +165,7 @@ if CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]:
|
||||
"test/TestInterfaceObservableArray.cpp",
|
||||
"test/TestInterfaceSetlike.cpp",
|
||||
"test/TestInterfaceSetlikeNode.cpp",
|
||||
"test/TestReflectedHTMLAttribute.cpp",
|
||||
"test/TestTrialInterface.cpp",
|
||||
"test/WrapperCachedNonISupportsTestInterface.cpp",
|
||||
]
|
||||
|
@ -911,7 +911,7 @@ class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMix
|
||||
def setNonPartial(self, location, members):
|
||||
if self._isKnownNonPartial:
|
||||
raise WebIDLError(
|
||||
"Two non-partial definitions for the " "same %s" % self.typeName(),
|
||||
"Two non-partial definitions for the same %s" % self.typeName(),
|
||||
[location, self.location],
|
||||
)
|
||||
self._isKnownNonPartial = True
|
||||
@ -1019,14 +1019,14 @@ class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
)
|
||||
if member.isStatic():
|
||||
raise WebIDLError(
|
||||
"Interface mixin member cannot include " "a static member",
|
||||
"Interface mixin member cannot include a static member",
|
||||
[member.location, self.location],
|
||||
)
|
||||
|
||||
if member.isMethod():
|
||||
if member.isStatic():
|
||||
raise WebIDLError(
|
||||
"Interface mixin member cannot include " "a static operation",
|
||||
"Interface mixin member cannot include a static operation",
|
||||
[member.location, self.location],
|
||||
)
|
||||
if (
|
||||
@ -1036,7 +1036,7 @@ class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
or member.isLegacycaller()
|
||||
):
|
||||
raise WebIDLError(
|
||||
"Interface mixin member cannot include a " "special operation",
|
||||
"Interface mixin member cannot include a special operation",
|
||||
[member.location, self.location],
|
||||
)
|
||||
|
||||
@ -1220,7 +1220,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
if m.isAttr() or m.isMethod():
|
||||
if m.isStatic():
|
||||
raise WebIDLError(
|
||||
"Don't mark things explicitly static " "in namespaces",
|
||||
"Don't mark things explicitly static in namespaces",
|
||||
[self.location, m.location],
|
||||
)
|
||||
# Just mark all our methods/attributes as static. The other
|
||||
@ -1241,7 +1241,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
# because ancestors of a [Global] interface can have other
|
||||
# descendants.
|
||||
raise WebIDLError(
|
||||
"[Global] interface has another interface " "inheriting from it",
|
||||
"[Global] interface has another interface inheriting from it",
|
||||
[self.location, self.parent.location],
|
||||
)
|
||||
|
||||
@ -1446,6 +1446,9 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
member.getExtendedAttribute("StoreInSlot")
|
||||
or member.getExtendedAttribute("Cached")
|
||||
or member.type.isObservableArray()
|
||||
or member.getExtendedAttribute(
|
||||
"ReflectedHTMLAttributeReturningFrozenArray"
|
||||
)
|
||||
)
|
||||
) or member.isMaplikeOrSetlike():
|
||||
if self.isJSImplemented() and not member.isMaplikeOrSetlike():
|
||||
@ -1587,7 +1590,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
# Make sure we're not [LegacyOverrideBuiltIns]
|
||||
if self.getExtendedAttribute("LegacyOverrideBuiltIns"):
|
||||
raise WebIDLError(
|
||||
"Interface with [Global] also has " "[LegacyOverrideBuiltIns]",
|
||||
"Interface with [Global] also has [LegacyOverrideBuiltIns]",
|
||||
[self.location],
|
||||
)
|
||||
# Mark all of our ancestors as being on the global's proto chain too
|
||||
@ -1735,7 +1738,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
)
|
||||
if member.isStatic():
|
||||
raise WebIDLError(
|
||||
"[Alias] must not be used on a " "static operation",
|
||||
"[Alias] must not be used on a static operation",
|
||||
[member.location],
|
||||
)
|
||||
if member.isIdentifierLess():
|
||||
@ -1768,7 +1771,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
and not self.hasInterfaceObject()
|
||||
):
|
||||
raise WebIDLError(
|
||||
"Interface with no interface object is " "exposed conditionally",
|
||||
"Interface with no interface object is exposed conditionally",
|
||||
[self.location],
|
||||
)
|
||||
|
||||
@ -1787,7 +1790,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
|
||||
if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
|
||||
raise WebIDLError(
|
||||
"Iterable type does not match indexed " "getter type",
|
||||
"Iterable type does not match indexed getter type",
|
||||
[iterableDecl.location, indexedGetter.location],
|
||||
)
|
||||
|
||||
@ -1801,7 +1804,7 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
|
||||
assert iterableDecl.isPairIterator()
|
||||
if indexedGetter:
|
||||
raise WebIDLError(
|
||||
"Interface with pair iterator supports " "indexed properties",
|
||||
"Interface with pair iterator supports indexed properties",
|
||||
[self.location, iterableDecl.location, indexedGetter.location],
|
||||
)
|
||||
|
||||
@ -2482,7 +2485,7 @@ class IDLEnum(IDLObjectWithIdentifier):
|
||||
def addExtendedAttributes(self, attrs):
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError(
|
||||
"There are no extended attributes that are " "allowed on enums",
|
||||
"There are no extended attributes that are allowed on enums",
|
||||
[attrs[0].location, self.location],
|
||||
)
|
||||
|
||||
@ -3477,7 +3480,7 @@ class IDLTypedef(IDLObjectWithIdentifier):
|
||||
def addExtendedAttributes(self, attrs):
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError(
|
||||
"There are no extended attributes that are " "allowed on typedefs",
|
||||
"There are no extended attributes that are allowed on typedefs",
|
||||
[attrs[0].location, self.location],
|
||||
)
|
||||
|
||||
@ -5464,14 +5467,17 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
raise WebIDLError(
|
||||
"An attribute cannot be of a dictionary type", [self.location]
|
||||
)
|
||||
if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
|
||||
if self.type.isSequence() and not (
|
||||
self.getExtendedAttribute("Cached")
|
||||
or self.getExtendedAttribute("ReflectedHTMLAttributeReturningFrozenArray")
|
||||
):
|
||||
raise WebIDLError(
|
||||
"A non-cached attribute cannot be of a sequence " "type",
|
||||
"A non-cached attribute cannot be of a sequence type",
|
||||
[self.location],
|
||||
)
|
||||
if self.type.isRecord() and not self.getExtendedAttribute("Cached"):
|
||||
raise WebIDLError(
|
||||
"A non-cached attribute cannot be of a record " "type", [self.location]
|
||||
"A non-cached attribute cannot be of a record type", [self.location]
|
||||
)
|
||||
if self.type.isUnion():
|
||||
for f in self.type.unroll().flatMemberTypes:
|
||||
@ -5605,6 +5611,39 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
"record-valued attributes",
|
||||
[self.location],
|
||||
)
|
||||
if self.getExtendedAttribute("ReflectedHTMLAttributeReturningFrozenArray"):
|
||||
if self.getExtendedAttribute("Cached") or self.getExtendedAttribute(
|
||||
"StoreInSlot"
|
||||
):
|
||||
raise WebIDLError(
|
||||
"[ReflectedHTMLAttributeReturningFrozenArray] can't be combined "
|
||||
"with [Cached] or [StoreInSlot]",
|
||||
[self.location],
|
||||
)
|
||||
if not self.type.isSequence():
|
||||
raise WebIDLError(
|
||||
"[ReflectedHTMLAttributeReturningFrozenArray] is only allowed on "
|
||||
"sequence-valued attributes",
|
||||
[self.location],
|
||||
)
|
||||
|
||||
def interfaceTypeIsOrInheritsFromElement(type):
|
||||
return type.identifier.name == "Element" or (
|
||||
type.parent is not None
|
||||
and interfaceTypeIsOrInheritsFromElement(type.parent)
|
||||
)
|
||||
|
||||
sequenceMemberType = self.type.unroll()
|
||||
if (
|
||||
not sequenceMemberType.isInterface()
|
||||
or not interfaceTypeIsOrInheritsFromElement(sequenceMemberType.inner)
|
||||
):
|
||||
raise WebIDLError(
|
||||
"[ReflectedHTMLAttributeReturningFrozenArray] is only allowed on "
|
||||
"sequence-valued attributes containing interface values of type "
|
||||
"Element or an interface inheriting from Element",
|
||||
[self.location],
|
||||
)
|
||||
if not self.type.unroll().isExposedInAllOf(self.exposureSet):
|
||||
raise WebIDLError(
|
||||
"Attribute returns a type that is not exposed "
|
||||
@ -5614,7 +5653,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
if self.getExtendedAttribute("CEReactions"):
|
||||
if self.readonly:
|
||||
raise WebIDLError(
|
||||
"[CEReactions] is not allowed on " "readonly attributes",
|
||||
"[CEReactions] is not allowed on readonly attributes",
|
||||
[self.location],
|
||||
)
|
||||
|
||||
@ -5626,7 +5665,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
or identifier == "SetterNeedsSubjectPrincipal"
|
||||
) and self.readonly:
|
||||
raise WebIDLError(
|
||||
"Readonly attributes must not be flagged as " "[%s]" % identifier,
|
||||
"Readonly attributes must not be flagged as [%s]" % identifier,
|
||||
[self.location],
|
||||
)
|
||||
elif identifier == "BindingAlias":
|
||||
@ -5660,7 +5699,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
)
|
||||
if self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[LegacyLenientThis] is only allowed on non-static " "attributes",
|
||||
"[LegacyLenientThis] is only allowed on non-static attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.getExtendedAttribute("CrossOriginReadable"):
|
||||
@ -5679,7 +5718,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
elif identifier == "LegacyUnforgeable":
|
||||
if self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[LegacyUnforgeable] is only allowed on non-static " "attributes",
|
||||
"[LegacyUnforgeable] is only allowed on non-static attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
self._legacyUnforgeable = True
|
||||
@ -5696,17 +5735,17 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
elif identifier == "PutForwards":
|
||||
if not self.readonly:
|
||||
raise WebIDLError(
|
||||
"[PutForwards] is only allowed on readonly " "attributes",
|
||||
"[PutForwards] is only allowed on readonly attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.type.isPromise():
|
||||
raise WebIDLError(
|
||||
"[PutForwards] is not allowed on " "Promise-typed attributes",
|
||||
"[PutForwards] is not allowed on Promise-typed attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[PutForwards] is only allowed on non-static " "attributes",
|
||||
"[PutForwards] is only allowed on non-static attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.getExtendedAttribute("Replaceable") is not None:
|
||||
@ -5726,17 +5765,17 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
)
|
||||
if not self.readonly:
|
||||
raise WebIDLError(
|
||||
"[Replaceable] is only allowed on readonly " "attributes",
|
||||
"[Replaceable] is only allowed on readonly attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.type.isPromise():
|
||||
raise WebIDLError(
|
||||
"[Replaceable] is not allowed on " "Promise-typed attributes",
|
||||
"[Replaceable] is not allowed on Promise-typed attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[Replaceable] is only allowed on non-static " "attributes",
|
||||
"[Replaceable] is only allowed on non-static attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.getExtendedAttribute("PutForwards") is not None:
|
||||
@ -5752,7 +5791,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
)
|
||||
if not self.readonly:
|
||||
raise WebIDLError(
|
||||
"[LegacyLenientSetter] is only allowed on readonly " "attributes",
|
||||
"[LegacyLenientSetter] is only allowed on readonly attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.type.isPromise():
|
||||
@ -5763,7 +5802,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
)
|
||||
if self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[LegacyLenientSetter] is only allowed on non-static " "attributes",
|
||||
"[LegacyLenientSetter] is only allowed on non-static attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.getExtendedAttribute("PutForwards") is not None:
|
||||
@ -5811,7 +5850,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
)
|
||||
if self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[%s] is only allowed on non-static " "attributes" % identifier,
|
||||
"[%s] is only allowed on non-static attributes" % identifier,
|
||||
[attr.location, self.location],
|
||||
)
|
||||
if self.getExtendedAttribute("LegacyLenientThis"):
|
||||
@ -5855,7 +5894,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
elif identifier == "UseCounter":
|
||||
if self.stringifier:
|
||||
raise WebIDLError(
|
||||
"[UseCounter] must not be used on a " "stringifier attribute",
|
||||
"[UseCounter] must not be used on a stringifier attribute",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
elif identifier == "Unscopable":
|
||||
@ -5896,6 +5935,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
or identifier == "BinaryName"
|
||||
or identifier == "NonEnumerable"
|
||||
or identifier == "BindingTemplate"
|
||||
or identifier == "ReflectedHTMLAttributeReturningFrozenArray"
|
||||
):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
@ -6732,7 +6772,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
# Make sure either all our overloads return Promises or none do
|
||||
if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType:
|
||||
raise WebIDLError(
|
||||
"We have overloads with both Promise and " "non-Promise return types",
|
||||
"We have overloads with both Promise and non-Promise return types",
|
||||
[
|
||||
overloadWithPromiseReturnType.location,
|
||||
overloadWithoutPromiseReturnType.location,
|
||||
@ -6741,7 +6781,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
|
||||
if overloadWithPromiseReturnType and self._legacycaller:
|
||||
raise WebIDLError(
|
||||
"May not have a Promise return type for a " "legacycaller.",
|
||||
"May not have a Promise return type for a legacycaller.",
|
||||
[overloadWithPromiseReturnType.location],
|
||||
)
|
||||
|
||||
@ -6834,13 +6874,13 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
or identifier == "GetterNeedsSubjectPrincipal"
|
||||
):
|
||||
raise WebIDLError(
|
||||
"Methods must not be flagged as " "[%s]" % identifier,
|
||||
"Methods must not be flagged as [%s]" % identifier,
|
||||
[attr.location, self.location],
|
||||
)
|
||||
elif identifier == "LegacyUnforgeable":
|
||||
if self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[LegacyUnforgeable] is only allowed on non-static " "methods",
|
||||
"[LegacyUnforgeable] is only allowed on non-static methods",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
self._legacyUnforgeable = True
|
||||
@ -6891,7 +6931,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
)
|
||||
if identifier == "CrossOriginCallable" and self.isStatic():
|
||||
raise WebIDLError(
|
||||
"[CrossOriginCallable] is only allowed on non-static " "attributes",
|
||||
"[CrossOriginCallable] is only allowed on non-static attributes",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
elif identifier == "Pure":
|
||||
@ -6916,7 +6956,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
elif identifier == "UseCounter":
|
||||
if self.isSpecial():
|
||||
raise WebIDLError(
|
||||
"[UseCounter] must not be used on a special " "operation",
|
||||
"[UseCounter] must not be used on a special operation",
|
||||
[attr.location, self.location],
|
||||
)
|
||||
elif identifier == "Unscopable":
|
||||
@ -7096,17 +7136,17 @@ class IDLIncludesStatement(IDLObject):
|
||||
# locations.
|
||||
if not isinstance(interface, IDLInterface):
|
||||
raise WebIDLError(
|
||||
"Left-hand side of 'includes' is not an " "interface",
|
||||
"Left-hand side of 'includes' is not an interface",
|
||||
[self.interface.location, interface.location],
|
||||
)
|
||||
if interface.isCallback():
|
||||
raise WebIDLError(
|
||||
"Left-hand side of 'includes' is a callback " "interface",
|
||||
"Left-hand side of 'includes' is a callback interface",
|
||||
[self.interface.location, interface.location],
|
||||
)
|
||||
if not isinstance(mixin, IDLInterfaceMixin):
|
||||
raise WebIDLError(
|
||||
"Right-hand side of 'includes' is not an " "interface mixin",
|
||||
"Right-hand side of 'includes' is not an interface mixin",
|
||||
[self.mixin.location, mixin.location],
|
||||
)
|
||||
|
||||
@ -8864,7 +8904,7 @@ class Parser(Tokenizer):
|
||||
|
||||
if p[1].name == "Promise":
|
||||
raise WebIDLError(
|
||||
"Promise used without saying what it's " "parametrized over",
|
||||
"Promise used without saying what it's parametrized over",
|
||||
[self.getLocation(p, 1)],
|
||||
)
|
||||
|
||||
|
173
dom/bindings/parser/tests/test_reflected_attribute.py
Normal file
173
dom/bindings/parser/tests/test_reflected_attribute.py
Normal file
@ -0,0 +1,173 @@
|
||||
import WebIDL
|
||||
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
def parseWithNode(test):
|
||||
parser.parse(
|
||||
"""
|
||||
interface Node {};
|
||||
interface Document : Node {};
|
||||
interface Element : Node {};
|
||||
interface HTMLElement : Element {};
|
||||
"""
|
||||
+ test
|
||||
)
|
||||
|
||||
def parseFrozenArrayAttribute(innerType):
|
||||
parseWithNode(
|
||||
"""
|
||||
interface ReflectedAttribute {
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute sequence<%s>? reflectedHTMLAttribute;
|
||||
};
|
||||
"""
|
||||
% innerType
|
||||
)
|
||||
|
||||
results = parser.finish()
|
||||
|
||||
harness.check(len(results), 5, "Should know about one thing")
|
||||
harness.ok(
|
||||
isinstance(results[4], WebIDL.IDLInterface), "Should have an interface here"
|
||||
)
|
||||
members = results[4].members
|
||||
harness.check(len(members), 1, "Should have one member")
|
||||
harness.ok(members[0].isAttr(), "Should have attribute")
|
||||
harness.ok(
|
||||
members[0].getExtendedAttribute(
|
||||
"ReflectedHTMLAttributeReturningFrozenArray"
|
||||
)
|
||||
is not None,
|
||||
"Should have extended attribute",
|
||||
)
|
||||
|
||||
parseFrozenArrayAttribute("Element")
|
||||
|
||||
parser = parser.reset()
|
||||
parseFrozenArrayAttribute("HTMLElement")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parseWithNode(
|
||||
"""
|
||||
interface ReflectedAttribute {
|
||||
[ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute Element? reflectedHTMLAttribute;
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
||||
parser.finish()
|
||||
except WebIDL.WebIDLError:
|
||||
threw = True
|
||||
harness.ok(
|
||||
threw,
|
||||
"Should have thrown because [ReflectedHTMLAttributeReturningFrozenArray] "
|
||||
"should only be used on attributes with a sequence<*Element> type.",
|
||||
)
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parseWithNode(
|
||||
"""
|
||||
interface ReflectedAttribute {
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute sequence<long> reflectedHTMLAttribute;
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
||||
parser.finish()
|
||||
except WebIDL.WebIDLError:
|
||||
threw = True
|
||||
harness.ok(
|
||||
threw,
|
||||
"Should have thrown because [ReflectedHTMLAttributeReturningFrozenArray] "
|
||||
"should only be used on attributes with a sequence<*Element> type.",
|
||||
)
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parseWithNode(
|
||||
"""
|
||||
interface ReflectedAttribute {
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute sequence<Document> reflectedHTMLAttribute;
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
||||
parser.finish()
|
||||
except WebIDL.WebIDLError:
|
||||
threw = True
|
||||
harness.ok(
|
||||
threw,
|
||||
"Should have thrown because [ReflectedHTMLAttributeReturningFrozenArray] "
|
||||
"should only be used on attributes with a sequence<*Element> type.",
|
||||
)
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parseWithNode(
|
||||
"""
|
||||
interface ReflectedAttribute {
|
||||
[ReflectedHTMLAttributeReturningFrozenArray]
|
||||
sequence<Element>? reflectedHTMLAttribute();
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
||||
parser.finish()
|
||||
except WebIDL.WebIDLError:
|
||||
threw = True
|
||||
harness.ok(
|
||||
threw,
|
||||
"Should have thrown because [ReflectedHTMLAttributeReturningFrozenArray] "
|
||||
"should only be used on attributes with a sequence<*Element> type.",
|
||||
)
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parseWithNode(
|
||||
"""
|
||||
interface ReflectedAttribute {
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray, Cached, Pure]
|
||||
attribute sequence<Element>? reflectedHTMLAttribute;
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
||||
parser.finish()
|
||||
except WebIDL.WebIDLError:
|
||||
threw = True
|
||||
harness.ok(
|
||||
threw,
|
||||
"Should have thrown because [ReflectedHTMLAttributeReturningFrozenArray] "
|
||||
"should not be used together with [Cached].",
|
||||
)
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parseWithNode(
|
||||
"""
|
||||
interface ReflectedAttribute {
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray, StoreInSlot, Pure]
|
||||
attribute sequence<Element>? reflectedHTMLAttribute;
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
||||
parser.finish()
|
||||
except WebIDL.WebIDLError:
|
||||
threw = True
|
||||
harness.ok(
|
||||
threw,
|
||||
"Should have thrown because [ReflectedHTMLAttributeReturningFrozenArray] "
|
||||
"should not be used together with [Cached].",
|
||||
)
|
@ -1152,6 +1152,11 @@ class TestInterface : public nsISupports, public nsWrapperCache {
|
||||
void PassUnionAllowSharedArrayBuffer(
|
||||
const StringOrMaybeSharedArrayBuffer& foo);
|
||||
|
||||
void GetReflectedHTMLAttributeReturningFrozenArray(
|
||||
bool*, Nullable<nsTArray<RefPtr<Element>>>&) const;
|
||||
void SetReflectedHTMLAttributeReturningFrozenArray(
|
||||
const Nullable<Sequence<OwningNonNull<Element>>>&);
|
||||
|
||||
private:
|
||||
// We add signatures here that _could_ start matching if the codegen
|
||||
// got data types wrong. That way if it ever does we'll have a call
|
||||
|
@ -1083,6 +1083,9 @@ interface TestInterface {
|
||||
undefined passUnionArrayBuffer((DOMString or ArrayBuffer) foo);
|
||||
undefined passUnionAllowSharedArrayBuffer((DOMString or [AllowShared] ArrayBuffer) foo);
|
||||
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute sequence<Element>? reflectedHTMLAttributeReturningFrozenArray;
|
||||
|
||||
// If you add things here, add them to TestExampleGen as well
|
||||
};
|
||||
|
||||
|
@ -870,6 +870,9 @@ interface TestExampleInterface {
|
||||
undefined passUnionArrayBuffer((DOMString or ArrayBuffer) foo);
|
||||
undefined passUnionAllowSharedArrayBuffer((DOMString or [AllowShared] ArrayBuffer) foo);
|
||||
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute sequence<Element>? reflectedHTMLAttributeReturningFrozenArray;
|
||||
|
||||
// If you add things here, add them to TestExampleGen. If they need to be
|
||||
// supported in JS-implemented WebIDL then you need to add them to
|
||||
// TestJSImplGen as well, if they are not supported in JS-implemented WebIDL
|
||||
|
68
dom/bindings/test/TestReflectedHTMLAttribute.cpp
Normal file
68
dom/bindings/test/TestReflectedHTMLAttribute.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/TestReflectedHTMLAttribute.h"
|
||||
#include "mozilla/dom/TestFunctionsBinding.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestReflectedHTMLAttribute,
|
||||
mCachedElements, mNewElements)
|
||||
|
||||
/* static */
|
||||
already_AddRefed<TestReflectedHTMLAttribute>
|
||||
TestReflectedHTMLAttribute::Constructor(GlobalObject& aGlobal) {
|
||||
return MakeAndAddRef<TestReflectedHTMLAttribute>();
|
||||
}
|
||||
|
||||
template <typename ArrayLike>
|
||||
static void AssignElements(const ArrayLike& aFrom,
|
||||
Nullable<nsTArray<RefPtr<Element>>>& aTo) {
|
||||
if (aTo.IsNull()) {
|
||||
aTo.SetValue();
|
||||
} else {
|
||||
aTo.Value().Clear();
|
||||
}
|
||||
aTo.Value().AppendElements(aFrom);
|
||||
}
|
||||
|
||||
void TestReflectedHTMLAttribute::GetReflectedHTMLAttribute(
|
||||
bool* aUseCachedValue, Nullable<nsTArray<RefPtr<Element>>>& aResult) {
|
||||
if (aUseCachedValue) {
|
||||
if (mCachedElements == mNewElements) {
|
||||
*aUseCachedValue = true;
|
||||
return;
|
||||
}
|
||||
|
||||
*aUseCachedValue = false;
|
||||
}
|
||||
|
||||
if (mNewElements.IsNull()) {
|
||||
mCachedElements.SetNull();
|
||||
aResult.SetNull();
|
||||
} else {
|
||||
AssignElements(mNewElements.Value(), mCachedElements);
|
||||
aResult.SetValue(mCachedElements.Value().Clone());
|
||||
}
|
||||
}
|
||||
|
||||
void TestReflectedHTMLAttribute::SetReflectedHTMLAttribute(
|
||||
const Nullable<Sequence<OwningNonNull<Element>>>& aValue) {
|
||||
// We're just testing getters, so do nothing. But this would either clear or
|
||||
// set the "explicitly set attr-elements".
|
||||
}
|
||||
|
||||
void TestReflectedHTMLAttribute::SetReflectedHTMLAttributeValue(
|
||||
const Sequence<OwningNonNull<Element>>& aElements) {
|
||||
AssignElements(aElements, mNewElements);
|
||||
}
|
||||
|
||||
JSObject* TestReflectedHTMLAttribute::WrapObject(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
|
||||
return TestReflectedHTMLAttribute_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
45
dom/bindings/test/TestReflectedHTMLAttribute.h
Normal file
45
dom/bindings/test/TestReflectedHTMLAttribute.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TestReflectedHTMLAttribute_h
|
||||
#define mozilla_dom_TestReflectedHTMLAttribute_h
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class TestReflectedHTMLAttribute final : public nsWrapperCache {
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TestReflectedHTMLAttribute)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(TestReflectedHTMLAttribute)
|
||||
|
||||
static already_AddRefed<TestReflectedHTMLAttribute> Constructor(
|
||||
GlobalObject& aGlobal);
|
||||
|
||||
void GetReflectedHTMLAttribute(bool* aUseCachedValue,
|
||||
Nullable<nsTArray<RefPtr<Element>>>& aResult);
|
||||
void SetReflectedHTMLAttribute(
|
||||
const Nullable<Sequence<OwningNonNull<Element>>>& aValue);
|
||||
void SetReflectedHTMLAttributeValue(
|
||||
const Sequence<OwningNonNull<Element>>& aElements);
|
||||
|
||||
nsISupports* GetParentObject() const { return nullptr; }
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~TestReflectedHTMLAttribute() = default;
|
||||
|
||||
Nullable<nsTArray<RefPtr<Element>>> mCachedElements;
|
||||
Nullable<nsTArray<RefPtr<Element>>> mNewElements;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // mozilla_dom_TestReflectedHTMLAttribute_h
|
@ -9,7 +9,7 @@ support-files = [
|
||||
["test_bug775543.html"]
|
||||
|
||||
["test_bug1123516_maplikesetlikechrome.xhtml"]
|
||||
skip-if = ["!debug"] # TestFunctions is only available in debug builds
|
||||
skip-if = ["!debug"] # Test WebIDL interfaces are only available in debug builds
|
||||
|
||||
["test_bug1287912.html"]
|
||||
|
||||
@ -20,9 +20,12 @@ skip-if = ["!debug"] # TestFunctions is only available in debug builds
|
||||
["test_document_location_via_xray_cached.html"]
|
||||
|
||||
["test_dom_xrays.html"]
|
||||
skip-if = ["debug == false"] # TestFunctions is only available in debug builds
|
||||
support-files = [
|
||||
"file_reflected_attribute_frozenarray.js",
|
||||
]
|
||||
skip-if = ["!debug"] # Test WebIDL interfaces are only available in debug builds
|
||||
|
||||
["test_interfaceLength_chrome.html"]
|
||||
skip-if = ["debug == false"] # TestFunctions is only available in debug builds
|
||||
skip-if = ["!debug"] # Test WebIDL interfaces are only available in debug builds
|
||||
|
||||
["test_proxies_via_xray.html"]
|
||||
|
90
dom/bindings/test/file_reflected_attribute_frozenarray.js
Normal file
90
dom/bindings/test/file_reflected_attribute_frozenarray.js
Normal file
@ -0,0 +1,90 @@
|
||||
function checkEquals(value, expected, valueCheckFn = (a, b) => a == b) {
|
||||
if (!valueCheckFn(value, expected)) {
|
||||
return `, got ${value}, expected ${expected}`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function checkReflectedAttributeWithFrozenArrayValues(obj, values, valueCheck) {
|
||||
if (!SimpleTest.isa(obj.reflectedHTMLAttribute, "Array")) {
|
||||
return `, expected array`;
|
||||
}
|
||||
let failure = checkEquals(obj.reflectedHTMLAttribute.length, values.length);
|
||||
if (!failure) {
|
||||
for (let [i, v] of obj.reflectedHTMLAttribute.entries()) {
|
||||
failure = checkEquals(values[i], v, valueCheck);
|
||||
if (failure) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return failure;
|
||||
}
|
||||
|
||||
function checkReflectedAttributeWithFrozenArray(
|
||||
obj,
|
||||
values,
|
||||
suffix,
|
||||
valueCheck
|
||||
) {
|
||||
let failure = checkReflectedAttributeWithFrozenArrayValues(
|
||||
obj,
|
||||
values,
|
||||
valueCheck
|
||||
);
|
||||
ok(
|
||||
!failure,
|
||||
`Cached value on object for HTML reflected FrozenArray attribute should contain the right values ${suffix}${
|
||||
failure || ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
function testReflectedAttributeWithFrozenArray(win) {
|
||||
let testObject = new win.TestReflectedHTMLAttribute();
|
||||
ok(
|
||||
testObject instanceof win.TestReflectedHTMLAttribute,
|
||||
"Got a TestReflectedHTMLAttribute object"
|
||||
);
|
||||
|
||||
is(
|
||||
testObject.reflectedHTMLAttribute,
|
||||
null,
|
||||
"Initial value for HTML reflected FrozenArray attribute should be null"
|
||||
);
|
||||
|
||||
let values = [win.document.head];
|
||||
testObject.setReflectedHTMLAttributeValue(values);
|
||||
checkReflectedAttributeWithFrozenArray(testObject, values, "after setting");
|
||||
|
||||
values = [win.document.body, win.document.body.firstElementChild];
|
||||
testObject.setReflectedHTMLAttributeValue(values);
|
||||
checkReflectedAttributeWithFrozenArray(testObject, values, "after resetting");
|
||||
|
||||
// Use a loop to ensure the JITs optimize the getter access.
|
||||
let failure;
|
||||
for (let i = 0; i < 10_000; i++) {
|
||||
failure = checkReflectedAttributeWithFrozenArrayValues(testObject, values);
|
||||
if (!failure) {
|
||||
break;
|
||||
}
|
||||
if (i == 9_990) {
|
||||
values = [win.document.head];
|
||||
testObject.setReflectedHTMLAttributeValue(values);
|
||||
}
|
||||
}
|
||||
ok(
|
||||
!failure,
|
||||
`Shouldn't use the cached value for HTML reflected FrozenArray attribute directly from JITted code${
|
||||
failure || ""
|
||||
}`
|
||||
);
|
||||
|
||||
is(
|
||||
testObject.reflectedHTMLAttribute,
|
||||
testObject.reflectedHTMLAttribute,
|
||||
"Getter for HTML reflected FrozenArray attribute should return the cached value"
|
||||
);
|
||||
|
||||
return [testObject, values];
|
||||
}
|
@ -168,8 +168,20 @@ skip-if = ["!debug"]
|
||||
|
||||
["test_proxy_missing_prop.html"]
|
||||
|
||||
["test_reflected_attribute_frozenarray.html"]
|
||||
support-files = [
|
||||
"file_reflected_attribute_frozenarray.js",
|
||||
]
|
||||
skip-if = ["!debug"] # Test WebIDL interfaces are only available in debug builds
|
||||
|
||||
["test_remoteProxyAsPrototype.html"]
|
||||
|
||||
["test_resizable_arraybufferview.html"]
|
||||
skip-if = [
|
||||
"!debug",
|
||||
"!nightly_build", # Bug 1670026
|
||||
]
|
||||
|
||||
["test_returnUnion.html"]
|
||||
skip-if = ["!debug"]
|
||||
|
||||
@ -199,9 +211,3 @@ skip-if = ["!debug"]
|
||||
skip-if = ["!debug"]
|
||||
|
||||
["test_worker_UnwrapArg.html"]
|
||||
|
||||
["test_resizable_arraybufferview.html"]
|
||||
skip-if = [
|
||||
"!debug",
|
||||
"!nightly_build", # Bug 1670026
|
||||
]
|
||||
|
@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=787070
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 787070</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="file_reflected_attribute_frozenarray.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
@ -390,6 +391,11 @@ function test() {
|
||||
"Calling an instance object for an interface marked with legacycaller shouldn't throw");
|
||||
checkXrayProperty(doc.all, 0, [ element ]);
|
||||
|
||||
let [ testObject, expectedValues ] = testReflectedAttributeWithFrozenArray(win);
|
||||
checkReflectedAttributeWithFrozenArray(testObject.wrappedJSObject, expectedValues,
|
||||
"on Xray and object",
|
||||
(a, b) => a.wrappedJSObject == b);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
24
dom/bindings/test/test_reflected_attribute_frozenarray.html
Normal file
24
dom/bindings/test/test_reflected_attribute_frozenarray.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 1773732</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="file_reflected_attribute_frozenarray.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({ set: [["dom.expose_test_interfaces", true]] }, () => {
|
||||
testReflectedAttributeWithFrozenArray(window)
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1987,6 +1987,54 @@ lexicographic order (which is specified by WebIDL).
|
||||
|
||||
This should only ever be used on internal APIs that are not exposed to the Web!
|
||||
|
||||
### `[ReflectedHTMLAttributeReturningFrozenArray]`
|
||||
|
||||
Used to flag a HTML reflected IDL attribute as having a `FrozenArray<T>?` type,
|
||||
where `T` is either `Element` or an interface that inherits from `Element`. This
|
||||
should only be used to implement the algorithms for that kind of reflected IDL
|
||||
attributes.
|
||||
|
||||
When this attribute's getter is called, it will cache the JS reflection of the
|
||||
returned value on the JS object. The C++ getter will be passed an additional
|
||||
`bool*` argument before the result argument. If this argument is not `null` then
|
||||
the implementation is supposed to set the `bool` that this argument is pointing
|
||||
to to whether the
|
||||
[attr-associated elements](https://html.spec.whatwg.org/#attr-associated-elements)
|
||||
and the
|
||||
[cached attr-associated elements](https://html.spec.whatwg.org/#cached-attr-associated-elements)
|
||||
are equal. If it is not `null`, and the getter sets the pointee to `true` then
|
||||
the cached JS value will be returned, and the result value from the C++ getter
|
||||
will be ignored (so there is no need to set the result's value). If it is
|
||||
`null`, or the getter sets the pointee to `false`, then the cached value will be
|
||||
set to the JS reflection of the result value from the C++ getter, and that JS
|
||||
value will then be returned.
|
||||
|
||||
Note that this will not cause the JIT to directly get the cached value from the
|
||||
slot (as `[StoreInSlot]` or `[Cached]` would). The setter will also not clear
|
||||
the cached value from the slot.
|
||||
|
||||
For example, this IDL:
|
||||
|
||||
``` webidl
|
||||
interface Element {
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute sequence<Element>? reflectedHTMLAttribute;
|
||||
};
|
||||
```
|
||||
|
||||
will require the following declarations in `Element`:
|
||||
|
||||
``` cpp
|
||||
class Element {
|
||||
// …
|
||||
void GetReflectedHTMLAttribute(
|
||||
bool* aUseCachedValue, Nullable<nsTArray<RefPtr<Element>>>& aResult);
|
||||
void SetReflectedHTMLAttribute(
|
||||
const Nullable<Sequence<OwningNonNull<Element>>>& aValue);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Helper objects
|
||||
|
||||
The C++ side of the bindings uses a number of helper objects.
|
||||
|
@ -146,6 +146,16 @@ interface TestFunctions {
|
||||
static boolean staticAndNonStaticOverload(optional unsigned long foo);
|
||||
};
|
||||
|
||||
[Pref="dom.expose_test_interfaces",
|
||||
Exposed=Window]
|
||||
interface TestReflectedHTMLAttribute {
|
||||
constructor();
|
||||
|
||||
[Frozen, ReflectedHTMLAttributeReturningFrozenArray]
|
||||
attribute sequence<Element>? reflectedHTMLAttribute;
|
||||
undefined setReflectedHTMLAttributeValue(sequence<Element> seq);
|
||||
};
|
||||
|
||||
dictionary DictWithAllowSharedBufferSource {
|
||||
ArrayBuffer arrayBuffer;
|
||||
ArrayBufferView arrayBufferView;
|
||||
|
Loading…
Reference in New Issue
Block a user