Bug 478251. Implement the Null and Undefined annotations from webidl in quickstubs. r=jst,bsmedberg,jorendorff

This commit is contained in:
Boris Zbarsky 2009-09-24 13:59:43 -04:00
parent 177d076ace
commit 6607af54ad
10 changed files with 206 additions and 67 deletions

View file

@ -360,7 +360,7 @@
try {
root.querySelectorAll(undefined);
} catch(e){ pass = e.code == DOMException.SYNTAX_ERR; }
assert_todo( pass, type + ".querySelectorAll undefined" );
assert( pass, type + ".querySelectorAll undefined" );
pass = false;
try {
@ -385,7 +385,7 @@
try {
root.querySelector(undefined);
} catch(e){ pass = e.code == DOMException.SYNTAX_ERR; }
assert_todo( pass, type + ".querySelector undefined" );
assert( pass, type + ".querySelector undefined" );
pass = false;
try {
@ -487,14 +487,6 @@
}
}
function assert_todo(pass, title) {
if (window.parent && window.parent.SimpleTest) {
window.parent.SimpleTest.todo(pass, title);
} else {
assert(pass, title);
}
}
function jqTests(type, root, select) {
function query(q, resolver){

View file

@ -90,6 +90,7 @@ _TEST_FILES = test_bug1682.html \
bug448564-iframe-3.html \
bug448564-echo.sjs \
bug448564-submit.js \
test_bug478251.html \
test_bug481440.html \
test_bug481647.html \
test_bug482659.html \

View file

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=478251
-->
<head>
<title>Test for Bug 478251</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=478251">Mozilla Bug 478251</a>
<p id="display"><iframe id="t"></iframe></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 478251 **/
var doc = $("t").contentDocument;
doc.open();
doc.write(null);
doc.close();
is(doc.documentElement.textContent, "null", "Writing |null| failed");
doc.open();
doc.write(null, null);
doc.close();
is(doc.documentElement.textContent, "nullnull", "Writing |null, null| failed");
doc.open();
doc.write(undefined);
doc.close();
is(doc.documentElement.textContent, "undefined", "Writing |undefined| failed");
doc.open();
doc.write(undefined, undefined);
doc.close();
is(doc.documentElement.textContent, "undefinedundefined", "Writing |undefined, undefined| failed");
doc.open();
doc.writeln(null);
doc.close();
is(doc.documentElement.textContent, "null\n", "Writing |null\\n| failed");
doc.open();
doc.writeln(null, null);
doc.close();
is(doc.documentElement.textContent, "nullnull\n", "Writing |null, null\\n| failed");
doc.open();
doc.writeln(undefined);
doc.close();
is(doc.documentElement.textContent, "undefined\n", "Writing |undefined\\n| failed");
doc.open();
doc.writeln(undefined, undefined);
doc.close();
is(doc.documentElement.textContent, "undefinedundefined\n", "Writing |undefined, undefined\\n| failed");
</script>
</pre>
</body>
</html>

View file

@ -48,6 +48,6 @@
[scriptable, uuid(7cebc153-168a-416c-ba5a-56a8c2ddb2ec)]
interface nsIDOMNodeSelector : nsISupports
{
nsIDOMElement querySelector(in DOMString selectors);
nsIDOMNodeList querySelectorAll(in DOMString selectors);
nsIDOMElement querySelector([Null(Empty), Undefined(Empty)] in DOMString selectors);
nsIDOMNodeList querySelectorAll([Null(Empty), Undefined(Empty)] in DOMString selectors);
};

View file

@ -73,8 +73,8 @@ interface nsIDOMHTMLDocument : nsIDOMDocument
[noscript] void open();
void close();
void write([optional] in DOMString text);
void writeln([optional] in DOMString text);
void write([optional, Null(Stringify)] in DOMString text);
void writeln([optional, Null(Stringify)] in DOMString text);
nsIDOMNodeList getElementsByName(in DOMString elementName);
};

View file

@ -520,7 +520,9 @@ nsIDOMNode_GetChildNodes_customMethodCallCode = """
nsIDOMHTMLDocument_Write_customMethodCallCode = """
nsAString &str = arg0;
for (uintN i = 1; i < argc; ++i) {
xpc_qsDOMString next_arg(cx, argv[i], &argv[i]);
xpc_qsDOMString next_arg(cx, argv[i], &argv[i],
xpc_qsDOMString::eStringify,
xpc_qsDOMString::eStringify);
if (!next_arg.IsValid())
return JS_FALSE;

View file

@ -399,7 +399,9 @@ argumentUnboxingTemplates = {
" return JS_FALSE;\n",
'[domstring]':
" xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr});\n"
" xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr},\n"
" xpc_qsDOMString::e${nullBehavior},\n"
" xpc_qsDOMString::e${undefinedBehavior});\n"
" if (!${name}.IsValid())\n"
" return JS_FALSE;\n",
@ -424,7 +426,8 @@ argumentUnboxingTemplates = {
# Omitted optional arguments are treated as though the caller had passed JS
# `null`; this behavior is from XPCWrappedNative::CallMethod.
#
def writeArgumentUnboxing(f, i, name, type, haveCcx, optional, rvdeclared):
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,
@ -450,7 +453,9 @@ def writeArgumentUnboxing(f, i, name, type, haveCcx, optional, rvdeclared):
params = {
'name': name,
'argVal': argVal,
'argPtr': argPtr
'argPtr': argPtr,
'nullBehavior': nullBehavior or 'DefaultNullBehavior',
'undefinedBehavior': undefinedBehavior or 'DefaultUndefinedBehavior'
}
typeName = getBuiltinOrNativeTypeName(type)
@ -791,11 +796,15 @@ def writeQuickStub(f, customMethodCalls, member, stubName, isSetter=False):
f, i, 'arg%d' % i, param.realtype,
haveCcx=haveCcx,
optional=param.optional,
rvdeclared=rvdeclared)
rvdeclared=rvdeclared,
nullBehavior=param.null,
undefinedBehavior=param.undefined)
elif isSetter:
rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype,
haveCcx=False, optional=False,
rvdeclared=rvdeclared)
rvdeclared=rvdeclared,
nullBehavior=member.null,
undefinedBehavior=member.undefined)
canFail = customMethodCall is None or customMethodCall.get('canFail', False)
if canFail and not rvdeclared:

View file

@ -679,7 +679,9 @@ xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
ThrowBadArg(cx, rv, ifaceName, memberName, 0);
}
xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval)
xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval,
StringificationBehavior nullBehavior,
StringificationBehavior undefinedBehavior)
{
// From the T_DOMSTRING case in XPCConvert::JSData2Native.
typedef implementation_type::char_traits traits;
@ -693,47 +695,20 @@ xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval)
}
else
{
StringificationBehavior behavior = eStringify;
if(JSVAL_IS_NULL(v))
{
(new(mBuf) implementation_type(
traits::sEmptyBuffer, PRUint32(0)))->SetIsVoid(PR_TRUE);
mValid = JS_TRUE;
return;
behavior = nullBehavior;
}
else if(JSVAL_IS_VOID(v))
{
behavior = undefinedBehavior;
}
s = JS_ValueToString(cx, v);
if(!s)
{
mValid = JS_FALSE;
return;
}
*pval = STRING_TO_JSVAL(s); // Root the new string.
}
len = JS_GetStringLength(s);
chars = (len == 0 ? traits::sEmptyBuffer : (const PRUnichar*)JS_GetStringChars(s));
new(mBuf) implementation_type(chars, len);
mValid = JS_TRUE;
}
xpc_qsAString::xpc_qsAString(JSContext *cx, jsval v, jsval *pval)
{
// From the T_ASTRING case in XPCConvert::JSData2Native.
typedef implementation_type::char_traits traits;
JSString *s;
const PRUnichar *chars;
size_t len;
if(JSVAL_IS_STRING(v))
{
s = JSVAL_TO_STRING(v);
}
else
{
if(JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
if (behavior != eStringify)
{
(new(mBuf) implementation_type(
traits::sEmptyBuffer, PRUint32(0)))->SetIsVoid(PR_TRUE);
traits::sEmptyBuffer, PRUint32(0)))->SetIsVoid(behavior == eNull);
mValid = JS_TRUE;
return;
}

View file

@ -257,17 +257,41 @@ protected:
class xpc_qsDOMString : public xpc_qsBasicString<nsAString, nsDependentString>
{
public:
xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval);
/* Enum that defines how JS |null| and |undefined| should be treated. See
* the WebIDL specification. eStringify means convert to the string "null"
* or "undefined" respectively, via the standard JS ToString() operation;
* eEmpty means convert to the string ""; eNull means convert to an empty
* string with the void bit set.
*
* Per webidl the default behavior of an unannotated interface is
* eStringify, but our de-facto behavior has been eNull for |null| and
* eStringify for |undefined|, so leaving it that way for now. If we ever
* get to a point where we go through and annotate our interfaces as
* needed, we can change that.
*/
enum StringificationBehavior {
eStringify,
eEmpty,
eNull,
eDefaultNullBehavior = eNull,
eDefaultUndefinedBehavior = eStringify
};
xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval,
StringificationBehavior nullBehavior,
StringificationBehavior undefinedBehavior);
};
/**
* The same as xpc_qsDOMString, but with slightly different conversion behavior,
* corresponding to the [astring] magic XPIDL annotation rather than [domstring].
*/
class xpc_qsAString : public xpc_qsBasicString<nsAString, nsDependentString>
class xpc_qsAString : public xpc_qsDOMString
{
public:
xpc_qsAString(JSContext *cx, jsval v, jsval *pval);
xpc_qsAString(JSContext *cx, jsval v, jsval *pval)
: xpc_qsDOMString(cx, v, pval, eNull, eNull)
{}
};
/**

View file

@ -99,6 +99,22 @@ def paramAttlistToIDL(attlist):
return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '')
for name, value, aloc in sorted])
def unaliasType(t):
while t.kind == 'typedef':
t = t.realtype
assert t is not None
return t
def getBuiltinOrNativeTypeName(t):
t = unaliasType(t)
if t.kind == 'builtin':
return t.name
elif t.kind == 'native':
assert t.specialtype is not None
return '[%s]' % t.specialtype
else:
return None
class BuiltinLocation(object):
def get(self):
return "<builtin type>"
@ -624,6 +640,8 @@ class Attribute(object):
notxpcom = False
readonly = False
binaryname = None
null = None
undefined = None
def __init__(self, type, name, attlist, readonly, location, doccomments):
self.type = type
@ -642,19 +660,48 @@ class Attribute(object):
self.binaryname = value
continue
if value is not None:
raise IDLError("Unexpected attribute value", aloc)
if name == 'noscript':
self.noscript = True
elif name == 'notxpcom':
self.notxpcom = True
if name == 'Null':
if value is None:
raise IDLError("'Null' attribute requires a value", aloc)
if readonly:
raise IDLError("'Null' attribute only makes sense for setters",
aloc);
if value not in ('Empty', 'Null', 'Stringify'):
raise IDLError("'Null' attribute value must be 'Empty', 'Null' or 'Stringify'",
aloc);
self.null = value
elif name == 'Undefined':
if value is None:
raise IDLError("'Undefined' attribute requires a value", aloc)
if readonly:
raise IDLError("'Undefined' attribute only makes sense for setters",
aloc);
if value not in ('Empty', 'Null'):
raise IDLError("'Undefined' attribute value must be 'Empty' or 'Null'",
aloc);
self.undefined = value
else:
raise IDLError("Unexpected attribute '%s'", aloc)
if value is not None:
raise IDLError("Unexpected attribute value", aloc)
if name == 'noscript':
self.noscript = True
elif name == 'notxpcom':
self.notxpcom = True
else:
raise IDLError("Unexpected attribute '%s'", aloc)
def resolve(self, iface):
self.iface = iface
self.realtype = iface.idl.getName(self.type, self.location)
if (self.null is not None and
getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
raise IDLError("'Null' attribute can only be used on DOMString",
self.location)
if (self.undefined is not None and
getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
raise IDLError("'Undefined' attribute can only be used on DOMString",
self.location)
def toIDL(self):
attribs = attlistToIDL(self.attlist)
@ -741,6 +788,8 @@ class Param(object):
retval = False
shared = False
optional = False
null = None
undefined = None
def __init__(self, paramtype, type, name, attlist, location, realtype=None):
self.paramtype = paramtype
@ -760,6 +809,20 @@ class Param(object):
if value is None:
raise IDLError("'iid_is' must specify a parameter", aloc)
self.iid_is = value
elif name == 'Null':
if value is None:
raise IDLError("'Null' must specify a parameter", aloc)
if value not in ('Empty', 'Null', 'Stringify'):
raise IDLError("'Null' parameter value must be 'Empty', 'Null', or 'Stringify'",
aloc);
self.null = value
elif name == 'Undefined':
if value is None:
raise IDLError("'Undefined' must specify a parameter", aloc)
if value not in ('Empty', 'Null'):
raise IDLError("'Undefined' parameter value must be 'Empty' or 'Null'",
aloc);
self.undefined = value
else:
if value is not None:
raise IDLError("Unexpected value for attribute '%s'" % name,
@ -782,6 +845,14 @@ class Param(object):
self.realtype = method.iface.idl.getName(self.type, self.location)
if self.array:
self.realtype = Array(self.realtype)
if (self.null is not None and
getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
raise IDLError("'Null' attribute can only be used on DOMString",
self.location)
if (self.undefined is not None and
getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
raise IDLError("'Undefined' attribute can only be used on DOMString",
self.location)
def nativeType(self):
kwargs = {}