Bug 648801 (new DOM list bindings) - Generate new DOM bindings. r=bz/jst/mrbkap.

--HG--
extra : rebase_source : 6b34182856cbb4ba3fba8d3001de648b10157a29
This commit is contained in:
Peter Van der Beken 2011-08-20 15:53:33 +02:00
parent 4ef486c32d
commit 075926c483
19 changed files with 1580 additions and 246 deletions

View File

@ -46,12 +46,11 @@ class nsIContent;
// IID for the nsINodeList interface
#define NS_INODELIST_IID \
{ 0xe683725e, 0xe75a, 0x4d62, \
{ 0x88, 0x5e, 0x5d, 0xd3, 0x1c, 0x27, 0x4b, 0xcc } }
{ 0xe60b773e, 0x5d20, 0x43f6, \
{ 0xb0, 0x8c, 0xfd, 0x65, 0x26, 0xce, 0xe0, 0x7a } }
/**
* An internal interface that allows QI-less getting of nodes from
* node lists and reasonably fast indexOf.
* An internal interface for a reasonably fast indexOf.
*/
class nsINodeList : public nsIDOMNodeList,
public nsWrapperCache
@ -59,11 +58,6 @@ class nsINodeList : public nsIDOMNodeList,
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODELIST_IID)
/**
* Get the node at the index. Returns null if the index is out of bounds
*/
virtual nsIContent* GetNodeAt(PRUint32 aIndex) = 0;
/**
* Get the index of the given node in the list. Will return -1 if the node
* is not in the list.

View File

@ -97,7 +97,6 @@ public:
NS_DECL_NSIDOMNODELIST
// nsINodeList
virtual nsIContent* GetNodeAt(PRUint32 aIndex);
virtual PRInt32 IndexOf(nsIContent* aContent);
PRUint32 Length() const {
@ -294,18 +293,12 @@ public:
// nsBaseContentList overrides
virtual PRInt32 IndexOf(nsIContent *aContent, bool aDoFlush);
virtual nsIContent* GetNodeAt(PRUint32 aIndex);
virtual PRInt32 IndexOf(nsIContent* aContent);
virtual nsINode* GetParentObject()
{
return mRootNode;
}
// nsIHTMLCollection
// GetNodeAt already declared as part of nsINodeList
virtual nsISupports* GetNamedItem(const nsAString& aName,
nsWrapperCache** aCache);
// nsContentList public methods
NS_HIDDEN_(PRUint32) Length(bool aDoFlush);
NS_HIDDEN_(nsIContent*) Item(PRUint32 aIndex, bool aDoFlush);

View File

@ -114,7 +114,6 @@ public:
NS_DECL_NSIDOMNODELIST
// nsINodeList interface
virtual nsIContent* GetNodeAt(PRUint32 aIndex);
virtual PRInt32 IndexOf(nsIContent* aContent);
void DropReference()

View File

@ -46,29 +46,17 @@ class nsWrapperCache;
// IID for the nsIHTMLCollection interface
#define NS_IHTMLCOLLECTION_IID \
{ 0x3fe47ab6, 0xb60f, 0x49aa, \
{ 0xb5, 0x93, 0xcc, 0xf8, 0xbe, 0xd0, 0x83, 0x19 } }
{ 0xdea91ad6, 0x57d1, 0x4e7a, \
{ 0xb5, 0x5a, 0xdb, 0xfc, 0x36, 0x7b, 0xc8, 0x22 } }
/**
* An internal interface that allows QI-less getting of nodes from HTML
* collections
* An internal interface
*/
class nsIHTMLCollection : public nsIDOMHTMLCollection
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLCOLLECTION_IID)
/**
* Get the node at the index. Returns null if the index is out of bounds.
*/
virtual nsIContent* GetNodeAt(PRUint32 aIndex) = 0;
/**
* Get the node for the name. Returns null if no node exists for the name.
*/
virtual nsISupports* GetNamedItem(const nsAString& aName,
nsWrapperCache** aCache) = 0;
/**
* Get the root node for this HTML collection.
*/

View File

@ -123,19 +123,6 @@ public:
// nsIDOMHTMLCollection interface
NS_DECL_NSIDOMHTMLCOLLECTION
virtual nsIContent* GetNodeAt(PRUint32 aIndex)
{
FlushPendingNotifications();
return mElements.SafeElementAt(aIndex, nsnull);
}
virtual nsISupports* GetNamedItem(const nsAString& aName,
nsWrapperCache **aCache)
{
nsISupports *item = NamedItemInternal(aName, PR_TRUE);
*aCache = nsnull;
return item;
}
virtual nsINode* GetParentObject()
{
return mForm;
@ -2567,3 +2554,19 @@ nsFormControlList::GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aContr
return NS_OK;
}
nsIContent*
nsFormControlList::GetNodeAt(PRUint32 aIndex)
{
FlushPendingNotifications();
return mElements.SafeElementAt(aIndex, nsnull);
}
nsISupports*
nsFormControlList::GetNamedItem(const nsAString& aName, nsWrapperCache **aCache)
{
nsISupports *item = NamedItemInternal(aName, true);
*aCache = nsnull;
return item;
}

View File

@ -71,9 +71,6 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIDOMHTMLCOLLECTION
virtual nsIContent* GetNodeAt(PRUint32 aIndex);
virtual nsISupports* GetNamedItem(const nsAString& aName,
nsWrapperCache **aCache);
virtual nsINode* GetParentObject()
{
return mParent;

View File

@ -103,7 +103,6 @@ public:
NS_DECL_NSIDOMNODELIST
// nsINodeList interface
virtual nsIContent* GetNodeAt(PRUint32 aIndex);
virtual PRInt32 IndexOf(nsIContent* aContent);
virtual nsINode *GetParentObject()
{

View File

@ -8427,9 +8427,9 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx, JSObject *obj,
if (!JSVAL_IS_PRIMITIVE(collection)) {
// We already have a node list in our reserved slot, use it.
JSObject *obj = JSVAL_TO_OBJECT(collection);
if (mozilla::dom::binding::HTMLCollection::objIsList(obj)) {
if (mozilla::dom::binding::HTMLCollection::objIsWrapper(obj)) {
nsIHTMLCollection *native =
mozilla::dom::binding::HTMLCollection::getListObject(obj);
mozilla::dom::binding::HTMLCollection::getNative(obj);
NS_ADDREF(*nodeList = static_cast<nsContentList*>(native));
}
else {

View File

@ -39,6 +39,12 @@
#include "nsISupports.idl"
%{C++
class nsWrapperCache;
%}
[ptr] native nsWrapperCachePtr(nsWrapperCache);
typedef unsigned long long DOMTimeStamp;
typedef unsigned long long DOMTimeMilliSec;

View File

@ -39,6 +39,8 @@
#include "domstubs.idl"
interface nsIContent;
/**
* The nsIDOMNodeList interface provides the abstraction of an ordered
* collection of nodes, without defining or constraining how this collection
@ -49,9 +51,14 @@
* http://www.w3.org/TR/DOM-Level-2-Core/
*/
[scriptable, uuid(a6cf907d-15b3-11d2-932e-00805f8add32)]
[scriptable, uuid(496852ba-e48d-4fa5-982e-e0dc1b475bf1)]
interface nsIDOMNodeList : nsISupports
{
nsIDOMNode item(in unsigned long index);
readonly attribute unsigned long length;
[getter,forward(getNodeAt)] nsIDOMNode item(in unsigned long index);
readonly attribute unsigned long length;
/**
* Get the node at the index. Returns null if the index is out of bounds
*/
[noscript,notxpcom,nostdcall] nsIContent getNodeAt(in unsigned long index);
};

View File

@ -39,6 +39,8 @@
#include "domstubs.idl"
interface nsIContent;
/**
* The nsIDOMHTMLCollection interface is an interface to a collection
* of [X]HTML elements.
@ -50,11 +52,22 @@
* http://www.whatwg.org/specs/web-apps/current-work/
*/
[scriptable, uuid(1af9e026-011d-4d0e-91db-09bcfa3e9622)]
[scriptable, uuid(b7ccd7b3-86aa-4322-a50c-b972643bb662)]
interface nsIDOMHTMLCollection : nsISupports
{
readonly attribute unsigned long length;
nsIDOMNode item(in unsigned long index);
nsIDOMNode namedItem(in DOMString name);
[getter,forward(getNodeAt)] nsIDOMNode item(in unsigned long index);
[getter,forward(getNamedItem)] nsIDOMNode namedItem(in DOMString name);
/**
* Get the node at the index. Returns null if the index is out of bounds.
*/
[noscript,notxpcom,nostdcall] nsIContent getNodeAt(in unsigned long index);
/**
* Get the node for the name. Returns null if no node exists for the name.
*/
[noscript,notxpcom,nostdcall] nsISupports getNamedItem(in DOMString name,
out nsWrapperCachePtr cache);
};

View File

@ -49,7 +49,9 @@ MODULE = xpconnect
LIBRARY_NAME = xpconnect_s
FORCE_STATIC_LIB = 1
LIBXUL_LIBRARY = 1
EXPORTS = xpcpublic.h
EXPORTS = \
xpcpublic.h \
dombindings_gen.h
CPPSRCS = \
nsScriptError.cpp \
@ -172,11 +174,50 @@ dom_quickstubs.cpp: $(srcdir)/dom_quickstubs.qsconf \
$(ENABLE_TRACEABLE_FLAGS) \
$(srcdir)/dom_quickstubs.qsconf
dombindings.$(OBJ_SUFFIX): dombindings_gen.h \
dombindings_gen.cpp
dombindings_gen.h: $(srcdir)/dombindings.conf \
$(srcdir)/dombindingsgen.py \
$(srcdir)/codegen.py \
$(topsrcdir)/xpcom/idl-parser/header.py \
$(topsrcdir)/xpcom/idl-parser/xpidl.py \
$(DEPTH)/js/src/js-confdefs.h
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
-I$(topsrcdir)/other-licenses/ply \
-I$(topsrcdir)/xpcom/idl-parser \
$(srcdir)/dombindingsgen.py \
--idlpath=$(DEPTH)/dist/idl \
--cachedir=$(DEPTH)/xpcom/idl-parser/cache \
--header-output dombindings_gen.h \
$(srcdir)/dombindings.conf
exports:: dombindings_gen.h
dombindings_gen.cpp: $(srcdir)/dombindings.conf \
$(srcdir)/dombindingsgen.py \
$(srcdir)/codegen.py \
$(topsrcdir)/xpcom/idl-parser/header.py \
$(topsrcdir)/xpcom/idl-parser/xpidl.py \
$(DEPTH)/js/src/js-confdefs.h
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
-I$(topsrcdir)/other-licenses/ply \
-I$(topsrcdir)/xpcom/idl-parser \
$(srcdir)/dombindingsgen.py \
--idlpath=$(DEPTH)/dist/idl \
--cachedir=$(DEPTH)/xpcom/idl-parser/cache \
--stub-output dombindings_gen.cpp \
--makedepend-output $(MDDEPDIR)/dombindingsgen.pp \
$(srcdir)/dombindings.conf
GARBAGE += \
dom_quickstubs.h \
dom_quickstubs.cpp \
dombindings_gen.h \
dombindings_gen.cpp \
xpidl_debug \
$(MDDEPDIR)/dom_qsgen.pp \
$(MDDEPDIR)/dombindingsgen.pp \
$(wildcard $(topsrcdir)/other-licenses/ply/ply/*.pyc) \
$(wildcard $(topsrcdir)/xpcom/idl-parser/*.pyc) \
$(NULL)

View File

@ -0,0 +1,685 @@
#!/usr/bin/env/python
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Jason Orendorff <jorendorff@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import xpidl
import header
import sys
import os
import string
# === Utils
def warn(msg):
sys.stderr.write(msg + '\n')
def makeQuote(filename):
return filename.replace(' ', '\\ ') # enjoy!
# === Types
def isVoidType(type):
""" Return True if the given xpidl type is void. """
return type.kind == 'builtin' and type.name == 'void'
def isStringType(t):
t = xpidl.unaliasType(t)
return t.kind == 'native' and (t.specialtype == 'astring' or t.specialtype == 'domstring');
def isInterfaceType(t):
t = xpidl.unaliasType(t)
assert t.kind in ('builtin', 'native', 'interface', 'forward')
return t.kind in ('interface', 'forward')
def isSpecificInterfaceType(t, name):
""" True if `t` is an interface type with the given name, or a forward
declaration or typedef aliasing it.
`name` must not be the name of a typedef but the actual name of the
interface.
"""
t = xpidl.unaliasType(t)
return t.kind in ('interface', 'forward') and t.name == name
def isVariantType(t):
return isSpecificInterfaceType(t, 'nsIVariant')
# === Reading the file
class UserError(Exception):
pass
def findIDL(includePath, irregularFilenames, interfaceName):
filename = irregularFilenames.get(interfaceName, interfaceName) + '.idl'
for d in includePath:
# Not os.path.join: we need a forward slash even on Windows because
# this filename ends up in makedepend output.
path = d + '/' + filename
if os.path.exists(path):
return path
raise UserError("No IDL file found for interface %s "
"in include path %r"
% (interfaceName, includePath))
# === Generating code
argumentUnboxingTemplates = {
'octet':
" uint32 ${name}_u32;\n"
" if (!JS_ValueToECMAUint32(cx, ${argVal}, &${name}_u32))\n"
" return JS_FALSE;\n"
" uint8 ${name} = (uint8) ${name}_u32;\n",
'short':
" int32 ${name}_i32;\n"
" if (!JS_ValueToECMAInt32(cx, ${argVal}, &${name}_i32))\n"
" return JS_FALSE;\n"
" int16 ${name} = (int16) ${name}_i32;\n",
'unsigned short':
" uint32 ${name}_u32;\n"
" if (!JS_ValueToECMAUint32(cx, ${argVal}, &${name}_u32))\n"
" return JS_FALSE;\n"
" uint16 ${name} = (uint16) ${name}_u32;\n",
'long':
" int32 ${name};\n"
" if (!JS_ValueToECMAInt32(cx, ${argVal}, &${name}))\n"
" return JS_FALSE;\n",
'unsigned long':
" uint32 ${name};\n"
" if (!JS_ValueToECMAUint32(cx, ${argVal}, &${name}))\n"
" return JS_FALSE;\n",
'long long':
" PRInt64 ${name};\n"
" if (!xpc_qsValueToInt64(cx, ${argVal}, &${name}))\n"
" return JS_FALSE;\n",
'unsigned long long':
" PRUint64 ${name};\n"
" if (!xpc_qsValueToUint64(cx, ${argVal}, &${name}))\n"
" return JS_FALSE;\n",
'float':
" jsdouble ${name}_dbl;\n"
" if (!JS_ValueToNumber(cx, ${argVal}, &${name}_dbl))\n"
" return JS_FALSE;\n"
" float ${name} = (float) ${name}_dbl;\n",
'double':
" jsdouble ${name};\n"
" if (!JS_ValueToNumber(cx, ${argVal}, &${name}))\n"
" return JS_FALSE;\n",
'boolean':
" bool ${name};\n"
" JS_ValueToBoolean(cx, ${argVal}, &${name});\n",
'[astring]':
" xpc_qsAString ${name}(cx, ${argVal}, ${argPtr});\n"
" if (!${name}.IsValid())\n"
" return JS_FALSE;\n",
'[domstring]':
" xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr}, "
"xpc_qsDOMString::e${nullBehavior}, "
"xpc_qsDOMString::e${undefinedBehavior});\n"
" if (!${name}.IsValid())\n"
" return JS_FALSE;\n",
'string':
" JSAutoByteString ${name}_bytes;\n"
" if (!xpc_qsJsvalToCharStr(cx, ${argVal}, &${name}_bytes))\n"
" return JS_FALSE;\n"
" char *${name} = ${name}_bytes.ptr();\n",
'wstring':
" PRUnichar *${name};\n"
" if (!xpc_qsJsvalToWcharStr(cx, ${argVal}, ${argPtr}, &${name}))\n"
" return JS_FALSE;\n",
'[cstring]':
" xpc_qsACString ${name}(cx, ${argVal}, ${argPtr});\n"
" if (!${name}.IsValid())\n"
" return JS_FALSE;\n",
'[utf8string]':
" xpc_qsAUTF8String ${name}(cx, ${argVal}, ${argPtr});\n"
" if (!${name}.IsValid())\n"
" return JS_FALSE;\n",
'[jsval]':
" jsval ${name} = ${argVal};\n"
}
# From JSData2Native.
#
# Omitted optional arguments are treated as though the caller had passed JS
# `null`; this behavior is from XPCWrappedNative::CallMethod. The 'jsval' type,
# however, defaults to 'undefined'.
#
def writeArgumentUnboxing(f, i, name, type, haveCcx, optional, rvdeclared,
nullBehavior, undefinedBehavior):
# f - file to write to
# i - int or None - Indicates the source jsval. If i is an int, the source
# jsval is argv[i]; otherwise it is *vp. But if Python i >= C++ argc,
# which can only happen if optional is True, the argument is missing;
# use JSVAL_NULL as the source jsval instead.
# name - str - name of the native C++ variable to create.
# type - xpidl.{Interface,Native,Builtin} - IDL type of argument
# optional - bool - True if the parameter is optional.
# rvdeclared - bool - False if no |nsresult rv| has been declared earlier.
typeName = xpidl.getBuiltinOrNativeTypeName(type)
isSetter = (i is None)
if isSetter:
argPtr = "vp"
argVal = "*vp"
elif optional:
if typeName == "[jsval]":
val = "JSVAL_VOID"
else:
val = "JSVAL_NULL"
argVal = "(%d < argc ? argv[%d] : %s)" % (i, i, val)
argPtr = "(%d < argc ? &argv[%d] : NULL)" % (i, i)
else:
argVal = "argv[%d]" % i
argPtr = "&" + argVal
params = {
'name': name,
'argVal': argVal,
'argPtr': argPtr,
'nullBehavior': nullBehavior or 'DefaultNullBehavior',
'undefinedBehavior': undefinedBehavior or 'DefaultUndefinedBehavior'
}
if typeName is not None:
template = argumentUnboxingTemplates.get(typeName)
if template is not None:
f.write(string.Template(template).substitute(params))
return rvdeclared
# else fall through; the type isn't supported yet.
elif isInterfaceType(type):
if type.name == 'nsIVariant':
# Totally custom.
assert haveCcx
template = (
" nsCOMPtr<nsIVariant> ${name}(already_AddRefed<nsIVariant>("
"XPCVariant::newVariant(ccx, ${argVal})));\n"
" if (!${name}) {\n"
" xpc_qsThrowBadArgWithCcx(ccx, NS_ERROR_XPC_BAD_CONVERT_JS, %d);\n"
" return JS_FALSE;\n"
" }\n") % i
f.write(string.Template(template).substitute(params))
return rvdeclared
elif type.name == 'nsIAtom':
# Should have special atomizing behavior. Fall through.
pass
else:
if not rvdeclared:
f.write(" nsresult rv;\n");
f.write(" %s *%s;\n" % (type.name, name))
f.write(" xpc_qsSelfRef %sref;\n" % name)
f.write(" rv = xpc_qsUnwrapArg<%s>("
"cx, %s, &%s, &%sref.ptr, %s);\n"
% (type.name, argVal, name, name, argPtr))
f.write(" if (NS_FAILED(rv)) {\n")
if isSetter:
f.write(" xpc_qsThrowBadSetterValue("
"cx, rv, JSVAL_TO_OBJECT(*tvr.jsval_addr()), id);\n")
elif haveCcx:
f.write(" xpc_qsThrowBadArgWithCcx(ccx, rv, %d);\n" % i)
else:
f.write(" xpc_qsThrowBadArg(cx, rv, vp, %d);\n" % i)
f.write(" return JS_FALSE;\n"
" }\n")
return True
warn("Unable to unbox argument of type %s (native type %s)" % (type.name, typeName))
if i is None:
src = '*vp'
else:
src = 'argv[%d]' % i
f.write(" !; // TODO - Unbox argument %s = %s\n" % (name, src))
return rvdeclared
def writeResultDecl(f, member, varname):
type = member.realtype
if isVoidType(type):
return # nothing to declare
t = xpidl.unaliasType(type)
if t.kind == 'builtin':
if not t.nativename.endswith('*'):
if type.kind == 'typedef':
typeName = type.name # use it
else:
typeName = t.nativename
f.write(" %s %s;\n" % (typeName, varname))
return
elif t.kind == 'native':
name = xpidl.getBuiltinOrNativeTypeName(t)
if name in ('[domstring]', '[astring]'):
f.write(" nsString %s;\n" % varname)
return
elif name == '[jsval]':
return # nothing to declare; see special case in outParamForm
elif t.kind in ('interface', 'forward'):
if member.kind == 'method' and member.notxpcom:
f.write(" %s *%s;\n" % (type.name, varname))
else:
f.write(" nsCOMPtr<%s> %s;\n" % (type.name, varname))
return
warn("Unable to declare result of type %s" % type.name)
f.write(" !; // TODO - Declare out parameter `%s`.\n" % varname)
def outParamForm(name, type):
type = xpidl.unaliasType(type)
# If we start allowing [jsval] return types here, we need to tack
# the return value onto the arguments list in the callers,
# possibly, and handle properly returning it too. See bug 604198.
assert type.kind != 'native' or type.specialtype != 'jsval'
if type.kind == 'builtin':
return '&' + name
elif type.kind == 'native':
if type.specialtype == 'jsval':
return 'vp'
elif type.modifier == 'ref':
return name
else:
return '&' + name
else:
return 'getter_AddRefs(%s)' % name
# From NativeData2JS.
resultConvTemplates = {
'void':
" ${jsvalRef} = JSVAL_VOID;\n"
" return JS_TRUE;\n",
'octet':
" ${jsvalRef} = INT_TO_JSVAL((int32) result);\n"
" return JS_TRUE;\n",
'short':
" ${jsvalRef} = INT_TO_JSVAL((int32) result);\n"
" return JS_TRUE;\n",
'long':
" return xpc_qsInt32ToJsval(cx, result, ${jsvalPtr});\n",
'long long':
" return xpc_qsInt64ToJsval(cx, result, ${jsvalPtr};\n",
'unsigned short':
" ${jsvalRef} = INT_TO_JSVAL((int32) result);\n"
" return JS_TRUE;\n",
'unsigned long':
" return xpc_qsUint32ToJsval(cx, result, ${jsvalPtr});\n",
'unsigned long long':
" return xpc_qsUint64ToJsval(cx, result, ${jsvalPtr});\n",
'float':
" return JS_NewNumberValue(cx, result, ${jsvalPtr});\n",
'double':
" return JS_NewNumberValue(cx, result, ${jsvalPtr});\n",
'boolean':
" ${jsvalRef} = (result ? JSVAL_TRUE : JSVAL_FALSE);\n"
" return JS_TRUE;\n",
'[astring]':
" return xpc_qsStringToJsval(cx, result, ${jsvalPtr});\n",
'[domstring]':
" return xpc_qsStringToJsval(cx, result, ${jsvalPtr});\n",
'[jsval]':
# Here there's nothing to convert, because the result has already been
# written directly to *rv. See the special case in outParamForm.
" return JS_TRUE;\n"
}
def writeResultConv(f, type, interfaceResultTemplate, jsvalPtr, jsvalRef):
""" Emit code to convert the C++ variable `result` to a jsval.
The emitted code contains a return statement; it returns JS_TRUE on
success, JS_FALSE on error.
"""
# From NativeData2JS.
typeName = xpidl.getBuiltinOrNativeTypeName(type)
if typeName is not None:
template = resultConvTemplates.get(typeName)
elif isInterfaceType(type):
if isVariantType(type):
template = " return xpc_qsVariantToJsval(lccx, result, ${jsvalPtr});\n"
else:
template = (" if (!result) {\n"
" *${jsvalPtr} = JSVAL_NULL;\n"
" return JS_TRUE;\n"
" }\n")
template += interfaceResultTemplate
if template is not None:
values = {'jsvalRef': jsvalRef,
'jsvalPtr': jsvalPtr,
'typeName': type.name}
f.write(string.Template(template).substitute(values))
return
# else fall through; this type isn't supported yet
warn("Unable to convert result of type %s" % type.name)
f.write(" !; // TODO - Convert `result` to jsval, store in `%s`.\n"
% jsvalRef)
f.write(" return xpc_qsThrow(cx, NS_ERROR_UNEXPECTED); // FIXME\n")
def anyParamRequiresCcx(member):
for p in member.params:
if isVariantType(p.realtype):
return True
return False
def memberNeedsCcx(member):
return member.kind == 'method' and anyParamRequiresCcx(member)
def validateParam(member, param):
def pfail(msg):
raise UserError(
member.iface.name + '.' + member.name + ": "
"parameter " + param.name + ": " + msg)
if param.iid_is is not None:
pfail("iid_is parameters are not supported.")
if param.size_is is not None:
pfail("size_is parameters are not supported.")
if param.retval:
pfail("Unexpected retval parameter!")
if param.paramtype in ('out', 'inout'):
pfail("inout parameters are not supported.")
if param.const or param.array or param.shared:
pfail("I am a simple caveman.")
# returns the number of arguments that a method takes in JS (including optional arguments)
def argumentsLength(member):
assert member.kind == 'method'
inArgs = len(member.params)
if inArgs and member.notxpcom and member.params[inArgs - 1].paramtype == 'out':
if member.params[inArgs - 1].realtype.kind != 'native' or member.params[inArgs - 1].realtype.nativename != 'nsWrapperCache':
pfail("We only support a wrapper cache as out argument")
inArgs -= 1
return inArgs
def writeStub(f, customMethodCalls, member, stubName, writeThisUnwrapping, writeCheckForFailure, writeResultWrapping, isSetter=False):
""" Write a single quick stub (a custom SpiderMonkey getter/setter/method)
for the specified XPCOM interface-member.
"""
if member.kind == 'method' and member.forward:
member = member.iface.namemap[member.forward]
isAttr = (member.kind == 'attribute')
isMethod = (member.kind == 'method')
assert isAttr or isMethod
isNotxpcom = isMethod and member.notxpcom
isGetter = isAttr and not isSetter
signature = "static JSBool\n"
if isAttr:
# JSPropertyOp signature.
if isSetter:
signature += "%s(JSContext *cx, JSObject *obj, jsid id, JSBool strict,%s jsval *vp)\n"
else:
signature += "%s(JSContext *cx, JSObject *obj, jsid id,%s jsval *vp)\n"
else:
# JSFastNative.
signature += "%s(JSContext *cx, uintN argc,%s jsval *vp)\n"
customMethodCall = customMethodCalls.get(stubName, None)
if customMethodCall is None:
customMethodCall = customMethodCalls.get(member.iface.name + '_', None)
if customMethodCall is not None:
if isMethod:
code = customMethodCall.get('code', None)
elif isGetter:
code = customMethodCall.get('getter_code', None)
else:
code = customMethodCall.get('setter_code', None)
else:
code = None
if code is not None:
templateName = member.iface.name
if isGetter:
templateName += '_Get'
elif isSetter:
templateName += '_Set'
# Generate the code for the stub, calling the template function
# that's shared between the stubs. The stubs can't have additional
# arguments, only the template function can.
callTemplate = signature % (stubName, '')
callTemplate += "{\n"
argumentValues = (customMethodCall['additionalArgumentValues']
% header.methodNativeName(member))
if isAttr:
callTemplate += (" return %s(cx, obj, id%s, %s, vp);\n"
% (templateName, ", strict" if isSetter else "", argumentValues))
else:
callTemplate += (" return %s(cx, argc, %s, vp);\n"
% (templateName, argumentValues))
callTemplate += "}\n\n"
# Fall through and create the template function stub called from the
# real stubs, but only generate the stub once. Otherwise, just write
# out the call to the template function and return.
templateGenerated = templateName + '_generated'
if templateGenerated in customMethodCall:
f.write(callTemplate)
return
customMethodCall[templateGenerated] = True
stubName = templateName
else:
callTemplate = ""
else:
callTemplate = ""
code = customMethodCall.get('code', None)
# Function prolog.
# Only template functions can have additional arguments.
if customMethodCall is None or not 'additionalArguments' in customMethodCall:
additionalArguments = ''
else:
additionalArguments = " %s," % customMethodCall['additionalArguments']
f.write(signature % (stubName, additionalArguments))
f.write("{\n")
f.write(" XPC_QS_ASSERT_CONTEXT_OK(cx);\n")
# For methods, compute "this".
if isMethod:
f.write(" JSObject *obj = JS_THIS_OBJECT(cx, vp);\n"
" if (!obj)\n"
" return JS_FALSE;\n")
# Create ccx if needed.
haveCcx = memberNeedsCcx(member)
if haveCcx:
f.write(" XPCCallContext ccx(JS_CALLER, cx, obj, "
"JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));\n")
if isInterfaceType(member.realtype):
f.write(" XPCLazyCallContext lccx(ccx);\n")
selfname = writeThisUnwrapping(f, member, isMethod, isGetter, customMethodCall, haveCcx)
rvdeclared = False
if isMethod:
inArgs = argumentsLength(member)
# If there are any required arguments, check argc.
requiredArgs = inArgs
while requiredArgs and member.params[requiredArgs-1].optional:
requiredArgs -= 1
if requiredArgs:
f.write(" if (argc < %d)\n" % requiredArgs)
f.write(" return xpc_qsThrow(cx, "
"NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
# Convert in-parameters.
if inArgs > 0:
f.write(" jsval *argv = JS_ARGV(cx, vp);\n")
for i in range(inArgs):
param = member.params[i]
argName = 'arg%d' % i
argTypeKey = argName + 'Type'
if customMethodCall is None or not argTypeKey in customMethodCall:
validateParam(member, param)
realtype = param.realtype
else:
realtype = xpidl.Forward(name=customMethodCall[argTypeKey],
location='', doccomments='')
# Emit code to convert this argument from jsval.
rvdeclared = writeArgumentUnboxing(
f, i, argName, realtype,
haveCcx=haveCcx,
optional=param.optional,
rvdeclared=rvdeclared,
nullBehavior=param.null,
undefinedBehavior=param.undefined)
if inArgs < len(member.params):
f.write(" nsWrapperCache *cache;\n")
elif isSetter:
rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype,
haveCcx=False, optional=False,
rvdeclared=rvdeclared,
nullBehavior=member.null,
undefinedBehavior=member.undefined)
canFail = not isNotxpcom and (customMethodCall is None or customMethodCall.get('canFail', True))
if canFail and not rvdeclared:
f.write(" nsresult rv;\n")
rvdeclared = True
if code is not None:
f.write("%s\n" % code)
if code is None or (isGetter and callTemplate is ""):
debugGetter = code is not None
if debugGetter:
f.write("#ifdef DEBUG\n")
f.write(" nsresult debug_rv;\n")
f.write(" nsCOMPtr<%s> debug_self;\n"
" CallQueryInterface(self, getter_AddRefs(debug_self));\n"
% member.iface.name);
prefix = 'debug_'
else:
prefix = ''
resultname = prefix + 'result'
selfname = prefix + selfname
nsresultname = prefix + 'rv'
# Prepare out-parameter.
if isMethod or isGetter:
writeResultDecl(f, member, resultname)
# Call the method.
if isMethod:
comName = header.methodNativeName(member)
argv = ['arg' + str(i) for i in range(inArgs)]
if inArgs < len(member.params):
argv.append(outParamForm('cache', member.params[inArgs].realtype))
if member.implicit_jscontext:
argv.append('cx')
if member.optional_argc:
argv.append('argc - %d' % requiredArgs)
if not isNotxpcom and not isVoidType(member.realtype):
argv.append(outParamForm(resultname, member.realtype))
args = ', '.join(argv)
else:
comName = header.attributeNativeName(member, isGetter)
if isGetter:
args = outParamForm(resultname, member.realtype)
else:
args = "arg0"
if member.implicit_jscontext:
args = "cx, " + args
f.write(" ")
if canFail or debugGetter:
f.write("%s = " % nsresultname)
elif isNotxpcom:
f.write("%s = " % resultname)
f.write("%s->%s(%s);\n" % (selfname, comName, args))
if debugGetter:
checkSuccess = "NS_SUCCEEDED(debug_rv)"
if canFail:
checkSuccess += " == NS_SUCCEEDED(rv)"
f.write(" NS_ASSERTION(%s && "
"xpc_qsSameResult(debug_result, result),\n"
" \"Got the wrong answer from the custom "
"method call!\");\n" % checkSuccess)
f.write("#endif\n")
if canFail:
# Check for errors.
writeCheckForFailure(f, isMethod, isGetter, haveCcx)
# Convert the return value.
if isMethod or isGetter:
writeResultWrapping(f, member, 'vp', '*vp')
else:
f.write(" return JS_TRUE;\n")
# Epilog.
f.write("}\n\n")
# Now write out the call to the template function.
if customMethodCall is not None:
f.write(callTemplate)

View File

@ -0,0 +1,4 @@
classes = {
'NodeList': 'nsINodeList',
'HTMLCollection': 'nsIHTMLCollection',
}

View File

@ -58,8 +58,6 @@ static jsid s_constructor_id = JSID_VOID;
static jsid s_prototype_id = JSID_VOID;
static jsid s_length_id = JSID_VOID;
static jsid s_item_id = JSID_VOID;
static jsid s_namedItem_id = JSID_VOID;
bool
DefineStaticJSVal(JSContext *cx, jsid &id, const char *string)
@ -82,8 +80,7 @@ DefineStaticJSVals(JSContext *cx)
return SET_JSID_TO_STRING(cx, constructor) &&
SET_JSID_TO_STRING(cx, prototype) &&
SET_JSID_TO_STRING(cx, length) &&
SET_JSID_TO_STRING(cx, item) &&
SET_JSID_TO_STRING(cx, namedItem);
DefinePropertyStaticJSVals(cx);
}
@ -225,18 +222,6 @@ Unwrap(JSContext *cx, jsval v, NoType **ppArg, nsISupports **ppArgRef, jsval *vp
template<class LC>
ListBase<LC> ListBase<LC>::instance;
void
Register(nsDOMClassInfoData *aData)
{
#define REGISTER_PROTO(_dom_class) \
aData[eDOMClassInfo_##_dom_class##_id].mDefineDOMInterface = _dom_class::getPrototype
REGISTER_PROTO(NodeList);
REGISTER_PROTO(HTMLCollection);
#undef REGISTER_PROTO
}
bool
DefineConstructor(JSContext *cx, JSObject *obj, DefineInterface aDefine, nsresult *aResult)
{
@ -349,29 +334,6 @@ ListBase<LC>::setNamedItem(ListType *list, const nsAString& aName, NameSetterTyp
return false;
}
template<class LC>
JSBool
ListBase<LC>::item(JSContext *cx, uintN argc, jsval *vp)
{
JSObject *obj = JS_THIS_OBJECT(cx, vp);
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
if (!obj || !instanceIsListObject(cx, obj, callee))
return false;
if (argc < 1)
return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
jsval *argv = JS_ARGV(cx, vp);
uint32 u;
if (!JS_ValueToECMAUint32(cx, argv[0], &u))
return false;
IndexGetterType result;
if (!getItemAt(getListObject(obj), u, result)) {
*vp = JSVAL_NULL;
return JS_TRUE;
}
return Wrap(cx, obj, result, vp);
}
template<class LC>
bool
ListBase<LC>::namedItem(JSContext *cx, JSObject *obj, jsval *name, NameGetterType &result,
@ -386,28 +348,6 @@ ListBase<LC>::namedItem(JSContext *cx, JSObject *obj, jsval *name, NameGetterTyp
return true;
}
template<class LC>
JSBool
ListBase<LC>::namedItem(JSContext *cx, uintN argc, jsval *vp)
{
JSObject *obj = JS_THIS_OBJECT(cx, vp);
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
if (!obj || !instanceIsListObject(cx, obj, callee))
return false;
if (argc < 1)
return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
jsval *argv = JS_ARGV(cx, vp);
bool hasResult;
NameGetterType result;
if (!namedItem(cx, obj, &argv[0], result, &hasResult))
return JS_FALSE;
if (!hasResult) {
*vp = JSVAL_NULL;
return JS_TRUE;
}
return Wrap(cx, obj, result, vp);
}
JSBool
interface_hasInstance(JSContext *cx, JSObject *obj, const js::Value *vp, JSBool *bp)
{
@ -1129,111 +1069,7 @@ NoBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope)
}
// NodeList
template
JSObject*
NodeList::create(JSContext *cx, XPCWrappedNativeScope *scope, NodeList::ListType *aList,
nsWrapperCache* aWrapperCache, bool *triedToWrap);
template<>
Class NodeList::sInterfaceClass = {
"NodeList",
0,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
interface_hasInstance
};
template<>
NodeList::Properties NodeList::sProtoProperties[] = {
{ s_length_id, length_getter, NULL }
};
template<>
NodeList::Methods NodeList::sProtoMethods[] = {
{ s_item_id, &item, 1 }
};
template<>
bool
NodeList::getItemAt(nsINodeList *list, uint32 i, nsIContent *&item)
{
return !!(item = list->GetNodeAt(i));
}
// HTMLCollection
template
JSObject*
HTMLCollection::create(JSContext *cx, XPCWrappedNativeScope *scope,
HTMLCollection::ListType *aList, nsWrapperCache* aWrapperCache,
bool *triedToWrap);
template
nsIHTMLCollection*
HTMLCollection::getListObject(JSObject *obj);
template<>
Class HTMLCollection::sInterfaceClass = {
"HTMLCollection",
0,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
interface_hasInstance
};
template<>
HTMLCollection::Properties HTMLCollection::sProtoProperties[] = {
{ s_length_id, length_getter, NULL }
};
template<>
HTMLCollection::Methods HTMLCollection::sProtoMethods[] = {
{ s_item_id, &item, 1 },
{ s_namedItem_id, &namedItem, 1 }
};
template<>
bool
HTMLCollection::getItemAt(nsIHTMLCollection *list, uint32 i, nsIContent *&item)
{
return !!(item = list->GetNodeAt(i));
}
template<>
bool
HTMLCollection::getNamedItem(nsIHTMLCollection *list, const nsAString& aName,
nsISupportsResult &item)
{
item.mResult = list->GetNamedItem(aName, &item.mCache);
return !!item.mResult;
}
}
}
}
#include "dombindings_gen.cpp"

View File

@ -44,10 +44,6 @@
#include "jsproxy.h"
#include "xpcpublic.h"
class nsIContent;
class nsINodeList;
class nsIHTMLCollection;
namespace mozilla {
namespace dom {
namespace binding {
@ -70,6 +66,7 @@ GetWrapperCache(void *p)
return nsnull;
}
class ProxyHandler : public js::ProxyHandler {
protected:
ProxyHandler() : js::ProxyHandler(ProxyFamily())
@ -194,8 +191,6 @@ private:
static void setProtoShape(JSObject *obj, uint32 shape);
static JSBool length_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
static JSBool item(JSContext *cx, uintN argc, jsval *vp);
static JSBool namedItem(JSContext *cx, uintN argc, jsval *vp);
static inline bool getItemAt(ListType *list, uint32 i, IndexGetterType &item);
static inline bool setItemAt(ListType *list, uint32 i, IndexSetterType item);
@ -215,16 +210,10 @@ private:
return !nativeGet(cx, proxy, js::GetObjectProto(proxy), id, &found, NULL) || found;
}
public:
static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, ListType *list,
nsWrapperCache* cache, bool *triedToWrap);
public:
template <typename I>
static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, I *aList, bool *triedToWrap)
{
return create(cx, scope, aList, GetWrapperCache(aList), triedToWrap);
}
static JSObject *getPrototype(JSContext *cx, XPCWrappedNativeScope *scope, bool *enabled);
bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
@ -282,14 +271,10 @@ struct nsISupportsResult
nsWrapperCache *mCache;
};
typedef ListClass<nsINodeList, Ops<Getter<nsIContent*> > > NodeListClass;
typedef ListBase<NodeListClass> NodeList;
}
}
}
typedef ListClass<nsIHTMLCollection, Ops<Getter<nsIContent*> >, Ops<Getter<nsISupportsResult> > > HTMLCollectionClass;
typedef ListBase<HTMLCollectionClass> HTMLCollection;
}
}
}
#include "dombindings_gen.h"
#endif /* dombindings_h */

View File

@ -0,0 +1,728 @@
#!/usr/bin/env/python
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from codegen import *
import xpidl
import os, re
import string
import UserDict
# === Preliminaries
# --makedepend-output support.
make_dependencies = []
make_targets = []
# === Reading the file
def addStubMember(memberId, member):
# Add this member to the list.
member.iface.stubMembers.append(member)
def checkStubMember(member):
memberId = member.iface.name + "." + member.name
if member.kind not in ('method', 'attribute'):
raise UserError("Member %s is %r, not a method or attribute."
% (memberId, member.kind))
if member.noscript:
raise UserError("%s %s is noscript."
% (member.kind.capitalize(), memberId))
if member.notxpcom:
raise UserError(
"%s %s: notxpcom methods are not supported."
% (member.kind.capitalize(), memberId))
# Check for unknown properties.
for attrname, value in vars(member).items():
if value is True and attrname not in ('readonly','optional_argc',
'implicit_jscontext','getter',
'stringifier'):
raise UserError("%s %s: unrecognized property %r"
% (member.kind.capitalize(), memberId,
attrname))
def loadIDL(parser, includePath, filename):
make_dependencies.append(filename)
text = open(filename, 'r').read()
idl = parser.parse(text, filename=filename)
idl.resolve(includePath, parser)
return idl
def firstCap(str):
return str[0].upper() + str[1:]
class DOMClass(UserDict.DictMixin):
def __init__(self, name, nativeClass):
self.name = name
self.base = None
self.isBase = False
self.nativeClass = nativeClass
self.indexGetter = None
self.nameGetter = None
self.stringifier = False
self.members = set()
@staticmethod
def getterNativeType(getter):
if isStringType(getter.realtype):
return 'nsString'
type = getter.realtype
if type.kind in ('interface', 'forward'):
if not getter.notxpcom:
return "nsCOMPtr<%s>" % type.name
if len(getter.params) > 1:
assert len(getter.params) == 2
assert getter.params[1].realtype.kind == 'native' and getter.params[1].realtype.nativename == 'nsWrapperCache'
return 'nsISupportsResult'
return type.nativeType('in').strip()
@staticmethod
def getterNativeCall(getter):
if isStringType(getter.realtype):
template = (" list->%s(index, item);\n"
" return !DOMStringIsNull(item);\n")
else:
type = getter.realtype
if type.kind in ('interface', 'forward'):
if not getter.notxpcom:
template = " return NS_SUCCEEDED(list->%s(index, getter_AddRefs(item)));\n"
elif len(getter.params) > 1:
template = (" item.mResult = list->%s(index, &item.mCache);\n"
" return !!item.mResult;\n")
else:
template = (" item = list->%s(index);\n"
" return !!item;\n")
else:
template = (" item = list->%s(index);\n"
" return !!item;\n")
return template % header.methodNativeName(getter)
def __setattr__(self, name, value):
self.__dict__[name] = value
if value:
if name == 'indexGetter':
if value.forward:
self.realIndexGetter = value.iface.namemap[value.forward]
else:
self.realIndexGetter = value
self.indexGetterType = self.getterNativeType(self.realIndexGetter)
elif name == 'nameGetter':
if value.forward:
self.realNameGetter = value.iface.namemap[value.forward]
else:
self.realNameGetter = value
self.nameGetterType = self.getterNativeType(self.realNameGetter)
def __getitem__(self, key):
assert type(key) == str
if key == 'indexGet':
return DOMClass.getterNativeCall(self.realIndexGetter)
if key == 'nameGet':
return DOMClass.getterNativeCall(self.realNameGetter)
def ops(getterType):
def opType(type):
return type + (" " if type.endswith('>') else "")
if getterType:
opsClass = ", Ops<"
opsClass += "Getter<" + opType(getterType) + ">"
opsClass += " >"
else:
opsClass = ", NoOps"
return opsClass
if key == 'indexOps':
return ops(self.indexGetter and self.indexGetterType)
if key == 'nameOps':
return ops(self.nameGetter and self.nameGetterType) if self.nameGetter else ""
if key == 'listClass':
if self.base:
template = "DerivedListClass<${nativeClass}, ${base}Wrapper${indexOps}${nameOps} >"
else:
template = "ListClass<${nativeClass}${indexOps}${nameOps} >"
return string.Template(template).substitute(self)
return self.__dict__[key]
def __cmp__(x, y):
if x.isBase != y.isBase:
return -1 if x.isBase else 1
return cmp(x.name, y.name)
class Configuration:
def __init__(self, filename, includePath):
self.includePath = includePath
config = {}
execfile(filename, config)
# required settings
if 'classes' not in config:
raise UserError(filename + ": `%s` was not defined." % name)
self.classes = {}
for clazz in config['classes']:
self.classes[clazz] = DOMClass(name=clazz, nativeClass=config['classes'][clazz])
# optional settings
self.customInheritance = config.get('customInheritance', {})
self.derivedClasses = {}
self.irregularFilenames = config.get('irregularFilenames', {})
self.customIncludes = config.get('customIncludes', [])
def readConfigFile(filename, includePath):
# Read the config file.
return Configuration(filename, includePath)
def completeConfiguration(conf, includePath, cachedir):
# Now read IDL files to connect the information in the config file to
# actual XPCOM interfaces, methods, and attributes.
interfaces = []
interfacesByName = {}
parser = xpidl.IDLParser(cachedir)
def getInterface(interfaceName, errorLoc):
iface = interfacesByName.get(interfaceName)
if iface is None:
idlFile = findIDL(conf.includePath, conf.irregularFilenames,
interfaceName)
idl = loadIDL(parser, conf.includePath, idlFile)
if not idl.hasName(interfaceName):
raise UserError("The interface %s was not found "
"in the idl file %r."
% (interfaceName, idlFile))
iface = idl.getName(interfaceName, errorLoc)
if not iface.attributes.scriptable:
raise UserError("Interface %s is not scriptable. "
"IDL file: %r." % (interfaceName, idlFile))
iface.stubMembers = []
interfaces.append(iface)
interfacesByName[interfaceName] = iface
return iface
stubbedInterfaces = []
for clazz in conf.classes.itervalues():
interfaceName = 'nsIDOM' + clazz.name
iface = getInterface(interfaceName, errorLoc='looking for %r' % clazz.name)
for member in iface.members:
if member.kind in ('method', 'attribute') and not member.noscript:
#addStubMember(iface.name + '.' + member.name, member)
clazz.members.add(member)
# Stub all scriptable members of this interface.
while True:
if iface not in stubbedInterfaces:
stubbedInterfaces.append(iface)
if not clazz.indexGetter and iface.ops['index']['getter']:
clazz.indexGetter = iface.ops['index']['getter']
if not clazz.nameGetter and iface.ops['name']['getter']:
clazz.nameGetter = iface.ops['name']['getter']
if not clazz.stringifier and iface.ops['stringifier']:
clazz.stringifier = iface.ops['stringifier']
interfaceName = conf.customInheritance.get(iface.name, iface.base)
iface = getInterface(interfaceName, errorLoc='looking for %r' % clazz.name)
if iface.name == 'nsISupports':
break
assert iface.name.startswith('nsIDOM') and not iface.name.startswith('nsIDOMNS')
clazz.base = iface.name[6:]
# For now we only support base classes that are real DOM classes
assert clazz.base in conf.classes
if not conf.classes[clazz.base].isBase:
conf.classes[clazz.base].isBase = True
conf.derivedClasses[clazz.base] = []
conf.derivedClasses[clazz.base].append(clazz.name)
# Now go through and check all the interfaces' members
for iface in stubbedInterfaces:
for member in iface.stubMembers:
checkStubMember(member)
return interfaces
# === Generating the header file
def needsForwardDeclaration(type):
return isInterfaceType(type) or (type.kind == 'native' and type.specialtype is None)
def getTypes(classes, map={}):
def getTranslatedType(type):
return map.get(type, type)
types = set()
for clazz in classes.itervalues():
types.add(getTranslatedType(clazz.nativeClass))
if clazz.indexGetter and needsForwardDeclaration(clazz.realIndexGetter.realtype):
types.add(getTranslatedType(clazz.realIndexGetter.realtype.name))
if clazz.nameGetter and needsForwardDeclaration(clazz.realNameGetter.realtype):
types.add(getTranslatedType(clazz.realNameGetter.realtype.name))
return sorted(types)
listDefinitionTemplate = (
"class ${name} {\n"
"public:\n"
" template<typename I>\n"
" static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, I *list, bool *triedToWrap)\n"
" {\n"
" return create(cx, scope, list, GetWrapperCache(list), triedToWrap);\n"
" }\n"
"\n"
" static bool objIsWrapper(JSObject *obj);\n"
" static ${nativeClass} *getNative(JSObject *obj);\n"
"\n"
"private:\n"
" static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, ${nativeClass} *list, nsWrapperCache *cache, bool *triedToWrap);\n"
"};"
"\n"
"\n")
def writeHeaderFile(filename, config):
print "Creating header file", filename
headerMacro = '__gen_%s__' % filename.replace('.', '_')
f = open(filename, 'w')
try:
f.write("/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
"#ifndef " + headerMacro + "\n"
"#define " + headerMacro + "\n\n")
namespaces = []
for type in getTypes(config.classes, {}):
newNamespaces = type.split('::')
type = newNamespaces.pop()
j = 0
for i in range(min(len(namespaces), len(newNamespaces))):
if namespaces[i] != newNamespaces[i]:
break
j += 1
for i in range(j, len(namespaces)):
f.write("}\n")
namespaces.pop()
for i in range(j, len(newNamespaces)):
f.write("namespace %s {\n" % newNamespaces[i])
namespaces.append(newNamespaces[i])
f.write("class %s;\n" % type)
for namespace in namespaces:
f.write("}\n")
f.write("\n")
f.write("namespace mozilla {\n"
"namespace dom {\n"
"namespace binding {\n\n")
f.write("bool\n"
"DefinePropertyStaticJSVals(JSContext *cx);\n\n")
for clazz in config.classes.itervalues():
f.write(string.Template(listDefinitionTemplate).substitute(clazz))
f.write("\n"
"}\n"
"}\n"
"}\n\n")
f.write("#endif\n")
finally:
f.close()
def writeMakeDependOutput(filename):
print "Creating makedepend file", filename
f = open(filename, 'w')
try:
if len(make_targets) > 0:
f.write("%s:" % makeQuote(make_targets[0]))
for filename in make_dependencies:
f.write(' \\\n\t\t%s' % makeQuote(filename))
f.write('\n\n')
for filename in make_targets[1:]:
f.write('%s: %s\n' % (makeQuote(filename), makeQuote(make_targets[0])))
finally:
f.close()
# === Generating the source file
listTemplateHeader = (
"// ${name}\n"
"\n"
"typedef ${listClass} ${name}Class;\n"
"typedef ListBase<${name}Class> ${name}Wrapper;\n"
"\n"
"\n")
listTemplate = (
"// ${name}\n"
"\n"
"template<>\n"
"js::Class ${name}Wrapper::sInterfaceClass = {\n"
" \"${name}\",\n"
" 0,\n"
" JS_PropertyStub, /* addProperty */\n"
" JS_PropertyStub, /* delProperty */\n"
" JS_PropertyStub, /* getProperty */\n"
" JS_StrictPropertyStub, /* setProperty */\n"
" JS_EnumerateStub,\n"
" JS_ResolveStub,\n"
" JS_ConvertStub,\n"
" NULL, /* finalize */\n"
" NULL, /* reserved0 */\n"
" NULL, /* checkAccess */\n"
" NULL, /* call */\n"
" NULL, /* construct */\n"
" NULL, /* xdrObject */\n"
" interface_hasInstance\n"
"};\n"
"\n")
derivedClassTemplate = (
"template<>\n"
"bool\n"
"${name}Wrapper::objIsList(JSObject *obj)\n"
"{\n"
" if (!js::IsProxy(obj))\n"
" return false;\n"
" js::ProxyHandler *handler = js::GetProxyHandler(obj);\n"
" return proxyHandlerIsList(handler) ||\n"
"${checkproxyhandlers};\n"
"}\n"
"\n"
"template<>\n"
"${nativeClass}*\n"
"${name}Wrapper::getNative(JSObject *obj)\n"
"{\n"
" js::ProxyHandler *handler = js::GetProxyHandler(obj);\n"
" if (proxyHandlerIsList(handler))\n"
" return static_cast<${nativeClass}*>(js::GetProxyPrivate(obj).toPrivate());\n"
"${castproxyhandlers}"
"\n"
" NS_RUNTIMEABORT(\"Unknown list type!\");\n"
" return NULL;\n"
"}\n"
"\n")
toStringTemplate = (
"template<>\n"
"JSString *\n"
"${name}Wrapper::obj_toString(JSContext *cx, JSObject *proxy)\n"
"{\n"
" nsString result;\n"
" nsresult rv = ${name}Wrapper::getListObject(proxy)->ToString(result);\n"
" JSString *jsresult;\n"
" return NS_SUCCEEDED(rv) && xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;\n"
"}\n"
"\n")
indexGetterTemplate = (
"template<>\n"
"bool\n"
"${name}Wrapper::getItemAt(${nativeClass} *list, uint32 index, ${indexGetterType} &item)\n"
"{\n"
"${indexGet}"
"}\n"
"\n")
nameGetterTemplate = (
"template<>\n"
"bool\n"
"${name}Wrapper::getNamedItem(${nativeClass} *list, const nsAString& index, ${nameGetterType} &item)\n"
"{\n"
"${nameGet}"
"}\n"
"\n")
listTemplateFooter = (
"template<>\n"
"${name}Wrapper::Properties ${name}Wrapper::sProtoProperties[] = {\n"
"${properties}\n"
"};\n"
"\n""template<>\n"
"${name}Wrapper::Methods ${name}Wrapper::sProtoMethods[] = {\n"
"${methods}\n"
"};\n"
"\n"
"JSObject*\n"
"${name}::create(JSContext *cx, XPCWrappedNativeScope *scope, ${nativeClass} *list, nsWrapperCache *cache, bool *triedToWrap)\n"
"{\n"
" return ${name}Wrapper::create(cx, scope, list, cache, triedToWrap);\n"
"}\n"
"\n"
"bool\n"
"${name}::objIsWrapper(JSObject *obj)\n"
"{\n"
" return ${name}Wrapper::objIsList(obj);\n"
"}\n"
"\n"
"${nativeClass}*\n"
"${name}::getNative(JSObject *obj)\n"
"{\n"
" return ${name}Wrapper::getListObject(obj);\n"
"}\n"
"\n")
def writeBindingStub(f, classname, member, stubName, isSetter=False):
def writeThisUnwrapping(f, member, isMethod, isGetter, customMethodCall, haveCcx):
if isMethod:
f.write(" JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));\n"
" if (!%sWrapper::instanceIsListObject(cx, obj, callee))\n"
" return false;\n" % classname)
else:
f.write(" if (!%sWrapper::instanceIsListObject(cx, obj, NULL))\n"
" return false;\n" % classname)
return "%sWrapper::getListObject(obj)" % classname
def writeCheckForFailure(f, isMethod, isGeter, haveCcx):
f.write(" if (NS_FAILED(rv)) {\n"
" xpc_qsThrowMethodFailedWithDetails(cx, rv, \"%s\", \"%s\");\n"
" return JS_FALSE;\n"
" }\n" % (classname, member.name))
def writeResultWrapping(f, member, jsvalPtr, jsvalRef):
if member.kind == 'method' and member.notxpcom and len(member.params) > 0 and member.params[len(member.params) - 1].paramtype == 'out':
assert member.params[len(member.params) - 1].realtype.kind == 'native' and member.params[len(member.params) - 1].realtype.nativename == 'nsWrapperCache'
template = " return Wrap(cx, obj, result, cache, ${jsvalPtr});\n"
else:
template = " return Wrap(cx, obj, result, ${jsvalPtr});\n"
writeResultConv(f, member.realtype, template, jsvalPtr, jsvalRef)
writeStub(f, {}, member, stubName, writeThisUnwrapping, writeCheckForFailure, writeResultWrapping, isSetter)
def writeAttrStubs(f, classname, attr):
getterName = classname + '_' + header.attributeNativeName(attr, True)
writeBindingStub(f, classname, attr, getterName)
if attr.readonly:
setterName = 'xpc_qsGetterOnlyPropertyStub'
else:
setterName = (classname + '_'
+ header.attributeNativeName(attr, False))
writeBindingStub(f, classname, attr, setterName, isSetter=True)
return " { s_%s_id, %s, %s }" % (attr.name, getterName, setterName)
def writeMethodStub(f, classname, method):
stubName = classname + '_' + header.methodNativeName(method)
writeBindingStub(f, classname, method, stubName)
return " { s_%s_id, %s, %i }" % (method.name, stubName, argumentsLength(method))
def writeStubFile(filename, config, interfaces):
print "Creating stub file", filename
make_targets.append(filename)
f = open(filename, 'w')
filesIncluded = set()
def includeType(type):
type = unaliasType(type)
if type.kind in ('builtin', 'native'):
return None
file = conf.irregularFilenames.get(type.name, type.name) + '.h'
if file not in filesIncluded:
f.write('#include "%s"\n' % file)
filesIncluded.add(file)
return type
def writeIncludesForMember(member):
assert member.kind in ('attribute', 'method')
resulttype = includeType(member.realtype)
if member.kind == 'method':
for p in member.params:
includeType(p.realtype)
return resulttype
headerFilename = re.sub(r'(\.cpp)?$', '.h', filename)
try:
f.write("/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n")
f.write("".join([("#include \"%s.h\"\n" % re.sub(r'(([^:]+::)*)', '', type)) for type in getTypes(config.classes, config.irregularFilenames)]))
f.write("\n")
f.write("namespace mozilla {\n"
"namespace dom {\n"
"namespace binding {\n\n")
f.write("// Property name ids\n\n")
ids = set()
for clazz in config.classes.itervalues():
assert clazz.indexGetter
ids.add(clazz.indexGetter.name)
if clazz.nameGetter:
ids.add(clazz.nameGetter.name)
if clazz.stringifier:
ids.add('toString')
for member in clazz.members:
if member.name != 'length':
ids.add(member.name)
ids = sorted(ids)
for id in ids:
f.write("static jsid s_%s_id = JSID_VOID;\n" % id)
f.write("\n"
"bool\n"
"DefinePropertyStaticJSVals(JSContext *cx)\n"
"{\n")
f.write(" return %s;" % (" &&\n ".join([("SET_JSID_TO_STRING(cx, %s)" % id) for id in ids])))
f.write("\n"
"}\n\n")
classes = sorted(config.classes.values())
f.write("// Typedefs\n\n")
for clazz in classes:
f.write(string.Template(listTemplateHeader).substitute(clazz))
f.write("// Implementation\n\n")
for clazz in classes:
f.write(string.Template(listTemplate).substitute(clazz))
derivedClasses = config.derivedClasses.get(clazz.name, None)
if derivedClasses:
# If this hits we might need to do something better than just compare instance pointers
assert len(derivedClasses) <= 3
checkproxyhandlers = "||\n".join(map(lambda d: " %sWrapper::proxyHandlerIsList(handler)" % d, derivedClasses))
castproxyhandlers = "\n".join(map(lambda d: " if (%sWrapper::proxyHandlerIsList(handler))\n return %sWrapper::getNative(obj);\n" % (d, d), derivedClasses))
f.write(string.Template(derivedClassTemplate).substitute(clazz, checkproxyhandlers=checkproxyhandlers, castproxyhandlers=castproxyhandlers))
methodsList = []
propertiesList = []
if clazz.stringifier:
f.write(string.Template(toStringTemplate).substitute(clazz))
if clazz.stringifier.name != 'toString':
methodsList.append(" { s_toString_id, %s_%s, 0 }", clazz.name, header.methodNativeName(clazz.stringifier))
if clazz.indexGetter:
#methodsList.append(" { s_%s_id, &item, 1 }" % clazz.indexGetter.name)
f.write(string.Template(indexGetterTemplate).substitute(clazz))
if clazz.nameGetter:
#methodsList.append(" { s_%s_id, &namedItem, 1 }" % clazz.nameGetter.name)
f.write(string.Template(nameGetterTemplate).substitute(clazz))
for member in clazz.members:
if member.name == 'length':
if not member.readonly:
setterName = (clazz.name + '_' + header.attributeNativeName(member, False))
writeBindingStub(f, clazz.name, member, setterName, isSetter=True)
else:
setterName = "NULL"
propertiesList.append(" { s_length_id, length_getter, %s }" % setterName)
continue
isAttr = (member.kind == 'attribute')
isMethod = (member.kind == 'method')
assert isAttr or isMethod
if isMethod:
methodsList.append(writeMethodStub(f, clazz.name, member))
else:
propertiesList.append(writeAttrStubs(f, clazz.name, member))
f.write(string.Template(listTemplateFooter).substitute(clazz, methods=",\n".join(methodsList), properties=",\n".join(propertiesList)))
f.write("// Register prototypes\n\n")
f.write("void\n"
"Register(nsDOMClassInfoData *aData)\n"
"{\n"
"#define REGISTER_PROTO(_dom_class) \\\n"
" aData[eDOMClassInfo_##_dom_class##_id].mDefineDOMInterface = _dom_class##Wrapper::getPrototype\n"
"\n")
for clazz in config.classes.itervalues():
f.write(" REGISTER_PROTO(%s);\n" % clazz.name)
f.write("\n"
"#undef REGISTER_PROTO\n"
"}\n\n")
f.write("}\n"
"}\n"
"}\n")
finally:
f.close()
def main():
from optparse import OptionParser
o = OptionParser(usage="usage: %prog [options] configfile")
o.add_option('-o', "--stub-output",
type='string', dest='stub_output', default=None,
help="Quick stub C++ source output file", metavar="FILE")
o.add_option('--header-output', type='string', default=None,
help="Quick stub header output file", metavar="FILE")
o.add_option('--makedepend-output', type='string', default=None,
help="gnumake dependencies output file", metavar="FILE")
o.add_option('--idlpath', type='string', default='.',
help="colon-separated directories to search for idl files",
metavar="PATH")
o.add_option('--cachedir', dest='cachedir', default='',
help="Directory in which to cache lex/parse tables.")
o.add_option("--verbose-errors", action='store_true', default=False,
help="When an error happens, display the Python traceback.")
(options, filenames) = o.parse_args()
if len(filenames) != 1:
o.error("Exactly one config filename is needed.")
filename = filenames[0]
if options.cachedir != '':
sys.path.append(options.cachedir)
if not os.path.isdir(options.cachedir):
os.makedirs(options.cachedir)
try:
includePath = options.idlpath.split(':')
conf = readConfigFile(filename,
includePath=includePath)
if options.header_output is not None:
writeHeaderFile(options.header_output, conf)
elif options.stub_output is not None:
interfaces = completeConfiguration(conf,
includePath=includePath,
cachedir=options.cachedir)
writeStubFile(options.stub_output, conf, interfaces)
if options.makedepend_output is not None:
writeMakeDependOutput(options.makedepend_output)
except Exception, exc:
if options.verbose_errors:
raise
elif isinstance(exc, (UserError, xpidl.IDLError)):
warn(str(exc))
elif isinstance(exc, OSError):
warn("%s: %s" % (exc.__class__.__name__, exc))
else:
raise
sys.exit(1)
if __name__ == '__main__':
main()

View File

@ -256,7 +256,8 @@ def checkStubMember(member, isCustom):
# Check for unknown properties.
for attrname, value in vars(member).items():
if value is True and attrname not in ('readonly','optional_argc',
'traceable','implicit_jscontext'):
'traceable','implicit_jscontext',
'getter', 'stringifier'):
raise UserError("%s %s: unrecognized property %r"
% (member.kind.capitalize(), memberId,
attrname))

View File

@ -502,6 +502,18 @@ class Interface(object):
if not isinstance(m, CDATA):
self.namemap.set(m)
self.ops = {
'index':
{
'getter': None
},
'name':
{
'getter': None
},
'stringifier': None
}
def __eq__(self, other):
return self.name == other.name and self.location == other.location
@ -530,8 +542,16 @@ class Interface(object):
if self.attributes.scriptable and not realbase.attributes.scriptable:
print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True)
forwardedMembers = set()
for member in self.members:
member.resolve(self)
if member.kind is 'method' and member.forward:
forwardedMembers.add(member.forward)
for member in self.members:
if member.kind is 'method' and member.name in forwardedMembers:
forwardedMembers.remove(member.name)
for member in forwardedMembers:
raise IDLError("member '%s' on interface '%s' forwards to '%s' which is not on the interface itself" % (member.name, self.name, member.forward), self.location)
# The number 250 is NOT arbitrary; this number is the maximum number of
# stub entries defined in xpcom/reflect/xptcall/public/genstubs.pl
@ -795,6 +815,9 @@ class Method(object):
nostdcall = False
optional_argc = False
deprecated = False
getter = False
stringifier = False
forward = None
def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
self.type = type
@ -813,6 +836,13 @@ class Method(object):
self.binaryname = value
continue
if name == 'forward':
if value is None:
raise IDLError("forward attribute requires a value",
aloc)
self.forward = value
continue
if value is not None:
raise IDLError("Unexpected attribute value", aloc)
@ -829,6 +859,14 @@ class Method(object):
self.deprecated = True
elif name == 'nostdcall':
self.nostdcall = True
elif name == 'getter':
if (len(self.params) != 1):
raise IDLError("Methods marked as getter must take 1 argument", aloc)
self.getter = True
elif name == 'stringifier':
if (len(self.params) != 0):
raise IDLError("Methods marked as stringifier must take 0 arguments", aloc)
self.stringifier = True
else:
raise IDLError("Unexpected attribute '%s'" % name, aloc)
@ -841,6 +879,23 @@ class Method(object):
self.realtype = self.iface.idl.getName(self.type, self.location)
for p in self.params:
p.resolve(self)
if self.getter:
if getBuiltinOrNativeTypeName(self.params[0].realtype) == 'unsigned long':
ops = 'index'
else:
if getBuiltinOrNativeTypeName(self.params[0].realtype) != '[domstring]':
raise IDLError("a getter must take a single unsigned long or DOMString argument" % self.iface.name, self.location)
ops = 'name'
if self.iface.ops[ops]['getter']:
raise IDLError("a %s getter was already defined on interface '%s'" % (ops, self.iface.name), self.location)
self.iface.ops[ops]['getter'] = self
if self.stringifier:
if self.iface.ops['stringifier']:
raise IDLError("a stringifier was already defined on interface '%s'" % self.iface.name, self.location)
if getBuiltinOrNativeTypeName(self.realtype) != '[domstring]':
raise IDLError("'stringifier' attribute can only be used on methods returning DOMString",
self.location)
self.iface.ops['stringifier'] = self
def isScriptable(self):
if not self.iface.attributes.scriptable: return False