diff options
Diffstat (limited to 'dom/bindings')
-rw-r--r-- | dom/bindings/BindingUtils.cpp | 24 | ||||
-rw-r--r-- | dom/bindings/BindingUtils.h | 106 | ||||
-rw-r--r-- | dom/bindings/CallbackObject.cpp | 1 | ||||
-rw-r--r-- | dom/bindings/Codegen.py | 362 | ||||
-rw-r--r-- | dom/bindings/Configuration.py | 2 | ||||
-rw-r--r-- | dom/bindings/DOMJSProxyHandler.cpp | 13 | ||||
-rw-r--r-- | dom/bindings/DOMJSProxyHandler.h | 5 | ||||
-rw-r--r-- | dom/bindings/ErrorResult.h | 2 | ||||
-rw-r--r-- | dom/bindings/Exceptions.cpp | 1 | ||||
-rw-r--r-- | dom/bindings/MozMap.h | 121 | ||||
-rw-r--r-- | dom/bindings/Record.h | 91 | ||||
-rw-r--r-- | dom/bindings/SimpleGlobalObject.cpp | 2 | ||||
-rw-r--r-- | dom/bindings/moz.build | 12 | ||||
-rw-r--r-- | dom/bindings/nsIScriptError.idl | 135 | ||||
-rw-r--r-- | dom/bindings/nsScriptError.cpp | 438 | ||||
-rw-r--r-- | dom/bindings/nsScriptError.h | 109 | ||||
-rw-r--r-- | dom/bindings/nsScriptErrorWithStack.cpp | 120 | ||||
-rw-r--r-- | dom/bindings/parser/WebIDL.py | 152 | ||||
-rw-r--r-- | dom/bindings/test/test_Object.prototype_props.html | 6 |
19 files changed, 1289 insertions, 413 deletions
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 7056658a7..6e0db29b1 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -119,7 +119,7 @@ ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE; MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) <= 2); JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr, - static_cast<const unsigned>(errorNumber), + static_cast<unsigned>(errorNumber), funcNameStr.get(), ifaceName.get()); return false; } @@ -226,7 +226,7 @@ TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(JSContext* aCx) args[argCount] = nullptr; JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr, - static_cast<const unsigned>(message->mErrorNumber), + static_cast<unsigned>(message->mErrorNumber), argCount > 0 ? args : nullptr); ClearMessage(); @@ -1184,14 +1184,6 @@ GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, } bool -UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp) -{ - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - args.rval().set(args.thisv()); - return true; -} - -bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) { return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR); @@ -1976,8 +1968,6 @@ const js::ObjectOps sInterfaceObjectClassObjectOps = { nullptr, /* setProperty */ nullptr, /* getOwnPropertyDescriptor */ nullptr, /* deleteProperty */ - nullptr, /* watch */ - nullptr, /* unwatch */ nullptr, /* getElements */ nullptr, /* enumerate */ InterfaceObjectToString, /* funToString */ @@ -2558,7 +2548,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 +2565,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 3e58390c9..5cab835b3 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); @@ -515,7 +515,7 @@ class ProtoAndIfaceCache { public: PageTableCache() { - memset(&mPages, 0, sizeof(mPages)); + memset(mPages.begin(), 0, sizeof(mPages)); } ~PageTableCache() { @@ -2045,9 +2045,6 @@ GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, } bool -UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp); - -bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp); bool @@ -2127,11 +2124,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 +2174,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 +2316,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); -} - -template<typename T> -void TraceMozMap(JSTracer* trc, MozMap<T>& map) -{ - 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 +2413,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/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp index 7c7d2c6b4..bb01c804c 100644 --- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -37,7 +37,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CallbackObject) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncumbentGlobal) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CallbackObject) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncumbentGlobal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackObject) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 7a6668687..924241aa0 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 @@ -2388,11 +2390,10 @@ class MethodDefiner(PropertyDefiner): # Synthesize our valueOf method self.regular.append({ "name": 'valueOf', - "nativeName": "UnforgeableValueOf", + "selfHostedName": "Object_valueOf", "methodInfo": False, "length": 0, - "flags": "JSPROP_ENUMERATE", # readonly/permanent added - # automatically. + "flags": "0", # readonly/permanent added automatically. "condition": MemberCondition() }) @@ -3454,19 +3455,15 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode, "nsContentUtils::ThreadsafeIsCallerChrome()")) if descriptor.interface.getExtendedAttribute("Unforgeable"): - # We do our undefined toJSON and toPrimitive here, not as a regular - # property because we don't have a concept of value props anywhere in - # IDL. + # We do our undefined toPrimitive here, not as a regular property + # because we don't have a concept of value props anywhere in IDL. unforgeables.append(CGGeneric(fill( """ JS::RootedId toPrimitive(aCx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive))); if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive, JS::UndefinedHandleValue, - JSPROP_READONLY | JSPROP_PERMANENT) || - !JS_DefineProperty(aCx, ${holderName}, "toJSON", - JS::UndefinedHandleValue, - JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) { + JSPROP_READONLY | JSPROP_PERMANENT)) { $*{failureCode} } """, @@ -4290,6 +4287,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 +4355,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 +4367,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 +4572,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 +4738,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 +4785,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 +4983,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 +5008,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 +5031,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 +5213,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 +5594,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 +5757,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 +5769,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 +5793,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 +5837,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 +6326,7 @@ def getMaybeWrapValueFuncForType(type): sequenceWrapLevel = 0 -mozMapWrapLevel = 0 +recordWrapLevel = 0 def getWrapTemplateForType(type, descriptorProvider, result, successCode, @@ -6361,7 +6431,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 +6504,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 +6524,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 +6547,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 +6569,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 +6854,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 +6950,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 +7061,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 +7223,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 +7284,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 +8193,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 +9491,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 +9752,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 +10128,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() @@ -13081,9 +13169,9 @@ class CGDictionary(CGThing): # continues to match the list in test_Object.prototype_props.html if (member.identifier.name in ["constructor", "toSource", "toString", "toLocaleString", "valueOf", - "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", - "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", - "__lookupGetter__", "__lookupSetter__", "__proto__"]): + "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", + "__defineGetter__", "__defineSetter__", "__lookupGetter__", + "__lookupSetter__", "__proto__"]): raise TypeError("'%s' member of %s dictionary shadows " "a property of Object.prototype, and Xrays to " "Object can't handle that.\n" @@ -13172,8 +13260,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 +13671,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 +14177,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 +14227,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 +14295,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 +14306,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(): @@ -14994,7 +15083,6 @@ class CGJSImplClass(CGBindingImplClass): NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName}) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName}) NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName}) 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/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 23f0abd88..49281c1c2 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -275,19 +275,6 @@ DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy, } bool -BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, - JS::Handle<JSObject*> callable) const -{ - return js::WatchGuts(cx, proxy, id, callable); -} - -bool -BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const -{ - return js::UnwatchGuts(cx, proxy, id); -} - -bool BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector& props) const diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index 1781649cc..e3e151b7a 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -72,11 +72,6 @@ public: virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector &props) const override; - bool watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, - JS::Handle<JSObject*> callable) const override; - bool unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, - JS::Handle<jsid> id) const override; - protected: // Hook for subclasses to implement shared ownPropertyKeys()/keys() // functionality. The "flags" argument is either JSITER_OWNONLY (for keys()) diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h index 7c3fc9e2f..da7ec30e7 100644 --- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -75,7 +75,7 @@ template<typename... Ts> inline bool ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, Ts&&... aArgs) { - binding_detail::ThrowErrorMessage(aCx, static_cast<const unsigned>(aErrorNumber), + binding_detail::ThrowErrorMessage(aCx, static_cast<unsigned>(aErrorNumber), mozilla::Forward<Ts>(aArgs)...); return false; } diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp index a3f807688..8ace37051 100644 --- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -305,7 +305,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSStackFrame) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCaller) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAsyncCaller) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(JSStackFrame) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack) 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/SimpleGlobalObject.cpp b/dom/bindings/SimpleGlobalObject.cpp index 6ac397019..88710f7d9 100644 --- a/dom/bindings/SimpleGlobalObject.cpp +++ b/dom/bindings/SimpleGlobalObject.cpp @@ -29,8 +29,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SimpleGlobalObject) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SimpleGlobalObject) - - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS tmp->TraverseHostObjectURIs(cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index f1ce9e276..ed8a4d37e 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -6,6 +6,12 @@ TEST_DIRS += ['test'] +XPIDL_SOURCES += [ + 'nsIScriptError.idl' +] + +XPIDL_MODULE = 'dom_bindings' + EXPORTS.ipc += [ 'ErrorIPCUtils.h', ] @@ -32,10 +38,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', @@ -91,6 +97,8 @@ UNIFIED_SOURCES += [ 'DOMJSProxyHandler.cpp', 'Exceptions.cpp', 'IterableIterator.cpp', + 'nsScriptError.cpp', + 'nsScriptErrorWithStack.cpp', 'SimpleGlobalObject.cpp', 'ToJSValue.cpp', 'WebIDLGlobalNameHash.cpp', @@ -139,7 +147,7 @@ FINAL_LIBRARY = 'xul' SPHINX_TREES['webidl'] = 'docs' SPHINX_PYTHON_PACKAGE_DIRS += ['mozwebidlcodegen'] -if CONFIG['MOZ_PHOENIX'] or CONFIG['MOZ_FENNEC'] or CONFIG['MOZ_XULRUNNER']: +if CONFIG['MOZ_PHOENIX'] or CONFIG['MOZ_XULRUNNER']: # This is needed for Window.webidl DEFINES['HAVE_SIDEBAR'] = True diff --git a/dom/bindings/nsIScriptError.idl b/dom/bindings/nsIScriptError.idl new file mode 100644 index 000000000..8436361a8 --- /dev/null +++ b/dom/bindings/nsIScriptError.idl @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * nsIConsoleMessage subclass for representing JavaScript errors and warnings. + */ + + +#include "nsISupports.idl" +#include "nsIArray.idl" +#include "nsIConsoleMessage.idl" + +%{C++ +#include "nsStringGlue.h" // for nsDependentCString +%} + +[scriptable, uuid(e8933fc9-c302-4e12-a55b-4f88611d9c6c)] +interface nsIScriptErrorNote : nsISupports +{ + readonly attribute AString errorMessage; + readonly attribute AString sourceName; + readonly attribute uint32_t lineNumber; + readonly attribute uint32_t columnNumber; + + AUTF8String toString(); +}; + +[scriptable, uuid(63eb4d3e-7d99-4150-b4f3-11314f9d82a9)] +interface nsIScriptError : nsIConsoleMessage +{ + /** pseudo-flag for default case */ + const unsigned long errorFlag = 0x0; + + /** message is warning */ + const unsigned long warningFlag = 0x1; + + /** exception was thrown for this case - exception-aware hosts can ignore */ + const unsigned long exceptionFlag = 0x2; + + // XXX check how strict is implemented these days. + /** error or warning is due to strict option */ + const unsigned long strictFlag = 0x4; + + /** just a log message */ + const unsigned long infoFlag = 0x8; + + /** + * The error message without any context/line number information. + * + * @note nsIConsoleMessage.message will return the error formatted + * with file/line information. + */ + readonly attribute AString errorMessage; + + readonly attribute AString sourceName; + readonly attribute AString sourceLine; + readonly attribute uint32_t lineNumber; + readonly attribute uint32_t columnNumber; + readonly attribute uint32_t flags; + + /** + * Categories I know about - + * XUL javascript + * content javascript (both of these from nsDocShell, currently) + * system javascript (errors in JS components and other system JS) + */ + readonly attribute string category; + + /* Get the window id this was initialized with. Zero will be + returned if init() was used instead of initWithWindowID(). */ + readonly attribute unsigned long long outerWindowID; + + /* Get the inner window id this was initialized with. Zero will be + returned if init() was used instead of initWithWindowID(). */ + readonly attribute unsigned long long innerWindowID; + + readonly attribute boolean isFromPrivateWindow; + + attribute jsval stack; + + /** + * The name of a template string, as found in js.msg, associated with the + * error message. + */ + attribute AString errorMessageName; + + readonly attribute nsIArray notes; + + void init(in AString message, + in AString sourceName, + in AString sourceLine, + in uint32_t lineNumber, + in uint32_t columnNumber, + in uint32_t flags, + in string category); + + /* This should be called instead of nsIScriptError.init to + initialize with a window id. The window id should be for the + inner window associated with this error. */ + void initWithWindowID(in AString message, + in AString sourceName, + in AString sourceLine, + in uint32_t lineNumber, + in uint32_t columnNumber, + in uint32_t flags, + in ACString category, + in unsigned long long innerWindowID); +%{C++ + // This overload allows passing a literal string for category. + template<uint32_t N> + nsresult InitWithWindowID(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char (&c)[N], + uint64_t aInnerWindowID) + { + nsDependentCString category(c, N - 1); + return InitWithWindowID(message, sourceName, sourceLine, lineNumber, + columnNumber, flags, category, aInnerWindowID); + } +%} + +}; + +%{ C++ +#define NS_SCRIPTERROR_CID \ +{ 0x1950539a, 0x90f0, 0x4d22, { 0xb5, 0xaf, 0x71, 0x32, 0x9c, 0x68, 0xfa, 0x35 }} + +#define NS_SCRIPTERROR_CONTRACTID "@mozilla.org/scripterror;1" +%} diff --git a/dom/bindings/nsScriptError.cpp b/dom/bindings/nsScriptError.cpp new file mode 100644 index 000000000..9248d1a31 --- /dev/null +++ b/dom/bindings/nsScriptError.cpp @@ -0,0 +1,438 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* 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/. */ + +/* + * nsIScriptError implementation. + */ + +#include "nsScriptError.h" +#include "jsprf.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "nsGlobalWindow.h" +#include "nsNetUtil.h" +#include "nsPIDOMWindow.h" +#include "nsILoadContext.h" +#include "nsIDocShell.h" +#include "nsIMutableArray.h" +#include "nsIScriptError.h" +#include "nsISensitiveInfoHiddenURI.h" + +static_assert(nsIScriptError::errorFlag == JSREPORT_ERROR && + nsIScriptError::warningFlag == JSREPORT_WARNING && + nsIScriptError::exceptionFlag == JSREPORT_EXCEPTION && + nsIScriptError::strictFlag == JSREPORT_STRICT && + nsIScriptError::infoFlag == JSREPORT_USER_1, + "flags should be consistent"); + +nsScriptErrorBase::nsScriptErrorBase() + : mMessage(), + mMessageName(), + mSourceName(), + mLineNumber(0), + mSourceLine(), + mColumnNumber(0), + mFlags(0), + mCategory(), + mOuterWindowID(0), + mInnerWindowID(0), + mTimeStamp(0), + mInitializedOnMainThread(false), + mIsFromPrivateWindow(false) +{ +} + +nsScriptErrorBase::~nsScriptErrorBase() {} + +void +nsScriptErrorBase::AddNote(nsIScriptErrorNote* note) +{ + mNotes.AppendObject(note); +} + +void +nsScriptErrorBase::InitializeOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mInitializedOnMainThread); + + if (mInnerWindowID) { + nsGlobalWindow* window = + nsGlobalWindow::GetInnerWindowWithId(mInnerWindowID); + if (window) { + nsPIDOMWindowOuter* outer = window->GetOuterWindow(); + if (outer) + mOuterWindowID = outer->WindowID(); + + nsIDocShell* docShell = window->GetDocShell(); + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); + + if (loadContext) { + // Never mark exceptions from chrome windows as having come from + // private windows, since we always want them to be reported. + nsIPrincipal* winPrincipal = window->GetPrincipal(); + mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() && + !nsContentUtils::IsSystemPrincipal(winPrincipal); + } + } + } + + mInitializedOnMainThread = true; +} + +// nsIConsoleMessage methods +NS_IMETHODIMP +nsScriptErrorBase::GetMessageMoz(char16_t** result) { + nsresult rv; + + nsAutoCString message; + rv = ToString(message); + if (NS_FAILED(rv)) + return rv; + + *result = UTF8ToNewUnicode(message); + if (!*result) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + + +NS_IMETHODIMP +nsScriptErrorBase::GetLogLevel(uint32_t* aLogLevel) +{ + if (mFlags & (uint32_t)nsIScriptError::infoFlag) { + *aLogLevel = nsIConsoleMessage::info; + } else if (mFlags & (uint32_t)nsIScriptError::warningFlag) { + *aLogLevel = nsIConsoleMessage::warn; + } else { + *aLogLevel = nsIConsoleMessage::error; + } + return NS_OK; +} + +// nsIScriptError methods +NS_IMETHODIMP +nsScriptErrorBase::GetErrorMessage(nsAString& aResult) { + aResult.Assign(mMessage); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetSourceName(nsAString& aResult) { + aResult.Assign(mSourceName); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetSourceLine(nsAString& aResult) { + aResult.Assign(mSourceLine); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetLineNumber(uint32_t* result) { + *result = mLineNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetColumnNumber(uint32_t* result) { + *result = mColumnNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetFlags(uint32_t* result) { + *result = mFlags; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetCategory(char** result) { + *result = ToNewCString(mCategory); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetStack(JS::MutableHandleValue aStack) { + aStack.setUndefined(); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::SetStack(JS::HandleValue aStack) { + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) { + aErrorMessageName = mMessageName; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) { + mMessageName = aErrorMessageName; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::Init(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char* category) +{ + return InitWithWindowID(message, sourceName, sourceLine, lineNumber, + columnNumber, flags, + category ? nsDependentCString(category) + : EmptyCString(), + 0); +} + +static void +AssignSourceNameHelper(nsString& aSourceNameDest, const nsAString& aSourceNameSrc) +{ + if (aSourceNameSrc.IsEmpty()) + return; + + aSourceNameDest.Assign(aSourceNameSrc); + + nsCOMPtr<nsIURI> uri; + nsAutoCString pass; + if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), aSourceNameSrc)) && + NS_SUCCEEDED(uri->GetPassword(pass)) && + !pass.IsEmpty()) + { + nsCOMPtr<nsISensitiveInfoHiddenURI> safeUri = do_QueryInterface(uri); + + nsAutoCString loc; + if (safeUri && NS_SUCCEEDED(safeUri->GetSensitiveInfoHiddenSpec(loc))) + aSourceNameDest.Assign(NS_ConvertUTF8toUTF16(loc)); + } +} + +NS_IMETHODIMP +nsScriptErrorBase::InitWithWindowID(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const nsACString& category, + uint64_t aInnerWindowID) +{ + mMessage.Assign(message); + AssignSourceNameHelper(mSourceName, sourceName); + mLineNumber = lineNumber; + mSourceLine.Assign(sourceLine); + mColumnNumber = columnNumber; + mFlags = flags; + mCategory = category; + mTimeStamp = JS_Now() / 1000; + mInnerWindowID = aInnerWindowID; + + if (aInnerWindowID && NS_IsMainThread()) { + InitializeOnMainThread(); + } + + return NS_OK; +} + +static nsresult +ToStringHelper(const char* aSeverity, const nsString& aMessage, + const nsString& aSourceName, const nsString* aSourceLine, + uint32_t aLineNumber, uint32_t aColumnNumber, + nsACString& /*UTF8*/ aResult) +{ + static const char format0[] = + "[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]"; + static const char format1[] = + "[%s: \"%s\" {file: \"%s\" line: %d}]"; + static const char format2[] = + "[%s: \"%s\"]"; + + char* temp; + char* tempMessage = nullptr; + char* tempSourceName = nullptr; + char* tempSourceLine = nullptr; + + if (!aMessage.IsEmpty()) + tempMessage = ToNewUTF8String(aMessage); + if (!aSourceName.IsEmpty()) + // Use at most 512 characters from mSourceName. + tempSourceName = ToNewUTF8String(StringHead(aSourceName, 512)); + if (aSourceLine && !aSourceLine->IsEmpty()) + // Use at most 512 characters from mSourceLine. + tempSourceLine = ToNewUTF8String(StringHead(*aSourceLine, 512)); + + if (nullptr != tempSourceName && nullptr != tempSourceLine) { + temp = JS_smprintf(format0, + aSeverity, + tempMessage, + tempSourceName, + aLineNumber, + aColumnNumber, + tempSourceLine); + } else if (!aSourceName.IsEmpty()) { + temp = JS_smprintf(format1, + aSeverity, + tempMessage, + tempSourceName, + aLineNumber); + } else { + temp = JS_smprintf(format2, + aSeverity, + tempMessage); + } + + if (nullptr != tempMessage) + free(tempMessage); + if (nullptr != tempSourceName) + free(tempSourceName); + if (nullptr != tempSourceLine) + free(tempSourceLine); + + if (!temp) + return NS_ERROR_OUT_OF_MEMORY; + + aResult.Assign(temp); + JS_smprintf_free(temp); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult) +{ + static const char error[] = "JavaScript Error"; + static const char warning[] = "JavaScript Warning"; + + const char* severity = !(mFlags & JSREPORT_WARNING) ? error : warning; + + return ToStringHelper(severity, mMessage, mSourceName, &mSourceLine, + mLineNumber, mColumnNumber, aResult); +} + +NS_IMETHODIMP +nsScriptErrorBase::GetOuterWindowID(uint64_t* aOuterWindowID) +{ + NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread, + "This can't be safely determined off the main thread, " + "returning an inaccurate value!"); + + if (!mInitializedOnMainThread && NS_IsMainThread()) { + InitializeOnMainThread(); + } + + *aOuterWindowID = mOuterWindowID; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetInnerWindowID(uint64_t* aInnerWindowID) +{ + *aInnerWindowID = mInnerWindowID; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetTimeStamp(int64_t* aTimeStamp) +{ + *aTimeStamp = mTimeStamp; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) +{ + NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread, + "This can't be safely determined off the main thread, " + "returning an inaccurate value!"); + + if (!mInitializedOnMainThread && NS_IsMainThread()) { + InitializeOnMainThread(); + } + + *aIsFromPrivateWindow = mIsFromPrivateWindow; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetNotes(nsIArray** aNotes) +{ + nsresult rv = NS_OK; + nsCOMPtr<nsIMutableArray> array = + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t len = mNotes.Length(); + for (uint32_t i = 0; i < len; i++) + array->AppendElement(mNotes[i], false); + array.forget(aNotes); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError) + +nsScriptErrorNote::nsScriptErrorNote() + : mMessage(), + mSourceName(), + mLineNumber(0), + mColumnNumber(0) +{ +} + +nsScriptErrorNote::~nsScriptErrorNote() {} + +void +nsScriptErrorNote::Init(const nsAString& message, + const nsAString& sourceName, + uint32_t lineNumber, + uint32_t columnNumber) +{ + mMessage.Assign(message); + AssignSourceNameHelper(mSourceName, sourceName); + mLineNumber = lineNumber; + mColumnNumber = columnNumber; +} + +// nsIScriptErrorNote methods +NS_IMETHODIMP +nsScriptErrorNote::GetErrorMessage(nsAString& aResult) { + aResult.Assign(mMessage); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::GetSourceName(nsAString& aResult) { + aResult.Assign(mSourceName); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::GetLineNumber(uint32_t* result) { + *result = mLineNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::GetColumnNumber(uint32_t* result) { + *result = mColumnNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::ToString(nsACString& /*UTF8*/ aResult) +{ + return ToStringHelper("JavaScript Note", mMessage, mSourceName, nullptr, + mLineNumber, mColumnNumber, aResult); +} + +NS_IMPL_ISUPPORTS(nsScriptErrorNote, nsIScriptErrorNote) diff --git a/dom/bindings/nsScriptError.h b/dom/bindings/nsScriptError.h new file mode 100644 index 000000000..b8049d0a0 --- /dev/null +++ b/dom/bindings/nsScriptError.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_nsScriptError_h +#define mozilla_dom_nsScriptError_h + +#include "mozilla/Atomics.h" + +#include <stdint.h> + +#include "jsapi.h" +#include "js/RootingAPI.h" + +#include "nsIScriptError.h" +#include "nsString.h" + +class nsScriptErrorNote final : public nsIScriptErrorNote { + public: + nsScriptErrorNote(); + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISCRIPTERRORNOTE + + void Init(const nsAString& message, const nsAString& sourceName, + uint32_t lineNumber, uint32_t columnNumber); + + private: + virtual ~nsScriptErrorNote(); + + nsString mMessage; + nsString mSourceName; + nsString mSourceLine; + uint32_t mLineNumber; + uint32_t mColumnNumber; +}; + +// Definition of nsScriptError.. +class nsScriptErrorBase : public nsIScriptError { +public: + nsScriptErrorBase(); + + NS_DECL_NSICONSOLEMESSAGE + NS_DECL_NSISCRIPTERROR + + void AddNote(nsIScriptErrorNote* note); + +protected: + virtual ~nsScriptErrorBase(); + + void + InitializeOnMainThread(); + + nsCOMArray<nsIScriptErrorNote> mNotes; + nsString mMessage; + nsString mMessageName; + nsString mSourceName; + uint32_t mLineNumber; + nsString mSourceLine; + uint32_t mColumnNumber; + uint32_t mFlags; + nsCString mCategory; + // mOuterWindowID is set on the main thread from InitializeOnMainThread(). + uint64_t mOuterWindowID; + uint64_t mInnerWindowID; + int64_t mTimeStamp; + // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main + // thread from InitializeOnMainThread(). + mozilla::Atomic<bool> mInitializedOnMainThread; + bool mIsFromPrivateWindow; +}; + +class nsScriptError final : public nsScriptErrorBase { +public: + nsScriptError() {} + NS_DECL_THREADSAFE_ISUPPORTS + +private: + virtual ~nsScriptError() {} +}; + +class nsScriptErrorWithStack : public nsScriptErrorBase { +public: + explicit nsScriptErrorWithStack(JS::HandleObject); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack) + + NS_IMETHOD Init(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char* category) override; + + NS_IMETHOD GetStack(JS::MutableHandleValue) override; + NS_IMETHOD ToString(nsACString& aResult) override; + +private: + virtual ~nsScriptErrorWithStack(); + // Complete stackframe where the error happened. + // Must be SavedFrame object. + JS::Heap<JSObject*> mStack; +}; + +#endif /* mozilla_dom_nsScriptError_h */ diff --git a/dom/bindings/nsScriptErrorWithStack.cpp b/dom/bindings/nsScriptErrorWithStack.cpp new file mode 100644 index 000000000..74c00999f --- /dev/null +++ b/dom/bindings/nsScriptErrorWithStack.cpp @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* 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/. */ + +/* + * nsScriptErrorWithStack implementation. + * a main-thread-only, cycle-collected subclass of nsScriptErrorBase + * that can store a SavedFrame stack trace object. + */ + +#include "nsScriptError.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsGlobalWindow.h" +#include "nsCycleCollectionParticipant.h" + +using namespace mozilla::dom; + +namespace { + +static nsCString +FormatStackString(JSContext* cx, HandleObject aStack) { + JS::RootedString formattedStack(cx); + + if (!JS::BuildStackString(cx, aStack, &formattedStack)) { + return nsCString(); + } + + nsAutoJSString stackJSString; + if (!stackJSString.init(cx, formattedStack)) { + return nsCString(); + } + + return NS_ConvertUTF16toUTF8(stackJSString.get()); +} + +} + + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptErrorWithStack) + tmp->mStack = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptErrorWithStack) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIConsoleMessage) + NS_INTERFACE_MAP_ENTRY(nsIScriptError) +NS_INTERFACE_MAP_END + +nsScriptErrorWithStack::nsScriptErrorWithStack(JS::HandleObject aStack) + : mStack(aStack) +{ + MOZ_ASSERT(NS_IsMainThread(), "You can't use this class on workers."); + mozilla::HoldJSObjects(this); +} + +nsScriptErrorWithStack::~nsScriptErrorWithStack() { + mozilla::DropJSObjects(this); +} + +NS_IMETHODIMP +nsScriptErrorWithStack::Init(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char* category) +{ + MOZ_CRASH("nsScriptErrorWithStack requires to be initialized with a document, by using InitWithWindowID"); +} + +NS_IMETHODIMP +nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) { + aStack.setObjectOrNull(mStack); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCString message; + nsresult rv = nsScriptErrorBase::ToString(message); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mStack) { + aResult.Assign(message); + return NS_OK; + } + + AutoJSAPI jsapi; + if (!jsapi.Init(mStack)) { + return NS_ERROR_FAILURE; + } + + JSContext* cx = jsapi.cx(); + RootedObject stack(cx, mStack); + nsCString stackString = FormatStackString(cx, stack); + nsCString combined = message + NS_LITERAL_CSTRING("\n") + stackString; + aResult.Assign(combined); + + return NS_OK; +} 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/bindings/test/test_Object.prototype_props.html b/dom/bindings/test/test_Object.prototype_props.html index 03147eb03..3ab27c5e4 100644 --- a/dom/bindings/test/test_Object.prototype_props.html +++ b/dom/bindings/test/test_Object.prototype_props.html @@ -11,9 +11,9 @@ test(function() { // Codegen.py's CGDictionary.getMemberDefinition method. var expected = [ "constructor", "toSource", "toString", "toLocaleString", "valueOf", - "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", - "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", - "__lookupGetter__", "__lookupSetter__", "__proto__" + "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", + "__defineGetter__", "__defineSetter__", "__lookupGetter__", + "__lookupSetter__", "__proto__" ]; assert_array_equals(props.sort(), expected.sort()); }, "Own properties of Object.prototype"); |