Bug 679509 - Make sure convert hooks in plugin code always return a primitive value. r=mrbkap, r=luke, r=jst

--HG--
extra : rebase_source : adf240c22329d3461a2c939d11798247a98b867f
This commit is contained in:
Jeff Walden 2011-10-14 16:27:50 -07:00
Родитель 47acf14974
Коммит 97742a5075
5 изменённых файлов: 91 добавлений и 7 удалений

Просмотреть файл

@ -1684,15 +1684,39 @@ NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
}
static JSBool
NPObjWrapper_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
NPObjWrapper_Convert(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
{
// The sole reason we implement this hook is to prevent the JS
// engine from calling valueOf() on NPObject's. Some NPObject's may
// actually implement a method named valueOf, but it's unlikely to
// behave as the JS engine expects it to. IOW, this is an empty hook
// that overrides what the default hook does.
JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
return JS_TRUE;
// Plugins do not simply use JS_ConvertStub, and the default [[DefaultValue]]
// behavior, because that behavior involves calling toString or valueOf on
// objects which weren't designed to accommodate this. Usually this wouldn't
// be a problem, because the absence of either property, or the presence of
// either property with a value that isn't callable, will cause that property
// to simply be ignored. But there is a problem in one specific case: Java,
// specifically java.lang.Integer. The Integer class has static valueOf
// methods, none of which are nullary, so the JS-reflected method will behave
// poorly when called with no arguments. We work around this problem by
// giving plugins a [[DefaultValue]] which uses only toString and not valueOf.
jsval v = JSVAL_VOID;
if (!JS_GetProperty(cx, obj, "toString", &v))
return false;
if (!JSVAL_IS_PRIMITIVE(v) && JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(v))) {
if (!JS_CallFunctionValue(cx, obj, v, 0, NULL, vp))
return false;
if (JSVAL_IS_PRIMITIVE(*vp))
return true;
}
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
JS_GET_CLASS(cx, obj)->name,
hint == JSTYPE_VOID
? "primitive type"
: hint == JSTYPE_NUMBER
? "number"
: "string");
return false;
}
static void
@ -2192,6 +2216,11 @@ NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
case JSTYPE_VOID:
case JSTYPE_STRING:
case JSTYPE_NUMBER:
*vp = memberPrivate->fieldValue;
if (!JSVAL_IS_PRIMITIVE(*vp)) {
return JS_DefaultValue(cx, JSVAL_TO_OBJECT(*vp), type, vp);
}
return JS_TRUE;
case JSTYPE_BOOLEAN:
case JSTYPE_OBJECT:
*vp = memberPrivate->fieldValue;

Просмотреть файл

@ -46,6 +46,7 @@ include $(topsrcdir)/config/rules.mk
_MOCHITEST_FILES = \
utils.js \
test_defaultValue.html \
test_getauthenticationinfo.html \
test_npobject_getters.html \
test_npruntime_npnevaluate.html \

Просмотреть файл

@ -0,0 +1,36 @@
<html>
<head>
<title>NPObject [[DefaultValue]] implementation</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="run()">
<embed id="plugin" type="application/x-test" wmode="window"></embed>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
function run() {
var plugin = document.getElementById("plugin");
var pluginProto = Object.getPrototypeOf(plugin);
plugin.propertyAndMethod = {};
plugin.propertyAndMethod + "baz";
ok(true, "|plugin.propertyAndMethod + \"baz\"| shouldn't assert");
pluginProto.propertyAndMethod = {};
pluginProto.propertyAndMethod + "quux";
ok(true, "|pluginProto.propertyAndMethod + \"quux\"| shouldn't assert");
plugin + "foo";
ok(true, "|plugin + \"foo\"| shouldn't assert");
pluginProto + "bar";
ok(true, "|pluginProto + \"bar\"| shouldn't assert");
SimpleTest.finish();
}
</script>
</body>
</html>

Просмотреть файл

@ -2926,6 +2926,15 @@ JS_IdToValue(JSContext *cx, jsid id, jsval *vp)
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
{
CHECK_REQUEST(cx);
JS_ASSERT(obj != NULL);
JS_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER);
return obj->defaultValue(cx, hint, vp);
}
JS_PUBLIC_API(JSBool)
JS_PropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{

Просмотреть файл

@ -3125,6 +3125,15 @@ JS_IdToValue(JSContext *cx, jsid id, jsval *vp);
#define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */
#define JSRESOLVE_WITH 0x20 /* resolve inside a with statement */
/*
* Invoke the [[DefaultValue]] hook (see ES5 8.6.2) with the provided hint on
* the specified object, computing a primitive default value for the object.
* The hint must be JSTYPE_STRING, JSTYPE_NUMBER, or JSTYPE_VOID (no hint). On
* success the resulting value is stored in *vp.
*/
extern JS_PUBLIC_API(JSBool)
JS_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp);
extern JS_PUBLIC_API(JSBool)
JS_PropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp);