Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45493 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
We already treat all objects as references anyway.
dlls/jscript/dispex.c | 2 + dlls/jscript/tests/run.c | 94 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 319617b..7f4d1e1 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -1627,6 +1627,8 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc } break; } + case DISPATCH_PROPERTYPUTREF | DISPATCH_PROPERTYPUT: + case DISPATCH_PROPERTYPUTREF: case DISPATCH_PROPERTYPUT: { jsval_t val; DWORD i; diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index 5e797a1..e4408be 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -2787,6 +2787,91 @@ static void test_retval(void) close_script(engine); }
+static void test_propputref(void) +{ + static DISPID propput_dispid = DISPID_PROPERTYPUT; + DISPPARAMS dp = {0}, dp_get = {0}; + IActiveScript *script, *script2; + IDispatch *disp, *obj; + HRESULT hres; + VARIANT v; + DISPID id; + BSTR str; + + hres = parse_script_expr(L"new Object()", &v, &script2); + ok(hres == S_OK, "parse_script_expr failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + obj = V_DISPATCH(&v); + + hres = parse_script_expr(L"var disp = new Object(); disp.a = disp; disp", &v, &script); + ok(hres == S_OK, "parse_script_expr failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + disp = V_DISPATCH(&v); + + str = SysAllocString(L"a"); + hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &str, 1, 0, &id); + ok(hres == S_OK, "GetIDsOfNames failed: %08lx\n", hres); + SysFreeString(str); + + dp.cArgs = dp.cNamedArgs = 1; + dp.rgdispidNamedArgs = &propput_dispid; + dp.rgvarg = &v; + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = obj; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF, &dp, NULL, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp_get, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == obj, "V_DISPATCH(v) = %p\n", V_DISPATCH(&v)); + VariantClear(&v); + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = obj; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF | DISPATCH_PROPERTYPUT, &dp, NULL, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp_get, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == obj, "V_DISPATCH(v) = %p\n", V_DISPATCH(&v)); + IDispatch_Release(obj); + close_script(script2); + VariantClear(&v); + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = NULL; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF, &dp, NULL, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp_get, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(!V_DISPATCH(&v), "V_DISPATCH(v) = %p\n", V_DISPATCH(&v)); + + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF, &dp, NULL, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp_get, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + ok(V_VT(&v) == VT_EMPTY, "V_VT(v) = %d\n", V_VT(&v)); + + V_VT(&v) = VT_I4; + V_I4(&v) = 42; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF, &dp, NULL, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + V_VT(&v) = VT_EMPTY; + hres = IDispatch_Invoke(disp, id, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp_get, &v, NULL, NULL); + ok(hres == S_OK, "Invoke failed: %08lx\n", hres); + ok(V_VT(&v) == VT_I4, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_I4(&v) == 42, "V_I4(v) = %ld\n", V_I4(&v)); + + IDispatch_Release(disp); + close_script(script); +} + static void test_default_value(void) { static DISPID propput_dispid = DISPID_PROPERTYPUT; @@ -2888,6 +2973,10 @@ static void test_default_value(void) 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: %08lx\n", hres); + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF, &dp, NULL, NULL, NULL); + ok(hres == DISP_E_MEMBERNOTFOUND, "Invoke failed: %08lx\n", hres); + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF | DISPATCH_PROPERTYPUT, &dp, NULL, NULL, NULL); + ok(hres == DISP_E_MEMBERNOTFOUND, "Invoke failed: %08lx\n", hres); IDispatch_Release(disp); close_script(script);
@@ -2899,6 +2988,10 @@ static void test_default_value(void) 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: %08lx\n", hres); + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF, &dp, NULL, NULL, NULL); + ok(hres == DISP_E_MEMBERNOTFOUND, "Invoke failed: %08lx\n", hres); + hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF | DISPATCH_PROPERTYPUT, &dp, NULL, NULL, NULL); + ok(hres == DISP_E_MEMBERNOTFOUND, "Invoke failed: %08lx\n", hres); IDispatch_Release(disp); close_script(script); } @@ -2968,6 +3061,7 @@ static void test_script_exprs(void) VariantClear(&v);
test_default_value(); + test_propputref(); test_retval();
testing_expr = FALSE;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/tests/documentmode.js | 58 +++++++++++++++++++ dlls/mshtml/tests/es5.js | 96 +++++++++++++++++++++++++++++++ dlls/mshtml/tests/script.c | 68 ++++++++++++++++++++++ 3 files changed, 222 insertions(+)
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 55de9b7..5f475cc 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1322,6 +1322,64 @@ sync_test("builtins_diffs", function() { } });
+sync_test("nullDisp", function() { + var v = document.documentMode, nullDisp = external.nullDisp, r; + + todo_wine. + ok(external.getVT(nullDisp) === "VT_NULL", "getVT(nullDisp) is not VT_NULL"); + ok(typeof(nullDisp) === "object", "typeof(nullDisp) = " + typeof(nullDisp)); + ok(nullDisp === nullDisp, "nullDisp !== nullDisp"); + ok(nullDisp === null, "nullDisp === null"); + ok(nullDisp == null, "nullDisp == null"); + ok(!nullDisp === true, "!nullDisp = " + !nullDisp); + ok(String(nullDisp) === "null", "String(nullDisp) = " + String(nullDisp)); + ok(+nullDisp === 0, "+nullDisp !== 0"); + ok(''+nullDisp === "null", "''+nullDisp !== null"); + ok(nullDisp != new Object(), "nullDisp == new Object()"); + ok(new Object() != nullDisp, "new Object() == nullDisp"); + ok((typeof Object(nullDisp)) === "object", "typeof Object(nullDisp) !== 'object'"); + r = Object(nullDisp).toString(); + ok(r === "[object Object]", "Object(nullDisp).toString() = " + r); + ok(Object(nullDisp) != nullDisp, "Object(nullDisp) == nullDisp"); + ok(new Object(nullDisp) != nullDisp, "new Object(nullDisp) == nullDisp"); + + if(v >= 8) { + r = JSON.stringify.call(null, nullDisp); + todo_wine. + ok(r === "null", "JSON.stringify(nullDisp) returned " + r); + } + + try { + (new Object()) instanceof nullDisp; + ok(false, "expected exception on (new Object()) instanceof nullDisp"); + }catch(e) { + ok(e.number === 0xa138a - 0x80000000, "(new Object()) instanceof nullDisp threw " + e.number); + } + + try { + Function.prototype.apply.call(nullDisp, Object, []); + ok(false, "expected exception calling Function.apply on nullDisp"); + }catch(e) { + todo_wine. + ok(e.number === 0xa138a - 0x80000000, "Function.apply on nullDisp threw " + e.number); + } + try { + Function.prototype.call.call(nullDisp, Object); + ok(false, "expected exception calling Function.call on nullDisp"); + }catch(e) { + todo_wine. + ok(e.number === 0xa138a - 0x80000000, "Function.call on nullDisp threw " + e.number); + } + + try { + new nullDisp; + ok(false, "expected exception for new nullDisp"); + }catch(e) { + todo_wine. + ok(e.number === 0xa138f - 0x80000000, "new nullDisp threw " + e.number); + } +}); + sync_test("__proto__", function() { var v = document.documentMode; var r, x = 42; diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 6baebbd..14ec4f9 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -298,9 +298,25 @@ sync_test("getOwnPropertyDescriptor", function() { test_own_data_prop_desc(function(){}, "prototype", true, false, false); test_own_data_prop_desc(Function, "prototype", false, false, false); test_own_data_prop_desc(String.prototype, "constructor", true, false, true); + + try { + Object.getOwnPropertyDescriptor(null, "prototype"); + ok(false, "expected exception with null"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "with null context threw " + n); + } + try { + Object.getOwnPropertyDescriptor(external.nullDisp, "prototype"); + ok(false, "expected exception calling getOwnPropertyDescriptor(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "getOwnPropertyDescriptor(nullDisp) threw " + n); + } });
sync_test("defineProperty", function() { + var nullDisp = external.nullDisp; function test_accessor_prop_desc(obj, prop, orig_desc) { var expected_enumerable = "enumerable" in orig_desc && !!orig_desc.enumerable; var expected_configurable = "configurable" in orig_desc && !!orig_desc.configurable; @@ -548,6 +564,16 @@ sync_test("defineProperty", function() { Object.defineProperty(obj, "funcprop", desc); test_accessor_prop_desc(obj, "funcprop", desc); ok(obj.funcprop(100) === 10, "obj.funcprop() = " + obj.funcprop(100)); + + expect_exception(function() { + Object.defineProperty(null, "funcprop", desc); + }, JS_E_OBJECT_EXPECTED); + expect_exception(function() { + Object.defineProperty(nullDisp, "funcprop", desc); + }, JS_E_OBJECT_EXPECTED); + expect_exception(function() { + Object.defineProperty(obj, "funcprop", nullDisp); + }, JS_E_OBJECT_EXPECTED); });
sync_test("defineProperties", function() { @@ -831,6 +857,13 @@ sync_test("getPrototypeOf", function() { ok(Object.getPrototypeOf(obj) === null, "Object.getPrototypeOf(obj) = " + Object.getPrototypeOf(obj));
ok(Object.getPrototypeOf(external) === null, "Object.getPrototypeOf(non-JS obj) = " + Object.getPrototypeOf(external)); + try { + Object.getOwnPropertyDescriptor(external.nullDisp); + ok(false, "expected exception calling getOwnPropertyDescriptor(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "getOwnPropertyDescriptor(nullDisp) threw " + n); + } });
sync_test("toString", function() { @@ -956,6 +989,14 @@ sync_test("keys", function() { ok(keys === "", "keys([]) = " + keys);
ok(Object.keys.length === 1, "Object.keys.length = " + Object.keys.length); + + try { + Object.keys(external.nullDisp); + ok(false, "expected exception calling keys(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "keys(nullDisp) threw " + n); + } });
sync_test("getOwnPropertyNames", function() { @@ -980,6 +1021,14 @@ sync_test("getOwnPropertyNames", function() { ok(names === "length", "names = " + names);
ok(Object.getOwnPropertyNames.length === 1, "Object.getOwnPropertyNames.length = " + Object.getOwnPropertyNames.length); + + try { + Object.getOwnPropertyNames(external.nullDisp); + ok(false, "expected exception calling getOwnPropertyNames(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "getOwnPropertyNames(nullDisp) threw " + n); + } });
sync_test("reduce", function() { @@ -1091,6 +1140,14 @@ sync_test("preventExtensions", function() {
ok(Object.preventExtensions.length === 1, "Object.preventExtensions.length = " + Object.preventExtensions.length); ok(Object.isExtensible.length === 1, "Object.isExtensible.length = " + Object.isExtensible.length); + + try { + Object.preventExtensions(external.nullDisp); + ok(false, "expected exception calling preventExtensions(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "preventExtensions(nullDisp) threw " + n); + } });
sync_test("freeze", function() { @@ -1143,6 +1200,14 @@ sync_test("freeze", function() { } ok(o[0] === 1, "o[0] = " + o[0]); ok(o.length === 1, "o.length = " + o.length); + + try { + Object.freeze(external.nullDisp); + ok(false, "expected exception freeze(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "freeze(nullDisp) threw " + n); + } });
sync_test("seal", function() { @@ -1195,9 +1260,18 @@ sync_test("seal", function() { } ok(o[0] === 1, "o[0] = " + o[0]); ok(o.length === 1, "o.length = " + o.length); + + try { + Object.seal(external.nullDisp); + ok(false, "expected exception calling seal(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "seal(nullDisp) threw " + n); + } });
sync_test("isFrozen", function() { + var nullDisp = external.nullDisp; ok(Object.isFrozen.length === 1, "Object.isFrozen.length = " + Object.isFrozen.length); ok(Object.isSealed.length === 1, "Object.isSealed.length = " + Object.isSealed.length);
@@ -1263,6 +1337,28 @@ sync_test("isFrozen", function() { ok(Object.isFrozen(o) === true, "o is not frozen"); ok(Object.isSealed(o) === true, "o is not sealed"); ok(Object.isExtensible(o) === false, "o is extensible"); + + try { + Object.isFrozen(nullDisp); + ok(false, "expected exception calling isFrozen(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "isFrozen(nullDisp) threw " + n); + } + try { + Object.isSealed(nullDisp); + ok(false, "expected exception calling isSealed(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "isSealed(nullDisp) threw " + n); + } + try { + Object.isExtensible(nullDisp); + ok(false, "expected exception calling isExtensible(nullDisp)"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "isExtensible(nullDisp) threw " + n); + } });
sync_test("builtin_context", function() { diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index de42e89..ceb4fc7 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -151,6 +151,8 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_BROKEN 0x300004 #define DISPID_EXTERNAL_WIN_SKIP 0x300005 #define DISPID_EXTERNAL_WRITESTREAM 0x300006 +#define DISPID_EXTERNAL_GETVT 0x300007 +#define DISPID_EXTERNAL_NULL_DISP 0x300008
static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; @@ -589,6 +591,14 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_WRITESTREAM; return S_OK; } + if(!lstrcmpW(bstrName, L"getVT")) { + *pid = DISPID_EXTERNAL_GETVT; + return S_OK; + } + if(!lstrcmpW(bstrName, L"nullDisp")) { + *pid = DISPID_EXTERNAL_NULL_DISP; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -716,6 +726,64 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID stream_write(V_BSTR(pdp->rgvarg+1), V_BSTR(pdp->rgvarg)); return S_OK;
+ case DISPID_EXTERNAL_GETVT: + ok(pdp != NULL, "pdp == NULL\n"); + ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(pdp->cArgs == 1, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pvarRes != NULL, "pvarRes == NULL\n"); + ok(V_VT(pvarRes) == VT_EMPTY, "V_VT(pvarRes) = %d\n", V_VT(pvarRes)); + ok(pei != NULL, "pei == NULL\n"); + + V_VT(pvarRes) = VT_BSTR; + switch(V_VT(pdp->rgvarg)) { + case VT_EMPTY: + V_BSTR(pvarRes) = SysAllocString(L"VT_EMPTY"); + break; + case VT_NULL: + V_BSTR(pvarRes) = SysAllocString(L"VT_NULL"); + break; + case VT_I4: + V_BSTR(pvarRes) = SysAllocString(L"VT_I4"); + break; + case VT_R8: + V_BSTR(pvarRes) = SysAllocString(L"VT_R8"); + break; + case VT_BSTR: + V_BSTR(pvarRes) = SysAllocString(L"VT_BSTR"); + break; + case VT_DISPATCH: + V_BSTR(pvarRes) = SysAllocString(L"VT_DISPATCH"); + break; + case VT_BOOL: + V_BSTR(pvarRes) = SysAllocString(L"VT_BOOL"); + break; + case VT_DATE: + V_BSTR(pvarRes) = SysAllocString(L"VT_DATE"); + break; + default: + ok(0, "unknown vt %d\n", V_VT(pdp->rgvarg)); + return E_FAIL; + } + + return S_OK; + + case DISPID_EXTERNAL_NULL_DISP: + ok(wFlags == INVOKE_PROPERTYGET, "wFlags = %x\n", wFlags); + ok(pdp != NULL, "pdp == NULL\n"); + ok(!pdp->rgvarg, "rgvarg != NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pvarRes != NULL, "pvarRes == NULL\n"); + ok(V_VT(pvarRes) == VT_EMPTY, "V_VT(pvarRes) = %d\n", V_VT(pvarRes)); + ok(pei != NULL, "pei == NULL\n"); + + V_VT(pvarRes) = VT_DISPATCH; + V_DISPATCH(pvarRes) = NULL; + return S_OK; + default: ok(0, "unexpected call\n"); return E_NOTIMPL;
Signed-off-by: Jacek Caban jacek@codeweavers.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 14 +++++++------- dlls/jscript/enumerator.c | 10 +++++----- dlls/jscript/jsutils.c | 8 ++++---- dlls/jscript/jsval.h | 2 +- dlls/jscript/vbarray.c | 4 ++-- dlls/mshtml/tests/documentmode.js | 7 ++----- dlls/mshtml/tests/es5.js | 8 ++++++++ 7 files changed, 29 insertions(+), 24 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 7f4d1e1..8682766 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -386,7 +386,7 @@ static IDispatch *get_this(DISPPARAMS *dp) return NULL; }
-static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret) +static HRESULT convert_params(script_ctx_t *ctx, const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret) { jsval_t *argv; unsigned cnt; @@ -404,7 +404,7 @@ static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc }
for(i = 0; i < cnt; i++) { - hres = variant_to_jsval(dp->rgvarg+dp->cArgs-i-1, argv+i); + hres = variant_to_jsval(ctx, dp->rgvarg+dp->cArgs-i-1, argv+i); if(FAILED(hres)) { while(i--) jsval_release(argv[i]); @@ -1593,7 +1593,7 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc jsval_t *argv, buf[6], r; unsigned argc;
- hres = convert_params(pdp, buf, &argc, &argv); + hres = convert_params(This->ctx, pdp, buf, &argc, &argv); if(FAILED(hres)) break;
@@ -1649,7 +1649,7 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc break; }
- hres = variant_to_jsval(pdp->rgvarg+i, &val); + hres = variant_to_jsval(This->ctx, pdp->rgvarg+i, &val); if(FAILED(hres)) break;
@@ -2141,7 +2141,7 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, uns heap_free(dp.rgvarg);
if(SUCCEEDED(hres) && ret) - hres = variant_to_jsval(&retv, ret); + hres = variant_to_jsval(ctx, &retv, ret); VariantClear(&retv); return hres; } @@ -2209,7 +2209,7 @@ HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, W if(!r) return S_OK;
- hres = variant_to_jsval(&retv, r); + hres = variant_to_jsval(ctx, &retv, r); VariantClear(&retv); return hres; } @@ -2385,7 +2385,7 @@ HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val V_VT(&var) = VT_EMPTY; hres = disp_invoke(ctx, disp, id, INVOKE_PROPERTYGET, &dp, &var); if(SUCCEEDED(hres)) { - hres = variant_to_jsval(&var, val); + hres = variant_to_jsval(ctx, &var, val); VariantClear(&var); } return hres; diff --git a/dlls/jscript/enumerator.c b/dlls/jscript/enumerator.c index 0fc6750..94eb9f2 100644 --- a/dlls/jscript/enumerator.c +++ b/dlls/jscript/enumerator.c @@ -44,7 +44,7 @@ static inline EnumeratorInstance *enumerator_this(jsval_t vthis) return (jsdisp && is_class(jsdisp, JSCLASS_ENUMERATOR)) ? enumerator_from_jsdisp(jsdisp) : NULL; }
-static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This) +static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This, script_ctx_t *ctx) { HRESULT hres; VARIANT nextitem; @@ -60,7 +60,7 @@ static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This) hres = IEnumVARIANT_Next(This->enumvar, 1, &nextitem, NULL); if (hres == S_OK) { - hres = variant_to_jsval(&nextitem, &This->item); + hres = variant_to_jsval(ctx, &nextitem, &This->item); VariantClear(&nextitem); if (FAILED(hres)) { @@ -134,7 +134,7 @@ static HRESULT Enumerator_moveFirst(script_ctx_t *ctx, jsval_t vthis, WORD flags return hres;
This->atend = FALSE; - hres = enumvar_get_next_item(This); + hres = enumvar_get_next_item(This, ctx); if(FAILED(hres)) return hres; } @@ -157,7 +157,7 @@ static HRESULT Enumerator_moveNext(script_ctx_t *ctx, jsval_t vthis, WORD flags,
if (This->enumvar) { - hres = enumvar_get_next_item(This); + hres = enumvar_get_next_item(This, ctx); if (FAILED(hres)) return hres; } @@ -272,7 +272,7 @@ static HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **re
enumerator->enumvar = enumvar; enumerator->atend = !enumvar; - hres = enumvar_get_next_item(enumerator); + hres = enumvar_get_next_item(enumerator, ctx); if (FAILED(hres)) { jsdisp_release(&enumerator->dispex); diff --git a/dlls/jscript/jsutils.c b/dlls/jscript/jsutils.c index 7debf07..3251abf 100644 --- a/dlls/jscript/jsutils.c +++ b/dlls/jscript/jsutils.c @@ -244,7 +244,7 @@ HRESULT jsval_copy(jsval_t v, jsval_t *r) return E_FAIL; }
-HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) +HRESULT variant_to_jsval(script_ctx_t *ctx, VARIANT *var, jsval_t *r) { if(V_VT(var) == (VT_VARIANT|VT_BYREF)) var = V_VARIANTREF(var); @@ -281,7 +281,7 @@ HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) } case VT_DISPATCH: { if(!V_DISPATCH(var)) { - *r = jsval_null_disp(); + *r = ctx->html_mode ? jsval_null() : jsval_null_disp(); return S_OK; } IDispatch_AddRef(V_DISPATCH(var)); @@ -333,7 +333,7 @@ HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) return S_OK; } }else { - *r = jsval_null_disp(); + *r = ctx->html_mode ? jsval_null() : jsval_null_disp(); return S_OK; } /* fall through */ @@ -882,7 +882,7 @@ HRESULT variant_change_type(script_ctx_t *ctx, VARIANT *dst, VARIANT *src, VARTY jsval_t val; HRESULT hres;
- hres = variant_to_jsval(src, &val); + hres = variant_to_jsval(ctx, src, &val); if(FAILED(hres)) return hres;
diff --git a/dlls/jscript/jsval.h b/dlls/jscript/jsval.h index 4e00d08..2517745 100644 --- a/dlls/jscript/jsval.h +++ b/dlls/jscript/jsval.h @@ -250,7 +250,7 @@ static inline BOOL get_bool(jsval_t v) return __JSVAL_BOOL(v); }
-HRESULT variant_to_jsval(VARIANT*,jsval_t*) DECLSPEC_HIDDEN; +HRESULT variant_to_jsval(script_ctx_t*,VARIANT*,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsval_to_variant(jsval_t,VARIANT*) DECLSPEC_HIDDEN; void jsval_release(jsval_t) DECLSPEC_HIDDEN; HRESULT jsval_copy(jsval_t,jsval_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/vbarray.c b/dlls/jscript/vbarray.c index 881c45e..987d714 100644 --- a/dlls/jscript/vbarray.c +++ b/dlls/jscript/vbarray.c @@ -93,7 +93,7 @@ static HRESULT VBArray_getItem(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns return hres;
if(r) { - hres = variant_to_jsval(&out, r); + hres = variant_to_jsval(ctx, &out, r); VariantClear(&out); } return hres; @@ -163,7 +163,7 @@ static HRESULT VBArray_toArray(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns }
for(i=0; i<size; i++) { - hres = variant_to_jsval(v, &val); + hres = variant_to_jsval(ctx, v, &val); if(SUCCEEDED(hres)) { hres = jsdisp_propput_idx(array, i, val); jsval_release(val); diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 5f475cc..28450f7 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1325,7 +1325,6 @@ sync_test("builtins_diffs", function() { sync_test("nullDisp", function() { var v = document.documentMode, nullDisp = external.nullDisp, r;
- todo_wine. ok(external.getVT(nullDisp) === "VT_NULL", "getVT(nullDisp) is not VT_NULL"); ok(typeof(nullDisp) === "object", "typeof(nullDisp) = " + typeof(nullDisp)); ok(nullDisp === nullDisp, "nullDisp !== nullDisp"); @@ -1342,10 +1341,11 @@ sync_test("nullDisp", function() { ok(r === "[object Object]", "Object(nullDisp).toString() = " + r); ok(Object(nullDisp) != nullDisp, "Object(nullDisp) == nullDisp"); ok(new Object(nullDisp) != nullDisp, "new Object(nullDisp) == nullDisp"); + r = (nullDisp instanceof Object); + ok(r === false, "nullDisp instance of Object");
if(v >= 8) { r = JSON.stringify.call(null, nullDisp); - todo_wine. ok(r === "null", "JSON.stringify(nullDisp) returned " + r); }
@@ -1360,14 +1360,12 @@ sync_test("nullDisp", function() { Function.prototype.apply.call(nullDisp, Object, []); ok(false, "expected exception calling Function.apply on nullDisp"); }catch(e) { - todo_wine. ok(e.number === 0xa138a - 0x80000000, "Function.apply on nullDisp threw " + e.number); } try { Function.prototype.call.call(nullDisp, Object); ok(false, "expected exception calling Function.call on nullDisp"); }catch(e) { - todo_wine. ok(e.number === 0xa138a - 0x80000000, "Function.call on nullDisp threw " + e.number); }
@@ -1375,7 +1373,6 @@ sync_test("nullDisp", function() { new nullDisp; ok(false, "expected exception for new nullDisp"); }catch(e) { - todo_wine. ok(e.number === 0xa138f - 0x80000000, "new nullDisp threw " + e.number); } }); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 14ec4f9..e8ee713 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1362,6 +1362,7 @@ sync_test("isFrozen", function() { });
sync_test("builtin_context", function() { + var nullDisp = external.nullDisp; var tests = [ [ "Array.map", JS_E_OBJECT_EXPECTED, function(ctx) { Array.prototype.map.call(ctx, function(a, b) {}); } ], [ "Array.sort", JS_E_OBJECT_EXPECTED, function(ctx) { Array.prototype.sort.call(ctx); } ], @@ -1400,6 +1401,13 @@ sync_test("builtin_context", function() { var n = ex.number >>> 0; ok(n === tests[i][1], tests[i][0] + " with undefined context exception code = " + n); } + try { + tests[i][2](nullDisp); + ok(false, "expected exception calling " + tests[i][0] + " with nullDisp context"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === tests[i][1], tests[i][0] + " with nullDisp context exception code = " + n); + } }
var obj = (function() { return this; }).call(null);
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=112425
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
=== 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
=== w10pro64_zh_CN (testbot log) ===
WineRunTask.pl:error: The previous 1 run(s) terminated abnormally
Signed-off-by: Jacek Caban jacek@codeweavers.com
Functions declared as function statements have an associated local_ref and can be changed from within themselves by using their name (by literally changing the local variable), while function expressions can not.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/engine.c | 6 ++-- dlls/mshtml/tests/es5.js | 59 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index c483f2a..73b9d86 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -587,7 +587,8 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_ scope->obj = to_disp(scope->jsobj); }
- if (scope == frame->base_scope && func->name && ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + if (scope == frame->base_scope && func->name && func->local_ref == INVALID_LOCAL_REF && + ctx->version >= SCRIPTLANGUAGEVERSION_ES5) jsdisp_propput_name(scope->jsobj, func->name, jsval_obj(jsdisp_addref(frame->function_instance)));
index = scope->scope_index; @@ -716,7 +717,8 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re }
/* ECMA-262 5.1 Edition 13 */ - if(func->name && ctx->version >= SCRIPTLANGUAGEVERSION_ES5 && !wcscmp(identifier, func->name)) { + if(func->name && ctx->version >= SCRIPTLANGUAGEVERSION_ES5 && + func->local_ref == INVALID_LOCAL_REF && !wcscmp(identifier, func->name)) { TRACE("returning a function from scope chain\n"); ret->type = EXPRVAL_JSVAL; ret->u.val = jsval_obj(jsdisp_addref(scope->frame->function_instance)); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index e8ee713..2c08080 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1691,6 +1691,65 @@ sync_test("functions scope", function() { func_outer(o); func(); ok(ret === o, "ret != o"); + + func_outer = function g(i) + { + ok(ret === (i ? 42 : o), "ret during g(" + i + ") = " + ret); + ok(typeof g == "function", "typeof g == " + typeof g); + + g = function() { ok(false, "redefined g was executed"); } + ret = 42; + if(!i) g(1); + } + func_outer(0); + + function h() + { + ok(typeof h == "function", "typeof h == " + typeof h); + var f = function func_inner(i) + { + if(i === 101) { + ok(h === "string", "h during old h(101) = " + h); + ret = -2; + return; + } + if(i === 100) { + ok(h.toString() === "function foo() {}", "h.toString() during old h(100) = " + h.toString()); + h = "string"; + ok(h === "string", "h during old h(100) after set to string = " + h); + ret = -1; + return; + } + if(i === 1) { + ok(h !== func_inner, "h during h(1) === func_inner"); + return; + } + ok(h === func_inner, "h during h() !== func_inner"); + if(i) { + ok(ret === 42, "ret during h(2) = " + ret); + return; + } + ret = 13; + } + f(1); + h = f; + h(2); + } + func_outer = h; + h(); + ok(ret === 42, "ret after calling h() first time = " + ret); + ok(func_outer !== h, "func_outer after calling h() first time === h"); + func_outer = h; + h(); + ok(ret === 13, "ret after calling h() second time = " + ret); + ok(func_outer === h, "func_outer after calling h() second time === h"); + h = function foo() {} + ok(func_outer !== h, "func_outer after setting h to empty function === h"); + func_outer(100); + ok(ret === -1, "ret after calling old h(100) = " + ret); + ok(h === "string", "h after calling old h(100) = " + h); + func_outer(101); + ok(ret === -2, "ret after calling old h(101) = " + ret); });
sync_test("console", 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=112426
Your paranoid android.
=== w7u_adm (32 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1649783637546 expected 1649783637609"
=== w10pro64_he (64 bit report) ===
mshtml: script.c:3042: Test failed: ReportResult failed: 80ef0001
Hi Gabriel,
On 4/12/22 16:47, Gabriel Ivăncescu wrote:
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index e8ee713..2c08080 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1691,6 +1691,65 @@ sync_test("functions scope", function() { func_outer(o); func(); ok(ret === o, "ret != o");
- func_outer = function g(i)
- {
ok(ret === (i ? 42 : o), "ret during g(" + i + ") = " + ret);
ok(typeof g == "function", "typeof g == " + typeof g);
g = function() { ok(false, "redefined g was executed"); }
ret = 42;
if(!i) g(1);
- }
- func_outer(0);
- function h()
- {
ok(typeof h == "function", "typeof h == " + typeof h);
var f = function func_inner(i)
{
if(i === 101) {
ok(h === "string", "h during old h(101) = " + h);
ret = -2;
return;
}
if(i === 100) {
ok(h.toString() === "function foo() {}", "h.toString() during old h(100) = " + h.toString());
h = "string";
ok(h === "string", "h during old h(100) after set to string = " + h);
ret = -1;
return;
}
if(i === 1) {
ok(h !== func_inner, "h during h(1) === func_inner");
return;
}
ok(h === func_inner, "h during h() !== func_inner");
if(i) {
ok(ret === 42, "ret during h(2) = " + ret);
return;
}
ret = 13;
}
f(1);
h = f;
h(2);
- }
- func_outer = h;
- h();
- ok(ret === 42, "ret after calling h() first time = " + ret);
- ok(func_outer !== h, "func_outer after calling h() first time === h");
- func_outer = h;
- h();
- ok(ret === 13, "ret after calling h() second time = " + ret);
- ok(func_outer === h, "func_outer after calling h() second time === h");
- h = function foo() {}
- ok(func_outer !== h, "func_outer after setting h to empty function === h");
- func_outer(100);
- ok(ret === -1, "ret after calling old h(100) = " + ret);
- ok(h === "string", "h after calling old h(100) = " + h);
- func_outer(101);
- ok(ret === -2, "ret after calling old h(101) = " + ret); });
I think that you made it much more complicated than it needs to be. See the attached patch for an example how it could be made more readable.
Thanks,
Jacek
On 12/04/2022 21:16, Jacek Caban wrote:
Hi Gabriel,
On 4/12/22 16:47, Gabriel Ivăncescu wrote:
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index e8ee713..2c08080 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1691,6 +1691,65 @@ sync_test("functions scope", function() { func_outer(o); func(); ok(ret === o, "ret != o");
+ func_outer = function g(i) + { + ok(ret === (i ? 42 : o), "ret during g(" + i + ") = " + ret); + ok(typeof g == "function", "typeof g == " + typeof g);
+ g = function() { ok(false, "redefined g was executed"); } + ret = 42; + if(!i) g(1); + } + func_outer(0);
+ function h() + { + ok(typeof h == "function", "typeof h == " + typeof h); + var f = function func_inner(i) + { + if(i === 101) { + ok(h === "string", "h during old h(101) = " + h); + ret = -2; + return; + } + if(i === 100) { + ok(h.toString() === "function foo() {}", "h.toString() during old h(100) = " + h.toString()); + h = "string"; + ok(h === "string", "h during old h(100) after set to string = " + h); + ret = -1; + return; + } + if(i === 1) { + ok(h !== func_inner, "h during h(1) === func_inner"); + return; + } + ok(h === func_inner, "h during h() !== func_inner"); + if(i) { + ok(ret === 42, "ret during h(2) = " + ret); + return; + } + ret = 13; + } + f(1); + h = f; + h(2); + } + func_outer = h; + h(); + ok(ret === 42, "ret after calling h() first time = " + ret); + ok(func_outer !== h, "func_outer after calling h() first time === h"); + func_outer = h; + h(); + ok(ret === 13, "ret after calling h() second time = " + ret); + ok(func_outer === h, "func_outer after calling h() second time === h"); + h = function foo() {} + ok(func_outer !== h, "func_outer after setting h to empty function === h"); + func_outer(100); + ok(ret === -1, "ret after calling old h(100) = " + ret); + ok(h === "string", "h after calling old h(100) = " + h); + func_outer(101); + ok(ret === -2, "ret after calling old h(101) = " + ret); });
I think that you made it much more complicated than it needs to be. See the attached patch for an example how it could be made more readable.
Thanks,
Jacek
Hi Jacek,
I wanted to be thorough with the tests and include weird cases (such as when it's detached vs not) just to make sure, but if you think your tests are enough I'd be happy to resort to it.
Thanks, Gabriel
On 4/13/22 13:43, Gabriel Ivăncescu wrote:
On 12/04/2022 21:16, Jacek Caban wrote:
Hi Gabriel,
On 4/12/22 16:47, Gabriel Ivăncescu wrote:
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index e8ee713..2c08080 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1691,6 +1691,65 @@ sync_test("functions scope", function() { func_outer(o); func(); ok(ret === o, "ret != o");
+ func_outer = function g(i) + { + ok(ret === (i ? 42 : o), "ret during g(" + i + ") = " + ret); + ok(typeof g == "function", "typeof g == " + typeof g);
+ g = function() { ok(false, "redefined g was executed"); } + ret = 42; + if(!i) g(1); + } + func_outer(0);
+ function h() + { + ok(typeof h == "function", "typeof h == " + typeof h); + var f = function func_inner(i) + { + if(i === 101) { + ok(h === "string", "h during old h(101) = " + h); + ret = -2; + return; + } + if(i === 100) { + ok(h.toString() === "function foo() {}", "h.toString() during old h(100) = " + h.toString()); + h = "string"; + ok(h === "string", "h during old h(100) after set to string = " + h); + ret = -1; + return; + } + if(i === 1) { + ok(h !== func_inner, "h during h(1) === func_inner"); + return; + } + ok(h === func_inner, "h during h() !== func_inner"); + if(i) { + ok(ret === 42, "ret during h(2) = " + ret); + return; + } + ret = 13; + } + f(1); + h = f; + h(2); + } + func_outer = h; + h(); + ok(ret === 42, "ret after calling h() first time = " + ret); + ok(func_outer !== h, "func_outer after calling h() first time === h"); + func_outer = h; + h(); + ok(ret === 13, "ret after calling h() second time = " + ret); + ok(func_outer === h, "func_outer after calling h() second time === h"); + h = function foo() {} + ok(func_outer !== h, "func_outer after setting h to empty function === h"); + func_outer(100); + ok(ret === -1, "ret after calling old h(100) = " + ret); + ok(h === "string", "h after calling old h(100) = " + h); + func_outer(101); + ok(ret === -2, "ret after calling old h(101) = " + ret); });
I think that you made it much more complicated than it needs to be. See the attached patch for an example how it could be made more readable.
Thanks,
Jacek
Hi Jacek,
I wanted to be thorough with the tests and include weird cases (such as when it's detached vs not) just to make sure, but if you think your tests are enough I'd be happy to resort to it.
Having thorough tests is obviously welcomed, but it doesn't mean that they need to be so hard to read. The version that I sent you already tests detached function statement scopes, it should be easy to test function expressions in a similar way. Is there anything else we'd miss from your tests?
Thanks,
Jacek
On 13/04/2022 16:18, Jacek Caban wrote:
On 4/13/22 13:43, Gabriel Ivăncescu wrote:
On 12/04/2022 21:16, Jacek Caban wrote:
Hi Gabriel,
On 4/12/22 16:47, Gabriel Ivăncescu wrote:
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index e8ee713..2c08080 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1691,6 +1691,65 @@ sync_test("functions scope", function() { func_outer(o); func(); ok(ret === o, "ret != o");
+ func_outer = function g(i) + { + ok(ret === (i ? 42 : o), "ret during g(" + i + ") = " + ret); + ok(typeof g == "function", "typeof g == " + typeof g);
+ g = function() { ok(false, "redefined g was executed"); } + ret = 42; + if(!i) g(1); + } + func_outer(0);
+ function h() + { + ok(typeof h == "function", "typeof h == " + typeof h); + var f = function func_inner(i) + { + if(i === 101) { + ok(h === "string", "h during old h(101) = " + h); + ret = -2; + return; + } + if(i === 100) { + ok(h.toString() === "function foo() {}", "h.toString() during old h(100) = " + h.toString()); + h = "string"; + ok(h === "string", "h during old h(100) after set to string = " + h); + ret = -1; + return; + } + if(i === 1) { + ok(h !== func_inner, "h during h(1) === func_inner"); + return; + } + ok(h === func_inner, "h during h() !== func_inner"); + if(i) { + ok(ret === 42, "ret during h(2) = " + ret); + return; + } + ret = 13; + } + f(1); + h = f; + h(2); + } + func_outer = h; + h(); + ok(ret === 42, "ret after calling h() first time = " + ret); + ok(func_outer !== h, "func_outer after calling h() first time === h"); + func_outer = h; + h(); + ok(ret === 13, "ret after calling h() second time = " + ret); + ok(func_outer === h, "func_outer after calling h() second time === h"); + h = function foo() {} + ok(func_outer !== h, "func_outer after setting h to empty function === h"); + func_outer(100); + ok(ret === -1, "ret after calling old h(100) = " + ret); + ok(h === "string", "h after calling old h(100) = " + h); + func_outer(101); + ok(ret === -2, "ret after calling old h(101) = " + ret); });
I think that you made it much more complicated than it needs to be. See the attached patch for an example how it could be made more readable.
Thanks,
Jacek
Hi Jacek,
I wanted to be thorough with the tests and include weird cases (such as when it's detached vs not) just to make sure, but if you think your tests are enough I'd be happy to resort to it.
Having thorough tests is obviously welcomed, but it doesn't mean that they need to be so hard to read. The version that I sent you already tests detached function statement scopes, it should be easy to test function expressions in a similar way. Is there anything else we'd miss from your tests?
Thanks,
Jacek
It's probably not relevant, but for example testing stuff like checking the function 'g' after setting it within the function itself (in fact, this was the main thing I had to fix it for) to see whether it was changed as well, rather than just outside of it. I should probably add those of course since they don't make it complicated.
Most of my tests were just an artifact of my debugging trying a bunch of things to fix it until I realized how it's supposed to work :-)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstylesheet.c | 152 ++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/dom.c | 27 +++++++ 2 files changed, 177 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlstylesheet.c b/dlls/mshtml/htmlstylesheet.c index 2d16f4a..2d76e37 100644 --- a/dlls/mshtml/htmlstylesheet.c +++ b/dlls/mshtml/htmlstylesheet.c @@ -50,6 +50,15 @@ struct HTMLStyleSheetsCollection { nsIDOMStyleSheetList *nslist; };
+typedef struct { + IEnumVARIANT IEnumVARIANT_iface; + + LONG ref; + + ULONG iter; + HTMLStyleSheetsCollection *col; +} HTMLStyleSheetsCollectionEnum; + struct HTMLStyleSheetRulesCollection { DispatchEx dispex; IHTMLStyleSheetRulesCollection IHTMLStyleSheetRulesCollection_iface; @@ -479,6 +488,130 @@ static HRESULT create_style_sheet_rules_collection(nsIDOMCSSRuleList *nslist, co return S_OK; }
+static inline HTMLStyleSheetsCollectionEnum *HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, HTMLStyleSheetsCollectionEnum, IEnumVARIANT_iface); +} + +static HRESULT WINAPI HTMLStyleSheetsCollectionEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + HTMLStyleSheetsCollectionEnum *This = HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + *ppv = &This->IEnumVARIANT_iface; + }else if(IsEqualGUID(riid, &IID_IEnumVARIANT)) { + *ppv = &This->IEnumVARIANT_iface; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI HTMLStyleSheetsCollectionEnum_AddRef(IEnumVARIANT *iface) +{ + HTMLStyleSheetsCollectionEnum *This = HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI HTMLStyleSheetsCollectionEnum_Release(IEnumVARIANT *iface) +{ + HTMLStyleSheetsCollectionEnum *This = HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if(!ref) { + IHTMLStyleSheetsCollection_Release(&This->col->IHTMLStyleSheetsCollection_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI HTMLStyleSheetsCollectionEnum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + HTMLStyleSheetsCollectionEnum *This = HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(iface); + VARIANT index; + HRESULT hres; + ULONG num, i; + UINT32 len; + + TRACE("(%p)->(%lu %p %p)\n", This, celt, rgVar, pCeltFetched); + + nsIDOMStyleSheetList_GetLength(This->col->nslist, &len); + num = min(len - This->iter, celt); + V_VT(&index) = VT_I4; + + for(i = 0; i < num; i++) { + V_I4(&index) = This->iter + i; + hres = IHTMLStyleSheetsCollection_item(&This->col->IHTMLStyleSheetsCollection_iface, &index, &rgVar[i]); + if(FAILED(hres)) { + while(i--) + VariantClear(&rgVar[i]); + return hres; + } + } + + This->iter += num; + if(pCeltFetched) + *pCeltFetched = num; + return num == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI HTMLStyleSheetsCollectionEnum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + HTMLStyleSheetsCollectionEnum *This = HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(iface); + UINT32 len; + + TRACE("(%p)->(%lu)\n", This, celt); + + nsIDOMStyleSheetList_GetLength(This->col->nslist, &len); + if(This->iter + celt > len) { + This->iter = len; + return S_FALSE; + } + + This->iter += celt; + return S_OK; +} + +static HRESULT WINAPI HTMLStyleSheetsCollectionEnum_Reset(IEnumVARIANT *iface) +{ + HTMLStyleSheetsCollectionEnum *This = HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(iface); + + TRACE("(%p)->()\n", This); + + This->iter = 0; + return S_OK; +} + +static HRESULT WINAPI HTMLStyleSheetsCollectionEnum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + HTMLStyleSheetsCollectionEnum *This = HTMLStyleSheetsCollectionEnum_from_IEnumVARIANT(iface); + FIXME("(%p)->(%p)\n", This, ppEnum); + return E_NOTIMPL; +} + +static const IEnumVARIANTVtbl HTMLStyleSheetsCollectionEnumVtbl = { + HTMLStyleSheetsCollectionEnum_QueryInterface, + HTMLStyleSheetsCollectionEnum_AddRef, + HTMLStyleSheetsCollectionEnum_Release, + HTMLStyleSheetsCollectionEnum_Next, + HTMLStyleSheetsCollectionEnum_Skip, + HTMLStyleSheetsCollectionEnum_Reset, + HTMLStyleSheetsCollectionEnum_Clone +}; + static inline HTMLStyleSheetsCollection *impl_from_IHTMLStyleSheetsCollection(IHTMLStyleSheetsCollection *iface) { return CONTAINING_RECORD(iface, HTMLStyleSheetsCollection, IHTMLStyleSheetsCollection_iface); @@ -586,8 +719,23 @@ static HRESULT WINAPI HTMLStyleSheetsCollection_get__newEnum(IHTMLStyleSheetsCol IUnknown **p) { HTMLStyleSheetsCollection *This = impl_from_IHTMLStyleSheetsCollection(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + HTMLStyleSheetsCollectionEnum *ret; + + TRACE("(%p)->(%p)\n", This, p); + + ret = heap_alloc(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IEnumVARIANT_iface.lpVtbl = &HTMLStyleSheetsCollectionEnumVtbl; + ret->ref = 1; + ret->iter = 0; + + HTMLStyleSheetsCollection_AddRef(&This->IHTMLStyleSheetsCollection_iface); + ret->col = This; + + *p = (IUnknown*)&ret->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI HTMLStyleSheetsCollection_item(IHTMLStyleSheetsCollection *iface, diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 4af0e9b..73b9831 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -8577,7 +8577,10 @@ static void test_stylesheet(IDispatch *disp) static void test_stylesheets(IHTMLDocument2 *doc) { IHTMLStyleSheetsCollection *col = NULL; + IEnumVARIANT *enum_var; + IUnknown *enum_unk; VARIANT idx, res; + ULONG fetched; LONG len = 0; HRESULT hres;
@@ -8611,6 +8614,30 @@ static void test_stylesheets(IHTMLDocument2 *doc) ok(V_VT(&res) == VT_EMPTY, "V_VT(res) = %d\n", V_VT(&res)); VariantClear(&res);
+ hres = IHTMLStyleSheetsCollection_get__newEnum(col, &enum_unk); + ok(hres == S_OK, "_newEnum failed: %08lx\n", hres); + + hres = IUnknown_QueryInterface(enum_unk, &IID_IEnumVARIANT, (void**)&enum_var); + IUnknown_Release(enum_unk); + ok(hres == S_OK, "Could not get IEnumVARIANT iface: %08lx\n", hres); + + fetched = 0; + V_VT(&res) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &res, &fetched); + ok(hres == S_OK, "Next failed: %08lx\n", hres); + ok(fetched == 1, "fetched = %lu\n", fetched); + ok(V_VT(&res) == VT_DISPATCH, "V_VT(res) = %d\n", V_VT(&res)); + ok(V_DISPATCH(&res) != NULL, "V_DISPATCH(&res) == NULL\n"); + test_disp2((IUnknown*)V_DISPATCH(&res), &DIID_DispHTMLStyleSheet, &IID_IHTMLStyleSheet, NULL, L"[object]"); + VariantClear(&res); + + fetched = 0; + V_VT(&res) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &res, &fetched); + ok(hres == S_FALSE, "Next failed: %08lx\n", hres); + ok(fetched == 0, "fetched = %lu\n", fetched); + IEnumVARIANT_Release(enum_var); + IHTMLStyleSheetsCollection_Release(col); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlelem.c | 152 +++++++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/dom.c | 27 +++++++ 2 files changed, 177 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index 47c1827..9ed2249 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -781,6 +781,139 @@ typedef struct { nsIDOMClientRectList *rect_list; } HTMLRectCollection;
+typedef struct { + IEnumVARIANT IEnumVARIANT_iface; + + LONG ref; + + ULONG iter; + HTMLRectCollection *col; +} HTMLRectCollectionEnum; + +static inline HTMLRectCollectionEnum *HTMLRectCollectionEnum_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, HTMLRectCollectionEnum, IEnumVARIANT_iface); +} + +static HRESULT WINAPI HTMLRectCollectionEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + HTMLRectCollectionEnum *This = HTMLRectCollectionEnum_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + *ppv = &This->IEnumVARIANT_iface; + }else if(IsEqualGUID(riid, &IID_IEnumVARIANT)) { + *ppv = &This->IEnumVARIANT_iface; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI HTMLRectCollectionEnum_AddRef(IEnumVARIANT *iface) +{ + HTMLRectCollectionEnum *This = HTMLRectCollectionEnum_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI HTMLRectCollectionEnum_Release(IEnumVARIANT *iface) +{ + HTMLRectCollectionEnum *This = HTMLRectCollectionEnum_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if(!ref) { + IHTMLRectCollection_Release(&This->col->IHTMLRectCollection_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI HTMLRectCollectionEnum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + HTMLRectCollectionEnum *This = HTMLRectCollectionEnum_from_IEnumVARIANT(iface); + VARIANT index; + HRESULT hres; + ULONG num, i; + UINT32 len; + + TRACE("(%p)->(%lu %p %p)\n", This, celt, rgVar, pCeltFetched); + + nsIDOMClientRectList_GetLength(This->col->rect_list, &len); + num = min(len - This->iter, celt); + V_VT(&index) = VT_I4; + + for(i = 0; i < num; i++) { + V_I4(&index) = This->iter + i; + hres = IHTMLRectCollection_item(&This->col->IHTMLRectCollection_iface, &index, &rgVar[i]); + if(FAILED(hres)) { + while(i--) + VariantClear(&rgVar[i]); + return hres; + } + } + + This->iter += num; + if(pCeltFetched) + *pCeltFetched = num; + return num == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI HTMLRectCollectionEnum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + HTMLRectCollectionEnum *This = HTMLRectCollectionEnum_from_IEnumVARIANT(iface); + UINT32 len; + + TRACE("(%p)->(%lu)\n", This, celt); + + nsIDOMClientRectList_GetLength(This->col->rect_list, &len); + if(This->iter + celt > len) { + This->iter = len; + return S_FALSE; + } + + This->iter += celt; + return S_OK; +} + +static HRESULT WINAPI HTMLRectCollectionEnum_Reset(IEnumVARIANT *iface) +{ + HTMLRectCollectionEnum *This = HTMLRectCollectionEnum_from_IEnumVARIANT(iface); + + TRACE("(%p)->()\n", This); + + This->iter = 0; + return S_OK; +} + +static HRESULT WINAPI HTMLRectCollectionEnum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + HTMLRectCollectionEnum *This = HTMLRectCollectionEnum_from_IEnumVARIANT(iface); + FIXME("(%p)->(%p)\n", This, ppEnum); + return E_NOTIMPL; +} + +static const IEnumVARIANTVtbl HTMLRectCollectionEnumVtbl = { + HTMLRectCollectionEnum_QueryInterface, + HTMLRectCollectionEnum_AddRef, + HTMLRectCollectionEnum_Release, + HTMLRectCollectionEnum_Next, + HTMLRectCollectionEnum_Skip, + HTMLRectCollectionEnum_Reset, + HTMLRectCollectionEnum_Clone +}; + static inline HTMLRectCollection *impl_from_IHTMLRectCollection(IHTMLRectCollection *iface) { return CONTAINING_RECORD(iface, HTMLRectCollection, IHTMLRectCollection_iface); @@ -883,8 +1016,23 @@ static HRESULT WINAPI HTMLRectCollection_get_length(IHTMLRectCollection *iface, static HRESULT WINAPI HTMLRectCollection_get__newEnum(IHTMLRectCollection *iface, IUnknown **p) { HTMLRectCollection *This = impl_from_IHTMLRectCollection(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + HTMLRectCollectionEnum *ret; + + TRACE("(%p)->(%p)\n", This, p); + + ret = heap_alloc(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IEnumVARIANT_iface.lpVtbl = &HTMLRectCollectionEnumVtbl; + ret->ref = 1; + ret->iter = 0; + + HTMLRectCollection_AddRef(&This->IHTMLRectCollection_iface); + ret->col = This; + + *p = (IUnknown*)&ret->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI HTMLRectCollection_item(IHTMLRectCollection *iface, VARIANT *index, VARIANT *result) diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 73b9831..00c1577 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -5229,8 +5229,11 @@ static void test_elem_bounding_client_rect(IUnknown *unk) { IHTMLRectCollection *rects; IHTMLRect *rect, *rect2; + IEnumVARIANT *enum_var; IHTMLElement2 *elem2; + IUnknown *enum_unk; VARIANT v, index; + ULONG fetched; LONG l; HRESULT hres;
@@ -5285,6 +5288,30 @@ static void test_elem_bounding_client_rect(IUnknown *unk) test_disp((IUnknown*)V_DISPATCH(&v), &IID_IHTMLRect, NULL, L"[object]"); VariantClear(&v);
+ hres = IHTMLRectCollection_get__newEnum(rects, &enum_unk); + ok(hres == S_OK, "_newEnum failed: %08lx\n", hres); + + hres = IUnknown_QueryInterface(enum_unk, &IID_IEnumVARIANT, (void**)&enum_var); + IUnknown_Release(enum_unk); + ok(hres == S_OK, "Could not get IEnumVARIANT iface: %08lx\n", hres); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_OK, "Next failed: %08lx\n", hres); + ok(fetched == 1, "fetched = %lu\n", fetched); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(&v) == NULL\n"); + test_disp((IUnknown*)V_DISPATCH(&v), &IID_IHTMLRect, NULL, L"[object]"); + VariantClear(&v); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_FALSE, "Next failed: %08lx\n", hres); + ok(fetched == 0, "fetched = %lu\n", fetched); + IEnumVARIANT_Release(enum_var); + IHTMLRectCollection_Release(rects); IHTMLElement2_Release(elem2); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlform.c | 157 +++++++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/dom.c | 39 +++++++++- 2 files changed, 193 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlform.c b/dlls/mshtml/htmlform.c index 45ff19a..18b56ae 100644 --- a/dlls/mshtml/htmlform.c +++ b/dlls/mshtml/htmlform.c @@ -41,6 +41,15 @@ struct HTMLFormElement { nsIDOMHTMLFormElement *nsform; };
+typedef struct { + IEnumVARIANT IEnumVARIANT_iface; + + LONG ref; + + ULONG iter; + HTMLFormElement *elem; +} HTMLFormElementEnum; + HRESULT return_nsform(nsresult nsres, nsIDOMHTMLFormElement *form, IHTMLFormElement **p) { nsIDOMNode *form_node; @@ -108,6 +117,135 @@ static HRESULT htmlform_item(HTMLFormElement *This, int i, IDispatch **ret) return S_OK; }
+static inline HTMLFormElementEnum *impl_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, HTMLFormElementEnum, IEnumVARIANT_iface); +} + +static HRESULT WINAPI HTMLFormElementEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + HTMLFormElementEnum *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + *ppv = &This->IEnumVARIANT_iface; + }else if(IsEqualGUID(riid, &IID_IEnumVARIANT)) { + *ppv = &This->IEnumVARIANT_iface; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI HTMLFormElementEnum_AddRef(IEnumVARIANT *iface) +{ + HTMLFormElementEnum *This = impl_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI HTMLFormElementEnum_Release(IEnumVARIANT *iface) +{ + HTMLFormElementEnum *This = impl_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if(!ref) { + IHTMLFormElement_Release(&This->elem->IHTMLFormElement_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI HTMLFormElementEnum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + HTMLFormElementEnum *This = impl_from_IEnumVARIANT(iface); + nsresult nsres; + HRESULT hres; + ULONG num, i; + LONG len; + + TRACE("(%p)->(%lu %p %p)\n", This, celt, rgVar, pCeltFetched); + + nsres = nsIDOMHTMLFormElement_GetLength(This->elem->nsform, &len); + if(NS_FAILED(nsres)) + return E_FAIL; + num = min(len - This->iter, celt); + + for(i = 0; i < num; i++) { + hres = htmlform_item(This->elem, This->iter + i, &V_DISPATCH(&rgVar[i])); + if(FAILED(hres)) { + while(i--) + VariantClear(&rgVar[i]); + return hres; + } + V_VT(&rgVar[i]) = VT_DISPATCH; + } + + This->iter += num; + if(pCeltFetched) + *pCeltFetched = num; + return num == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI HTMLFormElementEnum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + HTMLFormElementEnum *This = impl_from_IEnumVARIANT(iface); + nsresult nsres; + LONG len; + + TRACE("(%p)->(%lu)\n", This, celt); + + nsres = nsIDOMHTMLFormElement_GetLength(This->elem->nsform, &len); + if(NS_FAILED(nsres)) + return E_FAIL; + + if(This->iter + celt > len) { + This->iter = len; + return S_FALSE; + } + + This->iter += celt; + return S_OK; +} + +static HRESULT WINAPI HTMLFormElementEnum_Reset(IEnumVARIANT *iface) +{ + HTMLFormElementEnum *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p)->()\n", This); + + This->iter = 0; + return S_OK; +} + +static HRESULT WINAPI HTMLFormElementEnum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + HTMLFormElementEnum *This = impl_from_IEnumVARIANT(iface); + FIXME("(%p)->(%p)\n", This, ppEnum); + return E_NOTIMPL; +} + +static const IEnumVARIANTVtbl HTMLFormElementEnumVtbl = { + HTMLFormElementEnum_QueryInterface, + HTMLFormElementEnum_AddRef, + HTMLFormElementEnum_Release, + HTMLFormElementEnum_Next, + HTMLFormElementEnum_Skip, + HTMLFormElementEnum_Reset, + HTMLFormElementEnum_Clone +}; + static inline HTMLFormElement *impl_from_IHTMLFormElement(IHTMLFormElement *iface) { return CONTAINING_RECORD(iface, HTMLFormElement, IHTMLFormElement_iface); @@ -545,8 +683,23 @@ static HRESULT WINAPI HTMLFormElement_get_length(IHTMLFormElement *iface, LONG * static HRESULT WINAPI HTMLFormElement__newEnum(IHTMLFormElement *iface, IUnknown **p) { HTMLFormElement *This = impl_from_IHTMLFormElement(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + HTMLFormElementEnum *ret; + + TRACE("(%p)->(%p)\n", This, p); + + ret = heap_alloc(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IEnumVARIANT_iface.lpVtbl = &HTMLFormElementEnumVtbl; + ret->ref = 1; + ret->iter = 0; + + HTMLFormElement_AddRef(&This->IHTMLFormElement_iface); + ret->elem = This; + + *p = (IUnknown*)&ret->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI HTMLFormElement_item(IHTMLFormElement *iface, VARIANT name, diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 00c1577..bd4b772 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -5504,7 +5504,10 @@ static void test_form_item(IHTMLElement *elem) { IHTMLFormElement *form = get_form_iface((IUnknown*)elem); IDispatch *disp, *disp2; - VARIANT name, index; + IEnumVARIANT *enum_var; + VARIANT name, index, v; + IUnknown *enum_unk; + ULONG fetched; HRESULT hres;
V_VT(&index) = VT_EMPTY; @@ -5543,6 +5546,40 @@ static void test_form_item(IHTMLElement *elem) ok(iface_cmp((IUnknown*)disp, (IUnknown*)disp2), "disp != disp2\n"); IDispatch_Release(disp2); IDispatch_Release(disp); + + hres = IHTMLFormElement_get__newEnum(form, &enum_unk); + ok(hres == S_OK, "_newEnum failed: %08lx\n", hres); + + hres = IUnknown_QueryInterface(enum_unk, &IID_IEnumVARIANT, (void**)&enum_var); + IUnknown_Release(enum_unk); + ok(hres == S_OK, "Could not get IEnumVARIANT iface: %08lx\n", hres); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_OK, "Next failed: %08lx\n", hres); + ok(fetched == 1, "fetched = %lu\n", fetched); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(&v) == NULL\n"); + test_disp((IUnknown*)V_DISPATCH(&v), &DIID_DispHTMLTextAreaElement, &CLSID_HTMLTextAreaElement, NULL); + VariantClear(&v); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_OK, "Next failed: %08lx\n", hres); + ok(fetched == 1, "fetched = %lu\n", fetched); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(&v) == NULL\n"); + test_disp((IUnknown*)V_DISPATCH(&v), &DIID_DispHTMLInputElement, &CLSID_HTMLInputElement, NULL); + VariantClear(&v); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_FALSE, "Next failed: %08lx\n", hres); + ok(fetched == 0, "fetched = %lu\n", fetched); + IEnumVARIANT_Release(enum_var); }
static void test_create_option_elem(IHTMLDocument2 *doc)
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=112429
Your paranoid android.
=== w864 (32 bit report) ===
mshtml: 027c:dom: unhandled exception c0000005 at 7750576E
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlselect.c | 157 ++++++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/dom.c | 39 +++++++++- 2 files changed, 193 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlselect.c b/dlls/mshtml/htmlselect.c index 44981a5..ae6543a 100644 --- a/dlls/mshtml/htmlselect.c +++ b/dlls/mshtml/htmlselect.c @@ -674,6 +674,15 @@ struct HTMLSelectElement { nsIDOMHTMLSelectElement *nsselect; };
+typedef struct { + IEnumVARIANT IEnumVARIANT_iface; + + LONG ref; + + ULONG iter; + HTMLSelectElement *elem; +} HTMLSelectElementEnum; + static inline HTMLSelectElement *impl_from_IHTMLSelectElement(IHTMLSelectElement *iface) { return CONTAINING_RECORD(iface, HTMLSelectElement, IHTMLSelectElement_iface); @@ -714,6 +723,135 @@ static HRESULT htmlselect_item(HTMLSelectElement *This, int i, IDispatch **ret) return S_OK; }
+static inline HTMLSelectElementEnum *impl_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, HTMLSelectElementEnum, IEnumVARIANT_iface); +} + +static HRESULT WINAPI HTMLSelectElementEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + HTMLSelectElementEnum *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + *ppv = &This->IEnumVARIANT_iface; + }else if(IsEqualGUID(riid, &IID_IEnumVARIANT)) { + *ppv = &This->IEnumVARIANT_iface; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI HTMLSelectElementEnum_AddRef(IEnumVARIANT *iface) +{ + HTMLSelectElementEnum *This = impl_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI HTMLSelectElementEnum_Release(IEnumVARIANT *iface) +{ + HTMLSelectElementEnum *This = impl_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if(!ref) { + IHTMLSelectElement_Release(&This->elem->IHTMLSelectElement_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI HTMLSelectElementEnum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + HTMLSelectElementEnum *This = impl_from_IEnumVARIANT(iface); + nsresult nsres; + HRESULT hres; + ULONG num, i; + UINT32 len; + + TRACE("(%p)->(%lu %p %p)\n", This, celt, rgVar, pCeltFetched); + + nsres = nsIDOMHTMLSelectElement_GetLength(This->elem->nsselect, &len); + if(NS_FAILED(nsres)) + return E_FAIL; + num = min(len - This->iter, celt); + + for(i = 0; i < num; i++) { + hres = htmlselect_item(This->elem, This->iter + i, &V_DISPATCH(&rgVar[i])); + if(FAILED(hres)) { + while(i--) + VariantClear(&rgVar[i]); + return hres; + } + V_VT(&rgVar[i]) = VT_DISPATCH; + } + + This->iter += num; + if(pCeltFetched) + *pCeltFetched = num; + return num == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI HTMLSelectElementEnum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + HTMLSelectElementEnum *This = impl_from_IEnumVARIANT(iface); + nsresult nsres; + UINT32 len; + + TRACE("(%p)->(%lu)\n", This, celt); + + nsres = nsIDOMHTMLSelectElement_GetLength(This->elem->nsselect, &len); + if(NS_FAILED(nsres)) + return E_FAIL; + + if(This->iter + celt > len) { + This->iter = len; + return S_FALSE; + } + + This->iter += celt; + return S_OK; +} + +static HRESULT WINAPI HTMLSelectElementEnum_Reset(IEnumVARIANT *iface) +{ + HTMLSelectElementEnum *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p)->()\n", This); + + This->iter = 0; + return S_OK; +} + +static HRESULT WINAPI HTMLSelectElementEnum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + HTMLSelectElementEnum *This = impl_from_IEnumVARIANT(iface); + FIXME("(%p)->(%p)\n", This, ppEnum); + return E_NOTIMPL; +} + +static const IEnumVARIANTVtbl HTMLSelectElementEnumVtbl = { + HTMLSelectElementEnum_QueryInterface, + HTMLSelectElementEnum_AddRef, + HTMLSelectElementEnum_Release, + HTMLSelectElementEnum_Next, + HTMLSelectElementEnum_Skip, + HTMLSelectElementEnum_Reset, + HTMLSelectElementEnum_Clone +}; + static HRESULT WINAPI HTMLSelectElement_QueryInterface(IHTMLSelectElement *iface, REFIID riid, void **ppv) { @@ -1117,8 +1255,23 @@ static HRESULT WINAPI HTMLSelectElement_get_length(IHTMLSelectElement *iface, LO static HRESULT WINAPI HTMLSelectElement_get__newEnum(IHTMLSelectElement *iface, IUnknown **p) { HTMLSelectElement *This = impl_from_IHTMLSelectElement(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + HTMLSelectElementEnum *ret; + + TRACE("(%p)->(%p)\n", This, p); + + ret = heap_alloc(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IEnumVARIANT_iface.lpVtbl = &HTMLSelectElementEnumVtbl; + ret->ref = 1; + ret->iter = 0; + + HTMLSelectElement_AddRef(&This->IHTMLSelectElement_iface); + ret->elem = This; + + *p = (IUnknown*)&ret->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI HTMLSelectElement_item(IHTMLSelectElement *iface, VARIANT name, diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index bd4b772..0f5120a 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -5425,7 +5425,10 @@ static IHTMLElement *get_doc_elem_by_id(IHTMLDocument2 *doc, const WCHAR *id) static void test_select_elem(IHTMLSelectElement *select) { IDispatch *disp, *disp2; - VARIANT name, index; + VARIANT name, index, v; + IEnumVARIANT *enum_var; + IUnknown *enum_unk; + ULONG fetched; HRESULT hres;
test_select_type(select, L"select-one"); @@ -5495,6 +5498,40 @@ static void test_select_elem(IHTMLSelectElement *select) IDispatch_Release(disp2); IDispatch_Release(disp);
+ hres = IHTMLSelectElement_get__newEnum(select, &enum_unk); + ok(hres == S_OK, "_newEnum failed: %08lx\n", hres); + + hres = IUnknown_QueryInterface(enum_unk, &IID_IEnumVARIANT, (void**)&enum_var); + IUnknown_Release(enum_unk); + ok(hres == S_OK, "Could not get IEnumVARIANT iface: %08lx\n", hres); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_OK, "Next failed: %08lx\n", hres); + ok(fetched == 1, "fetched = %lu\n", fetched); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(&v) == NULL\n"); + test_disp((IUnknown*)V_DISPATCH(&v), &DIID_DispHTMLOptionElement, &CLSID_HTMLOptionElement, NULL); + VariantClear(&v); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_OK, "Next failed: %08lx\n", hres); + ok(fetched == 1, "fetched = %lu\n", fetched); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(&v) == NULL\n"); + test_disp((IUnknown*)V_DISPATCH(&v), &DIID_DispHTMLOptionElement, &CLSID_HTMLOptionElement, NULL); + VariantClear(&v); + + fetched = 0; + V_VT(&v) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &v, &fetched); + ok(hres == S_FALSE, "Next failed: %08lx\n", hres); + ok(fetched == 0, "fetched = %lu\n", fetched); + IEnumVARIANT_Release(enum_var); + test_select_multiple(select, VARIANT_FALSE); test_select_set_multiple(select, VARIANT_TRUE); test_select_remove(select);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
So that they don't have to be forward declared in next patch.
dlls/mshtml/htmlelem.c | 176 ++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 88 deletions(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index 9ed2249..ea5c65b 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -7578,6 +7578,94 @@ static HRESULT create_filters_collection(compat_mode_t compat_mode, IHTMLFilters return S_OK; }
+static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, DISPID *dispid) +{ + IDispatchEx *dispex = &This->elem->node.event_target.dispex.IDispatchEx_iface; + DISPID id = DISPID_STARTENUM; + LONG len = -1; + HRESULT hres; + + FIXME("filter non-enumerable attributes out\n"); + + while(1) { + hres = IDispatchEx_GetNextDispID(dispex, fdexEnumAll, id, &id); + if(FAILED(hres)) + return hres; + else if(hres == S_FALSE) + break; + + len++; + if(len == *idx) + break; + } + + if(dispid) { + *dispid = id; + return *idx==len ? S_OK : DISP_E_UNKNOWNNAME; + } + + *idx = len+1; + return S_OK; +} + +static inline HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, BSTR name, DISPID *id) +{ + HRESULT hres; + + if(name[0]>='0' && name[0]<='9') { + WCHAR *end_ptr; + LONG idx; + + idx = wcstoul(name, &end_ptr, 10); + if(!*end_ptr) { + hres = get_attr_dispid_by_idx(This, &idx, id); + if(SUCCEEDED(hres)) + return hres; + } + } + + if(!This->elem) { + WARN("NULL elem\n"); + return E_UNEXPECTED; + } + + hres = IDispatchEx_GetDispID(&This->elem->node.event_target.dispex.IDispatchEx_iface, + name, fdexNameCaseInsensitive, id); + return hres; +} + +static inline HRESULT get_domattr(HTMLAttributeCollection *This, DISPID id, LONG *list_pos, HTMLDOMAttribute **attr) +{ + HTMLDOMAttribute *iter; + LONG pos = 0; + HRESULT hres; + + *attr = NULL; + LIST_FOR_EACH_ENTRY(iter, &This->attrs, HTMLDOMAttribute, entry) { + if(iter->dispid == id) { + *attr = iter; + break; + } + pos++; + } + + if(!*attr) { + if(!This->elem) { + WARN("NULL elem\n"); + return E_UNEXPECTED; + } + + hres = HTMLDOMAttribute_Create(NULL, This->elem, id, dispex_compat_mode(&This->elem->node.event_target.dispex), attr); + if(FAILED(hres)) + return hres; + } + + IHTMLDOMAttribute_AddRef(&(*attr)->IHTMLDOMAttribute_iface); + if(list_pos) + *list_pos = pos; + return S_OK; +} + /* interface IHTMLAttributeCollection */ static inline HTMLAttributeCollection *impl_from_IHTMLAttributeCollection(IHTMLAttributeCollection *iface) { @@ -7672,94 +7760,6 @@ static HRESULT WINAPI HTMLAttributeCollection_Invoke(IHTMLAttributeCollection *i wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); }
-static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, DISPID *dispid) -{ - IDispatchEx *dispex = &This->elem->node.event_target.dispex.IDispatchEx_iface; - DISPID id = DISPID_STARTENUM; - LONG len = -1; - HRESULT hres; - - FIXME("filter non-enumerable attributes out\n"); - - while(1) { - hres = IDispatchEx_GetNextDispID(dispex, fdexEnumAll, id, &id); - if(FAILED(hres)) - return hres; - else if(hres == S_FALSE) - break; - - len++; - if(len == *idx) - break; - } - - if(dispid) { - *dispid = id; - return *idx==len ? S_OK : DISP_E_UNKNOWNNAME; - } - - *idx = len+1; - return S_OK; -} - -static inline HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, BSTR name, DISPID *id) -{ - HRESULT hres; - - if(name[0]>='0' && name[0]<='9') { - WCHAR *end_ptr; - LONG idx; - - idx = wcstoul(name, &end_ptr, 10); - if(!*end_ptr) { - hres = get_attr_dispid_by_idx(This, &idx, id); - if(SUCCEEDED(hres)) - return hres; - } - } - - if(!This->elem) { - WARN("NULL elem\n"); - return E_UNEXPECTED; - } - - hres = IDispatchEx_GetDispID(&This->elem->node.event_target.dispex.IDispatchEx_iface, - name, fdexNameCaseInsensitive, id); - return hres; -} - -static inline HRESULT get_domattr(HTMLAttributeCollection *This, DISPID id, LONG *list_pos, HTMLDOMAttribute **attr) -{ - HTMLDOMAttribute *iter; - LONG pos = 0; - HRESULT hres; - - *attr = NULL; - LIST_FOR_EACH_ENTRY(iter, &This->attrs, HTMLDOMAttribute, entry) { - if(iter->dispid == id) { - *attr = iter; - break; - } - pos++; - } - - if(!*attr) { - if(!This->elem) { - WARN("NULL elem\n"); - return E_UNEXPECTED; - } - - hres = HTMLDOMAttribute_Create(NULL, This->elem, id, dispex_compat_mode(&This->elem->node.event_target.dispex), attr); - if(FAILED(hres)) - return hres; - } - - IHTMLDOMAttribute_AddRef(&(*attr)->IHTMLDOMAttribute_iface); - if(list_pos) - *list_pos = pos; - return S_OK; -} - static HRESULT WINAPI HTMLAttributeCollection_get_length(IHTMLAttributeCollection *iface, LONG *p) { HTMLAttributeCollection *This = impl_from_IHTMLAttributeCollection(iface);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Cache the current iterator's DISPID and obtain the next one relatively from it to avoid excessively iterating through them from the beginning, on every single element obtained.
dlls/mshtml/htmlelem.c | 183 +++++++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/dom.c | 133 ++++++++++++++++++----------- 2 files changed, 265 insertions(+), 51 deletions(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index ea5c65b..d911d0a 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -7578,10 +7578,10 @@ static HRESULT create_filters_collection(compat_mode_t compat_mode, IHTMLFilters return S_OK; }
-static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, DISPID *dispid) +static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LONG *idx, DISPID start, DISPID *dispid) { IDispatchEx *dispex = &This->elem->node.event_target.dispex.IDispatchEx_iface; - DISPID id = DISPID_STARTENUM; + DISPID id = start; LONG len = -1; HRESULT hres;
@@ -7608,6 +7608,11 @@ static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, return S_OK; }
+static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, DISPID *dispid) +{ + return get_attr_dispid_by_relative_idx(This, idx, DISPID_STARTENUM, dispid); +} + static inline HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, BSTR name, DISPID *id) { HRESULT hres; @@ -7666,6 +7671,160 @@ static inline HRESULT get_domattr(HTMLAttributeCollection *This, DISPID id, LONG return S_OK; }
+typedef struct { + IEnumVARIANT IEnumVARIANT_iface; + + LONG ref; + + ULONG iter; + DISPID iter_dispid; + HTMLAttributeCollection *col; +} HTMLAttributeCollectionEnum; + +static inline HTMLAttributeCollectionEnum *HTMLAttributeCollectionEnum_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, HTMLAttributeCollectionEnum, IEnumVARIANT_iface); +} + +static HRESULT WINAPI HTMLAttributeCollectionEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + *ppv = &This->IEnumVARIANT_iface; + }else if(IsEqualGUID(riid, &IID_IEnumVARIANT)) { + *ppv = &This->IEnumVARIANT_iface; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI HTMLAttributeCollectionEnum_AddRef(IEnumVARIANT *iface) +{ + HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI HTMLAttributeCollectionEnum_Release(IEnumVARIANT *iface) +{ + HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if(!ref) { + IHTMLAttributeCollection_Release(&This->col->IHTMLAttributeCollection_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI HTMLAttributeCollectionEnum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + DISPID tmp, dispid = This->iter_dispid; + HTMLDOMAttribute *attr; + LONG rel_index = 0; + HRESULT hres; + ULONG i; + + TRACE("(%p)->(%lu %p %p)\n", This, celt, rgVar, pCeltFetched); + + for(i = 0; i < celt; i++) { + hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, dispid, &tmp); + if(SUCCEEDED(hres)) { + dispid = tmp; + hres = get_domattr(This->col, dispid, NULL, &attr); + } + else if(hres == DISP_E_UNKNOWNNAME) + break; + + if(FAILED(hres)) { + while(i--) + VariantClear(&rgVar[i]); + return hres; + } + + V_VT(&rgVar[i]) = VT_DISPATCH; + V_DISPATCH(&rgVar[i]) = (IDispatch*)&attr->IHTMLDOMAttribute_iface; + } + + This->iter += i; + This->iter_dispid = dispid; + if(pCeltFetched) + *pCeltFetched = i; + return i == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI HTMLAttributeCollectionEnum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + LONG remaining, rel_index; + DISPID dispid; + HRESULT hres; + + TRACE("(%p)->(%lu)\n", This, celt); + + if(!celt) + return S_OK; + + rel_index = -1; + hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, This->iter_dispid, NULL); + if(FAILED(hres)) + return hres; + remaining = min(celt, rel_index); + + if(remaining) { + rel_index = remaining - 1; + hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, This->iter_dispid, &dispid); + if(FAILED(hres)) + return hres; + This->iter += remaining; + This->iter_dispid = dispid; + } + return celt > remaining ? S_FALSE : S_OK; +} + +static HRESULT WINAPI HTMLAttributeCollectionEnum_Reset(IEnumVARIANT *iface) +{ + HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + + TRACE("(%p)->()\n", This); + + This->iter = 0; + This->iter_dispid = DISPID_STARTENUM; + return S_OK; +} + +static HRESULT WINAPI HTMLAttributeCollectionEnum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + FIXME("(%p)->(%p)\n", This, ppEnum); + return E_NOTIMPL; +} + +static const IEnumVARIANTVtbl HTMLAttributeCollectionEnumVtbl = { + HTMLAttributeCollectionEnum_QueryInterface, + HTMLAttributeCollectionEnum_AddRef, + HTMLAttributeCollectionEnum_Release, + HTMLAttributeCollectionEnum_Next, + HTMLAttributeCollectionEnum_Skip, + HTMLAttributeCollectionEnum_Reset, + HTMLAttributeCollectionEnum_Clone +}; + /* interface IHTMLAttributeCollection */ static inline HTMLAttributeCollection *impl_from_IHTMLAttributeCollection(IHTMLAttributeCollection *iface) { @@ -7775,8 +7934,24 @@ static HRESULT WINAPI HTMLAttributeCollection_get_length(IHTMLAttributeCollectio static HRESULT WINAPI HTMLAttributeCollection__newEnum(IHTMLAttributeCollection *iface, IUnknown **p) { HTMLAttributeCollection *This = impl_from_IHTMLAttributeCollection(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + HTMLAttributeCollectionEnum *ret; + + TRACE("(%p)->(%p)\n", This, p); + + ret = heap_alloc(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IEnumVARIANT_iface.lpVtbl = &HTMLAttributeCollectionEnumVtbl; + ret->ref = 1; + ret->iter = 0; + ret->iter_dispid = DISPID_STARTENUM; + + HTMLAttributeCollection_AddRef(&This->IHTMLAttributeCollection_iface); + ret->col = This; + + *p = (IUnknown*)&ret->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI HTMLAttributeCollection_item(IHTMLAttributeCollection *iface, VARIANT *name, IDispatch **ppItem) diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 0f5120a..1b1e677 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -3636,6 +3636,61 @@ static void _test_attr_parent(unsigned line, IHTMLDOMAttribute *attr) IHTMLDOMAttribute2_Release(attr2); }
+static LONG test_attr_collection_attr(IDispatch *attr, LONG i) +{ + IHTMLDOMAttribute *dom_attr; + LONG ret = 1; + HRESULT hres; + VARIANT val; + BSTR name; + + hres = IDispatch_QueryInterface(attr, &IID_IHTMLDOMAttribute, (void**)&dom_attr); + ok(hres == S_OK, "%ld) QueryInterface failed: %08lx\n", i, hres); + + hres = IHTMLDOMAttribute_get_nodeName(dom_attr, &name); + ok(hres == S_OK, "%ld) get_nodeName failed: %08lx\n", i, hres); + + if(!lstrcmpW(name, L"id")) { + hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); + ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); + ok(V_VT(&val) == VT_BSTR, "id: V_VT(&val) = %d\n", V_VT(&val)); + ok(!lstrcmpW(V_BSTR(&val), L"attr"), "id: V_BSTR(&val) = %s\n", wine_dbgstr_w(V_BSTR(&val))); + test_attr_expando(dom_attr, VARIANT_FALSE); + test_attr_value(dom_attr, L"attr"); + } else if(!lstrcmpW(name, L"attr1")) { + hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); + ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); + ok(V_VT(&val) == VT_BSTR, "attr1: V_VT(&val) = %d\n", V_VT(&val)); + ok(!lstrcmpW(V_BSTR(&val), L"attr1"), "attr1: V_BSTR(&val) = %s\n", wine_dbgstr_w(V_BSTR(&val))); + test_attr_expando(dom_attr, VARIANT_TRUE); + test_attr_value(dom_attr, L"attr1"); + } else if(!lstrcmpW(name, L"attr2")) { + hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); + ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); + ok(V_VT(&val) == VT_BSTR, "attr2: V_VT(&val) = %d\n", V_VT(&val)); + ok(!V_BSTR(&val), "attr2: V_BSTR(&val) != NULL\n"); + test_attr_value(dom_attr, L""); + } else if(!lstrcmpW(name, L"attr3")) { + hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); + ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); + ok(V_VT(&val) == VT_BSTR, "attr3: V_VT(&val) = %d\n", V_VT(&val)); + ok(!lstrcmpW(V_BSTR(&val), L"attr3"), "attr3: V_BSTR(&val) = %s\n", wine_dbgstr_w(V_BSTR(&val))); + test_attr_value(dom_attr, L"attr3"); + } else if(!lstrcmpW(name, L"test")) { + hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); + ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); + ok(V_VT(&val) == VT_I4, "test: V_VT(&val) = %d\n", V_VT(&val)); + ok(V_I4(&val) == 1, "test: V_I4(&val) = %ld\n", V_I4(&val)); + test_attr_value(dom_attr, L"1"); + } else + ret = 0; + + IHTMLDOMAttribute_Release(dom_attr); + SysFreeString(name); + VariantClear(&val); + return ret; +} + static void test_attr_collection_disp(IDispatch *disp) { IDispatchEx *dispex; @@ -3689,11 +3744,13 @@ static void test_attr_collection(IHTMLElement *elem)
IHTMLDOMNode *node; IDispatch *disp, *attr; - IHTMLDOMAttribute *dom_attr; IHTMLAttributeCollection *attr_col; BSTR name = SysAllocString(testW); + IEnumVARIANT *enum_var; + IUnknown *enum_unk; VARIANT id, val; LONG i, len, checked; + ULONG fetched; HRESULT hres;
hres = IHTMLElement_QueryInterface(elem, &IID_IHTMLDOMNode, (void**)&node); @@ -3724,6 +3781,13 @@ static void test_attr_collection(IHTMLElement *elem) ok(hres == S_OK, "get_length failed: %08lx\n", hres); ok(len == i+1, "get_length returned %ld, expected %ld\n", len, i+1);
+ hres = IHTMLAttributeCollection_get__newEnum(attr_col, &enum_unk); + ok(hres == S_OK, "_newEnum failed: %08lx\n", hres); + + hres = IUnknown_QueryInterface(enum_unk, &IID_IEnumVARIANT, (void**)&enum_var); + IUnknown_Release(enum_unk); + ok(hres == S_OK, "Could not get IEnumVARIANT iface: %08lx\n", hres); + checked = 0; for(i=0; i<len; i++) { V_VT(&id) = VT_I4; @@ -3731,58 +3795,33 @@ static void test_attr_collection(IHTMLElement *elem) hres = IHTMLAttributeCollection_item(attr_col, &id, &attr); ok(hres == S_OK, "%ld) item failed: %08lx\n", i, hres);
- hres = IDispatch_QueryInterface(attr, &IID_IHTMLDOMAttribute, (void**)&dom_attr); - ok(hres == S_OK, "%ld) QueryInterface failed: %08lx\n", i, hres); + checked += test_attr_collection_attr(attr, i); IDispatch_Release(attr); + } + ok(checked==5, "invalid number of specified attributes (%ld)\n", checked);
- hres = IHTMLDOMAttribute_get_nodeName(dom_attr, &name); - ok(hres == S_OK, "%ld) get_nodeName failed: %08lx\n", i, hres); - - if(!lstrcmpW(name, L"id")) { - checked++; - hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); - ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); - ok(V_VT(&val) == VT_BSTR, "id: V_VT(&val) = %d\n", V_VT(&val)); - ok(!lstrcmpW(V_BSTR(&val), L"attr"), "id: V_BSTR(&val) = %s\n", wine_dbgstr_w(V_BSTR(&val))); - test_attr_expando(dom_attr, VARIANT_FALSE); - test_attr_value(dom_attr, L"attr"); - } else if(!lstrcmpW(name, L"attr1")) { - checked++; - hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); - ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); - ok(V_VT(&val) == VT_BSTR, "attr1: V_VT(&val) = %d\n", V_VT(&val)); - ok(!lstrcmpW(V_BSTR(&val), L"attr1"), "attr1: V_BSTR(&val) = %s\n", wine_dbgstr_w(V_BSTR(&val))); - test_attr_expando(dom_attr, VARIANT_TRUE); - test_attr_value(dom_attr, L"attr1"); - } else if(!lstrcmpW(name, L"attr2")) { - checked++; - hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); - ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); - ok(V_VT(&val) == VT_BSTR, "attr2: V_VT(&val) = %d\n", V_VT(&val)); - ok(!V_BSTR(&val), "attr2: V_BSTR(&val) != NULL\n"); - test_attr_value(dom_attr, L""); - } else if(!lstrcmpW(name, L"attr3")) { - checked++; - hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); - ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); - ok(V_VT(&val) == VT_BSTR, "attr3: V_VT(&val) = %d\n", V_VT(&val)); - ok(!lstrcmpW(V_BSTR(&val), L"attr3"), "attr3: V_BSTR(&val) = %s\n", wine_dbgstr_w(V_BSTR(&val))); - test_attr_value(dom_attr, L"attr3"); - } else if(!lstrcmpW(name, L"test")) { - checked++; - hres = IHTMLDOMAttribute_get_nodeValue(dom_attr, &val); - ok(hres == S_OK, "%ld) get_nodeValue failed: %08lx\n", i, hres); - ok(V_VT(&val) == VT_I4, "test: V_VT(&val) = %d\n", V_VT(&val)); - ok(V_I4(&val) == 1, "test: V_I4(&val) = %ld\n", V_I4(&val)); - test_attr_value(dom_attr, L"1"); - } + checked = 0; + for(i=0; i<len; i++) { + fetched = 0; + V_VT(&val) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &val, &fetched); + ok(hres == S_OK, "Next failed: %08lx\n", hres); + ok(fetched == 1, "fetched = %lu\n", fetched); + ok(V_VT(&val) == VT_DISPATCH, "V_VT(val) = %d\n", V_VT(&val)); + ok(V_DISPATCH(&val) != NULL, "V_DISPATCH(&val) == NULL\n");
- IHTMLDOMAttribute_Release(dom_attr); - SysFreeString(name); - VariantClear(&val); + checked += test_attr_collection_attr(V_DISPATCH(&val), i); + IDispatch_Release(V_DISPATCH(&val)); } ok(checked==5, "invalid number of specified attributes (%ld)\n", checked);
+ fetched = 0; + V_VT(&val) = VT_ERROR; + hres = IEnumVARIANT_Next(enum_var, 1, &val, &fetched); + ok(hres == S_FALSE, "Next failed: %08lx\n", hres); + ok(fetched == 0, "fetched = %lu\n", fetched); + IEnumVARIANT_Release(enum_var); + V_I4(&id) = len; hres = IHTMLAttributeCollection_item(attr_col, &id, &attr); ok(hres == E_INVALIDARG, "item failed: %08lx\n", hres);