Bug 1658946 - Wrap non-wrappercached interfaces more precisely in ToJSValue, r=peterv

This will allow resolving DOM promises with non-wrappercached XPIDL interfaces
in a more convenient manner, as the wrapped JS object will have more concrete
interface information without needing to invoke QueryInterface.

Differential Revision: https://phabricator.services.mozilla.com/D87002
This commit is contained in:
Nika Layzell 2020-09-14 19:30:56 +00:00
parent e5976b05e4
commit b2d811fc35
3 changed files with 60 additions and 1 deletions

View File

@ -94,5 +94,27 @@ bool ToJSValue(JSContext* aCx, const WindowProxyHolder& aArgument,
return true;
}
// Static assertion tests for the `binding_detail::ScriptableInterfaceType`
// helper template, used by `ToJSValue`.
namespace binding_detail {
static_assert(std::is_same_v<ScriptableInterfaceType<nsISupports>, nsISupports>,
"nsISupports works with ScriptableInterfaceType");
static_assert(
std::is_same_v<ScriptableInterfaceType<nsIGlobalObject>, nsISupports>,
"non-scriptable interfaces get a fallback");
static_assert(std::is_same_v<ScriptableInterfaceType<nsIObserver>, nsIObserver>,
"scriptable interfaces should get the correct type");
static_assert(std::is_same_v<ScriptableInterfaceType<nsIRunnable>, nsIRunnable>,
"scriptable interfaces should get the correct type");
class SingleScriptableInterface : public nsIObserver {};
static_assert(
std::is_same_v<ScriptableInterfaceType<SingleScriptableInterface>,
nsIObserver>,
"Concrete type with one scriptable interface picks the correct interface");
class MultiScriptableInterface : public nsIObserver, public nsIRunnable {};
static_assert(std::is_same_v<ScriptableInterfaceType<MultiScriptableInterface>,
nsISupports>,
"Concrete type with multiple scriptable interfaces falls back");
} // namespace binding_detail
} // namespace dom
} // namespace mozilla

View File

@ -191,6 +191,32 @@ MOZ_MUST_USE
return true;
}
namespace binding_detail {
// Helper type alias for picking a script-exposable non-wrappercached XPIDL
// interface to expose to JS code. Falls back to `nsISupports` if the specific
// interface type is ambiguous.
template <typename T, typename = void>
struct GetScriptableInterfaceType {
using Type = nsISupports;
static_assert(std::is_base_of_v<nsISupports, T>,
"T must inherit from nsISupports");
};
template <typename T>
struct GetScriptableInterfaceType<
T, std::void_t<typename T::ScriptableInterfaceType>> {
using Type = typename T::ScriptableInterfaceType;
static_assert(std::is_base_of_v<Type, T>,
"T must inherit from ScriptableInterfaceType");
static_assert(std::is_base_of_v<nsISupports, Type>,
"ScriptableInterfaceType must inherit from nsISupports");
};
template <typename T>
using ScriptableInterfaceType = typename GetScriptableInterfaceType<T>::Type;
} // namespace binding_detail
// Accept objects that inherit from nsISupports but not nsWrapperCache (e.g.
// DOM File).
template <class T>
@ -204,7 +230,9 @@ ToJSValue(JSContext* aCx, T& aArgument, JS::MutableHandle<JS::Value> aValue) {
xpcObjectHelper helper(ToSupports(&aArgument));
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
return XPCOMObjectToJsval(aCx, scope, helper, nullptr, true, aValue);
const nsIID& iid =
NS_GET_TEMPLATE_IID(binding_detail::ScriptableInterfaceType<T>);
return XPCOMObjectToJsval(aCx, scope, helper, &iid, true, aValue);
}
MOZ_MUST_USE bool ToJSValue(JSContext* aCx, const WindowProxyHolder& aArgument,

View File

@ -348,6 +348,12 @@ iface_prolog = """ {
"""
iface_scriptable = """\
/* Used by ToJSValue to check which scriptable interface is implemented. */
using ScriptableInterfaceType = %(name)s;
"""
iface_epilog = """};
NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
@ -505,6 +511,9 @@ def write_interface(iface, fd):
fd.write(" : public %s" % iface.base)
fd.write(iface_prolog % names)
if iface.attributes.scriptable:
fd.write(iface_scriptable % names)
for key, group in itertools.groupby(iface.members, key=type):
if key == xpidl.ConstMember:
write_const_decls(group) # iterator of all the consts