[PATCH v2 7/7] mshtml: Implement enumerator for HTMLAttributeCollection.
Gabriel Ivăncescu
gabrielopcode at gmail.com
Wed Apr 13 09:34:58 CDT 2022
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at 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);
--
2.34.1
More information about the wine-devel
mailing list