Bug 661980: Add ability to make interfaces scriptable but not scriptimplementable. r=bsmedberg
authorJonas Sicking <jonas@sicking.cc>
Thu, 16 Jun 2011 12:21:25 -0700
changeset 71546 8e30eba8ff6456654f618071faa71b7ca8a7ea82
parent 71545 37db25250817ccb627a212a730ef6e0911974a1e
child 71547 56854b8cf1a6a146f52bd0f2aed944e08960b7e8
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs661980
milestone7.0a1
Bug 661980: Add ability to make interfaces scriptable but not scriptimplementable. r=bsmedberg
content/base/public/nsIXMLHttpRequest.idl
dom/interfaces/events/nsIDOMEventTarget.idl
dom/interfaces/threads/nsIDOMWorkers.idl
js/src/xpconnect/src/xpcwrappedjsclass.cpp
js/src/xpconnect/tests/mochitest/Makefile.in
js/src/xpconnect/tests/mochitest/test_bug661980.html
xpcom/ds/nsIAtom.idl
xpcom/idl-parser/xpidl.py
xpcom/reflect/xptcall/src/xptcall.cpp
xpcom/reflect/xptinfo/public/nsIInterfaceInfo.idl
xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
xpcom/reflect/xptinfo/src/xptiprivate.h
xpcom/typelib/xpidl/xpidl_typelib.c
xpcom/typelib/xpidl/xpidl_util.c
xpcom/typelib/xpt/public/xpt_struct.h
xpcom/typelib/xpt/tools/xpt.py
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -48,28 +48,28 @@ interface nsPIDOMWindow;
 interface nsIInputStream;
 interface nsIDOMBlob;
 
 %{C++
 // for jsval
 #include "jsapi.h"
 %}
 
-[scriptable, uuid(dea238a1-240f-45f4-9f07-7769bc69eb76)]
+[scriptable, builtinclass, uuid(dea238a1-240f-45f4-9f07-7769bc69eb76)]
 interface nsIXMLHttpRequestEventTarget : nsIDOMEventTarget {
   // event handler attributes
   attribute nsIDOMEventListener onabort;
   attribute nsIDOMEventListener onerror;
   attribute nsIDOMEventListener onload;
   attribute nsIDOMEventListener onloadstart;
   attribute nsIDOMEventListener onprogress;
   attribute nsIDOMEventListener onloadend;
 };
 
-[scriptable, uuid(09ff3682-7759-4441-a765-f70e1a1fabcf)]
+[scriptable, builtinclass, uuid(09ff3682-7759-4441-a765-f70e1a1fabcf)]
 interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget {
   // for future use
 };
 
 /**
  * Mozilla's XMLHttpRequest is modelled after Microsoft's IXMLHttpRequest
  * object. The goal has been to make Mozilla's version match Microsoft's
  * version as closely as possible, but there are bound to be some differences.
--- a/dom/interfaces/events/nsIDOMEventTarget.idl
+++ b/dom/interfaces/events/nsIDOMEventTarget.idl
@@ -42,17 +42,17 @@
 /**
  * The nsIDOMEventTarget interface is the interface implemented by all
  * event targets in the Document Object Model.
  *
  * For more information on this interface please see 
  * http://www.w3.org/TR/DOM-Level-2-Events/
  */
 
-[scriptable, uuid(1c773b30-d1cf-11d2-bd95-00805f8ae3f4)]
+[scriptable, builtinclass, uuid(1c773b30-d1cf-11d2-bd95-00805f8ae3f4)]
 interface nsIDOMEventTarget : nsISupports
 {
   /**
    * This method allows the registration of event listeners on the event target.
    * If an EventListener is added to an EventTarget while it is processing an
    * event, it will not be triggered by the current actions but may be 
    * triggered during a later stage of event flow, such as the bubbling phase.
    * 
--- a/dom/interfaces/threads/nsIDOMWorkers.idl
+++ b/dom/interfaces/threads/nsIDOMWorkers.idl
@@ -123,23 +123,23 @@ interface nsIWorkerScope : nsIWorkerGlob
   void postMessage(/* in JSObject aMessage */);
 
   void close();
 
   attribute nsIDOMEventListener onmessage;
   attribute nsIDOMEventListener onclose;
 };
 
-[scriptable, uuid(b90b7561-b5e2-4545-84b0-280dbaaa94ea)]
+[scriptable, builtinclass, uuid(b90b7561-b5e2-4545-84b0-280dbaaa94ea)]
 interface nsIAbstractWorker : nsIDOMEventTarget
 {
   attribute nsIDOMEventListener onerror;
 };
 
-[scriptable, uuid(daf945c3-8d29-4724-8939-dd383f7d27a7)]
+[scriptable, builtinclass, uuid(daf945c3-8d29-4724-8939-dd383f7d27a7)]
 interface nsIWorker : nsIAbstractWorker
 {
   void postMessage(/* in JSObject aMessage */);
 
   attribute nsIDOMEventListener onmessage;
 
   void terminate();
 };
--- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp
@@ -161,18 +161,19 @@ nsXPCWrappedJSClass::GetNewOrUsed(XPCCal
     }
 
     if(!clazz)
     {
         nsCOMPtr<nsIInterfaceInfo> info;
         ccx.GetXPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
         if(info)
         {
-            PRBool canScript;
+            PRBool canScript, isBuiltin;
             if(NS_SUCCEEDED(info->IsScriptable(&canScript)) && canScript &&
+               NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin &&
                nsXPConnect::IsISupportsDescendant(info))
             {
                 clazz = new nsXPCWrappedJSClass(ccx, aIID, info);
                 if(clazz && !clazz->mDescriptors)
                     NS_RELEASE(clazz);  // sets clazz to nsnull
             }
         }
     }
@@ -290,18 +291,19 @@ nsXPCWrappedJSClass::CallQueryInterfaceO
     // implement intentionally (for security) unscriptable interfaces.
     // We so often ask for nsISupports that we can short-circuit the test...
     if(!aIID.Equals(NS_GET_IID(nsISupports)))
     {
         nsCOMPtr<nsIInterfaceInfo> info;
         ccx.GetXPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
         if(!info)
             return nsnull;
-        PRBool canScript;
-        if(NS_FAILED(info->IsScriptable(&canScript)) || !canScript)
+        PRBool canScript, isBuiltin;
+        if(NS_FAILED(info->IsScriptable(&canScript)) || !canScript ||
+           NS_FAILED(info->IsBuiltinClass(&isBuiltin)) || isBuiltin)
             return nsnull;
     }
 
     id = xpc_NewIDObject(cx, jsobj, aIID);
     if(id)
     {
         // Throwing NS_NOINTERFACE is the prescribed way to fail QI from JS. It
         // is not an exception that is ever worth reporting, but we don't want
--- a/js/src/xpconnect/tests/mochitest/Makefile.in
+++ b/js/src/xpconnect/tests/mochitest/Makefile.in
@@ -84,16 +84,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug629227.html \
 		file1_bug629227.html \
 		file2_bug629227.html \
 		test_bug629331.html \
 		test1_bug629331.html \
 		test2_bug629331.html \
 		test_bug618017.html \
 		test_bug636097.html \
+		test_bug661980.html \
 		test_bug650273.html \
 		file_bug650273.html \
 		file_bug658560.html \
 		$(NULL)
 
 ifneq ($(OS_TARGET),Android)
 ifndef MOZ_PLATFORM_MAEMO
 _TEST_FILES +=	test_bug657267.html \
new file mode 100644
--- /dev/null
+++ b/js/src/xpconnect/tests/mochitest/test_bug661980.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=661980
+-->
+<head>
+  <title>Test for Bug 661980</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=661980">Mozilla Bug 661980</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 661980 **/
+
+// While not currently needed, make this as similar as possible to a real
+// EventTarget just to make sure that we're tripping on the wrapping and
+// nothing else.
+var fakeTarget = {
+  addEventListener: function() {},
+  removeEventListener: function() {},
+  dispatchEvent: function() {}
+}
+
+var mouseevent = document.createEvent("MouseEvent");
+var didThrow = false;
+dump("hello nurse");
+try {
+  mouseevent.initMouseEvent("mouseover",
+                            false, false,
+                            window,
+                            1, 2, 3, 4, 5,
+                            false, false, false, false,
+                            0,
+                            fakeTarget);
+}
+catch (ex) {
+  didThrow = true;
+}
+ok(didThrow, "should not be able to implement EventTarget using script");
+
+mouseevent.initMouseEvent("mouseout",
+                          false, false,
+                          window,
+                          1, 2, 3, 4, 5,
+                          false, false, false, false,
+                          0,
+                          document.body);
+is(mouseevent.type, "mouseout",
+   "should able to implement EventTarget using Element");
+
+</script>
+</pre>
+</body>
+</html>
--- a/xpcom/ds/nsIAtom.idl
+++ b/xpcom/ds/nsIAtom.idl
@@ -44,17 +44,17 @@
 %}
 
 /*
  * Should this really be scriptable?  Using atoms from script or proxies
  * could be dangerous since double-wrapping could lead to loss of
  * pointer identity.
  */
  
-[scriptable, uuid(1f341018-521a-49de-b806-1bef5c9a00b0)]
+[scriptable, builtinclass, uuid(1f341018-521a-49de-b806-1bef5c9a00b0)]
 interface nsIAtom : nsISupports
 {
   /**
    * Get the Unicode or UTF8 value for the string
    */
   [binaryname(ScriptableToString)] AString toString(); 
   [noscript] AUTF8String toUTF8String();
   
--- a/xpcom/idl-parser/xpidl.py
+++ b/xpcom/idl-parser/xpidl.py
@@ -544,38 +544,43 @@ class Interface(object):
         if c.kind != 'const':
             raise IDLError("symbol '%s' is not a constant", c.location)
 
         return c.getValue()
 
 class InterfaceAttributes(object):
     uuid = None
     scriptable = False
+    builtinclass = False
     function = False
     deprecated = False
     noscript = False
 
     def setuuid(self, value):
         self.uuid = value.lower()
 
     def setscriptable(self):
         self.scriptable = True
 
     def setfunction(self):
         self.function = True
 
     def setnoscript(self):
         self.noscript = True
 
+    def setbuiltinclass(self):
+        self.builtinclass = True
+
     def setdeprecated(self):
         self.deprecated = True
 
     actions = {
         'uuid':       (True, setuuid),
         'scriptable': (False, setscriptable),
+        'builtinclass': (False, setbuiltinclass),
         'function':   (False, setfunction),
         'noscript':   (False, setnoscript),
         'deprecated': (False, setdeprecated),
         'object':     (False, lambda self: True),
         }
 
     def __init__(self, attlist, location):
         def badattribute(self):
@@ -600,16 +605,18 @@ class InterfaceAttributes(object):
             raise IDLError("interface has no uuid", location)
 
     def __str__(self):
         l = []
         if self.uuid:
             l.append("\tuuid: %s\n" % self.uuid)
         if self.scriptable:
             l.append("\tscriptable\n")
+        if self.builtinclass:
+            l.append("\tbuiltinclass\n")
         if self.function:
             l.append("\tfunction\n")
         return "".join(l)
 
 class ConstMember(object):
     kind = 'const'
     def __init__(self, type, name, value, location, doccomments):
         self.type = type
--- a/xpcom/reflect/xptcall/src/xptcall.cpp
+++ b/xpcom/reflect/xptcall/src/xptcall.cpp
@@ -71,17 +71,17 @@ NS_GetXPTCallStub(REFNSIID aIID, nsIXPTC
 {
     NS_ENSURE_ARG(aOuter && aResult);
 
     xptiInterfaceInfoManager *iim =
         xptiInterfaceInfoManager::GetSingleton();
     NS_ENSURE_TRUE(iim, NS_ERROR_NOT_INITIALIZED);
 
     xptiInterfaceEntry *iie = iim->GetInterfaceEntryForIID(&aIID);
-    if (!iie || !iie->EnsureResolved())
+    if (!iie || !iie->EnsureResolved() || iie->GetBuiltinClassFlag())
         return NS_ERROR_FAILURE;
 
     nsXPTCStubBase* newbase = new nsXPTCStubBase(aOuter, iie);
     if (!newbase)
         return NS_ERROR_OUT_OF_MEMORY;
 
     *aResult = newbase;
     return NS_OK;
--- a/xpcom/reflect/xptinfo/public/nsIInterfaceInfo.idl
+++ b/xpcom/reflect/xptinfo/public/nsIInterfaceInfo.idl
@@ -53,23 +53,24 @@
 %{C++
 class nsXPTMethodInfo;
 class nsXPTConstant;
 class nsXPTParamInfo;
 class nsXPTType;
 %}
 
 /* this is NOT intended to be scriptable */
-[uuid(215DBE04-94A7-11d2-BA58-00805F8A5DD7)]
+[uuid(7de126a2-ef4b-4e3b-a952-78ce4c133e38)]
 interface nsIInterfaceInfo : nsISupports
 {
     readonly attribute string   name;
     readonly attribute nsIIDPtr InterfaceIID;
 
     PRBool isScriptable();
+    PRBool isBuiltinClass();
 
     readonly attribute nsIInterfaceInfo parent;
 
     /**
     * These include counts for parent (and all ancestors).
     */
     readonly attribute PRUint16 methodCount;
     readonly attribute PRUint16 constantCount;
--- a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
+++ b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
@@ -268,16 +268,17 @@ xptiInterfaceInfoManager::VerifyAndAddEn
                                        iface->iid,
                                        iface->interface_descriptor,
                                        typelib);
     if (!entry)
         return;
 
     //XXX  We should SetHeader too as part of the validation, no?
     entry->SetScriptableFlag(XPT_ID_IS_SCRIPTABLE(iface->interface_descriptor->flags));
+    entry->SetBuiltinClassFlag(XPT_ID_IS_BUILTINCLASS(iface->interface_descriptor->flags));
 
     mWorkingSet.mIIDTable.Put(entry->IID(), entry);
     mWorkingSet.mNameTable.Put(entry->GetTheName(), entry);
 
     typelib->SetEntryAt(idx, entry);
 
     LOG_AUTOREG(("      added interface: %s\n", iface->name));
 }
--- a/xpcom/reflect/xptinfo/src/xptiprivate.h
+++ b/xpcom/reflect/xptinfo/src/xptiprivate.h
@@ -245,27 +245,31 @@ public:
 
     enum {
         PARTIALLY_RESOLVED    = 1,
         FULLY_RESOLVED        = 2,
         RESOLVE_FAILED        = 3
     };
     
     // Additional bit flags...
-    enum {SCRIPTABLE = 4};
+    enum {SCRIPTABLE = 4, BUILTINCLASS = 8};
 
     PRUint8 GetResolveState() const {return mFlags.GetState();}
     
     PRBool IsFullyResolved() const 
         {return GetResolveState() == (PRUint8) FULLY_RESOLVED;}
 
     void   SetScriptableFlag(PRBool on)
                 {mFlags.SetFlagBit(PRUint8(SCRIPTABLE),on);}
     PRBool GetScriptableFlag() const
                 {return mFlags.GetFlagBit(PRUint8(SCRIPTABLE));}
+    void   SetBuiltinClassFlag(PRBool on)
+                {mFlags.SetFlagBit(PRUint8(BUILTINCLASS),on);}
+    PRBool GetBuiltinClassFlag() const
+                {return mFlags.GetFlagBit(PRUint8(BUILTINCLASS));}
 
     const nsID* GetTheIID()  const {return &mIID;}
     const char* GetTheName() const {return mName;}
 
     PRBool EnsureResolved()
         {return IsFullyResolved() ? PR_TRUE : Resolve();}
 
     nsresult GetInterfaceInfo(xptiInterfaceInfo** info);
@@ -283,16 +287,20 @@ public:
     const nsID& IID() const { return mIID; }
 
     //////////////////////
     // These non-virtual methods handle the delegated nsIInterfaceInfo methods.
 
     nsresult GetName(char * *aName);
     nsresult GetIID(nsIID * *aIID);
     nsresult IsScriptable(PRBool *_retval);
+    nsresult IsBuiltinClass(PRBool *_retval) {
+        *_retval = GetBuiltinClassFlag();
+        return NS_OK;
+    }
     // Except this one.
     //nsresult GetParent(nsIInterfaceInfo * *aParent);
     nsresult GetMethodCount(PRUint16 *aMethodCount);
     nsresult GetConstantCount(PRUint16 *aConstantCount);
     nsresult GetMethodInfo(PRUint16 index, const nsXPTMethodInfo * *info);
     nsresult GetMethodInfoForName(const char *methodName, PRUint16 *index, const nsXPTMethodInfo * *info);
     nsresult GetConstant(PRUint16 index, const nsXPTConstant * *constant);
     nsresult GetInfoForParam(PRUint16 methodIndex, const nsXPTParamInfo * param, nsIInterfaceInfo **_retval);
@@ -358,16 +366,17 @@ class xptiInterfaceInfo : public nsIInte
 {
 public:
     NS_DECL_ISUPPORTS
 
     // Use delegation to implement (most!) of nsIInterfaceInfo.
     NS_IMETHOD GetName(char * *aName) { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetName(aName); }
     NS_IMETHOD GetInterfaceIID(nsIID * *aIID) { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetIID(aIID); }
     NS_IMETHOD IsScriptable(PRBool *_retval) { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->IsScriptable(_retval); }
+    NS_IMETHOD IsBuiltinClass(PRBool *_retval) { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->IsBuiltinClass(_retval); }
     // Except this one.
     NS_IMETHOD GetParent(nsIInterfaceInfo * *aParent) 
     {
         if(!EnsureResolved() || !EnsureParent())
             return NS_ERROR_UNEXPECTED;
         NS_IF_ADDREF(*aParent = mParent);
         return NS_OK;
     }
--- a/xpcom/typelib/xpidl/xpidl_typelib.c
+++ b/xpcom/typelib/xpidl/xpidl_typelib.c
@@ -515,16 +515,19 @@ typelib_interface(TreeState *state)
         return FALSE;
 
     if (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "scriptable"))
         interface_flags |= XPT_ID_SCRIPTABLE;
 
     if (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "function"))
         interface_flags |= XPT_ID_FUNCTION;
 
+    if (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "builtinclass"))
+        interface_flags |= XPT_ID_BUILTINCLASS;
+
     ide = FindInterfaceByName(HEADER(state)->interface_directory,
                               HEADER(state)->num_interfaces, name);
     if (!ide) {
         IDL_tree_error(iface, "ERROR: didn't find interface %s in "
                        "IDE block. Giving up.\n", name);
         return FALSE;
     }
 
--- a/xpcom/typelib/xpidl/xpidl_util.c
+++ b/xpcom/typelib/xpidl/xpidl_util.c
@@ -880,33 +880,50 @@ xpidl_list_foreach(IDL_tree p, IDL_tree_
 }
 
 /*
  * Verify that the interface declaration is correct
  */
 gboolean
 verify_interface_declaration(IDL_tree interface_tree)
 {
+    gboolean scriptable =
+      IDL_tree_property_get(IDL_INTERFACE(interface_tree).ident,
+                            "scriptable") != NULL;
+    gboolean builtinclass =
+      IDL_tree_property_get(IDL_INTERFACE(interface_tree).ident,
+                            "builtinclass") != NULL;
+
     IDL_tree iter;
     /* 
      * If we have the scriptable attribute then make sure all of our direct
      * parents have it as well.
-     * NOTE: We don't recurse since all interfaces will fall through here
+     * NOTE: We don't recurse since all interfaces will come through here
      */
-    if (IDL_tree_property_get(IDL_INTERFACE(interface_tree).ident, 
-        "scriptable")) {
+    if (scriptable || !builtinclass) {
         for (iter = IDL_INTERFACE(interface_tree).inheritance_spec; iter; 
             iter = IDL_LIST(iter).next) {
-            if (IDL_tree_property_get(
-                IDL_INTERFACE(iter).ident, "scriptable") == 0) {
+            if (scriptable &&
+                IDL_tree_property_get(
+                  IDL_INTERFACE(iter).ident, "scriptable") == 0) {
                 XPIDL_WARNING((interface_tree,IDL_WARNING1,
                     "%s is scriptable but inherits from the non-scriptable interface %s\n",
                     IDL_IDENT(IDL_INTERFACE(interface_tree).ident).str,
                     IDL_IDENT(IDL_INTERFACE(iter).ident).str));
             }
+            if (!builtinclass &&
+                IDL_tree_property_get(
+                  IDL_INTERFACE(iter).ident, "builtinclass")) {
+                IDL_tree_error(interface_tree,
+                               "%s is not [builtinclass] but extends "
+                               "[builtinclass] interface %s",
+                               IDL_IDENT(IDL_INTERFACE(interface_tree).ident).str,
+                               IDL_IDENT(IDL_INTERFACE(iter).ident).str);
+                return FALSE;
+            }
         }
     }
     return TRUE;
 }
 
 /*
  * Return a pointer to the start of the base filename of path
  */
--- a/xpcom/typelib/xpt/public/xpt_struct.h
+++ b/xpcom/typelib/xpt/public/xpt_struct.h
@@ -259,22 +259,24 @@ struct XPTInterfaceDescriptor {
     */
 
     XPTTypeDescriptor       *additional_types;
     PRUint16                num_additional_types;
 };
 
 #define XPT_ID_SCRIPTABLE           0x80
 #define XPT_ID_FUNCTION             0x40
-#define XPT_ID_FLAGMASK             0xc0
+#define XPT_ID_BUILTINCLASS         0x20
+#define XPT_ID_FLAGMASK             0xe0
 #define XPT_ID_TAGMASK              (~XPT_ID_FLAGMASK)
 #define XPT_ID_TAG(id)              ((id).flags & XPT_ID_TAGMASK)
 
 #define XPT_ID_IS_SCRIPTABLE(flags) (!!(flags & XPT_ID_SCRIPTABLE))
 #define XPT_ID_IS_FUNCTION(flags) (!!(flags & XPT_ID_FUNCTION))
+#define XPT_ID_IS_BUILTINCLASS(flags) (!!(flags & XPT_ID_BUILTINCLASS))
 
 extern XPT_PUBLIC_API(PRBool)
 XPT_GetInterfaceIndexByName(XPTInterfaceDirectoryEntry *ide_block,
                             PRUint16 num_interfaces, char *name, 
                             PRUint16 *indexp);
 
 extern XPT_PUBLIC_API(XPTInterfaceDescriptor *)
 XPT_NewInterfaceDescriptor(XPTArena *arena, 
--- a/xpcom/typelib/xpt/tools/xpt.py
+++ b/xpcom/typelib/xpt/tools/xpt.py
@@ -840,28 +840,29 @@ class Interface(object):
     """
     _direntry = struct.Struct(">16sIII")    
     _descriptorstart = struct.Struct(">HH")
 
     UNRESOLVED_IID = "00000000-0000-0000-0000-000000000000"
     
     def __init__(self, name, iid=UNRESOLVED_IID, namespace="",
                  resolved=False, parent=None, methods=[], constants=[],
-                 scriptable=False, function=False):
+                 scriptable=False, function=False, builtinclass=False):
         self.resolved = resolved
         #TODO: should validate IIDs!
         self.iid = iid
         self.name = name
         self.namespace = namespace
         # if unresolved, all the members following this are unusable
         self.parent = parent
         self.methods = list(methods)
         self.constants = list(constants)
         self.scriptable = scriptable
         self.function = function
+        self.builtinclass = builtinclass
         # For sanity, if someone constructs an Interface and passes
         # in methods or constants, then it's resolved.
         if self.methods or self.constants:
             # make sure it has a valid IID
             if self.iid == Interface.UNRESOLVED_IID:
                 raise DataError, "Cannot instantiate Interface %s containing methods or constants with an unresolved IID" % self.name
             self.resolved = True
         # These are only used for writing out the interface
@@ -917,21 +918,23 @@ class Interface(object):
         for i in range(num_constants):
             c, offset = Constant.read(typelib, map, data_pool, offset)
             self.constants.append(c)
         # Read flags
         start = data_pool + offset - 1
         (flags, ) = struct.unpack(">B", map[start:start + struct.calcsize(">B")])
         offset = offset + struct.calcsize(">B")
         # only the first two bits are flags
-        flags &= 0xC0
+        flags &= 0xE0
         if flags & 0x80:
             self.scriptable = True
         if flags & 0x40:
             self.function = True
+        if flags & 0x20:
+            self.builtinclass = True
         self.resolved = True
 
     def write_directory_entry(self, file):
         """
         Write an InterfaceDirectoryEntry for this interface
         to |file|, which is assumed to be seeked to the correct offset.
 
         """
@@ -960,16 +963,18 @@ class Interface(object):
         file.write(struct.pack(">H", len(self.constants)))
         for c in self.constants:
             c.write(typelib, file)
         flags = 0
         if self.scriptable:
             flags |= 0x80
         if self.function:
             flags |= 0x40
+        if self.builtinclass:
+            flags |= 0x20
         file.write(struct.pack(">B", flags))
         
     def write_names(self, file, data_pool_offset):
         """
         Write this interface's name and namespace to |file|,
         as well as the names of all of its methods and constants.
         Assumes that |file| is currently seeked to an unused portion
         of the data pool.
@@ -1255,17 +1260,19 @@ class Typelib(object):
             if not i.resolved:
                 out.write("      [Unresolved]\n")
             else:
                 if i.parent:
                     out.write("      Parent: %s::%s\n" % (i.parent.namespace,
                                                     i.parent.name))
                 out.write("""      Flags:
          Scriptable: %s
+         BuiltinClass: %s
          Function: %s\n""" % (i.scriptable and "TRUE" or "FALSE",
+                              i.builtinclass and "TRUE" or "FALSE",
                               i.function and "TRUE" or "FALSE"))
                 out.write("      Methods:\n")
                 if len(i.methods) == 0:
                     out.write("         No Methods\n")
                 else:
                     for m in i.methods:
                         out.write("   %s%s%s%s%s%s%s %s %s(%s);\n" % (
                             m.getter and "G" or " ",