Bug 793267. Add support for [Unforgeable] in WebIDL. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 24 Oct 2012 16:10:49 -0400
changeset 111416 b02152aa3ed82842390caef6bf392ceb97eb1b01
parent 111415 b6089a8b78d3ab43a075f67a4a4baed96e68f91d
child 111417 44b335425300654fa8d1cd5d78ce0dba40307c1b
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewerspeterv
bugs793267
milestone19.0a1
Bug 793267. Add support for [Unforgeable] in WebIDL. r=peterv Unforgeable attributes are defined directly on the object, not on the prototype. So we keep them in a separate spec array and define them during object creation as needed. This means that we have to pass that separate spec array to the Xray helpers, unfortunately, which somewhat complicates those.
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_unforgeable.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestExampleGen.webidl
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -83,16 +83,24 @@ DefinePrefable(JSContext* cx, JSObject* 
       if (!Define(cx, obj, props->specs)) {
         return false;
       }
     }
   } while ((++props)->specs);
   return true;
 }
 
+bool
+DefineUnforgeableAttributes(JSContext* cx, JSObject* obj,
+                            Prefable<JSPropertySpec>* props)
+{
+  return DefinePrefable(cx, obj, props);
+}
+
+
 // We should use JSFunction objects for interface objects, but we need a custom
 // hasInstance hook because we have new interface objects on prototype chains of
 // old (XPConnect-based) bindings. Because Function.prototype.toString throws if
 // passed a non-Function object we also need to provide our own toString method
 // for interface objects.
 
 enum {
   TOSTRING_CLASS_RESERVED_SLOT = 0,
@@ -494,59 +502,68 @@ XrayResolveProperty(JSContext* cx, JSObj
             desc->getter = nullptr;
            return true;
           }
         }
       }
     }
   }
 
-  if (nativeProperties->attributes) {
-    Prefable<JSPropertySpec>* attr;
-    for (attr = nativeProperties->attributes; attr->specs; ++attr) {
-      if (attr->enabled) {
-        // Set i to be the index into our full list of ids/specs that we're
-        // looking at now.
-        size_t i = attr->specs - nativeProperties->attributeSpecs;
-        for ( ; nativeProperties->attributeIds[i] != JSID_VOID; ++i) {
-          if (id == nativeProperties->attributeIds[i]) {
-            JSPropertySpec& attrSpec = nativeProperties->attributeSpecs[i];
-            // Because of centralization, we need to make sure we fault in the
-            // JitInfos as well. At present, until the JSAPI changes, the easiest
-            // way to do this is wrap them up as functions ourselves.
-            desc->attrs = attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS;
-            // They all have getters, so we can just make it.
-            JSObject *global = JS_GetGlobalForObject(cx, wrapper);
-            JSFunction *fun = JS_NewFunction(cx, (JSNative)attrSpec.getter.op,
-                                             0, 0, global, nullptr);
-            if (!fun)
-              return false;
-            SET_JITINFO(fun, attrSpec.getter.info);
-            JSObject *funobj = JS_GetFunctionObject(fun);
-            desc->getter = js::CastAsJSPropertyOp(funobj);
-            desc->attrs |= JSPROP_GETTER;
-            if (attrSpec.setter.op) {
-              // We have a setter! Make it.
-              fun = JS_NewFunction(cx, (JSNative)attrSpec.setter.op, 1, 0,
-                                   global, nullptr);
+  JSPropertySpec* attributeSpecs = nativeProperties->attributeSpecs;
+  Prefable<JSPropertySpec>* attr = nativeProperties->attributes;
+  jsid* attributeIds = nativeProperties->attributeIds;
+  // Do the attribute stuff for attributes, then for unforgeable attributes
+  for (int attrIteration = 0; attrIteration < 2; ++attrIteration) {
+    if (attr) {
+      for (; attr->specs; ++attr) {
+        if (attr->enabled) {
+          // Set i to be the index into our full list of ids/specs that we're
+          // looking at now.
+          size_t i = attr->specs - attributeSpecs;
+          for ( ; attributeIds[i] != JSID_VOID; ++i) {
+            if (id == attributeIds[i]) {
+              JSPropertySpec& attrSpec = attributeSpecs[i];
+              // Because of centralization, we need to make sure we fault in the
+              // JitInfos as well. At present, until the JSAPI changes, the easiest
+              // way to do this is wrap them up as functions ourselves.
+              desc->attrs = attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS;
+              // They all have getters, so we can just make it.
+              JSObject *global = JS_GetGlobalForObject(cx, wrapper);
+              JSFunction *fun = JS_NewFunction(cx, (JSNative)attrSpec.getter.op,
+                                               0, 0, global, nullptr);
               if (!fun)
                 return false;
-              SET_JITINFO(fun, attrSpec.setter.info);
-              funobj = JS_GetFunctionObject(fun);
-              desc->setter = js::CastAsJSStrictPropertyOp(funobj);
-              desc->attrs |= JSPROP_SETTER;
-            } else {
-              desc->setter = nullptr;
+              SET_JITINFO(fun, attrSpec.getter.info);
+              JSObject *funobj = JS_GetFunctionObject(fun);
+              desc->getter = js::CastAsJSPropertyOp(funobj);
+              desc->attrs |= JSPROP_GETTER;
+              if (attrSpec.setter.op) {
+                // We have a setter! Make it.
+                fun = JS_NewFunction(cx, (JSNative)attrSpec.setter.op, 1, 0,
+                                     global, nullptr);
+                if (!fun)
+                  return false;
+                SET_JITINFO(fun, attrSpec.setter.info);
+                funobj = JS_GetFunctionObject(fun);
+                desc->setter = js::CastAsJSStrictPropertyOp(funobj);
+                desc->attrs |= JSPROP_SETTER;
+              } else {
+                desc->setter = nullptr;
+              }
+              desc->obj = wrapper;
+              return true;
             }
-            desc->obj = wrapper;
-            return true;
           }
         }
       }
     }
+
+    attributeSpecs = nativeProperties->unforgeableAttributeSpecs;
+    attr = nativeProperties->unforgeableAttributes;
+    attributeIds = nativeProperties->unforgeableAttributeIds;
   }
 
   if (nativeProperties->constants) {
     Prefable<ConstantSpec>* constant;
     for (constant = nativeProperties->constants; constant->specs; ++constant) {
       if (constant->enabled) {
         // Set i to be the index into our full list of ids/specs that we're
         // looking at now.
@@ -603,31 +620,40 @@ XrayEnumerateProperties(JS::AutoIdVector
               !props.append(nativeProperties->methodIds[i])) {
             return false;
           }
         }
       }
     }
   }
 
-  if (nativeProperties->attributes) {
-    Prefable<JSPropertySpec>* attr;
-    for (attr = nativeProperties->attributes; attr->specs; ++attr) {
-      if (attr->enabled) {
-        // Set i to be the index into our full list of ids/specs that we're
-        // looking at now.
-        size_t i = attr->specs - nativeProperties->attributeSpecs;
-        for ( ; nativeProperties->attributeIds[i] != JSID_VOID; ++i) {
-          if ((nativeProperties->attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
-              !props.append(nativeProperties->attributeIds[i])) {
-            return false;
+  JSPropertySpec* attributeSpecs = nativeProperties->attributeSpecs;
+  Prefable<JSPropertySpec>* attr = nativeProperties->attributes;
+  jsid* attributeIds = nativeProperties->attributeIds;
+  // Do the attribute stuff for attributes, then for unforgeable attributes
+  for (int attrIteration = 0; attrIteration < 2; ++attrIteration) {
+    if (attr) {
+      for (; attr->specs; ++attr) {
+        if (attr->enabled) {
+          // Set i to be the index into our full list of ids/specs that we're
+          // looking at now.
+          size_t i = attr->specs - attributeSpecs;
+          for ( ; attributeIds[i] != JSID_VOID; ++i) {
+            if ((attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
+                !props.append(attributeIds[i])) {
+              return false;
+            }
           }
         }
       }
     }
+
+    attributeSpecs = nativeProperties->unforgeableAttributeSpecs;
+    attr = nativeProperties->unforgeableAttributes;
+    attributeIds = nativeProperties->unforgeableAttributeIds;
   }
 
   if (nativeProperties->constants) {
     Prefable<ConstantSpec>* constant;
     for (constant = nativeProperties->constants; constant->specs; ++constant) {
       if (constant->enabled) {
         // Set i to be the index into our full list of ids/specs that we're
         // looking at now.
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -345,16 +345,19 @@ struct NativeProperties
   jsid* staticMethodIds;
   JSFunctionSpec* staticMethodsSpecs;
   Prefable<JSFunctionSpec>* methods;
   jsid* methodIds;
   JSFunctionSpec* methodsSpecs;
   Prefable<JSPropertySpec>* attributes;
   jsid* attributeIds;
   JSPropertySpec* attributeSpecs;
+  Prefable<JSPropertySpec>* unforgeableAttributes;
+  jsid* unforgeableAttributeIds;
+  JSPropertySpec* unforgeableAttributeSpecs;
   Prefable<ConstantSpec>* constants;
   jsid* constantIds;
   ConstantSpec* constantSpecs;
 };
 
 /*
  * Create a DOM interface object (if constructorClass is non-null) and/or a
  * DOM interface prototype object (if protoClass is non-null).
@@ -397,16 +400,23 @@ JSObject*
 CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* receiver,
                        JSObject* protoProto, JSClass* protoClass,
                        JSClass* constructorClass, JSNative constructor,
                        unsigned ctorNargs, const DOMClass* domClass,
                        const NativeProperties* properties,
                        const NativeProperties* chromeProperties,
                        const char* name);
 
+/*
+ * Define the unforgeable attributes on an object.
+ */
+bool
+DefineUnforgeableAttributes(JSContext* cx, JSObject* obj,
+                            Prefable<JSPropertySpec>* props);
+
 inline bool
 MaybeWrapValue(JSContext* cx, JSObject* obj, JS::Value* vp)
 {
   if (vp->isObject() &&
       js::GetObjectCompartment(&vp->toObject()) != js::GetObjectCompartment(obj)) {
     return JS_WrapValue(cx, vp);
   }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -131,17 +131,17 @@ DOMJSClass Class = {
     %s, /* finalize */
     NULL,                  /* checkAccess */
     NULL,                  /* call */
     NULL,                  /* hasInstance */
     NULL,                  /* construct */
     %s, /* trace */
     JSCLASS_NO_INTERNAL_MEMBERS
   },
-  %s
+%s
 };
 """ % (self.descriptor.interface.identifier.name,
        ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'JS_PropertyStub',
        FINALIZE_HOOK_NAME, traceHook,
        CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
 
 class CGPrototypeJSClass(CGThing):
     def __init__(self, descriptor):
@@ -887,16 +887,17 @@ class PropertyDefiner:
         else:
             if self.hasNonChromeOnly():
                 return "s" + self.name
         return "nullptr"
     def usedForXrays(self):
         # We only need Xrays for methods, attributes and constants, but in
         # workers there are no Xrays.
         return (self.name is "Methods" or self.name is "Attributes" or
+                self.name is "UnforgeableAttributes" or
                 self.name is "Constants") and not self.descriptor.workers
 
     def __str__(self):
         # We only need to generate id arrays for things that will end
         # up used via ResolveProperty or EnumerateProperties.
         str = self.generateArray(self.regular, self.variableName(False),
                                  self.usedForXrays())
         if self.hasChromeOnly():
@@ -1079,29 +1080,40 @@ class MethodDefiner(PropertyDefiner):
         return self.generatePrefableArray(
             array, name,
             '  JS_FNINFO("%s", %s, %s, %s, %s)',
             '  JS_FS_END',
             'JSFunctionSpec',
             pref, specData, doIdArrays)
 
 class AttrDefiner(PropertyDefiner):
-    def __init__(self, descriptor, name):
+    def __init__(self, descriptor, name, unforgeable):
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
-        attributes = [m for m in descriptor.interface.members if m.isAttr()]
+        attributes = [m for m in descriptor.interface.members
+                      if m.isAttr() and m.isUnforgeable() == unforgeable]
         self.chrome = [m for m in attributes if isChromeOnly(m)]
         self.regular = [m for m in attributes if not isChromeOnly(m)]
+        self.unforgeable = unforgeable
+
+        if unforgeable and len(attributes) != 0 and descriptor.proxy:
+            raise TypeError("Unforgeable properties are not supported on "
+                            "proxy bindings without [NamedPropertiesObject].  "
+                            "And not even supported on the ones with "
+                            "[NamedPropertiesObject] yet, but we should fix "
+                            "that, since they're safe there.")
 
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def flags(attr):
-            return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+            unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
+            return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" +
+                    unforgeable)
 
         def getter(attr):
             native = ("genericLenientGetter" if attr.hasLenientThis()
                       else "genericGetter")
             return ("{(JSPropertyOp)%(native)s, &%(name)s_getterinfo}"
                     % {"name" : attr.identifier.name,
                        "native" : native})
 
@@ -1150,26 +1162,29 @@ class ConstDefiner(PropertyDefiner):
             '  { 0, JSVAL_VOID }',
             'ConstantSpec',
             PropertyDefiner.getControllingPref, specData, doIdArrays)
 
 class PropertyArrays():
     def __init__(self, descriptor):
         self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
         self.methods = MethodDefiner(descriptor, "Methods", False)
-        self.attrs = AttrDefiner(descriptor, "Attributes")
+        self.attrs = AttrDefiner(descriptor, "Attributes", unforgeable=False)
+        self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
+                                            unforgeable=True)
         self.consts = ConstDefiner(descriptor, "Constants")
 
     @staticmethod
     def arrayNames():
-        return [ "staticMethods", "methods", "attrs", "consts" ]
+        return [ "staticMethods", "methods", "attrs", "unforgeableAttrs",
+                 "consts" ]
 
     @staticmethod
     def xrayRelevantArrayNames():
-        return [ "methods", "attrs", "consts" ]
+        return [ "methods", "attrs", "unforgeableAttrs", "consts" ]
 
     def hasChromeOnly(self):
         return 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():
@@ -1306,20 +1321,17 @@ class CGCreateInterfaceObjectsMethod(CGA
         else:
             domClass = "nullptr"
 
         if self.properties.hasNonChromeOnly():
             properties = "&sNativeProperties"
         else:
             properties = "nullptr"
         if self.properties.hasChromeOnly():
-            if self.descriptor.workers:
-                accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
-            else:
-                accessCheck = "xpc::AccessCheck::isChrome(aGlobal)"
+            accessCheck = GetAccessCheck(self.descriptor, "aGlobal")
             chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr"
         else:
             chromeProperties = "nullptr"
         call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,
                                    %s, %s, %s, %d,
                                    %s,
                                    %s,
                                    %s,
@@ -1509,24 +1521,69 @@ def CreateBindingJSObject(descriptor, pa
     else:
         assert descriptor.nativeOwnership == 'owned'
         create += """  // Make sure the native objects inherit from NonRefcountedDOMObject so that we
   // log their ctor and dtor.
   MustInheritFromNonRefcountedDOMObject(aObject);
 """
     return create % parent
 
+def GetAccessCheck(descriptor, globalName):
+    """
+    globalName is the name of the global JSObject*
+
+    returns a string
+    """
+    if descriptor.workers:
+        accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
+    else:
+        accessCheck = "xpc::AccessCheck::isChrome(%s)" % globalName
+    return accessCheck
+
+def InitUnforgeableProperties(descriptor, properties):
+    """
+    properties is a PropertyArrays instance
+    """
+    defineUnforgeables = ("if (!DefineUnforgeableAttributes(aCx, obj, %s)) {\n"
+                          "  return nullptr;\n"
+                          "}")
+    unforgeableAttrs = properties.unforgeableAttrs
+    unforgeables = []
+    if unforgeableAttrs.hasNonChromeOnly():
+        unforgeables.append(CGGeneric(defineUnforgeables %
+                                      unforgeableAttrs.variableName(False)))
+    if unforgeableAttrs.hasChromeOnly():
+        unforgeables.append(
+            CGIfWrapper(CGGeneric(defineUnforgeables %
+                                  unforgeableAttrs.variableName(True)),
+                        GetAccessCheck(descriptor, "global")))
+
+    return CGIndenter(CGWrapper(
+            CGList(unforgeables, "\n"),
+            pre=("\n"
+            "// Important: do unforgeable property setup after we have handed\n"
+            "// over ownership of the C++ object to obj as needed, so that if\n"
+            "// we fail and it ends up GCed it won't have problems in the\n"
+            "// finalizer trying to drop its ownership of the C++ object.\n"),
+            post="\n")).define() if len(unforgeables) > 0 else ""
+
 class CGWrapWithCacheMethod(CGAbstractMethod):
-    def __init__(self, descriptor):
+    """
+    Create a wrapper JSObject for a given native that implements nsWrapperCache.
+
+    properties should be a PropertyArrays instance.
+    """
+    def __init__(self, descriptor, properties):
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
                 Argument(descriptor.nativeType + '*', 'aObject'),
                 Argument('nsWrapperCache*', 'aCache'),
                 Argument('bool*', 'aTriedToWrap')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+        self.properties = properties
 
     def definition_body(self):
         if self.descriptor.workers:
             return """  *aTriedToWrap = true;
   return aObject->GetJSObject();"""
 
         return """  *aTriedToWrap = true;
 
@@ -1539,52 +1596,61 @@ class CGWrapWithCacheMethod(CGAbstractMe
   JSObject* global = JS_GetGlobalForObject(aCx, parent);
 %s
   JSObject* proto = GetProtoObject(aCx, global, global);
   if (!proto) {
     return NULL;
   }
 
 %s
-
+%s
   aCache->SetWrapper(obj);
 
   return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"),
-                    CreateBindingJSObject(self.descriptor, "parent"))
+                    CreateBindingJSObject(self.descriptor, "parent"),
+                    InitUnforgeableProperties(self.descriptor, self.properties))
 
 class CGWrapMethod(CGAbstractMethod):
     def __init__(self, descriptor):
         # XXX can we wrap if we don't have an interface prototype object?
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
                 Argument('T*', 'aObject'), Argument('bool*', 'aTriedToWrap')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, inline=True, templateArgs=["class T"])
 
     def definition_body(self):
         return "  return Wrap(aCx, aScope, aObject, aObject, aTriedToWrap);"
 
 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
-    def __init__(self, descriptor):
+    """
+    Create a wrapper JSObject for a given native that does not implement
+    nsWrapperCache.
+
+    properties should be a PropertyArrays instance.
+    """
+    def __init__(self, descriptor, properties):
         # XXX can we wrap if we don't have an interface prototype object?
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
                 Argument(descriptor.nativeType + '*', 'aObject')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+        self.properties = properties
 
     def definition_body(self):
         return """
   JSObject* global = JS_GetGlobalForObject(aCx, aScope);
   JSObject* proto = GetProtoObject(aCx, global, global);
   if (!proto) {
     return NULL;
   }
 
 %s
-
-  return obj;""" % CreateBindingJSObject(self.descriptor, "global")
+%s
+  return obj;""" % (CreateBindingJSObject(self.descriptor, "global"),
+                    InitUnforgeableProperties(self.descriptor, self.properties))
 
 builtinNames = {
     IDLType.Tags.bool: 'bool',
     IDLType.Tags.int8: 'int8_t',
     IDLType.Tags.int16: 'int16_t',
     IDLType.Tags.int32: 'int32_t',
     IDLType.Tags.int64: 'int64_t',
     IDLType.Tags.uint8: 'uint8_t',
@@ -5326,20 +5392,21 @@ class CGDescriptor(CGThing):
                 cgThings.append(CGProxyUnwrap(descriptor))
                 cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
                 cgThings.append(CGDOMJSProxyHandler(descriptor))
                 cgThings.append(CGIsMethod(descriptor))
             else:
                 cgThings.append(CGDOMJSClass(descriptor))
 
             if descriptor.wrapperCache:
-                cgThings.append(CGWrapWithCacheMethod(descriptor))
+                cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
                 cgThings.append(CGWrapMethod(descriptor))
             else:
-                cgThings.append(CGWrapNonWrapperCacheMethod(descriptor))
+                cgThings.append(CGWrapNonWrapperCacheMethod(descriptor,
+                                                            properties))
 
         cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
         cgThings = CGWrapper(cgThings, pre='\n', post='\n')
         self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
                                             cgThings),
                                 post='\n')
 
     def declare(self):
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -512,16 +512,32 @@ class IDLInterface(IDLObjectWithScope):
                                       [self.location, self.parent.location])
             elif self.parent.isCallback():
                 raise WebIDLError("Non-callback interface %s inheriting from "
                                   "callback interface %s" %
                                   (self.identifier.name,
                                    self.parent.identifier.name),
                                   [self.location, self.parent.location])
 
+            # Now make sure our parent doesn't have any [Unforgeable]
+            # attributes.  We don't need to check its ancestors, because it has
+            # already checked those.  We don't need to check its consequential
+            # interfaces, because it has already imported those into its
+            # .members.
+            unforgeableParentMembers = [
+                attr for attr in parent.members
+                if attr.isAttr() and attr.isUnforgeable() ]
+            if len(unforgeableParentMembers) != 0:
+                locs = [self.location, parent.location]
+                locs.extend(attr.location for attr in unforgeableParentMembers)
+                raise WebIDLError("Interface %s inherits from %s, which has "
+                                  "[Unforgeable] members" %
+                                  (self.identifier.name, parent.identifier.name),
+                                  locs)
+
         for iface in self.implementedInterfaces:
             iface.finish(scope)
 
         cycleInGraph = self.findInterfaceLoopPoint(self)
         if cycleInGraph:
             raise WebIDLError("Interface %s has itself as ancestor or "
                               "implemented interface" % self.identifier.name,
                               [self.location, cycleInGraph.location])
@@ -1941,16 +1957,17 @@ class IDLAttribute(IDLInterfaceMember):
                                     IDLInterfaceMember.Tags.Attr)
 
         assert isinstance(type, IDLType)
         self.type = type
         self.readonly = readonly
         self.inherit = inherit
         self.static = static
         self.lenientThis = False
+        self._unforgeable = False
 
         if readonly and inherit:
             raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
                               [self.location])
 
     def isStatic(self):
         return self.static
 
@@ -2002,30 +2019,38 @@ class IDLAttribute(IDLInterfaceMember):
         elif identifier == "LenientThis":
             if not attr.noArguments():
                 raise WebIDLError("[LenientThis] must take no arguments",
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[LenientThis] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             self.lenientThis = True
+        elif identifier == "Unforgeable":
+            if not self.readonly:
+                raise WebIDLError("[Unforgeable] is only allowed on readonly "
+                                  "attributes", [attr.location, self.location])
+            self._unforgeable = True
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
     def resolve(self, parentScope):
         assert isinstance(parentScope, IDLScope)
         self.type.resolveType(parentScope)
         IDLObjectWithIdentifier.resolve(self, parentScope)
 
     def addExtendedAttributes(self, attrs):
         attrs = self.checkForStringHandlingExtendedAttributes(attrs)
         IDLInterfaceMember.addExtendedAttributes(self, attrs)
 
     def hasLenientThis(self):
         return self.lenientThis
 
+    def isUnforgeable(self):
+        return self._unforgeable
+
 class IDLArgument(IDLObjectWithIdentifier):
     def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
         IDLObjectWithIdentifier.__init__(self, location, None, identifier)
 
         assert isinstance(type, IDLType)
         self.type = type
 
         self.optional = optional
@@ -2464,20 +2489,24 @@ class IDLMethod(IDLInterfaceMember, IDLS
                           locations)
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
         if identifier == "GetterInfallible":
             raise WebIDLError("Methods must not be flagged as "
                               "[GetterInfallible]",
                               [attr.location, self.location])
-        if identifier == "SetterInfallible":
+        elif identifier == "SetterInfallible":
             raise WebIDLError("Methods must not be flagged as "
                               "[SetterInfallible]",
                               [attr.location, self.location])
+        elif identifier == "Unforgeable":
+            raise WebIDLError("Methods must not be flagged as "
+                              "[Unforgeable]",
+                              [attr.location, self.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
 class IDLImplementsStatement(IDLObject):
     def __init__(self, location, implementor, implementee):
         IDLObject.__init__(self, location)
         self.implementor = implementor;
         self.implementee = implementee
 
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_unforgeable.py
@@ -0,0 +1,50 @@
+def WebIDLTest(parser, harness):
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
+            };
+            interface Parent {
+              [Unforgeable] readonly attribute long foo;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
+            };
+            interface Parent {};
+            interface Consequential {
+              [Unforgeable] readonly attribute long foo;
+            };
+            Parent implements Consequential;
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface iface {
+              [Unforgeable] attribute long foo;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -410,16 +410,18 @@ public:
   // Typedefs
   void ExerciseTypedefInterfaces1(TestInterface&);
   already_AddRefed<TestInterface> ExerciseTypedefInterfaces2(TestInterface*);
   void ExerciseTypedefInterfaces3(TestInterface&);
 
   // Miscellania
   int32_t AttrWithLenientThis();
   void SetAttrWithLenientThis(int32_t);
+  uint32_t UnforgeableAttr();
+  uint32_t UnforgeableAttr2();
 
   // Methods and properties imported via "implements"
   bool ImplementedProperty();
   void SetImplementedProperty(bool);
   void ImplementedMethod();
   bool ImplementedParentProperty();
   void SetImplementedParentProperty(bool);
   void ImplementedParentMethod();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -328,16 +328,18 @@ interface TestInterface {
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
+  [Unforgeable] readonly attribute long unforgeableAttr;
+  [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
 
   // If you add things here, add them to TestExampleGen as well
 };
 
 interface TestNonWrapperCacheInterface {
 };
 
 interface ImplementedInterfaceParent {
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -298,11 +298,13 @@ interface TestExampleInterface {
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
+  [Unforgeable] readonly attribute long unforgeableAttr;
+  [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
 
   // If you add things here, add them to TestCodeGen as well
 };