Bug 1428531 - Add chrome-only Foo.isInstance static methods to interface objects. r?bz
MozReview-Commit-ID: Kq0xONMnbEO
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2494,16 +2494,57 @@ InterfaceHasInstance(JSContext* cx, int
"Why do we have a hasInstance hook if we don't have a prototype "
"ID?");
*bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID);
return true;
}
bool
+InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp,
+ prototypes::ID prototypeID, int depth)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ if (MOZ_UNLIKELY(args.length() < 1)) {
+ nsPrintfCString message("%s.isInstance",
+ NamesOfInterfacesWithProtos(prototypeID));
+ return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, message.get());
+ }
+
+ if (!args[0].isObject()) {
+ nsPrintfCString message("Argument 1 of %s.isInstance",
+ NamesOfInterfacesWithProtos(prototypeID));
+ return ThrowErrorMessage(cx, MSG_NOT_OBJECT, message.get());
+ }
+
+ JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
+
+ const DOMJSClass* domClass =
+ GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
+
+ if (domClass && domClass->mInterfaceChain[depth] == prototypeID) {
+ args.rval().setBoolean(true);
+ return true;
+ }
+
+ if (jsipc::IsWrappedCPOW(instance)) {
+ bool boolp = false;
+ if (!jsipc::DOMInstanceOf(cx, js::UncheckedUnwrap(instance), prototypeID,
+ depth, &boolp)) {
+ return false;
+ }
+ args.rval().setBoolean(boolp);
+ return true;
+ }
+
+ args.rval().setBoolean(false);
+ return true;
+}
+
+bool
ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj)
{
JS::Rooted<JSObject*> rootedObj(cx, obj);
GlobalObject global(cx, rootedObj);
if (global.Failed()) {
return false;
}
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2708,16 +2708,21 @@ ReparentWrapper(JSContext* aCx, JS::Hand
bool
InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp);
bool
InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
JS::Handle<JSObject*> instance,
bool* bp);
+// Used to implement the cross-context <Interface>.isInstance static method.
+bool
+InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp,
+ prototypes::ID 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, JSObject* obj);
// Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
// interface, get the nsIGlobalObject corresponding to the content side, if any.
// A false return means an exception was thrown.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1764,16 +1764,33 @@ class CGClassObjectMovedHook(CGAbstractC
def JSNativeArguments():
return [Argument('JSContext*', 'cx'),
Argument('unsigned', 'argc'),
Argument('JS::Value*', 'vp')]
+class CGIsInstanceMethod(CGAbstractStaticMethod):
+ """
+ A class for generating the static isInstance method.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ CGAbstractStaticMethod.__init__(self, descriptor, "isInstance", "bool",
+ JSNativeArguments())
+
+ def definition_body(self):
+ return fill(
+ """
+ return InterfaceIsInstance(cx, argc, vp, prototypes::id::${name},
+ PrototypeTraits<prototypes::id::${name}>::Depth);
+ """,
+ name=self.descriptor.name)
+
class CGClassConstructor(CGAbstractStaticMethod):
"""
JS-visible constructor for our objects
"""
def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool',
JSNativeArguments())
self._ctor = ctor
@@ -2411,16 +2428,28 @@ class MethodDefiner(PropertyDefiner):
if m.isStatic():
method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name))
if isChromeOnly(m):
self.chrome.append(method)
else:
self.regular.append(method)
+ # Generate the isInstance static method.
+ if (static and
+ (self.descriptor.interface.hasInterfaceObject() and
+ self.descriptor.interface.hasInterfacePrototypeObject())):
+ self.chrome.append({
+ "name": "isInstance",
+ "methodInfo": False,
+ "length": 1,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": MemberCondition(),
+ })
+
# TODO: Once iterable is implemented, use tiebreak rules instead of
# failing. Also, may be more tiebreak rules to implement once spec bug
# is resolved.
# https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
def hasIterator(methods, regular):
return (any("@@iterator" in m.aliases for m in methods) or
any("@@iterator" == r["name"] for r in regular))
@@ -2798,16 +2827,17 @@ class ConstDefiner(PropertyDefiner):
lambda fields: ' { "%s", %s }' % fields,
' { 0, JS::UndefinedValue() }',
'ConstantSpec',
PropertyDefiner.getControllingCondition, specData)
class PropertyArrays():
def __init__(self, descriptor):
+ self.descriptor = descriptor
self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
static=True)
self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
static=True)
self.methods = MethodDefiner(descriptor, "Methods", static=False)
self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
self.unforgeableMethods = MethodDefiner(descriptor, "UnforgeableMethods",
static=False, unforgeable=True)
@@ -2816,17 +2846,21 @@ class PropertyArrays():
self.consts = ConstDefiner(descriptor, "Constants")
@staticmethod
def arrayNames():
return ["staticMethods", "staticAttrs", "methods", "attrs",
"unforgeableMethods", "unforgeableAttrs", "consts"]
def hasChromeOnly(self):
- return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
+ # All interfaces that generate an interface object and interface
+ # prototype object have a chrome only isInstance static method.
+ return ((self.staticMethods.descriptor.interface.hasInterfaceObject() and
+ self.staticMethods.descriptor.interface.hasInterfacePrototypeObject()) or
+ any(getattr(self, a).hasChromeOnly() for a in self.arrayNames()))
def hasNonChromeOnly(self):
return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
def __str__(self):
define = ""
for array in self.arrayNames():
define += str(getattr(self, array))
@@ -12754,16 +12788,19 @@ class CGDescriptor(CGThing):
hasPromiseReturningMethod, hasPromiseReturningGetter) = (
False, False, False, False, False, False, False)
jsonifierMethod = None
crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
unscopableNames = list()
for n in descriptor.interface.namedConstructors:
cgThings.append(CGClassConstructor(descriptor, n,
NamedConstructorName(n)))
+ if (descriptor.interface.hasInterfaceObject() and
+ descriptor.interface.hasInterfacePrototypeObject()):
+ cgThings.append(CGIsInstanceMethod(descriptor))
for m in descriptor.interface.members:
if m.isMethod() and m.identifier.name == 'queryInterface':
continue
props = memberProperties(m, descriptor)
if m.isMethod():
if m.getExtendedAttribute("Unscopable"):
@@ -14274,19 +14311,23 @@ class CGBindingRoot(CGThing):
return (any(isChromeOnly(a) or needsContainsHack(a) or
needsCallerType(a)
for a in desc.interface.members) or
desc.interface.getExtendedAttribute("ChromeOnly") is not None or
# JS-implemented interfaces with an interface object get a
# chromeonly _create method. And interfaces with an
# interface object might have a ChromeOnly constructor.
+ # Also interfaces whose interface prototype object is
+ # generated (which is most of them) for the isInstance
+ # method.
(desc.interface.hasInterfaceObject() and
(desc.interface.isJSImplemented() or
- (ctor and isChromeOnly(ctor)))) or
+ (ctor and isChromeOnly(ctor)) or
+ desc.interface.hasInterfacePrototypeObject())) or
# JS-implemented interfaces with clearable cached
# attrs have chromeonly _clearFoo methods.
(desc.interface.isJSImplemented() and
any(clearableCachedAttrs(desc))))
# XXXkhuey ugly hack but this is going away soon.
bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")