Despite common sense, native doesn't seem to look for exact match first; it simply case-insensitively compares the props and returns as soon as it finds one. This is also reliant on implementation details in case the object has multiple props with same case-insensitive names, e.g. an object having `Foo` prop, with `foo` prop on its prototype, can still find `Foo` even if you look up `foo` instead (which matches exactly on the prototype). Which is not always reliable, sometimes it finds the prototype first.
See the following diff to illustrate possible behavior on native (which is what I get). Note that especially for jscript, the behavior makes no sense logically; first it picks 'Foo', despite 'foo' being available on the prototype (i.e. exact match). You might think it just looks up on props first, and prototype later, but the second ok() added shows that it finds the prototype's prop first, instead!
```diff diff --git a/dlls/jscript/tests/jscript.c b/dlls/jscript/tests/jscript.c index 03774da..2428363 100644 --- a/dlls/jscript/tests/jscript.c +++ b/dlls/jscript/tests/jscript.c @@ -999,6 +999,7 @@ static void test_case_sens(void) /* Native picks one "arbitrarily" here, depending how it's laid out, so can't compare exact id */ hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + ok(id != id2, "id == id2\n");
hr = IDispatchEx_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id2); ok(hr == S_OK, "GetIDsOfNames failed: %08lx\n", hr); @@ -1031,6 +1032,7 @@ static void test_case_sens(void)
hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + ok(id == id2, "id != id2\n"); SysFreeString(bstr);
IDispatchEx_Release(disp); diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index 14e3a14..563b87a 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -241,6 +241,7 @@ static void test_script_vars(unsigned argc, VARIANTARG *argv) /* Native picks one "arbitrarily" here, depending how it's laid out, so can't compare exact id */ hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + ok(id != id2, "id == id2\n");
hres = IDispatchEx_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id2); ok(hres == S_OK, "GetIDsOfNames failed: %08lx\n", hres); @@ -273,6 +274,7 @@ static void test_script_vars(unsigned argc, VARIANTARG *argv)
hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + ok(id != id2, "id == id2\n"); SysFreeString(bstr);
IDispatchEx_Release(disp); ```
Strangely, mshtml's jscript still finds the prop on the object the second time, but that could be to different underlying implementation. I don't think it's necessary to replicate this quirk.
The main point of this case insensitive handling is that it will be needed later when IE9+ modes use jscript proxy objects for the mshtml objects, because those default to case-insensitive search in GetIDsOfNames, likely to preserve backwards compatibility with pre-IE9 modes. It's a weird quirk, I know, but we have tests for that, which would otherwise break when proxies get added. I'll also add more tests around it later, to prove this, of course, that it also applies to jscript props (e.g. hasOwnProperty on the prototype is still looked case-insensitively if GetIDsOfNames is used).
-- v2: jscript: Implement fdexNameCaseInsensitive flag handling.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Despite common sense, native doesn't seem to look for exact match first; it simply case-insensitively compares the props and returns as soon as it finds one. This is also reliant on implementation details in case the object has multiple props with same case-insensitive names, e.g. an object having `Foo` prop, with `foo` prop on its prototype, can still find `Foo` even if you look up `foo` instead (which matches exactly on the prototype). Which is not always reliable, sometimes it finds the prototype first.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 94 ++++++++++++++------------- dlls/jscript/tests/jscript.c | 119 +++++++++++++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 12 ++++ dlls/mshtml/tests/script.c | 103 ++++++++++++++++++++++++++++++ 4 files changed, 285 insertions(+), 43 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 298f7e1af89..e25378e866b 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -128,28 +128,24 @@ static DWORD get_flags(jsdisp_t *This, dispex_prop_t *prop) return prop->flags; }
-static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name) +static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name, BOOL case_insens) { - int min = 0, max, i, r; + int min = 0, max = This->builtin_info->props_cnt-1, i, r; + unsigned version; + + if(case_insens) { + for(i = min; i <= max; i++) + if(!wcsicmp(name, This->builtin_info->props[i].name)) + goto found; + return NULL; + }
- max = This->builtin_info->props_cnt-1; while(min <= max) { i = (min+max)/2;
r = wcscmp(name, This->builtin_info->props[i].name); - if(!r) { - /* Skip prop if it's available only in higher compatibility mode. */ - unsigned version = (This->builtin_info->props[i].flags & PROPF_VERSION_MASK) - >> PROPF_VERSION_SHIFT; - if(version && version > This->ctx->version) - return NULL; - - /* Skip prop if it's available only in HTML mode and we're not running in HTML mode. */ - if((This->builtin_info->props[i].flags & PROPF_HTML) && !This->ctx->html_mode) - return NULL; - - return This->builtin_info->props + i; - } + if(!r) + goto found;
if(r < 0) max = i-1; @@ -158,6 +154,18 @@ static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name }
return NULL; + +found: + /* Skip prop if it's available only in higher compatibility mode. */ + version = (This->builtin_info->props[i].flags & PROPF_VERSION_MASK) >> PROPF_VERSION_SHIFT; + if(version && version > This->ctx->version) + return NULL; + + /* Skip prop if it's available only in HTML mode and we're not running in HTML mode. */ + if((This->builtin_info->props[i].flags & PROPF_HTML) && !This->ctx->html_mode) + return NULL; + + return This->builtin_info->props + i; }
static inline unsigned string_hash(const WCHAR *name) @@ -237,7 +245,7 @@ static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref return ret; }
-static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret) +static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret) { const builtin_prop_t *builtin; unsigned bucket, pos, prev = ~0; @@ -247,7 +255,7 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, bucket = get_props_idx(This, hash); pos = This->props[bucket].bucket_head; while(pos != ~0) { - if(!wcscmp(name, This->props[pos].name)) { + if(case_insens ? !wcsicmp(name, This->props[pos].name) : !wcscmp(name, This->props[pos].name)) { if(prev != ~0) { This->props[prev].bucket_next = This->props[pos].bucket_next; This->props[pos].bucket_next = This->props[bucket].bucket_head; @@ -262,7 +270,7 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, pos = This->props[pos].bucket_next; }
- builtin = find_builtin_prop(This, name); + builtin = find_builtin_prop(This, name, case_insens); if(builtin) { unsigned flags = builtin->flags; if(flags & PROPF_METHOD) { @@ -272,7 +280,7 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, if(FAILED(hres)) return hres;
- prop = alloc_prop(This, name, PROP_JSVAL, (flags & PROPF_ALL) | PROPF_WRITABLE | PROPF_CONFIGURABLE); + prop = alloc_prop(This, builtin->name, PROP_JSVAL, (flags & PROPF_ALL) | PROPF_WRITABLE | PROPF_CONFIGURABLE); if(!prop) { jsdisp_release(obj); return E_OUTOFMEMORY; @@ -285,7 +293,7 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, }else if(builtin->setter) flags |= PROPF_WRITABLE; flags &= PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE; - prop = alloc_prop(This, name, PROP_BUILTIN, flags); + prop = alloc_prop(This, builtin->name, PROP_BUILTIN, flags); if(!prop) return E_OUTOFMEMORY;
@@ -318,12 +326,12 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, return S_OK; }
-static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret) +static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret) { dispex_prop_t *prop, *del=NULL; HRESULT hres;
- hres = find_prop_name(This, hash, name, &prop); + hres = find_prop_name(This, hash, name, case_insens, &prop); if(FAILED(hres)) return hres; if(prop && prop->type==PROP_DELETED) { @@ -335,7 +343,7 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n }
if(This->prototype) { - hres = find_prop_name_prot(This->prototype, hash, name, &prop); + hres = find_prop_name_prot(This->prototype, hash, name, case_insens, &prop); if(FAILED(hres)) return hres; if(prop && prop->type != PROP_DELETED) { @@ -358,12 +366,12 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n return S_OK; }
-static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, dispex_prop_t **ret) +static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, BOOL case_insens, dispex_prop_t **ret) { dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(This, string_hash(name), name, &prop); + hres = find_prop_name_prot(This, string_hash(name), name, case_insens, &prop); if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) { TRACE("creating prop %s flags %lx\n", debugstr_w(name), create_flags);
@@ -613,7 +621,7 @@ static HRESULT fill_protrefs(jsdisp_t *This) fill_protrefs(This->prototype);
for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) { - hres = find_prop_name(This, iter->hash, iter->name, &prop); + hres = find_prop_name(This, iter->hash, iter->name, FALSE, &prop); if(FAILED(hres)) return hres; if(!prop || prop->type==PROP_DELETED) { @@ -1532,7 +1540,7 @@ static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DW
TRACE("(%p)->(%s %lx %p)\n", This, debugstr_w(bstrName), grfdex, pid);
- if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) { + if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) { FIXME("Unsupported grfdex %lx\n", grfdex); return E_NOTIMPL; } @@ -1678,10 +1686,10 @@ static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bst
TRACE("(%p)->(%s %lx)\n", This, debugstr_w(bstrName), grfdex);
- if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) + if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) FIXME("Unsupported grfdex %lx\n", grfdex);
- hres = find_prop_name(This, string_hash(bstrName), bstrName, &prop); + hres = find_prop_name(This, string_hash(bstrName), bstrName, grfdex & fdexNameCaseInsensitive, &prop); if(FAILED(hres)) return hres; if(!prop) { @@ -1902,7 +1910,7 @@ HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const built dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", &prop); + hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", FALSE, &prop); if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) { jsval_t val;
@@ -1941,9 +1949,9 @@ HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *
if(jsdisp->extensible && (flags & fdexNameEnsure)) hres = ensure_prop_name(jsdisp, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, - &prop); + flags & fdexNameCaseInsensitive, &prop); else - hres = find_prop_name_prot(jsdisp, string_hash(name), name, &prop); + hres = find_prop_name_prot(jsdisp, string_hash(name), name, flags & fdexNameCaseInsensitive, &prop); if(FAILED(hres)) return hres;
@@ -1996,7 +2004,7 @@ HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(disp, string_hash(name), name, &prop); + hres = find_prop_name_prot(disp, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres;
@@ -2227,9 +2235,9 @@ HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, BOOL throw HRESULT hres;
if(obj->extensible) - hres = ensure_prop_name(obj, name, flags, &prop); + hres = ensure_prop_name(obj, name, flags, FALSE, &prop); else - hres = find_prop_name(obj, string_hash(name), name, &prop); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres; if(!prop || (prop->type == PROP_DELETED && !obj->extensible)) @@ -2330,7 +2338,7 @@ HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val) dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(obj, string_hash(name), name, &prop); + hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres;
@@ -2350,7 +2358,7 @@ HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r)
swprintf(name, ARRAY_SIZE(name), L"%d", idx);
- hres = find_prop_name_prot(obj, string_hash(name), name, &prop); + hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres;
@@ -2407,7 +2415,7 @@ HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
- hres = find_prop_name(obj, string_hash(buf), buf, &prop); + hres = find_prop_name(obj, string_hash(buf), buf, FALSE, &prop); if(FAILED(hres) || !prop) return hres;
@@ -2465,7 +2473,7 @@ HRESULT jsdisp_next_prop(jsdisp_t *obj, DISPID id, enum jsdisp_enum_type enum_ty
for(i = 0; i < len; i++) { swprintf(name, ARRAY_SIZE(name), L"%d", i); - hres = find_prop_name(obj, string_hash(name), name, &iter); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &iter); if(FAILED(hres)) return hres; } @@ -2514,7 +2522,7 @@ HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL return E_OUTOFMEMORY; }
- hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop); + hres = find_prop_name(jsdisp, string_hash(ptr), ptr, FALSE, &prop); if(prop) { hres = delete_prop(prop, ret); }else { @@ -2561,7 +2569,7 @@ HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_onl dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name(obj, string_hash(name), name, &prop); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres;
@@ -2605,7 +2613,7 @@ HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name(obj, string_hash(name), name, &prop); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres;
diff --git a/dlls/jscript/tests/jscript.c b/dlls/jscript/tests/jscript.c index 872354161fc..03774dac4f3 100644 --- a/dlls/jscript/tests/jscript.c +++ b/dlls/jscript/tests/jscript.c @@ -930,6 +930,124 @@ static void test_aggregation(void) ok(!unk || broken(unk != NULL), "unk = %p\n", unk); }
+static void test_case_sens(void) +{ + static const WCHAR *const names[] = { L"abc", L"foo", L"bar", L"mAth", L"evaL" }; + DISPPARAMS dp = { NULL, NULL, 0, 0 }; + IActiveScriptParse *parser; + IActiveScript *script; + EXCEPINFO ei = { 0 }; + IDispatchEx *disp; + DISPID id, id2; + unsigned i; + HRESULT hr; + VARIANT v; + BSTR bstr; + + script = create_jscript(); + + hr = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parser); + ok(hr == S_OK, "Could not get IActiveScriptParse iface: %08lx\n", hr); + + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08lx\n", hr); + CHECK_CALLED(GetLCID); + + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScriptParse_InitNew(parser); + ok(hr == S_OK, "InitNew failed: %08lx\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hr = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08lx\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + + parse_script(parser, L"var aBc; var abC; function Foo() { }\nFoo.prototype.foo = 13; var Bar = new Foo(); Bar.Foo = 42;"); + disp = get_script_dispatch(script, NULL); + + for(i = 0; i < ARRAY_SIZE(names); i++) { + bstr = SysAllocString(names[i]); + hr = IDispatchEx_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id); + ok(hr == DISP_E_UNKNOWNNAME, "GetIDsOfNames(%s) returned %08lx, expected %08lx\n", debugstr_w(bstr), hr, DISP_E_UNKNOWNNAME); + + hr = IDispatchEx_GetDispID(disp, bstr, 0, &id); + ok(hr == DISP_E_UNKNOWNNAME, "GetDispID(%s) returned %08lx, expected %08lx\n", debugstr_w(bstr), hr, DISP_E_UNKNOWNNAME); + + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id); + ok(hr == S_OK, "GetDispID(%s) with fdexNameCaseInsensitive failed: %08lx\n", debugstr_w(bstr), hr); + ok(id > 0, "Unexpected DISPID for %s: %ld\n", debugstr_w(bstr), id); + SysFreeString(bstr); + } + + get_disp_id(disp, L"Bar", S_OK, &id); + hr = IDispatchEx_InvokeEx(disp, id, 0, DISPATCH_PROPERTYGET, &dp, &v, &ei, NULL); + ok(hr == S_OK, "InvokeEx failed: %08lx\n", hr); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(v) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(v) = NULL\n"); + IDispatchEx_Release(disp); + + hr = IDispatch_QueryInterface(V_DISPATCH(&v), &IID_IDispatchEx, (void**)&disp); + ok(hr == S_OK, "Could not get IDispatchEx iface: %08lx\n", hr); + VariantClear(&v); + + bstr = SysAllocString(L"foo"); + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseSensitive, &id); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + + /* Native picks one "arbitrarily" here, depending how it's laid out, so can't compare exact id */ + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + + hr = IDispatchEx_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id2); + ok(hr == S_OK, "GetIDsOfNames failed: %08lx\n", hr); + ok(id == id2, "id != id2\n"); + + hr = IDispatchEx_DeleteMemberByName(disp, bstr, fdexNameCaseInsensitive); + ok(hr == S_OK, "DeleteMemberByName failed: %08lx\n", hr); + + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + ok(id == id2, "id != id2\n"); + + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + ok(id == id2, "id != id2\n"); + SysFreeString(bstr); + + bstr = SysAllocString(L"fOo"); + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + ok(id == id2, "id != id2\n"); + + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + ok(id == id2, "id != id2\n"); + + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameEnsure, &id2); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + ok(id != id2, "id == id2\n"); + + hr = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); + ok(hr == S_OK, "GetDispID failed: %08lx\n", hr); + SysFreeString(bstr); + + IDispatchEx_Release(disp); + IActiveScriptParse_Release(parser); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hr = IActiveScript_Close(script); + ok(hr == S_OK, "Close failed: %08lx\n", hr); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScript_Release(script); +} + static void test_param_ids(void) { static const WCHAR *const names1[] = { L"test", L"c", L"foo", L"b", L"a" }; @@ -2324,6 +2442,7 @@ START_TEST(jscript) test_jscript2(); test_jscript_uninitializing(); test_aggregation(); + test_case_sens(); test_param_ids(); test_code_persistence(); test_named_items(); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index c18639582d0..bfaacd1bd5b 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -33,6 +33,18 @@ var JS_E_WRONG_THIS = 0x800a13fc;
var tests = [];
+sync_test("script vars", function() { + function foo() { } + foo.prototype.foo = 13; + var obj = new foo(); + obj.Foo = 42; + obj.aBc = 1; + obj.abC = 2; + obj.Bar = 3; + document.body.foobar = 42; + external.testVars(document.body, obj); +}); + sync_test("date_now", function() { var now = Date.now(); var time = (new Date()).getTime(); diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index 660f62759c9..14e3a148239 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -155,6 +155,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_NULL_DISP 0x300008 #define DISPID_EXTERNAL_IS_ENGLISH 0x300009 #define DISPID_EXTERNAL_LIST_SEP 0x30000A +#define DISPID_EXTERNAL_TEST_VARS 0x30000B
static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; @@ -198,6 +199,95 @@ static BOOL init_key(const char *key_name, const char *def_value, BOOL init) return res == ERROR_SUCCESS; }
+static void test_script_vars(unsigned argc, VARIANTARG *argv) +{ + static const WCHAR *const jsobj_names[] = { L"abc", L"foO", L"bar", L"TostRing", L"hasownpropERty" }; + IHTMLBodyElement *body; + IDispatchEx *disp; + DISPID id, id2; + HRESULT hres; + unsigned i; + BSTR bstr; + + ok(argc == 2, "argc = %d\n", argc); + ok(V_VT(&argv[0]) == VT_DISPATCH, "VT = %d\n", V_VT(&argv[0])); + ok(V_VT(&argv[1]) == VT_DISPATCH, "VT = %d\n", V_VT(&argv[1])); + + /* JS object disp */ + hres = IDispatch_QueryInterface(V_DISPATCH(&argv[0]), &IID_IDispatchEx, (void**)&disp); + ok(hres == S_OK, "Could not get IDispatchEx iface: %08lx\n", hres); + + hres = IDispatchEx_QueryInterface(disp, &IID_IHTMLBodyElement, (void**)&body); + ok(hres == E_NOINTERFACE, "Got IHTMLBodyElement iface on JS object? %08lx\n", hres); + + for(i = 0; i < ARRAY_SIZE(jsobj_names); i++) { + bstr = SysAllocString(jsobj_names[i]); + hres = IDispatchEx_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id); + ok(hres == DISP_E_UNKNOWNNAME, "GetIDsOfNames(%s) returned %08lx, expected %08lx\n", debugstr_w(bstr), hres, DISP_E_UNKNOWNNAME); + + hres = IDispatchEx_GetDispID(disp, bstr, 0, &id); + ok(hres == DISP_E_UNKNOWNNAME, "GetDispID(%s) returned %08lx, expected %08lx\n", debugstr_w(bstr), hres, DISP_E_UNKNOWNNAME); + + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id); + ok(hres == S_OK, "GetDispID(%s) with fdexNameCaseInsensitive failed: %08lx\n", debugstr_w(bstr), hres); + ok(id > 0, "Unexpected DISPID for %s: %ld\n", debugstr_w(bstr), id); + SysFreeString(bstr); + } + + bstr = SysAllocString(L"foo"); + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseSensitive, &id); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + + /* Native picks one "arbitrarily" here, depending how it's laid out, so can't compare exact id */ + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + + hres = IDispatchEx_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id2); + ok(hres == S_OK, "GetIDsOfNames failed: %08lx\n", hres); + ok(id == id2, "id != id2\n"); + + hres = IDispatchEx_DeleteMemberByName(disp, bstr, fdexNameCaseInsensitive); + ok(hres == S_OK, "DeleteMemberByName failed: %08lx\n", hres); + + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + ok(id == id2, "id != id2\n"); + + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + ok(id == id2, "id != id2\n"); + SysFreeString(bstr); + + bstr = SysAllocString(L"fOo"); + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive, &id2); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + ok(id == id2, "id != id2\n"); + + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + ok(id == id2, "id != id2\n"); + + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameEnsure, &id2); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + ok(id != id2, "id == id2\n"); + + hres = IDispatchEx_GetDispID(disp, bstr, fdexNameCaseInsensitive | fdexNameEnsure, &id2); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + SysFreeString(bstr); + + IDispatchEx_Release(disp); + + /* Body element disp */ + hres = IDispatch_QueryInterface(V_DISPATCH(&argv[1]), &IID_IDispatchEx, (void**)&disp); + ok(hres == S_OK, "Could not get IDispatchEx iface: %08lx\n", hres); + + hres = IDispatchEx_QueryInterface(disp, &IID_IHTMLBodyElement, (void**)&body); + ok(hres == S_OK, "Could not get IHTMLBodyElement iface: %08lx\n", hres); + IHTMLBodyElement_Release(body); + + IDispatchEx_Release(disp); +} + static HRESULT WINAPI PropertyNotifySink_QueryInterface(IPropertyNotifySink *iface, REFIID riid, void**ppv) { @@ -609,6 +699,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_LIST_SEP; return S_OK; } + if(!lstrcmpW(bstrName, L"testVars")) { + *pid = DISPID_EXTERNAL_TEST_VARS; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -833,6 +927,15 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID return S_OK; }
+ case DISPID_EXTERNAL_TEST_VARS: + ok(pdp != NULL, "pdp == NULL\n"); + ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pei != NULL, "pei == NULL\n"); + test_script_vars(pdp->cArgs, pdp->rgvarg); + return S_OK; + default: ok(0, "unexpected call\n"); return E_NOTIMPL;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=121367
Your paranoid android.
=== w10pro64 (64 bit report) ===
mshtml: htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=121366
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/jscript/tests/jscript.c:999 error: patch failed: dlls/mshtml/tests/script.c:241 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/jscript/tests/jscript.c:999 error: patch failed: dlls/mshtml/tests/script.c:241 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/jscript/tests/jscript.c:999 error: patch failed: dlls/mshtml/tests/script.c:241 Task: Patch failed to apply
This merge request was approved by Jacek Caban.