jscript: Implement fdexNameCaseInsensitive flag handling.

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>
This commit is contained in:
Gabriel Ivăncescu 2022-08-16 21:02:06 +03:00 committed by Alexandre Julliard
parent 015491ab32
commit 7be0cffa06
4 changed files with 285 additions and 43 deletions

View file

@ -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;

View file

@ -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();

View file

@ -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();

View file

@ -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;