mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 865951. If the return value of a JS-implemented method or attribute is a JS-implemented interface and the returned object is not a DOM object, automatically wrap it up in an instance of that interface. r=mccr8
This does not invoke __init on the chrome object, since there are no arguments to do it with anyway. It also doesn't invoke init(), so don't do this in cases in which the chrome-side object will need to know its window.
This commit is contained in:
parent
182d5fd80d
commit
0307d91885
@ -1770,5 +1770,40 @@ Date::ToDateObject(JSContext* cx, JS::Value* vp) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
nsPIDOMWindow** window)
|
||||
{
|
||||
// Be very careful to not get tricked here.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) {
|
||||
NS_RUNTIMEABORT("Should have a chrome object here");
|
||||
}
|
||||
|
||||
// Look up the content-side object.
|
||||
JS::Rooted<JS::Value> domImplVal(cx);
|
||||
if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", domImplVal.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!domImplVal.isObject()) {
|
||||
ThrowErrorMessage(cx, MSG_NOT_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Go ahead and get the global from it. GlobalObject will handle
|
||||
// doing unwrapping as needed.
|
||||
GlobalObject global(cx, &domImplVal.toObject());
|
||||
if (global.Failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's OK if we have null here: that just means the content-side
|
||||
// object really wasn't associated with any window.
|
||||
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global.Get()));
|
||||
win.forget(window);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/CallbackObject.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -1991,6 +1993,13 @@ GetUnforgeableHolder(JSObject* aGlobal, prototypes::ID aId)
|
||||
DOM_INTERFACE_PROTO_SLOTS_BASE).toObject();
|
||||
}
|
||||
|
||||
// Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
|
||||
// interface, get the nsPIDOMWindow corresponding to the content side, if any.
|
||||
// A false return means an exception was thrown.
|
||||
bool
|
||||
GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
nsPIDOMWindow** window);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -2287,13 +2287,21 @@ class CastableObjectUnwrapper():
|
||||
called by the name in the "target" argument.
|
||||
|
||||
codeOnFailure is the code to run if unwrapping fails.
|
||||
|
||||
If isCallbackReturnValue is "JSImpl" and our descriptor is also
|
||||
JS-implemented, fall back to just creating the right object if what we
|
||||
have isn't one already.
|
||||
"""
|
||||
def __init__(self, descriptor, source, target, codeOnFailure):
|
||||
def __init__(self, descriptor, source, target, codeOnFailure,
|
||||
exceptionCode=None, isCallbackReturnValue=False):
|
||||
if not exceptionCode:
|
||||
exceptionCode = codeOnFailure
|
||||
self.substitution = { "type" : descriptor.nativeType,
|
||||
"protoID" : "prototypes::id::" + descriptor.name,
|
||||
"source" : source,
|
||||
"target" : target,
|
||||
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define() }
|
||||
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure)).define(),
|
||||
"exceptionCode" : CGIndenter(CGGeneric(exceptionCode), 4).define() }
|
||||
if descriptor.hasXPConnectImpls:
|
||||
# We don't use xpc_qsUnwrapThis because it will always throw on
|
||||
# unwrap failure, whereas we want to control whether we throw or
|
||||
@ -2311,6 +2319,26 @@ class CastableObjectUnwrapper():
|
||||
"// We should have an object, too!\n"
|
||||
"MOZ_ASSERT(objPtr);\n"
|
||||
"${target} = objPtr;").substitute(self.substitution)), 4).define()
|
||||
elif (isCallbackReturnValue == "JSImpl" and
|
||||
descriptor.interface.isJSImplemented()):
|
||||
self.substitution["codeOnFailure"] = CGIndenter(CGGeneric(string.Template(
|
||||
"// Be careful to not wrap random DOM objects here, even if\n"
|
||||
"// they're wrapped in opaque security wrappers for some reason.\n"
|
||||
"// XXXbz Wish we could check for a JS-implemented object\n"
|
||||
"// that already has a content reflection...\n"
|
||||
"if (!IsDOMObject(js::UncheckedUnwrap(${source}))) {\n"
|
||||
" nsCOMPtr<nsPIDOMWindow> ourWindow;\n"
|
||||
" if (!GetWindowForJSImplementedObject(cx, Callback(), getter_AddRefs(ourWindow))) {\n"
|
||||
"${exceptionCode}\n"
|
||||
" }\n"
|
||||
" JS::Rooted<JSObject*> jsImplSourceObj(cx, ${source});\n"
|
||||
" ${target} = new ${type}(jsImplSourceObj, ourWindow);\n"
|
||||
"} else {\n"
|
||||
"${codeOnFailure}\n"
|
||||
"}").substitute(self.substitution)), 4).define()
|
||||
else:
|
||||
self.substitution["codeOnFailure"] = CGIndenter(
|
||||
CGGeneric(self.substitution["codeOnFailure"])).define()
|
||||
|
||||
def __str__(self):
|
||||
return string.Template(
|
||||
@ -2325,11 +2353,14 @@ class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
|
||||
"""
|
||||
As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
|
||||
"""
|
||||
def __init__(self, descriptor, source, target, exceptionCode):
|
||||
def __init__(self, descriptor, source, target, exceptionCode,
|
||||
isCallbackReturnValue):
|
||||
CastableObjectUnwrapper.__init__(
|
||||
self, descriptor, source, target,
|
||||
'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");\n'
|
||||
'%s' % (descriptor.name, exceptionCode))
|
||||
'%s' % (descriptor.name, exceptionCode),
|
||||
exceptionCode,
|
||||
isCallbackReturnValue)
|
||||
|
||||
class CallbackObjectUnwrapper:
|
||||
"""
|
||||
@ -2519,9 +2550,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull]
|
||||
extended attributes on nullable callback functions will be honored.
|
||||
|
||||
If isCallbackReturnValue is true, then the declType may be adjusted to make
|
||||
it easier to return from a callback. Since that type is never directly
|
||||
observable by any consumers of the callback code, this is OK.
|
||||
If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
|
||||
adjusted to make it easier to return from a callback. Since that type is
|
||||
never directly observable by any consumers of the callback code, this is OK.
|
||||
Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
|
||||
of the FailureFatalCastableObjectUnwrapper conversion; this is used for
|
||||
implementing auto-wrapping of JS-implemented return values from a
|
||||
JS-implemented interface.
|
||||
|
||||
The return value from this function is a JSToNativeConversionInfo.
|
||||
"""
|
||||
@ -2998,9 +3033,14 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
isCallbackReturnValue)
|
||||
|
||||
# Sequences and non-worker callbacks have to hold a strong ref to the
|
||||
# thing being passed down.
|
||||
forceOwningType = (descriptor.interface.isCallback() and
|
||||
not descriptor.workers) or isMember
|
||||
# thing being passed down. Also, callback return values always end up
|
||||
# addrefing anyway, so there is no point trying to avoid it here and it
|
||||
# makes other things simpler since we can assume the return value is a
|
||||
# strong ref.
|
||||
forceOwningType = ((descriptor.interface.isCallback() and
|
||||
not descriptor.workers) or
|
||||
isMember or
|
||||
isCallbackReturnValue)
|
||||
|
||||
typeName = descriptor.nativeType
|
||||
typePtr = typeName + "*"
|
||||
@ -3050,7 +3090,8 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
descriptor,
|
||||
"&${val}.toObject()",
|
||||
"${declName}",
|
||||
exceptionCode))
|
||||
exceptionCode,
|
||||
isCallbackReturnValue))
|
||||
elif descriptor.workers:
|
||||
return handleJSObjectType(type, isMember, failureCode)
|
||||
else:
|
||||
@ -8275,7 +8316,7 @@ class CGBindingRoot(CGThing):
|
||||
return self.root.deps()
|
||||
|
||||
class CGNativeMember(ClassMethod):
|
||||
def __init__(self, descriptor, member, name, signature, extendedAttrs,
|
||||
def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
|
||||
breakAfter=True, passCxAsNeeded=True, visibility="public",
|
||||
jsObjectsArePtr=False, variadicIsSequence=False):
|
||||
"""
|
||||
@ -8286,10 +8327,10 @@ class CGNativeMember(ClassMethod):
|
||||
JSContext* based on the return and argument types. We can
|
||||
still pass it based on 'implicitJSContext' annotations.
|
||||
"""
|
||||
self.descriptor = descriptor
|
||||
self.descriptorProvider = descriptorProvider
|
||||
self.member = member
|
||||
self.extendedAttrs = extendedAttrs
|
||||
self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.descriptor,
|
||||
self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.descriptorProvider,
|
||||
self.extendedAttrs)
|
||||
self.passCxAsNeeded = passCxAsNeeded
|
||||
self.jsObjectsArePtr = jsObjectsArePtr
|
||||
@ -8354,7 +8395,7 @@ class CGNativeMember(ClassMethod):
|
||||
return enumName, defaultValue, "return ${declName};"
|
||||
if type.isGeckoInterface():
|
||||
iface = type.unroll().inner;
|
||||
nativeType = self.descriptor.getDescriptor(
|
||||
nativeType = self.descriptorProvider.getDescriptor(
|
||||
iface.identifier.name).nativeType
|
||||
# Now trim off unnecessary namespaces
|
||||
nativeType = nativeType.split("::")
|
||||
@ -8377,25 +8418,10 @@ class CGNativeMember(ClassMethod):
|
||||
post=">")
|
||||
else:
|
||||
result = CGWrapper(result, post="*")
|
||||
if iface.isExternal():
|
||||
# The holder is an nsRefPtr. If we're nullable and end up null,
|
||||
# the holder will be null anyway, so it's safe to just always
|
||||
# return it here.
|
||||
returnCode = ("(void)${declName}; // avoid warning. May end up not being read\n"
|
||||
"return ${holderName}.forget();")
|
||||
elif iface.isCallback():
|
||||
# The decl is an OwningNonNull or nsRefPtr, depending
|
||||
# on whether we're nullable.
|
||||
returnCode = "return ${declName}.forget();"
|
||||
elif type.nullable():
|
||||
# Decl is a raw pointer
|
||||
returnCode = ("NS_IF_ADDREF(${declName});\n"
|
||||
"return dont_AddRef(${declName});")
|
||||
else:
|
||||
# Decl is a non-null raw pointer.
|
||||
returnCode = ("NS_ADDREF(${declName});\n"
|
||||
"return dont_AddRef(${declName});")
|
||||
return result.define(), "nullptr", returnCode
|
||||
# Since we always force an owning type for callback return values,
|
||||
# our ${declName} is an OwningNonNull or nsRefPtr. So we can just
|
||||
# .forget() to get our already_AddRefed.
|
||||
return result.define(), "nullptr", "return ${declName}.forget();"
|
||||
if type.isCallback():
|
||||
return ("already_AddRefed<%s>" % type.unroll().identifier.name,
|
||||
"nullptr", "return ${declName}.forget();")
|
||||
@ -8459,12 +8485,12 @@ class CGNativeMember(ClassMethod):
|
||||
args.insert(0, Argument("JS::Value", "aThisVal"))
|
||||
# And jscontext bits.
|
||||
if needCx(returnType, argList, self.extendedAttrs,
|
||||
self.descriptor, self.passCxAsNeeded):
|
||||
self.descriptorProvider, self.passCxAsNeeded):
|
||||
args.insert(0, Argument("JSContext*", "cx"))
|
||||
# And if we're static, a global
|
||||
if self.member.isStatic():
|
||||
globalObjectType = "GlobalObject"
|
||||
if self.descriptor.workers:
|
||||
if self.descriptorProvider.workers:
|
||||
globalObjectType = "Worker" + globalObjectType
|
||||
args.insert(0, Argument("const %s&" % globalObjectType, "global"))
|
||||
return args
|
||||
@ -8514,7 +8540,7 @@ class CGNativeMember(ClassMethod):
|
||||
else:
|
||||
typeDecl = "%s&"
|
||||
return ((typeDecl %
|
||||
self.descriptor.getDescriptor(iface.identifier.name).nativeType),
|
||||
self.descriptorProvider.getDescriptor(iface.identifier.name).nativeType),
|
||||
False, False)
|
||||
|
||||
if type.isSpiderMonkeyInterface():
|
||||
@ -8575,8 +8601,8 @@ class CGNativeMember(ClassMethod):
|
||||
return declType, False, False
|
||||
|
||||
if type.isDictionary():
|
||||
typeName = CGDictionary.makeDictionaryName(type.inner,
|
||||
self.descriptor.workers)
|
||||
typeName = CGDictionary.makeDictionaryName(
|
||||
type.inner, self.descriptorProvider.workers)
|
||||
return typeName, True, True
|
||||
|
||||
if type.isDate():
|
||||
@ -8914,6 +8940,7 @@ class CGJSImplMethod(CGNativeMember):
|
||||
variadicIsSequence=True,
|
||||
passCxAsNeeded=False)
|
||||
self.signature = signature
|
||||
self.descriptor = descriptor
|
||||
if isConstructor:
|
||||
self.body = self.getConstructorImpl()
|
||||
else:
|
||||
@ -9159,6 +9186,10 @@ class CGJSImplClass(CGBindingImplClass):
|
||||
def getGetParentObjectBody(self):
|
||||
return "return mParent;"
|
||||
|
||||
def isJSImplementedDescriptor(descriptorProvider):
|
||||
return (isinstance(descriptorProvider, Descriptor) and
|
||||
descriptorProvider.interface.isJSImplemented())
|
||||
|
||||
class CGCallback(CGClass):
|
||||
def __init__(self, idlObject, descriptorProvider, baseName, methods,
|
||||
getters=[], setters=[]):
|
||||
@ -9167,7 +9198,7 @@ class CGCallback(CGClass):
|
||||
name = idlObject.identifier.name
|
||||
if descriptorProvider.workers:
|
||||
name += "Workers"
|
||||
if isinstance(descriptorProvider, Descriptor) and descriptorProvider.interface.isJSImplemented():
|
||||
if isJSImplementedDescriptor(descriptorProvider):
|
||||
name = jsImplName(name)
|
||||
# For our public methods that needThisHandling we want most of the
|
||||
# same args and the same return type as what CallbackMember
|
||||
@ -9391,11 +9422,15 @@ class CallbackMember(CGNativeMember):
|
||||
"obj": "nullptr"
|
||||
}
|
||||
|
||||
if isJSImplementedDescriptor(self.descriptorProvider):
|
||||
isCallbackReturnValue = "JSImpl"
|
||||
else:
|
||||
isCallbackReturnValue = "Callback"
|
||||
convertType = instantiateJSToNativeConversion(
|
||||
getJSToNativeConversionInfo(self.retvalType,
|
||||
self.descriptor,
|
||||
self.descriptorProvider,
|
||||
exceptionCode=self.exceptionCode,
|
||||
isCallbackReturnValue=True),
|
||||
isCallbackReturnValue=isCallbackReturnValue),
|
||||
replacements)
|
||||
assignRetval = string.Template(
|
||||
self.getRetvalInfo(self.retvalType,
|
||||
@ -9443,7 +9478,7 @@ class CallbackMember(CGNativeMember):
|
||||
prepend = ""
|
||||
|
||||
conversion = prepend + wrapForType(
|
||||
arg.type, self.descriptor,
|
||||
arg.type, self.descriptorProvider,
|
||||
{
|
||||
'result' : result,
|
||||
'successCode' : "continue;" if arg.variadic else "break;",
|
||||
|
Loading…
Reference in New Issue
Block a user