Merge m-c to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2013-09-26 13:56:53 +02:00
commit a34e4464aa
97 changed files with 2677 additions and 1218 deletions

View File

@ -317,10 +317,6 @@ def is_module_header(enclosing_inclname, header_inclname):
if m is not None and module.endswith('/' + m.group(1)):
return True
# A weird public header case.
if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h':
return True
return False

View File

@ -216,6 +216,7 @@
#include "mozilla/dom/BrowserElementDictionariesBinding.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/WindowBinding.h"
#include "mozilla/dom/TabChild.h"
#ifdef MOZ_WEBSPEECH
#include "mozilla/dom/SpeechSynthesis.h"
@ -5713,6 +5714,9 @@ nsGlobalWindow::Focus()
return fm->SetFocus(frameElement, flags);
}
}
else if (TabChild *child = TabChild::GetFrom(this)) {
child->SendRequestFocus(canFocus);
}
else if (canFocus) {
// if there is no parent, this must be a toplevel window, so raise the
// window if canFocus is true

View File

@ -36,7 +36,7 @@ def generate_binding_files(config, outputprefix, srcprefix, webidlfile,
# too much. See the comment explaining $(binding_dependency_trackers) in
# Makefile.in.
rule = mk.create_rule([outputprefix])
rule.add_dependencies(os.path.join(srcprefix, x) for x in root.deps())
rule.add_dependencies(os.path.join(srcprefix, x) for x in sorted(root.deps()))
rule.add_dependencies(iter_modules_in_path(topsrcdir))
with open(depsname, 'w') as f:
mk.dump(f)

View File

@ -872,6 +872,22 @@ GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
return ifaceAndProtoJSClass->mNativeHooks;
}
// Try to resolve a property as an unforgeable property from the given
// NativeProperties, if it's there. nativeProperties is allowed to be null (in
// which case we of course won't resolve anything).
static bool
XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc,
const NativeProperties* nativeProperties);
static bool
XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
const NativePropertyHooks* nativePropertyHooks,
DOMObjectType type, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc);
bool
XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
@ -881,7 +897,32 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
const NativePropertyHooks *nativePropertyHooks =
GetNativePropertyHooks(cx, obj, type);
return type != eInstance || !nativePropertyHooks->mResolveOwnProperty ||
if (type != eInstance) {
// For prototype objects and interface objects, just return their
// normal set of properties.
return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
obj, id, desc);
}
// Check for unforgeable properties before doing mResolveOwnProperty weirdness
const NativePropertiesHolder& nativeProperties =
nativePropertyHooks->mNativeProperties;
if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc,
nativeProperties.regular)) {
return false;
}
if (desc.object()) {
return true;
}
if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc,
nativeProperties.chromeOnly)) {
return false;
}
if (desc.object()) {
return true;
}
return !nativePropertyHooks->mResolveOwnProperty ||
nativePropertyHooks->mResolveOwnProperty(cx, wrapper, obj, id, desc, flags);
}
@ -936,6 +977,20 @@ XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
return true;
}
/* static */ bool
XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc,
const NativeProperties* nativeProperties)
{
return !nativeProperties || !nativeProperties->unforgeableAttributes ||
XrayResolveAttribute(cx, wrapper, obj, id,
nativeProperties->unforgeableAttributes,
nativeProperties->unforgeableAttributeIds,
nativeProperties->unforgeableAttributeSpecs,
desc);
}
static bool
XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
@ -1008,18 +1063,6 @@ XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
return true;
}
}
if (nativeProperties->unforgeableAttributes) {
if (!XrayResolveAttribute(cx, wrapper, obj, id,
nativeProperties->unforgeableAttributes,
nativeProperties->unforgeableAttributeIds,
nativeProperties->unforgeableAttributeSpecs,
desc)) {
return false;
}
if (desc.object()) {
return true;
}
}
}
if (nativeProperties->constants) {
@ -1068,7 +1111,7 @@ ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper,
return JS_WrapPropertyDescriptor(cx, desc);
}
bool
/* static */ bool
XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
const NativePropertyHooks* nativePropertyHooks,
DOMObjectType type, JS::Handle<JSObject*> obj,
@ -1904,9 +1947,10 @@ InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
}
bool
ReportLenientThisUnwrappingFailure(JSContext* cx, JS::Handle<JSObject*> obj)
ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj)
{
GlobalObject global(cx, obj);
JS::Rooted<JSObject*> rootedObj(cx, obj);
GlobalObject global(cx, rootedObj);
if (global.Failed()) {
return false;
}

View File

@ -17,6 +17,7 @@
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/NonRefcountedDOMObject.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/workers/Workers.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Likely.h"
@ -1756,42 +1757,6 @@ public:
SequenceType mSequenceType;
};
template<typename T>
class MOZ_STACK_CLASS RootedDictionary : public T,
private JS::CustomAutoRooter
{
public:
RootedDictionary(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
T(),
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
virtual void trace(JSTracer *trc) MOZ_OVERRIDE
{
this->TraceDictionary(trc);
}
};
template<typename T>
class MOZ_STACK_CLASS NullableRootedDictionary : public Nullable<T>,
private JS::CustomAutoRooter
{
public:
NullableRootedDictionary(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
Nullable<T>(),
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
virtual void trace(JSTracer *trc) MOZ_OVERRIDE
{
if (!this->IsNull()) {
this->Value().TraceDictionary(trc);
}
}
};
template<typename T>
class MOZ_STACK_CLASS RootedUnion : public T,
private JS::CustomAutoRooter
@ -2062,7 +2027,7 @@ InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
// Helper for lenient getters/setters to report to console. If this
// returns false, we couldn't even get a global.
bool
ReportLenientThisUnwrappingFailure(JSContext* cx, JS::Handle<JSObject*> obj);
ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
inline JSObject*
GetUnforgeableHolder(JSObject* aGlobal, prototypes::ID aId)

View File

@ -826,8 +826,13 @@ def UnionTypes(descriptors, dictionaries, callbacks, config):
declarations.add((typeDesc.nativeType, False))
implheaders.add(typeDesc.headerFile)
elif f.isDictionary():
declarations.add((f.inner.identifier.name, True))
implheaders.add(CGHeaders.getDeclarationFilename(f.inner))
# For a dictionary, we need to see its declaration in
# UnionTypes.h so we have its sizeof and know how big to
# make our union.
headers.add(CGHeaders.getDeclarationFilename(f.inner))
# And if it needs rooting, we need RootedDictionary too
if typeNeedsRooting(f):
headers.add("mozilla/dom/RootedDictionary.h")
elif t.isPrimitive():
implheaders.add("mozilla/dom/PrimitiveConversions.h")
@ -2554,7 +2559,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
lenientFloatCode=None,
allowTreatNonCallableAsNull=False,
isCallbackReturnValue=False,
isInOwningUnion=False,
sourceDescription="value"):
"""
Get a template for converting a JS value to a native object based on the
@ -2575,9 +2579,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if isMember is not False, we're being converted from a property of some JS
object, not from an actual method argument, so we can't rely on our jsval
being rooted or outliving us in any way. Callers can pass "Dictionary",
"Variadic", or "Sequence" to indicate that the conversion is for something
that is a dictionary member, a variadic argument, or a sequence
respectively.
"Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is
for something that is a dictionary member, a variadic argument, a sequence,
or an owning union respectively.
If isOptional is true, then we are doing conversion of an optional
argument with no default value.
@ -2720,26 +2724,28 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
return templateBody
# A helper function for converting things that look like a JSObject*.
def handleJSObjectType(type, isMember, isInOwningUnion, failureCode):
if not isMember and not isInOwningUnion:
def handleJSObjectType(type, isMember, failureCode):
if not isMember:
if isOptional:
# We have a specialization of Optional that will use a
# Rooted for the storage here.
declType = CGGeneric("JS::Handle<JSObject*>")
else:
declType = CGGeneric("JS::Rooted<JSObject*>")
declArgs="cx"
else:
assert (isMember == "Sequence" or isMember == "Variadic" or
isMember == "Dictionary" or isInOwningUnion)
isMember == "Dictionary" or isMember == "OwningUnion")
# We'll get traced by the sequence or dictionary or union tracer
declType = CGGeneric("JSObject*")
declArgs = None
templateBody = "${declName} = &${val}.toObject();"
setToNullCode = "${declName} = nullptr;"
template = wrapObjectTemplate(templateBody, type, setToNullCode,
failureCode)
return JSToNativeConversionInfo(template, declType=declType,
dealWithOptional=isOptional,
declArgs="cx")
declArgs=declArgs)
assert not (isEnforceRange and isClamp) # These are mutually exclusive
@ -2925,31 +2931,31 @@ for (uint32_t i = 0; i < length; ++i) {
dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
if len(dictionaryMemberTypes) > 0:
raise TypeError("No support for unwrapping dictionaries as member "
"of a union")
assert len(dictionaryMemberTypes) == 1
name = dictionaryMemberTypes[0].inner.identifier.name
setDictionary = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
else:
dictionaryObject = None
setDictionary = None
objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
if len(objectMemberTypes) > 0:
assert len(objectMemberTypes) == 1
object = CGGeneric("%s.SetToObject(cx, argObj);\n"
"done = true;" % unionArgumentObj)
else:
object = None
hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or dictionaryObject or object
hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or object
if hasObjectTypes:
# "object" is not distinguishable from other types
assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject or dictionaryObject)
if arrayObject or dateObject or callbackObject or dictionaryObject:
assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject)
if arrayObject or dateObject or callbackObject:
# An object can be both an array object and a callback or
# dictionary, but we shouldn't have both in the union's members
# because they are not distinguishable.
assert not (arrayObject and callbackObject)
assert not (arrayObject and dictionaryObject)
assert not (dictionaryObject and callbackObject)
templateBody = CGList([arrayObject, dateObject, callbackObject,
dictionaryObject], " else ")
templateBody = CGList([arrayObject, dateObject, callbackObject],
" else ")
else:
templateBody = None
if interfaceObject:
@ -2960,13 +2966,17 @@ for (uint32_t i = 0; i < length; ++i) {
else:
templateBody = CGList([templateBody, object], "\n")
if any([arrayObject, dateObject, callbackObject, dictionaryObject,
object]):
if any([arrayObject, dateObject, callbackObject, object]):
templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());"))
templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
else:
templateBody = CGGeneric()
if setDictionary:
assert not object
templateBody = CGList([templateBody,
CGIfWrapper(setDictionary, "!done")], "\n")
stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
numericTypes = [t for t in memberTypes if t.isNumeric()]
booleanTypes = [t for t in memberTypes if t.isBoolean()]
@ -3005,7 +3015,7 @@ for (uint32_t i = 0; i < length; ++i) {
other.append(booleanConversion[0])
other = CGWrapper(CGIndenter(other), pre="do {\n", post="\n} while (0);")
if hasObjectTypes:
if hasObjectTypes or setDictionary:
other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
if object:
join = " else "
@ -3119,8 +3129,7 @@ for (uint32_t i = 0; i < length; ++i) {
if descriptor.nativeType == 'JSObject':
# XXXbz Workers code does this sometimes
assert descriptor.workers
return handleJSObjectType(type, isMember, isInOwningUnion,
failureCode)
return handleJSObjectType(type, isMember, failureCode)
if descriptor.interface.isCallback():
name = descriptor.interface.identifier.name
@ -3156,7 +3165,6 @@ for (uint32_t i = 0; i < length; ++i) {
forceOwningType = ((descriptor.interface.isCallback() and
not descriptor.workers) or
isMember or
isInOwningUnion or
isCallbackReturnValue)
typeName = descriptor.nativeType
@ -3266,7 +3274,7 @@ for (uint32_t i = 0; i < length; ++i) {
CGIndenter(onFailureBadType(failureCode, type.name)).define()))
template = wrapObjectTemplate(template, type, "${declName}.SetNull()",
failureCode)
if not isMember and not isInOwningUnion:
if not isMember:
# This is a bit annoying. In a union we don't want to have a
# holder, since unions don't support that. But if we're optional we
# want to have a holder, so that the callee doesn't see
@ -3358,22 +3366,14 @@ for (uint32_t i = 0; i < length; ++i) {
if isOptional:
declType = "Optional<nsAString>"
elif isInOwningUnion:
declType = "nsString"
else:
declType = "NonNull<nsAString>"
# No need to deal with optional here; we handled it already
decl = ""
if isInOwningUnion:
decl += "FakeDependentString str;\n"
return JSToNativeConversionInfo(
"%s"
"%s\n"
"${declName} = %s" %
(decl,
getConversionCode("str" if isInOwningUnion else "${holderName}"),
("str;" if isInOwningUnion else "&${holderName};")),
("%s\n"
"${declName} = &${holderName};" %
getConversionCode("${holderName}")),
declType=CGGeneric(declType),
holderType=CGGeneric("FakeDependentString"))
@ -3526,13 +3526,9 @@ for (uint32_t i = 0; i < length; ++i) {
if type.isObject():
assert not isEnforceRange and not isClamp
return handleJSObjectType(type, isMember, isInOwningUnion, failureCode)
return handleJSObjectType(type, isMember, failureCode)
if type.isDictionary():
if failureCode is not None and not isDefinitelyObject:
raise TypeError("Can't handle dictionaries when failureCode is "
"not None and we don't know we're an object")
# There are no nullable dictionaries
assert not type.nullable()
# All optional dictionaries always have default values, so we
@ -3557,12 +3553,15 @@ for (uint32_t i = 0; i < length; ++i) {
val = "${val}"
if failureCode is not None:
assert isDefinitelyObject
if isDefinitelyObject:
dictionaryTest = "IsObjectValueConvertibleToDictionary"
else:
dictionaryTest = "IsConvertibleToDictionary"
# Check that the value we have can in fact be converted to
# a dictionary, and return failureCode if not.
template = CGIfWrapper(
CGGeneric(failureCode),
"!IsObjectValueConvertibleToDictionary(cx, ${val})").define() + "\n\n"
"!%s(cx, ${val})" % dictionaryTest).define() + "\n\n"
else:
template = ""
@ -5321,9 +5320,18 @@ class CGAbstractBindingMethod(CGAbstractStaticMethod):
|this| object. Subclasses are expected to override the generate_code
function to do the rest of the work. This function should return a
CGThing which is already properly indented.
getThisObj should be code for getting a JSObject* for the binding
object. If this is None, we will auto-generate code based on
descriptor to do the right thing. "" can be passed in if the
binding object is already stored in 'obj'.
callArgs should be code for getting a JS::CallArgs into a variable
called 'args'. This can be "" if there is already such a variable
around.
"""
def __init__(self, descriptor, name, args, unwrapFailureCode=None,
getThisObj="args.computeThis(cx).toObjectOrNull()",
getThisObj=None,
callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);"):
CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
@ -5331,7 +5339,26 @@ class CGAbstractBindingMethod(CGAbstractStaticMethod):
self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");' % descriptor.interface.identifier.name
else:
self.unwrapFailureCode = unwrapFailureCode
self.getThisObj = getThisObj
if getThisObj == "":
self.getThisObj = None
else:
if getThisObj is None:
if descriptor.interface.isOnGlobalProtoChain():
ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()"
getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())"
else:
ensureCondition = "!args.thisv().isObject()"
getThisObj = "&args.thisv().toObject()"
ensureThisObj = CGIfWrapper(CGGeneric(self.unwrapFailureCode),
ensureCondition)
else:
ensureThisObj = None
self.getThisObj = CGList(
[ensureThisObj,
CGGeneric("JS::RootedObject obj(cx, %s);\n" %
getThisObj)],
"\n")
self.callArgs = callArgs
def definition_body(self):
@ -5341,10 +5368,7 @@ class CGAbstractBindingMethod(CGAbstractStaticMethod):
# consumption by CastableObjectUnwrapper.
getThis = CGList([
CGGeneric(self.callArgs) if self.callArgs != "" else None,
CGGeneric("JS::RootedObject obj(cx, %s);\n"
"if (!obj) {\n"
" return false;\n"
"}" % self.getThisObj) if self.getThisObj else None,
self.getThisObj,
CGGeneric("%s* self;" % self.descriptor.nativeType)
], "\n")
unwrapThis = CGGeneric(
@ -5585,7 +5609,7 @@ class CGGenericGetter(CGAbstractBindingMethod):
name = "genericLenientGetter"
unwrapFailureCode = (
"MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
"if (!ReportLenientThisUnwrappingFailure(cx, obj)) {\n"
"if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {\n"
" return false;\n"
"}\n"
"args.rval().set(JS::UndefinedValue());\n"
@ -5665,7 +5689,7 @@ class CGGenericSetter(CGAbstractBindingMethod):
name = "genericLenientSetter"
unwrapFailureCode = (
"MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
"if (!ReportLenientThisUnwrappingFailure(cx, obj)) {\n"
"if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {\n"
" return false;\n"
"}\n"
"args.rval().set(JS::UndefinedValue());\n"
@ -6067,6 +6091,9 @@ def getUnionAccessorSignatureType(type, descriptorProvider):
if type.isObject():
return CGGeneric("JSObject*")
if type.isDictionary():
return CGGeneric("const %s&" % type.inner.identifier.name)
if not type.isPrimitive():
raise TypeError("Need native type for argument type '%s'" % str(type))
@ -6081,14 +6108,11 @@ def getUnionTypeTemplateVars(unionType, type, descriptorProvider,
# for getJSToNativeConversionInfo.
# Also, for dictionaries we would need to handle conversion of
# null/undefined to the dictionary correctly.
if type.isDictionary() or type.isSequence():
raise TypeError("Can't handle dictionaries or sequences in unions")
if type.isSequence():
raise TypeError("Can't handle sequences in unions")
name = getUnionMemberName(type)
ctorNeedsCx = type.isSpiderMonkeyInterface() and not ownsMembers
ctorArgs = "cx" if ctorNeedsCx else ""
tryNextCode = ("tryNext = true;\n"
"return true;")
if type.isGeckoInterface():
@ -6098,9 +6122,13 @@ def getUnionTypeTemplateVars(unionType, type, descriptorProvider,
"}") % (prefix, prefix, prefix, name) + tryNextCode
conversionInfo = getJSToNativeConversionInfo(
type, descriptorProvider, failureCode=tryNextCode,
isDefinitelyObject=True, isInOwningUnion=ownsMembers,
isDefinitelyObject=not type.isDictionary(),
isMember=("OwningUnion" if ownsMembers else None),
sourceDescription="member of %s" % unionType)
ctorNeedsCx = conversionInfo.declArgs == "cx"
ctorArgs = "cx" if ctorNeedsCx else ""
# This is ugly, but UnionMember needs to call a constructor with no
# arguments so the type can't be const.
structType = conversionInfo.declType.define()
@ -6267,6 +6295,11 @@ class CGUnionStruct(CGThing):
CGGeneric('JS_CallObjectTracer(trc, %s, "%s");' %
("&mValue.m" + vars["name"] + ".Value()",
"mValue.m" + vars["name"]))))
elif t.isDictionary():
traceCases.append(
CGCase("e" + vars["name"],
CGGeneric("mValue.m%s.Value().TraceDictionary(trc);" %
vars["name"])))
else:
assert t.isSpiderMonkeyInterface()
traceCases.append(
@ -7285,14 +7318,16 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
return false;
}
MOZ_ASSERT_IF(desc.object(), desc.object() == ${holder});"""
# We don't want to look at the unforgeable holder at all
# in the xray case; that part got handled already.
getUnforgeable = CallOnUnforgeableHolder(self.descriptor,
getUnforgeable, "isXray")
getUnforgeable)
getUnforgeable += """if (desc.object()) {
desc.object().set(proxy);
return !isXray || JS_WrapPropertyDescriptor(cx, desc);
}
"""
return true;
}"""
getUnforgeable = CGIfWrapper(CGGeneric(getUnforgeable),
"!isXray").define() + "\n\n"
else:
getUnforgeable = ""
@ -8123,13 +8158,26 @@ class CGDictionary(CGThing):
isClamp=member.clamp,
isMember="Dictionary",
isOptional=(not member.defaultValue),
# Set this to true so that we get an owning union.
isInOwningUnion=True,
defaultValue=member.defaultValue,
sourceDescription=("'%s' member of %s" %
(member.identifier.name,
dictionary.identifier.name))))
for member in dictionary.members ]
# If we have a union member containing something in the same
# file as us, bail: the C++ includes won't work out.
for member in dictionary.members:
type = member.type.unroll()
if type.isUnion():
for t in type.flatMemberTypes:
if (t.isDictionary() and
CGHeaders.getDeclarationFilename(t.inner) ==
CGHeaders.getDeclarationFilename(dictionary)):
raise TypeError(
"Dictionary contains a union that contains a "
"dictionary in the same WebIDL file. This won't "
"compile. Move the inner dictionary to a "
"different file.\n%s\n%s" %
(t.location, t.inner.location))
self.structs = self.getStructs()
def declare(self):

View File

@ -23,7 +23,7 @@ exported_generated_events_headers := $(subst .webidl,.h,$(generated_events_webid
linked_binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files))
linked_generated_events_cpp_files := $(subst .webidl,.cpp,$(generated_events_webidl_files))
all_webidl_files += $(test_webidl_files)
all_webidl_files += $(test_webidl_files) $(preprocessed_test_webidl_files)
generated_header_files := $(subst .webidl,Binding.h,$(all_webidl_files)) $(exported_generated_events_headers)
generated_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files)) $(linked_generated_events_cpp_files)
@ -168,6 +168,12 @@ $(preprocessed_webidl_files): %: $(webidl_base)/% $(GLOBAL_DEPS)
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \
$(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(webidl_base)/$* -o $@
# See the comment about PP_TARGETS for $(preprocessed_webidl_files)
$(preprocessed_test_webidl_files): %: $(srcdir)/test/% $(GLOBAL_DEPS)
$(RM) $@
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \
$(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(srcdir)/test/$* -o $@
# Make is dumb and can get confused between "foo" and "$(CURDIR)/foo". Make
# sure that the latter depends on the former, since the latter gets used in .pp
# files.

View File

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* vim: set ts=2 sw=2 et tw=79: */
/* 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_RootedDictionary_h__
#define mozilla_dom_RootedDictionary_h__
#include "mozilla/GuardObjects.h"
#include "mozilla/dom/Nullable.h"
#include "jsapi.h"
namespace mozilla {
namespace dom {
template<typename T>
class MOZ_STACK_CLASS RootedDictionary : public T,
private JS::CustomAutoRooter
{
public:
RootedDictionary(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
T(),
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
virtual void trace(JSTracer *trc) MOZ_OVERRIDE
{
this->TraceDictionary(trc);
}
};
template<typename T>
class MOZ_STACK_CLASS NullableRootedDictionary : public Nullable<T>,
private JS::CustomAutoRooter
{
public:
NullableRootedDictionary(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
Nullable<T>(),
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
virtual void trace(JSTracer *trc) MOZ_OVERRIDE
{
if (!this->IsNull()) {
this->Value().TraceDictionary(trc);
}
}
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_RootedDictionary_h__ */

View File

@ -27,6 +27,7 @@ EXPORTS.mozilla.dom += [
'Nullable.h',
'OwningNonNull.h',
'PrimitiveConversions.h',
'RootedDictionary.h',
'TypedArray.h',
'UnionMember.h',
]

View File

@ -523,6 +523,7 @@ class IDLInterface(IDLObjectWithScope):
# have self as a consequential interface
self.interfacesImplementingSelf = set()
self._hasChildInterfaces = False
self._isOnGlobalProtoChain = False
IDLObjectWithScope.__init__(self, location, parentScope, name)
@ -586,6 +587,15 @@ class IDLInterface(IDLObjectWithScope):
self.parent._hasChildInterfaces = True
# Interfaces with [Global] must not have anything inherit from them
if self.parent.getExtendedAttribute("Global"):
# Note: This is not a self.parent.isOnGlobalProtoChain() check
# because ancestors of a [Global] interface can have other
# descendants.
raise WebIDLError("[Global] interface has another interface "
"inheriting from it",
[self.location, self.parent.location])
# Callbacks must not inherit from non-callbacks or inherit from
# anything that has consequential interfaces.
# XXXbz Can non-callbacks inherit from callbacks? Spec issue pending.
@ -741,6 +751,31 @@ class IDLInterface(IDLObjectWithScope):
specialMembersSeen[memberType] = member
if self._isOnGlobalProtoChain:
# Make sure we have no named setters, creators, or deleters
for memberType in ["setter", "creator", "deleter"]:
memberId = "named " + memberType + "s"
if memberId in specialMembersSeen:
raise WebIDLError("Interface with [Global] has a named %s" %
memberType,
[self.location,
specialMembersSeen[memberId].location])
# Make sure we're not [OverrideBuiltins]
if self.getExtendedAttribute("OverrideBuiltins"):
raise WebIDLError("Interface with [Global] also has "
"[OverrideBuiltins]",
[self.location])
# Mark all of our ancestors as being on the global's proto chain too
parent = self.parent
while parent:
# Must not inherit from an interface with [OverrideBuiltins]
if parent.getExtendedAttribute("OverrideBuiltins"):
raise WebIDLError("Interface with [Global] inherits from "
"interface with [OverrideBuiltins]",
[self.location, parent.location])
parent._isOnGlobalProtoChain = True
parent = parent.parent
def validate(self):
for member in self.members:
member.validate()
@ -924,6 +959,11 @@ class IDLInterface(IDLObjectWithScope):
raise WebIDLError("[ArrayClass] must not be specified on "
"an interface with inherited interfaces",
[attr.location, self.location])
elif identifier == "Global":
if not attr.noArguments():
raise WebIDLError("[Global] must take no arguments",
[attr.location])
self._isOnGlobalProtoChain = True
elif (identifier == "PrefControlled" or
identifier == "NeedNewResolve" or
identifier == "OverrideBuiltins" or
@ -1042,6 +1082,9 @@ class IDLInterface(IDLObjectWithScope):
def hasChildInterfaces(self):
return self._hasChildInterfaces
def isOnGlobalProtoChain(self):
return self._isOnGlobalProtoChain
def _getDependentObjects(self):
deps = set(self.members)
deps.union(self.implementedInterfaces)
@ -2458,6 +2501,7 @@ class IDLNullValue(IDLObject):
def coerceToType(self, type, location):
if (not isinstance(type, IDLNullableType) and
not (type.isUnion() and type.hasNullableType) and
not (type.isUnion() and type.hasDictionaryType) and
not type.isDictionary() and
not type.isAny()):
raise WebIDLError("Cannot coerce null value to type %s." % type,
@ -2797,7 +2841,9 @@ class IDLArgument(IDLObjectWithIdentifier):
assert not isinstance(type.name, IDLUnresolvedIdentifier)
self.type = type
if self.type.isDictionary() and self.optional and not self.defaultValue:
if ((self.type.isDictionary() or
self.type.isUnion() and self.type.unroll().hasDictionaryType) and
self.optional and not self.defaultValue):
# Default optional dictionaries to null, for simplicity,
# so the codegen doesn't have to special-case this.
self.defaultValue = IDLNullValue(self.location)

View File

@ -0,0 +1,122 @@
def WebIDLTest(parser, harness):
parser.parse("""
[Global]
interface Foo : Bar {
getter any(DOMString name);
};
interface Bar {};
""")
results = parser.finish()
harness.ok(results[0].isOnGlobalProtoChain(),
"[Global] interface should be on global's proto chain")
harness.ok(results[1].isOnGlobalProtoChain(),
"[Global] interface should be on global's proto chain")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Global]
interface Foo {
getter any(DOMString name);
setter void(DOMString name, any arg);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [Global] used on an interface with a "
"named setter")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Global]
interface Foo {
getter any(DOMString name);
creator void(DOMString name, any arg);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [Global] used on an interface with a "
"named creator")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Global]
interface Foo {
getter any(DOMString name);
deleter void(DOMString name);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [Global] used on an interface with a "
"named deleter")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Global, OverrideBuiltins]
interface Foo {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [Global] used on an interface with a "
"[OverrideBuiltins]")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Global]
interface Foo : Bar {
};
[OverrideBuiltins]
interface Bar {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [Global] used on an interface with an "
"[OverrideBuiltins] ancestor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Global]
interface Foo {
};
interface Bar : Foo {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [Global] used on an interface with a "
"descendant")

View File

@ -13,11 +13,15 @@ endif
# Need this to find all our DOM source files.
include $(topsrcdir)/dom/dom-config.mk
# And need this for $(test_webidl_files)
# And need this for $(test_webidl_files) and $(preprocessed_test_webidl_files)
include ../webidlsrcs.mk
# But the webidl actually lives in our parent dir
test_webidl_files := $(addprefix ../,$(test_webidl_files))
# Store the actual locations of our source preprocessed files, so we
# can depend on them sanely.
source_preprocessed_test_webidl_files := $(addprefix $(srcdir)/,$(preprocessed_test_webidl_files))
preprocessed_test_webidl_files := $(addprefix ../,$(preprocessed_test_webidl_files))
LOCAL_INCLUDES += \
-I$(topsrcdir)/js/xpconnect/src \
@ -68,11 +72,13 @@ MOCHITEST_FILES := \
test_exception_messages.html \
test_bug707564.html \
test_defineProperty.html \
file_document_location_set_via_xray.html \
$(NULL)
MOCHITEST_CHROME_FILES = \
test_bug775543.html \
test_bug707564-chrome.html \
test_document_location_set_via_xray.html \
$(NULL)
ifdef GNU_CC
@ -87,6 +93,7 @@ $(CPPSRCS): .BindingGen
.BindingGen: $(bindinggen_dependencies) \
$(test_webidl_files) \
$(source_preprocessed_test_webidl_files) \
$(NULL)
# The export phase in dom/bindings is what actually looks at
# dependencies and regenerates things as needed, so just go ahead and

View File

@ -502,6 +502,18 @@ public:
returnValue.SetAsLong() = i;
}
}
#ifdef DEBUG
void PassUnion2(const LongOrBoolean& arg);
void PassUnion3(JSContext*, const ObjectOrLongOrBoolean& arg);
void PassUnion4(const NodeOrLongOrBoolean& arg);
void PassUnion5(JSContext*, const ObjectOrBoolean& arg);
void PassUnion6(JSContext*, const ObjectOrString& arg);
void PassUnion7(JSContext*, const ObjectOrStringOrLong& arg);
void PassUnion8(JSContext*, const ObjectOrStringOrBoolean& arg);
void PassUnion9(JSContext*, const ObjectOrStringOrLongOrBoolean& arg);
void PassUnion10(const EventInitOrLong& arg);
void PassUnion11(JSContext*, const CustomEventInitOrLong& arg);
#endif
void PassNullableUnion(JSContext*, const Nullable<ObjectOrLong>&);
void PassOptionalUnion(JSContext*, const Optional<ObjectOrLong>&);
void PassOptionalNullableUnion(JSContext*, const Optional<Nullable<ObjectOrLong> >&);

View File

@ -440,15 +440,20 @@ interface TestInterface {
// Union types
void passUnion((object or long) arg);
// Commented out tests 2-9 to avoid creating all those unused union types
/* void passUnion2((long or boolean) arg);
// Some union tests are debug-only to avoid creating all those
// unused union types in opt builds.
#ifdef DEBUG
void passUnion2((long or boolean) arg);
void passUnion3((object or long or boolean) arg);
void passUnion4((Node or long or boolean) arg);
void passUnion5((object or boolean) arg);
void passUnion6((object or DOMString) arg);
void passUnion7((object or DOMString or long) arg);
void passUnion8((object or DOMString or boolean) arg);
void passUnion9((object or DOMString or long or boolean) arg); */
void passUnion9((object or DOMString or long or boolean) arg);
void passUnion10(optional (EventInit or long) arg);
void passUnion11(optional (CustomEventInit or long) arg);
#endif
void passUnionWithNullable((object? or long) arg);
void passNullableUnion((object or long)? arg);
void passOptionalUnion(optional (object or long) arg);
@ -744,6 +749,15 @@ dictionary Dict : ParentDict {
(float or DOMString) floatOrString = "str";
(object or long) objectOrLong;
#ifdef DEBUG
(EventInit or long) eventInitOrLong;
// CustomEventInit is useful to test because it needs rooting.
(CustomEventInit or long) eventInitOrLong2;
(EventInit or long) eventInitOrLongWithDefaultValue = null;
(CustomEventInit or long) eventInitOrLongWithDefaultValue2 = null;
(EventInit or long) eventInitOrLongWithDefaultValue3 = 5;
(CustomEventInit or long) eventInitOrLongWithDefaultValue4 = 5;
#endif
ArrayBuffer arrayBuffer;
ArrayBuffer? nullableArrayBuffer;

View File

@ -336,15 +336,20 @@ interface TestExampleInterface {
// Union types
void passUnion((object or long) arg);
// Commented out tests 2-9 to avoid creating all those unused union types
/* void passUnion2((long or boolean) arg);
// Some union tests are debug-only to avoid creating all those
// unused union types in opt builds.
#ifdef DEBUG
void passUnion2((long or boolean) arg);
void passUnion3((object or long or boolean) arg);
void passUnion4((Node or long or boolean) arg);
void passUnion5((object or boolean) arg);
void passUnion6((object or DOMString) arg);
void passUnion7((object or DOMString or long) arg);
void passUnion8((object or DOMString or boolean) arg);
void passUnion9((object or DOMString or long or boolean) arg); */
void passUnion9((object or DOMString or long or boolean) arg);
void passUnion10(optional (EventInit or long) arg);
void passUnion11(optional (CustomEventInit or long) arg);
#endif
void passUnionWithNullable((object? or long) arg);
void passNullableUnion((object or long)? arg);
void passOptionalUnion(optional (object or long) arg);

View File

@ -358,15 +358,20 @@ interface TestJSImplInterface {
// Union types
void passUnion((object or long) arg);
// Commented out tests 2-9 to avoid creating all those unused union types
/* void passUnion2((long or boolean) arg);
// Some union tests are debug-only to avoid creating all those
// unused union types in opt builds.
#ifdef DEBUG
void passUnion2((long or boolean) arg);
void passUnion3((object or long or boolean) arg);
void passUnion4((Node or long or boolean) arg);
void passUnion5((object or boolean) arg);
void passUnion6((object or DOMString) arg);
void passUnion7((object or DOMString or long) arg);
void passUnion8((object or DOMString or boolean) arg);
void passUnion9((object or DOMString or long or boolean) arg); */
void passUnion9((object or DOMString or long or boolean) arg);
void passUnion10(optional (EventInit or long) arg);
void passUnion11(optional (CustomEventInit or long) arg);
#endif
void passUnionWithNullable((object? or long) arg);
// FIXME: Bug 863948 Nullable unions not supported yet
// void passNullableUnion((object or long)? arg);

View File

@ -0,0 +1,5 @@
<body>
<script>
document.x = 5;
</script>
</body>

View File

@ -8,6 +8,7 @@ MODULE = 'dom'
CPP_SOURCES += [
'$(subst .webidl,Binding.cpp,$(test_webidl_files))',
'$(subst .webidl,Binding.cpp,$(preprocessed_test_webidl_files))',
]
LIBXUL_LIBRARY = True

View File

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=905493
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 905493</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=905493">Mozilla Bug 905493</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe id="t" src="http://example.org/tests/dom/bindings/test/file_document_location_set_via_xray.html"></iframe>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 905493 **/
function test()
{
var doc = document.getElementById("t").contentWindow.document;
ok(!("x" in doc), "Should have an Xray here");
is(doc.x, undefined, "Really should have an Xray here");
is(doc.wrappedJSObject.x, 5, "And wrapping the right thing");
document.getElementById("t").onload = function() {
ok(true, "Load happened");
SimpleTest.finish();
};
try {
// Test the forwarding location setter
doc.location = "chrome://mochikit/content/tests/SimpleTest/test.css";
} catch (e) {
// Load failed
ok(false, "Load failed");
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(test);
</script>
</pre>
</body>
</html>

View File

@ -163,6 +163,12 @@ parent:
*/
sync EndIMEComposition(bool cancel) returns (nsString composition);
/**
* Request that the parent process move focus to the browser's frame. If
* canRaise is true, the window can be raised if it is inactive.
*/
RequestFocus(bool canRaise);
sync GetInputContext() returns (int32_t IMEEnabled, int32_t IMEOpen,
intptr_t NativeIMEContext);

View File

@ -895,6 +895,28 @@ TabParent::RecvNotifyIMETextHint(const nsString& aText)
return true;
}
bool
TabParent::RecvRequestFocus(const bool& aCanRaise)
{
nsCOMPtr<nsIFocusManager> fm = nsFocusManager::GetFocusManager();
if (!fm) {
return true;
}
nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
if (!content || !content->OwnerDoc()) {
return true;
}
uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
if (aCanRaise)
flags |= nsIFocusManager::FLAG_RAISE;
nsCOMPtr<nsIDOMElement> node = do_QueryInterface(mFrameElement);
fm->SetFocus(node, flags);
return true;
}
/**
* Try to answer query event using cached text.
*

View File

@ -145,6 +145,7 @@ public:
const nsString& aActionHint,
const int32_t& aCause,
const int32_t& aFocusChange);
virtual bool RecvRequestFocus(const bool& aCanRaise);
virtual bool RecvSetCursor(const uint32_t& aValue);
virtual bool RecvSetBackgroundColor(const nscolor& aValue);
virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus);

View File

@ -165,6 +165,7 @@ function RTCPeerConnection() {
this._pendingType = null;
this._localType = null;
this._remoteType = null;
this._trickleIce = false;
/**
* Everytime we get a request from content, we put it in the queue. If
@ -193,6 +194,7 @@ RTCPeerConnection.prototype = {
init: function(win) { this._win = win; },
__init: function(rtcConfig) {
this._trickleIce = Services.prefs.getBoolPref("media.peerconnection.trickle_ice");
if (!rtcConfig.iceServers ||
!Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
rtcConfig = {iceServers:
@ -224,11 +226,11 @@ RTCPeerConnection.prototype = {
// Add a reference to the PeerConnection to global list (before init).
_globalPCList.addPC(this);
// Nothing starts until ICE gathering completes.
this._queueOrRun({
func: this._getPC().initialize,
args: [this._observer, this._win, rtcConfig, Services.tm.currentThread],
wait: true
// If not trickling, suppress start.
wait: !this._trickleIce
});
},
@ -894,13 +896,11 @@ PeerConnectionObserver.prototype = {
this._dompc._pendingType = null;
this.callCB(this._dompc._onSetLocalDescriptionSuccess);
// Until we support generating trickle ICE candidates,
// we go ahead and trigger a call of onicecandidate here.
// This is to provide some level of compatibility with
// scripts that expect this behavior (which is how Chrome
// signals that no further trickle candidates will be sent).
// TODO: This needs to be removed when Bug 842459 lands.
this.foundIceCandidate(null);
if (this._iceGatheringState == "complete") {
// If we are not trickling or we completed gathering prior
// to setLocal, then trigger a call of onicecandidate here.
this.foundIceCandidate(null);
}
this._dompc._executeNext();
},
@ -938,6 +938,16 @@ PeerConnectionObserver.prototype = {
this._dompc._executeNext();
},
onIceCandidate: function(level, mid, candidate) {
this.foundIceCandidate(new this._dompc._win.mozRTCIceCandidate(
{
candidate: candidate,
sdpMid: mid,
sdpMLineIndex: level
}
));
},
handleIceStateChanges: function(iceState) {
var histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
switch (iceState) {
@ -945,8 +955,18 @@ PeerConnectionObserver.prototype = {
this._dompc.changeIceConnectionState("new");
this.callCB(this._dompc.ongatheringchange, "complete");
this.callCB(this._onicechange, "starting");
// Now that the PC is ready to go, execute any pending operations.
this._dompc._executeNext();
if (!this._dompc._trickleIce) {
// If we are not trickling, then the queue is in a pending state
// waiting for ICE gathering and executeNext frees it
this._dompc._executeNext();
}
else if (this.localDescription) {
// If we are trickling but we have already done setLocal,
// then we need to send a final foundIceCandidate(null) to indicate
// that we are done gathering.
this.foundIceCandidate(null);
}
break;
case Ci.IPeerConnection.kIceChecking:
this._dompc.changeIceConnectionState("checking");
@ -1009,9 +1029,13 @@ PeerConnectionObserver.prototype = {
{ stream: stream }));
},
foundIceCandidate: function(c) {
foundIceCandidate: function(cand, mid, line) {
this.dispatchEvent(new this._dompc._win.RTCPeerConnectionIceEvent("icecandidate",
{ candidate: c }));
{
candidate: cand,
sdpMid: mid,
sdpMLineIndex: line
}));
},
notifyDataChannel: function(channel) {

View File

@ -24,7 +24,7 @@ interface IPeerConnectionManager : nsISupports
*
* See media/webrtc/signaling/include/PeerConnectionImpl.h
*/
[scriptable, uuid(cf9152f0-c9a8-4093-9435-1daa056e0177)]
[scriptable, uuid(896dc16a-05d6-45e4-bdbf-aba57123ed3e)]
interface IPeerConnectionObserver : nsISupports
{
/* Constants */
@ -45,6 +45,7 @@ interface IPeerConnectionObserver : nsISupports
void onSetRemoteDescriptionError(in unsigned long name, in string message);
void onAddIceCandidateSuccess();
void onAddIceCandidateError(in unsigned long name, in string message);
void onIceCandidate(in unsigned short level, in string mid, in string candidate);
/* Data channel callbacks */
void notifyDataChannel(in nsIDOMDataChannel channel);
@ -59,13 +60,6 @@ interface IPeerConnectionObserver : nsISupports
void onRemoveStream();
void onAddTrack();
void onRemoveTrack();
/* When SDP is parsed and a candidate line is found this method is called.
* It should hook back into the media transport to notify it of ICE candidates
* listed in the SDP PeerConnectionImpl does not parse ICE candidates, just
* pulls them out of the SDP.
*/
void foundIceCandidate(in string candidate);
};
[scriptable, uuid(930dce8b-7c5e-4393-b8c0-cb3a928f68bd)]

View File

@ -49,6 +49,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741267
ok(xhr, "'XMLHttpRequest.prototype' in a sandbox should return the XMLHttpRequest interface prototype object");
ok(isXrayWrapper(xhr), "Getting an interface prototype object on an Xray wrapper should return an Xray wrapper");
ok(isXrayWrapper(xhr.constructor), "Getting the constructor property on an Xray wrapper of an interface prototype object should return an Xray wrapper");
isnot(Object.getOwnPropertyDescriptor(xhr, "send"), undefined,
"We should claim to have a send() method");
isnot(Object.keys(xhr).indexOf("responseType"), -1,
"We should claim to have a responseType property");
isnot(Object.getOwnPropertyNames(xhr).indexOf("open"), -1,
"We should claim to have an open() method");
isnot(Object.getOwnPropertyDescriptor(xhr, "constructor"), undefined,
"We should claim to have a 'constructor' property");
} catch (e) {
ok(false, "'XMLHttpRequest.prototype' shouldn't throw in a sandbox");
}
@ -87,6 +95,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741267
var xhr = Components.utils.evalInSandbox("XMLHttpRequest", sandbox);
is(xhr, "[object XrayWrapper " + XMLHttpRequest + "]", "'XMLHttpRequest' in a sandbox should return the XMLHttpRequest interface object");
ok(isXrayWrapper(xhr.prototype), "Getting the prototype property on an Xray wrapper of an interface object should return an Xray wrapper");
isnot(Object.getOwnPropertyDescriptor(xhr, "UNSENT"), undefined,
"We should claim to have an UNSENT constant");
isnot(Object.keys(xhr).indexOf("OPENED"), -1,
"We should claim to have an OPENED constant");
isnot(Object.getOwnPropertyNames(xhr).indexOf("DONE"), -1,
"We should claim to have a DONE constant");
isnot(Object.getOwnPropertyDescriptor(xhr, "prototype"), undefined,
"We should claim to have 'prototype' property");
} catch (e) {
ok(false, "'XMLHttpRequest' shouldn't throw in a sandbox");
}

View File

@ -7,7 +7,8 @@
// Dummy bindings that we need to force generation of things that
// aren't actually referenced anywhere in IDL yet but are used in C++.
interface DummyInterface {
[Global]
interface DummyInterface : EventTarget {
readonly attribute OnErrorEventHandlerNonNull onErrorEventHandler;
FilePropertyBag fileBag();
InspectorRGBTriple rgbTriple();

View File

@ -522,13 +522,15 @@ if CONFIG['MOZ_B2G_FM']:
if CONFIG['ENABLE_TESTS']:
TEST_WEBIDL_FILES += [
'TestCodeGen.webidl',
'TestDictionary.webidl',
'TestExampleGen.webidl',
'TestJSImplGen.webidl',
'TestJSImplInheritanceGen.webidl',
'TestTypedef.webidl',
]
PREPROCESSED_TEST_WEBIDL_FILES += [
'TestCodeGen.webidl',
'TestExampleGen.webidl',
'TestJSImplGen.webidl',
]
if CONFIG['MOZ_B2G']:
WEBIDL_FILES += [

View File

@ -344,6 +344,7 @@ LayerManagerComposite::Render()
}
if (actualBounds.IsEmpty()) {
mCompositor->GetWidget()->PostRender(this);
return;
}
@ -369,6 +370,8 @@ LayerManagerComposite::Render()
PROFILER_LABEL("LayerManagerComposite", "EndFrame");
mCompositor->EndFrame();
}
mCompositor->GetWidget()->PostRender(this);
}
void

View File

@ -7,8 +7,8 @@
#ifndef js_MemoryMetrics_h
#define js_MemoryMetrics_h
// These declarations are not within jsapi.h because they are highly likely to
// change in the future. Depend on them at your own risk.
// These declarations are highly likely to change in the future. Depend on them
// at your own risk.
#include "mozilla/MemoryReporting.h"
#include "mozilla/NullPtr.h"
@ -23,7 +23,7 @@
#include "js/Utility.h"
#include "js/Vector.h"
class nsISupports; // This is needed for ObjectPrivateVisitor.
class nsISupports; // Needed for ObjectPrivateVisitor.
namespace js {
@ -46,49 +46,65 @@ struct InefficientNonFlatteningStringHashPolicy
static bool match(const JSString *const &k, const Lookup &l);
};
// This file features many classes with numerous size_t fields, and each such
// class has one or more methods that need to operate on all of these fields.
// Writing these individually is error-prone -- it's easy to add a new field
// without updating all the required methods. So we define a single macro list
// in each class to name the fields (and notable characteristics of them), and
// then use the following macros to transform those lists into the required
// methods.
//
// In some classes, one or more of the macro arguments aren't used. We use '_'
// for those.
//
#define DECL_SIZE(gc, mSize) size_t mSize;
#define ZERO_SIZE(gc, mSize) mSize(0),
#define COPY_OTHER_SIZE(gc, mSize) mSize(other.mSize),
#define ADD_OTHER_SIZE(gc, mSize) mSize += other.mSize;
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(gc, mSize) n += (gc == js::IsLiveGCThing) ? mSize : 0;
// Used to annotate which size_t fields measure live GC things and which don't.
enum {
IsLiveGCThing,
NotLiveGCThing
};
struct ZoneStatsPod
{
ZoneStatsPod() {
mozilla::PodZero(this);
}
#define FOR_EACH_SIZE(macro) \
macro(NotLiveGCThing, gcHeapArenaAdmin) \
macro(NotLiveGCThing, gcHeapUnusedGcThings) \
macro(IsLiveGCThing, gcHeapStringsNormal) \
macro(IsLiveGCThing, gcHeapStringsShort) \
macro(IsLiveGCThing, gcHeapLazyScripts) \
macro(IsLiveGCThing, gcHeapTypeObjects) \
macro(IsLiveGCThing, gcHeapIonCodes) \
macro(NotLiveGCThing, stringCharsNonNotable) \
macro(NotLiveGCThing, lazyScripts) \
macro(NotLiveGCThing, typeObjects) \
macro(NotLiveGCThing, typePool)
ZoneStatsPod()
: FOR_EACH_SIZE(ZERO_SIZE)
extra()
{}
void add(const ZoneStatsPod &other) {
#define ADD(x) this->x += other.x
ADD(gcHeapArenaAdmin);
ADD(gcHeapUnusedGcThings);
ADD(gcHeapStringsNormal);
ADD(gcHeapStringsShort);
ADD(gcHeapLazyScripts);
ADD(gcHeapTypeObjects);
ADD(gcHeapIonCodes);
ADD(stringCharsNonNotable);
ADD(lazyScripts);
ADD(typeObjects);
ADD(typePool);
#undef ADD
FOR_EACH_SIZE(ADD_OTHER_SIZE)
// Do nothing with |extra|.
}
// This field can be used by embedders.
void *extra;
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
// Do nothing with |extra|.
return n;
}
size_t gcHeapArenaAdmin;
size_t gcHeapUnusedGcThings;
FOR_EACH_SIZE(DECL_SIZE)
void *extra; // This field can be used by embedders.
size_t gcHeapStringsNormal;
size_t gcHeapStringsShort;
size_t gcHeapLazyScripts;
size_t gcHeapTypeObjects;
size_t gcHeapIonCodes;
size_t stringCharsNonNotable;
size_t lazyScripts;
size_t typeObjects;
size_t typePool;
#undef FOR_EACH_SIZE
};
} // namespace js
@ -98,71 +114,92 @@ namespace JS {
// Data for tracking memory usage of things hanging off objects.
struct ObjectsExtraSizes
{
size_t slots;
size_t elementsNonAsmJS;
size_t elementsAsmJSHeap;
size_t elementsAsmJSNonHeap;
size_t asmJSModuleCode;
size_t asmJSModuleData;
size_t argumentsData;
size_t regExpStatics;
size_t propertyIteratorData;
size_t ctypesData;
size_t private_; // The '_' suffix is required because |private| is a keyword.
// Note that this field is measured separately from the others.
#define FOR_EACH_SIZE(macro) \
macro(js::NotLiveGCThing, slots) \
macro(js::NotLiveGCThing, elementsNonAsmJS) \
macro(js::NotLiveGCThing, elementsAsmJSHeap) \
macro(js::NotLiveGCThing, elementsAsmJSNonHeap) \
macro(js::NotLiveGCThing, asmJSModuleCode) \
macro(js::NotLiveGCThing, asmJSModuleData) \
macro(js::NotLiveGCThing, argumentsData) \
macro(js::NotLiveGCThing, regExpStatics) \
macro(js::NotLiveGCThing, propertyIteratorData) \
macro(js::NotLiveGCThing, ctypesData)
ObjectsExtraSizes() { memset(this, 0, sizeof(ObjectsExtraSizes)); }
ObjectsExtraSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
void add(ObjectsExtraSizes &sizes) {
this->slots += sizes.slots;
this->elementsNonAsmJS += sizes.elementsNonAsmJS;
this->elementsAsmJSHeap += sizes.elementsAsmJSHeap;
this->elementsAsmJSNonHeap += sizes.elementsAsmJSNonHeap;
this->asmJSModuleCode += sizes.asmJSModuleCode;
this->asmJSModuleData += sizes.asmJSModuleData;
this->argumentsData += sizes.argumentsData;
this->regExpStatics += sizes.regExpStatics;
this->propertyIteratorData += sizes.propertyIteratorData;
this->ctypesData += sizes.ctypesData;
this->private_ += sizes.private_;
void add(const ObjectsExtraSizes &other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE)
}
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
return n;
}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
#undef FOR_EACH_SIZE
};
// Data for tracking analysis/inference memory usage.
struct TypeInferenceSizes
{
size_t typeScripts;
size_t typeResults;
size_t pendingArrays;
size_t allocationSiteTables;
size_t arrayTypeTables;
size_t objectTypeTables;
#define FOR_EACH_SIZE(macro) \
macro(js::NotLiveGCThing, typeScripts) \
macro(js::NotLiveGCThing, typeResults) \
macro(js::NotLiveGCThing, pendingArrays) \
macro(js::NotLiveGCThing, allocationSiteTables) \
macro(js::NotLiveGCThing, arrayTypeTables) \
macro(js::NotLiveGCThing, objectTypeTables)
TypeInferenceSizes() { memset(this, 0, sizeof(TypeInferenceSizes)); }
TypeInferenceSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
void add(TypeInferenceSizes &sizes) {
this->typeScripts += sizes.typeScripts;
this->typeResults += sizes.typeResults;
this->pendingArrays += sizes.pendingArrays;
this->allocationSiteTables += sizes.allocationSiteTables;
this->arrayTypeTables += sizes.arrayTypeTables;
this->objectTypeTables += sizes.objectTypeTables;
void add(const TypeInferenceSizes &other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE)
}
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
return n;
}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
#undef FOR_EACH_SIZE
};
// Data for tracking JIT-code memory usage.
struct CodeSizes
{
size_t ion;
size_t baseline;
size_t regexp;
size_t other;
size_t unused;
#define FOR_EACH_SIZE(macro) \
macro(_, ion) \
macro(_, baseline) \
macro(_, regexp) \
macro(_, other) \
macro(_, unused)
CodeSizes() { memset(this, 0, sizeof(CodeSizes)); }
CodeSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
#undef FOR_EACH_SIZE
};
// This class holds information about the memory taken up by identical copies of
// a particular string. Multiple JSStrings may have their sizes aggregated
// together into one StringInfo object.
@ -247,25 +284,32 @@ struct NotableStringInfo : public StringInfo
char *buffer;
};
// These measurements relate directly to the JSRuntime, and not to
// These measurements relate directly to the JSRuntime, and not to zones and
// compartments within it.
struct RuntimeSizes
{
RuntimeSizes() { memset(this, 0, sizeof(RuntimeSizes)); }
#define FOR_EACH_SIZE(macro) \
macro(_, object) \
macro(_, atomsTable) \
macro(_, contexts) \
macro(_, dtoa) \
macro(_, temporary) \
macro(_, regexpData) \
macro(_, interpreterStack) \
macro(_, gcMarker) \
macro(_, mathCache) \
macro(_, scriptData) \
macro(_, scriptSources)
size_t object;
size_t atomsTable;
size_t contexts;
size_t dtoa;
size_t temporary;
size_t regexpData;
size_t interpreterStack;
size_t gcMarker;
size_t mathCache;
size_t scriptData;
size_t scriptSources;
RuntimeSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
code()
{}
FOR_EACH_SIZE(DECL_SIZE)
CodeSizes code;
#undef FOR_EACH_SIZE
};
struct ZoneStats : js::ZoneStatsPod
@ -310,156 +354,88 @@ struct ZoneStats : js::ZoneStatsPod
StringsHashMap strings;
js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
// The size of all the live things in the GC heap that don't belong to any
// compartment.
size_t GCHeapThingsSize();
};
struct CompartmentStats
{
#define FOR_EACH_SIZE(macro) \
macro(js::IsLiveGCThing, gcHeapObjectsOrdinary) \
macro(js::IsLiveGCThing, gcHeapObjectsFunction) \
macro(js::IsLiveGCThing, gcHeapObjectsDenseArray) \
macro(js::IsLiveGCThing, gcHeapObjectsSlowArray) \
macro(js::IsLiveGCThing, gcHeapObjectsCrossCompartmentWrapper) \
macro(js::IsLiveGCThing, gcHeapShapesTreeGlobalParented) \
macro(js::IsLiveGCThing, gcHeapShapesTreeNonGlobalParented) \
macro(js::IsLiveGCThing, gcHeapShapesDict) \
macro(js::IsLiveGCThing, gcHeapShapesBase) \
macro(js::IsLiveGCThing, gcHeapScripts) \
macro(js::NotLiveGCThing, objectsPrivate) \
macro(js::NotLiveGCThing, shapesExtraTreeTables) \
macro(js::NotLiveGCThing, shapesExtraDictTables) \
macro(js::NotLiveGCThing, shapesExtraTreeShapeKids) \
macro(js::NotLiveGCThing, shapesCompartmentTables) \
macro(js::NotLiveGCThing, scriptData) \
macro(js::NotLiveGCThing, baselineData) \
macro(js::NotLiveGCThing, baselineStubsFallback) \
macro(js::NotLiveGCThing, baselineStubsOptimized) \
macro(js::NotLiveGCThing, ionData) \
macro(js::NotLiveGCThing, compartmentObject) \
macro(js::NotLiveGCThing, crossCompartmentWrappersTable) \
macro(js::NotLiveGCThing, regexpCompartment) \
macro(js::NotLiveGCThing, debuggeesSet) \
CompartmentStats()
: extra(nullptr),
gcHeapObjectsOrdinary(0),
gcHeapObjectsFunction(0),
gcHeapObjectsDenseArray(0),
gcHeapObjectsSlowArray(0),
gcHeapObjectsCrossCompartmentWrapper(0),
gcHeapShapesTreeGlobalParented(0),
gcHeapShapesTreeNonGlobalParented(0),
gcHeapShapesDict(0),
gcHeapShapesBase(0),
gcHeapScripts(0),
: FOR_EACH_SIZE(ZERO_SIZE)
objectsExtra(),
shapesExtraTreeTables(0),
shapesExtraDictTables(0),
shapesExtraTreeShapeKids(0),
shapesCompartmentTables(0),
scriptData(0),
baselineData(0),
baselineStubsFallback(0),
baselineStubsOptimized(0),
ionData(0),
compartmentObject(0),
crossCompartmentWrappersTable(0),
regexpCompartment(0),
debuggeesSet(0),
typeInference()
typeInference(),
extra()
{}
CompartmentStats(const CompartmentStats &other)
: extra(other.extra),
gcHeapObjectsOrdinary(other.gcHeapObjectsOrdinary),
gcHeapObjectsFunction(other.gcHeapObjectsFunction),
gcHeapObjectsDenseArray(other.gcHeapObjectsDenseArray),
gcHeapObjectsSlowArray(other.gcHeapObjectsSlowArray),
gcHeapObjectsCrossCompartmentWrapper(other.gcHeapObjectsCrossCompartmentWrapper),
gcHeapShapesTreeGlobalParented(other.gcHeapShapesTreeGlobalParented),
gcHeapShapesTreeNonGlobalParented(other.gcHeapShapesTreeNonGlobalParented),
gcHeapShapesDict(other.gcHeapShapesDict),
gcHeapShapesBase(other.gcHeapShapesBase),
gcHeapScripts(other.gcHeapScripts),
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
objectsExtra(other.objectsExtra),
shapesExtraTreeTables(other.shapesExtraTreeTables),
shapesExtraDictTables(other.shapesExtraDictTables),
shapesExtraTreeShapeKids(other.shapesExtraTreeShapeKids),
shapesCompartmentTables(other.shapesCompartmentTables),
scriptData(other.scriptData),
baselineData(other.baselineData),
baselineStubsFallback(other.baselineStubsFallback),
baselineStubsOptimized(other.baselineStubsOptimized),
ionData(other.ionData),
compartmentObject(other.compartmentObject),
crossCompartmentWrappersTable(other.crossCompartmentWrappersTable),
regexpCompartment(other.regexpCompartment),
debuggeesSet(other.debuggeesSet),
typeInference(other.typeInference)
{
typeInference(other.typeInference),
extra(other.extra)
{}
void add(const CompartmentStats &other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE)
objectsExtra.add(other.objectsExtra);
typeInference.add(other.typeInference);
// Do nothing with |extra|.
}
// This field can be used by embedders.
void *extra;
// If you add a new number, remember to update the constructors, add(), and
// maybe gcHeapThingsSize()!
size_t gcHeapObjectsOrdinary;
size_t gcHeapObjectsFunction;
size_t gcHeapObjectsDenseArray;
size_t gcHeapObjectsSlowArray;
size_t gcHeapObjectsCrossCompartmentWrapper;
size_t gcHeapShapesTreeGlobalParented;
size_t gcHeapShapesTreeNonGlobalParented;
size_t gcHeapShapesDict;
size_t gcHeapShapesBase;
size_t gcHeapScripts;
ObjectsExtraSizes objectsExtra;
size_t shapesExtraTreeTables;
size_t shapesExtraDictTables;
size_t shapesExtraTreeShapeKids;
size_t shapesCompartmentTables;
size_t scriptData;
size_t baselineData;
size_t baselineStubsFallback;
size_t baselineStubsOptimized;
size_t ionData;
size_t compartmentObject;
size_t crossCompartmentWrappersTable;
size_t regexpCompartment;
size_t debuggeesSet;
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
n += objectsExtra.sizeOfLiveGCThings();
n += typeInference.sizeOfLiveGCThings();
// Do nothing with |extra|.
return n;
}
FOR_EACH_SIZE(DECL_SIZE)
ObjectsExtraSizes objectsExtra;
TypeInferenceSizes typeInference;
void *extra; // This field can be used by embedders.
// Add cStats's numbers to this object's numbers.
void add(CompartmentStats &cStats) {
#define ADD(x) this->x += cStats.x
ADD(gcHeapObjectsOrdinary);
ADD(gcHeapObjectsFunction);
ADD(gcHeapObjectsDenseArray);
ADD(gcHeapObjectsSlowArray);
ADD(gcHeapObjectsCrossCompartmentWrapper);
ADD(gcHeapShapesTreeGlobalParented);
ADD(gcHeapShapesTreeNonGlobalParented);
ADD(gcHeapShapesDict);
ADD(gcHeapShapesBase);
ADD(gcHeapScripts);
objectsExtra.add(cStats.objectsExtra);
ADD(shapesExtraTreeTables);
ADD(shapesExtraDictTables);
ADD(shapesExtraTreeShapeKids);
ADD(shapesCompartmentTables);
ADD(scriptData);
ADD(baselineData);
ADD(baselineStubsFallback);
ADD(baselineStubsOptimized);
ADD(ionData);
ADD(compartmentObject);
ADD(crossCompartmentWrappersTable);
ADD(regexpCompartment);
ADD(debuggeesSet);
#undef ADD
typeInference.add(cStats.typeInference);
}
// The size of all the live things in the GC heap.
size_t GCHeapThingsSize();
#undef FOR_EACH_SIZE
};
struct RuntimeStats
{
#define FOR_EACH_SIZE(macro) \
macro(_, gcHeapChunkTotal) \
macro(_, gcHeapDecommittedArenas) \
macro(_, gcHeapUnusedChunks) \
macro(_, gcHeapUnusedArenas) \
macro(_, gcHeapUnusedGcThings) \
macro(_, gcHeapChunkAdmin) \
macro(_, gcHeapGcThings) \
RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
: runtime(),
gcHeapChunkTotal(0),
gcHeapDecommittedArenas(0),
gcHeapUnusedChunks(0),
gcHeapUnusedArenas(0),
gcHeapUnusedGcThings(0),
gcHeapChunkAdmin(0),
gcHeapGcThings(0),
: FOR_EACH_SIZE(ZERO_SIZE)
runtime(),
cTotals(),
zTotals(),
compartmentStatsVector(),
@ -468,10 +444,6 @@ struct RuntimeStats
mallocSizeOf_(mallocSizeOf)
{}
RuntimeSizes runtime;
// If you add a new number, remember to update the constructor!
// Here's a useful breakdown of the GC heap.
//
// - rtStats.gcHeapChunkTotal
@ -491,17 +463,12 @@ struct RuntimeStats
// it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
// multiple of the chunk size, which is good.
size_t gcHeapChunkTotal;
size_t gcHeapDecommittedArenas;
size_t gcHeapUnusedChunks;
size_t gcHeapUnusedArenas;
size_t gcHeapUnusedGcThings;
size_t gcHeapChunkAdmin;
size_t gcHeapGcThings;
FOR_EACH_SIZE(DECL_SIZE)
// The sum of all compartment's measurements.
CompartmentStats cTotals;
ZoneStats zTotals;
RuntimeSizes runtime;
CompartmentStats cTotals; // The sum of this runtime's compartments' measurements.
ZoneStats zTotals; // The sum of this runtime's zones' measurements.
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
js::Vector<ZoneStats, 0, js::SystemAllocPolicy> zoneStatsVector;
@ -512,6 +479,8 @@ struct RuntimeStats
virtual void initExtraCompartmentStats(JSCompartment *c, CompartmentStats *cstats) = 0;
virtual void initExtraZoneStats(JS::Zone *zone, ZoneStats *zstats) = 0;
#undef FOR_EACH_SIZE
};
class ObjectPrivateVisitor

View File

@ -317,10 +317,6 @@ def is_module_header(enclosing_inclname, header_inclname):
if m is not None and module.endswith('/' + m.group(1)):
return True
# A weird public header case.
if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h':
return True
return False

View File

@ -60,7 +60,7 @@ LOCAL_OBJDIR = $(OBJDIR)/editline
# Default IEEE libm
#
CFLAGS += -DXP_UNIX $(OPTIMIZER) $(OS_CFLAGS) $(DEFINES) $(INCLUDES) \
-DJSFILE $(XCFLAGS) $(DEFS)
$(XCFLAGS) $(DEFS)
INCFILES = editline.h
.INIT: $(INCFILES)

View File

@ -8581,7 +8581,8 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
: MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
{
setMovable();
// Disabled due to errors, see bug 919958
// setMovable();
if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64)
setResultType(MIRType_Double);
else

View File

@ -735,7 +735,7 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
}
Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, script, tmp.address()))
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, tmp.address()))
return false;
*objp = tmp;
}

View File

@ -99,6 +99,7 @@ CPP_SOURCES += [
'MapObject.cpp',
'Marking.cpp',
'Memory.cpp',
'MemoryMetrics.cpp',
'Module.cpp',
'Monitor.cpp',
'NameFunctions.cpp',
@ -164,7 +165,6 @@ CPP_SOURCES += [
'jsinfer.cpp',
'jsiter.cpp',
'jsmath.cpp',
'jsmemorymetrics.cpp',
'jsnativestack.cpp',
'jsnum.cpp',
'jsobj.cpp',

View File

@ -153,50 +153,17 @@ NotableStringInfo &NotableStringInfo::operator=(MoveRef<NotableStringInfo> info)
typedef HashSet<ScriptSource *, DefaultHasher<ScriptSource *>, SystemAllocPolicy> SourceSet;
struct IteratorClosure
struct StatsClosure
{
RuntimeStats *rtStats;
ObjectPrivateVisitor *opv;
SourceSet seenSources;
IteratorClosure(RuntimeStats *rt, ObjectPrivateVisitor *v) : rtStats(rt), opv(v) {}
StatsClosure(RuntimeStats *rt, ObjectPrivateVisitor *v) : rtStats(rt), opv(v) {}
bool init() {
return seenSources.init();
}
};
size_t
ZoneStats::GCHeapThingsSize()
{
// These are just the GC-thing measurements.
size_t n = 0;
n += gcHeapStringsNormal;
n += gcHeapStringsShort;
n += gcHeapLazyScripts;
n += gcHeapTypeObjects;
n += gcHeapIonCodes;
return n;
}
size_t
CompartmentStats::GCHeapThingsSize()
{
// These are just the GC-thing measurements.
size_t n = 0;
n += gcHeapObjectsOrdinary;
n += gcHeapObjectsFunction;
n += gcHeapObjectsDenseArray;
n += gcHeapObjectsSlowArray;
n += gcHeapObjectsCrossCompartmentWrapper;
n += gcHeapShapesTreeGlobalParented;
n += gcHeapShapesTreeNonGlobalParented;
n += gcHeapShapesDict;
n += gcHeapShapesBase;
n += gcHeapScripts;
return n;
}
static void
DecommittedArenasChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
{
@ -217,7 +184,7 @@ static void
StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
{
// Append a new CompartmentStats to the vector.
RuntimeStats *rtStats = static_cast<IteratorClosure *>(data)->rtStats;
RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
// CollectRuntimeStats reserves enough space.
MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
@ -241,7 +208,7 @@ static void
StatsZoneCallback(JSRuntime *rt, void *data, Zone *zone)
{
// Append a new CompartmentStats to the vector.
RuntimeStats *rtStats = static_cast<IteratorClosure *>(data)->rtStats;
RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
// CollectRuntimeStats reserves enough space.
MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1));
@ -257,7 +224,7 @@ static void
StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize)
{
RuntimeStats *rtStats = static_cast<IteratorClosure *>(data)->rtStats;
RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
// The admin space includes (a) the header and (b) the padding between the
// end of the header and the start of the first GC thing.
@ -281,7 +248,7 @@ static void
StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
size_t thingSize)
{
IteratorClosure *closure = static_cast<IteratorClosure *>(data);
StatsClosure *closure = static_cast<StatsClosure *>(data);
RuntimeStats *rtStats = closure->rtStats;
ZoneStats *zStats = rtStats->currZoneStats;
switch (traceKind) {
@ -301,12 +268,12 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
obj->sizeOfExcludingThis(rtStats->mallocSizeOf_, &objectsExtra);
cStats->objectsExtra.add(objectsExtra);
// JSObject::sizeOfExcludingThis() doesn't measure objectsExtraPrivate,
// JSObject::sizeOfExcludingThis() doesn't measure objectsPrivate,
// so we do it here.
if (ObjectPrivateVisitor *opv = closure->opv) {
nsISupports *iface;
if (opv->getISupports_(obj, &iface) && iface) {
cStats->objectsExtra.private_ += opv->sizeOfIncludingThis(iface);
cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
}
}
break;
@ -473,7 +440,7 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit
DecommittedArenasChunkCallback);
// Take the per-compartment measurements.
IteratorClosure closure(rtStats, opv);
StatsClosure closure(rtStats, opv);
if (!closure.init())
return false;
rtStats->runtime.scriptSources = 0;
@ -490,7 +457,7 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit
ZoneStats &zStats = rtStats->zoneStatsVector[i];
rtStats->zTotals.add(zStats);
rtStats->gcHeapGcThings += zStats.GCHeapThingsSize();
rtStats->gcHeapGcThings += zStats.sizeOfLiveGCThings();
#ifdef DEBUG
totalArenaSize += zStats.gcHeapArenaAdmin + zStats.gcHeapUnusedGcThings;
#endif
@ -506,7 +473,7 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit
CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
rtStats->cTotals.add(cStats);
rtStats->gcHeapGcThings += cStats.GCHeapThingsSize();
rtStats->gcHeapGcThings += cStats.sizeOfLiveGCThings();
}
#ifdef DEBUG

View File

@ -730,7 +730,7 @@ const Class BlockObject::class_ = {
template<XDRMode mode>
bool
js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
StaticBlockObject **objp)
{
/* NB: Keep this in sync with CloneStaticBlockObject. */
@ -834,10 +834,10 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, Handl
}
template bool
js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, StaticBlockObject **);
js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, StaticBlockObject **);
template bool
js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, HandleScript, StaticBlockObject **);
js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, StaticBlockObject **);
JSObject *
js::CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> srcBlock)

View File

@ -470,7 +470,7 @@ class ClonedBlockObject : public BlockObject
template<XDRMode mode>
bool
XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
StaticBlockObject **objp);
extern JSObject *

View File

@ -11,6 +11,5 @@ LOCAL_INCLUDES += \
include $(topsrcdir)/config/rules.mk
DEFINES += \
-DJSFILE \
-DJS_THREADSAFE \
$(NULL)

View File

@ -46,9 +46,7 @@ include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
DEFINES += \
-DJSFILE \
-DJS_THREADSAFE \
-DEXPORT_XPC_API \
$(NULL)
ifdef MOZ_JSDEBUGGER

View File

@ -1731,7 +1731,7 @@ private:
rtTotal += amount; \
} while (0)
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf)
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JSMallocSizeOf)
namespace xpc {
@ -2067,7 +2067,7 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
// DOM nodes in the JS reporter, but we want to report them in a "dom"
// sub-tree rather than a "js" sub-tree.
ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
cStats.objectsExtra.private_,
cStats.objectsPrivate,
"Memory used by orphan DOM nodes that are only reachable "
"from JavaScript objects.");
@ -2441,7 +2441,7 @@ class XPCJSRuntimeStats : public JS::RuntimeStats
public:
XPCJSRuntimeStats(WindowPaths *windowPaths, WindowPaths *topWindowPaths,
bool getLocations)
: JS::RuntimeStats(JsMallocSizeOf),
: JS::RuntimeStats(JSMallocSizeOf),
mWindowPaths(windowPaths),
mTopWindowPaths(topWindowPaths),
mGetLocations(getLocations)
@ -2574,8 +2574,8 @@ JSReporter::CollectReports(WindowPaths *windowPaths,
return NS_ERROR_FAILURE;
size_t xpconnect =
xpcrt->SizeOfIncludingThis(JsMallocSizeOf) +
XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf);
xpcrt->SizeOfIncludingThis(JSMallocSizeOf) +
XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JSMallocSizeOf);
// This is the second step (see above). First we report stuff in the
// "explicit" tree, then we report other stuff.
@ -3047,7 +3047,7 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
XPCJSRuntime* self = new XPCJSRuntime(aXPConnect);
if (self &&
self->Runtime() &&
self->Runtime() &&
self->GetWrappedJSMap() &&
self->GetWrappedJSClassMap() &&
self->GetIID2NativeInterfaceMap() &&

View File

@ -8,37 +8,32 @@
namespace mozilla {
#define STACK_ARENA_MARK_INCREMENT 50
/* a bit under 4096, for malloc overhead */
#define STACK_ARENA_BLOCK_INCREMENT 4044
/**A block of memory that the stack will
* chop up and hand out
*/
// A block of memory that the stack will chop up and hand out.
struct StackBlock {
// a block of memory. Note that this must be first so that it will
// be aligned.
char mBlock[STACK_ARENA_BLOCK_INCREMENT];
// Subtract sizeof(StackBlock*) to give space for the |mNext| field.
static const size_t MAX_USABLE_SIZE = 4096 - sizeof(StackBlock*);
// another block of memory that would only be created
// if our stack overflowed. Yes we have the ability
// to grow on a stack overflow
StackBlock* mNext;
// A block of memory.
char mBlock[MAX_USABLE_SIZE];
StackBlock() : mNext(nullptr) { }
~StackBlock() { }
// Another block of memory that would only be created if our stack
// overflowed.
StackBlock* mNext;
StackBlock() : mNext(nullptr) { }
~StackBlock() { }
};
/* we hold an array of marks. A push pushes a mark on the stack
* a pop pops it off.
*/
static_assert(sizeof(StackBlock) == 4096, "StackBlock must be 4096 bytes");
// We hold an array of marks. A push pushes a mark on the stack.
// A pop pops it off.
struct StackMark {
// the block of memory we are currently handing out chunks of
StackBlock* mBlock;
// our current position in the memory
size_t mPos;
// The block of memory from which we are currently handing out chunks.
StackBlock* mBlock;
// Our current position in the block.
size_t mPos;
};
StackArena* AutoStackArena::gStackArena;
@ -48,7 +43,7 @@ StackArena::StackArena()
mMarkLength = 0;
mMarks = nullptr;
// allocate our stack memory
// Allocate our stack memory.
mBlocks = new StackBlock();
mCurBlock = mBlocks;
@ -58,15 +53,14 @@ StackArena::StackArena()
StackArena::~StackArena()
{
// free up our data
delete[] mMarks;
while(mBlocks)
{
// Free up our data.
delete [] mMarks;
while (mBlocks) {
StackBlock* toDelete = mBlocks;
mBlocks = mBlocks->mNext;
delete toDelete;
}
}
}
size_t
StackArena::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
@ -81,19 +75,21 @@ StackArena::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
return n;
}
static const int STACK_ARENA_MARK_INCREMENT = 50;
void
StackArena::Push()
{
// Resize the mark array if we overrun it. Failure to allocate the
// mark array is not fatal; we just won't free to that mark. This
// allows callers not to worry about error checking.
if (mStackTop >= mMarkLength)
{
if (mStackTop >= mMarkLength) {
uint32_t newLength = mStackTop + STACK_ARENA_MARK_INCREMENT;
StackMark* newMarks = new StackMark[newLength];
if (newMarks) {
if (mMarkLength)
if (mMarkLength) {
memcpy(newMarks, mMarks, sizeof(StackMark)*mMarkLength);
}
// Fill in any marks that we couldn't allocate during a prior call
// to Push().
for (; mMarkLength < mStackTop; ++mMarkLength) {
@ -107,7 +103,7 @@ StackArena::Push()
}
}
// set a mark at the top (if we can)
// Set a mark at the top (if we can).
NS_ASSERTION(mStackTop < mMarkLength, "out of memory");
if (mStackTop < mMarkLength) {
mMarks[mStackTop].mBlock = mCurBlock;
@ -122,23 +118,22 @@ StackArena::Allocate(size_t aSize)
{
NS_ASSERTION(mStackTop > 0, "Allocate called without Push");
// make sure we are aligned. Beard said 8 was safer then 4.
// Round size to multiple of 8
// Align to a multiple of 8.
aSize = NS_ROUNDUP<size_t>(aSize, 8);
// if the size makes the stack overflow. Grab another block for the stack
if (mPos + aSize >= STACK_ARENA_BLOCK_INCREMENT)
{
NS_ASSERTION(aSize <= STACK_ARENA_BLOCK_INCREMENT,
// On stack overflow, grab another block.
if (mPos + aSize >= StackBlock::MAX_USABLE_SIZE) {
NS_ASSERTION(aSize <= StackBlock::MAX_USABLE_SIZE,
"Requested memory is greater that our block size!!");
if (mCurBlock->mNext == nullptr)
if (mCurBlock->mNext == nullptr) {
mCurBlock->mNext = new StackBlock();
}
mCurBlock = mCurBlock->mNext;
mCurBlock = mCurBlock->mNext;
mPos = 0;
}
// return the chunk they need.
// Return the chunk they need.
void *result = mCurBlock->mBlock + mPos;
mPos += aSize;
@ -148,7 +143,7 @@ StackArena::Allocate(size_t aSize)
void
StackArena::Pop()
{
// pop off the mark
// Pop off the mark.
NS_ASSERTION(mStackTop > 0, "unmatched pop");
mStackTop--;

View File

@ -12,10 +12,8 @@ namespace mozilla {
struct StackBlock;
struct StackMark;
class AutoStackArena;
/**
* Private helper class for AutoStackArena.
*/
// Private helper class for AutoStackArena.
class StackArena {
private:
friend class AutoStackArena;
@ -24,49 +22,49 @@ private:
nsresult Init() { return mBlocks ? NS_OK : NS_ERROR_OUT_OF_MEMORY; }
// Memory management functions
// Memory management functions.
void* Allocate(size_t aSize);
void Push();
void Pop();
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// our current position in memory
// Our current position in memory.
size_t mPos;
// a list of memory block. Usually there is only one
// A list of memory blocks. Usually there is only one
// but if we overrun our stack size we can get more memory.
StackBlock* mBlocks;
// the current block of memory we are passing our chucks of
// The current block.
StackBlock* mCurBlock;
// our stack of mark where push has been called
// Our stack of mark where push has been called.
StackMark* mMarks;
// the current top of the mark list
// The current top of the mark list.
uint32_t mStackTop;
// the size of the mark array
// The size of the mark array.
uint32_t mMarkLength;
};
/**
* Class for stack scoped arena memory allocations.
*
* Callers who wish to allocate memory whose lifetime corresponds to
* the lifetime of a stack-allocated object can use this class.
* First, declare an AutoStackArena object on the stack.
* Then all subsequent calls to Allocate will allocate memory from an
* arena pool that will be freed when that variable goes out of scope.
* Nesting is allowed.
*
* The allocations cannot be for more than 4044 bytes.
*/
// Class for stack scoped arena memory allocations.
//
// Callers who wish to allocate memory whose lifetime corresponds to the
// lifetime of a stack-allocated object can use this class. First,
// declare an AutoStackArena object on the stack. Then all subsequent
// calls to Allocate will allocate memory from an arena pool that will
// be freed when that variable goes out of scope. Nesting is allowed.
//
// Individual allocations cannot exceed StackBlock::MAX_USABLE_SIZE
// bytes.
//
class MOZ_STACK_CLASS AutoStackArena {
public:
AutoStackArena() : mOwnsStackArena(false) {
AutoStackArena()
: mOwnsStackArena(false)
{
if (!gStackArena) {
gStackArena = new StackArena();
mOwnsStackArena = true;
@ -84,7 +82,6 @@ public:
}
static void* Allocate(size_t aSize) {
MOZ_ASSERT(aSize <= 4044);
return gStackArena->Allocate(aSize);
}
@ -93,5 +90,4 @@ private:
bool mOwnsStackArena;
};
} // namespace mozilla

View File

@ -252,7 +252,7 @@ int NrIceCtx::select_pair(void *obj,nr_ice_media_stream *stream,
int NrIceCtx::stream_ready(void *obj, nr_ice_media_stream *stream) {
MOZ_MTLOG(ML_DEBUG, "stream_ready called");
// Get the ICE ctx
// Get the ICE ctx.
NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
@ -306,6 +306,30 @@ int NrIceCtx::msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
return 0;
}
void NrIceCtx::trickle_cb(void *arg, nr_ice_ctx *ice_ctx,
nr_ice_media_stream *stream,
int component_id,
nr_ice_candidate *candidate) {
// Get the ICE ctx
NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
// Streams which do not exist shouldn't have candidates.
MOZ_ASSERT(s);
// Format the candidate.
char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE];
int r = nr_ice_format_candidate_attribute(candidate, candidate_str,
sizeof(candidate_str));
MOZ_ASSERT(!r);
if (r)
return;
MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate "
<< candidate_str);
s->SignalCandidate(s, candidate_str);
}
RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name,
bool offerer,
@ -383,6 +407,14 @@ RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name,
}
#endif // USE_INTERFACE_PRIORITIZER
if (ctx->generating_trickle()) {
r = nr_ice_ctx_set_trickle_cb(ctx->ctx_, &NrIceCtx::trickle_cb, ctx);
if (r) {
MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name << "'");
return nullptr;
}
}
// Create the handler objects
ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl();
ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair;
@ -535,15 +567,6 @@ nsresult NrIceCtx::StartGathering() {
return NS_OK;
}
void NrIceCtx::EmitAllCandidates() {
MOZ_MTLOG(ML_NOTICE, "Gathered all ICE candidates for '"
<< name_ << "'");
for(size_t i=0; i<streams_.size(); ++i) {
streams_[i]->EmitAllCandidates();
}
}
RefPtr<NrIceMediaStream> NrIceCtx::FindStream(
nr_ice_media_stream *stream) {
for (size_t i=0; i<streams_.size(); ++i) {
@ -630,8 +653,6 @@ nsresult NrIceCtx::StartChecks() {
void NrIceCtx::initialized_cb(NR_SOCKET s, int h, void *arg) {
NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
ctx->EmitAllCandidates();
ctx->SetState(ICE_CTX_GATHERED);
}

View File

@ -69,6 +69,7 @@ typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx;
typedef struct nr_ice_media_stream_ nr_ice_media_stream;
typedef struct nr_ice_handler_ nr_ice_handler;
typedef struct nr_ice_handler_vtbl_ nr_ice_handler_vtbl;
typedef struct nr_ice_candidate_ nr_ice_candidate;
typedef struct nr_ice_cand_pair_ nr_ice_cand_pair;
typedef struct nr_ice_stun_server_ nr_ice_stun_server;
typedef struct nr_ice_turn_server_ nr_ice_turn_server;
@ -221,6 +222,9 @@ class NrIceCtx {
// more forking.
nsresult Finalize();
// Are we trickling?
bool generating_trickle() const { return trickle_; }
// Signals to indicate events. API users can (and should)
// register for these.
// TODO(ekr@rtfm.com): refactor this to be state change instead
@ -244,8 +248,8 @@ class NrIceCtx {
ctx_(nullptr),
peer_(nullptr),
ice_handler_vtbl_(nullptr),
ice_handler_(nullptr)
{
ice_handler_(nullptr),
trickle_(true) {
// XXX: offerer_ will be used eventually; placate clang in the meantime.
(void)offerer_;
}
@ -265,10 +269,8 @@ class NrIceCtx {
static int msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
nr_ice_media_stream *stream, int component_id,
unsigned char *msg, int len);
// Iterate through all media streams and emit the candidates
// Note that we don't do trickle ICE yet
void EmitAllCandidates();
static void trickle_cb(void *arg, nr_ice_ctx *ctx, nr_ice_media_stream *stream,
int component_id, nr_ice_candidate *candidate);
// Find a media stream by stream ptr. Gross
RefPtr<NrIceMediaStream> FindStream(nr_ice_media_stream *stream);
@ -284,6 +286,7 @@ class NrIceCtx {
nr_ice_peer_ctx *peer_;
nr_ice_handler_vtbl* ice_handler_vtbl_; // Must be pointer
nr_ice_handler* ice_handler_; // Must be pointer
bool trickle_;
nsCOMPtr<nsIEventTarget> sts_target_; // The thread to run on
};

View File

@ -243,26 +243,6 @@ nsresult NrIceMediaStream::GetActivePair(int component,
}
void NrIceMediaStream::EmitAllCandidates() {
char **attrs = 0;
int attrct;
int r;
r = nr_ice_media_stream_get_attributes(stream_,
&attrs, &attrct);
if (r) {
MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '"
<< name_ << "'");
return;
}
for (int i=0; i<attrct; i++) {
SignalCandidate(this, attrs[i]);
RFREE(attrs[i]);
}
RFREE(attrs);
}
nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>*
out_pairs) const {
MOZ_ASSERT(out_pairs);
@ -332,23 +312,35 @@ nsresult NrIceMediaStream::GetDefaultCandidate(int component,
r = nr_ice_media_stream_get_default_candidate(stream_,
component, &cand);
if (r) {
MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
<< name_ << "'");
return NS_ERROR_NOT_AVAILABLE;
if (ctx_->generating_trickle()) {
// Generate default trickle candidates.
// draft-ivov-mmusic-trickle-ice-01.txt says to use port 9
// but "::" instead of "0.0.0.0". Since we don't do any
// IPv6 we are ignoring that for now.
*addrp = "0.0.0.0";
*portp = 9;
}
else {
MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
<< name_ << "'");
return NS_ERROR_NOT_AVAILABLE;
}
}
else {
char addr[64]; // Enough for IPv6 with colons.
r = nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr));
if (r)
return NS_ERROR_FAILURE;
char addr[64]; // Enough for IPv6 with colons.
r = nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr));
if (r)
return NS_ERROR_FAILURE;
int port;
r=nr_transport_addr_get_port(&cand->addr,&port);
if (r)
return NS_ERROR_FAILURE;
int port;
r=nr_transport_addr_get_port(&cand->addr,&port);
if (r)
return NS_ERROR_FAILURE;
*addrp = addr;
*portp = port;
*addrp = addr;
*portp = port;
}
return NS_OK;
}

View File

@ -106,6 +106,12 @@ struct NrIceCandidatePair {
// TODO(bcampen@mozilla.com): Is it important to put the foundation in here?
};
// Abstract base class for opaque values.
class NrIceOpaque {
public:
virtual ~NrIceOpaque() {}
};
class NrIceMediaStream {
public:
static RefPtr<NrIceMediaStream> Create(NrIceCtx *ctx,
@ -164,6 +170,12 @@ class NrIceMediaStream {
// the context has been destroyed.
void Close();
// Set an opaque value. Owned by the media stream.
void SetOpaque(NrIceOpaque *opaque) { opaque_ = opaque; }
// Get the opaque
NrIceOpaque* opaque() const { return opaque_; }
sigslot::signal2<NrIceMediaStream *, const std::string& >
SignalCandidate; // A new ICE candidate:
sigslot::signal1<NrIceMediaStream *> SignalReady; // Candidate pair ready.
@ -171,10 +183,6 @@ class NrIceMediaStream {
sigslot::signal4<NrIceMediaStream *, int, const unsigned char *, int>
SignalPacketReceived; // Incoming packet
// Emit all the ICE candidates. Note that this doesn't
// work for trickle ICE yet--called internally
void EmitAllCandidates();
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceMediaStream)
private:
@ -184,7 +192,8 @@ class NrIceMediaStream {
ctx_(ctx),
name_(name),
components_(components),
stream_(nullptr) {}
stream_(nullptr),
opaque_(nullptr) {}
DISALLOW_COPY_ASSIGN(NrIceMediaStream);
@ -193,6 +202,7 @@ class NrIceMediaStream {
const std::string name_;
const int components_;
nr_ice_media_stream *stream_;
ScopedDeletePtr<NrIceOpaque> opaque_;
};

View File

@ -54,13 +54,16 @@ const uint16_t kDefaultStunServerPort=3478;
const std::string kBogusIceCandidate(
(char *)"candidate:0 2 UDP 2113601790 192.168.178.20 50769 typ");
std::string g_stun_server_address(kDefaultStunServerAddress);
std::string g_stun_server_hostname(kDefaultStunServerHostname);
std::string g_turn_server;
std::string g_turn_user;
std::string g_turn_password;
namespace {
enum TrickleMode { TRICKLE_NONE, TRICKLE_DEFERRED };
enum TrickleMode { TRICKLE_NONE, TRICKLE_SIMULATE, TRICKLE_REAL };
typedef bool (*CandidateFilter)(const std::string& candidate);
static bool IsRelayCandidate(const std::string& candidate) {
@ -128,7 +131,9 @@ class IceTestPeer : public sigslot::has_slots<> {
remote_(nullptr),
candidate_filter_(nullptr),
expected_local_type_(NrIceCandidate::ICE_HOST),
expected_remote_type_(NrIceCandidate::ICE_HOST) {
expected_remote_type_(NrIceCandidate::ICE_HOST),
trickle_mode_(TRICKLE_NONE),
trickled_(0) {
ice_ctx_->SignalGatheringCompleted.connect(this,
&IceTestPeer::GatheringComplete);
ice_ctx_->SignalCompleted.connect(this, &IceTestPeer::IceCompleted);
@ -154,7 +159,7 @@ class IceTestPeer : public sigslot::has_slots<> {
ASSERT_TRUE(stream);
streams_.push_back(stream);
stream->SignalCandidate.connect(this, &IceTestPeer::GotCandidate);
stream->SignalCandidate.connect(this, &IceTestPeer::CandidateInitialized);
stream->SignalReady.connect(this, &IceTestPeer::StreamReady);
stream->SignalFailed.connect(this, &IceTestPeer::StreamFailed);
stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived);
@ -189,11 +194,11 @@ class IceTestPeer : public sigslot::has_slots<> {
void SetFakeResolver() {
ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
PRNetAddr addr;
PRStatus status = PR_StringToNetAddr(kDefaultStunServerAddress.c_str(),
&addr);
PRStatus status = PR_StringToNetAddr(g_stun_server_address.c_str(),
&addr);
addr.inet.port = kDefaultStunServerPort;
ASSERT_EQ(PR_SUCCESS, status);
fake_resolver_.SetAddr(kDefaultStunServerHostname, addr);
fake_resolver_.SetAddr(g_stun_server_hostname, addr);
ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetResolver(
fake_resolver_.AllocateResolver())));
}
@ -219,7 +224,18 @@ class IceTestPeer : public sigslot::has_slots<> {
return ice_ctx_->GetGlobalAttributes();
}
std::vector<std::string> GetCandidates(size_t stream) {
std::vector<std::string> GetCandidates(size_t stream) {
std::vector<std::string> v;
RUN_ON_THREAD(
test_utils->sts_target(),
WrapRunnableRet(this, &IceTestPeer::GetCandidates_s, stream, &v),
NS_DISPATCH_SYNC);
return v;
}
std::vector<std::string> GetCandidates_s(size_t stream) {
std::vector<std::string> candidates;
if (stream >= streams_.size())
@ -239,7 +255,8 @@ class IceTestPeer : public sigslot::has_slots<> {
return candidates;
}
void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote) {
void SetExpectedTypes(NrIceCandidate::Type local,
NrIceCandidate::Type remote) {
expected_local_type_ = local;
expected_remote_type_ = remote;
}
@ -254,46 +271,53 @@ class IceTestPeer : public sigslot::has_slots<> {
size_t sent() { return sent_; }
// Start connecting to another peer
void Connect(IceTestPeer *remote, TrickleMode trickle_mode,
bool start = true) {
void Connect_s(IceTestPeer *remote, TrickleMode trickle_mode,
bool start = true) {
nsresult res;
remote_ = remote;
test_utils->sts_target()->Dispatch(
WrapRunnableRet(ice_ctx_,
&NrIceCtx::ParseGlobalAttributes, remote->GetGlobalAttributes(), &res),
NS_DISPATCH_SYNC);
trickle_mode_ = trickle_mode;
res = ice_ctx_->ParseGlobalAttributes(remote->GetGlobalAttributes());
ASSERT_TRUE(NS_SUCCEEDED(res));
if (trickle_mode == TRICKLE_NONE) {
if (trickle_mode == TRICKLE_NONE ||
trickle_mode == TRICKLE_REAL) {
for (size_t i=0; i<streams_.size(); ++i) {
test_utils->sts_target()->Dispatch(
WrapRunnableRet(streams_[i], &NrIceMediaStream::ParseAttributes,
remote->GetCandidates(i),
&res), NS_DISPATCH_SYNC);
std::vector<std::string> candidates =
remote->GetCandidates(i);
for (size_t j=0; j<candidates.size(); ++j) {
std::cerr << "Candidate: " + candidates[j] << std::endl;
}
res = streams_[i]->ParseAttributes(candidates);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
} else {
// Parse empty attributes and then trickle them out later
for (size_t i=0; i<streams_.size(); ++i) {
std::vector<std::string> empty_attrs;
test_utils->sts_target()->Dispatch(
WrapRunnableRet(streams_[i], &NrIceMediaStream::ParseAttributes,
empty_attrs,
&res), NS_DISPATCH_SYNC);
res = streams_[i]->ParseAttributes(empty_attrs);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
}
if (start) {
StartChecks();
// Now start checks
res = ice_ctx_->StartChecks();
ASSERT_TRUE(NS_SUCCEEDED(res));
}
}
void DoTrickle(size_t stream) {
void Connect(IceTestPeer *remote, TrickleMode trickle_mode,
bool start = true) {
test_utils->sts_target()->Dispatch(
WrapRunnable(
this, &IceTestPeer::Connect_s, remote, trickle_mode, start),
NS_DISPATCH_SYNC);
}
void SimulateTrickle(size_t stream) {
std::cerr << "Doing trickle for stream " << stream << std::endl;
// If we are in trickle deferred mode, now trickle in the candidates
// for |stream}
@ -392,12 +416,41 @@ class IceTestPeer : public sigslot::has_slots<> {
// Handle events
void GatheringComplete(NrIceCtx *ctx) {
std::cerr << "Gathering complete for " << name_ << std::endl;
gathering_complete_ = true;
std::cerr << "CANDIDATES:" << std::endl;
for (size_t i=0; i<streams_.size(); ++i) {
std::cerr << "Stream " << name_ << std::endl;
std::vector<std::string> candidates =
streams_[i]->GetCandidates();
for(size_t j=0; j<candidates.size(); ++j) {
std::cerr << candidates[j] << std::endl;
}
}
std::cerr << std::endl;
}
void GotCandidate(NrIceMediaStream *stream, const std::string &candidate) {
std::cerr << "Got candidate " << candidate << std::endl;
void CandidateInitialized(NrIceMediaStream *stream, const std::string &candidate) {
std::cerr << "Candidate initialized: " << candidate << std::endl;
candidates_[stream->name()].push_back(candidate);
// If we are connected, then try to trickle to the
// other side.
if (remote_ && remote_->remote_) {
std::vector<mozilla::RefPtr<NrIceMediaStream> >::iterator it =
std::find(streams_.begin(), streams_.end(), stream);
ASSERT_NE(streams_.end(), it);
size_t index = it - streams_.begin();
ASSERT_GT(remote_->streams_.size(), index);
nsresult res = remote_->streams_[index]->ParseTrickleCandidate(
candidate);
ASSERT_TRUE(NS_SUCCEEDED(res));
++trickled_;
}
}
nsresult GetCandidatePairs(size_t stream_index,
@ -570,6 +623,8 @@ class IceTestPeer : public sigslot::has_slots<> {
ASSERT_TRUE(NS_SUCCEEDED(res));
}
int trickled() { return trickled_; }
private:
std::string name_;
nsRefPtr<NrIceCtx> ice_ctx_;
@ -586,6 +641,8 @@ class IceTestPeer : public sigslot::has_slots<> {
CandidateFilter candidate_filter_;
NrIceCandidate::Type expected_local_type_;
NrIceCandidate::Type expected_remote_type_;
TrickleMode trickle_mode_;
int trickled_;
};
class IceGatherTest : public ::testing::Test {
@ -598,9 +655,15 @@ class IceGatherTest : public ::testing::Test {
peer_->AddStream(1);
}
void Gather() {
peer_->Gather();
void Gather(bool wait = true) {
peer_->Gather();
if (wait) {
WaitForGather();
}
}
void WaitForGather() {
ASSERT_TRUE_WAIT(peer_->gathering_complete(), 10000);
}
@ -655,18 +718,19 @@ class IceConnectTest : public ::testing::Test {
bool Gather(bool wait) {
Init(false);
p1_->SetStunServer(kDefaultStunServerAddress, kDefaultStunServerPort);
p2_->SetStunServer(kDefaultStunServerAddress, kDefaultStunServerPort);
p1_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
p2_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
p1_->Gather();
p2_->Gather();
EXPECT_TRUE_WAIT(p1_->gathering_complete(), 10000);
if (!p1_->gathering_complete())
return false;
EXPECT_TRUE_WAIT(p2_->gathering_complete(), 10000);
if (!p2_->gathering_complete())
return false;
if (wait) {
EXPECT_TRUE_WAIT(p1_->gathering_complete(), 10000);
if (!p1_->gathering_complete())
return false;
EXPECT_TRUE_WAIT(p2_->gathering_complete(), 10000);
if (!p2_->gathering_complete())
return false;
}
return true;
}
@ -720,24 +784,29 @@ class IceConnectTest : public ::testing::Test {
ASSERT_TRUE_WAIT(p1_->ice_complete() && p2_->ice_complete(), 5000);
}
void ConnectTrickle() {
p1_->Connect(p2_, TRICKLE_DEFERRED);
p2_->Connect(p1_, TRICKLE_DEFERRED);
void WaitForGather() {
ASSERT_TRUE_WAIT(p1_->gathering_complete(), 10000);
ASSERT_TRUE_WAIT(p2_->gathering_complete(), 10000);
}
void DoTrickle(size_t stream) {
p1_->DoTrickle(stream);
p2_->DoTrickle(stream);
void ConnectTrickle(TrickleMode trickle = TRICKLE_SIMULATE) {
p1_->Connect(p2_, trickle);
p2_->Connect(p1_, trickle);
}
void SimulateTrickle(size_t stream) {
p1_->SimulateTrickle(stream);
p2_->SimulateTrickle(stream);
ASSERT_TRUE_WAIT(p1_->is_ready(stream), 5000);
ASSERT_TRUE_WAIT(p2_->is_ready(stream), 5000);
}
void DoTrickleP1(size_t stream) {
p1_->DoTrickle(stream);
void SimulateTrickleP1(size_t stream) {
p1_->SimulateTrickle(stream);
}
void DoTrickleP2(size_t stream) {
p2_->DoTrickle(stream);
void SimulateTrickleP2(size_t stream) {
p2_->SimulateTrickle(stream);
}
void VerifyConnected() {
@ -828,18 +897,18 @@ class PrioritizerTest : public ::testing::Test {
} // end namespace
TEST_F(IceGatherTest, TestGatherFakeStunServerHostnameNoResolver) {
peer_->SetStunServer(kDefaultStunServerHostname, kDefaultStunServerPort);
peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort);
Gather();
}
TEST_F(IceGatherTest, TestGatherFakeStunServerIpAddress) {
peer_->SetStunServer(kDefaultStunServerAddress, kDefaultStunServerPort);
peer_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
peer_->SetFakeResolver();
Gather();
}
TEST_F(IceGatherTest, TestGatherFakeStunServerHostname) {
peer_->SetStunServer(kDefaultStunServerHostname, kDefaultStunServerPort);
peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort);
peer_->SetFakeResolver();
Gather();
}
@ -851,14 +920,14 @@ TEST_F(IceGatherTest, TestGatherFakeStunBogusHostname) {
}
TEST_F(IceGatherTest, TestGatherDNSStunServerIpAddress) {
peer_->SetStunServer(kDefaultStunServerAddress, kDefaultStunServerPort);
peer_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
peer_->SetDNSResolver();
Gather();
// TODO(jib@mozilla.com): ensure we get server reflexive candidates Bug 848094
}
TEST_F(IceGatherTest, TestGatherDNSStunServerHostname) {
peer_->SetStunServer(kDefaultStunServerHostname, kDefaultStunServerPort);
peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort);
peer_->SetDNSResolver();
Gather();
}
@ -923,6 +992,16 @@ TEST_F(IceGatherTest, TestStunServerReturnsLoopbackAddr) {
ASSERT_FALSE(StreamHasMatchingCandidate(0, " 127.0.0.133 "));
}
TEST_F(IceGatherTest, TestStunServerTrickle) {
UseFakeStunServerWithResponse("192.0.2.1", 3333);
TestStunServer::GetInstance()->SetActive(false);
Gather(false);
ASSERT_FALSE(StreamHasMatchingCandidate(0, "192.0.2.1"));
TestStunServer::GetInstance()->SetActive(true);
WaitForGather();
ASSERT_TRUE(StreamHasMatchingCandidate(0, "192.0.2.1"));
}
TEST_F(IceConnectTest, TestGather) {
AddStream("first", 1);
ASSERT_TRUE(Gather(true));
@ -970,8 +1049,8 @@ TEST_F(IceConnectTest, TestConnectP2ThenP1Trickle) {
ASSERT_TRUE(Gather(true));
ConnectP2();
PR_Sleep(1000);
ConnectP1(TRICKLE_DEFERRED);
DoTrickleP1(0);
ConnectP1(TRICKLE_SIMULATE);
SimulateTrickleP1(0);
WaitForComplete();
}
@ -981,12 +1060,12 @@ TEST_F(IceConnectTest, TestConnectP2ThenP1TrickleTwoComponents) {
ASSERT_TRUE(Gather(true));
ConnectP2();
PR_Sleep(1000);
ConnectP1(TRICKLE_DEFERRED);
DoTrickleP1(0);
ConnectP1(TRICKLE_SIMULATE);
SimulateTrickleP1(0);
std::cerr << "Sleeping between trickle streams" << std::endl;
PR_Sleep(1000); // Give this some time to settle but not complete
// all of ICE.
DoTrickleP1(1);
SimulateTrickleP1(1);
WaitForComplete(2);
}
@ -1001,7 +1080,7 @@ TEST_F(IceConnectTest, TestConnectTrickleOneStreamOneComponent) {
AddStream("first", 1);
ASSERT_TRUE(Gather(true));
ConnectTrickle();
DoTrickle(0);
SimulateTrickle(0);
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
}
@ -1011,12 +1090,21 @@ TEST_F(IceConnectTest, TestConnectTrickleTwoStreamsOneComponent) {
AddStream("second", 1);
ASSERT_TRUE(Gather(true));
ConnectTrickle();
DoTrickle(0);
DoTrickle(1);
SimulateTrickle(0);
SimulateTrickle(1);
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
}
TEST_F(IceConnectTest, TestConnectRealTrickleOneStreamOneComponent) {
AddStream("first", 1);
AddStream("second", 1);
ASSERT_TRUE(Gather(false));
ConnectTrickle(TRICKLE_REAL);
ASSERT_TRUE_WAIT(p1_->ice_complete(), 5000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 5000);
WaitForGather(); // ICE can complete before we finish gathering.
}
TEST_F(IceConnectTest, TestSendReceive) {
AddStream("first", 1);
@ -1195,6 +1283,15 @@ int main(int argc, char **argv)
g_turn_server="";
}
std::string tmp = get_environment("STUN_SERVER_ADDRESS");
if (tmp != "")
g_stun_server_address = tmp;
tmp = get_environment("STUN_SERVER_HOSTNAME");
if (tmp != "")
g_stun_server_hostname = tmp;
test_utils = new MtransportTestUtils();
NSS_NoDB_Init(nullptr);
NSS_SetDomesticPolicy();

View File

@ -416,8 +416,8 @@ static void nr_ice_candidate_fire_ready_cb(NR_SOCKET s, int how, void *cb_arg)
{
nr_ice_candidate *cand = cb_arg;
cand->ready_cb(0, 0, cand->ready_cb_arg);
cand->ready_cb_timer = 0;
cand->ready_cb(0, 0, cand->ready_cb_arg);
}
int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg)

View File

@ -249,7 +249,7 @@ int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *compon
ABORT(r);
cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
cand->done_cb=nr_ice_initialize_finished_cb;
cand->cb_arg=ctx;
cand->cb_arg=cand;
TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
component->candidate_ct++;
@ -315,7 +315,7 @@ int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *compon
cand=TAILQ_FIRST(&component->candidates);
while(cand){
if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){
if(r=nr_ice_candidate_initialize(cand,nr_ice_initialize_finished_cb,ctx)){
if(r=nr_ice_candidate_initialize(cand,nr_ice_initialize_finished_cb,cand)){
if(r!=R_WOULDBLOCK){
ctx->uninitialized_candidates--;
cand->state=NR_ICE_CAND_STATE_FAILED;
@ -330,74 +330,64 @@ int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *compon
return(_status);
}
/* Prune redundant candidates. We use an n^2 algorithm for now.
/*
Compare this newly initialized candidate against the other initialized
candidates and discard the lower-priority one if they are redundant.
This algorithm combined with the other algorithms, favors
host > srflx > relay
This actually won't prune relayed in the very rare
case that relayed is the same. Not relevant in practice.
*/
int nr_ice_component_prune_candidates(nr_ice_ctx *ctx, nr_ice_component *comp)
*/
int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned)
{
nr_ice_candidate *c1,*c1n,*c2;
nr_ice_candidate *c2, *tmp = NULL;
c1=TAILQ_FIRST(&comp->candidates);
while(c1){
c1n=TAILQ_NEXT(c1,entry_comp);
if(c1->state!=NR_ICE_CAND_STATE_INITIALIZED){
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Removing non-initialized candidate %s",
ctx->label,c1->label);
if (c1->state == NR_ICE_CAND_STATE_INITIALIZING) {
r_log(LOG_ICE,LOG_NOTICE, "ICE(%s): Removing candidate %s which is in INITIALIZING state",
ctx->label, c1->label);
}
TAILQ_REMOVE(&comp->candidates,c1,entry_comp);
comp->candidate_ct--;
TAILQ_REMOVE(&c1->isock->candidates,c1,entry_sock);
/* schedule this delete for later as we don't want to delete the underlying
* objects while in the middle of a callback on one of those objects */
NR_ASYNC_SCHEDULE(nr_ice_candidate_destroy_cb,c1);
goto next_c1;
}
*was_pruned = 0;
c2 = TAILQ_FIRST(&comp->candidates);
while(c2){
if((c1 != c2) &&
(c2->state == NR_ICE_CAND_STATE_INITIALIZED) &&
!nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) &&
!nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
c2=TAILQ_NEXT(c1,entry_comp);
if((c1->type == c2->type) ||
(c1->type==HOST && c2->type == SERVER_REFLEXIVE) ||
(c2->type==HOST && c1->type == SERVER_REFLEXIVE)){
while(c2){
nr_ice_candidate *tmp;
/*
These are redundant. Remove the lower pri one.
if(!nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) && !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
if((c1->type == c2->type) ||
(c1->type==HOST && c2->type == SERVER_REFLEXIVE) ||
(c2->type==HOST && c1->type == SERVER_REFLEXIVE)){
/* OK these are redundant. Remove the lower pri one */
tmp=c2;
c2=TAILQ_NEXT(c2,entry_comp);
if(c1n==tmp)
c1n=c2;
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Removing redundant candidate %s",
ctx->label,tmp->label);
TAILQ_REMOVE(&comp->candidates,tmp,entry_comp);
comp->candidate_ct--;
TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock);
nr_ice_candidate_destroy(&tmp);
Since this algorithmis run whenever a new candidate
is initialized, there should at most one duplicate.
*/
if (c1->priority < c2->priority) {
tmp = c1;
*was_pruned = 1;
}
}
else{
c2=TAILQ_NEXT(c2,entry_comp);
else {
tmp = c2;
}
break;
}
}
next_c1:
c1=c1n;
c2=TAILQ_NEXT(c2,entry_comp);
}
return(0);
if (tmp) {
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Removing redundant candidate %s",
ctx->label,tmp->label);
TAILQ_REMOVE(&comp->candidates,tmp,entry_comp);
comp->candidate_ct--;
TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock);
nr_ice_candidate_destroy(&tmp);
}
return 0;
}
/* Section 7.2.1 */
@ -652,74 +642,85 @@ int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_c
return(_status);
}
int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp)
int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
{
nr_ice_candidate *lcand,*pcand;
int r, _status;
nr_ice_candidate *pcand;
nr_ice_cand_pair *pair=0;
nr_ice_socket *isock;
int r,_status;
char codeword[5];
nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword);
r_log(LOG_ICE,LOG_DEBUG,"Pairing local candidate %s:%s",codeword,lcand->label);
switch(lcand->type){
case HOST:
break;
case SERVER_REFLEXIVE:
case PEER_REFLEXIVE:
/* Don't actually pair these candidates */
goto done;
break;
case RELAYED:
break;
default:
assert(0);
ABORT(R_INTERNAL);
break;
}
pcand=TAILQ_FIRST(&pcomp->candidates);
while(pcand){
/*
Two modes, depending on |pair_all_remote|
1. Pair remote candidates which have not been paired
(used in initial pairing or in processing the other side's
trickle candidates).
2. Pair any remote candidate (used when processing our own
trickle candidates).
*/
if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) {
/* If we are pairing our own trickle candidates, the remote candidate should
all be paired */
if (pair_all_remote)
assert (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_PAIRED);
nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword);
r_log(LOG_ICE,LOG_DEBUG,"Pairing with peer candidate %s:%s",codeword,pcand->label);
if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair))
ABORT(r);
if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list,
pair))
ABORT(r);
}
pcand=TAILQ_NEXT(pcand,entry_comp);
}
done:
_status = 0;
abort:
return(_status);
}
int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp)
{
nr_ice_candidate *lcand, *pcand;
nr_ice_socket *isock;
int r,_status;
r_log(LOG_ICE,LOG_DEBUG,"Pairing candidates======");
/* Create the candidate pairs */
lcand=TAILQ_FIRST(&lcomp->candidates);
while(lcand){
int was_paired = 0;
nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword);
r_log(LOG_ICE,LOG_DEBUG,"Examining local candidate %s:%s",codeword,lcand->label);
switch(lcand->type){
case HOST:
break;
case SERVER_REFLEXIVE:
case PEER_REFLEXIVE:
/* Don't actually pair these candidates */
goto next_cand;
break;
case RELAYED:
break;
default:
assert(0);
ABORT(R_INTERNAL);
break;
if (lcand->state == NR_ICE_CAND_STATE_INITIALIZED) {
if ((r = nr_ice_component_pair_candidate(pctx, pcomp, lcand, 0)))
ABORT(r);
}
/* PAIR with each peer*/
if(TAILQ_EMPTY(&pcomp->candidates)) {
/* can happen if our peer proposes no (or all bogus) candidates */
goto next_cand;
}
pcand=TAILQ_FIRST(&pcomp->candidates);
while(pcand){
/* Only pair peer candidates which have not yet been paired.
This allows "trickle ICE". (Not yet standardized, but
part of WebRTC).
TODO(ekr@rtfm.com): Add refernece to the spec when there
is one.
*/
if (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED) {
nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword);
r_log(LOG_ICE,LOG_DEBUG,"Examining peer candidate %s:%s",codeword,pcand->label);
if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair))
ABORT(r);
if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list,
pair))
ABORT(r);
}
else {
was_paired = 1;
}
pcand=TAILQ_NEXT(pcand,entry_comp);
}
if(!pair)
goto next_cand;
next_cand:
lcand=TAILQ_NEXT(lcand,entry_comp);
}

View File

@ -82,8 +82,9 @@ typedef STAILQ_HEAD(nr_ice_component_head_,nr_ice_component_) nr_ice_component_h
int nr_ice_component_create(struct nr_ice_media_stream_ *stream, int component_id, nr_ice_component **componentp);
int nr_ice_component_destroy(nr_ice_component **componentp);
int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component);
int nr_ice_component_prune_candidates(nr_ice_ctx *ctx, nr_ice_component *comp);
int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp);
int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned);
int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote);
int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp, nr_ice_component *pcomp);
int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced);
int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);

View File

@ -64,6 +64,7 @@ static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out);
static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out);
#endif /* USE_TURN */
static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg);
static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand);
int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out)
{
@ -431,6 +432,7 @@ int nr_ice_ctx_destroy(nr_ice_ctx **ctxp)
return(0);
(*ctxp)->done_cb=0;
(*ctxp)->trickle_cb=0;
NR_ASYNC_SCHEDULE(nr_ice_ctx_destroy_cb,*ctxp);
@ -441,13 +443,38 @@ int nr_ice_ctx_destroy(nr_ice_ctx **ctxp)
void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg)
{
nr_ice_ctx *ctx=cb_arg;
int r,_status;
nr_ice_candidate *cand=cb_arg;
nr_ice_ctx *ctx;
assert(cb_arg);
if (!cb_arg)
return;
ctx = cand->ctx;
/* r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Candidate %s %s",ctx->label,
cand->label, cand->state==NR_ICE_CAND_STATE_INITIALIZED?"INITIALIZED":"FAILED");
*/
ctx->uninitialized_candidates--;
if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
int was_pruned = 0;
if (r=nr_ice_component_maybe_prune_candidate(ctx, cand->component,
cand, &was_pruned)) {
r_log(LOG_ICE, LOG_NOTICE, "ICE(%s): Problem pruning candidates",ctx->label);
}
/* If we are initialized, the candidate wasn't pruned,
and we have a trickle ICE callback fire the callback */
if (ctx->trickle_cb && !was_pruned) {
ctx->trickle_cb(ctx->trickle_cb_arg, ctx, cand->stream, cand->component_id, cand);
if (r==nr_ice_ctx_pair_new_trickle_candidates(ctx, cand)) {
r_log(LOG_ICE,LOG_ERR, "ICE(%s): All could not pair new trickle candidate",ctx->label);
/* But continue */
}
}
}
if(ctx->uninitialized_candidates==0){
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label);
ctx->state=NR_ICE_STATE_INITIALIZED;
@ -463,6 +490,28 @@ void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg)
}
}
static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand)
{
int r,_status;
nr_ice_peer_ctx *pctx;
pctx=STAILQ_FIRST(&ctx->peers);
while(pctx){
if (pctx->state == NR_ICE_PEER_STATE_PAIRED) {
r = nr_ice_peer_ctx_pair_new_trickle_candidate(ctx, pctx, cand);
if (r)
ABORT(r);
}
pctx=STAILQ_NEXT(pctx,entry);
}
_status=0;
abort:
return(_status);
}
#define MAXADDRS 100 // Ridiculously high
int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg)
{
@ -689,3 +738,11 @@ int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx)
return(0);
}
int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg)
{
ctx->trickle_cb = cb;
ctx->trickle_cb_arg = cb_arg;
return 0;
}

View File

@ -89,6 +89,9 @@ typedef struct nr_ice_ctx_ nr_ice_ctx;
typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx;
typedef struct nr_ice_candidate_ nr_ice_candidate;
typedef struct nr_ice_cand_pair_ nr_ice_cand_pair;
typedef void (*nr_ice_trickle_candidate_cb) (void *cb_arg,
nr_ice_ctx *ctx, nr_ice_media_stream *stream, int component_id,
nr_ice_candidate *candidate);
#include "ice_socket.h"
#include "ice_component.h"
@ -144,6 +147,9 @@ struct nr_ice_ctx_ {
NR_async_cb done_cb;
void *cb_arg;
nr_ice_trickle_candidate_cb trickle_cb;
void *trickle_cb_arg;
};
int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp);
@ -166,6 +172,9 @@ int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers, int
int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int ct);
int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver);
int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *prioritizer);
int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg);
#define NR_ICE_MAX_ATTRIBUTE_SIZE 256
extern int LOG_ICE;

View File

@ -140,14 +140,13 @@ int nr_ice_media_stream_initialize(nr_ice_ctx *ctx, nr_ice_media_stream *stream)
return(_status);
}
#define MAX_ATTRIBUTE_SIZE 256
int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp, int *attrctp)
{
int attrct=0;
nr_ice_component *comp;
char **attrs=0;
int index=0;
nr_ice_candidate *cand;
int r,_status;
*attrctp=0;
@ -156,12 +155,15 @@ int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attr
comp=STAILQ_FIRST(&stream->components);
while(comp){
if (comp->state != NR_ICE_COMPONENT_DISABLED) {
if(r=nr_ice_component_prune_candidates(stream->ctx,comp))
ABORT(r);
cand = TAILQ_FIRST(&comp->candidates);
while(cand){
if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
++attrct;
}
attrct+=comp->candidate_ct;
cand = TAILQ_NEXT(cand, entry_comp);
}
}
comp=STAILQ_NEXT(comp,entry);
}
@ -174,7 +176,7 @@ int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attr
if(!(attrs=RCALLOC(sizeof(char *)*attrct)))
ABORT(R_NO_MEMORY);
for(index=0;index<attrct;index++){
if(!(attrs[index]=RMALLOC(MAX_ATTRIBUTE_SIZE)))
if(!(attrs[index]=RMALLOC(NR_ICE_MAX_ATTRIBUTE_SIZE)))
ABORT(R_NO_MEMORY);
}
@ -187,12 +189,15 @@ int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attr
cand=TAILQ_FIRST(&comp->candidates);
while(cand){
assert(index < attrct);
if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
assert(index < attrct);
if(r=nr_ice_format_candidate_attribute(cand, attrs[index],MAX_ATTRIBUTE_SIZE))
ABORT(r);
index++;
if(r=nr_ice_format_candidate_attribute(cand, attrs[index],NR_ICE_MAX_ATTRIBUTE_SIZE))
ABORT(r);
index++;
}
cand=TAILQ_NEXT(cand,entry_comp);
}
@ -243,15 +248,17 @@ int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int c
*/
cand=TAILQ_FIRST(&comp->candidates);
while(cand){
if (!best_cand) {
best_cand = cand;
}
else {
if (best_cand->type < cand->type) {
if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
if (!best_cand) {
best_cand = cand;
} else if (best_cand->type == cand->type) {
if (best_cand->priority < cand->priority)
}
else {
if (best_cand->type < cand->type) {
best_cand = cand;
} else if (best_cand->type == cand->type) {
if (best_cand->priority < cand->priority)
best_cand = cand;
}
}
}
@ -811,6 +818,21 @@ int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *
return(0);
}
int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand)
{
int r,_status;
nr_ice_component *comp;
if ((r=nr_ice_media_stream_find_component(pstream, cand->component_id, &comp)))
ABORT(R_NOT_FOUND);
if (r=nr_ice_component_pair_candidate(pctx, comp, cand, 1))
ABORT(r);
_status=0;
abort:
return(_status);
}
int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id)
{

View File

@ -95,7 +95,7 @@ int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, i
int
nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr);
int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id);
int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand);
#ifdef __cplusplus
}

View File

@ -56,6 +56,8 @@ int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label,
if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx))))
ABORT(R_NO_MEMORY);
pctx->state = NR_ICE_PEER_STATE_UNPAIRED;
if(!(pctx->label=r_strdup(label)))
ABORT(R_NO_MEMORY);
@ -257,25 +259,14 @@ int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str
int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate)
{
/* First need to find the stream. Because we don't have forward pointers,
iterate through all the peer streams to find one that matches us */
nr_ice_media_stream *pstream;
int r,_status;
int needs_pairing = 0;
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) parsing trickle ICE candidate %s",pctx->ctx->label,pctx->label,candidate);
pstream=STAILQ_FIRST(&pctx->peer_streams);
while(pstream) {
if (pstream->local_stream == stream)
break;
pstream = STAILQ_NEXT(pstream, entry);
}
if (!pstream) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label);
ABORT(R_NOT_FOUND);
}
r = nr_ice_peer_ctx_find_pstream(pctx, stream, &pstream);
if (r)
ABORT(r);
switch(pstream->ice_state) {
case NR_ICE_MEDIA_STREAM_UNPAIRED:
@ -354,17 +345,36 @@ int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx)
stream=STAILQ_NEXT(stream,entry);
}
pctx->state = NR_ICE_PEER_STATE_PAIRED;
_status=0;
abort:
return(_status);
}
int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand)
{
int r, _status;
nr_ice_media_stream *pstream;
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) pairing local trickle ICE candidate %s",pctx->ctx->label,pctx->label,cand->label);
if ((r = nr_ice_peer_ctx_find_pstream(pctx, cand->stream, &pstream)))
ABORT(r);
if ((r = nr_ice_media_stream_pair_new_trickle_candidate(pctx, pstream, cand)))
ABORT(r);
_status=0;
abort:
return _status;
}
int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id)
{
int r, _status;
nr_ice_media_stream *pstream;
nr_ice_component *component;
int j;
if ((r=nr_ice_peer_ctx_find_pstream(pctx, lstream, &pstream)))
ABORT(r);
@ -543,7 +553,6 @@ static void nr_ice_peer_ctx_fire_done(NR_SOCKET s, int how, void *cb_arg)
}
}
/* OK, a stream just went ready. Examine all the streams to see if we're
maybe miraculously done */
int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)

View File

@ -40,6 +40,10 @@ extern "C" {
#endif /* __cplusplus */
struct nr_ice_peer_ctx_ {
int state;
#define NR_ICE_PEER_STATE_UNPAIRED 1
#define NR_ICE_PEER_STATE_PAIRED 2
char *label;
nr_ice_ctx *ctx;
nr_ice_handler *handler;
@ -80,6 +84,7 @@ int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stre
int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp);
int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id);
int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand);
#ifdef __cplusplus
}

View File

@ -362,5 +362,10 @@ namespace CSF
of its memory.
*/
virtual Timecard *takeTimecard() = 0;
/**
Get the latest candidate.
*/
virtual std::string getCandidate() = 0;
};
};

View File

@ -152,6 +152,7 @@ public:
public: \
static classname ## Ptr wrap(handletype handle); \
static void reset(); \
static void release(handletype handle); \
private: \
friend class Wrapper<classname>; \
typedef classname ## Ptr Ptr; \
@ -169,5 +170,8 @@ public:
void classname::reset() \
{ \
getWrapper().reset(); \
}
} \
void classname::release(handletype handle) \
{ \
getWrapper().release(handle); \
}

View File

@ -34,6 +34,7 @@
extern "C" {
#include "ccsdp.h"
#include "vcm.h"
#include "cc_call_feature.h"
#include "cip_mmgr_mediadefinitions.h"
#include "cip_Sipcc_CodecMask.h"
@ -68,6 +69,7 @@ VcmSIPCCBinding * VcmSIPCCBinding::gSelf = NULL;
int VcmSIPCCBinding::gAudioCodecMask = 0;
int VcmSIPCCBinding::gVideoCodecMask = 0;
nsIThread *VcmSIPCCBinding::gMainThread = NULL;
nsIEventTarget *VcmSIPCCBinding::gSTSThread = NULL;
static mozilla::RefPtr<TransportFlow> vcmCreateTransportFlow(
sipcc::PeerConnectionImpl *pc,
@ -102,10 +104,58 @@ VcmSIPCCBinding::VcmSIPCCBinding ()
gSelf = this;
}
class VcmIceOpaque : public NrIceOpaque {
public:
VcmIceOpaque(cc_streamid_t stream_id,
cc_call_handle_t call_handle,
uint16_t level) :
stream_id_(stream_id),
call_handle_(call_handle),
level_(level) {}
virtual ~VcmIceOpaque() {}
cc_streamid_t stream_id_;
cc_call_handle_t call_handle_;
uint16_t level_;
};
VcmSIPCCBinding::~VcmSIPCCBinding ()
{
assert(gSelf);
gSelf = NULL;
// In case we're torn down while STS is still running,
// we try to dispatch to STS to disconnect all of the
// ICE signals. If STS is no longer running, this will
// harmlessly fail.
SyncRunnable::DispatchToThread(
gSTSThread,
WrapRunnable(this, &VcmSIPCCBinding::disconnect_all),
true);
}
void VcmSIPCCBinding::CandidateReady(NrIceMediaStream* stream,
const std::string& candidate)
{
// This is called on the STS thread
NrIceOpaque *opaque = stream->opaque();
MOZ_ASSERT(opaque);
VcmIceOpaque *vcm_opaque = static_cast<VcmIceOpaque *>(opaque);
CSFLogDebug(logTag, "Candidate ready on call %u, level %u",
vcm_opaque->call_handle_, vcm_opaque->level_);
char *candidate_tmp = (char *)malloc(candidate.size() + 1);
if (!candidate_tmp)
return;
sstrncpy(candidate_tmp, candidate.c_str(), candidate.size() + 1);
// Send a message to the GSM thread.
CC_CallFeature_FoundICECandidate(vcm_opaque->call_handle_,
candidate_tmp,
NULL,
vcm_opaque->level_,
NULL);
}
void VcmSIPCCBinding::setStreamObserver(StreamObserver* obs)
@ -163,11 +213,23 @@ void VcmSIPCCBinding::setMainThread(nsIThread *thread)
gMainThread = thread;
}
void VcmSIPCCBinding::setSTSThread(nsIEventTarget *thread)
{
gSTSThread = thread;
}
nsIThread* VcmSIPCCBinding::getMainThread()
{
return gMainThread;
}
void VcmSIPCCBinding::connectCandidateSignal(
NrIceMediaStream *stream)
{
stream->SignalCandidate.connect(gSelf,
&VcmSIPCCBinding::CandidateReady);
}
/* static */
AudioTermination * VcmSIPCCBinding::getAudioTermination()
{
@ -462,6 +524,12 @@ static short vcmRxAllocICE_m(cc_mcapid_t mcap_id,
return VCM_ERROR;
}
// Set the opaque so we can correlate events.
stream->SetOpaque(new VcmIceOpaque(stream_id, call_handle, level));
// Attach ourself to the candidate signal.
VcmSIPCCBinding::connectCandidateSignal(stream);
std::vector<std::string> candidates = stream->GetCandidates();
CSFLogDebug( logTag, "%s: Got %lu candidates", __FUNCTION__, candidates.size());
@ -733,6 +801,7 @@ static short vcmSetIceCandidate_m(const char *peerconnection,
return 0;
}
/* Set ice candidate for trickle ICE.
*
* This is a thunk to vcmSetIceCandidate_m
@ -792,7 +861,6 @@ static short vcmStartIceChecks_m(const char *peerconnection, cc_boolean isContro
return 0;
}
/* Start ICE checks
*
* This is a thunk to vcmStartIceChecks_m

View File

@ -10,7 +10,14 @@ extern "C"
#include "ccapi_types.h"
}
#include "sigslot.h"
class nsIThread;
class nsIEventTarget;
namespace mozilla {
class NrIceMediaStream;
};
namespace CSF
{
@ -29,13 +36,12 @@ namespace CSF
virtual void dtmfBurst(int digit, int direction, int duration) = 0;
virtual void sendIFrame(cc_call_handle_t call) = 0;
};
class VcmSIPCCBinding
class VcmSIPCCBinding : public sigslot::has_slots<>
{
public:
VcmSIPCCBinding ();
virtual ~VcmSIPCCBinding();
// The getter is only for use by the vcm_* impl functions.
void setStreamObserver(StreamObserver*);
static StreamObserver* getStreamObserver();
@ -58,13 +64,21 @@ namespace CSF
static void setMainThread(nsIThread *thread);
static nsIThread *getMainThread();
static void setSTSThread(nsIEventTarget *thread);
static void connectCandidateSignal(mozilla::NrIceMediaStream* stream);
private:
void CandidateReady(mozilla::NrIceMediaStream* stream,
const std::string& candidate);
static VcmSIPCCBinding * gSelf;
StreamObserver* streamObserver;
MediaProviderObserver *mediaProviderObserver;
static int gAudioCodecMask;
static int gVideoCodecMask;
static nsIThread *gMainThread;
static nsIEventTarget *gSTSThread;
};
}

View File

@ -122,7 +122,8 @@ static void join_waiter() {
NS_ProcessPendingEvents(PeerConnectionCtx::gMainThread);
}
nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread) {
nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread,
nsIEventTarget* stsThread) {
if (!gMainThread) {
gMainThread = mainThread;
CSF::VcmSIPCCBinding::setMainThread(gMainThread);
@ -133,6 +134,8 @@ nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread) {
#endif
}
CSF::VcmSIPCCBinding::setSTSThread(stsThread);
nsresult res;
#ifdef MOZILLA_INTERNAL_API

View File

@ -34,7 +34,7 @@ namespace sipcc {
// * The observer class that demuxes events onto individual PCs.
class PeerConnectionCtx : public CSF::CC_Observer {
public:
static nsresult InitializeGlobal(nsIThread *mainThread);
static nsresult InitializeGlobal(nsIThread *mainThread, nsIEventTarget *stsThread);
static PeerConnectionCtx* GetInstance();
static bool isActive();
static void Destroy();

View File

@ -3,6 +3,8 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <string>
#include <cstdlib>
#include <cerrno>
#include "base/histogram.h"
#include "vcm.h"
@ -139,6 +141,7 @@ public:
mCode(static_cast<PeerConnectionImpl::Error>(aInfo->getStatusCode())),
mReason(aInfo->getStatus()),
mSdpStr(),
mCandidateStr(),
mCallState(aInfo->getCallState()),
mFsmState(aInfo->getFsmState()),
mStateStr(aInfo->callStateToString(mCallState)),
@ -148,8 +151,10 @@ public:
streams = aInfo->getMediaStreams();
mRemoteStream = mPC->media()->GetRemoteStream(streams->media_stream_id);
MOZ_ASSERT(mRemoteStream);
}
if ((mCallState == CREATEOFFERSUCCESS) || (mCallState == CREATEANSWERSUCCESS)) {
} else if (mCallState == FOUNDICECANDIDATE) {
mCandidateStr = aInfo->getCandidate();
} else if ((mCallState == CREATEOFFERSUCCESS) ||
(mCallState == CREATEANSWERSUCCESS)) {
mSdpStr = aInfo->getSDP();
}
}
@ -279,6 +284,45 @@ public:
mObserver->OnAddIceCandidateError(mCode, mReason.c_str());
break;
case FOUNDICECANDIDATE:
{
size_t end_of_level = mCandidateStr.find('\t');
if (end_of_level == std::string::npos) {
MOZ_ASSERT(false);
return NS_OK;
}
std::string level = mCandidateStr.substr(0, end_of_level);
if (!level.size()) {
MOZ_ASSERT(false);
return NS_OK;
}
char *endptr;
errno = 0;
unsigned long level_long =
strtoul(level.c_str(), &endptr, 10);
if (errno || *endptr != 0 || level_long > 65535) {
/* Conversion failure */
MOZ_ASSERT(false);
return NS_OK;
}
size_t end_of_mid = mCandidateStr.find('\t', end_of_level + 1);
if (end_of_mid == std::string::npos) {
MOZ_ASSERT(false);
return NS_OK;
}
std::string mid = mCandidateStr.substr(end_of_level + 1,
end_of_mid - (end_of_level + 1));
std::string candidate = mCandidateStr.substr(end_of_mid + 1);
mObserver->OnIceCandidate(
level_long & 0xffff,
mid.c_str(),
candidate.c_str());
}
break;
case REMOTESTREAMADD:
{
DOMMediaStream* stream = nullptr;
@ -323,6 +367,7 @@ private:
PeerConnectionImpl::Error mCode;
std::string mReason;
std::string mSdpStr;
std::string mCandidateStr;
cc_call_state_t mCallState;
fsmdef_states_t mFsmState;
std::string mStateStr;
@ -346,7 +391,9 @@ PeerConnectionImpl::PeerConnectionImpl()
, mMedia(NULL)
, mNumAudioStreams(0)
, mNumVideoStreams(0)
, mHaveDataStream(false) {
, mHaveDataStream(false)
, mTrickle(true) // TODO(ekr@rtfm.com): Use pref
{
#ifdef MOZILLA_INTERNAL_API
MOZ_ASSERT(NS_IsMainThread());
#endif
@ -606,7 +653,7 @@ PeerConnectionImpl::Initialize(IPeerConnectionObserver* aObserver,
mHandle = hex;
STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx");
res = PeerConnectionCtx::InitializeGlobal(mThread);
res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread);
NS_ENSURE_SUCCESS(res, res);
PeerConnectionCtx *pcctx = PeerConnectionCtx::GetInstance();
@ -1311,7 +1358,7 @@ nsresult
PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const
{
PC_AUTO_ENTER_API_CALL_NO_CHECK();
PR_ASSERT(!assert_ice_ready || (mIceState != kIceGathering));
MOZ_ASSERT(mTrickle || !assert_ice_ready || (mIceState != kIceGathering));
if (mReadyState == kClosed)
return NS_ERROR_FAILURE;

View File

@ -386,6 +386,8 @@ private:
// Holder for error messages from parsing SDP
std::vector<std::string> mSDPParseErrorMessages;
bool mTrickle;
public:
//these are temporary until the DataChannel Listen/Connect API is removed
unsigned short listenPort;

View File

@ -107,6 +107,7 @@ typedef struct cc_call_info_t_{
unsigned int media_stream_track_id;
unsigned int media_stream_id;
cc_media_constraints_t* cc_constraints;
string_t candidate;
Timecard * timecard;
} session_data_t;

View File

@ -149,6 +149,10 @@ cc_return_t cc_invokeFeatureSDPMode(cc_call_handle_t call_handle,
callFeature.featData.ccData.info = strlib_malloc(data, strlen(data));
callFeature.featData.ccData.info1 = strlib_malloc(data1, strlen(data1));
break;
case CC_FEATURE_FOUNDICECANDIDATE:
callFeature.featData.ccData.info = strlib_malloc(data, strlen(data));
callFeature.featData.ccData.info1 = NULL;
break;
default:
callFeature.featData.ccData.info = NULL;
@ -374,6 +378,19 @@ cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle,
0, 0, NO_STREAM, (uint16_t)level, constraints, candidate, mid, tc);
}
cc_return_t CC_CallFeature_FoundICECandidate(cc_call_handle_t call_handle,
const char* candidate,
const char *mid,
cc_level_t level,
Timecard *tc) {
cc_media_constraints_t *constraints = NULL;
CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
GET_LINE_ID(call_handle), __FUNCTION__));
return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_FOUNDICECANDIDATE, JSEP_NO_ACTION,
0, 0, NO_STREAM, (uint16_t)level, constraints, candidate, mid, tc);
}
/**
* Initiate a speed dial.
* @param call handle

View File

@ -747,6 +747,24 @@ cc_string_t CCAPI_CallInfo_getSDP(cc_callinfo_ref_t handle){
return strlib_empty();
}
/**
* get candidate for trickle ICE
* @param handle - call handle
* @return sdp
*/
cc_string_t CCAPI_CallInfo_getCandidate(cc_callinfo_ref_t handle){
static const char *fname="CCAPI_CallInfo_getCandiate";
session_data_t *data = (session_data_t *)handle;
CCAPP_DEBUG(DEB_F_PREFIX"Entering", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
if (data){
CCAPP_DEBUG(DEB_F_PREFIX"returned %s", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->candidate);
return data->candidate;
}
return strlib_empty();
}
/**
* get status code from internal JSEP functions
* @param handle - call handle

View File

@ -710,6 +710,13 @@ processSessionEvent (line_t line_id, callid_t call_id, unsigned int event, sdp_d
sstrncpy(featdata.candidate.mid, data1, sizeof(featdata.candidate.mid)-1);
cc_int_feature2(CC_MSG_ADDCANDIDATE, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_ADDICECANDIDATE, &featdata, timecard);
break;
case CC_FEATURE_FOUNDICECANDIDATE:
STAMP_TIMECARD(timecard, "Processing found candidate event");
featdata.candidate.level = ccData.level;
sstrncpy(featdata.candidate.candidate, data, sizeof(featdata.candidate.candidate)-1);
sstrncpy(featdata.candidate.mid, data1, sizeof(featdata.candidate.mid)-1);
cc_int_feature2(CC_MSG_FOUNDCANDIDATE, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_FOUNDICECANDIDATE, &featdata, timecard);
break;
case CC_FEATURE_DIALSTR:
if (CheckAndGetAvailableLine(&line_id, &call_id) == TRUE) {
getDigits(data, digits, sizeof(digits));
@ -1061,7 +1068,8 @@ session_data_t * getDeepCopyOfSessionData(session_data_t *data)
newData->plcd_number = strlib_copy(data->plcd_number);
newData->status = strlib_copy(data->status);
newData->sdp = strlib_copy(data->sdp);
newData->candidate = data->candidate ?
strlib_copy(data->candidate) : strlib_empty();
/* The timecard can have only one owner */
newData->timecard = data->timecard;
data->timecard = NULL;
@ -1085,6 +1093,7 @@ session_data_t * getDeepCopyOfSessionData(session_data_t *data)
newData->plcd_number = strlib_empty();
newData->status = strlib_empty();
newData->sdp = strlib_empty();
newData->candidate = strlib_empty();
newData->timecard = NULL;
calllogger_init_call_log(&newData->call_log);
}
@ -1132,6 +1141,9 @@ void cleanSessionData(session_data_t *data)
data->status = strlib_empty();
strlib_free(data->sdp);
data->sdp = strlib_empty();
if (data->candidate)
strlib_free(data->candidate);
data->candidate = strlib_empty();
data->timecard = NULL;
calllogger_free_call_log(&data->call_log);
}
@ -1450,6 +1462,7 @@ static void ccappUpdateSessionData (session_update_t *sessUpd)
sessUpd->eventID == UPDATE_LOCAL_DESC ||
sessUpd->eventID == UPDATE_REMOTE_DESC ||
sessUpd->eventID == ICE_CANDIDATE_ADD ||
sessUpd->eventID == ICE_CANDIDATE_FOUND ||
sessUpd->eventID == REMOTE_STREAM_ADD ) {
data->attr = sessUpd->update.ccSessionUpd.data.state_data.attr;
data->inst = sessUpd->update.ccSessionUpd.data.state_data.inst;
@ -1832,6 +1845,12 @@ static void ccappUpdateSessionData (session_update_t *sessUpd)
case UPDATE_LOCAL_DESC:
case UPDATE_REMOTE_DESC:
case ICE_CANDIDATE_ADD:
case ICE_CANDIDATE_FOUND:
if (sessUpd->update.ccSessionUpd.data.state_data.extra) {
if (sessUpd->eventID == ICE_CANDIDATE_FOUND) {
data->candidate = sessUpd->update.ccSessionUpd.data.state_data.extra;
}
}
data->sdp = sessUpd->update.ccSessionUpd.data.state_data.sdp;
/* Fall through to the next case... */
case REMOTE_STREAM_ADD:

View File

@ -1556,6 +1556,7 @@ static void post_message_helper(group_call_event_t eventId,
callid_t nCallId,
uint16_t call_instance_id,
string_t sdp,
string_t extra,
Timecard *timecard,
pc_error error,
const char *format,
@ -1580,6 +1581,7 @@ static void post_message_helper(group_call_event_t eventId,
msg.update.ccSessionUpd.data.state_data.inst = call_instance_id;
msg.update.ccSessionUpd.data.state_data.line_id = nLine;
msg.update.ccSessionUpd.data.state_data.sdp = sdp;
msg.update.ccSessionUpd.data.state_data.extra = extra;
msg.update.ccSessionUpd.data.state_data.cause = error;
msg.update.ccSessionUpd.data.state_data.timecard = timecard;
@ -1627,7 +1629,7 @@ void ui_create_offer(call_events event,
va_start(ap, format);
post_message_helper(CREATE_OFFER, event, new_state, nLine, nCallID,
call_instance_id, sdp, timecard, error, format, ap);
call_instance_id, sdp, NULL, timecard, error, format, ap);
va_end(ap);
return;
@ -1655,7 +1657,7 @@ void ui_create_answer(call_events event,
va_start(ap, format);
post_message_helper(CREATE_ANSWER, event, new_state, nLine, nCallID,
call_instance_id, sdp, timecard, error, format, ap);
call_instance_id, sdp, NULL, timecard, error, format, ap);
va_end(ap);
return;
@ -1683,7 +1685,7 @@ void ui_set_local_description(call_events event,
va_start(ap, format);
post_message_helper(SET_LOCAL_DESC, event, new_state, nLine, nCallID,
call_instance_id, sdp, timecard, error, format, ap);
call_instance_id, sdp, NULL, timecard, error, format, ap);
va_end(ap);
return;
@ -1711,7 +1713,7 @@ void ui_set_remote_description(call_events event,
va_start(ap, format);
post_message_helper(SET_REMOTE_DESC, event, new_state, nLine, nCallID,
call_instance_id, sdp, timecard, error, format, ap);
call_instance_id, sdp, NULL, timecard, error, format, ap);
va_end(ap);
return;
@ -1740,7 +1742,7 @@ void ui_update_local_description(call_events event,
va_start(ap, format);
post_message_helper(UPDATE_LOCAL_DESC, event, new_state, nLine, nCallID,
call_instance_id, sdp, timecard, error, format, ap);
call_instance_id, sdp, NULL, timecard, error, format, ap);
va_end(ap);
return;
@ -1768,7 +1770,35 @@ void ui_ice_candidate_add(call_events event,
va_start(ap, format);
post_message_helper(ICE_CANDIDATE_ADD, event, new_state, nLine, nCallID,
call_instance_id, sdp, timecard, error, format, ap);
call_instance_id, sdp, NULL, timecard, error, format, ap);
va_end(ap);
}
/**
* Send data from foundIceCandidate to the UI
*
* @return none
*/
void ui_ice_candidate_found(call_events event,
fsmdef_states_t new_state,
line_t nLine,
callid_t nCallID,
uint16_t call_instance_id,
string_t sdp,
string_t candidate,
Timecard *timecard,
pc_error error,
const char *format, ...)
{
va_list ap;
TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
va_start(ap, format);
post_message_helper(ICE_CANDIDATE_FOUND, event, new_state, nLine, nCallID,
call_instance_id, sdp, candidate, timecard, error, format, ap);
va_end(ap);
}

View File

@ -37,6 +37,7 @@
#include "platform_api.h"
#include "peer_connection_types.h"
#include "prlog.h"
#include "prprf.h"
#include "sessionHash.h"
extern void update_kpmlconfig(int kpmlVal);
@ -93,6 +94,7 @@ static sm_rcs_t fsmdef_ev_setpeerconnection(sm_event_t *event);
static sm_rcs_t fsmdef_ev_addstream(sm_event_t *event);
static sm_rcs_t fsmdef_ev_removestream(sm_event_t *event);
static sm_rcs_t fsmdef_ev_addcandidate(sm_event_t *event);
static sm_rcs_t fsmdef_ev_foundcandidate(sm_event_t *event);
static sm_rcs_t fsmdef_ev_default(sm_event_t *event);
static sm_rcs_t fsmdef_ev_default_feature_ack(sm_event_t *event);
@ -202,7 +204,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_COLLECT_INFO ---------------------------------------------------- */
@ -232,7 +235,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_CALL_SENT ------------------------------------------------------- */
@ -262,7 +266,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_OUTGOING_PROCEEDING --------------------------------------------- */
@ -292,7 +297,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_KPML_COLLECT_INFO ----------------------------------------------- */
@ -322,7 +328,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_OUTGOING_ALERTING ----------------------------------------------- */
@ -352,7 +359,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_INCOMING_ALERTING ----------------------------------------------- */
@ -382,7 +390,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_CONNECTING ------------------------------------------------------ */
@ -412,7 +421,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_JOINING --------------------------------------------------------- */
@ -442,7 +452,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_CONNECTED ------------------------------------------------------- */
@ -472,7 +483,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_CONNECTED_MEDIA_PEND ------------------------------------------- */
@ -502,7 +514,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_RELEASING ------------------------------------------------------- */
@ -532,7 +545,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_HOLD_PENDING ---------------------------------------------------- */
@ -562,7 +576,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_HOLDING --------------------------------------------------------- */
@ -592,7 +607,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_RESUME_PENDING -------------------------------------------------- */
@ -622,7 +638,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_PRESERVED ------------------------------------------------------ */
@ -652,7 +669,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
@ -683,7 +701,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_foundcandidate
},
/* FSMDEF_S_HAVE_LOCAL_OFFER ----------------------------------------------- */
@ -713,8 +732,9 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default /* Reject lame-duck
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default, /* Reject lame-duck
candidates */
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_foundcandidate
},
/* FSMDEF_S_HAVE_REMOTE_OFFER ---------------------------------------------- */
@ -744,7 +764,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
},
/* FSMDEF_S_HAVE_LOCAL_PRANSWER -------------------------------------------- */
@ -774,7 +795,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_foundcandidate
},
/* FSMDEF_S_HAVE_REMOTE_PRANSWER ------------------------------------------- */
@ -804,7 +826,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_foundcandidate
},
/* FSMDEF_S_CLOSED --------------------------------------------------------- */
@ -834,7 +857,8 @@ static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
/* CC_MSG_ADDSTREAM */ fsmdef_ev_default,
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_default,
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_default,
/* CC_MSG_FOUNDCANDIDATE */ fsmdef_ev_default
}
};
@ -1302,6 +1326,8 @@ fsmdef_init_dcb (fsmdef_dcb_t *dcb, callid_t call_id,
dcb->digest_alg[0] = '\0';
dcb->digest[0] = '\0';
sll_lite_init(&dcb->candidate_list);
}
@ -1351,6 +1377,9 @@ fsmdef_free_dcb (fsmdef_dcb_t *dcb)
/* clean media list */
gsmsdp_clean_media_list(dcb);
/* clean candidate list */
gsmsdp_clean_candidate_list(dcb);
gsmsdp_free(dcb);
fsmdef_init_dcb(dcb, CC_NO_CALL_ID, FSMDEF_CALL_TYPE_NONE, NULL,
@ -2455,6 +2484,14 @@ fsmdef_ev_default (sm_event_t *event)
fsmdef_state_name(fcb->state));
break;
case CC_MSG_FOUNDCANDIDATE:
ui_ice_candidate_found(evFoundIceCandidateError, fcb->state, msg->line,
msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
NULL, msg->timecard,
PC_INVALID_STATE, "Cannot add found ICE candidate in state %s",
fsmdef_state_name(fcb->state));
break;
case CC_MSG_ADDSTREAM:
case CC_MSG_REMOVESTREAM:
/* This shouldn't happen, since PeerConnection should check
@ -3170,6 +3207,9 @@ fsmdef_ev_createoffer (sm_event_t *event) {
return (SM_RC_END);
}
/* clean candidate list, since we are about to return the candidates */
gsmsdp_clean_candidate_list(dcb);
dcb->inbound = FALSE;
if (msg->data.session.constraints) {
@ -3331,6 +3371,9 @@ fsmdef_ev_createanswer (sm_event_t *event) {
return (SM_RC_END);
}
/* clean candidate list, since we are about to return the candidates */
gsmsdp_clean_candidate_list(dcb);
dcb->inbound = TRUE;
if (msg->data.session.constraints) {
@ -3459,6 +3502,7 @@ fsmdef_ev_setlocaldesc(sm_event_t *event) {
cc_causes_t lsm_rc;
char *local_sdp = NULL;
uint32_t local_sdp_len = 0;
fsmdef_candidate_t *candidate = NULL;
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
@ -3603,6 +3647,21 @@ fsmdef_ev_setlocaldesc(sm_event_t *event) {
msg->call_id, dcb->caller_id.call_instance_id,
strlib_malloc(local_sdp,-1), msg->timecard, PC_NO_ERROR, NULL);
/* If we have pending candidates flush them too */
while (TRUE) {
/* unlink head and free the media */
candidate = (fsmdef_candidate_t *)sll_lite_unlink_head(&dcb->candidate_list);
if (candidate) {
ui_ice_candidate_found(evFoundIceCandidate, fcb->state, line, call_id,
dcb->caller_id.call_instance_id, strlib_malloc(local_sdp,-1),
candidate->candidate, /* Transfer ownership */
NULL, PC_NO_ERROR, NULL);
free(candidate);
} else {
break;
}
}
free(local_sdp);
return (SM_RC_END);
}
@ -4122,6 +4181,117 @@ fsmdef_ev_addcandidate(sm_event_t *event) {
return (SM_RC_END);
}
static sm_rcs_t
fsmdef_ev_foundcandidate(sm_event_t *event) {
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
fsmdef_dcb_t *dcb = fcb->dcb;
cc_causes_t cause = CC_CAUSE_NORMAL;
cc_feature_t *msg = (cc_feature_t *) event->msg;
int sdpmode = 0;
short vcm_res;
uint16_t level;
line_t line = msg->line;
callid_t call_id = msg->call_id;
char *local_sdp = 0;
uint32_t local_sdp_len = 0;
string_t candidate = 0;
char candidate_tmp[CANDIDATE_SIZE + 32]; /* Sort of arbitrary */
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
if (!dcb) {
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
ui_ice_candidate_add(evAddIceCandidateError, fcb->state, line, call_id,
0, strlib_empty(), msg->timecard, PC_INTERNAL_ERROR,
"DCB has not been created.");
return SM_RC_CLEANUP;
}
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
if (!sdpmode) {
MOZ_CRASH();
}
MOZ_ASSERT(dcb->sdp && dcb->sdp->src_sdp);
if (!dcb->sdp || !dcb->sdp->src_sdp) {
FSM_DEBUG_SM(DEB_F_PREFIX"Has the "
"local description been set yet?\n",
DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
ui_ice_candidate_found(evFoundIceCandidateError, fcb->state, line, call_id,
dcb->caller_id.call_instance_id, strlib_empty(),
NULL, msg->timecard,
PC_INVALID_STATE, "Cannot add found ICE candidates without"
"local SDP.");
return SM_RC_END;
}
/* Store the candidate in the SDP for future reference */
level = msg->data.candidate.level;
gsmsdp_set_ice_attribute (SDP_ATTR_ICE_CANDIDATE, level,
dcb->sdp->src_sdp,
(char *)msg->data.candidate.candidate);
local_sdp = sipsdp_write_to_buf(dcb->sdp->src_sdp, &local_sdp_len);
if (!local_sdp) {
ui_ice_candidate_found(evFoundIceCandidateError, fcb->state, line, call_id,
dcb->caller_id.call_instance_id, strlib_empty(), NULL,
msg->timecard,
PC_INTERNAL_ERROR, "Could not serialize new SDP after adding ICE "
"candidate.");
return (SM_RC_END);
}
/* Distinguish between the following two cases:
1. CreateOffer() has been called but SetLocalDesc() has not.
2. We are mid-call.
Both of these are in state STABLE but only in one do we
pass up trickle candidates. In the other we buffer them
and send them later.
*/
/* Smuggle the entire candidate structure in a string */
PR_snprintf(candidate_tmp, sizeof(candidate_tmp), "%d\t%s\t%s",
msg->data.candidate.level,
(char *)msg->data.candidate.mid,
(char *)msg->data.candidate.candidate);
if (fcb->state == FSMDEF_S_STABLE) {
if (!dcb->sdp->dest_sdp) {
fsmdef_candidate_t *buffered_cand = NULL;
FSM_DEBUG_SM(DEB_F_PREFIX"dcb->sdp->dest_sdp is null."
"assuming CreateOffer called but not SetLocal...\n",
DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
buffered_cand = (fsmdef_candidate_t *)cpr_malloc(sizeof(fsmdef_candidate_t));
if (!buffered_cand)
return SM_RC_END;
buffered_cand->candidate = strlib_malloc(candidate_tmp, -1);
if (sll_lite_link_head(&dcb->candidate_list,
(sll_lite_node_t *)buffered_cand) != SLL_LITE_RET_SUCCESS)
return SM_RC_END;
/* Don't notify upward */
return SM_RC_END;
}
}
ui_ice_candidate_found(evFoundIceCandidate, fcb->state, line, call_id,
dcb->caller_id.call_instance_id, strlib_malloc(local_sdp,-1),
strlib_malloc(candidate_tmp, -1),
msg->timecard, PC_NO_ERROR, NULL);
return SM_RC_END;
}
static void
fsmdef_check_active_feature (fsmdef_dcb_t *dcb, cc_features_t ftr_id)
{

View File

@ -7327,3 +7327,31 @@ gsmsdp_find_level_from_mid(fsmdef_dcb_t * dcb_p, const char * mid, uint16_t *lev
}
return CC_CAUSE_VALUE_NOT_FOUND;
}
/**
* The function performs cleaning candidate list of a given call. It walks
* through the list and deallocates each candidate entry.
*
* @param[in]dcb - pointer to fsmdef_def_t for the dcb whose
* media list to be cleaned.
*
* @return none
*
* @pre (dcb not_eq NULL)
*/
void gsmsdp_clean_candidate_list (fsmdef_dcb_t *dcb_p)
{
fsmdef_candidate_t *candidate = NULL;
while (TRUE) {
/* unlink head and free the media */
candidate = (fsmdef_candidate_t *)sll_lite_unlink_head(&dcb_p->candidate_list);
if (candidate) {
strlib_free(candidate->candidate);
free(candidate);
} else {
break;
}
}
}

View File

@ -429,6 +429,7 @@ typedef struct {
char digest_alg[FSMDEF_MAX_DIGEST_ALG_LEN];
char digest[FSMDEF_MAX_DIGEST_LEN];
sll_lite_list_t candidate_list;
} fsmdef_dcb_t;
typedef enum fsm_types_t_ {
@ -498,6 +499,11 @@ typedef enum fsmxfr_modes_t_ {
FSMXFR_MODE_TARGET
} fsmxfr_modes_t;
typedef struct fsmdef_candidate_t_ {
sll_lite_node_t node; /* link node, must be first member of struct */
string_t candidate; /* the candidate value */
} fsmdef_candidate_t;
struct fsmxfr_xcb_t_;
typedef struct fsmxfr_xcb_t_ {
cc_srcs_t xfr_orig;

View File

@ -141,6 +141,7 @@ fsmdef_media_t* gsmsdp_find_media_by_media_type(fsmdef_dcb_t *dcb, sdp_media_e
extern void gsmsdp_set_ice_attribute (sdp_attr_e sdp_attr, uint16_t level,
void *sdp_p, char *ice_attrib);
extern void gsmsdp_clean_candidate_list(fsmdef_dcb_t *dcb);
#endif

View File

@ -103,6 +103,7 @@ typedef enum {
CC_FEATURE_ADDSTREAM,
CC_FEATURE_REMOVESTREAM,
CC_FEATURE_ADDICECANDIDATE,
CC_FEATURE_FOUNDICECANDIDATE,
CC_FEATURE_MAX
} group_cc_feature_t;
@ -168,6 +169,7 @@ static const char *const cc_feature_names[] = {
"ADDSTREAM",
"REMOVESTREAM",
"ADDICECANDIDATE",
"FOUNDICECANDIDATE",
"MAX"
};
@ -241,6 +243,7 @@ typedef enum cc_msgs_t_ {
CC_MSG_ADDSTREAM,
CC_MSG_REMOVESTREAM,
CC_MSG_ADDCANDIDATE,
CC_MSG_FOUNDCANDIDATE,
CC_MSG_AUDIT_ACK,
CC_MSG_OPTIONS,
CC_MSG_OPTIONS_ACK,
@ -280,6 +283,7 @@ static const char *const cc_msg_names[] = {
"ADDSTREAM",
"REMOVESTREAM",
"ADDCANDIDATE",
"FOUNDCANDIDATE",
"AUDIT_ACK",
"OPTIONS",
"OPTIONS_ACK",

View File

@ -223,7 +223,8 @@ typedef enum {
UPDATE_LOCAL_DESC,
UPDATE_REMOTE_DESC,
REMOTE_STREAM_ADD,
ICE_CANDIDATE_ADD
ICE_CANDIDATE_ADD,
ICE_CANDIDATE_FOUND
} group_call_event_t;
/* File Player Session Events */

View File

@ -66,6 +66,7 @@ typedef struct {
int cause;
string_t reason_text;
string_t sdp;
string_t extra;
unsigned int media_stream_id;
unsigned int media_stream_track_id;
Timecard * timecard;

View File

@ -44,6 +44,8 @@ typedef enum {
evOnRemoteStreamAdd = REMOTESTREAMADD,
evAddIceCandidate = ADDICECANDIDATE,
evAddIceCandidateError = ADDICECANDIDATEERROR,
evFoundIceCandidate = FOUNDICECANDIDATE,
evFoundIceCandidateError = FOUNDICECANDIDATEERROR,
evMaxEvent
} call_events;
@ -240,6 +242,17 @@ void ui_ice_candidate_add(call_events event,
pc_error error,
const char *format, ...);
void ui_ice_candidate_found(call_events event,
fsmdef_states_t new_state,
line_t nLine,
callid_t nCallID,
uint16_t call_instance_id,
string_t sdp,
string_t candidate,
Timecard *timecard,
pc_error error,
const char *format, ...);
void ui_on_remote_stream_added(call_events event,
fsmdef_states_t new_state,
line_t nLine,

View File

@ -186,6 +186,11 @@ cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle,
const char *mid,
cc_level_t level,
Timecard *tc);
cc_return_t CC_CallFeature_FoundICECandidate(cc_call_handle_t call_handle,
const char* candidate,
const char *mid,
cc_level_t level,
Timecard *tc);
/**
* Initiate a speed dial.

View File

@ -289,6 +289,8 @@ typedef enum {
REMOTESTREAMADD,
ADDICECANDIDATE,
ADDICECANDIDATEERROR,
FOUNDICECANDIDATE,
FOUNDICECANDIDATEERROR,
MAX_CALL_STATES
} cc_call_state_t;

View File

@ -302,6 +302,13 @@ cc_boolean CCAPI_CallInfo_isVideoMuted(cc_callinfo_ref_t handle);
*/
cc_string_t CCAPI_CallInfo_getSDP(cc_callinfo_ref_t handle);
/**
* get trickle candidate
* @param [in] handle - call info handle
* @return sdp
*/
cc_string_t CCAPI_CallInfo_getCandidate(cc_callinfo_ref_t handle);
/**
* get status code from internal JSEP functions
* @param [in] handle - call info handle

View File

@ -454,6 +454,11 @@ Timecard *CC_SIPCCCallInfo::takeTimecard()
return CCAPI_CallInfo_takeTimecard(callinfo_ref);
}
std::string CC_SIPCCCallInfo::getCandidate()
{
return CCAPI_CallInfo_getCandidate(callinfo_ref);
}
bool CC_SIPCCCallInfo::isMediaStateAvailable()
{
// for softphone it will always be possible to query the mute state and video direction

View File

@ -80,6 +80,7 @@ namespace CSF
virtual bool isAudioMuted();
virtual bool isVideoMuted();
virtual std::string getSDP();
virtual std::string getCandidate();
virtual cc_int32_t getStatusCode();
virtual MediaStreamTable* getMediaStreams() const;
virtual Timecard *takeTimecard();

View File

@ -723,6 +723,18 @@ void CC_SIPCCService::onCallEvent(ccapi_call_event_e eventType, cc_call_handle_t
call_event_getname(eventType), callPtr->toString().c_str(),
call_state_getname(infoPtr->getCallState()), CC_CallCapabilityEnum::toString(capSet).c_str());
_self->notifyCallEventObservers(eventType, callPtr.get(), infoPtr.get());
// To prevent leaking CC_SIPCCCalls, we remove them from the wrapper
// map when the call event information indicates an "on hook" event.
if (infoPtr->getCallState() == ONHOOK) {
CSFLogDebug( logTag, "Removing call info from wrapper map (handle=%u)",
handle);
CC_SIPCCCall::release(handle);
}
// Once the call info is dispatched to the observers, we never need to
// refer to it again. The next operation will contain its own unique info.
CC_SIPCCCallInfo::release(info);
}
void CC_SIPCCService::addCCObserver ( CC_Observer * observer )

View File

@ -114,6 +114,8 @@ endif
DEFINES += \
-DUSE_FAKE_MEDIA_STREAMS \
-DNR_SOCKET_IS_VOID_PTR \
-DHAVE_STRDUP \
$(NULL)
ifeq ($(OS_TARGET),Darwin)
@ -141,9 +143,35 @@ LOCAL_INCLUDES += \
-I$(topsrcdir)/media/webrtc/signaling/src/peerconnection \
-I$(topsrcdir)/media/webrtc/signaling/media-conduit\
-I$(topsrcdir)/media/webrtc/trunk/third_party/libjingle/source/ \
-I$(topsrcdir)/media/mtransport/third_party/nICEr/src/ice \
-I$(topsrcdir)/media/mtransport/third_party/nICEr/src/net \
-I$(topsrcdir)/media/mtransport/third_party/nICEr/src/stun \
-I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/share \
-I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/libekr \
-I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/log \
-I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/registry \
-I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/stats \
-I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/plugin \
-I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/event \
-I$(topsrcdir)/xpcom/base/ \
$(NULL)
ifneq (,$(filter Darwin DragonFly FreeBSD NetBSD OpenBSD,$(OS_TARGET)))
LOCAL_INCLUDES += -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/darwin/include
ifneq (,$(filter DragonFly FreeBSD NetBSD OpenBSD,$(OS_TARGET)))
LOCAL_INCLUDES += -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/generic/include
endif
endif
ifeq ($(OS_TARGET), Linux)
LOCAL_INCLUDES += -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/linux/include
endif
ifeq ($(OS_TARGET), Android)
LOCAL_INCLUDES += -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/android/include
endif
ifneq ($(OS_TARGET),WINNT)
ifneq (gonk,$(MOZ_WIDGET_TOOLKIT))
ifdef JS_SHARED_LIBRARY

View File

@ -40,7 +40,8 @@ static bool SetupGlobalThread() {
return false;
gThread = thread;
sipcc::PeerConnectionCtx::InitializeGlobal(gThread);
sipcc::PeerConnectionCtx::InitializeGlobal(gThread,
test_utils->sts_target());
}
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -230,6 +230,7 @@ pref("media.navigator.video.default_minfps",10);
pref("media.peerconnection.enabled", true);
pref("media.navigator.permission.disabled", false);
pref("media.peerconnection.default_iceservers", "[{\"url\": \"stun:23.21.150.121\"}]");
pref("media.peerconnection.trickle_ice", true);
pref("media.peerconnection.use_document_iceservers", true);
// These values (aec, agc, and noice) are from media/webrtc/trunk/webrtc/common_types.h
// kXxxUnchanged = 0, kXxxDefault = 1, and higher values are specific to each

View File

@ -27,6 +27,7 @@ from ..frontend.data import (
GeneratedWebIDLFile,
IPDLFile,
LocalInclude,
PreprocessedTestWebIDLFile,
PreprocessedWebIDLFile,
Program,
SandboxDerived,
@ -266,6 +267,7 @@ class RecursiveMakeBackend(CommonBackend):
self._webidl_sources = set()
self._generated_events_webidl_sources = set()
self._test_webidl_sources = set()
self._preprocessed_test_webidl_sources = set()
self._preprocessed_webidl_sources = set()
self._generated_webidl_sources = set()
@ -371,6 +373,11 @@ class RecursiveMakeBackend(CommonBackend):
obj.basename))
# Test WebIDL files are not exported.
elif isinstance(obj, PreprocessedTestWebIDLFile):
self._preprocessed_test_webidl_sources.add(mozpath.join(obj.srcdir,
obj.basename))
# Test WebIDL files are not exported.
elif isinstance(obj, GeneratedWebIDLFile):
self._generated_webidl_sources.add(mozpath.join(obj.srcdir,
obj.basename))
@ -575,6 +582,8 @@ class RecursiveMakeBackend(CommonBackend):
webidls.write('generated_events_webidl_files += %s\n' % os.path.basename(webidl))
for webidl in sorted(self._test_webidl_sources):
webidls.write('test_webidl_files += %s\n' % os.path.basename(webidl))
for webidl in sorted(self._preprocessed_test_webidl_sources):
webidls.write('preprocessed_test_webidl_files += %s\n' % os.path.basename(webidl))
for webidl in sorted(self._generated_webidl_sources):
webidls.write('generated_webidl_files += %s\n' % os.path.basename(webidl))
for webidl in sorted(self._preprocessed_webidl_sources):

View File

@ -235,6 +235,19 @@ class TestWebIDLFile(SandboxDerived):
self.basename = path
class PreprocessedTestWebIDLFile(SandboxDerived):
"""Describes an individual test-only .webidl source file that requires
preprocessing."""
__slots__ = (
'basename',
)
def __init__(self, sandbox, path):
SandboxDerived.__init__(self, sandbox)
self.basename = path
class PreprocessedWebIDLFile(SandboxDerived):
"""Describes an individual .webidl source file that requires preprocessing."""

View File

@ -26,6 +26,7 @@ from .data import (
GeneratedWebIDLFile,
IPDLFile,
LocalInclude,
PreprocessedTestWebIDLFile,
PreprocessedWebIDLFile,
Program,
ReaderSummary,
@ -189,6 +190,7 @@ class TreeMetadataEmitter(LoggingMixin):
('GENERATED_WEBIDL_FILES', GeneratedWebIDLFile),
('IPDL_SOURCES', IPDLFile),
('LOCAL_INCLUDES', LocalInclude),
('PREPROCESSED_TEST_WEBIDL_FILES', PreprocessedTestWebIDLFile),
('PREPROCESSED_WEBIDL_FILES', PreprocessedWebIDLFile),
('TEST_WEBIDL_FILES', TestWebIDLFile),
('WEBIDL_FILES', WebIDLFile),
@ -237,7 +239,7 @@ class TreeMetadataEmitter(LoggingMixin):
path = mozpath.normpath(mozpath.join(sandbox['SRCDIR'], manifest_path))
manifest_dir = mozpath.dirname(path)
manifest_reldir = mozpath.dirname(mozpath.relpath(path,
self.config.topsrcdir))
sandbox['TOPSRCDIR']))
try:
m = manifestparser.TestManifest(manifests=[path], strict=True)

View File

@ -444,6 +444,13 @@ VARIABLES = {
These will be generated from some other files.
"""),
'PREPROCESSED_TEST_WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
"""Preprocessed test WebIDL source files.
These will be preprocessed, then parsed and converted to .cpp
and .h files if tests are enabled.
"""),
'PREPROCESSED_WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
"""Preprocessed WebIDL source files.

View File

@ -198,14 +198,14 @@
"dom/imptests/editing/conformancetest/test_runtest.html": "",
"dom/imptests/editing/selecttest/test_addRange.html": "bug 775227",
"dom/imptests/html/webgl": "WebGL",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-cloneContents.html": "bug 775227",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-compareBoundaryPoints.html": "bug 775227",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-deleteContents.html": "bug 775227",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-extractContents.html": "bug 775227",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-insertNode.html": "bug 775227",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-mutations.html": "bug 775227",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-set.html": "bug 775227",
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-surroundContents.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-cloneContents.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-compareBoundaryPoints.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-deleteContents.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-extractContents.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-insertNode.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-mutations.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-set.html": "bug 775227",
"dom/imptests/html/dom/ranges/test_Range-surroundContents.html": "bug 775227",
"dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html": "bug 775227",
"dom/indexedDB/ipc/test_ipc.html": "bug 783513",
"dom/indexedDB/test/test_third_party.html": "TIMED_OUT",

View File

@ -334,6 +334,7 @@ typedef NSInteger NSEventGestureAxis;
- (void)setGLContext:(NSOpenGLContext *)aGLContext;
- (void)preRender:(NSOpenGLContext *)aGLContext;
- (void)postRender:(NSOpenGLContext *)aGLContext;
- (BOOL)isCoveringTitlebar;
@ -537,6 +538,7 @@ public:
virtual void PrepareWindowEffects() MOZ_OVERRIDE;
virtual void CleanupWindowEffects() MOZ_OVERRIDE;
virtual void PreRender(LayerManager* aManager) MOZ_OVERRIDE;
virtual void PostRender(LayerManager* aManager) MOZ_OVERRIDE;
virtual void DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect) MOZ_OVERRIDE;
virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries);

View File

@ -2043,6 +2043,17 @@ nsChildView::PreRender(LayerManager* aManager)
[(ChildView*)mView preRender:glContext];
}
void
nsChildView::PostRender(LayerManager* aManager)
{
nsAutoPtr<GLManager> manager(GLManager::CreateGLManager(aManager));
if (!manager) {
return;
}
NSOpenGLContext *glContext = (NSOpenGLContext *)manager->gl()->GetNativeData(GLContext::NativeGLContext);
[(ChildView*)mView postRender:glContext];
}
void
nsChildView::DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect)
{
@ -2885,6 +2896,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
[aGLContext setView:self];
[aGLContext update];
CGLLockContext((CGLContextObj)[aGLContext CGLContextObj]);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
-(void)postRender:(NSOpenGLContext *)aGLContext
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
CGLUnlockContext((CGLContextObj)[aGLContext CGLContextObj]);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
@ -3223,7 +3245,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
[super lockFocus];
if (mGLContext) {
if (mGLContext && !mUsingOMTCompositor) {
if ([mGLContext view] != self) {
[mGLContext setView:self];
}

View File

@ -1216,6 +1216,7 @@ class nsIWidget : public nsISupports {
virtual void CleanupWindowEffects() = 0;
virtual void PreRender(LayerManager* aManager) = 0;
virtual void PostRender(LayerManager* aManager) = 0;
/**
* Called before the LayerManager draws the layer tree.

View File

@ -137,6 +137,7 @@ public:
virtual void PrepareWindowEffects() {}
virtual void CleanupWindowEffects() {}
virtual void PreRender(LayerManager* aManager) {}
virtual void PostRender(LayerManager* aManager) {}
virtual void DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect) {}
virtual void DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect) {}
virtual mozilla::TemporaryRef<mozilla::gfx::DrawTarget> StartRemoteDrawing();

View File

@ -34,16 +34,19 @@ public:
, mMonitor("SyncRunnable")
{ }
void DispatchToThread(nsIEventTarget* thread)
void DispatchToThread(nsIEventTarget* thread,
bool forceDispatch = false)
{
nsresult rv;
bool on;
rv = thread->IsOnCurrentThread(&on);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_SUCCEEDED(rv) && on) {
mRunnable->Run();
return;
if (!forceDispatch) {
rv = thread->IsOnCurrentThread(&on);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_SUCCEEDED(rv) && on) {
mRunnable->Run();
return;
}
}
mozilla::MonitorAutoLock lock(mMonitor);
@ -54,10 +57,11 @@ public:
}
static void DispatchToThread(nsIEventTarget* thread,
nsIRunnable* r)
nsIRunnable* r,
bool forceDispatch = false)
{
nsRefPtr<SyncRunnable> s(new SyncRunnable(r));
s->DispatchToThread(thread);
s->DispatchToThread(thread, forceDispatch);
}
protected: