[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