Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index e64a520..a5b3543 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -1630,10 +1630,14 @@ static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret)
if(prop->type == PROP_JSVAL) { jsval_release(prop->u.val); - prop->type = PROP_DELETED; } - if(prop->type == PROP_ACCESSOR) - FIXME("not supported on accessor property\n"); + if(prop->type == PROP_ACCESSOR) { + if(prop->u.accessor.getter) + jsdisp_release(prop->u.accessor.getter); + if(prop->u.accessor.setter) + jsdisp_release(prop->u.accessor.setter); + } + prop->type = PROP_DELETED; return S_OK; }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/array.c | 13 +----- dlls/jscript/bool.c | 4 +- dlls/jscript/date.c | 11 +---- dlls/jscript/dispex.c | 22 +++++++--- dlls/jscript/enumerator.c | 4 +- dlls/jscript/error.c | 4 +- dlls/jscript/function.c | 2 +- dlls/jscript/global.c | 2 +- dlls/jscript/jscript.c | 2 +- dlls/jscript/jscript.h | 5 ++- dlls/jscript/json.c | 2 +- dlls/jscript/jsregexp.c | 4 +- dlls/jscript/math.c | 2 +- dlls/jscript/number.c | 14 +----- dlls/jscript/object.c | 18 +------- dlls/jscript/set.c | 8 ++-- dlls/jscript/string.c | 14 +----- dlls/jscript/tests/run.c | 92 +++++++++++++++++++++++++++++++++++++++ dlls/jscript/vbarray.c | 2 +- 19 files changed, 139 insertions(+), 86 deletions(-)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index 7941031..ad14abf 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1222,15 +1222,6 @@ static HRESULT Array_unshift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsi return S_OK; }
-static HRESULT Array_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) -{ - ArrayInstance *array = array_from_jsdisp(jsthis); - - TRACE("\n"); - - return array_join(ctx, &array->dispex, array->length, L",", 1, r); -} - static void Array_destructor(jsdisp_t *dispex) { heap_free(dispex); @@ -1279,7 +1270,7 @@ static const builtin_prop_t Array_props[] = {
static const builtin_info_t Array_info = { JSCLASS_ARRAY, - {NULL, NULL,0, Array_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(Array_props), Array_props, Array_destructor, @@ -1292,7 +1283,7 @@ static const builtin_prop_t ArrayInst_props[] = {
static const builtin_info_t ArrayInst_info = { JSCLASS_ARRAY, - {NULL, NULL,0, Array_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(ArrayInst_props), ArrayInst_props, Array_destructor, diff --git a/dlls/jscript/bool.c b/dlls/jscript/bool.c index 44a8b72..15229d8 100644 --- a/dlls/jscript/bool.c +++ b/dlls/jscript/bool.c @@ -114,7 +114,7 @@ static const builtin_prop_t Bool_props[] = {
static const builtin_info_t Bool_info = { JSCLASS_BOOLEAN, - {NULL, Bool_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Bool_value), ARRAY_SIZE(Bool_props), Bool_props, NULL, @@ -123,7 +123,7 @@ static const builtin_info_t Bool_info = {
static const builtin_info_t BoolInst_info = { JSCLASS_BOOLEAN, - {NULL, Bool_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Bool_value), 0, NULL, NULL, NULL diff --git a/dlls/jscript/date.c b/dlls/jscript/date.c index 96d366c..5627ba4 100644 --- a/dlls/jscript/date.c +++ b/dlls/jscript/date.c @@ -1846,13 +1846,6 @@ static HRESULT Date_setYear(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsi return S_OK; }
-static HRESULT Date_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) -{ - TRACE("\n"); - - return dateobj_to_string(date_from_jsdisp(jsthis), r); -} - static const builtin_prop_t Date_props[] = { {L"getDate", Date_getDate, PROPF_METHOD}, {L"getDay", Date_getDay, PROPF_METHOD}, @@ -1903,7 +1896,7 @@ static const builtin_prop_t Date_props[] = {
static const builtin_info_t Date_info = { JSCLASS_DATE, - {NULL, NULL,0, Date_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(Date_props), Date_props, NULL, @@ -1912,7 +1905,7 @@ static const builtin_info_t Date_info = {
static const builtin_info_t DateInst_info = { JSCLASS_DATE, - {NULL, NULL,0, Date_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), 0, NULL, NULL, NULL diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index a5b3543..a3da9fa 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -1591,6 +1591,11 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc jsval_t val; DWORD i;
+ if(id == DISPID_VALUE) { + hres = DISP_E_MEMBERNOTFOUND; + break; + } + for(i=0; i < pdp->cNamedArgs; i++) { if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT) break; @@ -1773,12 +1778,8 @@ HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *b jsdisp_addref(prototype);
dispex->prop_cnt = 1; - if(builtin_info->value_prop.invoke || builtin_info->value_prop.getter) { - dispex->props[0].type = PROP_BUILTIN; - dispex->props[0].u.p = &builtin_info->value_prop; - }else { - dispex->props[0].type = PROP_DELETED; - } + dispex->props[0].type = PROP_BUILTIN; + dispex->props[0].u.p = &builtin_info->value_prop;
script_addref(ctx); dispex->ctx = ctx; @@ -1786,9 +1787,16 @@ HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *b return S_OK; }
+HRESULT jsdisp_builtin_get_default_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) +{ + HRESULT hres = to_primitive(ctx, jsval_obj(jsthis), r, NO_HINT); + + return hres == JS_E_TO_PRIMITIVE ? DISP_E_MEMBERNOTFOUND : hres; +} + static const builtin_info_t dispex_info = { JSCLASS_NONE, - {NULL, NULL, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), 0, NULL, NULL, NULL diff --git a/dlls/jscript/enumerator.c b/dlls/jscript/enumerator.c index dea1940..35865da 100644 --- a/dlls/jscript/enumerator.c +++ b/dlls/jscript/enumerator.c @@ -180,7 +180,7 @@ static const builtin_prop_t Enumerator_props[] = {
static const builtin_info_t Enumerator_info = { JSCLASS_ENUMERATOR, - {NULL, NULL, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(Enumerator_props), Enumerator_props, NULL, @@ -189,7 +189,7 @@ static const builtin_info_t Enumerator_info = {
static const builtin_info_t EnumeratorInst_info = { JSCLASS_ENUMERATOR, - {NULL, NULL, 0, NULL}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), 0, NULL, Enumerator_destructor, diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index bb9e5a2..a5494f6 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -136,7 +136,7 @@ static const builtin_prop_t Error_props[] = {
static const builtin_info_t Error_info = { JSCLASS_ERROR, - {NULL, Error_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Error_value), ARRAY_SIZE(Error_props), Error_props, NULL, @@ -145,7 +145,7 @@ static const builtin_info_t Error_info = {
static const builtin_info_t ErrorInst_info = { JSCLASS_ERROR, - {NULL, Error_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Error_value), 0, NULL, NULL, diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index 318d6be..9998e8b 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -172,7 +172,7 @@ static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val)
static const builtin_info_t Arguments_info = { JSCLASS_ARGUMENTS, - {NULL, Arguments_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Arguments_value), 0, NULL, Arguments_destructor, NULL, diff --git a/dlls/jscript/global.c b/dlls/jscript/global.c index 2665bec..e488a37 100644 --- a/dlls/jscript/global.c +++ b/dlls/jscript/global.c @@ -904,7 +904,7 @@ static const builtin_prop_t JSGlobal_props[] = {
static const builtin_info_t JSGlobal_info = { JSCLASS_GLOBAL, - {NULL, NULL, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(JSGlobal_props), JSGlobal_props, NULL, diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 9148451..f2440b3 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -112,7 +112,7 @@ HRESULT create_named_item_script_obj(script_ctx_t *ctx, named_item_t *item) { static const builtin_info_t disp_info = { JSCLASS_GLOBAL, - {NULL, NULL, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), 0, NULL, NULL, NULL diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 69897cd..d37fbd1 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -230,6 +230,9 @@ typedef struct { builtin_setter_t setter; } builtin_prop_t;
+HRESULT jsdisp_builtin_get_default_value(script_ctx_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN; +#define JSDISP_DEFINE_BUILTIN_VALUE(value) {NULL, value,0, jsdisp_builtin_get_default_value} + typedef struct { jsclass_t class; builtin_prop_t value_prop; @@ -340,7 +343,7 @@ HRESULT Function_invoke(jsdisp_t*,IDispatch*,WORD,unsigned,jsval_t*,jsval_t*) DE HRESULT Function_value(script_ctx_t*,vdisp_t*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT Function_get_value(script_ctx_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN; struct _function_code_t *Function_get_code(jsdisp_t*) DECLSPEC_HIDDEN; -#define DEFAULT_FUNCTION_VALUE {NULL, Function_value,0, Function_get_value} +#define DEFAULT_FUNCTION_VALUE JSDISP_DEFINE_BUILTIN_VALUE(Function_value)
HRESULT throw_error(script_ctx_t*,HRESULT,const WCHAR*) DECLSPEC_HIDDEN; jsdisp_t *create_builtin_error(script_ctx_t *ctx) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/json.c b/dlls/jscript/json.c index f2fbb80..55fe124 100644 --- a/dlls/jscript/json.c +++ b/dlls/jscript/json.c @@ -839,7 +839,7 @@ static const builtin_prop_t JSON_props[] = {
static const builtin_info_t JSON_info = { JSCLASS_JSON, - {NULL, NULL, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(JSON_props), JSON_props, NULL, diff --git a/dlls/jscript/jsregexp.c b/dlls/jscript/jsregexp.c index f80452d..4d73715 100644 --- a/dlls/jscript/jsregexp.c +++ b/dlls/jscript/jsregexp.c @@ -563,7 +563,7 @@ static const builtin_prop_t RegExp_props[] = {
static const builtin_info_t RegExp_info = { JSCLASS_REGEXP, - {NULL, RegExp_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(RegExp_value), ARRAY_SIZE(RegExp_props), RegExp_props, RegExp_destructor, @@ -580,7 +580,7 @@ static const builtin_prop_t RegExpInst_props[] = {
static const builtin_info_t RegExpInst_info = { JSCLASS_REGEXP, - {NULL, RegExp_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(RegExp_value), ARRAY_SIZE(RegExpInst_props), RegExpInst_props, RegExp_destructor, diff --git a/dlls/jscript/math.c b/dlls/jscript/math.c index 475b9b2..d434366 100644 --- a/dlls/jscript/math.c +++ b/dlls/jscript/math.c @@ -492,7 +492,7 @@ static const builtin_prop_t Math_props[] = {
static const builtin_info_t Math_info = { JSCLASS_MATH, - {NULL, NULL, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(Math_props), Math_props, NULL, diff --git a/dlls/jscript/number.c b/dlls/jscript/number.c index 3c96532..dbd5b88 100644 --- a/dlls/jscript/number.c +++ b/dlls/jscript/number.c @@ -494,16 +494,6 @@ static HRESULT Number_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, un return S_OK; }
-static HRESULT Number_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) -{ - NumberInstance *number = number_from_jsdisp(jsthis); - - TRACE("(%p)\n", number); - - *r = jsval_number(number->value); - return S_OK; -} - static const builtin_prop_t Number_props[] = { {L"toExponential", Number_toExponential, PROPF_METHOD|1}, {L"toFixed", Number_toFixed, PROPF_METHOD}, @@ -515,7 +505,7 @@ static const builtin_prop_t Number_props[] = {
static const builtin_info_t Number_info = { JSCLASS_NUMBER, - {NULL, NULL,0, Number_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(Number_props), Number_props, NULL, @@ -524,7 +514,7 @@ static const builtin_info_t Number_info = {
static const builtin_info_t NumberInst_info = { JSCLASS_NUMBER, - {NULL, NULL,0, Number_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), 0, NULL, NULL, NULL diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index 89684f5..39248ab 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -243,20 +243,6 @@ static HRESULT Object_set_proto_(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t va return jsdisp_change_prototype(jsthis, proto); }
-static HRESULT Object_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) -{ - jsstr_t *ret; - - TRACE("\n"); - - ret = jsstr_alloc(L"[object Object]"); - if(!ret) - return E_OUTOFMEMORY; - - *r = jsval_string(ret); - return S_OK; -} - static void Object_destructor(jsdisp_t *dispex) { heap_free(dispex); @@ -274,7 +260,7 @@ static const builtin_prop_t Object_props[] = {
static const builtin_info_t Object_info = { JSCLASS_OBJECT, - {NULL, NULL,0, Object_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(Object_props), Object_props, Object_destructor, @@ -283,7 +269,7 @@ static const builtin_info_t Object_info = {
static const builtin_info_t ObjectInst_info = { JSCLASS_OBJECT, - {NULL, NULL,0, Object_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), 0, NULL, Object_destructor, NULL diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index 8f355f4..173f4f7 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -89,7 +89,7 @@ static const builtin_prop_t Set_props[] = {
static const builtin_info_t Set_prototype_info = { JSCLASS_SET, - {NULL, Set_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Set_value), ARRAY_SIZE(Set_props), Set_props, NULL, @@ -98,7 +98,7 @@ static const builtin_info_t Set_prototype_info = {
static const builtin_info_t Set_info = { JSCLASS_SET, - {NULL, Set_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Set_value), 0, NULL, NULL, NULL @@ -414,7 +414,7 @@ static const builtin_prop_t Map_props[] = {
static const builtin_info_t Map_prototype_info = { JSCLASS_OBJECT, - {NULL, Map_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Map_value), ARRAY_SIZE(Map_prototype_props), Map_prototype_props, NULL, @@ -423,7 +423,7 @@ static const builtin_info_t Map_prototype_info = {
static const builtin_info_t Map_info = { JSCLASS_MAP, - {NULL, Map_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(Map_value), ARRAY_SIZE(Map_props), Map_props, Map_destructor, diff --git a/dlls/jscript/string.c b/dlls/jscript/string.c index 4f6d8cd..dba0612 100644 --- a/dlls/jscript/string.c +++ b/dlls/jscript/string.c @@ -1493,16 +1493,6 @@ static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD fla return E_NOTIMPL; }
-static HRESULT String_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) -{ - StringInstance *This = string_from_jsdisp(jsthis); - - TRACE("\n"); - - *r = jsval_string(jsstr_addref(This->str)); - return S_OK; -} - static void String_destructor(jsdisp_t *dispex) { StringInstance *This = string_from_jsdisp(dispex); @@ -1579,7 +1569,7 @@ static const builtin_prop_t String_props[] = {
static const builtin_info_t String_info = { JSCLASS_STRING, - {NULL, NULL,0, String_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(String_props), String_props, String_destructor, @@ -1592,7 +1582,7 @@ static const builtin_prop_t StringInst_props[] = {
static const builtin_info_t StringInst_info = { JSCLASS_STRING, - {NULL, NULL,0, String_get_value}, + JSDISP_DEFINE_BUILTIN_VALUE(NULL), ARRAY_SIZE(StringInst_props), StringInst_props, String_destructor, diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index ef0f39f..0e2f633 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -2789,6 +2789,8 @@ static void test_retval(void)
static void test_default_value(void) { + static DISPID propput_dispid = DISPID_PROPERTYPUT; + IActiveScript *script; DISPPARAMS dp = {0}; IDispatch *disp; VARIANT v; @@ -2806,9 +2808,99 @@ static void test_default_value(void) { ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); } + VariantClear(&v); + IDispatch_Release(disp); + + hres = parse_script_expr(L"var arr = [5]; arr.toString = function() {return "foo";}; arr.valueOf = function() {return 42;}; arr", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08x\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v); + + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08x\n", hres); + ok(V_VT(&v) == VT_I4, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_I4(&v) == 42, "V_I4(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); + IDispatch_Release(disp); + close_script(script); + + hres = parse_script_expr(L"var arr = [5]; arr.toString = function() {return "foo";}; arr", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08x\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v);
+ V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08x\n", hres); + ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), L"foo"), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); VariantClear(&v); IDispatch_Release(disp); + close_script(script); + + hres = parse_script_expr(L"var arr = [5]; arr", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08x\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v); + + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08x\n", hres); + ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), L"5"), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + IDispatch_Release(disp); + close_script(script); + + hres = parse_script_expr(L"var obj = Object.prototype; delete obj.valueOf; obj", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08x\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v); + + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08x\n", hres); + ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), L"[object Object]"), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + IDispatch_Release(disp); + close_script(script); + + hres = parse_script_expr(L"var obj = Object.prototype; delete obj.toString; obj", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08x\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v); + + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == DISP_E_MEMBERNOTFOUND, "Invoke failed: %08x\n", hres); + IDispatch_Release(disp); + close_script(script); + + hres = parse_script_expr(L"Object.prototype", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08x\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v); + + dp.cArgs = dp.cNamedArgs = 1; + dp.rgdispidNamedArgs = &propput_dispid; + dp.rgvarg = &v; + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dp, NULL, NULL, NULL); + ok(hres == DISP_E_MEMBERNOTFOUND, "Invoke failed: %08x\n", hres); + IDispatch_Release(disp); + close_script(script); + + hres = parse_script_expr(L"var f = function() {return 42;}; f", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08x\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v); + + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dp, NULL, NULL, NULL); + ok(hres == DISP_E_MEMBERNOTFOUND, "Invoke failed: %08x\n", hres); + IDispatch_Release(disp); + close_script(script); }
static void test_script_exprs(void) diff --git a/dlls/jscript/vbarray.c b/dlls/jscript/vbarray.c index 69a77f1..50d53b9 100644 --- a/dlls/jscript/vbarray.c +++ b/dlls/jscript/vbarray.c @@ -251,7 +251,7 @@ static const builtin_prop_t VBArray_props[] = {
static const builtin_info_t VBArray_info = { JSCLASS_VBARRAY, - {NULL, VBArray_value, 0}, + JSDISP_DEFINE_BUILTIN_VALUE(VBArray_value), ARRAY_SIZE(VBArray_props), VBArray_props, VBArray_destructor,
Hi Gabriel,
On 11/19/21 7:03 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 69897cd..d37fbd1 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -230,6 +230,9 @@ typedef struct { builtin_setter_t setter; } builtin_prop_t;
+HRESULT jsdisp_builtin_get_default_value(script_ctx_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN; +#define JSDISP_DEFINE_BUILTIN_VALUE(value) {NULL, value,0, jsdisp_builtin_get_default_value}
Could we just handle DISPID_VALUE in dispex.c callers without using builtin_prop_t at all?
Thanks,
Jacek
On 22/11/2021 14:22, Jacek Caban wrote:
Hi Gabriel,
On 11/19/21 7:03 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 69897cd..d37fbd1 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -230,6 +230,9 @@ typedef struct { builtin_setter_t setter; } builtin_prop_t; +HRESULT jsdisp_builtin_get_default_value(script_ctx_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN; +#define JSDISP_DEFINE_BUILTIN_VALUE(value) {NULL, value,0, jsdisp_builtin_get_default_value}
Could we just handle DISPID_VALUE in dispex.c callers without using builtin_prop_t at all?
Thanks,
Jacek
How are we going to handle the methods then, for the jsdisps that have them? For example, Function_value. Wouldn't it require special-casing them? I thought it's less "elegant" since it doesn't re-use the same code path as now, just different builtin data.
On 11/22/21 1:52 PM, Gabriel Ivăncescu wrote:
On 22/11/2021 14:22, Jacek Caban wrote:
Hi Gabriel,
On 11/19/21 7:03 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 69897cd..d37fbd1 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -230,6 +230,9 @@ typedef struct { builtin_setter_t setter; } builtin_prop_t; +HRESULT jsdisp_builtin_get_default_value(script_ctx_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN; +#define JSDISP_DEFINE_BUILTIN_VALUE(value) {NULL, value,0, jsdisp_builtin_get_default_value}
Could we just handle DISPID_VALUE in dispex.c callers without using builtin_prop_t at all?
Thanks,
Jacek
How are we going to handle the methods then, for the jsdisps that have them? For example, Function_value. Wouldn't it require special-casing them? I thought it's less "elegant" since it doesn't re-use the same code path as now, just different builtin data.
I meant it only for getter, Function_value is for calling DISPID_VALUE. If getter is the same in every builtin_info_t, then it means that it's redundant, which is not really elegant. DISPID_VALUE is special in many ways, I don't see what's wrong with handling it separately.
Jacek
On 22/11/2021 16:41, Jacek Caban wrote:
On 11/22/21 1:52 PM, Gabriel Ivăncescu wrote:
On 22/11/2021 14:22, Jacek Caban wrote:
Hi Gabriel,
On 11/19/21 7:03 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 69897cd..d37fbd1 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -230,6 +230,9 @@ typedef struct { builtin_setter_t setter; } builtin_prop_t; +HRESULT jsdisp_builtin_get_default_value(script_ctx_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN; +#define JSDISP_DEFINE_BUILTIN_VALUE(value) {NULL, value,0, jsdisp_builtin_get_default_value}
Could we just handle DISPID_VALUE in dispex.c callers without using builtin_prop_t at all?
Thanks,
Jacek
How are we going to handle the methods then, for the jsdisps that have them? For example, Function_value. Wouldn't it require special-casing them? I thought it's less "elegant" since it doesn't re-use the same code path as now, just different builtin data.
I meant it only for getter, Function_value is for calling DISPID_VALUE. If getter is the same in every builtin_info_t, then it means that it's redundant, which is not really elegant. DISPID_VALUE is special in many ways, I don't see what's wrong with handling it separately.
Jacek
Well, it's not redundant since it's only for DISPID_VALUE props, but the code handling the getters is universal to *all* props not just DISPID_VALUE, which of course is not always the same. Right now we don't add any code and just re-use the existing prop code.
That said, if you still aren't convinced, I have a question. Should I remove the value_prop completely from the builtin_info_t and from the prop list? Right now it's always the first prop, i.e. zero, to match DISPID_VALUE.
Then, since I have to special-case it anyway, I'll just add a builtin_invoke_t value_invoke instead and call it if needed in InvokeEx, without having an actual prop for it.
Or do you want to keep the value_prop like now, and always have it NULL getter, and on top of that add special handling in InvokeEx? imo this adds more redundancy and extra code, but it's up to you.
Which choice should I go with?
Thanks, Gabriel
On 11/22/21 4:19 PM, Gabriel Ivăncescu wrote:
On 22/11/2021 16:41, Jacek Caban wrote:
On 11/22/21 1:52 PM, Gabriel Ivăncescu wrote:
On 22/11/2021 14:22, Jacek Caban wrote:
Hi Gabriel,
On 11/19/21 7:03 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 69897cd..d37fbd1 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -230,6 +230,9 @@ typedef struct { builtin_setter_t setter; } builtin_prop_t; +HRESULT jsdisp_builtin_get_default_value(script_ctx_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN; +#define JSDISP_DEFINE_BUILTIN_VALUE(value) {NULL, value,0, jsdisp_builtin_get_default_value}
Could we just handle DISPID_VALUE in dispex.c callers without using builtin_prop_t at all?
Thanks,
Jacek
How are we going to handle the methods then, for the jsdisps that have them? For example, Function_value. Wouldn't it require special-casing them? I thought it's less "elegant" since it doesn't re-use the same code path as now, just different builtin data.
I meant it only for getter, Function_value is for calling DISPID_VALUE. If getter is the same in every builtin_info_t, then it means that it's redundant, which is not really elegant. DISPID_VALUE is special in many ways, I don't see what's wrong with handling it separately.
Jacek
Well, it's not redundant since it's only for DISPID_VALUE props, but the code handling the getters is universal to *all* props not just DISPID_VALUE, which of course is not always the same. Right now we don't add any code and just re-use the existing prop code.
That said, if you still aren't convinced, I have a question. Should I remove the value_prop completely from the builtin_info_t and from the prop list? Right now it's always the first prop, i.e. zero, to match DISPID_VALUE.
Then, since I have to special-case it anyway, I'll just add a builtin_invoke_t value_invoke instead and call it if needed in InvokeEx, without having an actual prop for it.
Yes, removing value_prop seems right. I would call just "call" or something that would match [[Call]] from the spec.
Jacek
Some javascript libraries such as prototype.js use this (by setting to an array) to examine setAttribute quirks.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/nsembed.c | 3 +- dlls/mshtml/tests/documentmode.js | 77 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/nsembed.c b/dlls/mshtml/nsembed.c index 4224d6a..25ad92d 100644 --- a/dlls/mshtml/nsembed.c +++ b/dlls/mshtml/nsembed.c @@ -1013,7 +1013,8 @@ HRESULT variant_to_nsstr(VARIANT *v, BOOL hex_int, nsAString *nsstr) nsAString_Init(nsstr, buf); break;
- case VT_R8: { + case VT_R8: + case VT_DISPATCH: { VARIANT strv; HRESULT hres;
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index d5f33dd..954dafd 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1088,6 +1088,72 @@ sync_test("elem_attr", function() { r = elem.getAttribute("className"); ok(r === "cls3", "className attr = " + r);
+ var arr = [3]; + elem.setAttribute("testattr", arr); + r = elem.getAttribute("testattr"); + ok(r === (v < 8 ? arr : "3"), "testattr = " + r); + todo_wine_if(v === 8). + ok(elem.testattr === (v < 9 ? arr : undefined), "elem.testattr = " + elem.testattr); + r = elem.removeAttribute("testattr"); + ok(r === (v < 9 ? true : undefined), "testattr removeAttribute returned " + r); + ok(elem.testattr === undefined, "removed testattr = " + elem.testattr); + + arr[0] = 9; + elem.setAttribute("testattr", "string"); + elem.testattr = arr; + r = elem.getAttribute("testattr"); + todo_wine_if(v === 8). + ok(r === (v < 8 ? arr : (v < 9 ? "9" : "string")), "testattr = " + r); + ok(elem.testattr === arr, "elem.testattr = " + elem.testattr); + arr[0] = 3; + r = elem.getAttribute("testattr"); + todo_wine_if(v === 8). + ok(r === (v < 8 ? arr : (v < 9 ? "3" : "string")), "testattr = " + r); + ok(elem.testattr === arr, "elem.testattr = " + elem.testattr); + r = elem.removeAttribute("testattr"); + ok(r === (v < 9 ? true : undefined), "testattr removeAttribute returned " + r); + todo_wine_if(v === 8). + ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr = " + elem.testattr); + + arr.toString = function() { return 42; } + elem.testattr = arr; + r = elem.getAttribute("testattr"); + todo_wine_if(v === 8). + ok(r === (v < 8 ? arr : (v < 9 ? "42" : null)), "testattr with custom toString = " + r); + elem.setAttribute("testattr", arr); + r = elem.getAttribute("testattr"); + ok(r === (v < 8 ? arr : "42"), "testattr after setAttribute with custom toString = " + r); + ok(elem.testattr === arr, "elem.testattr after setAttribute with custom toString = " + elem.testattr); + r = elem.removeAttribute("testattr"); + ok(r === (v < 9 ? true : undefined), "testattr removeAttribute with custom toString returned " + r); + todo_wine_if(v === 8). + ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr with custom toString = " + elem.testattr); + + arr.valueOf = function() { return "arrval"; } + elem.testattr = arr; + r = elem.getAttribute("testattr"); + todo_wine_if(v === 8). + ok(r === (v < 8 ? arr : (v < 9 ? "arrval" : null)), "testattr with custom valueOf = " + r); + elem.setAttribute("testattr", arr); + r = elem.getAttribute("testattr"); + todo_wine_if(v >= 10). + ok(r === (v < 8 ? arr : (v < 10 ? "arrval" : "42")), "testattr after setAttribute with custom valueOf = " + r); + ok(elem.testattr === arr, "elem.testattr after setAttribute with custom valueOf = " + elem.testattr); + r = elem.removeAttribute("testattr"); + ok(r === (v < 9 ? true : undefined), "testattr removeAttribute with custom valueOf returned " + r); + todo_wine_if(v === 8). + ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr with custom valueOf = " + elem.testattr); + delete arr.valueOf; + delete arr.toString; + + elem.setAttribute("id", arr); + r = elem.getAttribute("id"); + todo_wine_if(v >= 8 && v < 10). + ok(r === (v < 8 || v >= 10 ? "3" : "[object]"), "id = " + r); + r = elem.removeAttribute("id"); + ok(r === (v < 9 ? true : undefined), "id removeAttribute returned " + r); + ok(elem.id === "", "removed id = " + elem.id); + var func = function() { }; elem.onclick = func; ok(elem.onclick === func, "onclick = " + elem.onclick); @@ -1136,6 +1202,17 @@ sync_test("elem_attr", function() { todo_wine_if(v >= 8). ok(elem.onclick === null, "removed onclick = " + elem.onclick);
+ elem.setAttribute("ondblclick", arr); + r = elem.getAttribute("ondblclick"); + todo_wine_if(v >= 8 && v < 10). + ok(r === (v < 8 ? arr : (v < 10 ? "[object]" : "3")), "ondblclick = " + r); + r = elem.removeAttribute("ondblclick"); + ok(r === (v < 8 ? false : (v < 9 ? true : undefined)), "ondblclick removeAttribute returned " + r); + r = Object.prototype.toString.call(elem.ondblclick); + todo_wine_if(v >= 9). + ok(r === (v < 8 ? "[object Array]" : (v < 9 ? "[object Object]" : (v < 11 ? "[object Null]" : "[object Function]"))), + "removed ondblclick Object.toString returned " + r); + elem.setAttribute("ondblclick", "string"); r = elem.getAttribute("ondblclick"); ok(r === "string", "ondblclick string = " + r);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102337
Your paranoid android.
=== w10pro64_ar (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w10pro64_he (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w10pro64_ja (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w10pro64_zh_CN (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 2 +- dlls/jscript/jscript.h | 3 ++- dlls/mshtml/script.c | 5 ++++- dlls/mshtml/tests/documentmode.js | 1 - 4 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index a3da9fa..7365100 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -1789,7 +1789,7 @@ HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *b
HRESULT jsdisp_builtin_get_default_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) { - HRESULT hres = to_primitive(ctx, jsval_obj(jsthis), r, NO_HINT); + HRESULT hres = to_primitive(ctx, jsval_obj(jsthis), r, ctx->version > SCRIPTLANGUAGEVERSION_ES5 ? HINT_STRING : NO_HINT);
return hres == JS_E_TO_PRIMITIVE ? DISP_E_MEMBERNOTFOUND : hres; } diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index d37fbd1..4d17b2f 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -45,7 +45,8 @@ * This is Wine jscript extension for ES5 and ES6 compatible mode. Allowed only in HTML mode. */ #define SCRIPTLANGUAGEVERSION_ES5 0x102 -#define SCRIPTLANGUAGEVERSION_ES6 0x103 +#define SCRIPTLANGUAGEVERSION_ES5b 0x103 +#define SCRIPTLANGUAGEVERSION_ES6 0x104
typedef struct _jsval_t jsval_t; typedef struct _jsstr_t jsstr_t; diff --git a/dlls/mshtml/script.c b/dlls/mshtml/script.c index 28e07a5..cc6a25b 100644 --- a/dlls/mshtml/script.c +++ b/dlls/mshtml/script.c @@ -65,7 +65,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(mshtml); /* See jscript.h in jscript.dll. */ #define SCRIPTLANGUAGEVERSION_HTML 0x400 #define SCRIPTLANGUAGEVERSION_ES5 0x102 -#define SCRIPTLANGUAGEVERSION_ES6 0x103 +#define SCRIPTLANGUAGEVERSION_ES5b 0x103 +#define SCRIPTLANGUAGEVERSION_ES6 0x104
struct ScriptHost { IActiveScriptSite IActiveScriptSite_iface; @@ -156,6 +157,8 @@ static BOOL init_script_engine(ScriptHost *script_host) if(IsEqualGUID(&script_host->guid, &CLSID_JScript)) { if(compat_mode >= COMPAT_MODE_IE11) script_mode = SCRIPTLANGUAGEVERSION_ES6; + else if(compat_mode >= COMPAT_MODE_IE10) + script_mode = SCRIPTLANGUAGEVERSION_ES5b; else if(compat_mode >= COMPAT_MODE_IE9) script_mode = SCRIPTLANGUAGEVERSION_ES5; script_mode |= SCRIPTLANGUAGEVERSION_HTML; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 954dafd..38a9545 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1136,7 +1136,6 @@ sync_test("elem_attr", function() { ok(r === (v < 8 ? arr : (v < 9 ? "arrval" : null)), "testattr with custom valueOf = " + r); elem.setAttribute("testattr", arr); r = elem.getAttribute("testattr"); - todo_wine_if(v >= 10). ok(r === (v < 8 ? arr : (v < 10 ? "arrval" : "42")), "testattr after setAttribute with custom valueOf = " + r); ok(elem.testattr === arr, "elem.testattr after setAttribute with custom valueOf = " + elem.testattr); r = elem.removeAttribute("testattr");
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102338
Your paranoid android.
=== w10pro64 (32 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w7u_el (32 bit report) ===
mshtml: script.c:624: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1637352313315 expected 1637352313455"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlelem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index d54f729..e8d108f 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -4005,7 +4005,7 @@ static HRESULT WINAPI HTMLElement3_put_contentEditable(IHTMLElement3 *iface, BST return E_NOTIMPL; }
- nsAString_InitDepend(&str, v); + nsAString_InitDepend(&str, v[0] ? v : L"inherit"); nsres = nsIDOMHTMLElement_SetContentEditable(This->html_element, &str); nsAString_Finish(&str);
For non-builtin props, getAttribute retrieves the stringified value of the prop. For builtins, however, getAttribute returns null unless they were set to a string.
Note that setAttribute stringifies the value if it's a builtin, and this works even for read-only builtins (unlike in previous modes).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
I'm testing for/htmlFor since prototype.js also uses it for translations, but it doesn't seem to be affected here.
Unfortunately this patch can't be split without introducing temporary bad behavior and failing tests. It does, however, add a lot more tests to it as well.
dlls/mshtml/dispex.c | 10 ++ dlls/mshtml/htmlelem.c | 115 ++++++++++++++--- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/tests/documentmode.js | 207 +++++++++++++++++++++++++++--- 4 files changed, 301 insertions(+), 32 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 4605fda..c30a44e 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -1412,6 +1412,16 @@ static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD return hres; }
+BOOL is_readonly_builtin(DispatchEx *dispex, DISPID id) +{ + func_info_t *func; + + if(FAILED(get_builtin_func(dispex->info, id, &func))) + return FALSE; + + return !func->put_vtbl_off; +} + HRESULT remove_attribute(DispatchEx *This, DISPID id, VARIANT_BOOL *success) { switch(get_dispid_type(id)) { diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index e8d108f..f853887 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -1086,18 +1086,59 @@ static HRESULT WINAPI HTMLElement_setAttribute(IHTMLElement *iface, BSTR strAttr VARIANT AttributeValue, LONG lFlags) { HTMLElement *This = impl_from_IHTMLElement(iface); + compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex); + VARIANT val = AttributeValue; + BOOL needs_free = FALSE; DISPID dispid; HRESULT hres;
TRACE("(%p)->(%s %s %08x)\n", This, debugstr_w(strAttributeName), debugstr_variant(&AttributeValue), lFlags);
- if(This->dom_element && dispex_compat_mode(&This->node.event_target.dispex) >= COMPAT_MODE_IE8) { + /* class and className are special case in IE8, behave like IE9 */ + if(compat_mode == COMPAT_MODE_IE8 && !wcsnicmp(strAttributeName, L"class", 5) && + (!strAttributeName[5] || !wcsicmp(strAttributeName + 5, L"Name"))) + compat_mode = COMPAT_MODE_IE9; + + if(compat_mode < COMPAT_MODE_IE9 || !This->dom_element) { + hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, strAttributeName, + (lFlags&ATTRFLAG_CASESENSITIVE ? fdexNameCaseSensitive : fdexNameCaseInsensitive) | fdexNameEnsure, &dispid); + if(FAILED(hres)) + return hres; + + if(compat_mode >= COMPAT_MODE_IE8 && get_dispid_type(dispid) == DISPEXPROP_BUILTIN) { + if(V_VT(&val) != VT_BSTR && V_VT(&val) != VT_NULL) { + V_VT(&val) = VT_EMPTY; + hres = VariantChangeTypeEx(&val, &AttributeValue, + MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR); + if(FAILED(hres)) + return hres; + + if(V_BSTR(&val)) + needs_free = TRUE; + else + V_VT(&val) = VT_NULL; + } + } + + /* style is special case */ + if(compat_mode == COMPAT_MODE_IE8 && dispid == DISPID_IHTMLELEMENT_STYLE) + compat_mode = COMPAT_MODE_IE9; + } + + if(compat_mode < COMPAT_MODE_IE9 || !This->dom_element) { + hres = set_elem_attr_value_by_dispid(This, dispid, &val); + if(FAILED(hres) && compat_mode < COMPAT_MODE_IE8) + goto done; + }else + hres = E_FAIL; + + if(hres == E_FAIL && This->dom_element) { nsAString name_str, value_str; nsresult nsres;
- hres = variant_to_nsstr(&AttributeValue, FALSE, &value_str); + hres = variant_to_nsstr(&val, FALSE, &value_str); if(FAILED(hres)) - return hres; + goto done;
nsAString_InitDepend(&name_str, strAttributeName); nsres = nsIDOMElement_SetAttribute(This->dom_element, &name_str, &value_str); @@ -1105,15 +1146,13 @@ static HRESULT WINAPI HTMLElement_setAttribute(IHTMLElement *iface, BSTR strAttr nsAString_Finish(&value_str); if(NS_FAILED(nsres)) WARN("SetAttribute failed: %08x\n", nsres); - return map_nsresult(nsres); + hres = map_nsresult(nsres); }
- hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, strAttributeName, - (lFlags&ATTRFLAG_CASESENSITIVE ? fdexNameCaseSensitive : fdexNameCaseInsensitive) | fdexNameEnsure, &dispid); - if(FAILED(hres)) - return hres; - - return set_elem_attr_value_by_dispid(This, dispid, &AttributeValue); +done: + if(needs_free) + SysFreeString(V_BSTR(&val)); + return hres; }
HRESULT get_elem_attr_value_by_dispid(HTMLElement *elem, DISPID dispid, VARIANT *ret) @@ -1156,6 +1195,9 @@ static HRESULT WINAPI HTMLElement_getAttribute(IHTMLElement *iface, BSTR strAttr LONG lFlags, VARIANT *AttributeValue) { HTMLElement *This = impl_from_IHTMLElement(iface); + compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex); + nsAString name_str, value_str; + nsresult nsres; DISPID dispid; HRESULT hres;
@@ -1164,10 +1206,12 @@ static HRESULT WINAPI HTMLElement_getAttribute(IHTMLElement *iface, BSTR strAttr if(lFlags & ~(ATTRFLAG_CASESENSITIVE|ATTRFLAG_ASSTRING)) FIXME("Unsupported flags %x\n", lFlags);
- if(This->dom_element && dispex_compat_mode(&This->node.event_target.dispex) >= COMPAT_MODE_IE8) { - nsAString name_str, value_str; - nsresult nsres; + /* class and className are special case in IE8, behave like IE9 */ + if(compat_mode == COMPAT_MODE_IE8 && !wcsnicmp(strAttributeName, L"class", 5) && + (!strAttributeName[5] || !wcsicmp(strAttributeName + 5, L"Name"))) + compat_mode = COMPAT_MODE_IE9;
+ if(This->dom_element && compat_mode >= COMPAT_MODE_IE9) { nsAString_InitDepend(&name_str, strAttributeName); nsAString_InitDepend(&value_str, NULL); nsres = nsIDOMElement_GetAttribute(This->dom_element, &name_str, &value_str); @@ -1187,8 +1231,37 @@ static HRESULT WINAPI HTMLElement_getAttribute(IHTMLElement *iface, BSTR strAttr return hres; }
+ if(This->dom_element && compat_mode >= COMPAT_MODE_IE8 && get_dispid_type(dispid) == DISPEXPROP_BUILTIN) { + if(is_readonly_builtin(&This->node.event_target.dispex, dispid)) { + nsAString_InitDepend(&name_str, strAttributeName); + nsAString_InitDepend(&value_str, NULL); + nsres = nsIDOMElement_GetAttribute(This->dom_element, &name_str, &value_str); + nsAString_Finish(&name_str); + return return_nsstr_variant(nsres, &value_str, 0, AttributeValue); + } + + hres = get_elem_attr_value_by_dispid(This, dispid, AttributeValue); + if(SUCCEEDED(hres) && V_VT(AttributeValue) != VT_BSTR) { + VariantClear(AttributeValue); + V_VT(AttributeValue) = VT_NULL; + } + return hres; + } + hres = get_elem_attr_value_by_dispid(This, dispid, AttributeValue); - if(SUCCEEDED(hres) && (lFlags & ATTRFLAG_ASSTRING)) + if(FAILED(hres)) + return hres; + + if(compat_mode >= COMPAT_MODE_IE8 && V_VT(AttributeValue) != VT_BSTR && V_VT(AttributeValue) != VT_NULL) { + hres = VariantChangeTypeEx(AttributeValue, AttributeValue, + MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR); + if(FAILED(hres)) { + VariantClear(AttributeValue); + return hres; + } + if(!V_BSTR(AttributeValue)) + V_VT(AttributeValue) = VT_NULL; + }else if(lFlags & ATTRFLAG_ASSTRING) hres = attr_value_to_string(AttributeValue); return hres; } @@ -1197,16 +1270,23 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA LONG lFlags, VARIANT_BOOL *pfSuccess) { HTMLElement *This = impl_from_IHTMLElement(iface); + compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex); DISPID id; HRESULT hres;
TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(strAttributeName), lFlags, pfSuccess);
- if(dispex_compat_mode(&This->node.event_target.dispex) >= COMPAT_MODE_IE8) { + /* class and className are special case in IE8, behave like IE9 */ + if(compat_mode == COMPAT_MODE_IE8 && !wcsnicmp(strAttributeName, L"class", 5) && + (!strAttributeName[5] || !wcsicmp(strAttributeName + 5, L"Name"))) + compat_mode = COMPAT_MODE_IE9; + + if(compat_mode >= COMPAT_MODE_IE8) { *pfSuccess = element_has_attribute(This, strAttributeName); if(*pfSuccess) return element_remove_attribute(This, strAttributeName); - return S_OK; + if(compat_mode >= COMPAT_MODE_IE9) + return S_OK; }
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, strAttributeName, @@ -6463,6 +6543,9 @@ static HRESULT HTMLElement_populate_props(DispatchEx *dispex) nsresult nsres; HRESULT hres;
+ if(dispex_compat_mode(dispex) >= COMPAT_MODE_IE9) + return S_OK; + if(!This->dom_element) return S_FALSE;
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 5cf53bb..101f005 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -396,6 +396,7 @@ const void *dispex_get_vtbl(DispatchEx*) DECLSPEC_HIDDEN; void dispex_info_add_interface(dispex_data_t*,tid_t,const dispex_hook_t*) DECLSPEC_HIDDEN; compat_mode_t dispex_compat_mode(DispatchEx*) DECLSPEC_HIDDEN; HRESULT dispex_to_string(DispatchEx*,BSTR*) DECLSPEC_HIDDEN; +BOOL is_readonly_builtin(DispatchEx*,DISPID) DECLSPEC_HIDDEN;
typedef enum { DISPEXPROP_CUSTOM, diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 38a9545..81af28b 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -518,9 +518,7 @@ sync_test("createElement_inline_attr", function() { for(var i = 0; i < tags.length; i++) { e = document.createElement("<" + tags[i] + " test='a"' abcd=""b"">"); ok(e.tagName === tags[i].toUpperCase(), "<" + tags[i] + " test="a" abcd="b">.tagName returned " + e.tagName); - todo_wine_if(v == 8). ok(e.test === "a"", "<" + tags[i] + " test='a"' abcd=""b"">.test returned " + e.test); - todo_wine_if(v == 8). ok(e.abcd === ""b"", "<" + tags[i] + " test='a"' abcd=""b"">.abcd returned " + e.abcd); } }else { @@ -1063,6 +1061,13 @@ sync_test("elem_attr", function() { var v = document.documentMode; var elem = document.createElement("div"), r;
+ function test_exposed(prop, expect) { + if(expect) + ok(prop in elem, prop + " is not exposed from elem"); + else + ok(!(prop in elem), prop + " is exposed from elem"); + } + r = elem.getAttribute("class"); ok(r === null, "class attr = " + r); r = elem.getAttribute("className"); @@ -1088,11 +1093,37 @@ sync_test("elem_attr", function() { r = elem.getAttribute("className"); ok(r === "cls3", "className attr = " + r);
+ elem.htmlFor = "for"; + r = elem.getAttribute("for"); + ok(r === null, "for attr = " + r); + r = elem.getAttribute("htmlFor"); + ok(r === (v < 9 ? "for" : null), "htmlFor attr = " + r); + + elem.setAttribute("for", "for2"); + ok(elem.htmlFor === "for", "elem.htmlFor = " + elem.htmlFor); + r = elem.getAttribute("for"); + ok(r === "for2", "for attr = " + r); + r = elem.getAttribute("htmlFor"); + ok(r === (v < 9 ? "for" : null), "htmlFor attr = " + r); + + elem.setAttribute("htmlFor", "for3"); + ok(elem.htmlFor === (v < 9 ? "for3" : "for"), "elem.htmlFor = " + elem.htmlFor); + r = elem.getAttribute("for"); + ok(r === "for2", "for attr = " + r); + r = elem.getAttribute("htmlFor"); + ok(r === "for3", "htmlFor attr = " + r); + + elem.setAttribute("testattr", "test"); + test_exposed("class", v < 8); + test_exposed("className", true); + test_exposed("for", v < 9); + test_exposed("htmlFor", true); + test_exposed("testattr", v < 9); + var arr = [3]; elem.setAttribute("testattr", arr); r = elem.getAttribute("testattr"); ok(r === (v < 8 ? arr : "3"), "testattr = " + r); - todo_wine_if(v === 8). ok(elem.testattr === (v < 9 ? arr : undefined), "elem.testattr = " + elem.testattr); r = elem.removeAttribute("testattr"); ok(r === (v < 9 ? true : undefined), "testattr removeAttribute returned " + r); @@ -1102,23 +1133,19 @@ sync_test("elem_attr", function() { elem.setAttribute("testattr", "string"); elem.testattr = arr; r = elem.getAttribute("testattr"); - todo_wine_if(v === 8). ok(r === (v < 8 ? arr : (v < 9 ? "9" : "string")), "testattr = " + r); ok(elem.testattr === arr, "elem.testattr = " + elem.testattr); arr[0] = 3; r = elem.getAttribute("testattr"); - todo_wine_if(v === 8). ok(r === (v < 8 ? arr : (v < 9 ? "3" : "string")), "testattr = " + r); ok(elem.testattr === arr, "elem.testattr = " + elem.testattr); r = elem.removeAttribute("testattr"); ok(r === (v < 9 ? true : undefined), "testattr removeAttribute returned " + r); - todo_wine_if(v === 8). ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr = " + elem.testattr);
arr.toString = function() { return 42; } elem.testattr = arr; r = elem.getAttribute("testattr"); - todo_wine_if(v === 8). ok(r === (v < 8 ? arr : (v < 9 ? "42" : null)), "testattr with custom toString = " + r); elem.setAttribute("testattr", arr); r = elem.getAttribute("testattr"); @@ -1126,13 +1153,11 @@ sync_test("elem_attr", function() { ok(elem.testattr === arr, "elem.testattr after setAttribute with custom toString = " + elem.testattr); r = elem.removeAttribute("testattr"); ok(r === (v < 9 ? true : undefined), "testattr removeAttribute with custom toString returned " + r); - todo_wine_if(v === 8). ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr with custom toString = " + elem.testattr);
arr.valueOf = function() { return "arrval"; } elem.testattr = arr; r = elem.getAttribute("testattr"); - todo_wine_if(v === 8). ok(r === (v < 8 ? arr : (v < 9 ? "arrval" : null)), "testattr with custom valueOf = " + r); elem.setAttribute("testattr", arr); r = elem.getAttribute("testattr"); @@ -1140,7 +1165,6 @@ sync_test("elem_attr", function() { ok(elem.testattr === arr, "elem.testattr after setAttribute with custom valueOf = " + elem.testattr); r = elem.removeAttribute("testattr"); ok(r === (v < 9 ? true : undefined), "testattr removeAttribute with custom valueOf returned " + r); - todo_wine_if(v === 8). ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr with custom valueOf = " + elem.testattr); delete arr.valueOf; delete arr.toString; @@ -1166,7 +1190,6 @@ sync_test("elem_attr", function() { elem.onclick_test = func; ok(elem.onclick_test === func, "onclick_test = " + elem.onclick_test); r = elem.getAttribute("onclick_test"); - todo_wine_if(v === 8). ok(r === (v < 8 ? func : (v < 9 ? func.toString() : null)), "onclick_test attr = " + r);
elem.setAttribute("onclick", "test"); @@ -1176,11 +1199,11 @@ sync_test("elem_attr", function() { ok(r === (v < 9 ? true : undefined), "removeAttribute after setAttribute returned " + r);
/* IE11 returns an empty function, which we can't check directly */ - todo_wine_if(v >= 8). + todo_wine_if(v >= 9). ok((v < 11) ? (elem.onclick === null) : (elem.onclick !== func), "removed onclick after setAttribute = " + elem.onclick);
r = Object.prototype.toString.call(elem.onclick); - todo_wine_if(v >= 8 && v < 11). + todo_wine_if(v >= 9 && v < 11). ok(r === (v < 9 ? "[object Object]" : (v < 11 ? "[object Null]" : "[object Function]")), "removed onclick after setAttribute Object.toString returned " + r);
@@ -1190,15 +1213,13 @@ sync_test("elem_attr", function() { elem.onclick = func; ok(elem.onclick === func, "onclick = " + elem.onclick); r = elem.getAttribute("onclick"); - todo_wine_if(v === 8). ok(r === (v < 8 ? func : (v < 9 ? null : "string")), "onclick attr = " + r); elem.onclick = "test"; r = elem.getAttribute("onclick"); - todo_wine_if(v === 8). ok(r === (v < 9 ? "test" : "string"), "onclick attr = " + r); r = elem.removeAttribute("onclick"); ok(r === (v < 9 ? true : undefined), "removeAttribute returned " + r); - todo_wine_if(v >= 8). + todo_wine_if(v >= 9). ok(elem.onclick === null, "removed onclick = " + elem.onclick);
elem.setAttribute("ondblclick", arr); @@ -1218,6 +1239,160 @@ sync_test("elem_attr", function() { r = elem.removeAttribute("ondblclick"); ok(r === (v < 9 ? true : undefined), "ondblclick string removeAttribute returned " + r); ok(elem.ondblclick === null, "removed ondblclick string = " + elem.ondblclick); + + if(v < 9) { + var props = [ + [ "contentEditable", "inherit", "true" ], + [ "dir", "", "ltr" ], + [ "id", "" ], + [ "lang", "" ], + [ "language", "" ], + [ "onbeforeactivate" ], + [ "onblur" ], + [ "oncontextmenu" ], + [ "ondataavailable" ], + [ "ondrag" ], + [ "ondragstart" ], + [ "onfocus" ], + [ "onfocusin" ], + [ "onfocusout" ], + [ "onhelp" ], + [ "onkeydown" ], + [ "onkeypress" ], + [ "onkeyup" ], + [ "onmousedown" ], + [ "onmousemove" ], + [ "onmouseout" ], + [ "onmouseover" ], + [ "onmouseup" ], + [ "onmousewheel" ], + [ "onpaste" ], + [ "onreadystatechange" ], + [ "onresize" ], + [ "onscroll" ], + [ "onselectstart" ], + [ "title", "" ] + ]; + + for(var i = 0; i < props.length; i++) { + var name = props[i][0]; + var val = props[i].length > 2 ? props[i][2] : "test"; + + r = elem.getAttribute(name); + todo_wine_if(v === 8 && props[i][0].substring(0, 2) !== "on"). + ok(r === (v < 8 && props[i].length > 1 ? props[i][1] : null), name + " attr before set = " + r); + eval("elem." + name + " = "" + val + ""; r = elem." + name + ";"); + ok(r === val, "elem." + name + " = " + r); + + r = elem.getAttribute(name); + ok(r === val, name + " attr = " + r); + r = elem.removeAttribute(name); + ok(r === true, "removeAttribute('" + name + "') returned " + r); + eval("r = elem." + name + ";"); + ok(r === (props[i].length > 1 ? props[i][1] : null), "removed elem." + name + " = " + r); + + elem.setAttribute(name, val); + r = elem.getAttribute(name); + ok(r === val, name + " attr after setAttribute = " + r); + eval("r = elem." + name + ";"); + ok(r === val, "elem." + name + " after setAttribute = " + r); + } + + /* read-only props */ + props = [ + "all", + "attributes", + "childNodes", + "children", + "clientHeight", + "clientLeft", + "clientTop", + "clientWidth", + "currentStyle", + "document", + "filters", + "firstChild", + "lastChild", + "nextSibling", + "nodeName", + "nodeType", + "offsetHeight", + "offsetLeft", + "offsetParent", + "offsetTop", + "offsetWidth", + "ownerDocument", + "parentElement", + "parentNode", + "previousSibling", + "readyState", + "runtimeStyle", + "sourceIndex", + "tagName", + "uniqueID", + "uniqueNumber" + ]; + + for(var i = 0; i < props.length; i++) { + var name = props[i], prop; + + try { + eval("elem." + name + " = "test";"); + ok(false, "expected exception setting elem." + name); + }catch(ex) { } + + r = elem.getAttribute(name); + eval("prop = elem." + name + ";"); + + if(v < 8) + ok(""+r === ""+prop, name + " attr = " + r); + else + ok(r === null, name + " attr = " + r); + r = elem.removeAttribute(name); + ok(r === false, "removeAttribute('" + name + "') returned " + r); + + try { + elem.setAttribute(name, "string"); + ok(v >= 8, "expected exception calling setAttribute('" + name + "')"); + }catch(ex) { + ok(v < 8, "did not expect exception calling setAttribute('" + name + "')"); + } + if(v >= 8) { + r = elem.getAttribute(name); + ok(r === "string", name + " attr after setAttribute = " + r); + eval("r = elem." + name + ";"); + todo_wine. + ok(r === "string", "elem." + name + " after setAttribute = " + r); + + r = elem.removeAttribute(name); + ok(r === true, "removeAttribute('" + name + "') after setAttribute returned " + r); + eval("r = elem." + name + ";"); + ok(""+r === ""+prop, name + " attr = " + r); + } + } + + /* style is special case */ + try { + elem.style = "opacity: 1.0"; + ok(false, "expected exception setting elem.style"); + }catch(ex) { } + + var style = elem.style; + r = elem.getAttribute("style"); + ok(r === (v < 8 ? style : null), "style attr = " + r); + r = elem.removeAttribute("style"); + ok(r === true, "removeAttribute('style') returned " + r); + r = elem.style; + ok(r === style, "removed elem.style = " + r); + r = elem.getAttribute("style"); + todo_wine_if(v === 8). + ok(r === (v < 8 ? style : null), "style attr after removal = " + r); + elem.setAttribute("style", "opacity: 1.0"); + r = elem.getAttribute("style"); + ok(r === (v < 8 ? style : "opacity: 1.0"), "style attr after setAttribute = " + r); + r = elem.style; + ok(r === style, "elem.style after setAttribute = " + r); + } });
sync_test("__proto__", function() {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102341
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w10pro64_he (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w7u_el (32 bit report) ===
mshtml: script.c:624: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1637357098858 expected 1637357099047"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/dispex.c | 2 +- dlls/mshtml/htmlelem.c | 15 +++++++++++++-- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/tests/documentmode.js | 2 -- 4 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index c30a44e..e626f70 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -1007,7 +1007,7 @@ static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **re return DISP_E_UNKNOWNNAME; }
-static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret) +HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret) { int min, max, n, c;
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index f853887..b1301c7 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -1087,8 +1087,8 @@ static HRESULT WINAPI HTMLElement_setAttribute(IHTMLElement *iface, BSTR strAttr { HTMLElement *This = impl_from_IHTMLElement(iface); compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex); + BOOL needs_free = FALSE, object_str = FALSE; VARIANT val = AttributeValue; - BOOL needs_free = FALSE; DISPID dispid; HRESULT hres;
@@ -1106,7 +1106,9 @@ static HRESULT WINAPI HTMLElement_setAttribute(IHTMLElement *iface, BSTR strAttr return hres;
if(compat_mode >= COMPAT_MODE_IE8 && get_dispid_type(dispid) == DISPEXPROP_BUILTIN) { - if(V_VT(&val) != VT_BSTR && V_VT(&val) != VT_NULL) { + if(V_VT(&val) == VT_DISPATCH && compat_mode < COMPAT_MODE_IE10) { + object_str = TRUE; + }else if(V_VT(&val) != VT_BSTR && V_VT(&val) != VT_NULL) { V_VT(&val) = VT_EMPTY; hres = VariantChangeTypeEx(&val, &AttributeValue, MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR); @@ -1123,6 +1125,15 @@ static HRESULT WINAPI HTMLElement_setAttribute(IHTMLElement *iface, BSTR strAttr /* style is special case */ if(compat_mode == COMPAT_MODE_IE8 && dispid == DISPID_IHTMLELEMENT_STYLE) compat_mode = COMPAT_MODE_IE9; + }else if(V_VT(&val) == VT_DISPATCH && compat_mode < COMPAT_MODE_IE10) { + object_str = get_builtin_id(&This->node.event_target.dispex, strAttributeName, fdexNameCaseInsensitive, &dispid) == S_OK; + } + + if(object_str) { + if(!(V_BSTR(&val) = SysAllocString(L"[object]"))) + return E_OUTOFMEMORY; + V_VT(&val) = VT_BSTR; + needs_free = TRUE; }
if(compat_mode < COMPAT_MODE_IE9 || !This->dom_element) { diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 101f005..cc5779b 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -384,6 +384,7 @@ extern void (__cdecl *note_cc_edge)(nsISupports*,const char*,nsCycleCollectionTr void init_dispatch(DispatchEx*,IUnknown*,dispex_static_data_t*,compat_mode_t) DECLSPEC_HIDDEN; void release_dispex(DispatchEx*) DECLSPEC_HIDDEN; BOOL dispex_query_interface(DispatchEx*,REFIID,void**) DECLSPEC_HIDDEN; +HRESULT get_builtin_id(DispatchEx*,BSTR,DWORD,DISPID*) DECLSPEC_HIDDEN; HRESULT dispex_get_dprop_ref(DispatchEx*,const WCHAR*,BOOL,VARIANT**) DECLSPEC_HIDDEN; HRESULT get_dispids(tid_t,DWORD*,DISPID**) DECLSPEC_HIDDEN; HRESULT remove_attribute(DispatchEx*,DISPID,VARIANT_BOOL*) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 81af28b..d01680b 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1171,7 +1171,6 @@ sync_test("elem_attr", function() {
elem.setAttribute("id", arr); r = elem.getAttribute("id"); - todo_wine_if(v >= 8 && v < 10). ok(r === (v < 8 || v >= 10 ? "3" : "[object]"), "id = " + r); r = elem.removeAttribute("id"); ok(r === (v < 9 ? true : undefined), "id removeAttribute returned " + r); @@ -1224,7 +1223,6 @@ sync_test("elem_attr", function() {
elem.setAttribute("ondblclick", arr); r = elem.getAttribute("ondblclick"); - todo_wine_if(v >= 8 && v < 10). ok(r === (v < 8 ? arr : (v < 10 ? "[object]" : "3")), "ondblclick = " + r); r = elem.removeAttribute("ondblclick"); ok(r === (v < 8 ? false : (v < 9 ? true : undefined)), "ondblclick removeAttribute returned " + r);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102342
Your paranoid android.
=== w10pro64_ja (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
If an attribute was set over a read-only builtin, reading the prop gives the attribute's value string, until it is removed, at which point it reverts back to retrieving the prop's value.
Hooks have to be used, though, because some builtins return non-string values (such as clientWidth returning integer value), so we need to override them.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This extends the hook callbacks with name, as well as the init_info method so that it optionally returns a structure that contains generic hooks for all read-only builtin props (and in next patch, for read-write builtins).
dlls/mshtml/dispex.c | 26 +++++++++++++-- dlls/mshtml/htmldoc.c | 3 +- dlls/mshtml/htmlelem.c | 54 ++++++++++++++++++++++++++++++- dlls/mshtml/htmlevent.c | 11 ++++--- dlls/mshtml/htmlnode.c | 4 +-- dlls/mshtml/htmlstyle.c | 3 +- dlls/mshtml/htmlstyle.h | 2 +- dlls/mshtml/htmlstyleelem.c | 6 ++-- dlls/mshtml/htmlstylesheet.c | 3 +- dlls/mshtml/htmlwindow.c | 4 +-- dlls/mshtml/mshtml_private.h | 20 ++++++++---- dlls/mshtml/tests/documentmode.js | 2 -- dlls/mshtml/xmlhttprequest.c | 9 +++--- 13 files changed, 114 insertions(+), 33 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index e626f70..846a678 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -470,11 +470,12 @@ static int __cdecl func_name_cmp(const void *p1, const void *p2)
static dispex_data_t *preprocess_dispex_data(dispex_static_data_t *desc, compat_mode_t compat_mode) { + const generic_prop_hooks_t *prop_hooks = NULL; const tid_t *tid; dispex_data_t *data; - DWORD i; ITypeInfo *dti; HRESULT hres; + DWORD i, j;
if(desc->disp_tid) { hres = get_typeinfo(desc->disp_tid, &dti); @@ -503,7 +504,7 @@ static dispex_data_t *preprocess_dispex_data(dispex_static_data_t *desc, compat_ list_add_tail(&dispex_data_list, &data->entry);
if(desc->init_info) - desc->init_info(data, compat_mode); + prop_hooks = desc->init_info(data, compat_mode);
for(tid = desc->iface_tids; *tid; tid++) { hres = process_interface(data, *tid, dti, NULL); @@ -519,6 +520,25 @@ static dispex_data_t *preprocess_dispex_data(dispex_static_data_t *desc, compat_ return data; }
+ if(prop_hooks) { + for(i = 0; i < data->func_cnt; i++) { + func_info_t *func = &data->funcs[i]; + + if(func->hook || func->func_disp_idx != -1) + continue; + + if(prop_hooks->exclude_list) { + for(j = 0; prop_hooks->exclude_list[j] != DISPID_UNKNOWN; j++) + if(prop_hooks->exclude_list[j] == func->id) + break; + if(prop_hooks->exclude_list[j] == func->id) + continue; + } + + func->hook = func->put_vtbl_off ? NULL : prop_hooks->ro_prop_hook; + } + } +
data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t)); qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp); @@ -1365,7 +1385,7 @@ static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD return hres;
if(func->hook) { - hres = func->hook(This, lcid, flags, dp, res, ei, caller); + hres = func->hook(This, func->name, lcid, flags, dp, res, ei, caller); if(hres != S_FALSE) return hres; } diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index d2caaef..1a02fc7 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -5628,7 +5628,7 @@ static const tid_t HTMLDocumentNode_iface_tids[] = { 0 };
-static void HTMLDocumentNode_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +static const generic_prop_hooks_t *HTMLDocumentNode_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { HTMLDOMNode_init_dispex_info(info, mode);
@@ -5646,6 +5646,7 @@ static void HTMLDocumentNode_init_dispex_info(dispex_data_t *info, compat_mode_t dispex_info_add_interface(info, IHTMLDocument6_tid, NULL); dispex_info_add_interface(info, IHTMLDocument3_tid, NULL); } + return NULL; }
static dispex_static_data_t HTMLDocumentNode_dispex = { diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index b1301c7..d7ae1cd 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -1323,6 +1323,9 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA if(FAILED(hres)) return hres;
+ if(compat_mode >= COMPAT_MODE_IE8) + element_remove_attribute(This, strAttributeName); + *pfSuccess = VARIANT_TRUE; return S_OK; } @@ -6679,13 +6682,60 @@ static IHTMLEventObj *HTMLElement_set_current_event(DispatchEx *dispex, IHTMLEve return default_set_current_event(This->node.doc->window, event); }
-void HTMLElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +static HRESULT ie8_ro_prop_hook(DispatchEx *dispex, BSTR name, LCID lcid, WORD flags, + DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +{ + HTMLElement *This = impl_from_DispatchEx(dispex); + nsAString name_str, val_str; + const PRUnichar *val; + nsresult nsres; + cpp_bool b; + + if(flags != DISPATCH_PROPERTYGET || !res || !This->dom_element) + return S_FALSE; + + nsAString_InitDepend(&name_str, name); + nsres = nsIDOMElement_HasAttribute(This->dom_element, &name_str, &b); + if(NS_FAILED(nsres) || !b) { + nsAString_Finish(&name_str); + return S_FALSE; + } + + nsAString_Init(&val_str, NULL); + nsres = nsIDOMElement_GetAttribute(This->dom_element, &name_str, &val_str); + nsAString_Finish(&name_str); + if(NS_FAILED(nsres)) { + nsAString_Finish(&val_str); + return S_FALSE; + } + + nsAString_GetData(&val_str, &val); + V_BSTR(res) = SysAllocString(val); + nsAString_Finish(&val_str); + + if(!V_BSTR(res)) + return E_OUTOFMEMORY; + V_VT(res) = VT_BSTR; + + return S_OK; +} + +const generic_prop_hooks_t *HTMLElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { static const dispex_hook_t elem2_ie11_hooks[] = { {DISPID_IHTMLELEMENT2_DOSCROLL, NULL}, {DISPID_IHTMLELEMENT2_READYSTATE, NULL}, {DISPID_UNKNOWN} }; + static const DISPID ie8_prop_hooks_exclude_list[] = { + DISPID_IHTMLELEMENT_CLASSNAME, + DISPID_IHTMLELEMENT_STYLE, + DISPID_UNKNOWN + }; + static const generic_prop_hooks_t ie8_prop_hooks = { + ie8_ro_prop_hook, + ie8_prop_hooks_exclude_list + };
HTMLDOMNode_init_dispex_info(info, mode);
@@ -6704,6 +6754,8 @@ void HTMLElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) dispex_info_add_interface(info, IHTMLElement7_tid, NULL); dispex_info_add_interface(info, IWineHTMLElementPrivate_tid, NULL); } + + return (mode == COMPAT_MODE_IE8) ? &ie8_prop_hooks : NULL; }
static const tid_t HTMLElement_iface_tids[] = { diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 3f545bb..84cd0b1 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -3314,8 +3314,8 @@ static HRESULT WINAPI EventTarget_dispatchEvent(IEventTarget *iface, IDOMEvent * return dispatch_event_object(This, event, DISPATCH_STANDARD, result); }
-static HRESULT IEventTarget_addEventListener_hook(DispatchEx *dispex, LCID lcid, WORD flags, - DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +static HRESULT IEventTarget_addEventListener_hook(DispatchEx *dispex, BSTR name, LCID lcid, + WORD flags, DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) { /* If only two arguments were given, implicitly set capture to false */ if((flags & DISPATCH_METHOD) && dp->cArgs == 2 && !dp->cNamedArgs) { @@ -3335,8 +3335,8 @@ static HRESULT IEventTarget_addEventListener_hook(DispatchEx *dispex, LCID lcid, return S_FALSE; /* fallback to default */ }
-static HRESULT IEventTarget_removeEventListener_hook(DispatchEx *dispex, LCID lcid, WORD flags, - DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +static HRESULT IEventTarget_removeEventListener_hook(DispatchEx *dispex, BSTR name, LCID lcid, + WORD flags, DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) { /* If only two arguments were given, implicitly set capture to false */ if((flags & DISPATCH_METHOD) && dp->cArgs == 2 && !dp->cNamedArgs) { @@ -3413,7 +3413,7 @@ HRESULT EventTarget_QI(EventTarget *event_target, REFIID riid, void **ppv) return E_NOINTERFACE; }
-void EventTarget_init_dispex_info(dispex_data_t *dispex_info, compat_mode_t compat_mode) +const generic_prop_hooks_t *EventTarget_init_dispex_info(dispex_data_t *dispex_info, compat_mode_t compat_mode) { static const dispex_hook_t IEventTarget_hooks[] = { {DISPID_IEVENTTARGET_ADDEVENTLISTENER, IEventTarget_addEventListener_hook}, @@ -3423,6 +3423,7 @@ void EventTarget_init_dispex_info(dispex_data_t *dispex_info, compat_mode_t comp
if(compat_mode >= COMPAT_MODE_IE9) dispex_info_add_interface(dispex_info, IEventTarget_tid, IEventTarget_hooks); + return NULL; }
static int event_id_cmp(const void *key, const struct wine_rb_entry *entry) diff --git a/dlls/mshtml/htmlnode.c b/dlls/mshtml/htmlnode.c index 996c28c..3d977b2 100644 --- a/dlls/mshtml/htmlnode.c +++ b/dlls/mshtml/htmlnode.c @@ -1432,12 +1432,12 @@ static HRESULT HTMLDOMNode_clone(HTMLDOMNode *This, nsIDOMNode *nsnode, HTMLDOMN return create_node(This->doc, nsnode, ret); }
-void HTMLDOMNode_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +const generic_prop_hooks_t *HTMLDOMNode_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { if(mode >= COMPAT_MODE_IE9) dispex_info_add_interface(info, IHTMLDOMNode3_tid, NULL);
- EventTarget_init_dispex_info(info, mode); + return EventTarget_init_dispex_info(info, mode); }
static const cpc_entry_t HTMLDOMNode_cpc[] = {{NULL}}; diff --git a/dlls/mshtml/htmlstyle.c b/dlls/mshtml/htmlstyle.c index 2cbd8a5..e3a5da8 100644 --- a/dlls/mshtml/htmlstyle.c +++ b/dlls/mshtml/htmlstyle.c @@ -9983,12 +9983,13 @@ static HRESULT CSSStyle_get_dispid(DispatchEx *dispex, BSTR name, DWORD flags, D return DISP_E_UNKNOWNNAME; }
-void CSSStyle_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +const generic_prop_hooks_t *CSSStyle_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { if(mode >= COMPAT_MODE_IE9) dispex_info_add_interface(info, IHTMLCSSStyleDeclaration_tid, NULL); if(mode >= COMPAT_MODE_IE10) dispex_info_add_interface(info, IHTMLCSSStyleDeclaration2_tid, NULL); + return NULL; }
const dispex_static_data_vtbl_t CSSStyle_dispex_vtbl = { diff --git a/dlls/mshtml/htmlstyle.h b/dlls/mshtml/htmlstyle.h index 25f87c8..59fc281 100644 --- a/dlls/mshtml/htmlstyle.h +++ b/dlls/mshtml/htmlstyle.h @@ -155,7 +155,7 @@ HRESULT create_computed_style(nsIDOMCSSStyleDeclaration*,compat_mode_t,IHTMLCSSS void init_css_style(CSSStyle*,nsIDOMCSSStyleDeclaration*,style_qi_t, dispex_static_data_t*,compat_mode_t) DECLSPEC_HIDDEN;
-void CSSStyle_init_dispex_info(dispex_data_t *info, compat_mode_t mode) DECLSPEC_HIDDEN; +const generic_prop_hooks_t *CSSStyle_init_dispex_info(dispex_data_t *info, compat_mode_t mode) DECLSPEC_HIDDEN; extern const dispex_static_data_vtbl_t CSSStyle_dispex_vtbl DECLSPEC_HIDDEN;
HRESULT get_style_property(CSSStyle*,styleid_t,BSTR*) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/htmlstyleelem.c b/dlls/mshtml/htmlstyleelem.c index cf57547..82ef2d7 100644 --- a/dlls/mshtml/htmlstyleelem.c +++ b/dlls/mshtml/htmlstyleelem.c @@ -420,21 +420,21 @@ static void HTMLStyleElement_unlink(HTMLDOMNode *iface) } }
-static void HTMLStyleElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +static const generic_prop_hooks_t *HTMLStyleElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { static const dispex_hook_t ie11_hooks[] = { {DISPID_IHTMLSTYLEELEMENT_READYSTATE, NULL}, {DISPID_IHTMLSTYLEELEMENT_STYLESHEET, NULL}, {DISPID_UNKNOWN} }; - - HTMLElement_init_dispex_info(info, mode); + const generic_prop_hooks_t *prop_hooks = HTMLElement_init_dispex_info(info, mode);
dispex_info_add_interface(info, IHTMLStyleElement_tid, mode >= COMPAT_MODE_IE11 ? ie11_hooks : NULL);
if(mode >= COMPAT_MODE_IE9) dispex_info_add_interface(info, IHTMLStyleElement2_tid, NULL); + return prop_hooks; }
static const NodeImplVtbl HTMLStyleElementImplVtbl = { diff --git a/dlls/mshtml/htmlstylesheet.c b/dlls/mshtml/htmlstylesheet.c index c31c780..4f38c12 100644 --- a/dlls/mshtml/htmlstylesheet.c +++ b/dlls/mshtml/htmlstylesheet.c @@ -1140,10 +1140,11 @@ static const IHTMLStyleSheet4Vtbl HTMLStyleSheet4Vtbl = { HTMLStyleSheet4_deleteRule, };
-static void HTMLStyleSheet_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +static const generic_prop_hooks_t *HTMLStyleSheet_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { if(mode >= COMPAT_MODE_IE9) dispex_info_add_interface(info, IHTMLStyleSheet4_tid, NULL); + return NULL; }
static const tid_t HTMLStyleSheet_iface_tids[] = { diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 635b046..7298b03 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -3734,7 +3734,7 @@ static void HTMLWindow_bind_event(DispatchEx *dispex, eventid_t eid) ensure_doc_nsevent_handler(This->doc, NULL, eid); }
-static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode) +static const generic_prop_hooks_t *HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode) { if(compat_mode >= COMPAT_MODE_IE9) dispex_info_add_interface(info, IHTMLWindow7_tid, NULL); @@ -3744,7 +3744,7 @@ static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compa dispex_info_add_interface(info, IWineHTMLWindowPrivate_tid, NULL);
dispex_info_add_interface(info, IHTMLWindow5_tid, NULL); - EventTarget_init_dispex_info(info, compat_mode); + return EventTarget_init_dispex_info(info, compat_mode); }
static IHTMLEventObj *HTMLWindow_set_current_event(DispatchEx *dispex, IHTMLEventObj *event) diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index cc5779b..0676a34 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -319,6 +319,9 @@ typedef struct dispex_dynamic_data_t dispex_dynamic_data_t;
typedef struct DispatchEx DispatchEx;
+typedef HRESULT (*dispex_hook_invoke_t)(DispatchEx*,BSTR,LCID,WORD,DISPPARAMS*, + VARIANT*,EXCEPINFO*,IServiceProvider*); + typedef struct { HRESULT (*value)(DispatchEx*,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,IServiceProvider*); HRESULT (*get_dispid)(DispatchEx*,BSTR,DWORD,DISPID*); @@ -327,19 +330,22 @@ typedef struct { HRESULT (*populate_props)(DispatchEx*); } dispex_static_data_vtbl_t;
+typedef struct { + dispex_hook_invoke_t ro_prop_hook; + + const DISPID *exclude_list; +} generic_prop_hooks_t; + typedef struct { const WCHAR *name; const dispex_static_data_vtbl_t *vtbl; const tid_t disp_tid; const tid_t* const iface_tids; - void (*init_info)(dispex_data_t*,compat_mode_t); + const generic_prop_hooks_t* (*init_info)(dispex_data_t*,compat_mode_t); dispex_data_t *info_cache[COMPAT_MODE_CNT]; dispex_data_t *delayed_init_info; } dispex_static_data_t;
-typedef HRESULT (*dispex_hook_invoke_t)(DispatchEx*,LCID,WORD,DISPPARAMS*,VARIANT*, - EXCEPINFO*,IServiceProvider*); - typedef struct { DISPID dispid; dispex_hook_invoke_t invoke; @@ -1132,18 +1138,18 @@ void HTMLElement_Init(HTMLElement*,HTMLDocumentNode*,nsIDOMElement*,dispex_stati
void EventTarget_Init(EventTarget*,IUnknown*,dispex_static_data_t*,compat_mode_t) DECLSPEC_HIDDEN; HRESULT EventTarget_QI(EventTarget*,REFIID,void**) DECLSPEC_HIDDEN; -void EventTarget_init_dispex_info(dispex_data_t*,compat_mode_t) DECLSPEC_HIDDEN; +const generic_prop_hooks_t *EventTarget_init_dispex_info(dispex_data_t*,compat_mode_t) DECLSPEC_HIDDEN;
HRESULT HTMLDOMNode_QI(HTMLDOMNode*,REFIID,void**) DECLSPEC_HIDDEN; void HTMLDOMNode_destructor(HTMLDOMNode*) DECLSPEC_HIDDEN; -void HTMLDOMNode_init_dispex_info(dispex_data_t*,compat_mode_t) DECLSPEC_HIDDEN; +const generic_prop_hooks_t *HTMLDOMNode_init_dispex_info(dispex_data_t*,compat_mode_t) DECLSPEC_HIDDEN;
HRESULT HTMLElement_QI(HTMLDOMNode*,REFIID,void**) DECLSPEC_HIDDEN; void HTMLElement_destructor(HTMLDOMNode*) DECLSPEC_HIDDEN; HRESULT HTMLElement_clone(HTMLDOMNode*,nsIDOMNode*,HTMLDOMNode**) DECLSPEC_HIDDEN; HRESULT HTMLElement_get_attr_col(HTMLDOMNode*,HTMLAttributeCollection**) DECLSPEC_HIDDEN; HRESULT HTMLElement_handle_event(HTMLDOMNode*,DWORD,nsIDOMEvent*,BOOL*) DECLSPEC_HIDDEN; -void HTMLElement_init_dispex_info(dispex_data_t*,compat_mode_t) DECLSPEC_HIDDEN; +const generic_prop_hooks_t *HTMLElement_init_dispex_info(dispex_data_t*,compat_mode_t) DECLSPEC_HIDDEN;
HRESULT get_node(nsIDOMNode*,BOOL,HTMLDOMNode**) DECLSPEC_HIDDEN; HRESULT get_element(nsIDOMElement*,HTMLElement**) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index d01680b..9d2d71a 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1359,7 +1359,6 @@ sync_test("elem_attr", function() { r = elem.getAttribute(name); ok(r === "string", name + " attr after setAttribute = " + r); eval("r = elem." + name + ";"); - todo_wine. ok(r === "string", "elem." + name + " after setAttribute = " + r);
r = elem.removeAttribute(name); @@ -1383,7 +1382,6 @@ sync_test("elem_attr", function() { r = elem.style; ok(r === style, "removed elem.style = " + r); r = elem.getAttribute("style"); - todo_wine_if(v === 8). ok(r === (v < 8 ? style : null), "style attr after removal = " + r); elem.setAttribute("style", "opacity: 1.0"); r = elem.getAttribute("style"); diff --git a/dlls/mshtml/xmlhttprequest.c b/dlls/mshtml/xmlhttprequest.c index 56e18d0..8b9cdde 100644 --- a/dlls/mshtml/xmlhttprequest.c +++ b/dlls/mshtml/xmlhttprequest.c @@ -475,8 +475,8 @@ static HRESULT WINAPI HTMLXMLHttpRequest_abort(IHTMLXMLHttpRequest *iface) return S_OK; }
-static HRESULT HTMLXMLHttpRequest_open_hook(DispatchEx *dispex, LCID lcid, WORD flags, - DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +static HRESULT HTMLXMLHttpRequest_open_hook(DispatchEx *dispex, BSTR name, LCID lcid, + WORD flags, DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) { /* If only two arguments were given, implicitly set async to false */ if((flags & DISPATCH_METHOD) && dp->cArgs == 2 && !dp->cNamedArgs) { @@ -870,15 +870,16 @@ static void HTMLXMLHttpRequest_bind_event(DispatchEx *dispex, eventid_t eid) This->event_listener->load_event = TRUE; }
-static void HTMLXMLHttpRequest_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode) +static const generic_prop_hooks_t *HTMLXMLHttpRequest_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode) { static const dispex_hook_t xhr_hooks[] = { {DISPID_IHTMLXMLHTTPREQUEST_OPEN, HTMLXMLHttpRequest_open_hook}, {DISPID_UNKNOWN} }; + const generic_prop_hooks_t *prop_hooks = EventTarget_init_dispex_info(info, compat_mode);
- EventTarget_init_dispex_info(info, compat_mode); dispex_info_add_interface(info, IHTMLXMLHttpRequest_tid, compat_mode >= COMPAT_MODE_IE10 ? xhr_hooks : NULL); + return prop_hooks; }
static event_target_vtbl_t HTMLXMLHttpRequest_event_target_vtbl = {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102343
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w1064_tsign (32 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w10pro64 (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w10pro64_ar (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w10pro64_he (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/dispex.c | 2 +- dlls/mshtml/htmlelem.c | 57 ++++++++++++++++++++++++++----- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/tests/documentmode.js | 1 - 4 files changed, 51 insertions(+), 10 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 846a678..b9d43e8 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -535,7 +535,7 @@ static dispex_data_t *preprocess_dispex_data(dispex_static_data_t *desc, compat_ continue; }
- func->hook = func->put_vtbl_off ? NULL : prop_hooks->ro_prop_hook; + func->hook = func->put_vtbl_off ? prop_hooks->rw_prop_hook : prop_hooks->ro_prop_hook; } }
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index d7ae1cd..2e8bc9c 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -1211,6 +1211,7 @@ static HRESULT WINAPI HTMLElement_getAttribute(IHTMLElement *iface, BSTR strAttr nsresult nsres; DISPID dispid; HRESULT hres; + cpp_bool b;
TRACE("(%p)->(%s %08x %p)\n", This, debugstr_w(strAttributeName), lFlags, AttributeValue);
@@ -1243,18 +1244,26 @@ static HRESULT WINAPI HTMLElement_getAttribute(IHTMLElement *iface, BSTR strAttr }
if(This->dom_element && compat_mode >= COMPAT_MODE_IE8 && get_dispid_type(dispid) == DISPEXPROP_BUILTIN) { + nsAString_InitDepend(&name_str, strAttributeName); + if(is_readonly_builtin(&This->node.event_target.dispex, dispid)) { - nsAString_InitDepend(&name_str, strAttributeName); nsAString_InitDepend(&value_str, NULL); nsres = nsIDOMElement_GetAttribute(This->dom_element, &name_str, &value_str); nsAString_Finish(&name_str); return return_nsstr_variant(nsres, &value_str, 0, AttributeValue); }
- hres = get_elem_attr_value_by_dispid(This, dispid, AttributeValue); - if(SUCCEEDED(hres) && V_VT(AttributeValue) != VT_BSTR) { - VariantClear(AttributeValue); + nsres = nsIDOMElement_HasAttribute(This->dom_element, &name_str, &b); + nsAString_Finish(&name_str); + if(NS_SUCCEEDED(nsres) && b) { + hres = get_elem_attr_value_by_dispid(This, dispid, AttributeValue); + if(SUCCEEDED(hres) && V_VT(AttributeValue) != VT_BSTR) { + VariantClear(AttributeValue); + V_VT(AttributeValue) = VT_NULL; + } + }else { V_VT(AttributeValue) = VT_NULL; + hres = S_OK; } return hres; } @@ -1282,6 +1291,7 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA { HTMLElement *This = impl_from_IHTMLElement(iface); compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex); + VARIANT_BOOL tmp; DISPID id; HRESULT hres;
@@ -1294,10 +1304,12 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA
if(compat_mode >= COMPAT_MODE_IE8) { *pfSuccess = element_has_attribute(This, strAttributeName); - if(*pfSuccess) - return element_remove_attribute(This, strAttributeName); if(compat_mode >= COMPAT_MODE_IE9) - return S_OK; + return *pfSuccess ? element_remove_attribute(This, strAttributeName) : S_OK; + + /* remove the prop without influencing the return value */ + if(*pfSuccess) + pfSuccess = &tmp; }
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, strAttributeName, @@ -1330,7 +1342,10 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA return S_OK; }
- return remove_attribute(&This->node.event_target.dispex, id, pfSuccess); + hres = remove_attribute(&This->node.event_target.dispex, id, pfSuccess); + if(compat_mode >= COMPAT_MODE_IE8) + element_remove_attribute(This, strAttributeName); + return hres; }
static HRESULT WINAPI HTMLElement_put_className(IHTMLElement *iface, BSTR v) @@ -6720,6 +6735,31 @@ static HRESULT ie8_ro_prop_hook(DispatchEx *dispex, BSTR name, LCID lcid, WORD f return S_OK; }
+static HRESULT ie8_rw_prop_hook(DispatchEx *dispex, BSTR name, LCID lcid, WORD flags, + DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +{ + HTMLElement *This = impl_from_DispatchEx(dispex); + nsAString name_str, val_str; + nsresult nsres; + cpp_bool b; + + if(flags != DISPATCH_PROPERTYPUT || dp->cArgs != 1 || dp->cNamedArgs > 1 || V_VT(dp->rgvarg) != VT_BSTR || + (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT) || !This->dom_element) + return S_FALSE; + + /* make sure attribute is set if it doesn't exist, for getAttribute checks */ + nsAString_InitDepend(&name_str, name); + nsres = nsIDOMElement_HasAttribute(This->dom_element, &name_str, &b); + if(NS_SUCCEEDED(nsres) && !b) { + nsAString_InitDepend(&val_str, L""); + nsIDOMElement_SetAttribute(This->dom_element, &name_str, &val_str); + nsAString_Finish(&val_str); + } + nsAString_Finish(&name_str); + + return S_FALSE; +} + const generic_prop_hooks_t *HTMLElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { static const dispex_hook_t elem2_ie11_hooks[] = { @@ -6734,6 +6774,7 @@ const generic_prop_hooks_t *HTMLElement_init_dispex_info(dispex_data_t *info, co }; static const generic_prop_hooks_t ie8_prop_hooks = { ie8_ro_prop_hook, + ie8_rw_prop_hook, ie8_prop_hooks_exclude_list };
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 0676a34..46da4a1 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -332,6 +332,7 @@ typedef struct {
typedef struct { dispex_hook_invoke_t ro_prop_hook; + dispex_hook_invoke_t rw_prop_hook;
const DISPID *exclude_list; } generic_prop_hooks_t; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 9d2d71a..9608b41 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1277,7 +1277,6 @@ sync_test("elem_attr", function() { var val = props[i].length > 2 ? props[i][2] : "test";
r = elem.getAttribute(name); - todo_wine_if(v === 8 && props[i][0].substring(0, 2) !== "on"). ok(r === (v < 8 && props[i].length > 1 ? props[i][1] : null), name + " attr before set = " + r); eval("elem." + name + " = "" + val + ""; r = elem." + name + ";"); ok(r === val, "elem." + name + " = " + r);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102344
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w10pro64_zh_CN (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w7u_el (32 bit report) ===
mshtml: script.c:624: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1637367181792 expected 1637367181855"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/dispex.c | 8 ++++++-- dlls/mshtml/tests/documentmode.js | 1 - 2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index b9d43e8..c586fb3 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -1500,9 +1500,13 @@ HRESULT remove_attribute(DispatchEx *This, DISPID id, VARIANT_BOOL *success) if(FAILED(hres)) { VARIANT *ref; hres = dispex_get_dprop_ref(This, func->name, FALSE, &ref); - if(FAILED(hres) || V_VT(ref) != VT_BSTR) + if(FAILED(hres) || V_VT(ref) != VT_BSTR) { *success = VARIANT_FALSE; - else + if(dispex_compat_mode(This) >= COMPAT_MODE_IE8) { + V_VT(&var) = VT_NULL; + builtin_propput(This, func, &dp, NULL); + } + }else VariantClear(ref); } return S_OK; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 9608b41..08adc9e 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1183,7 +1183,6 @@ sync_test("elem_attr", function() { ok(r === (v < 8 ? func : null), "onclick attr = " + r); r = elem.removeAttribute("onclick"); ok(r === (v < 9 ? false : undefined), "removeAttribute returned " + r); - todo_wine_if(v === 8). ok(elem.onclick === (v != 8 ? func : null), "removed onclick = " + elem.onclick);
elem.onclick_test = func;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102345
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: htmldoc.c:3084: Test failed: Incorrect error code: -2146697211 htmldoc.c:3089: Test failed: Page address: L"http://test.winehq.org/tests/winehq_snapshot/" htmldoc.c:5861: Test failed: expected OnChanged_1012 htmldoc.c:5862: Test failed: expected Exec_HTTPEQUIV htmldoc.c:5864: Test failed: expected Exec_SETTITLE htmldoc.c:5905: Test failed: expected FireNavigateComplete2
This has to be special cased for Object_toString because null is otherwise replaced by the global object for any other case.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/function.c | 10 ++++++++++ dlls/jscript/jscript.h | 1 + dlls/jscript/object.c | 2 +- dlls/mshtml/tests/documentmode.js | 2 +- dlls/mshtml/tests/es5.js | 1 - 5 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index 9998e8b..1f88e98 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -604,6 +604,16 @@ static HRESULT NativeFunction_call(script_ctx_t *ctx, FunctionInstance *func, ID
if(this_disp) set_disp(&vthis, this_disp); + else if(function->proc == Object_toString && ctx->version >= SCRIPTLANGUAGEVERSION_ES5) { + jsstr_t *ret; + if(!r) + return S_OK; + ret = jsstr_alloc(L"[object Null]"); + if(!ret) + return E_OUTOFMEMORY; + *r = jsval_string(ret); + return S_OK; + } else set_disp(&vthis, lookup_global_host(ctx));
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 4d17b2f..fec7f7d 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -519,6 +519,7 @@ BOOL bool_obj_value(jsdisp_t*) DECLSPEC_HIDDEN; unsigned array_get_length(jsdisp_t*) DECLSPEC_HIDDEN;
HRESULT JSGlobal_eval(script_ctx_t*,vdisp_t*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; +HRESULT Object_toString(script_ctx_t*,vdisp_t*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
static inline BOOL is_class(jsdisp_t *jsdisp, jsclass_t class) { diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index 39248ab..5d94060 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -24,7 +24,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(jscript);
-static HRESULT Object_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, +HRESULT Object_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *jsdisp; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 08adc9e..0781ee6 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1226,7 +1226,7 @@ sync_test("elem_attr", function() { r = elem.removeAttribute("ondblclick"); ok(r === (v < 8 ? false : (v < 9 ? true : undefined)), "ondblclick removeAttribute returned " + r); r = Object.prototype.toString.call(elem.ondblclick); - todo_wine_if(v >= 9). + todo_wine_if(v >= 11). ok(r === (v < 8 ? "[object Array]" : (v < 9 ? "[object Object]" : (v < 11 ? "[object Null]" : "[object Function]"))), "removed ondblclick Object.toString returned " + r);
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 81c06a4..20ae374 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -784,7 +784,6 @@ sync_test("toString", function() { todo_wine. ok(tmp === "[object Window]", "toString.call(null) = " + tmp); tmp = Object.prototype.toString.call(null); - todo_wine. ok(tmp === "[object Null]", "toString.call(null) = " + tmp); tmp = Object.prototype.toString.call(undefined); todo_wine.
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102346
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w10pro64_ar (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w10pro64_zh_CN (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE