MozMap and string stuff

This commit is contained in:
Fedor 2019-05-20 09:00:47 +03:00
parent 5670037d3d
commit 7a4ff54306
18 changed files with 520 additions and 423 deletions

View File

@ -2558,7 +2558,7 @@ NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
template<typename T> static void
NormalizeUSVStringInternal(JSContext* aCx, T& aString)
NormalizeUSVStringInternal(T& aString)
{
char16_t* start = aString.BeginWriting();
// Must use const here because we can't pass char** to UTF16CharEnumerator as
@ -2575,15 +2575,15 @@ NormalizeUSVStringInternal(JSContext* aCx, T& aString)
}
void
NormalizeUSVString(JSContext* aCx, nsAString& aString)
NormalizeUSVString(nsAString& aString)
{
NormalizeUSVStringInternal(aCx, aString);
NormalizeUSVStringInternal(aString);
}
void
NormalizeUSVString(JSContext* aCx, binding_detail::FakeString& aString)
NormalizeUSVString(binding_detail::FakeString& aString)
{
NormalizeUSVStringInternal(aCx, aString);
NormalizeUSVStringInternal(aString);
}
bool

View File

@ -49,7 +49,7 @@ namespace mozilla {
enum UseCounter : int16_t;
namespace dom {
template<typename DataType> class MozMap;
template<typename KeyType, typename ValueType> class Record;
nsresult
UnwrapArgImpl(JS::Handle<JSObject*> src, const nsIID& iid, void** ppArg);
@ -2127,11 +2127,30 @@ ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
return AssignJSString(cx, result, s);
}
void
NormalizeUSVString(JSContext* aCx, nsAString& aString);
template<typename T>
static inline bool
ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v, T& result)
{
return ConvertJSValueToString(cx, v, eStringify, eStringify, result);
}
void
NormalizeUSVString(JSContext* aCx, binding_detail::FakeString& aString);
NormalizeUSVString(nsAString& aString);
void
NormalizeUSVString(binding_detail::FakeString& aString);
template<typename T>
static inline bool
ConvertJSValueToUSVString(JSContext* cx, JS::Handle<JS::Value> v, T& result)
{
if (!ConvertJSValueToString(cx, v, eStringify, eStringify, result)) {
return false;
}
NormalizeUSVString(result);
return true;
}
template<typename T>
inline bool
@ -2158,6 +2177,13 @@ bool
ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
bool nullable, nsACString& result);
inline bool
ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
nsACString& result)
{
return ConvertJSValueToByteString(cx, v, false, result);
}
template<typename T>
void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
template<typename T>
@ -2293,31 +2319,26 @@ public:
}
};
template<typename T>
static void
TraceMozMapValue(T* aValue, void* aClosure)
template<typename K, typename V>
void TraceRecord(JSTracer* trc, Record<K, V>& record)
{
JSTracer* trc = static_cast<JSTracer*>(aClosure);
// Act like it's a one-element sequence to leverage all that infrastructure.
SequenceTracer<T>::TraceSequence(trc, aValue, aValue + 1);
for (auto& entry : record.Entries()) {
// Act like it's a one-element sequence to leverage all that infrastructure.
SequenceTracer<V>::TraceSequence(trc, &entry.mValue, &entry.mValue + 1);
}
}
template<typename T>
void TraceMozMap(JSTracer* trc, MozMap<T>& map)
{
map.EnumerateValues(TraceMozMapValue<T>, trc);
}
// sequence<MozMap>
template<typename T>
class SequenceTracer<MozMap<T>, false, false, false>
// sequence<record>
template<typename K, typename V>
class SequenceTracer<Record<K, V>, false, false, false>
{
explicit SequenceTracer() = delete; // Should never be instantiated
public:
static void TraceSequence(JSTracer* trc, MozMap<T>* seqp, MozMap<T>* end) {
static void TraceSequence(JSTracer* trc, Record<K, V>* seqp,
Record<K, V>* end) {
for (; seqp != end; ++seqp) {
seqp->EnumerateValues(TraceMozMapValue<T>, trc);
TraceRecord(trc, *seqp);
}
}
};
@ -2395,51 +2416,51 @@ public:
SequenceType mSequenceType;
};
// Rooter class for MozMap; this is what we mostly use in the codegen.
template<typename T>
class MOZ_RAII MozMapRooter final : private JS::CustomAutoRooter
// Rooter class for Record; this is what we mostly use in the codegen.
template<typename K, typename V>
class MOZ_RAII RecordRooter final : private JS::CustomAutoRooter
{
public:
MozMapRooter(JSContext *aCx, MozMap<T>* aMozMap
RecordRooter(JSContext *aCx, Record<K, V>* aRecord
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mMozMap(aMozMap),
mMozMapType(eMozMap)
mRecord(aRecord),
mRecordType(eRecord)
{
}
MozMapRooter(JSContext *aCx, Nullable<MozMap<T>>* aMozMap
RecordRooter(JSContext *aCx, Nullable<Record<K, V>>* aRecord
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mNullableMozMap(aMozMap),
mMozMapType(eNullableMozMap)
mNullableRecord(aRecord),
mRecordType(eNullableRecord)
{
}
private:
enum MozMapType {
eMozMap,
eNullableMozMap
enum RecordType {
eRecord,
eNullableRecord
};
virtual void trace(JSTracer *trc) override
{
if (mMozMapType == eMozMap) {
TraceMozMap(trc, *mMozMap);
if (mRecordType == eRecord) {
TraceRecord(trc, *mRecord);
} else {
MOZ_ASSERT(mMozMapType == eNullableMozMap);
if (!mNullableMozMap->IsNull()) {
TraceMozMap(trc, mNullableMozMap->Value());
MOZ_ASSERT(mRecordType == eNullableRecord);
if (!mNullableRecord->IsNull()) {
TraceRecord(trc, mNullableRecord->Value());
}
}
}
union {
MozMap<T>* mMozMap;
Nullable<MozMap<T>>* mNullableMozMap;
Record<K, V>* mRecord;
Nullable<Record<K, V>>* mNullableRecord;
};
MozMapType mMozMapType;
RecordType mRecordType;
};
template<typename T>

View File

@ -84,7 +84,7 @@ def idlTypeNeedsCycleCollection(type):
return True
elif type.isUnion():
return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
elif type.isMozMap():
elif type.isRecord():
if idlTypeNeedsCycleCollection(type.inner):
raise TypeError("Cycle collection for type %s is not supported" % type)
return False
@ -996,6 +996,8 @@ class CGElseChain(CGThing):
class CGTemplatedType(CGWrapper):
def __init__(self, templateName, child, isConst=False, isReference=False):
if isinstance(child, list):
child = CGList(child, ", ")
const = "const " if isConst else ""
pre = "%s%s<" % (const, templateName)
ref = "&" if isReference else ""
@ -1171,12 +1173,12 @@ class CGHeaders(CGWrapper):
declareIncludes.add(filename)
elif unrolled.isPrimitive():
bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
elif unrolled.isMozMap():
elif unrolled.isRecord():
if dictionary or jsImplementedDescriptors:
declareIncludes.add("mozilla/dom/MozMap.h")
declareIncludes.add("mozilla/dom/Record.h")
else:
bindingHeaders.add("mozilla/dom/MozMap.h")
# Also add headers for the type the MozMap is
bindingHeaders.add("mozilla/dom/Record.h")
# Also add headers for the type the record is
# parametrized over, if needed.
addHeadersForType((t.inner, dictionary))
@ -1388,8 +1390,8 @@ def UnionTypes(unionTypes, config):
# the right header to be able to Release() in our inlined
# code.
headers.add(CGHeaders.getDeclarationFilename(f.callback))
elif f.isMozMap():
headers.add("mozilla/dom/MozMap.h")
elif f.isRecord():
headers.add("mozilla/dom/Record.h")
# And add headers for the type we're parametrized over
addHeadersForType(f.inner)
@ -1448,9 +1450,9 @@ def UnionConversions(unionTypes, config):
headers.add(CGHeaders.getDeclarationFilename(f.inner))
elif f.isPrimitive():
headers.add("mozilla/dom/PrimitiveConversions.h")
elif f.isMozMap():
headers.add("mozilla/dom/MozMap.h")
# And the internal type of the MozMap
elif f.isRecord():
headers.add("mozilla/dom/Record.h")
# And the internal type of the record
addHeadersForType(f.inner)
# We plan to include UnionTypes.h no matter what, so it's
@ -4290,6 +4292,9 @@ class JSToNativeConversionInfo():
for whether we have a JS::Value. Only used when
defaultValue is not None or when True is passed for
checkForValue to instantiateJSToNativeConversion.
This expression may not be already-parenthesized, so if
you use it with && or || make sure to put parens
around it.
${passedToJSImpl} replaced by an expression that evaluates to a boolean
for whether this value is being passed to a JS-
implemented interface.
@ -4355,7 +4360,9 @@ def handleDefaultStringValue(defaultValue, method):
passing as the second argument of handleDefault; in particular it does not
end with a ';'
"""
assert defaultValue.type.isDOMString() or defaultValue.type.isByteString()
assert (defaultValue.type.isDOMString() or
defaultValue.type.isUSVString() or
defaultValue.type.isByteString())
return ("static const %(char_t)s data[] = { %(data)s };\n"
"%(method)s(data, ArrayLength(data) - 1)") % {
'char_t': "char" if defaultValue.type.isByteString() else "char16_t",
@ -4365,6 +4372,17 @@ def handleDefaultStringValue(defaultValue, method):
}
def recordKeyType(recordType):
assert recordType.keyType.isString()
if recordType.keyType.isByteString():
return "nsCString"
return "nsString"
def recordKeyDeclType(recordType):
return CGGeneric(recordKeyType(recordType))
# If this function is modified, modify CGNativeMember.getArg and
# CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
# and holdertype we end up using, because it needs to be able to return the code
@ -4559,7 +4577,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
declArgs = "cx"
else:
assert (isMember in
("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap"))
("Sequence", "Variadic", "Dictionary", "OwningUnion", "Record"))
# We'll get traced by the sequence or dictionary or union tracer
declType = CGGeneric("JSObject*")
declArgs = None
@ -4725,39 +4743,41 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
dealWithOptional=isOptional,
holderArgs=holderArgs)
if type.isMozMap():
if type.isRecord():
assert not isEnforceRange and not isClamp
if failureCode is None:
notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
notRecord = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
"%s" % (firstCap(sourceDescription), exceptionCode))
else:
notMozMap = failureCode
notRecord = failureCode
nullable = type.nullable()
# Be very careful not to change "type": we need it later
if nullable:
valueType = type.inner.inner
recordType = type.inner
else:
valueType = type.inner
recordType = type
valueType = recordType.inner
valueInfo = getJSToNativeConversionInfo(
valueType, descriptorProvider, isMember="MozMap",
valueType, descriptorProvider, isMember="Record",
exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
isCallbackReturnValue=isCallbackReturnValue,
sourceDescription="value in %s" % sourceDescription,
nestingLevel=incrementNestingLevel())
if valueInfo.dealWithOptional:
raise TypeError("Shouldn't have optional things in MozMap")
raise TypeError("Shouldn't have optional things in record")
if valueInfo.holderType is not None:
raise TypeError("Shouldn't need holders for MozMap")
raise TypeError("Shouldn't need holders for record")
typeName = CGTemplatedType("MozMap", valueInfo.declType)
mozMapType = typeName.define()
declType = CGTemplatedType("Record", [recordKeyDeclType(recordType),
valueInfo.declType])
typeName = declType.define()
if nullable:
typeName = CGTemplatedType("Nullable", typeName)
mozMapRef = "${declName}.SetValue()"
declType = CGTemplatedType("Nullable", declType)
recordRef = "${declName}.SetValue()"
else:
mozMapRef = "${declName}"
recordRef = "${declName}"
valueConversion = string.Template(valueInfo.template).substitute({
"val": "temp",
@ -4770,68 +4790,124 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
"passedToJSImpl": "${passedToJSImpl}"
})
keyType = recordKeyType(recordType)
if recordType.keyType.isByteString():
keyConversionFunction = "ConvertJSValueToByteString"
hashKeyType = "nsCStringHashKey"
else:
hashKeyType = "nsStringHashKey"
if recordType.keyType.isDOMString():
keyConversionFunction = "ConvertJSValueToString"
else:
assert recordType.keyType.isUSVString()
keyConversionFunction = "ConvertJSValueToUSVString"
templateBody = fill(
"""
${mozMapType} &mozMap = ${mozMapRef};
auto& recordEntries = ${recordRef}.Entries();
JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject());
JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
if (!JS_Enumerate(cx, mozMapObj, &ids)) {
JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
JS::AutoIdVector ids(cx);
// Keep skipping symbols until
// https://github.com/heycam/webidl/issues/294 is sorted out.
if (!js::GetPropertyKeys(cx, recordObj,
JSITER_OWNONLY | JSITER_HIDDEN, &ids)) {
$*{exceptionCode}
}
if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
JS_ReportOutOfMemory(cx);
$*{exceptionCode}
}
JS::Rooted<JS::Value> propNameValue(cx);
JS::Rooted<JS::Value> temp(cx);
JS::Rooted<jsid> curId(cx);
JS::Rooted<JS::Value> idVal(cx);
// Use a hashset to keep track of ids seen, to avoid
// introducing nasty O(N^2) behavior scanning for them all the
// time. Ideally we'd use a data structure with O(1) lookup
// _and_ ordering for the MozMap, but we don't have one lying
// around.
nsTHashtable<${hashKeyType}> idsSeen;
for (size_t i = 0; i < ids.length(); ++i) {
// Make sure we get the value before converting the name, since
// getting the value can trigger GC but our name is a dependent
// string.
curId = ids[i];
binding_detail::FakeString propName;
bool isSymbol;
if (!ConvertIdToString(cx, curId, propName, isSymbol) ||
(!isSymbol && !JS_GetPropertyById(cx, mozMapObj, curId, &temp))) {
MOZ_ASSERT(!JSID_IS_SYMBOL(curId), "No symbols, we said!");
JS::Rooted<JS::PropertyDescriptor> desc(cx);
if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
&desc)) {
$*{exceptionCode}
}
if (isSymbol) {
if (!desc.object() /* == undefined in spec terms */ ||
!desc.enumerable()) {
continue;
}
${valueType}* slotPtr = mozMap.AddEntry(propName);
if (!slotPtr) {
JS_ReportOutOfMemory(cx);
idVal = js::IdToValue(curId);
${keyType} propName;
if (!${keyConversionFunction}(cx, idVal, propName)) {
$*{exceptionCode}
}
${valueType}& slot = *slotPtr;
if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
$*{exceptionCode}
}
${typeName}::EntryType* entry;
if (idsSeen.Contains(propName)) {
// Find the existing entry.
auto idx = recordEntries.IndexOf(propName);
MOZ_ASSERT(idx != recordEntries.NoIndex,
"Why is it not found?");
// Now blow it away to make it look like it was just added
// to the array, because it's not obvious that it's
// safe to write to its already-initialized mValue via our
// normal codegen conversions. For example, the value
// could be a union and this would change its type, but
// codegen assumes we won't do that.
entry = recordEntries.ReconstructElementAt(idx);
} else {
// Safe to do an infallible append here, because we did a
// SetCapacity above to the right capacity.
entry = recordEntries.AppendElement();
idsSeen.PutEntry(propName);
}
entry->mKey = propName;
${valueType}& slot = entry->mValue;
$*{valueConversion}
}
""",
exceptionCode=exceptionCode,
mozMapType=mozMapType,
mozMapRef=mozMapRef,
recordRef=recordRef,
hashKeyType=hashKeyType,
keyType=keyType,
keyConversionFunction=keyConversionFunction,
typeName=typeName,
valueType=valueInfo.declType.define(),
valueConversion=valueConversion)
templateBody = wrapObjectTemplate(templateBody, type,
"${declName}.SetNull();\n",
notMozMap)
notRecord)
declType = typeName
declArgs = None
holderType = None
holderArgs = None
# MozMap arguments that might contain traceable things need
# record arguments that might contain traceable things need
# to get traced
if not isMember and isCallbackReturnValue:
# Go ahead and just convert directly into our actual return value
declType = CGWrapper(declType, post="&")
declArgs = "aRetVal"
elif not isMember and typeNeedsRooting(valueType):
holderType = CGTemplatedType("MozMapRooter", valueInfo.declType)
# If our MozMap is nullable, this will set the Nullable to be
holderType = CGTemplatedType("RecordRooter",
[recordKeyDeclType(recordType),
valueInfo.declType])
# If our record is nullable, this will set the Nullable to be
# not-null, but that's ok because we make an explicit SetNull() call
# on it as needed if our JS value is actually null.
holderArgs = "cx, &%s" % mozMapRef
holderArgs = "cx, &%s" % recordRef
return JSToNativeConversionInfo(templateBody, declType=declType,
declArgs=declArgs,
@ -4914,16 +4990,16 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else:
setDictionary = None
mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
if len(mozMapMemberTypes) > 0:
assert len(mozMapMemberTypes) == 1
name = getUnionMemberName(mozMapMemberTypes[0])
mozMapObject = CGGeneric(
recordMemberTypes = filter(lambda t: t.isRecord(), memberTypes)
if len(recordMemberTypes) > 0:
assert len(recordMemberTypes) == 1
name = getUnionMemberName(recordMemberTypes[0])
recordObject = CGGeneric(
"done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
(unionArgumentObj, name))
names.append(name)
else:
mozMapObject = None
recordObject = None
objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
if len(objectMemberTypes) > 0:
@ -4939,10 +5015,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else:
object = None
hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or mozMapObject
hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or recordObject
if hasObjectTypes:
# "object" is not distinguishable from other types
assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or mozMapObject)
assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or recordObject)
if sequenceObject or dateObject or callbackObject:
# An object can be both an sequence object and a callback or
# dictionary, but we shouldn't have both in the union's members
@ -4962,9 +5038,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if dateObject:
templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n"))
if mozMapObject:
if recordObject:
templateBody = CGList([templateBody,
CGIfWrapper(mozMapObject, "!done")])
CGIfWrapper(recordObject, "!done")])
templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
else:
@ -5144,7 +5220,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if isinstance(defaultValue, IDLNullValue):
extraConditionForNull = "!(${haveValue}) || "
else:
extraConditionForNull = "${haveValue} && "
extraConditionForNull = "(${haveValue}) && "
else:
extraConditionForNull = ""
templateBody = handleNull(templateBody, declLoc,
@ -5525,7 +5601,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
def getConversionCode(varName):
normalizeCode = ""
if type.isUSVString():
normalizeCode = "NormalizeUSVString(cx, %s);\n" % varName
normalizeCode = "NormalizeUSVString(%s);\n" % varName
conversionCode = fill("""
if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
@ -5688,7 +5764,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
haveCallable = "${val}.isObject() && " + haveCallable
if defaultValue is not None:
assert(isinstance(defaultValue, IDLNullValue))
haveCallable = "${haveValue} && " + haveCallable
haveCallable = "(${haveValue}) && " + haveCallable
template = (
("if (%s) {\n" % haveCallable) +
conversion +
@ -5700,7 +5776,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
haveObject = "${val}.isObject()"
if defaultValue is not None:
assert(isinstance(defaultValue, IDLNullValue))
haveObject = "${haveValue} && " + haveObject
haveObject = "(${haveValue}) && " + haveObject
template = CGIfElseWrapper(haveObject,
CGGeneric(conversion),
CGGeneric("${declName} = nullptr;\n")).define()
@ -5724,7 +5800,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not isEnforceRange and not isClamp
declArgs = None
if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"):
if isMember in ("Variadic", "Sequence", "Dictionary", "Record"):
# Rooting is handled by the sequence and dictionary tracers.
declType = "JS::Value"
else:
@ -5768,8 +5844,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription)
if type.isDictionary():
# There are no nullable dictionaries
assert not type.nullable() or isCallbackReturnValue
# There are no nullable dictionary arguments or dictionary members
assert(not type.nullable() or isCallbackReturnValue or
(isMember and isMember != "Dictionary"))
# All optional dictionaries always have default values, so we
# should be able to assume not isOptional here.
assert not isOptional
@ -6256,7 +6333,7 @@ def getMaybeWrapValueFuncForType(type):
sequenceWrapLevel = 0
mozMapWrapLevel = 0
recordWrapLevel = 0
def getWrapTemplateForType(type, descriptorProvider, result, successCode,
@ -6361,7 +6438,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
if type is None or type.isVoid():
return (setUndefined(), True)
if (type.isSequence() or type.isMozMap()) and type.nullable():
if (type.isSequence() or type.isRecord()) and type.nullable():
# These are both wrapped in Nullable<>
recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode,
@ -6434,14 +6511,14 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
return (code, False)
if type.isMozMap():
# Now do non-nullable MozMap. Our success code is just to break to
if type.isRecord():
# Now do non-nullable record. Our success code is just to break to
# where we define the property on the object. Note that we bump the
# mozMapWrapLevel around this call so that nested MozMap conversions
# recordWrapLevel around this call so that nested record conversions
# will use different temp value names.
global mozMapWrapLevel
valueName = "mozMapValue%d" % mozMapWrapLevel
mozMapWrapLevel += 1
global recordWrapLevel
valueName = "recordValue%d" % recordWrapLevel
recordWrapLevel += 1
innerTemplate = wrapForType(
type.inner, descriptorProvider,
{
@ -6454,12 +6531,22 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'obj': "returnObj",
'typedArraysAreStructs': typedArraysAreStructs
})
mozMapWrapLevel -= 1
recordWrapLevel -= 1
if type.keyType.isByteString():
# There is no length-taking JS_DefineProperty. So to keep
# things sane with embedded nulls, we want to byte-inflate
# to an nsAString. The only byte-inflation function we
# have around is AppendASCIItoUTF16, which luckily doesn't
# assert anything about the input being ASCII.
expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n"
keyName = "expandedKey"
else:
expandedKeyDecl = ""
keyName = "entry.mKey"
code = fill(
"""
nsTArray<nsString> keys;
${result}.GetKeys(keys);
JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
if (!returnObj) {
$*{exceptionCode}
@ -6467,15 +6554,17 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
// Scope for 'tmp'
{
JS::Rooted<JS::Value> tmp(cx);
for (size_t idx = 0; idx < keys.Length(); ++idx) {
auto& ${valueName} = ${result}.Get(keys[idx]);
for (auto& entry : ${result}.Entries()) {
auto& ${valueName} = entry.mValue;
// Control block to let us common up the JS_DefineUCProperty calls when there
// are different ways to succeed at wrapping the value.
do {
$*{innerTemplate}
} while (0);
if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(),
keys[idx].Length(), tmp,
$*{expandedKeyDecl}
if (!JS_DefineUCProperty(cx, returnObj,
${keyName}.BeginReading(),
${keyName}.Length(), tmp,
JSPROP_ENUMERATE)) {
$*{exceptionCode}
}
@ -6487,6 +6576,8 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
exceptionCode=exceptionCode,
valueName=valueName,
innerTemplate=innerTemplate,
expandedKeyDecl=expandedKeyDecl,
keyName=keyName,
set=setObject("*returnObj"))
return (code, False)
@ -6770,7 +6861,7 @@ def typeMatchesLambda(type, func):
return False
if type.nullable():
return typeMatchesLambda(type.inner, func)
if type.isSequence() or type.isMozMap():
if type.isSequence() or type.isRecord():
return typeMatchesLambda(type.inner, func)
if type.isUnion():
return any(typeMatchesLambda(t, func) for t in
@ -6866,20 +6957,21 @@ def getRetvalDeclarationForType(returnType, descriptorProvider,
if nullable:
result = CGTemplatedType("Nullable", result)
return result, "ref", rooter, None, None
if returnType.isMozMap():
if returnType.isRecord():
nullable = returnType.nullable()
if nullable:
returnType = returnType.inner
result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner,
descriptorProvider,
isMember="MozMap")
isMember="Record")
# While we have our inner type, set up our rooter, if needed
if not isMember and typeNeedsRooting(returnType):
rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" %
result.define())
rooter = CGGeneric("RecordRooter<%s> resultRooter(cx, &result);\n" %
("nsString, " + result.define()))
else:
rooter = None
result = CGTemplatedType("MozMap", result)
result = CGTemplatedType("Record", [recordKeyDeclType(returnType),
result])
if nullable:
result = CGTemplatedType("Nullable", result)
return result, "ref", rooter, None, None
@ -6976,7 +7068,7 @@ class CGCallGenerator(CGThing):
return True
if a.type.isSequence():
return True
if a.type.isMozMap():
if a.type.isRecord():
return True
# isObject() types are always a JS::Rooted, whether
# nullable or not, and it turns out a const JS::Rooted
@ -7138,7 +7230,7 @@ class MethodNotNewObjectError(Exception):
# nested sequences we don't use the same variable name to iterate over
# different sequences.
sequenceWrapLevel = 0
mapWrapLevel = 0
recordWrapLevel = 0
def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
@ -7199,29 +7291,27 @@ def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
return wrapCode
if type.isMozMap():
origValue = value
if type.isRecord():
origType = type
if type.nullable():
type = type.inner
value = "%s.Value()" % value
global mapWrapLevel
key = "mapName%d" % mapWrapLevel
mapWrapLevel += 1
recordRef = "%s.Value()" % value
else:
recordRef = value
global recordWrapLevel
entryRef = "mapEntry%d" % recordWrapLevel
recordWrapLevel += 1
wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
"%s.Get(%sKeys[%sIndex])" % (value, key, key))
mapWrapLevel -= 1
"%s.mValue" % entryRef)
recordWrapLevel -= 1
if not wrapElement:
return None
wrapCode = CGWrapper(CGIndenter(wrapElement),
pre=("""
nsTArray<nsString> %sKeys;
%s.GetKeys(%sKeys);
for (uint32_t %sIndex = 0; %sIndex < %sKeys.Length(); ++%sIndex) {
""" % (key, value, key, key, key, key, key)),
pre=("for (auto& %s : %s.Entries()) {\n" %
(entryRef, recordRef)),
post="}\n")
if origType.nullable():
wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value)
return wrapCode
if type.isDictionary():
@ -8110,11 +8200,11 @@ class CGMethodCall(CGThing):
if distinguishingType(s).isSequence())
# Now append all the overloads that take a dictionary or callback
# interface or MozMap. There should be only one of these!
# interface or record. There should be only one of these!
genericObjectSigs = [
s for s in possibleSignatures
if (distinguishingType(s).isDictionary() or
distinguishingType(s).isMozMap() or
distinguishingType(s).isRecord() or
distinguishingType(s).isCallbackInterface())]
assert len(genericObjectSigs) <= 1
objectSigs.extend(genericObjectSigs)
@ -9408,7 +9498,7 @@ class CGMemberJITInfo(CGThing):
return "JSVAL_TYPE_UNDEFINED"
if t.isSequence():
return "JSVAL_TYPE_OBJECT"
if t.isMozMap():
if t.isRecord():
return "JSVAL_TYPE_OBJECT"
if t.isGeckoInterface():
return "JSVAL_TYPE_OBJECT"
@ -9669,17 +9759,22 @@ def getUnionAccessorSignatureType(type, descriptorProvider):
# Flat member types have already unwrapped nullables.
assert not type.nullable()
if type.isSequence() or type.isMozMap():
if type.isSequence() or type.isRecord():
if type.isSequence():
wrapperType = "Sequence"
else:
wrapperType = "MozMap"
wrapperType = "Record"
# We don't use the returned template here, so it's OK to just pass no
# sourceDescription.
elementInfo = getJSToNativeConversionInfo(type.inner,
descriptorProvider,
isMember=wrapperType)
return CGTemplatedType(wrapperType, elementInfo.declType,
if wrapperType == "Sequence":
innerType = elementInfo.declType
else:
innerType = [recordKeyDeclType(type), elementInfo.declType]
return CGTemplatedType(wrapperType, innerType,
isConst=True, isReference=True)
# Nested unions are unwrapped automatically into our flatMemberTypes.
@ -10040,10 +10135,10 @@ class CGUnionStruct(CGThing):
CGCase("e" + vars["name"],
CGGeneric("DoTraceSequence(trc, mValue.m%s.Value());\n" %
vars["name"])))
elif t.isMozMap():
elif t.isRecord():
traceCases.append(
CGCase("e" + vars["name"],
CGGeneric("TraceMozMap(trc, mValue.m%s.Value());\n" %
CGGeneric("TraceRecord(trc, mValue.m%s.Value());\n" %
vars["name"])))
else:
assert t.isSpiderMonkeyInterface()
@ -13172,8 +13267,8 @@ class CGDictionary(CGThing):
trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData)
if type.nullable():
trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
elif type.isMozMap():
# If you implement this, add a MozMap<object> to
elif type.isRecord():
# If you implement this, add a record<DOMString, object> to
# TestInterfaceJSDictionary and test it in test_bug1036214.html
# to make sure we end up with the correct security properties.
assert False
@ -13583,7 +13678,7 @@ class ForwardDeclarationBuilder:
# since we don't know which one we might want
self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
elif t.isMozMap():
elif t.isRecord():
self.forwardDeclareForType(t.inner, config)
# Don't need to do anything for void, primitive, string, any or object.
# There may be some other cases we are missing.
@ -14089,9 +14184,9 @@ class CGNativeMember(ClassMethod):
else:
returnCode = "aRetVal.SwapElements(${declName});\n"
return "void", "", returnCode
if type.isMozMap():
# If we want to handle MozMap-of-MozMap return values, we're
# going to need to fix example codegen to not produce MozMap<void>
if type.isRecord():
# If we want to handle record-of-record return values, we're
# going to need to fix example codegen to not produce record<void>
# for the relevant argument...
assert not isMember
# In this case we convert directly into our outparam to start with
@ -14139,13 +14234,14 @@ class CGNativeMember(ClassMethod):
if nullable:
type = CGTemplatedType("Nullable", type)
args.append(Argument("%s&" % type.define(), "aRetVal"))
elif returnType.isMozMap():
elif returnType.isRecord():
nullable = returnType.nullable()
if nullable:
returnType = returnType.inner
# And now the actual underlying type
elementDecl = self.getReturnType(returnType.inner, True)
type = CGTemplatedType("MozMap", CGGeneric(elementDecl))
type = CGTemplatedType("Record", [recordKeyDeclType(returnType),
CGGeneric(elementDecl)])
if nullable:
type = CGTemplatedType("Nullable", type)
args.append(Argument("%s&" % type.define(), "aRetVal"))
@ -14206,7 +14302,7 @@ class CGNativeMember(ClassMethod):
Nullable as needed.
isMember can be false or one of the strings "Sequence", "Variadic",
"MozMap"
"Record"
"""
if type.isSequence():
nullable = type.nullable()
@ -14217,13 +14313,13 @@ class CGNativeMember(ClassMethod):
decl = CGTemplatedType("Sequence", argType)
return decl.define(), True, True
if type.isMozMap():
if type.isRecord():
nullable = type.nullable()
if nullable:
type = type.inner
elementType = type.inner
argType = self.getArgType(elementType, False, "MozMap")[0]
decl = CGTemplatedType("MozMap", argType)
argType = self.getArgType(elementType, False, "Record")[0]
decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType])
return decl.define(), True, True
if type.isUnion():

View File

@ -115,7 +115,7 @@ class Configuration(DescriptorProvider):
for (t, _) in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
while True:
if t.isMozMap():
if t.isRecord():
t = t.inner
elif t.unroll() != t:
t = t.unroll()

View File

@ -1,121 +0,0 @@
/* -*- 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/. */
/**
* Class for representing MozMap arguments. This is an nsTHashtable
* under the hood, but we don't want to leak that implementation
* detail.
*/
#ifndef mozilla_dom_MozMap_h
#define mozilla_dom_MozMap_h
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
#include "mozilla/Move.h"
namespace mozilla {
namespace dom {
namespace binding_detail {
template<typename DataType>
class MozMapEntry : public nsStringHashKey
{
public:
explicit MozMapEntry(const nsAString* aKeyTypePointer)
: nsStringHashKey(aKeyTypePointer)
{
}
// Move constructor so we can do MozMaps of MozMaps.
MozMapEntry(MozMapEntry<DataType>&& aOther)
: nsStringHashKey(aOther),
mData(Move(aOther.mData))
{
}
DataType mData;
};
} // namespace binding_detail
template<typename DataType>
class MozMap : protected nsTHashtable<binding_detail::MozMapEntry<DataType>>
{
public:
typedef typename binding_detail::MozMapEntry<DataType> EntryType;
typedef nsTHashtable<EntryType> Base;
typedef MozMap<DataType> SelfType;
MozMap()
{
}
// Move constructor so we can do MozMap of MozMap.
MozMap(SelfType&& aOther) :
Base(Move(aOther))
{
}
// The return value is only safe to use until an AddEntry call.
const DataType& Get(const nsAString& aKey) const
{
const EntryType* ent = this->GetEntry(aKey);
MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?");
return ent->mData;
}
DataType& Get(const nsAString& aKey)
{
EntryType* ent = this->GetEntry(aKey);
MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?");
return ent->mData;
}
// The return value is only safe to use until an AddEntry call.
const DataType* GetIfExists(const nsAString& aKey) const
{
const EntryType* ent = this->GetEntry(aKey);
if (!ent) {
return nullptr;
}
return &ent->mData;
}
void GetKeys(nsTArray<nsString>& aKeys) const {
for (auto iter = this->ConstIter(); !iter.Done(); iter.Next()) {
aKeys.AppendElement(iter.Get()->GetKey());
}
}
// XXXbz we expose this generic enumerator for tracing. Otherwise we'd end up
// with a dependency on BindingUtils.h here for the SequenceTracer bits.
typedef void (* Enumerator)(DataType* aValue, void* aClosure);
void EnumerateValues(Enumerator aEnumerator, void *aClosure)
{
for (auto iter = this->Iter(); !iter.Done(); iter.Next()) {
aEnumerator(&iter.Get()->mData, aClosure);
}
}
MOZ_MUST_USE
DataType* AddEntry(const nsAString& aKey)
{
EntryType* ent = this->PutEntry(aKey, fallible);
if (!ent) {
return nullptr;
}
return &ent->mData;
}
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MozMap_h

91
dom/bindings/Record.h Normal file
View File

@ -0,0 +1,91 @@
/* -*- 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/. */
/**
* Class for representing record arguments. Basically an array under the hood.
*/
#ifndef mozilla_dom_Record_h
#define mozilla_dom_Record_h
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
#include "mozilla/Move.h"
namespace mozilla {
namespace dom {
namespace binding_detail {
template<typename KeyType, typename ValueType>
class RecordEntry
{
public:
RecordEntry()
{
}
// Move constructor so we can do Records of Records.
RecordEntry(RecordEntry<KeyType, ValueType>&& aOther)
: mKey(Move(aOther.mKey)),
mValue(Move(aOther.mValue))
{
}
KeyType mKey;
ValueType mValue;
};
} // namespace binding_detail
template<typename KeyType, typename ValueType>
class Record
{
public:
typedef typename binding_detail::RecordEntry<KeyType, ValueType> EntryType;
typedef Record<KeyType, ValueType> SelfType;
Record()
{
}
// Move constructor so we can do Record of Record.
Record(SelfType&& aOther) :
mEntries(Move(aOther.mEntries))
{
}
const nsTArray<EntryType>& Entries() const
{
return mEntries;
}
nsTArray<EntryType>& Entries()
{
return mEntries;
}
private:
nsTArray<EntryType> mEntries;
};
} // namespace dom
} // namespace mozilla
template<typename K, typename V>
class nsDefaultComparator<mozilla::dom::binding_detail::RecordEntry<K, V>, K>
{
public:
bool Equals(const mozilla::dom::binding_detail::RecordEntry<K, V>& aEntry,
const K& aKey) const
{
return aEntry.mKey == aKey;
}
};
#endif // mozilla_dom_Record_h

View File

@ -32,10 +32,10 @@ EXPORTS.mozilla.dom += [
'FakeString.h',
'IterableIterator.h',
'JSSlots.h',
'MozMap.h',
'NonRefcountedDOMObject.h',
'Nullable.h',
'PrimitiveConversions.h',
'Record.h',
'RootedDictionary.h',
'SimpleGlobalObject.h',
'StructuredClone.h',

View File

@ -1867,7 +1867,7 @@ class IDLDictionary(IDLObjectWithScope):
if (memberType.nullable() or
memberType.isSequence() or
memberType.isMozMap()):
memberType.isRecord()):
return typeContainsDictionary(memberType.inner, dictionary)
if memberType.isDictionary():
@ -1988,7 +1988,7 @@ class IDLType(IDLObject):
'callback',
'union',
'sequence',
'mozmap'
'record'
)
def __init__(self, location, name):
@ -2038,7 +2038,7 @@ class IDLType(IDLObject):
def isSequence(self):
return False
def isMozMap(self):
def isRecord(self):
return False
def isArrayBuffer(self):
@ -2263,8 +2263,8 @@ class IDLNullableType(IDLParameterizedType):
def isSequence(self):
return self.inner.isSequence()
def isMozMap(self):
return self.inner.isMozMap()
def isRecord(self):
return self.inner.isRecord()
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
@ -2321,8 +2321,10 @@ class IDLNullableType(IDLParameterizedType):
return self
def isDistinguishableFrom(self, other):
if (other.nullable() or (other.isUnion() and other.hasNullableType) or
other.isDictionary()):
if (other.nullable() or
other.isDictionary() or
(other.isUnion() and
(other.hasNullableType or other.hasDictionaryType()))):
# Can't tell which type null should become
return False
return self.inner.isDistinguishableFrom(other)
@ -2397,34 +2399,38 @@ class IDLSequenceType(IDLParameterizedType):
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isDate() or other.isInterface() or
other.isDictionary() or
other.isCallback() or other.isMozMap())
other.isCallback() or other.isRecord())
class IDLMozMapType(IDLParameterizedType):
def __init__(self, location, parameterType):
assert not parameterType.isVoid()
class IDLRecordType(IDLParameterizedType):
def __init__(self, location, keyType, valueType):
assert keyType.isString()
assert keyType.isComplete()
assert not valueType.isVoid()
IDLParameterizedType.__init__(self, location, valueType.name, valueType)
self.keyType = keyType
IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
# Need to set self.name up front if our inner type is already complete,
# since in that case our .complete() won't be called.
if self.inner.isComplete():
self.name = self.inner.name + "MozMap"
self.name = self.keyType.name + self.inner.name + "Record"
def __eq__(self, other):
return isinstance(other, IDLMozMapType) and self.inner == other.inner
return isinstance(other, IDLRecordType) and self.inner == other.inner
def __str__(self):
return self.inner.__str__() + "MozMap"
return self.keyType.__str__() + self.inner.__str__() + "Record"
def isMozMap(self):
def isRecord(self):
return True
def tag(self):
return IDLType.Tags.mozmap
return IDLType.Tags.record
def complete(self, scope):
self.inner = self.inner.complete(scope)
self.name = self.inner.name + "MozMap"
self.name = self.keyType.name + self.inner.name + "Record"
return self
def unroll(self):
@ -2614,8 +2620,8 @@ class IDLTypedefType(IDLType):
def isSequence(self):
return self.inner.isSequence()
def isMozMap(self):
return self.inner.isMozMap()
def isRecord(self):
return self.inner.isRecord()
def isDictionary(self):
return self.inner.isDictionary()
@ -2798,7 +2804,7 @@ class IDLWrapperType(IDLType):
if self.isEnum():
return (other.isPrimitive() or other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
other.isSequence() or other.isMozMap() or other.isDate())
other.isSequence() or other.isRecord() or other.isDate())
if self.isDictionary() and other.nullable():
return False
if (other.isPrimitive() or other.isString() or other.isEnum() or
@ -2820,7 +2826,7 @@ class IDLWrapperType(IDLType):
(self.isNonCallbackInterface() or
other.isNonCallbackInterface()))
if (other.isDictionary() or other.isCallback() or
other.isMozMap()):
other.isRecord()):
return self.isNonCallbackInterface()
# Not much else |other| can be
@ -3030,17 +3036,17 @@ class IDLBuiltinType(IDLType):
return (other.isNumeric() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
other.isSequence() or other.isMozMap() or other.isDate())
other.isSequence() or other.isRecord() or other.isDate())
if self.isNumeric():
return (other.isBoolean() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
other.isSequence() or other.isMozMap() or other.isDate())
other.isSequence() or other.isRecord() or other.isDate())
if self.isString():
return (other.isPrimitive() or other.isInterface() or
other.isObject() or
other.isCallback() or other.isDictionary() or
other.isSequence() or other.isMozMap() or other.isDate())
other.isSequence() or other.isRecord() or other.isDate())
if self.isAny():
# Can't tell "any" apart from anything
return False
@ -3050,7 +3056,7 @@ class IDLBuiltinType(IDLType):
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isInterface() or other.isCallback() or
other.isDictionary() or other.isSequence() or
other.isMozMap())
other.isRecord())
if self.isVoid():
return not other.isVoid()
# Not much else we could be!
@ -3058,7 +3064,7 @@ class IDLBuiltinType(IDLType):
# Like interfaces, but we know we're not a callback
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isCallback() or other.isDictionary() or
other.isSequence() or other.isMozMap() or other.isDate() or
other.isSequence() or other.isRecord() or other.isDate() or
(other.isInterface() and (
# ArrayBuffer is distinguishable from everything
# that's not an ArrayBuffer or a callback interface
@ -3843,6 +3849,9 @@ class IDLConst(IDLInterfaceMember):
if type.isDictionary():
raise WebIDLError("A constant cannot be of a dictionary type",
[self.location])
if type.isRecord():
raise WebIDLError("A constant cannot be of a record type",
[self.location])
self.type = type
self.value = value
@ -3954,8 +3963,8 @@ class IDLAttribute(IDLInterfaceMember):
if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("A non-cached attribute cannot be of a sequence "
"type", [self.location])
if self.type.isMozMap() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("A non-cached attribute cannot be of a MozMap "
if self.type.isRecord() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("A non-cached attribute cannot be of a record "
"type", [self.location])
if self.type.isUnion():
for f in self.type.unroll().flatMemberTypes:
@ -3971,11 +3980,11 @@ class IDLAttribute(IDLInterfaceMember):
"one of its member types's member "
"types, and so on) is a sequence "
"type", [self.location, f.location])
if f.isMozMap():
if f.isRecord():
raise WebIDLError("An attribute cannot be of a union "
"type if one of its member types (or "
"one of its member types's member "
"types, and so on) is a MozMap "
"types, and so on) is a record "
"type", [self.location, f.location])
if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
raise WebIDLError("An attribute with [PutForwards] must have an "
@ -3989,7 +3998,7 @@ class IDLAttribute(IDLInterfaceMember):
def typeContainsChromeOnlyDictionaryMember(type):
if (type.nullable() or
type.isSequence() or
type.isMozMap()):
type.isRecord()):
return typeContainsChromeOnlyDictionaryMember(type.inner)
if type.isUnion():
@ -4035,10 +4044,10 @@ class IDLAttribute(IDLInterfaceMember):
[self.location, location])
if self.getExtendedAttribute("Frozen"):
if (not self.type.isSequence() and not self.type.isDictionary() and
not self.type.isMozMap()):
not self.type.isRecord()):
raise WebIDLError("[Frozen] is only allowed on "
"sequence-valued, dictionary-valued, and "
"MozMap-valued attributes",
"record-valued attributes",
[self.location])
if not self.type.unroll().isExposedInAllOf(self.exposureSet):
raise WebIDLError("Attribute returns a type that is not exposed "
@ -5147,7 +5156,7 @@ class Tokenizer(object):
"Promise": "PROMISE",
"required": "REQUIRED",
"sequence": "SEQUENCE",
"MozMap": "MOZMAP",
"record": "RECORD",
"short": "SHORT",
"unsigned": "UNSIGNED",
"void": "VOID",
@ -6276,7 +6285,7 @@ class Parser(Tokenizer):
| OCTET
| OPTIONAL
| SEQUENCE
| MOZMAP
| RECORD
| SETTER
| SHORT
| STATIC
@ -6355,7 +6364,7 @@ class Parser(Tokenizer):
def p_NonAnyType(self, p):
"""
NonAnyType : PrimitiveOrStringType Null
NonAnyType : PrimitiveType Null
| ARRAYBUFFER Null
| SHAREDARRAYBUFFER Null
| OBJECT Null
@ -6371,6 +6380,12 @@ class Parser(Tokenizer):
p[0] = self.handleNullable(type, p[2])
def p_NonAnyTypeStringType(self, p):
"""
NonAnyType : StringType Null
"""
p[0] = self.handleNullable(p[1], p[2])
def p_NonAnyTypeSequenceType(self, p):
"""
NonAnyType : SEQUENCE LT Type GT Null
@ -6391,13 +6406,14 @@ class Parser(Tokenizer):
type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
p[0] = self.handleNullable(type, p[5])
def p_NonAnyTypeMozMapType(self, p):
def p_NonAnyTypeRecordType(self, p):
"""
NonAnyType : MOZMAP LT Type GT Null
NonAnyType : RECORD LT StringType COMMA Type GT Null
"""
innerType = p[3]
type = IDLMozMapType(self.getLocation(p, 1), innerType)
p[0] = self.handleNullable(type, p[5])
keyType = p[3]
valueType = p[5]
type = IDLRecordType(self.getLocation(p, 1), keyType, valueType)
p[0] = self.handleNullable(type, p[7])
def p_NonAnyTypeScopedName(self, p):
"""
@ -6440,7 +6456,7 @@ class Parser(Tokenizer):
def p_ConstType(self, p):
"""
ConstType : PrimitiveOrStringType Null
ConstType : PrimitiveType Null
"""
type = BuiltinTypes[p[1]]
p[0] = self.handleNullable(type, p[2])
@ -6454,69 +6470,75 @@ class Parser(Tokenizer):
type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
p[0] = self.handleNullable(type, p[2])
def p_PrimitiveOrStringTypeUint(self, p):
def p_PrimitiveTypeUint(self, p):
"""
PrimitiveOrStringType : UnsignedIntegerType
PrimitiveType : UnsignedIntegerType
"""
p[0] = p[1]
def p_PrimitiveOrStringTypeBoolean(self, p):
def p_PrimitiveTypeBoolean(self, p):
"""
PrimitiveOrStringType : BOOLEAN
PrimitiveType : BOOLEAN
"""
p[0] = IDLBuiltinType.Types.boolean
def p_PrimitiveOrStringTypeByte(self, p):
def p_PrimitiveTypeByte(self, p):
"""
PrimitiveOrStringType : BYTE
PrimitiveType : BYTE
"""
p[0] = IDLBuiltinType.Types.byte
def p_PrimitiveOrStringTypeOctet(self, p):
def p_PrimitiveTypeOctet(self, p):
"""
PrimitiveOrStringType : OCTET
PrimitiveType : OCTET
"""
p[0] = IDLBuiltinType.Types.octet
def p_PrimitiveOrStringTypeFloat(self, p):
def p_PrimitiveTypeFloat(self, p):
"""
PrimitiveOrStringType : FLOAT
PrimitiveType : FLOAT
"""
p[0] = IDLBuiltinType.Types.float
def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
def p_PrimitiveTypeUnrestictedFloat(self, p):
"""
PrimitiveOrStringType : UNRESTRICTED FLOAT
PrimitiveType : UNRESTRICTED FLOAT
"""
p[0] = IDLBuiltinType.Types.unrestricted_float
def p_PrimitiveOrStringTypeDouble(self, p):
def p_PrimitiveTypeDouble(self, p):
"""
PrimitiveOrStringType : DOUBLE
PrimitiveType : DOUBLE
"""
p[0] = IDLBuiltinType.Types.double
def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
def p_PrimitiveTypeUnrestictedDouble(self, p):
"""
PrimitiveOrStringType : UNRESTRICTED DOUBLE
PrimitiveType : UNRESTRICTED DOUBLE
"""
p[0] = IDLBuiltinType.Types.unrestricted_double
def p_PrimitiveOrStringTypeDOMString(self, p):
def p_StringType(self, p):
"""
PrimitiveOrStringType : DOMSTRING
StringType : BuiltinStringType
"""
p[0] = BuiltinTypes[p[1]]
def p_BuiltinStringTypeDOMString(self, p):
"""
BuiltinStringType : DOMSTRING
"""
p[0] = IDLBuiltinType.Types.domstring
def p_PrimitiveOrStringTypeBytestring(self, p):
def p_BuiltinStringTypeBytestring(self, p):
"""
PrimitiveOrStringType : BYTESTRING
BuiltinStringType : BYTESTRING
"""
p[0] = IDLBuiltinType.Types.bytestring
def p_PrimitiveOrStringTypeUSVString(self, p):
def p_BuiltinStringTypeUSVString(self, p):
"""
PrimitiveOrStringType : USVSTRING
BuiltinStringType : USVSTRING
"""
p[0] = IDLBuiltinType.Types.usvstring

View File

@ -25,7 +25,7 @@ NS_INTERFACE_MAP_END
// static
already_AddRefed<Headers>
Headers::Constructor(const GlobalObject& aGlobal,
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord>& aInit,
ErrorResult& aRv)
{
RefPtr<InternalHeaders> ih = new InternalHeaders();
@ -39,8 +39,8 @@ Headers::Constructor(const GlobalObject& aGlobal,
ih->Fill(*aInit.Value().GetAsHeaders().mInternalHeaders, aRv);
} else if (aInit.Value().IsByteStringSequenceSequence()) {
ih->Fill(aInit.Value().GetAsByteStringSequenceSequence(), aRv);
} else if (aInit.Value().IsByteStringMozMap()) {
ih->Fill(aInit.Value().GetAsByteStringMozMap(), aRv);
} else if (aInit.Value().IsByteStringByteStringRecord()) {
ih->Fill(aInit.Value().GetAsByteStringByteStringRecord(), aRv);
}
if (aRv.Failed()) {
@ -53,7 +53,7 @@ Headers::Constructor(const GlobalObject& aGlobal,
// static
already_AddRefed<Headers>
Headers::Constructor(const GlobalObject& aGlobal,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord& aInit,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
@ -62,7 +62,7 @@ Headers::Constructor(const GlobalObject& aGlobal,
/* static */ already_AddRefed<Headers>
Headers::Create(nsIGlobalObject* aGlobal,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord& aInit,
ErrorResult& aRv)
{
RefPtr<InternalHeaders> ih = new InternalHeaders();
@ -72,8 +72,8 @@ Headers::Create(nsIGlobalObject* aGlobal,
ih->Fill(*(aInit.GetAsHeaders().get()->mInternalHeaders), aRv);
} else if (aInit.IsByteStringSequenceSequence()) {
ih->Fill(aInit.GetAsByteStringSequenceSequence(), aRv);
} else if (aInit.IsByteStringMozMap()) {
ih->Fill(aInit.GetAsByteStringMozMap(), aRv);
} else if (aInit.IsByteStringByteStringRecord()) {
ih->Fill(aInit.GetAsByteStringByteStringRecord(), aRv);
}
if (NS_WARN_IF(aRv.Failed())) {

View File

@ -20,9 +20,9 @@ class ErrorResult;
namespace dom {
template<typename T> class MozMap;
class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
class OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap;
template<typename K, typename V> class Record;
class HeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord;
class OwningHeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord;
/**
* This Headers class is only used to represent the content facing Headers
@ -57,17 +57,17 @@ public:
static already_AddRefed<Headers>
Constructor(const GlobalObject& aGlobal,
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord>& aInit,
ErrorResult& aRv);
static already_AddRefed<Headers>
Constructor(const GlobalObject& aGlobal,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord& aInit,
ErrorResult& aRv);
static already_AddRefed<Headers>
Create(nsIGlobalObject* aGlobalObject,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringByteStringRecord& aInit,
ErrorResult& aRv);
void Append(const nsACString& aName, const nsACString& aValue,

View File

@ -314,12 +314,13 @@ InternalHeaders::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& a
}
void
InternalHeaders::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
InternalHeaders::Fill(const Record<nsCString, nsCString>& aInit, ErrorResult& aRv)
{
nsTArray<nsString> keys;
aInit.GetKeys(keys);
for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) {
Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
for (auto& entry : aInit.Entries()) {
Append(entry.mKey, entry.mValue, aRv);
if (aRv.Failed()) {
return;
}
}
}

View File

@ -20,7 +20,7 @@ class ErrorResult;
namespace dom {
template<typename T> class MozMap;
template<typename K, typename V> class Record;
class HeadersEntry;
class InternalHeaders final
@ -113,7 +113,7 @@ public:
void Fill(const InternalHeaders& aInit, ErrorResult& aRv);
void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
void Fill(const Record<nsCString, nsCString>& aInit, ErrorResult& aRv);
bool HasOnlySimpleHeaders() const;

View File

@ -314,14 +314,6 @@ URLSearchParams::URLSearchParams(nsISupports* aParent,
{
}
URLSearchParams::URLSearchParams(nsISupports* aParent,
const URLSearchParams& aOther)
: mParams(new URLParams(*aOther.mParams.get()))
, mParent(aParent)
, mObserver(nullptr)
{
}
URLSearchParams::~URLSearchParams()
{
DeleteAll();
@ -335,34 +327,43 @@ URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
/* static */ already_AddRefed<URLSearchParams>
URLSearchParams::Constructor(const GlobalObject& aGlobal,
const nsAString& aInit,
const USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString& aInit,
ErrorResult& aRv)
{
RefPtr<URLSearchParams> sp =
new URLSearchParams(aGlobal.GetAsSupports(), nullptr);
NS_ConvertUTF16toUTF8 input(aInit);
if (StringBeginsWith(input, NS_LITERAL_CSTRING("?"))) {
sp->ParseInput(Substring(input, 1, input.Length() - 1));
if (aInit.IsUSVString()) {
NS_ConvertUTF16toUTF8 input(aInit.GetAsUSVString());
if (StringBeginsWith(input, NS_LITERAL_CSTRING("?"))) {
sp->ParseInput(Substring(input, 1, input.Length() - 1));
} else {
sp->ParseInput(input);
}
} else if (aInit.IsUSVStringSequenceSequence()) {
const Sequence<Sequence<nsString>>& list =
aInit.GetAsUSVStringSequenceSequence();
for (uint32_t i = 0; i < list.Length(); ++i) {
const Sequence<nsString>& item = list[i];
if (item.Length() != 2) {
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
return nullptr;
}
sp->Append(item[0], item[1]);
}
} else if (aInit.IsUSVStringUSVStringRecord()) {
const Record<nsString, nsString>& record =
aInit.GetAsUSVStringUSVStringRecord();
for (auto& entry : record.Entries()) {
sp->Append(entry.mKey, entry.mValue);
}
} else {
sp->ParseInput(input);
MOZ_CRASH("This should not happen.");
}
return sp.forget();
}
/* static */ already_AddRefed<URLSearchParams>
URLSearchParams::Constructor(const GlobalObject& aGlobal,
URLSearchParams& aInit,
ErrorResult& aRv)
{
RefPtr<URLSearchParams> sp =
new URLSearchParams(aGlobal.GetAsSupports(), aInit);
return sp.forget();
}
void
URLSearchParams::ParseInput(const nsACString& aInput)
{

View File

@ -20,6 +20,7 @@ namespace mozilla {
namespace dom {
class URLSearchParams;
class USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString;
class URLSearchParamsObserver : public nsISupports
{
@ -43,14 +44,6 @@ public:
DeleteAll();
}
explicit URLParams(const URLParams& aOther)
: mParams(aOther.mParams)
{}
URLParams(const URLParams&& aOther)
: mParams(Move(aOther.mParams))
{}
class ForEachIterator
{
public:
@ -144,9 +137,6 @@ public:
explicit URLSearchParams(nsISupports* aParent,
URLSearchParamsObserver* aObserver=nullptr);
URLSearchParams(nsISupports* aParent,
const URLSearchParams& aOther);
// WebIDL methods
nsISupports* GetParentObject() const
{
@ -157,11 +147,8 @@ public:
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<URLSearchParams>
Constructor(const GlobalObject& aGlobal, const nsAString& aInit,
ErrorResult& aRv);
static already_AddRefed<URLSearchParams>
Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit,
Constructor(const GlobalObject& aGlobal,
const USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString& aInit,
ErrorResult& aRv);
void ParseInput(const nsACString& aInput);

View File

@ -8,7 +8,7 @@
* http://fetch.spec.whatwg.org/#headers-class
*/
typedef (Headers or sequence<sequence<ByteString>> or MozMap<ByteString>) HeadersInit;
typedef (Headers or sequence<sequence<ByteString>> or record<ByteString, ByteString>) HeadersInit;
enum HeadersGuardEnum {
"none",

View File

@ -57,7 +57,7 @@ interface InstallTriggerImpl {
* A callback to call as each installation succeeds or fails
* @return true if the installations were successfully started
*/
boolean install(MozMap<(DOMString or InstallTriggerData)> installs,
boolean install(record<DOMString, (DOMString or InstallTriggerData)> installs,
optional InstallTriggerCallback callback);
/**

View File

@ -24,7 +24,7 @@ interface TestInterfaceJS : EventTarget {
any pingPongObjectOrString((object or DOMString) objOrString);
TestInterfaceJSDictionary pingPongDictionary(optional TestInterfaceJSDictionary dict);
long pingPongDictionaryOrLong(optional (TestInterfaceJSUnionableDictionary or long) dictOrLong);
DOMString pingPongMap(MozMap<any> map);
DOMString pingPongMap(record<DOMString, any> map);
long objectSequenceLength(sequence<object> seq);
long anySequenceLength(sequence<any> seq);

View File

@ -13,8 +13,7 @@
* http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
*/
[Constructor(optional USVString init = ""),
Constructor(URLSearchParams init),
[Constructor(optional (sequence<sequence<USVString>> or record<USVString, USVString> or USVString) init = ""),
Exposed=(Window,Worker,WorkerDebugger,System)]
interface URLSearchParams {
void append(USVString name, USVString value);