Signed-off-by: Gabriel Ivăncescu gabrielopcode@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);