зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1123516 - Implement maplike/setlike in WebIDL Codegen; r=bz
This commit is contained in:
Родитель
907dea7e41
Коммит
c035543458
|
@ -696,6 +696,7 @@
|
|||
#ifdef MOZ_DEBUG
|
||||
@RESPATH@/components/TestInterfaceJS.js
|
||||
@RESPATH@/components/TestInterfaceJS.manifest
|
||||
@RESPATH@/components/TestInterfaceJSMaplike.js
|
||||
#endif
|
||||
|
||||
@RESPATH@/components/PACGenerator.js
|
||||
|
|
|
@ -623,6 +623,7 @@
|
|||
#ifdef MOZ_DEBUG
|
||||
@RESPATH@/components/TestInterfaceJS.js
|
||||
@RESPATH@/components/TestInterfaceJS.manifest
|
||||
@RESPATH@/components/TestInterfaceJSMaplike.js
|
||||
#endif
|
||||
|
||||
@RESPATH@/components/PACGenerator.js
|
||||
|
|
|
@ -2816,5 +2816,89 @@ SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj)
|
|||
ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
|
||||
}
|
||||
|
||||
template<decltype(JS::NewMapObject) Method>
|
||||
bool
|
||||
GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated)
|
||||
{
|
||||
JS::Rooted<JSObject*> reflector(aCx);
|
||||
reflector = IsDOMObject(aObj) ? aObj : js::UncheckedUnwrap(aObj,
|
||||
/* stopAtOuter = */ false);
|
||||
|
||||
// Retrieve the backing object from the reserved slot on the maplike/setlike
|
||||
// object. If it doesn't exist yet, create it.
|
||||
JS::Rooted<JS::Value> slotValue(aCx);
|
||||
slotValue = js::GetReservedSlot(reflector, aSlotIndex);
|
||||
if (slotValue.isUndefined()) {
|
||||
// Since backing object access can happen in non-originating compartments,
|
||||
// make sure to create the backing object in reflector compartment.
|
||||
{
|
||||
JSAutoCompartment ac(aCx, reflector);
|
||||
JS::Rooted<JSObject*> newBackingObj(aCx);
|
||||
newBackingObj.set(Method(aCx));
|
||||
if (NS_WARN_IF(!newBackingObj)) {
|
||||
return false;
|
||||
}
|
||||
js::SetReservedSlot(reflector, aSlotIndex, JS::ObjectValue(*newBackingObj));
|
||||
}
|
||||
slotValue = js::GetReservedSlot(reflector, aSlotIndex);
|
||||
*aBackingObjCreated = true;
|
||||
} else {
|
||||
*aBackingObjCreated = false;
|
||||
}
|
||||
if (!MaybeWrapNonDOMObjectValue(aCx, &slotValue)) {
|
||||
return false;
|
||||
}
|
||||
aBackingObj.set(&slotValue.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated)
|
||||
{
|
||||
return GetMaplikeSetlikeBackingObject<JS::NewMapObject>(aCx, aObj, aSlotIndex,
|
||||
aBackingObj,
|
||||
aBackingObjCreated);
|
||||
}
|
||||
|
||||
bool
|
||||
GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated)
|
||||
{
|
||||
return GetMaplikeSetlikeBackingObject<JS::NewSetObject>(aCx, aObj, aSlotIndex,
|
||||
aBackingObj,
|
||||
aBackingObjCreated);
|
||||
}
|
||||
|
||||
bool
|
||||
ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
||||
// Unpack callback and object from slots
|
||||
JS::Rooted<JS::Value>
|
||||
callbackFn(aCx, js::GetFunctionNativeReserved(&args.callee(),
|
||||
FOREACH_CALLBACK_SLOT));
|
||||
JS::Rooted<JS::Value>
|
||||
maplikeOrSetlikeObj(aCx,
|
||||
js::GetFunctionNativeReserved(&args.callee(),
|
||||
FOREACH_MAPLIKEORSETLIKEOBJ_SLOT));
|
||||
MOZ_ASSERT(aArgc == 3);
|
||||
JS::AutoValueVector newArgs(aCx);
|
||||
// Arguments are passed in as value, key, object. Keep value and key, replace
|
||||
// object with the maplike/setlike object.
|
||||
newArgs.append(args.get(0));
|
||||
newArgs.append(args.get(1));
|
||||
newArgs.append(maplikeOrSetlikeObj);
|
||||
JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
|
||||
// Now actually call the user specified callback
|
||||
return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval);
|
||||
}
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -3248,6 +3248,29 @@ bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
|
|||
// thrown.
|
||||
bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj);
|
||||
|
||||
// Slot indexes for maplike/setlike forEach functions
|
||||
#define FOREACH_CALLBACK_SLOT 0
|
||||
#define FOREACH_MAPLIKEORSETLIKEOBJ_SLOT 1
|
||||
|
||||
// Backing function for running .forEach() on maplike/setlike interfaces.
|
||||
// Unpacks callback and maplike/setlike object from reserved slots, then runs
|
||||
// callback for each key (and value, for maplikes)
|
||||
bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
|
||||
|
||||
// Unpacks backing object (ES6 map/set) from the reserved slot of a reflector
|
||||
// for a maplike/setlike interface. If backing object does not exist, creates
|
||||
// backing object in the compartment of the reflector involved, making this safe
|
||||
// to use across compartments/via xrays. Return values of these methods will
|
||||
// always be in the context compartment.
|
||||
bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated);
|
||||
bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated);
|
||||
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -1143,6 +1143,18 @@ class CGHeaders(CGWrapper):
|
|||
if funcList is not None:
|
||||
addHeaderForFunc(funcList[0])
|
||||
|
||||
for desc in descriptors:
|
||||
if desc.interface.maplikeOrSetlike:
|
||||
# We need ToJSValue.h for maplike/setlike type conversions
|
||||
bindingHeaders.add("mozilla/dom/ToJSValue.h")
|
||||
# Add headers for the key and value types of the maplike, since
|
||||
# they'll be needed for convenience functions
|
||||
addHeadersForType((desc.interface.maplikeOrSetlike.keyType,
|
||||
desc, None))
|
||||
if desc.interface.maplikeOrSetlike.valueType:
|
||||
addHeadersForType((desc.interface.maplikeOrSetlike.valueType,
|
||||
desc, None))
|
||||
|
||||
for d in dictionaries:
|
||||
if d.parent:
|
||||
declareIncludes.add(self.getDeclarationFilename(d.parent))
|
||||
|
@ -2164,7 +2176,9 @@ class MethodDefiner(PropertyDefiner):
|
|||
"name": m.identifier.name,
|
||||
"methodInfo": not m.isStatic(),
|
||||
"length": methodLength(m),
|
||||
"flags": "JSPROP_ENUMERATE",
|
||||
# Methods generated for a maplike/setlike declaration are not
|
||||
# enumerable.
|
||||
"flags": "JSPROP_ENUMERATE" if not m.isMaplikeOrSetlikeMethod() else "0",
|
||||
"condition": PropertyDefiner.getControllingCondition(m, descriptor),
|
||||
"allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
|
||||
"returnsPromise": m.returnsPromise(),
|
||||
|
@ -2175,9 +2189,21 @@ class MethodDefiner(PropertyDefiner):
|
|||
else:
|
||||
self.regular.append(method)
|
||||
|
||||
# FIXME Check for an existing iterator on the interface first.
|
||||
if (any(m.isGetter() and m.isIndexed() for m in methods) and
|
||||
not any("@@iterator" in m.aliases for m in methods)):
|
||||
# TODO: Once iterable is implemented, use tiebreak rules instead of
|
||||
# failing. Also, may be more tiebreak rules to implement once spec bug
|
||||
# is resolved.
|
||||
# https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
|
||||
def hasIterator(methods, regular):
|
||||
return (any("@@iterator" in m.aliases for m in methods) or
|
||||
any("@@iterator" == r["name"] for r in regular))
|
||||
|
||||
if (any(m.isGetter() and m.isIndexed() for m in methods)):
|
||||
if hasIterator(methods, self.regular):
|
||||
raise TypeError("Cannot have indexed getter/attr on "
|
||||
"interface %s with other members "
|
||||
"that generate @@iterator, such as "
|
||||
"maplike/setlike or aliased functions." %
|
||||
self.descriptor.interface.identifier.name)
|
||||
self.regular.append({
|
||||
"name": "@@iterator",
|
||||
"methodInfo": False,
|
||||
|
@ -2187,6 +2213,31 @@ class MethodDefiner(PropertyDefiner):
|
|||
"condition": MemberCondition(None, None)
|
||||
})
|
||||
|
||||
# Generate the maplike/setlike iterator, if one wasn't already
|
||||
# generated by a method. If we already have an @@iterator symbol, fail.
|
||||
if descriptor.interface.maplikeOrSetlike:
|
||||
if hasIterator(methods, self.regular):
|
||||
raise TypeError("Cannot have maplike/setlike interface with "
|
||||
"other members that generate @@iterator "
|
||||
"on interface %s, such as indexed getters "
|
||||
"or aliased functions." %
|
||||
self.descriptor.interface.identifier.name)
|
||||
for m in methods:
|
||||
if (m.isMaplikeOrSetlikeMethod() and
|
||||
((m.maplikeOrSetlike.isMaplike() and
|
||||
m.identifier.name == "entries") or
|
||||
(m.maplikeOrSetlike.isSetlike() and
|
||||
m.identifier.name == "values"))):
|
||||
self.regular.append({
|
||||
"name": "@@iterator",
|
||||
"methodName": m.identifier.name,
|
||||
"length": methodLength(m),
|
||||
"flags": "0",
|
||||
"condition": PropertyDefiner.getControllingCondition(m,
|
||||
descriptor),
|
||||
})
|
||||
break
|
||||
|
||||
if not static:
|
||||
stringifier = descriptor.operations['Stringifier']
|
||||
if (stringifier and
|
||||
|
@ -2284,7 +2335,9 @@ class MethodDefiner(PropertyDefiner):
|
|||
jitinfo = "nullptr"
|
||||
else:
|
||||
selfHostedName = "nullptr"
|
||||
accessor = m.get("nativeName", IDLToCIdentifier(m["name"]))
|
||||
# When defining symbols, function name may not match symbol name
|
||||
methodName = m.get("methodName", m["name"])
|
||||
accessor = m.get("nativeName", IDLToCIdentifier(methodName))
|
||||
if m.get("methodInfo", True):
|
||||
# Cast this in case the methodInfo is a
|
||||
# JSTypedMethodJitInfo.
|
||||
|
@ -2377,8 +2430,10 @@ class AttrDefiner(PropertyDefiner):
|
|||
|
||||
def flags(attr):
|
||||
unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
|
||||
return ("JSPROP_SHARED | JSPROP_ENUMERATE" +
|
||||
unforgeable)
|
||||
# Attributes generated as part of a maplike/setlike declaration are
|
||||
# not enumerable.
|
||||
enumerable = " | JSPROP_ENUMERATE" if not attr.isMaplikeOrSetlikeAttr() else ""
|
||||
return ("JSPROP_SHARED" + enumerable + unforgeable)
|
||||
|
||||
def getter(attr):
|
||||
if self.static:
|
||||
|
@ -5539,7 +5594,7 @@ class CGArgumentConverter(CGThing):
|
|||
will be automatically uppercased.
|
||||
"""
|
||||
def __init__(self, argument, index, descriptorProvider,
|
||||
argDescription,
|
||||
argDescription, member,
|
||||
invalidEnumValueFatal=True, lenientFloatCode=None):
|
||||
CGThing.__init__(self)
|
||||
self.argument = argument
|
||||
|
@ -5556,8 +5611,16 @@ class CGArgumentConverter(CGThing):
|
|||
"obj": "obj",
|
||||
"passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptorProvider))
|
||||
}
|
||||
self.replacementVariables["val"] = string.Template(
|
||||
"args[${index}]").substitute(replacer)
|
||||
# If we have a method generated by the maplike/setlike portion of an
|
||||
# interface, arguments can possibly be undefined, but will need to be
|
||||
# converted to the key/value type of the backing object. In this case,
|
||||
# use .get() instead of direct access to the argument.
|
||||
if member.isMethod() and member.isMaplikeOrSetlikeMethod():
|
||||
self.replacementVariables["val"] = string.Template(
|
||||
"args.get(${index})").substitute(replacer)
|
||||
else:
|
||||
self.replacementVariables["val"] = string.Template(
|
||||
"args[${index}]").substitute(replacer)
|
||||
haveValueCheck = string.Template(
|
||||
"args.hasDefined(${index})").substitute(replacer)
|
||||
self.replacementVariables["haveValue"] = haveValueCheck
|
||||
|
@ -6795,7 +6858,7 @@ class CGPerSignatureCall(CGThing):
|
|||
cgThings.append(
|
||||
CGArgumentConverter(arguments[i], i, self.descriptor,
|
||||
argDescription % {"index": i + 1},
|
||||
invalidEnumValueFatal=not setter,
|
||||
idlNode, invalidEnumValueFatal=not setter,
|
||||
lenientFloatCode=lenientFloatCode))
|
||||
|
||||
if needsUnwrap:
|
||||
|
@ -6832,11 +6895,19 @@ class CGPerSignatureCall(CGThing):
|
|||
CGIfWrapper(CGList(xraySteps),
|
||||
"objIsXray"))
|
||||
|
||||
cgThings.append(CGCallGenerator(
|
||||
self.getErrorReport() if self.isFallible() else None,
|
||||
self.getArguments(), argsPre, returnType,
|
||||
self.extendedAttributes, descriptor, nativeMethodName,
|
||||
static, argsPost=argsPost, resultVar=resultVar))
|
||||
# If this is a method that was generated by a maplike/setlike
|
||||
# interface, use the maplike/setlike generator to fill in the body.
|
||||
# Otherwise, use CGCallGenerator to call the native method.
|
||||
if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeMethod():
|
||||
cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
|
||||
idlNode.maplikeOrSetlike,
|
||||
idlNode.identifier.name))
|
||||
else:
|
||||
cgThings.append(CGCallGenerator(
|
||||
self.getErrorReport() if self.isFallible() else None,
|
||||
self.getArguments(), argsPre, returnType,
|
||||
self.extendedAttributes, descriptor, nativeMethodName,
|
||||
static, argsPost=argsPost, resultVar=resultVar))
|
||||
self.cgRoot = CGList(cgThings)
|
||||
|
||||
def getArguments(self):
|
||||
|
@ -7051,7 +7122,10 @@ class CGMethodCall(CGThing):
|
|||
self.cgRoot = CGList([getPerSignatureCall(signature)])
|
||||
requiredArgs = requiredArgCount(signature)
|
||||
|
||||
if requiredArgs > 0:
|
||||
# Skip required arguments check for maplike/setlike interfaces, as
|
||||
# they can have arguments which are not passed, and are treated as
|
||||
# if undefined had been explicitly passed.
|
||||
if requiredArgs > 0 and not method.isMaplikeOrSetlikeMethod():
|
||||
code = fill(
|
||||
"""
|
||||
if (MOZ_UNLIKELY(args.length() < ${requiredArgs})) {
|
||||
|
@ -7146,7 +7220,7 @@ class CGMethodCall(CGThing):
|
|||
# possibleSignatures[0]
|
||||
caseBody = [CGArgumentConverter(possibleSignatures[0][1][i],
|
||||
i, descriptor,
|
||||
argDesc % (i + 1))
|
||||
argDesc % (i + 1), method)
|
||||
for i in range(0, distinguishingIndex)]
|
||||
|
||||
# Select the right overload from our set.
|
||||
|
@ -7438,7 +7512,12 @@ class FakeArgument():
|
|||
self.variadic = False
|
||||
self.defaultValue = None
|
||||
self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
|
||||
self.treatNullAs = interfaceMember.treatNullAs
|
||||
# For FakeArguments generated by maplike/setlike convenience functions,
|
||||
# we won't have an interfaceMember to pass in.
|
||||
if interfaceMember:
|
||||
self.treatNullAs = interfaceMember.treatNullAs
|
||||
else:
|
||||
self.treatNullAs = "Default"
|
||||
if isinstance(interfaceMember, IDLAttribute):
|
||||
self.enforceRange = interfaceMember.enforceRange
|
||||
self.clamp = interfaceMember.clamp
|
||||
|
@ -8008,6 +8087,12 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
|||
CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
|
||||
|
||||
def definition_body(self):
|
||||
if self.attr.maplikeOrSetlike:
|
||||
# If the interface is maplike/setlike, there will be one getter
|
||||
# method for the size property of the backing object. Due to having
|
||||
# to unpack the backing object from the slot, this requires its own
|
||||
# generator.
|
||||
return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr)
|
||||
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
|
||||
self.attr)
|
||||
if self.attr.slotIndex is not None:
|
||||
|
@ -11263,6 +11348,10 @@ class CGDescriptor(CGThing):
|
|||
crossOriginMethods.add(m.identifier.name)
|
||||
if m.getExtendedAttribute("MethodIdentityTestable"):
|
||||
cgThings.append(CGMethodIdentityTest(descriptor, m))
|
||||
# If we've hit the maplike/setlike member itself, go ahead and
|
||||
# generate its convenience functions.
|
||||
elif m.isMaplikeOrSetlike():
|
||||
cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
|
||||
elif m.isAttr():
|
||||
if m.stringifier:
|
||||
raise TypeError("Stringifier attributes not supported yet. "
|
||||
|
@ -12438,6 +12527,15 @@ class CGForwardDeclarations(CGWrapper):
|
|||
# Needed for at least Wrap.
|
||||
for d in descriptors:
|
||||
builder.add(d.nativeType)
|
||||
# If we're an interface and we have a maplike/setlike declaration,
|
||||
# we'll have helper functions exposed to the native side of our
|
||||
# bindings, which will need to show up in the header. If either of
|
||||
# our key/value types are interfaces, they'll be passed as
|
||||
# arguments to helper functions, and they'll need to be forward
|
||||
# declared in the header.
|
||||
if d.interface.maplikeOrSetlike:
|
||||
forwardDeclareForType(d.interface.maplikeOrSetlike.keyType)
|
||||
forwardDeclareForType(d.interface.maplikeOrSetlike.valueType)
|
||||
|
||||
# We just about always need NativePropertyHooks
|
||||
builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
|
||||
|
@ -14070,8 +14168,12 @@ class FakeMember():
|
|||
|
||||
|
||||
class CallbackMember(CGNativeMember):
|
||||
# XXXbz It's OK to use CallbackPreserveColor for wrapScope because
|
||||
# CallSetup already handled the unmark-gray bits for us. we don't have
|
||||
# anything better to use for 'obj', really...
|
||||
def __init__(self, sig, name, descriptorProvider, needThisHandling,
|
||||
rethrowContentException=False, typedArraysAreStructs=False):
|
||||
rethrowContentException=False, typedArraysAreStructs=False,
|
||||
wrapScope='CallbackPreserveColor()'):
|
||||
"""
|
||||
needThisHandling is True if we need to be able to accept a specified
|
||||
thisObj, False otherwise.
|
||||
|
@ -14095,6 +14197,8 @@ class CallbackMember(CGNativeMember):
|
|||
# will handle generating public versions that handle the "this" stuff.
|
||||
visibility = "private" if needThisHandling else "public"
|
||||
self.rethrowContentException = rethrowContentException
|
||||
|
||||
self.wrapScope = wrapScope
|
||||
# We don't care, for callback codegen, whether our original member was
|
||||
# a method or attribute or whatnot. Just always pass FakeMember()
|
||||
# here.
|
||||
|
@ -14214,10 +14318,7 @@ class CallbackMember(CGNativeMember):
|
|||
'successCode': "continue;\n" if arg.variadic else "break;\n",
|
||||
'jsvalRef': "argv[%s]" % jsvalIndex,
|
||||
'jsvalHandle': "argv[%s]" % jsvalIndex,
|
||||
# XXXbz we don't have anything better to use for 'obj',
|
||||
# really... It's OK to use CallbackPreserveColor because
|
||||
# CallSetup already handled the unmark-gray bits for us.
|
||||
'obj': 'CallbackPreserveColor()',
|
||||
'obj': self.wrapScope,
|
||||
'returnsNewObject': False,
|
||||
'exceptionCode': self.exceptionCode,
|
||||
'typedArraysAreStructs': self.typedArraysAreStructs
|
||||
|
@ -14546,6 +14647,497 @@ class CGJSImplInitOperation(CallbackOperationBase):
|
|||
return "__init"
|
||||
|
||||
|
||||
def getMaplikeOrSetlikeErrorReturn(helperImpl):
|
||||
"""
|
||||
Generate return values based on whether a maplike or setlike generated
|
||||
method is an interface method (which returns bool) or a helper function
|
||||
(which uses ErrorResult).
|
||||
"""
|
||||
if helperImpl:
|
||||
return dedent(
|
||||
"""
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return%s;
|
||||
""" % helperImpl.getDefaultRetval())
|
||||
return "return false;\n"
|
||||
|
||||
|
||||
def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None):
|
||||
"""
|
||||
Generate code to get/create a JS backing object for a maplike/setlike
|
||||
declaration from the declaration slot.
|
||||
"""
|
||||
func_prefix = maplikeOrSetlike.maplikeOrSetlikeType.title()
|
||||
ret = fill(
|
||||
"""
|
||||
JS::Rooted<JSObject*> backingObj(cx);
|
||||
bool created = false;
|
||||
if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) {
|
||||
$*{errorReturn}
|
||||
}
|
||||
if (created) {
|
||||
PreserveWrapper<${selfType}>(self);
|
||||
}
|
||||
""",
|
||||
slot=memberReservedSlot(maplikeOrSetlike),
|
||||
func_prefix=func_prefix,
|
||||
errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
|
||||
selfType=descriptor.nativeType)
|
||||
return ret
|
||||
|
||||
|
||||
def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr):
|
||||
"""
|
||||
Creates the body for the size getter method of maplike/setlike interfaces.
|
||||
"""
|
||||
# We should only have one declaration attribute currently
|
||||
assert attr.identifier.name == "size"
|
||||
assert attr.isMaplikeOrSetlikeAttr()
|
||||
return fill(
|
||||
"""
|
||||
$*{getBackingObj}
|
||||
uint32_t result = JS::${funcPrefix}Size(cx, backingObj);
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
args.rval().setNumber(result);
|
||||
return true;
|
||||
""",
|
||||
getBackingObj=getMaplikeOrSetlikeBackingObject(descriptor,
|
||||
attr.maplikeOrSetlike),
|
||||
funcPrefix=attr.maplikeOrSetlike.prefix)
|
||||
|
||||
|
||||
class CGMaplikeOrSetlikeMethodGenerator(CGThing):
|
||||
"""
|
||||
Creates methods for maplike/setlike interfaces. It is expected that all
|
||||
methods will be have a maplike/setlike object attached. Unwrapping/wrapping
|
||||
will be taken care of by the usual method generation machinery in
|
||||
CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
|
||||
using CGCallGenerator.
|
||||
"""
|
||||
def __init__(self, descriptor, maplikeOrSetlike, methodName,
|
||||
helperImpl=None):
|
||||
CGThing.__init__(self)
|
||||
# True if this will be the body of a C++ helper function.
|
||||
self.helperImpl = helperImpl
|
||||
self.descriptor = descriptor
|
||||
self.maplikeOrSetlike = maplikeOrSetlike
|
||||
self.cgRoot = CGList([])
|
||||
impl_method_name = methodName
|
||||
if impl_method_name[0] == "_":
|
||||
# double underscore means this is a js-implemented chrome only rw
|
||||
# function. Truncate the double underscore so calling the right
|
||||
# underlying JSAPI function still works.
|
||||
impl_method_name = impl_method_name[2:]
|
||||
self.cgRoot.append(CGGeneric(
|
||||
getMaplikeOrSetlikeBackingObject(self.descriptor,
|
||||
self.maplikeOrSetlike,
|
||||
self.helperImpl)))
|
||||
self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl)
|
||||
|
||||
# Generates required code for the method. Method descriptions included
|
||||
# in definitions below. Throw if we don't have a method to fill in what
|
||||
# we're looking for.
|
||||
try:
|
||||
methodGenerator = getattr(self, impl_method_name)
|
||||
except AttributeError:
|
||||
raise TypeError("Missing %s method definition '%s'" %
|
||||
(self.maplikeOrSetlike.maplikeOrSetlikeType,
|
||||
methodName))
|
||||
# Method generator returns tuple, containing:
|
||||
#
|
||||
# - a list of CGThings representing setup code for preparing to call
|
||||
# the JS API function
|
||||
# - a list of arguments needed for the JS API function we're calling
|
||||
# - list of code CGThings needed for return value conversion.
|
||||
(setupCode, arguments, setResult) = methodGenerator()
|
||||
|
||||
# Create the actual method call, and then wrap it with the code to
|
||||
# return the value if needed.
|
||||
funcName = (self.maplikeOrSetlike.prefix +
|
||||
MakeNativeName(impl_method_name))
|
||||
# Append the list of setup code CGThings
|
||||
self.cgRoot.append(CGList(setupCode))
|
||||
# Create the JS API call
|
||||
self.cgRoot.append(CGWrapper(
|
||||
CGGeneric(fill(
|
||||
"""
|
||||
if (!JS::${funcName}(${args})) {
|
||||
$*{errorReturn}
|
||||
}
|
||||
""",
|
||||
funcName=funcName,
|
||||
args=", ".join(["cx", "backingObj"] + arguments),
|
||||
errorReturn=self.returnStmt))))
|
||||
# Append result conversion
|
||||
self.cgRoot.append(CGList(setResult))
|
||||
|
||||
def mergeTuples(self, a, b):
|
||||
"""
|
||||
Expecting to take 2 tuples were all elements are lists, append the lists in
|
||||
the second tuple to the lists in the first.
|
||||
"""
|
||||
return tuple([x + y for x, y in zip(a, b)])
|
||||
|
||||
def appendArgConversion(self, name):
|
||||
"""
|
||||
Generate code to convert arguments to JS::Values, so they can be
|
||||
passed into JSAPI functions.
|
||||
"""
|
||||
return CGGeneric(fill(
|
||||
"""
|
||||
JS::Rooted<JS::Value> ${name}Val(cx);
|
||||
if (!ToJSValue(cx, ${name}, &${name}Val)) {
|
||||
$*{errorReturn}
|
||||
}
|
||||
""",
|
||||
name=name,
|
||||
errorReturn=self.returnStmt))
|
||||
|
||||
def appendKeyArgConversion(self):
|
||||
"""
|
||||
Generates the key argument for methods. Helper functions will use
|
||||
an AutoValueVector, while interface methods have seperate JS::Values.
|
||||
"""
|
||||
if self.helperImpl:
|
||||
return ([], ["argv[0]"], [])
|
||||
return ([self.appendArgConversion("arg0")], ["arg0Val"], [])
|
||||
|
||||
def appendKeyAndValueArgConversion(self):
|
||||
"""
|
||||
Generates arguments for methods that require a key and value. Helper
|
||||
functions will use an AutoValueVector, while interface methods have
|
||||
seperate JS::Values.
|
||||
"""
|
||||
r = self.appendKeyArgConversion()
|
||||
if self.helperImpl:
|
||||
return self.mergeTuples(r, ([], ["argv[1]"], []))
|
||||
return self.mergeTuples(r, ([self.appendArgConversion("arg1")],
|
||||
["arg1Val"],
|
||||
[]))
|
||||
|
||||
def appendIteratorResult(self):
|
||||
"""
|
||||
Generate code to output JSObject* return values, needed for functions that
|
||||
return iterators. Iterators cannot currently be wrapped via Xrays. If
|
||||
something that would return an iterator is called via Xray, fail early.
|
||||
"""
|
||||
# TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
|
||||
code = CGGeneric(dedent(
|
||||
"""
|
||||
// TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
|
||||
// after bug 1023984 is fixed.
|
||||
if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
|
||||
JS_ReportError(cx, "Xray wrapping of iterators not supported.");
|
||||
return false;
|
||||
}
|
||||
JS::Rooted<JSObject*> result(cx);
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
"""))
|
||||
arguments = "&v"
|
||||
setResult = CGGeneric(dedent(
|
||||
"""
|
||||
result = &v.toObject();
|
||||
"""))
|
||||
return ([code], [arguments], [setResult])
|
||||
|
||||
def appendSelfResult(self):
|
||||
"""
|
||||
Generate code to return the interface object itself.
|
||||
"""
|
||||
code = CGGeneric(dedent(
|
||||
"""
|
||||
JS::Rooted<JSObject*> result(cx);
|
||||
"""))
|
||||
setResult = CGGeneric(dedent(
|
||||
"""
|
||||
result = obj;
|
||||
"""))
|
||||
return ([code], [], [setResult])
|
||||
|
||||
def appendBoolResult(self):
|
||||
if self.helperImpl:
|
||||
return ([CGGeneric()], ["&aRetVal"], [])
|
||||
return ([CGGeneric("bool result;\n")], ["&result"], [])
|
||||
|
||||
def forEach(self):
|
||||
"""
|
||||
void forEach(callback c, any thisval);
|
||||
|
||||
ForEach takes a callback, and a possible value to use as 'this'. The
|
||||
callback needs to take value, key, and the interface object
|
||||
implementing maplike/setlike. In order to make sure that the third arg
|
||||
is our interface object instead of the map/set backing object, we
|
||||
create a js function with the callback and original object in its
|
||||
storage slots, then use a helper function in BindingUtils to make sure
|
||||
the callback is called correctly.
|
||||
"""
|
||||
assert(not self.helperImpl)
|
||||
code = [CGGeneric(dedent(
|
||||
"""
|
||||
// Create a wrapper function.
|
||||
JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr);
|
||||
if (!func) {
|
||||
return false;
|
||||
}
|
||||
JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func));
|
||||
JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj));
|
||||
js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT,
|
||||
JS::ObjectValue(*arg0));
|
||||
js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT,
|
||||
JS::ObjectValue(*obj));
|
||||
"""))]
|
||||
arguments = ["funcVal", "arg1"]
|
||||
return (code, arguments, [])
|
||||
|
||||
def set(self):
|
||||
"""
|
||||
object set(key, value);
|
||||
|
||||
Maplike only function, takes key and sets value to it, returns
|
||||
interface object unless being called from a C++ helper.
|
||||
"""
|
||||
assert self.maplikeOrSetlike.isMaplike()
|
||||
r = self.appendKeyAndValueArgConversion()
|
||||
if self.helperImpl:
|
||||
return r
|
||||
return self.mergeTuples(r, self.appendSelfResult())
|
||||
|
||||
def add(self):
|
||||
"""
|
||||
object add(value);
|
||||
|
||||
Setlike only function, adds value to set, returns interface object
|
||||
unless being called from a C++ helper
|
||||
"""
|
||||
assert self.maplikeOrSetlike.isSetlike()
|
||||
r = self.appendKeyArgConversion()
|
||||
if self.helperImpl:
|
||||
return r
|
||||
return self.mergeTuples(r, self.appendSelfResult())
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
type? get(key);
|
||||
|
||||
Retrieves a value from a backing object based on the key. Returns value
|
||||
if key is in backing object, undefined otherwise.
|
||||
"""
|
||||
assert self.maplikeOrSetlike.isMaplike()
|
||||
r = self.appendKeyArgConversion()
|
||||
code = [CGGeneric(dedent(
|
||||
"""
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
"""))]
|
||||
arguments = ["&result"]
|
||||
return self.mergeTuples(r, (code, arguments, []))
|
||||
|
||||
def has(self):
|
||||
"""
|
||||
bool has(key);
|
||||
|
||||
Check if an entry exists in the backing object. Returns true if value
|
||||
exists in backing object, false otherwise.
|
||||
"""
|
||||
return self.mergeTuples(self.appendKeyArgConversion(),
|
||||
self.appendBoolResult())
|
||||
|
||||
def keys(self):
|
||||
"""
|
||||
object keys();
|
||||
|
||||
Returns new object iterator with all keys from backing object.
|
||||
"""
|
||||
return self.appendIteratorResult()
|
||||
|
||||
def values(self):
|
||||
"""
|
||||
object values();
|
||||
|
||||
Returns new object iterator with all values from backing object.
|
||||
"""
|
||||
return self.appendIteratorResult()
|
||||
|
||||
def entries(self):
|
||||
"""
|
||||
object entries();
|
||||
|
||||
Returns new object iterator with all keys and values from backing
|
||||
object. Keys will be null for set.
|
||||
"""
|
||||
return self.appendIteratorResult()
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
void clear();
|
||||
|
||||
Removes all entries from map/set.
|
||||
"""
|
||||
return ([], [], [])
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
bool delete(key);
|
||||
|
||||
Deletes an entry from the backing object. Returns true if value existed
|
||||
in backing object, false otherwise.
|
||||
"""
|
||||
return self.mergeTuples(self.appendKeyArgConversion(),
|
||||
self.appendBoolResult())
|
||||
|
||||
def define(self):
|
||||
return self.cgRoot.define()
|
||||
|
||||
|
||||
class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember):
|
||||
"""
|
||||
Generates code to allow C++ to perform operations on backing objects. Gets
|
||||
a context from the binding wrapper, turns arguments into JS::Values (via
|
||||
CallbackMember/CGNativeMember argument conversion), then uses
|
||||
CGMaplikeOrSetlikeMethodGenerator to generate the body.
|
||||
|
||||
"""
|
||||
|
||||
class HelperFunction(CGAbstractMethod):
|
||||
"""
|
||||
Generates context retrieval code and rooted JSObject for interface for
|
||||
CGMaplikeOrSetlikeMethodGenerator to use
|
||||
"""
|
||||
def __init__(self, descriptor, name, args, code, needsBoolReturn=False):
|
||||
self.code = code
|
||||
CGAbstractMethod.__init__(self, descriptor, name,
|
||||
"bool" if needsBoolReturn else "void",
|
||||
args)
|
||||
|
||||
def definition_body(self):
|
||||
return self.code
|
||||
|
||||
def __init__(self, descriptor, maplikeOrSetlike, name, needsKeyArg=False,
|
||||
needsValueArg=False, needsBoolReturn=False):
|
||||
args = []
|
||||
self.maplikeOrSetlike = maplikeOrSetlike
|
||||
self.needsBoolReturn = needsBoolReturn
|
||||
if needsKeyArg:
|
||||
args.append(FakeArgument(maplikeOrSetlike.keyType, None, 'aKey'))
|
||||
if needsValueArg:
|
||||
assert needsKeyArg
|
||||
args.append(FakeArgument(maplikeOrSetlike.valueType, None, 'aValue'))
|
||||
# Run CallbackMember init function to generate argument conversion code.
|
||||
# wrapScope is set to 'obj' when generating maplike or setlike helper
|
||||
# functions, as we don't have access to the CallbackPreserveColor
|
||||
# method.
|
||||
CallbackMember.__init__(self,
|
||||
[BuiltinTypes[IDLBuiltinType.Types.void], args],
|
||||
name, descriptor, False,
|
||||
wrapScope='obj')
|
||||
# Wrap CallbackMember body code into a CGAbstractMethod to make
|
||||
# generation easier.
|
||||
self.implMethod = CGMaplikeOrSetlikeHelperFunctionGenerator.HelperFunction(
|
||||
descriptor, name, self.args, self.body, needsBoolReturn)
|
||||
|
||||
def getCallSetup(self):
|
||||
return dedent(
|
||||
"""
|
||||
MOZ_ASSERT(self);
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
jsapi.TakeOwnershipOfErrorReporting();
|
||||
JSContext* cx = jsapi.cx();
|
||||
JSAutoCompartment tempCompartment(cx, xpc::UnprivilegedJunkScope());
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
if(!ToJSValue(cx, self, &v)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return%s;
|
||||
}
|
||||
// This is a reflector, but due to trying to name things
|
||||
// similarly across method generators, it's called obj here.
|
||||
JS::Rooted<JSObject*> obj(cx);
|
||||
obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtOuter = */ false);
|
||||
JSAutoCompartment reflectorCompartment(cx, obj);
|
||||
""" % self.getDefaultRetval())
|
||||
|
||||
def getArgs(self, returnType, argList):
|
||||
# We don't need the context or the value. We'll generate those instead.
|
||||
args = CGNativeMember.getArgs(self, returnType, argList)
|
||||
# Prepend a pointer to the binding object onto the arguments
|
||||
return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args
|
||||
|
||||
def getResultConversion(self):
|
||||
if self.needsBoolReturn:
|
||||
return "return aRetVal;\n"
|
||||
return "return;\n"
|
||||
|
||||
def getRvalDecl(self):
|
||||
if self.needsBoolReturn:
|
||||
return "bool aRetVal;\n"
|
||||
return ""
|
||||
|
||||
def getArgcDecl(self):
|
||||
# Don't need argc for anything.
|
||||
return None
|
||||
|
||||
def getDefaultRetval(self):
|
||||
if self.needsBoolReturn:
|
||||
return " false"
|
||||
return ""
|
||||
|
||||
|
||||
def getCall(self):
|
||||
return CGMaplikeOrSetlikeMethodGenerator(self.descriptorProvider,
|
||||
self.maplikeOrSetlike,
|
||||
self.name.lower(),
|
||||
helperImpl=self).define()
|
||||
|
||||
def getPrettyName(self):
|
||||
return self.name
|
||||
|
||||
def declare(self):
|
||||
return self.implMethod.declare()
|
||||
|
||||
def define(self):
|
||||
return self.implMethod.define()
|
||||
|
||||
|
||||
class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
|
||||
"""
|
||||
Declares and defines convenience methods for accessing backing objects on
|
||||
setlike/maplike interface. Generates function signatures, un/packs
|
||||
backing objects from slot, etc.
|
||||
"""
|
||||
def __init__(self, descriptor, maplikeOrSetlike):
|
||||
self.descriptor = descriptor
|
||||
self.maplikeOrSetlike = maplikeOrSetlike
|
||||
self.namespace = "%sHelpers" % (self.maplikeOrSetlike.maplikeOrSetlikeType.title())
|
||||
self.helpers = [
|
||||
CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
|
||||
maplikeOrSetlike,
|
||||
"Clear"),
|
||||
CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
|
||||
maplikeOrSetlike,
|
||||
"Delete",
|
||||
needsKeyArg=True,
|
||||
needsBoolReturn=True),
|
||||
CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
|
||||
maplikeOrSetlike,
|
||||
"Has",
|
||||
needsKeyArg=True,
|
||||
needsBoolReturn=True)]
|
||||
if self.maplikeOrSetlike.isMaplike():
|
||||
self.helpers.append(
|
||||
CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
|
||||
maplikeOrSetlike,
|
||||
"Set",
|
||||
needsKeyArg=True,
|
||||
needsValueArg=True))
|
||||
else:
|
||||
assert(self.maplikeOrSetlike.isSetlike())
|
||||
self.helpers.append(
|
||||
CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
|
||||
maplikeOrSetlike,
|
||||
"Add",
|
||||
needsKeyArg=True))
|
||||
CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
|
||||
|
||||
|
||||
class GlobalGenRoots():
|
||||
"""
|
||||
Roots for global codegen.
|
||||
|
|
|
@ -209,6 +209,15 @@ ToJSValue(JSContext* aCx,
|
|||
return ToJSValue(aCx, *aArgument.get(), aValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
ToJSValue(JSContext* aCx,
|
||||
const NonNull<T>& aArgument,
|
||||
JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
return ToJSValue(aCx, *aArgument.get(), aValue);
|
||||
}
|
||||
|
||||
// Accept WebIDL dictionaries
|
||||
template <class T>
|
||||
MOZ_WARN_UNUSED_RESULT
|
||||
|
|
|
@ -84,6 +84,25 @@ SOURCES += [
|
|||
'StructuredClone.cpp',
|
||||
]
|
||||
|
||||
# Tests for maplike and setlike require bindings to be built, which means they
|
||||
# must be included in libxul. This breaks the "no test classes are exported"
|
||||
# rule stated in the test/ directory, but it's the only way this will work.
|
||||
# Test classes are only built in debug mode, and all tests requiring use of
|
||||
# them are only run in debug mode.
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
EXPORTS.mozilla.dom += [
|
||||
"test/TestInterfaceMaplike.h",
|
||||
"test/TestInterfaceMaplikeObject.h",
|
||||
"test/TestInterfaceSetlike.h",
|
||||
"test/TestInterfaceSetlikeNode.h"
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
"test/TestInterfaceMaplike.cpp",
|
||||
"test/TestInterfaceMaplikeObject.cpp",
|
||||
"test/TestInterfaceSetlike.cpp",
|
||||
"test/TestInterfaceSetlikeNode.cpp",
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']:
|
||||
|
|
|
@ -3399,6 +3399,7 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
|||
[self.location])
|
||||
self.aliases.append(alias)
|
||||
|
||||
|
||||
# MaplikeOrSetlike adds a trait to an interface, like map or iteration
|
||||
# functions. To handle them while still getting all of the generated binding
|
||||
# code taken care of, we treat them as macros that are expanded into members
|
||||
|
@ -3425,6 +3426,12 @@ class IDLMaplikeOrSetlike(IDLInterfaceMember):
|
|||
self.disallowedMemberNames = []
|
||||
self.disallowedNonMethodNames = []
|
||||
|
||||
# When generating JSAPI access code, we need to know the backing object
|
||||
# type prefix to create the correct function. Generate here for reuse.
|
||||
if self.isMaplike():
|
||||
self.prefix = 'Map'
|
||||
elif self.isSetlike():
|
||||
self.prefix = 'Set'
|
||||
|
||||
def __str__(self):
|
||||
return "declared '%s' with key '%s'" % (self.maplikeOrSetlikeType, self.keyType)
|
||||
|
@ -4349,7 +4356,15 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
return self._hasOverloads
|
||||
|
||||
def isIdentifierLess(self):
|
||||
return self.identifier.name[:2] == "__"
|
||||
"""
|
||||
True if the method name started with __, and if the method is not a
|
||||
maplike/setlike method. Interfaces with maplike/setlike will generate
|
||||
methods starting with __ for chrome only backing object access in JS
|
||||
implemented interfaces, so while these functions use what is considered
|
||||
an non-identifier name, they actually DO have an identifier.
|
||||
"""
|
||||
return (self.identifier.name[:2] == "__" and
|
||||
not self.isMaplikeOrSetlikeMethod())
|
||||
|
||||
def resolve(self, parentScope):
|
||||
assert isinstance(parentScope, IDLScope)
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
component {2ac4e026-cf25-47d5-b067-78d553c3cad8} TestInterfaceJS.js
|
||||
contract @mozilla.org/dom/test-interface-js;1 {2ac4e026-cf25-47d5-b067-78d553c3cad8}
|
||||
component {4bc6f6f3-e005-4f0a-b42d-4d1663a9013a} TestInterfaceJSMaplike.js
|
||||
contract @mozilla.org/dom/test-interface-js-maplike;1 {4bc6f6f3-e005-4f0a-b42d-4d1663a9013a}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function TestInterfaceJSMaplike() {}
|
||||
|
||||
TestInterfaceJSMaplike.prototype = {
|
||||
classID: Components.ID("{4bc6f6f3-e005-4f0a-b42d-4d1663a9013a}"),
|
||||
contractID: "@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
__init: function () {},
|
||||
|
||||
setInternal: function(aKey, aValue) {
|
||||
return this.__DOM_IMPL__.__set(aKey, aValue);
|
||||
},
|
||||
|
||||
deleteInternal: function(aKey) {
|
||||
return this.__DOM_IMPL__.__delete(aKey);
|
||||
},
|
||||
|
||||
clearInternal: function() {
|
||||
return this.__DOM_IMPL__.__clear();
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJSMaplike])
|
|
@ -0,0 +1,84 @@
|
|||
/* 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/TestInterfaceMaplike.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceMaplike, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceMaplike)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceMaplike)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceMaplike)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceMaplike::TestInterfaceMaplike(nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceMaplike>
|
||||
TestInterfaceMaplike::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceMaplike> r = new TestInterfaceMaplike(window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceMaplike::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceMaplikeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceMaplike::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplike::SetInternal(const nsAString& aKey, int32_t aValue)
|
||||
{
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeBinding::MaplikeHelpers::Set(this, aKey, aValue, rv);
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplike::ClearInternal()
|
||||
{
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeBinding::MaplikeHelpers::Clear(this, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplike::DeleteInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeBinding::MaplikeHelpers::Delete(this, aKey, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplike::HasInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeBinding::MaplikeHelpers::Has(this, aKey, rv);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_TestInterfaceMaplike_h
|
||||
#define mozilla_dom_TestInterfaceMaplike_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl maplike interfaces, using
|
||||
// primitives for key and value types.
|
||||
class TestInterfaceMaplike final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceMaplike)
|
||||
|
||||
explicit TestInterfaceMaplike(nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceMaplike>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||
|
||||
// External access for testing internal convenience functions.
|
||||
void SetInternal(const nsAString& aKey, int32_t aValue);
|
||||
void ClearInternal();
|
||||
bool DeleteInternal(const nsAString& aKey);
|
||||
bool HasInternal(const nsAString& aKey);
|
||||
private:
|
||||
virtual ~TestInterfaceMaplike() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceMaplike_h
|
|
@ -0,0 +1,88 @@
|
|||
/* 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/TestInterfaceMaplikeObject.h"
|
||||
#include "mozilla/dom/TestInterfaceMaplike.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceMaplikeObject, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceMaplikeObject)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceMaplikeObject)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceMaplikeObject)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceMaplikeObject::TestInterfaceMaplikeObject(nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceMaplikeObject>
|
||||
TestInterfaceMaplikeObject::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceMaplikeObject> r =
|
||||
new TestInterfaceMaplikeObject(window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceMaplikeObject::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceMaplikeObjectBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceMaplikeObject::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplikeObject::SetInternal(const nsAString& aKey)
|
||||
{
|
||||
nsRefPtr<TestInterfaceMaplike> p(new TestInterfaceMaplike(mParent));
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Set(this, aKey, *p, rv);
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplikeObject::ClearInternal()
|
||||
{
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Clear(this, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplikeObject::DeleteInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Delete(this, aKey, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplikeObject::HasInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Has(this, aKey, rv);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_TestInterfaceMaplikeObject_h
|
||||
#define mozilla_dom_TestInterfaceMaplikeObject_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl maplike interfaces, using
|
||||
// primitives for key types and objects for value types.
|
||||
class TestInterfaceMaplikeObject final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceMaplikeObject)
|
||||
|
||||
explicit TestInterfaceMaplikeObject(nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceMaplikeObject>
|
||||
Constructor(const GlobalObject& aGlobal,ErrorResult& rv);
|
||||
|
||||
// External access for testing internal convenience functions.
|
||||
void SetInternal(const nsAString& aKey);
|
||||
void ClearInternal();
|
||||
bool DeleteInternal(const nsAString& aKey);
|
||||
bool HasInternal(const nsAString& aKey);
|
||||
private:
|
||||
virtual ~TestInterfaceMaplikeObject() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceMaplikeObject_h
|
|
@ -0,0 +1,58 @@
|
|||
/* 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/TestInterfaceSetlike.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceSetlike, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceSetlike)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceSetlike)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceSetlike)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceSetlike::TestInterfaceSetlike(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceSetlike>
|
||||
TestInterfaceSetlike::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceSetlike> r = new TestInterfaceSetlike(nullptr, window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceSetlike::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceSetlikeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceSetlike::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_TestInterfaceSetlike_h
|
||||
#define mozilla_dom_TestInterfaceSetlike_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl setlike interfaces, using
|
||||
// primitives for key type.
|
||||
class TestInterfaceSetlike final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceSetlike)
|
||||
explicit TestInterfaceSetlike(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceSetlike>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||
private:
|
||||
virtual ~TestInterfaceSetlike() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceSetlike_h
|
|
@ -0,0 +1,58 @@
|
|||
/* 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/TestInterfaceSetlikeNode.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceSetlikeNode, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceSetlikeNode)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceSetlikeNode)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceSetlikeNode)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceSetlikeNode::TestInterfaceSetlikeNode(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceSetlikeNode>
|
||||
TestInterfaceSetlikeNode::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceSetlikeNode> r = new TestInterfaceSetlikeNode(nullptr, window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceSetlikeNode::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceSetlikeNodeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceSetlikeNode::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_TestInterfaceSetlikeNode_h
|
||||
#define mozilla_dom_TestInterfaceSetlikeNode_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl setlike interfaces, using
|
||||
// primitives for key type.
|
||||
class TestInterfaceSetlikeNode final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceSetlikeNode)
|
||||
explicit TestInterfaceSetlikeNode(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceSetlikeNode>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||
private:
|
||||
virtual ~TestInterfaceSetlikeNode() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceSetlikeNode_h
|
|
@ -14,3 +14,5 @@ support-files =
|
|||
skip-if = e10s # prerendering doesn't work in e10s yet
|
||||
[test_kill_longrunning_prerendered_content.xul]
|
||||
skip-if = e10s # prerendering doesn't work in e10s yet
|
||||
[test_bug1123516_maplikesetlikechrome.xul]
|
||||
skip-if = debug == false
|
||||
|
|
|
@ -61,3 +61,5 @@ skip-if = debug == false
|
|||
[test_worker_UnwrapArg.html]
|
||||
[test_unforgeablesonexpando.html]
|
||||
[test_crossOriginWindowSymbolAccess.html]
|
||||
[test_bug1123516_maplikesetlike.html]
|
||||
skip-if = debug == false
|
|
@ -17,6 +17,7 @@ Library('dombindings_test_s')
|
|||
EXTRA_COMPONENTS += [
|
||||
'TestInterfaceJS.js',
|
||||
'TestInterfaceJS.manifest',
|
||||
'TestInterfaceJSMaplike.js'
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Maplike Interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
|
||||
|
||||
base_properties = [["has", "function", 1],
|
||||
["entries", "function", 0],
|
||||
["keys", "function", 0],
|
||||
["values", "function", 0],
|
||||
["forEach", "function", 1],
|
||||
["size", "number"]];
|
||||
maplike_properties = base_properties.concat([["set", "function", 2]]);
|
||||
setlike_properties = base_properties;
|
||||
rw_properties = [["clear", "function", 0],
|
||||
["delete", "function", 1]];
|
||||
setlike_rw_properties = base_properties.concat(rw_properties).concat([["add", "function", 1]]);
|
||||
maplike_rw_properties = maplike_properties.concat(rw_properties).concat([["get", "function",1]]);
|
||||
var testExistence = function testExistence(prefix, obj, properties) {
|
||||
for (var [name, type, args] of properties) {
|
||||
// Properties are somewhere up the proto chain, hasOwnProperty won't work
|
||||
isnot(obj[name], undefined,
|
||||
`${prefix} object has property ${name}`);
|
||||
|
||||
is(typeof obj[name], type,
|
||||
`${prefix} object property ${name} is a ${type}`);
|
||||
// Check function length
|
||||
if (type == "function") {
|
||||
is(obj[name].length, args,
|
||||
`${prefix} object property ${name} is length ${args}`);
|
||||
is(obj[name].name, name,
|
||||
`${prefix} object method name is ${name}`);
|
||||
}
|
||||
|
||||
// Find where property is on proto chain, check for enumerablility there.
|
||||
var owner = obj;
|
||||
while (owner) {
|
||||
var propDesc = Object.getOwnPropertyDescriptor(owner, name);
|
||||
if (propDesc) {
|
||||
ok(!propDesc.enumerable,
|
||||
`${prefix} object property ${name} is not enumerable`);
|
||||
break;
|
||||
}
|
||||
owner = Object.getPrototypeOf(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var m;
|
||||
var testSet;
|
||||
var testIndex;
|
||||
var iterable;
|
||||
// Simple map creation and functionality test
|
||||
info("SimpleMap: Testing simple map creation and functionality");
|
||||
m = new TestInterfaceMaplike();
|
||||
ok(m, "SimpleMap: got a TestInterfaceMaplike object");
|
||||
testExistence("SimpleMap: ", m, maplike_rw_properties);
|
||||
is(m.size, 0, "SimpleMap: size should be zero");
|
||||
ok(!m.has("test"), "SimpleMap: maplike has should return false");
|
||||
is(m.get("test"), undefined, "SimpleMap: maplike get should return undefined on bogus lookup");
|
||||
m1 = m.set("test", 1);
|
||||
is(m, m1, "SimpleMap: return from set should be map object");
|
||||
is(m.size, 1, "SimpleMap: size should be 1");
|
||||
ok(m.has("test"), "SimpleMap: maplike has should return true");
|
||||
is(m.get("test"), 1, "SimpleMap: maplike get should return value entered");
|
||||
m2 = m.set("test2", 2);
|
||||
is(m.size, 2, "SimpleMap: size should be 2");
|
||||
testSet = [["test", 1], ["test2", 2]];
|
||||
testIndex = 0;
|
||||
m.forEach(function(v, k, o) {
|
||||
"use strict";
|
||||
is(o, m, "SimpleMap: foreach obj is correct");
|
||||
is(k, testSet[testIndex][0], "SimpleMap: foreach map key: " + k + " = " + testSet[testIndex][0]);
|
||||
is(v, testSet[testIndex][1], "SimpleMap: foreach map value: " + v + " = " + testSet[testIndex][1]);
|
||||
testIndex += 1;
|
||||
});
|
||||
is(testIndex, 2, "SimpleMap: foreach ran correct number of times");
|
||||
ok(m.has("test2"), "SimpleMap: maplike has should return true");
|
||||
is(m.get("test2"), 2, "SimpleMap: maplike get should return value entered");
|
||||
is(m.delete("test2"), true, "SimpleMap: maplike deletion should return boolean");
|
||||
is(m.size, 1, "SimpleMap: size should be 1");
|
||||
iterable = false;
|
||||
for (var e of m) {
|
||||
iterable = true;
|
||||
is(e[0], "test", "SimpleMap: iterable first array element should be key");
|
||||
is(e[1], 1, "SimpleMap: iterable second array element should be value");
|
||||
}
|
||||
is(m[Symbol.iterator].length, 0, "SimpleMap: @@iterator symbol is correct length");
|
||||
is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleMap: @@iterator symbol has correct name");
|
||||
ok(iterable, "SimpleMap: @@iterator symbol resolved correctly");
|
||||
for (var k of m.keys()) {
|
||||
is(k, "test", "SimpleMap: first keys element should be 'test'");
|
||||
}
|
||||
for (var v of m.values()) {
|
||||
is(v, 1, "SimpleMap: first values elements should be 1");
|
||||
}
|
||||
for (var e of m.entries()) {
|
||||
is(e[0], "test", "SimpleMap: entries first array element should be 'test'");
|
||||
is(e[1], 1, "SimpleMap: entries second array element should be 1");
|
||||
}
|
||||
m.clear();
|
||||
is(m.size, 0, "SimpleMap: size should be 0 after clear");
|
||||
|
||||
// Simple set creation and functionality test
|
||||
info("SimpleSet: Testing simple set creation and functionality");
|
||||
m = new TestInterfaceSetlike();
|
||||
ok(m, "SimpleSet: got a TestInterfaceSetlike object");
|
||||
testExistence("SimpleSet: ", m, setlike_rw_properties);
|
||||
is(m.size, 0, "SimpleSet: size should be zero");
|
||||
ok(!m.has("test"), "SimpleSet: maplike has should return false");
|
||||
m1 = m.add("test");
|
||||
is(m, m1, "SimpleSet: return from set should be map object");
|
||||
is(m.size, 1, "SimpleSet: size should be 1");
|
||||
ok(m.has("test"), "SimpleSet: maplike has should return true");
|
||||
m2 = m.add("test2");
|
||||
is(m.size, 2, "SimpleSet: size should be 2");
|
||||
testSet = ["test", "test2"];
|
||||
testIndex = 0;
|
||||
m.forEach(function(v, k, o) {
|
||||
"use strict";
|
||||
is(o, m, "SimpleSet: foreach obj is correct");
|
||||
is(k, testSet[testIndex], "SimpleSet: foreach set key: " + k + " = " + testSet[testIndex]);
|
||||
testIndex += 1;
|
||||
});
|
||||
is(testIndex, 2, "SimpleSet: foreach ran correct number of times");
|
||||
ok(m.has("test2"), "SimpleSet: maplike has should return true");
|
||||
is(m.delete("test2"), true, "SimpleSet: maplike deletion should return true");
|
||||
is(m.size, 1, "SimpleSet: size should be 1");
|
||||
iterable = false;
|
||||
for (var e of m) {
|
||||
iterable = true;
|
||||
is(e, "test", "SimpleSet: iterable first array element should be key");
|
||||
}
|
||||
is(m[Symbol.iterator].length, 0, "SimpleSet: @@iterator symbol is correct length");
|
||||
is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleSet: @@iterator symbol has correct name");
|
||||
ok(iterable, "SimpleSet: @@iterator symbol resolved correctly");
|
||||
for (var k of m.keys()) {
|
||||
is(k, "test", "SimpleSet: first keys element should be 'test'");
|
||||
}
|
||||
for (var v of m.values()) {
|
||||
is(v, "test", "SimpleSet: first values elements should be 'test'");
|
||||
}
|
||||
for (var e of m.entries()) {
|
||||
is(e[0], "test", "SimpleSet: Entries first array element should be 'test'");
|
||||
is(e[1], "test", "SimpleSet: Entries second array element should be 'test'");
|
||||
}
|
||||
m.clear();
|
||||
is(m.size, 0, "SimpleSet: size should be 0 after clear");
|
||||
|
||||
// Map convenience function test
|
||||
info("Testing map convenience functions");
|
||||
m = new TestInterfaceMaplike();
|
||||
ok(m, "MapConvenience: got a TestInterfaceMaplike object");
|
||||
is(m.size, 0, "MapConvenience: size should be zero");
|
||||
ok(!m.hasInternal("test"), "MapConvenience: maplike hasInternal should return false");
|
||||
m.setInternal("test", 1);
|
||||
is(m.size, 1, "MapConvenience: size should be 1");
|
||||
ok(m.hasInternal("test"), "MapConvenience: maplike hasInternal should return true");
|
||||
is(m.get("test"), 1, "MapConvenience: maplike get should return value entered");
|
||||
m2 = m.setInternal("test2", 2);
|
||||
is(m.size, 2, "size should be 2");
|
||||
ok(m.hasInternal("test2"), "MapConvenience: maplike hasInternal should return true");
|
||||
is(m.get("test2"), 2, "MapConvenience: maplike get should return value entered");
|
||||
is(m.deleteInternal("test2"), true, "MapConvenience: maplike deleteInternal should return true");
|
||||
is(m.size, 1, "MapConvenience: size should be 1");
|
||||
m.clearInternal();
|
||||
is(m.size, 0, "MapConvenience: size should be 0 after clearInternal");
|
||||
|
||||
// Map convenience function test using objects and readonly
|
||||
|
||||
info("Testing Map convenience function test using objects and readonly");
|
||||
m = new TestInterfaceMaplikeObject();
|
||||
ok(m, "ReadOnlyMapConvenience: got a TestInterfaceMaplikeObject object");
|
||||
is(m.size, 0, "ReadOnlyMapConvenience: size should be zero");
|
||||
is(m["set"], undefined, "ReadOnlyMapConvenience: readonly map, should be no set function");
|
||||
is(m["clear"], undefined, "ReadOnlyMapConvenience: readonly map, should be no clear function");
|
||||
is(m["delete"], undefined, "ReadOnlyMapConvenience: readonly map, should be no delete function");
|
||||
ok(!m.hasInternal("test"), "ReadOnlyMapConvenience: maplike hasInternal should return false");
|
||||
m.setInternal("test");
|
||||
is(m.size, 1, "size should be 1");
|
||||
ok(m.hasInternal("test"), "ReadOnlyMapConvenience: maplike hasInternal should return true");
|
||||
m2 = m.setInternal("test2");
|
||||
is(m.size, 2, "size should be 2");
|
||||
ok(m.hasInternal("test2"), "ReadOnlyMapConvenience: maplike hasInternal should return true");
|
||||
is(m.deleteInternal("test2"), true, "ReadOnlyMapConvenience: maplike deleteInternal should return true");
|
||||
is(m.size, 1, "ReadOnlyMapConvenience: size should be 1");
|
||||
m.clearInternal();
|
||||
is(m.size, 0, "ReadOnlyMapConvenience: size should be 0 after clearInternal");
|
||||
|
||||
// JS implemented map creation convenience function test
|
||||
|
||||
info("JSMapConvenience: Testing JS implemented map creation convenience functions");
|
||||
m = new TestInterfaceJSMaplike();
|
||||
ok(m, "JSMapConvenience: got a TestInterfaceJSMaplike object");
|
||||
is(m.size, 0, "JSMapConvenience: size should be zero");
|
||||
ok(!m.has("test"), "JSMapConvenience: maplike has should return false");
|
||||
m.setInternal("test", 1);
|
||||
is(m.size, 1, "JSMapConvenience: size should be 1");
|
||||
ok(m.has("test"), "JSMapConvenience: maplike has should return true");
|
||||
is(m.get("test"), 1, "JSMapConvenience: maplike get should return value entered");
|
||||
m2 = m.setInternal("test2", 2);
|
||||
is(m.size, 2, "JSMapConvenience: size should be 2");
|
||||
ok(m.has("test2"), "JSMapConvenience: maplike has should return true");
|
||||
is(m.get("test2"), 2, "JSMapConvenience: maplike get should return value entered");
|
||||
is(m.deleteInternal("test2"), true, "JSMapConvenience: maplike deleteInternal should return true");
|
||||
is(m.size, 1, "JSMapConvenience: size should be 1");
|
||||
for (var k of m.keys()) {
|
||||
is(k, "test", "JSMapConvenience: first keys element should be 'test'");
|
||||
}
|
||||
for (var v of m.values()) {
|
||||
is(v, 1, "JSMapConvenience: first values elements should be 1");
|
||||
}
|
||||
for (var e of m.entries()) {
|
||||
is(e[0], "test", "JSMapConvenience: entries first array element should be 'test'");
|
||||
is(e[1], 1, "JSMapConvenience: entries second array element should be 1");
|
||||
}
|
||||
m.clearInternal();
|
||||
is(m.size, 0, "JSMapConvenience: size should be 0 after clearInternal");
|
||||
|
||||
// Test this override for forEach
|
||||
info("ForEachThisOverride: Testing this override for forEach");
|
||||
m = new TestInterfaceMaplike();
|
||||
m.set("test", 1);
|
||||
m.forEach(function(v, k, o) {
|
||||
"use strict";
|
||||
is(o, m, "ForEachThisOverride: foreach obj is correct");
|
||||
is(this, 5, "ForEachThisOverride: 'this' value should be correct");
|
||||
}, 5);
|
||||
|
||||
// Test defaulting arguments on maplike to undefined
|
||||
info("MapArgsDefault: Testing maplike defaulting arguments to undefined");
|
||||
m = new TestInterfaceMaplike();
|
||||
m.set();
|
||||
is(m.size, 1, "MapArgsDefault: should have 1 entry");
|
||||
m.forEach(function(v, k) {
|
||||
"use strict";
|
||||
is(typeof k, "string", "MapArgsDefault: key is a string");
|
||||
is(k, "undefined", "MapArgsDefault: key is the string undefined");
|
||||
is(v, 0, "MapArgsDefault: value is 0");
|
||||
});
|
||||
is(m.get(), 0, "MapArgsDefault: no argument to get() returns correct value");
|
||||
m.delete();
|
||||
is(m.size, 0, "MapArgsDefault: should have 0 entries");
|
||||
|
||||
// Test defaulting arguments on setlike to undefined
|
||||
info("SetArgsDefault: Testing setlike defaulting arguments to undefined");
|
||||
m = new TestInterfaceSetlike();
|
||||
m.add();
|
||||
is(m.size, 1, "SetArgsDefault: should have 1 entry");
|
||||
m.forEach(function(v, k) {
|
||||
"use strict";
|
||||
is(typeof k, "string", "SetArgsDefault: key is a string");
|
||||
is(k, "undefined", "SetArgsDefault: key is the string undefined");
|
||||
});
|
||||
m.delete();
|
||||
is(m.size, 0, "SetArgsDefault: should have 0 entries");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1123516
|
||||
-->
|
||||
<window title="Mozilla Bug 1123516"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<iframe id="t"></iframe>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1123516"
|
||||
target="_blank">Mozilla Bug 1123516</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug 1123516 **/
|
||||
const Cu = Components.utils;
|
||||
function doTest() {
|
||||
var win = $("t").contentWindow;
|
||||
var sandbox = Components.utils.Sandbox(win, { sandboxPrototype: win });
|
||||
is(sandbox._content, undefined, "_content does nothing over Xray");
|
||||
// Test cross-compartment usage of maplike/setlike WebIDL structures.
|
||||
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
|
||||
try {
|
||||
var maplike = Components.utils.evalInSandbox("var m = new TestInterfaceMaplike(); m;", sandbox);
|
||||
maplike.set("test2", 2);
|
||||
is(maplike.get("test2"), 2, "Should be able to create and use maplike/setlike across compartments");
|
||||
var test = Components.utils.evalInSandbox("m.get('test2');", sandbox);
|
||||
is(test, 2, "Maplike/setlike should still work in original compartment");
|
||||
is(maplike.size, 1, "Testing size retrieval across compartments");
|
||||
} catch(e) {
|
||||
ok(false, "Shouldn't throw when working with cross-compartment maplike/setlike interfaces " + e)
|
||||
};
|
||||
try {
|
||||
var setlike = Components.utils.evalInSandbox("var m = new TestInterfaceSetlikeNode(); m.add(document.documentElement); m;", sandbox);
|
||||
is(TestInterfaceSetlikeNode.prototype.has.call(setlike, win.document.documentElement), true,
|
||||
"Cross-compartment unwrapping/comparison has works");
|
||||
// TODO: Should throw until iterators are handled by Xrays, Bug 1023984
|
||||
try {
|
||||
var e = TestInterfaceSetlikeNode.prototype.keys.call(setlike);
|
||||
ok(false, "Calling iterators via xrays should fail");
|
||||
} catch(e) {
|
||||
ok(true, "Calling iterators via xrays should fail");
|
||||
}
|
||||
|
||||
setlike.forEach((v,k,t) => { is(v, win.document.documentElement, "Cross-compartment forEach works"); });
|
||||
TestInterfaceSetlikeNode.prototype.forEach.call(setlike,
|
||||
(v,k,t) => { is(v, win.document.documentElement, "Cross-compartment forEach works"); });
|
||||
is(TestInterfaceSetlikeNode.prototype.delete.call(setlike, win.document.documentElement), true,
|
||||
"Cross-compartment unwrapping/comparison delete works");
|
||||
} catch(e) {
|
||||
ok(false, "Shouldn't throw when working with cross-compartment maplike/setlike interfaces " + e)
|
||||
};
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
|
@ -7,7 +7,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741267
|
|||
<window title="Mozilla Bug 741267"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
|
||||
</script>
|
||||
<iframe id="t"></iframe>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceMaplike {
|
||||
maplike<DOMString, long>;
|
||||
void setInternal(DOMString aKey, long aValue);
|
||||
void clearInternal();
|
||||
boolean deleteInternal(DOMString aKey);
|
||||
boolean hasInternal(DOMString aKey);
|
||||
};
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceMaplikeObject {
|
||||
readonly maplike<DOMString, TestInterfaceMaplike>;
|
||||
void setInternal(DOMString aKey);
|
||||
void clearInternal();
|
||||
boolean deleteInternal(DOMString aKey);
|
||||
boolean hasInternal(DOMString aKey);
|
||||
};
|
||||
|
||||
[Pref="dom.expose_test_interfaces",
|
||||
JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
Constructor()]
|
||||
interface TestInterfaceJSMaplike {
|
||||
readonly maplike<DOMString, long>;
|
||||
void setInternal(DOMString aKey, long aValue);
|
||||
void clearInternal();
|
||||
boolean deleteInternal(DOMString aKey);
|
||||
};
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceSetlike {
|
||||
setlike<DOMString>;
|
||||
};
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceSetlikeNode {
|
||||
setlike<Node>;
|
||||
};
|
|
@ -643,7 +643,9 @@ WEBIDL_FILES += [
|
|||
# We only expose our prefable test interfaces in debug builds, just to be on
|
||||
# the safe side.
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
WEBIDL_FILES += ['TestInterfaceJS.webidl', 'TestInterfaceJSDictionaries.webidl']
|
||||
WEBIDL_FILES += ['TestInterfaceJS.webidl',
|
||||
'TestInterfaceJSDictionaries.webidl',
|
||||
'TestInterfaceJSMaplikeSetlike.webidl']
|
||||
|
||||
if CONFIG['MOZ_B2G_BT']:
|
||||
if CONFIG['MOZ_B2G_BT_API_V1']:
|
||||
|
|
|
@ -449,6 +449,7 @@
|
|||
#ifdef MOZ_DEBUG
|
||||
@BINPATH@/components/TestInterfaceJS.js
|
||||
@BINPATH@/components/TestInterfaceJS.manifest
|
||||
@BINPATH@/components/TestInterfaceJSMaplike.js
|
||||
#endif
|
||||
|
||||
@BINPATH@/components/nsAsyncShutdown.manifest
|
||||
|
|
Загрузка…
Ссылка в новой задаче