diff options
35 files changed, 798 insertions, 464 deletions
diff --git a/application/palemoon/app/profile/palemoon.js b/application/palemoon/app/profile/palemoon.js index 3df5d7194..85dc41c15 100644 --- a/application/palemoon/app/profile/palemoon.js +++ b/application/palemoon/app/profile/palemoon.js @@ -1121,6 +1121,9 @@ pref("security.csp.speccompliant", true); // Block insecure active content on https pages pref("security.mixed_content.block_active_content", true); +// Disable Microsoft Family Safety MitM support +pref("security.family_safety.mode", 0); + // Override the Gecko-default value of false for Pale Moon. pref("plain_text.wrap_long_lines", true); diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 323feca52..8d2bdaac6 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -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 diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 24b47a545..a3ec70f47 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -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); } +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, nsAString& aString); +NormalizeUSVString(nsAString& aString); void -NormalizeUSVString(JSContext* aCx, binding_detail::FakeString& aString); +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) -{ - 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); -} - -template<typename T> -void TraceMozMap(JSTracer* trc, MozMap<T>& map) +template<typename K, typename V> +void TraceRecord(JSTracer* trc, Record<K, V>& record) { - map.EnumerateValues(TraceMozMapValue<T>, trc); + 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); + } } -// 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> diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index cb93e4897..74acb5918 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -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,122 @@ 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); + if (!js::GetPropertyKeys(cx, recordObj, + JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &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))) { + + 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; + // This will just throw if idVal is a Symbol, like the spec says + // to do. + 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 +4988,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 +5013,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 +5036,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 +5218,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 +5599,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 +5762,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 +5774,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 +5798,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 +5842,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 +6331,7 @@ def getMaybeWrapValueFuncForType(type): sequenceWrapLevel = 0 -mozMapWrapLevel = 0 +recordWrapLevel = 0 def getWrapTemplateForType(type, descriptorProvider, result, successCode, @@ -6361,7 +6436,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 +6509,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 +6529,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 +6552,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 +6574,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 +6859,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 +6955,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 +7066,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 +7228,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 +7289,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 +8198,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 +9496,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 +9757,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 +10133,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 +13265,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 +13676,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 +14182,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 +14232,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 +14300,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 +14311,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(): diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 5c96580a1..f80c19c33 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -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() diff --git a/dom/bindings/MozMap.h b/dom/bindings/MozMap.h deleted file mode 100644 index 1e920c098..000000000 --- a/dom/bindings/MozMap.h +++ /dev/null @@ -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 diff --git a/dom/bindings/Record.h b/dom/bindings/Record.h new file mode 100644 index 000000000..d6ab31699 --- /dev/null +++ b/dom/bindings/Record.h @@ -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 diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index f1ce9e276..7e1358e9c 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -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', diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index f668d7d62..8c32a8738 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -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): + """ + StringType : BuiltinStringType + """ + p[0] = BuiltinTypes[p[1]] + + def p_BuiltinStringTypeDOMString(self, p): """ - PrimitiveOrStringType : DOMSTRING + 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 diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 1791399b7..6294b0dc5 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -903,11 +903,7 @@ FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const AutoTArray<InternalHeaders::Entry, 5> headers; mRequest->Headers()->GetEntries(headers); - bool hasAccept = false; for (uint32_t i = 0; i < headers.Length(); ++i) { - if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) { - hasAccept = true; - } if (headers[i].mValue.IsEmpty()) { aChannel->SetEmptyRequestHeader(headers[i].mName); } else { @@ -915,12 +911,6 @@ FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const } } - if (!hasAccept) { - aChannel->SetRequestHeader(NS_LITERAL_CSTRING("accept"), - NS_LITERAL_CSTRING("*/*"), - false /* merge */); - } - if (mRequest->ForceOriginHeader()) { nsAutoString origin; if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) { diff --git a/dom/fetch/Headers.cpp b/dom/fetch/Headers.cpp index 1e1a46c62..ca5aa57a6 100644 --- a/dom/fetch/Headers.cpp +++ b/dom/fetch/Headers.cpp @@ -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())) { diff --git a/dom/fetch/Headers.h b/dom/fetch/Headers.h index b603dc836..1dd92f779 100644 --- a/dom/fetch/Headers.h +++ b/dom/fetch/Headers.h @@ -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, diff --git a/dom/fetch/InternalHeaders.cpp b/dom/fetch/InternalHeaders.cpp index 11585615e..f66221d42 100644 --- a/dom/fetch/InternalHeaders.cpp +++ b/dom/fetch/InternalHeaders.cpp @@ -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; + } } } diff --git a/dom/fetch/InternalHeaders.h b/dom/fetch/InternalHeaders.h index 9a6d6dae7..98046f0ef 100644 --- a/dom/fetch/InternalHeaders.h +++ b/dom/fetch/InternalHeaders.h @@ -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; diff --git a/dom/url/URLSearchParams.cpp b/dom/url/URLSearchParams.cpp index d9492f81c..f762299f8 100644 --- a/dom/url/URLSearchParams.cpp +++ b/dom/url/URLSearchParams.cpp @@ -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("URLSearchParams: Invalid string"); } 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) { diff --git a/dom/url/URLSearchParams.h b/dom/url/URLSearchParams.h index 4b0aaa991..9fefd78dd 100644 --- a/dom/url/URLSearchParams.h +++ b/dom/url/URLSearchParams.h @@ -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); diff --git a/dom/webidl/Headers.webidl b/dom/webidl/Headers.webidl index 205ab9f9e..eef552a7f 100644 --- a/dom/webidl/Headers.webidl +++ b/dom/webidl/Headers.webidl @@ -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", diff --git a/dom/webidl/InstallTrigger.webidl b/dom/webidl/InstallTrigger.webidl index 789fb2bc4..68f48ddc6 100644 --- a/dom/webidl/InstallTrigger.webidl +++ b/dom/webidl/InstallTrigger.webidl @@ -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); /** diff --git a/dom/webidl/TestInterfaceJS.webidl b/dom/webidl/TestInterfaceJS.webidl index 1ca629c39..2cf8d701a 100644 --- a/dom/webidl/TestInterfaceJS.webidl +++ b/dom/webidl/TestInterfaceJS.webidl @@ -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); diff --git a/dom/webidl/URLSearchParams.webidl b/dom/webidl/URLSearchParams.webidl index 93e846071..b93f4e8b1 100644 --- a/dom/webidl/URLSearchParams.webidl +++ b/dom/webidl/URLSearchParams.webidl @@ -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); diff --git a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp index 726441757..c9bcc31ff 100644 --- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp @@ -462,10 +462,6 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); if (httpChannel) { - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("*/*"), - false); - nsCOMPtr<nsIURI> referrerURI; aReferrerPrincipal->GetURI(getter_AddRefs(referrerURI)); if (referrerURI) { diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index a1a0fcfd9..0ce337e29 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -1677,10 +1677,6 @@ Loader::LoadSheet(SheetLoadData* aLoadData, nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); if (httpChannel) { - // Send a minimal Accept header for text/css - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("text/css,*/*;q=0.1"), - false); nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI(); if (referrerURI) httpChannel->SetReferrerWithPolicy(referrerURI, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 3666ca425..4111ca8a9 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1474,7 +1474,10 @@ pref("network.http.request.max-start-delay", 10); pref("network.http.request.max-attempts", 10); // Headers -pref("network.http.accept.default", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); +pref("network.http.accept.default", "*/*"); +pref("network.http.accept.navigation", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); +pref("network.http.accept.image", "image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5"); +pref("network.http.accept.style", "text/css,*/*;q=0.1"); // Prefs allowing granular control of referers // 0=don't send any, 1=send only on clicks, 2=send on image requests as well diff --git a/netwerk/base/security-prefs.js b/netwerk/base/security-prefs.js index ea0b2236d..ef78ddccb 100644 --- a/netwerk/base/security-prefs.js +++ b/netwerk/base/security-prefs.js @@ -117,10 +117,6 @@ pref("security.webauth.u2f", false); pref("security.webauth.u2f_enable_softtoken", false); pref("security.webauth.u2f_enable_usbtoken", false); -pref("security.ssl.errorReporting.enabled", true); -pref("security.ssl.errorReporting.url", "https://incoming.telemetry.mozilla.org/submit/sslreports/"); -pref("security.ssl.errorReporting.automatic", false); - // OCSP must-staple pref("security.ssl.enable_ocsp_must_staple", true); diff --git a/netwerk/protocol/http/AlternateServices.cpp b/netwerk/protocol/http/AlternateServices.cpp index b3e6babe3..ee2fa9331 100644 --- a/netwerk/protocol/http/AlternateServices.cpp +++ b/netwerk/protocol/http/AlternateServices.cpp @@ -654,8 +654,13 @@ private: { nsID channelId; nsLoadFlags flags; + + nsContentPolicyType contentPolicyType = + loadInfo ? loadInfo->GetExternalContentPolicyType() + : nsIContentPolicy::TYPE_OTHER; + if (NS_FAILED(gHttpHandler->NewChannelId(&channelId)) || - NS_FAILED(chan->Init(uri, caps, nullptr, 0, nullptr, channelId)) || + NS_FAILED(chan->Init(uri, caps, nullptr, 0, nullptr, channelId, contentPolicyType)) || NS_FAILED(chan->SetAllowAltSvc(false)) || NS_FAILED(chan->SetRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_ERROR)) || NS_FAILED(chan->SetLoadInfo(loadInfo)) || diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 9e43d89e0..86e177e71 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -151,7 +151,8 @@ HttpBaseChannel::Init(nsIURI *aURI, nsProxyInfo *aProxyInfo, uint32_t aProxyResolveFlags, nsIURI *aProxyURI, - const nsID& aChannelId) + const nsID& aChannelId, + nsContentPolicyType aContentPolicyType) { LOG(("HttpBaseChannel::Init [this=%p]\n", this)); @@ -200,7 +201,7 @@ HttpBaseChannel::Init(nsIURI *aURI, rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); if (NS_FAILED(rv)) return rv; - rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead, isHTTPS); + rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead, isHTTPS, aContentPolicyType); if (NS_FAILED(rv)) return rv; nsAutoCString type; diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index 9aa696a70..8def0f23c 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -99,7 +99,8 @@ public: virtual nsresult Init(nsIURI *aURI, uint32_t aCaps, nsProxyInfo *aProxyInfo, uint32_t aProxyResolveFlags, nsIURI *aProxyURI, - const nsID& aChannelId); + const nsID& aChannelId, + nsContentPolicyType aContentPolicyType); // nsIRequest NS_IMETHOD GetName(nsACString& aName) override; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index a890c51b3..481df5ff0 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -324,10 +324,16 @@ nsHttpChannel::Init(nsIURI *uri, nsProxyInfo *proxyInfo, uint32_t proxyResolveFlags, nsIURI *proxyURI, - const nsID& channelId) -{ - nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo, - proxyResolveFlags, proxyURI, channelId); + const nsID& channelId, + nsContentPolicyType aContentPolicyType) +{ + nsresult rv = HttpBaseChannel::Init(uri, + caps, + proxyInfo, + proxyResolveFlags, + proxyURI, + channelId, + aContentPolicyType); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 554875b1c..0038e1f71 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -123,7 +123,8 @@ public: virtual nsresult Init(nsIURI *aURI, uint32_t aCaps, nsProxyInfo *aProxyInfo, uint32_t aProxyResolveFlags, nsIURI *aProxyURI, - const nsID& aChannelId) override; + const nsID& aChannelId, + nsContentPolicyType aContentPolicyType) override; nsresult OnPush(const nsACString &uri, Http2PushedStream *pushedStream); diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 477961454..0f4c94202 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -468,7 +468,9 @@ nsHttpHandler::InitConnectionMgr() } nsresult -nsHttpHandler::AddStandardRequestHeaders(nsHttpRequestHead *request, bool isSecure) +nsHttpHandler::AddStandardRequestHeaders(nsHttpRequestHead *request, + bool isSecure, + nsContentPolicyType aContentPolicyType) { nsresult rv; @@ -481,7 +483,20 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpRequestHead *request, bool isSecu // Add the "Accept" header. Note, this is set as an override because the // service worker expects to see it. The other "default" headers are // hidden from service worker interception. - rv = request->SetHeader(nsHttp::Accept, mAccept, + nsAutoCString accept; + if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || + aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) { + accept.Assign(mAcceptNavigation); + } else if (aContentPolicyType == nsIContentPolicy::TYPE_IMAGE || + aContentPolicyType == nsIContentPolicy::TYPE_IMAGESET) { + accept.Assign(mAcceptImage); + } else if (aContentPolicyType == nsIContentPolicy::TYPE_STYLESHEET) { + accept.Assign(mAcceptStyle); + } else { + accept.Assign(mAcceptDefault); + } + + rv = request->SetHeader(nsHttp::Accept, accept, false, nsHttpHeaderArray::eVarietyRequestOverride); if (NS_FAILED(rv)) return rv; @@ -1268,12 +1283,36 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) mQoSBits = (uint8_t) clamped(val, 0, 0xff); } + if (PREF_CHANGED(HTTP_PREF("accept.navigation"))) { + nsXPIDLCString accept; + rv = prefs->GetCharPref(HTTP_PREF("accept.navigation"), + getter_Copies(accept)); + if (NS_SUCCEEDED(rv)) + SetAccept(accept, ACCEPT_NAVIGATION); + } + + if (PREF_CHANGED(HTTP_PREF("accept.image"))) { + nsXPIDLCString accept; + rv = prefs->GetCharPref(HTTP_PREF("accept.image"), + getter_Copies(accept)); + if (NS_SUCCEEDED(rv)) + SetAccept(accept, ACCEPT_IMAGE); + } + + if (PREF_CHANGED(HTTP_PREF("accept.style"))) { + nsXPIDLCString accept; + rv = prefs->GetCharPref(HTTP_PREF("accept.style"), + getter_Copies(accept)); + if (NS_SUCCEEDED(rv)) + SetAccept(accept, ACCEPT_STYLE); + } + if (PREF_CHANGED(HTTP_PREF("accept.default"))) { nsXPIDLCString accept; rv = prefs->GetCharPref(HTTP_PREF("accept.default"), getter_Copies(accept)); if (NS_SUCCEEDED(rv)) - SetAccept(accept); + SetAccept(accept, ACCEPT_DEFAULT); } if (PREF_CHANGED(HTTP_PREF("accept-encoding"))) { @@ -1897,9 +1936,21 @@ nsHttpHandler::SetAcceptLanguages() } nsresult -nsHttpHandler::SetAccept(const char *aAccept) +nsHttpHandler::SetAccept(const char *aAccept, AcceptType aType) { - mAccept = aAccept; + switch (aType) { + case ACCEPT_NAVIGATION: + mAcceptNavigation = aAccept; + break; + case ACCEPT_IMAGE: + mAcceptImage = aAccept; + break; + case ACCEPT_STYLE: + mAcceptStyle = aAccept; + break; + case ACCEPT_DEFAULT: + mAcceptDefault = aAccept; + } return NS_OK; } @@ -2057,7 +2108,11 @@ nsHttpHandler::NewProxiedChannel2(nsIURI *uri, rv = NewChannelId(&channelId); NS_ENSURE_SUCCESS(rv, rv); - rv = httpChannel->Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI, channelId); + nsContentPolicyType contentPolicyType = + aLoadInfo ? aLoadInfo->GetExternalContentPolicyType() + : nsIContentPolicy::TYPE_OTHER; + + rv = httpChannel->Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI, channelId, contentPolicyType); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index f1ec0f947..67b9ebe0e 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -15,6 +15,7 @@ #include "nsCOMPtr.h" #include "nsWeakReference.h" +#include "nsIContentPolicy.h" #include "nsIHttpProtocolHandler.h" #include "nsIObserver.h" #include "nsISpeculativeConnect.h" @@ -50,6 +51,14 @@ enum FrameCheckLevel { FRAMECHECK_STRICT }; +// Fetch spec different http Accept types +enum AcceptType { + ACCEPT_NAVIGATION, + ACCEPT_IMAGE, + ACCEPT_STYLE, + ACCEPT_DEFAULT, +}; + //----------------------------------------------------------------------------- // nsHttpHandler - protocol handler for HTTP and HTTPS //----------------------------------------------------------------------------- @@ -70,7 +79,7 @@ public: nsHttpHandler(); nsresult Init(); - nsresult AddStandardRequestHeaders(nsHttpRequestHead *, bool isSecure); + nsresult AddStandardRequestHeaders(nsHttpRequestHead *, bool isSecure, nsContentPolicyType aContentPolicyType); nsresult AddConnectionHeader(nsHttpRequestHead *, uint32_t capabilities); bool IsAcceptableEncoding(const char *encoding, bool isSecure); @@ -385,7 +394,7 @@ private: void InitUserAgentComponents(); void PrefsChanged(nsIPrefBranch *prefs, const char *pref); - nsresult SetAccept(const char *); + nsresult SetAccept(const char *, AcceptType aType); nsresult SetAcceptLanguages(); nsresult SetAcceptEncodings(const char *, bool mIsSecure); @@ -394,8 +403,8 @@ private: void NotifyObservers(nsIHttpChannel *chan, const char *event); static void TimerCallback(nsITimer * aTimer, void * aClosure); + private: - // cached services nsMainThreadPtrHandle<nsIIOService> mIOService; nsMainThreadPtrHandle<nsIStreamConverterService> mStreamConvSvc; @@ -460,7 +469,10 @@ private: bool mPipeliningOverSSL; bool mEnforceAssocReq; - nsCString mAccept; + nsCString mAcceptNavigation; + nsCString mAcceptImage; + nsCString mAcceptStyle; + nsCString mAcceptDefault; nsCString mAcceptLanguages; nsCString mHttpAcceptEncodings; nsCString mHttpsAcceptEncodings; diff --git a/netwerk/test/mochitests/mochitest.ini b/netwerk/test/mochitests/mochitest.ini index f8a919031..3cd5a674b 100644 --- a/netwerk/test/mochitests/mochitest.ini +++ b/netwerk/test/mochitests/mochitest.ini @@ -25,3 +25,5 @@ support-files = [test_viewsource_unlinkable.html] [test_xhr_method_case.html] [test_1396395.html] +[test_accept_header.html] +support-files = test_accept_header.sjs diff --git a/netwerk/test/mochitests/test_accept_header.html b/netwerk/test/mochitests/test_accept_header.html new file mode 100644 index 000000000..b8434230f --- /dev/null +++ b/netwerk/test/mochitests/test_accept_header.html @@ -0,0 +1,106 @@ +<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Accept header</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script>
+
+// All the requests are sent to test_accept_header.sjs which will return
+// different content based on the queryString. When the queryString is 'get',
+// test_accept_header.sjs returns a JSON object with the latest request and its
+// accept header value.
+
+function test_last_request_and_continue(query, expected) {
+ fetch("test_accept_header.sjs?get").then(r => r.json()).then(json => {
+ is(json.type, query, "Expected: " + query);
+ is(json.accept, expected, "Accept header: " + expected);
+ next();
+ });
+}
+
+function test_iframe() {
+ let observer = new PerformanceObserver(function(list, obj) {
+ obj.disconnect();
+
+ list.getEntries().forEach(entry => {
+ if (entry.name.endsWith("test_accept_header.sjs?iframe")) {
+ obj.disconnect();
+ test_last_request_and_continue("iframe", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
+ }
+ });
+ });
+
+ observer.observe({entryTypes: ["resource"]});
+
+ let ifr = document.createElement("iframe");
+ ifr.src = "test_accept_header.sjs?iframe";
+ document.body.appendChild(ifr);
+}
+
+function test_image() {
+ let i = new Image();
+ i.src = "test_accept_header.sjs?image";
+ i.onload = function() {
+ // Fetch spec says we should have: "image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5"
+ test_last_request_and_continue("image", "image/webp,image/png,image/*;q=0.8,*/*;q=0.5");
+ }
+}
+
+function test_style() {
+ let observer = new PerformanceObserver(function(list, obj) {
+ obj.disconnect();
+
+ list.getEntries().forEach(entry => {
+ if (entry.name.endsWith("test_accept_header.sjs?style")) {
+ obj.disconnect();
+ test_last_request_and_continue("style", "text/css,*/*;q=0.1");
+ }
+ });
+ });
+
+ observer.observe({entryTypes: ["resource"]});
+
+ let head = document.getElementsByTagName("head")[0];
+ let link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.type = "text/css";
+ link.href = "test_accept_header.sjs?style";
+ head.appendChild(link);
+}
+
+function test_worker() {
+ let w = new Worker("test_accept_header.sjs?worker");
+ w.onmessage = function() {
+ test_last_request_and_continue("worker", "*/*");
+ }
+}
+
+let tests = [
+ test_iframe,
+ test_image,
+ test_style,
+ test_worker,
+];
+
+function next() {
+ if (tests.length == 0) {
+ SimpleTest.finish();
+ return;
+ }
+
+ let test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({ "set": [
+ [ "dom.enable_performance_observer", true ]
+]}, next);
+
+</script>
+</body>
+</html>
diff --git a/netwerk/test/mochitests/test_accept_header.sjs b/netwerk/test/mochitests/test_accept_header.sjs new file mode 100644 index 000000000..035c886aa --- /dev/null +++ b/netwerk/test/mochitests/test_accept_header.sjs @@ -0,0 +1,48 @@ +function handleRequest(request, response) {
+ response.setStatusLine(request.httpVersion, "200", "OK");
+
+ if (request.queryString == "worker") {
+ response.setHeader("Content-Type", "application/json", false);
+ response.write("postMessage(42)");
+
+ setState("data", JSON.stringify({type: "worker", accept: request.getHeader("Accept") }));
+ return;
+ }
+
+ if (request.queryString == "image") {
+ // A 1x1 PNG image.
+ // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+ const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+ "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+
+ response.setHeader("Content-Type", "image/png", false);
+ response.write(IMAGE);
+
+ setState("data", JSON.stringify({type: "image", accept: request.getHeader("Accept") }));
+ return;
+ }
+
+ if (request.queryString == "style") {
+ response.setHeader("Content-Type", "text/css", false);
+ response.write("");
+
+ setState("data", JSON.stringify({type: "style", accept: request.getHeader("Accept") }));
+ return;
+ }
+
+ if (request.queryString == "iframe") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("<h1>Hello world!</h1>");
+
+ setState("data", JSON.stringify({type: "iframe", accept: request.getHeader("Accept") }));
+ return;
+ }
+
+ if (request.queryString == "get") {
+ response.setHeader("Content-Type", "text/javascript", false);
+ response.write(getState("data"));
+
+ setState("data", "");
+ return;
+ }
+}
diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h index 82586a79a..64af17bbb 100644 --- a/xpcom/glue/nsTArray.h +++ b/xpcom/glue/nsTArray.h @@ -1503,6 +1503,24 @@ public: mozilla::Forward<Item>(aItem)); } + // Reconstruct the element at the given index, and return a pointer to the + // reconstructed element. This will destroy the existing element and + // default-construct a new one, giving you a state much like what single-arg + // InsertElementAt(), or no-arg AppendElement() does, but without changing the + // length of the array. + // + // array[idx] = T() + // + // would accomplish the same thing as long as T has the appropriate moving + // operator=, but some types don't for various reasons. + elem_type* ReconstructElementAt(index_type aIndex) + { + elem_type* elem = &ElementAt(aIndex); + elem_traits::Destruct(elem); + elem_traits::Construct(elem); + return elem; + } + // This method searches for the smallest index of an element that is strictly // greater than |aItem|. If |aItem| is inserted at this index, the array will // remain sorted and |aItem| would come after all elements that are equal to |