/* * Copyright 2007 Jacek Caban for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #define CONST_VTABLE #include #include #include #include "windef.h" #include "winbase.h" #include "ole2.h" #include "mshtml.h" #include "mshtmcid.h" #include "mshtmhst.h" #include "docobj.h" #include "dispex.h" static const char doc_blank[] = ""; static const char doc_str1[] = "test"; static const char range_test_str[] = "test \nabc\t123
it's\r\n \t
text
"; static const char range_test2_str[] = "abc
123

def"; static const char elem_test_str[] = "test" "text test" "link" "" "" "" "
" "" "" ""; static const char indent_test_str[] = "testabc
123"; static const WCHAR noneW[] = {'N','o','n','e',0}; static WCHAR characterW[] = {'c','h','a','r','a','c','t','e','r',0}; static WCHAR texteditW[] = {'t','e','x','t','e','d','i','t',0}; static WCHAR wordW[] = {'w','o','r','d',0}; static const WCHAR text_javascriptW[] = {'t','e','x','t','/','j','a','v','a','s','c','r','i','p','t',0}; static const WCHAR idW[] = {'i','d',0}; typedef enum { ET_NONE, ET_HTML, ET_HEAD, ET_TITLE, ET_BODY, ET_A, ET_INPUT, ET_SELECT, ET_TEXTAREA, ET_OPTION, ET_STYLE, ET_BLOCKQUOTE, ET_P, ET_BR, ET_TABLE, ET_TBODY, ET_SCRIPT, ET_TEST, ET_COMMENT, ET_IMG } elem_type_t; static REFIID const none_iids[] = { &IID_IUnknown, NULL }; static REFIID const elem_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const body_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLTextContainer, &IID_IHTMLBodyElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const anchor_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLAnchorElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const input_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLInputElement, &IID_IHTMLInputTextElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const select_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLSelectElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const textarea_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLTextAreaElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const option_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLOptionElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const table_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLTable, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const script_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLScriptElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const text_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLDOMTextNode, NULL }; static REFIID const location_iids[] = { &IID_IDispatch, &IID_IHTMLLocation, NULL }; static REFIID const window_iids[] = { &IID_IDispatch, &IID_IHTMLWindow2, &IID_IHTMLWindow3, &IID_IDispatchEx, NULL }; static REFIID const comment_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IHTMLCommentElement, &IID_IDispatchEx, &IID_IConnectionPointContainer, NULL }; static REFIID const img_iids[] = { &IID_IHTMLDOMNode, &IID_IHTMLDOMNode2, &IID_IHTMLElement, &IID_IHTMLElement2, &IID_IDispatchEx, &IID_IHTMLImgElement, &IID_IConnectionPointContainer, NULL }; typedef struct { const char *tag; REFIID *iids; const IID *dispiid; } elem_type_info_t; static const elem_type_info_t elem_type_infos[] = { {"", none_iids, NULL}, {"HTML", elem_iids, NULL}, {"HEAD", elem_iids, NULL}, {"TITLE", elem_iids, NULL}, {"BODY", body_iids, NULL}, {"A", anchor_iids, NULL}, {"INPUT", input_iids, &DIID_DispHTMLInputElement}, {"SELECT", select_iids, NULL}, {"TEXTAREA", textarea_iids, NULL}, {"OPTION", option_iids, &DIID_DispHTMLOptionElement}, {"STYLE", elem_iids, NULL}, {"BLOCKQUOTE",elem_iids, NULL}, {"P", elem_iids, NULL}, {"BR", elem_iids, NULL}, {"TABLE", table_iids, NULL}, {"TBODY", elem_iids, NULL}, {"SCRIPT", script_iids, NULL}, {"TEST", elem_iids, &DIID_DispHTMLUnknownElement}, {"!", comment_iids, &DIID_DispHTMLCommentElement}, {"IMG", img_iids, &DIID_DispHTMLImg} }; static const char *dbgstr_w(LPCWSTR str) { static char buf[512]; if(!str) return "(null)"; WideCharToMultiByte(CP_ACP, 0, str, -1, buf, sizeof(buf), NULL, NULL); return buf; } static const char *dbgstr_guid(REFIID riid) { static char buf[50]; sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", riid->Data1, riid->Data2, riid->Data3, riid->Data4[0], riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4], riid->Data4[5], riid->Data4[6], riid->Data4[7]); return buf; } static int strcmp_wa(LPCWSTR strw, const char *stra) { WCHAR buf[512]; MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR)); return lstrcmpW(strw, buf); } static BSTR a2bstr(const char *str) { BSTR ret; int len; len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); ret = SysAllocStringLen(NULL, len); MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); return ret; } static IHTMLDocument2 *create_document(void) { IHTMLDocument2 *doc; HRESULT hres; hres = CoCreateInstance(&CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, &IID_IHTMLDocument2, (void**)&doc); ok(hres == S_OK, "CoCreateInstance failed: %08x\n", hres); return doc; } #define test_ifaces(i,ids) _test_ifaces(__LINE__,i,ids) static void _test_ifaces(unsigned line, IUnknown *iface, REFIID *iids) { const IID * const *piid; IUnknown *unk; HRESULT hres; for(piid = iids; *piid; piid++) { hres = IDispatch_QueryInterface(iface, *piid, (void**)&unk); ok_(__FILE__,line) (hres == S_OK, "Could not get %s interface: %08x\n", dbgstr_guid(*piid), hres); if(SUCCEEDED(hres)) IUnknown_Release(unk); } } #define test_disp(u,id) _test_disp(__LINE__,u,id) static void _test_disp(unsigned line, IUnknown *unk, const IID *diid) { IDispatchEx *dispex; ITypeInfo *typeinfo; UINT ticnt; HRESULT hres; hres = IUnknown_QueryInterface(unk, &IID_IDispatchEx, (void**)&dispex); ok_(__FILE__,line) (hres == S_OK, "Could not get IDispatch: %08x\n", hres); if(FAILED(hres)) return; ticnt = 0xdeadbeef; hres = IDispatchEx_GetTypeInfoCount(dispex, &ticnt); ok_(__FILE__,line) (hres == S_OK, "GetTypeInfoCount failed: %08x\n", hres); ok_(__FILE__,line) (ticnt == 1, "ticnt=%u\n", ticnt); hres = IDispatchEx_GetTypeInfo(dispex, 0, 0, &typeinfo); ok_(__FILE__,line) (hres == S_OK, "GetTypeInfo failed: %08x\n", hres); if(SUCCEEDED(hres)) { TYPEATTR *type_attr; hres = ITypeInfo_GetTypeAttr(typeinfo, &type_attr); ok_(__FILE__,line) (hres == S_OK, "GetTypeAttr failed: %08x\n", hres); ok_(__FILE__,line) (IsEqualGUID(&type_attr->guid, diid), "unexpected guid %s\n", dbgstr_guid(&type_attr->guid)); ITypeInfo_ReleaseTypeAttr(typeinfo, type_attr); ITypeInfo_Release(typeinfo); } IDispatchEx_Release(dispex); } #define test_node_name(u,n) _test_node_name(__LINE__,u,n) static void _test_node_name(unsigned line, IUnknown *unk, const char *exname) { IHTMLDOMNode *node; BSTR name; HRESULT hres; hres = IUnknown_QueryInterface(unk, &IID_IHTMLDOMNode, (void**)&node); ok_(__FILE__, line) (hres == S_OK, "QueryInterface(IID_IHTMLNode) failed: %08x\n", hres); hres = IHTMLDOMNode_get_nodeName(node, &name); IHTMLDOMNode_Release(node); ok_(__FILE__, line) (hres == S_OK, "get_nodeName failed: %08x\n", hres); ok_(__FILE__, line) (!strcmp_wa(name, exname), "got name: %s, expected %s\n", dbgstr_w(name), exname); SysFreeString(name); } #define test_elem_tag(u,n) _test_elem_tag(__LINE__,u,n) static void _test_elem_tag(unsigned line, IUnknown *unk, const char *extag) { IHTMLElement *elem; BSTR tag; HRESULT hres; hres = IUnknown_QueryInterface(unk, &IID_IHTMLElement, (void**)&elem); ok_(__FILE__, line) (hres == S_OK, "QueryInterface(IID_IHTMLElement) failed: %08x\n", hres); hres = IHTMLElement_get_tagName(elem, &tag); IHTMLElement_Release(elem); ok_(__FILE__, line) (hres == S_OK, "get_tagName failed: %08x\n", hres); ok_(__FILE__, line) (!strcmp_wa(tag, extag), "got tag: %s, expected %s\n", dbgstr_w(tag), extag); SysFreeString(tag); } #define test_elem_type(ifc,t) _test_elem_type(__LINE__,ifc,t) static void _test_elem_type(unsigned line, IUnknown *unk, elem_type_t type) { _test_elem_tag(line, unk, elem_type_infos[type].tag); _test_ifaces(line, unk, elem_type_infos[type].iids); if(elem_type_infos[type].dispiid) _test_disp(line, unk, elem_type_infos[type].dispiid); } #define test_elem_attr(e,n,v) _test_elem_attr(__LINE__,e,n,v) static void _test_elem_attr(unsigned line, IHTMLElement *elem, LPCWSTR name, LPCWSTR exval) { VARIANT value; BSTR tmp; HRESULT hres; VariantInit(&value); tmp = SysAllocString(name); hres = IHTMLElement_getAttribute(elem, tmp, 0, &value); SysFreeString(tmp); ok_(__FILE__,line) (hres == S_OK, "getAttribute failed: %08x\n", hres); if(exval) { ok_(__FILE__,line) (V_VT(&value) == VT_BSTR, "vt=%d\n", V_VT(&value)); ok_(__FILE__,line) (!lstrcmpW(exval, V_BSTR(&value)), "unexpected value %s\n", dbgstr_w(V_BSTR(&value))); }else { ok_(__FILE__,line) (V_VT(&value) == VT_NULL, "vt=%d\n", V_VT(&value)); } VariantClear(&value); } static void test_doc_elem(IHTMLDocument2 *doc) { IHTMLElement *elem; IHTMLDocument3 *doc3; HRESULT hres; hres = IHTMLDocument2_QueryInterface(doc, &IID_IHTMLDocument3, (void**)&doc3); ok(hres == S_OK, "QueryInterface(IID_IHTMLDocument3) failed: %08x\n", hres); hres = IHTMLDocument3_get_documentElement(doc3, &elem); IHTMLDocument3_Release(doc3); ok(hres == S_OK, "get_documentElement failed: %08x\n", hres); test_node_name((IUnknown*)elem, "HTML"); test_elem_tag((IUnknown*)elem, "HTML"); IHTMLElement_Release(elem); } #define get_doc_elem(d) _get_doc_elem(__LINE__,d) static IHTMLElement *_get_doc_elem(unsigned line, IHTMLDocument2 *doc) { IHTMLElement *elem; IHTMLDocument3 *doc3; HRESULT hres; hres = IHTMLDocument2_QueryInterface(doc, &IID_IHTMLDocument3, (void**)&doc3); ok_(__FILE__,line) (hres == S_OK, "Could not get IHTMLDocument3 interface: %08x\n", hres); hres = IHTMLDocument3_get_documentElement(doc3, &elem); ok_(__FILE__,line) (hres == S_OK, "get_documentElement failed: %08x\n", hres); IHTMLDocument3_Release(doc3); return elem; } #define test_option_text(o,t) _test_option_text(__LINE__,o,t) static void _test_option_text(unsigned line, IHTMLOptionElement *option, const char *text) { BSTR bstr; HRESULT hres; hres = IHTMLOptionElement_get_text(option, &bstr); ok_(__FILE__,line) (hres == S_OK, "get_text failed: %08x\n", hres); ok_(__FILE__,line) (!strcmp_wa(bstr, text), "text=%s\n", dbgstr_w(bstr)); SysFreeString(bstr); } #define test_option_put_text(o,t) _test_option_put_text(__LINE__,o,t) static void _test_option_put_text(unsigned line, IHTMLOptionElement *option, const char *text) { BSTR bstr; HRESULT hres; bstr = a2bstr(text); hres = IHTMLOptionElement_put_text(option, bstr); SysFreeString(bstr); ok(hres == S_OK, "put_text failed: %08x\n", hres); _test_option_text(line, option, text); } #define test_option_value(o,t) _test_option_value(__LINE__,o,t) static void _test_option_value(unsigned line, IHTMLOptionElement *option, const char *value) { BSTR bstr; HRESULT hres; hres = IHTMLOptionElement_get_value(option, &bstr); ok_(__FILE__,line) (hres == S_OK, "get_value failed: %08x\n", hres); ok_(__FILE__,line) (!strcmp_wa(bstr, value), "value=%s\n", dbgstr_w(bstr)); SysFreeString(bstr); } #define test_option_put_value(o,t) _test_option_put_value(__LINE__,o,t) static void _test_option_put_value(unsigned line, IHTMLOptionElement *option, const char *value) { BSTR bstr; HRESULT hres; bstr = a2bstr(value); hres = IHTMLOptionElement_put_value(option, bstr); SysFreeString(bstr); ok(hres == S_OK, "put_value failed: %08x\n", hres); _test_option_value(line, option, value); } #define create_option_elem(d,t,v) _create_option_elem(__LINE__,d,t,v) static IHTMLOptionElement *_create_option_elem(unsigned line, IHTMLDocument2 *doc, const char *txt, const char *val) { IHTMLOptionElementFactory *factory; IHTMLOptionElement *option; IHTMLWindow2 *window; VARIANT text, value, empty; HRESULT hres; hres = IHTMLDocument2_get_parentWindow(doc, &window); ok_(__FILE__,line) (hres == S_OK, "get_parentElement failed: %08x\n", hres); hres = IHTMLWindow2_get_Option(window, &factory); IHTMLWindow2_Release(window); ok_(__FILE__,line) (hres == S_OK, "get_Option failed: %08x\n", hres); V_VT(&text) = VT_BSTR; V_BSTR(&text) = a2bstr(txt); V_VT(&value) = VT_BSTR; V_BSTR(&value) = a2bstr(val); V_VT(&empty) = VT_EMPTY; hres = IHTMLOptionElementFactory_create(factory, text, value, empty, empty, &option); ok_(__FILE__,line) (hres == S_OK, "create failed: %08x\n", hres); IHTMLOptionElementFactory_Release(factory); VariantClear(&text); VariantClear(&value); _test_option_text(line, option, txt); _test_option_value(line, option, val); return option; } #define test_select_length(s,l) _test_select_length(__LINE__,s,l) static void _test_select_length(unsigned line, IHTMLSelectElement *select, long length) { long len = 0xdeadbeef; HRESULT hres; hres = IHTMLSelectElement_get_length(select, &len); ok_(__FILE__,line) (hres == S_OK, "get_length failed: %08x\n", hres); ok_(__FILE__,line) (len == length, "len=%ld, expected %ld\n", len, length); } #define test_select_selidx(s,i) _test_select_selidx(__LINE__,s,i) static void _test_select_selidx(unsigned line, IHTMLSelectElement *select, long index) { long idx = 0xdeadbeef; HRESULT hres; hres = IHTMLSelectElement_get_selectedIndex(select, &idx); ok_(__FILE__,line) (hres == S_OK, "get_selectedIndex failed: %08x\n", hres); ok_(__FILE__,line) (idx == index, "idx=%ld, expected %ld\n", idx, index); } #define test_select_put_selidx(s,i) _test_select_put_selidx(__LINE__,s,i) static void _test_select_put_selidx(unsigned line, IHTMLSelectElement *select, long index) { HRESULT hres; hres = IHTMLSelectElement_put_selectedIndex(select, index); ok_(__FILE__,line) (hres == S_OK, "get_selectedIndex failed: %08x\n", hres); _test_select_selidx(line, select, index); } #define test_range_text(r,t) _test_range_text(__LINE__,r,t) static void _test_range_text(unsigned line, IHTMLTxtRange *range, const char *extext) { BSTR text; HRESULT hres; hres = IHTMLTxtRange_get_text(range, &text); ok_(__FILE__, line) (hres == S_OK, "get_text failed: %08x\n", hres); if(extext) { ok_(__FILE__, line) (text != NULL, "text == NULL\n"); ok_(__FILE__, line) (!strcmp_wa(text, extext), "text=\"%s\", expected \"%s\"\n", dbgstr_w(text), extext); }else { ok_(__FILE__, line) (text == NULL, "text=\"%s\", expected NULL\n", dbgstr_w(text)); } SysFreeString(text); } #define test_range_collapse(r,b) _test_range_collapse(__LINE__,r,b) static void _test_range_collapse(unsigned line, IHTMLTxtRange *range, BOOL b) { HRESULT hres; hres = IHTMLTxtRange_collapse(range, b); ok_(__FILE__, line) (hres == S_OK, "collapse failed: %08x\n", hres); _test_range_text(line, range, NULL); } #define test_range_expand(r,u,b,t) _test_range_expand(__LINE__,r,u,b,t) static void _test_range_expand(unsigned line, IHTMLTxtRange *range, LPWSTR unit, VARIANT_BOOL exb, const char *extext) { VARIANT_BOOL b = 0xe0e0; HRESULT hres; hres = IHTMLTxtRange_expand(range, unit, &b); ok_(__FILE__,line) (hres == S_OK, "expand failed: %08x\n", hres); ok_(__FILE__,line) (b == exb, "b=%x, expected %x\n", b, exb); _test_range_text(line, range, extext); } #define test_range_move(r,u,c,e) _test_range_move(__LINE__,r,u,c,e) static void _test_range_move(unsigned line, IHTMLTxtRange *range, LPWSTR unit, long cnt, long excnt) { long c = 0xdeadbeef; HRESULT hres; hres = IHTMLTxtRange_move(range, unit, cnt, &c); ok_(__FILE__,line) (hres == S_OK, "move failed: %08x\n", hres); ok_(__FILE__,line) (c == excnt, "count=%ld, expected %ld\n", c, excnt); _test_range_text(line, range, NULL); } #define test_range_movestart(r,u,c,e) _test_range_movestart(__LINE__,r,u,c,e) static void _test_range_movestart(unsigned line, IHTMLTxtRange *range, LPWSTR unit, long cnt, long excnt) { long c = 0xdeadbeef; HRESULT hres; hres = IHTMLTxtRange_moveStart(range, unit, cnt, &c); ok_(__FILE__,line) (hres == S_OK, "move failed: %08x\n", hres); ok_(__FILE__,line) (c == excnt, "count=%ld, expected %ld\n", c, excnt); } #define test_range_moveend(r,u,c,e) _test_range_moveend(__LINE__,r,u,c,e) static void _test_range_moveend(unsigned line, IHTMLTxtRange *range, LPWSTR unit, long cnt, long excnt) { long c = 0xdeadbeef; HRESULT hres; hres = IHTMLTxtRange_moveEnd(range, unit, cnt, &c); ok_(__FILE__,line) (hres == S_OK, "move failed: %08x\n", hres); ok_(__FILE__,line) (c == excnt, "count=%ld, expected %ld\n", c, excnt); } #define test_range_put_text(r,t) _test_range_put_text(__LINE__,r,t) static void _test_range_put_text(unsigned line, IHTMLTxtRange *range, const char *text) { HRESULT hres; BSTR bstr = a2bstr(text); hres = IHTMLTxtRange_put_text(range, bstr); ok_(__FILE__,line) (hres == S_OK, "put_text failed: %08x\n", hres); SysFreeString(bstr); _test_range_text(line, range, NULL); } #define test_range_inrange(r1,r2,b) _test_range_inrange(__LINE__,r1,r2,b) static void _test_range_inrange(unsigned line, IHTMLTxtRange *range1, IHTMLTxtRange *range2, VARIANT_BOOL exb) { VARIANT_BOOL b; HRESULT hres; b = 0xe0e0; hres = IHTMLTxtRange_inRange(range1, range2, &b); ok_(__FILE__,line) (hres == S_OK, "(1->2) isEqual failed: %08x\n", hres); ok_(__FILE__,line) (b == exb, "(1->2) b=%x, expected %x\n", b, exb); } #define test_range_isequal(r1,r2,b) _test_range_isequal(__LINE__,r1,r2,b) static void _test_range_isequal(unsigned line, IHTMLTxtRange *range1, IHTMLTxtRange *range2, VARIANT_BOOL exb) { VARIANT_BOOL b; HRESULT hres; b = 0xe0e0; hres = IHTMLTxtRange_isEqual(range1, range2, &b); ok_(__FILE__,line) (hres == S_OK, "(1->2) isEqual failed: %08x\n", hres); ok_(__FILE__,line) (b == exb, "(1->2) b=%x, expected %x\n", b, exb); b = 0xe0e0; hres = IHTMLTxtRange_isEqual(range2, range1, &b); ok_(__FILE__,line) (hres == S_OK, "(2->1) isEqual failed: %08x\n", hres); ok_(__FILE__,line) (b == exb, "(2->1) b=%x, expected %x\n", b, exb); if(exb) { test_range_inrange(range1, range2, VARIANT_TRUE); test_range_inrange(range2, range1, VARIANT_TRUE); } } #define test_range_parent(r,t) _test_range_parent(__LINE__,r,t) static void _test_range_parent(unsigned line, IHTMLTxtRange *range, elem_type_t type) { IHTMLElement *elem; HRESULT hres; hres = IHTMLTxtRange_parentElement(range, &elem); ok_(__FILE__,line) (hres == S_OK, "parentElement failed: %08x\n", hres); _test_elem_type(line, (IUnknown*)elem, type); IHTMLElement_Release(elem); } #define test_elem_collection(c,t,l) _test_elem_collection(__LINE__,c,t,l) static void _test_elem_collection(unsigned line, IHTMLElementCollection *col, const elem_type_t *elem_types, long exlen) { long len; DWORD i; VARIANT name, index; IDispatch *disp; HRESULT hres; test_disp((IUnknown*)col, &DIID_DispHTMLElementCollection); hres = IHTMLElementCollection_get_length(col, &len); ok_(__FILE__,line) (hres == S_OK, "get_length failed: %08x\n", hres); ok_(__FILE__,line) (len == exlen, "len=%ld, expected %ld\n", len, exlen); if(len > exlen) len = exlen; V_VT(&index) = VT_EMPTY; V_VT(&name) = VT_I4; for(i=0; i