For the IE9 attr node implementation (delegating to gecko attr nodes), this uses the existing infrastructure to keep track of the attributes in a collection (and so, associate DISPIDs for them, etc), since it also matches native (same DISPID broken behavior when removing attrs) and we already have the code for it. The current code is split into helpers called from many places already related to it, so instead I just extended the helpers with new code paths for IE9+ attrs.
There's also appropriate FIXMEs for things that aren't properly handled by simple delegation to gecko (from my older patchset), so at least if we find something that needs it, it will be way easier to debug.
The last patch is a bit big since it had to enable the new attr nodes everywhere in IE9+ modes, else either tests would fail or we wouldn't test anything, as I'm using existing tests (just in IE9 mode).
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlelem.c | 63 +++++++++++++++++----------- dlls/mshtml/tests/dom.c | 92 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 125 insertions(+), 30 deletions(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index a46a71755d9..e4afbd42574 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -47,6 +47,8 @@ typedef struct { } tag_desc_t;
static HRESULT HTMLElement_Ctor(HTMLDocumentNode*,nsIDOMElement*,HTMLElement**); +static HRESULT get_attr_dispid_by_name(HTMLAttributeCollection*,const WCHAR*,BOOL,DISPID*); +static HRESULT get_domattr(HTMLAttributeCollection*,DISPID,LONG*,HTMLDOMAttribute**);
static const tag_desc_t tag_descs[] = { {L"A", HTMLAnchorElement_Create}, @@ -4752,21 +4754,28 @@ static HRESULT WINAPI HTMLElement6_removeAttribute(IHTMLElement6 *iface, BSTR st static HRESULT WINAPI HTMLElement6_getAttributeNode(IHTMLElement6 *iface, BSTR strAttributeName, IHTMLDOMAttribute2 **ppretAttribute) { HTMLElement *This = impl_from_IHTMLElement6(iface); - IHTMLDOMAttribute *attr; + HTMLAttributeCollection *attrs; + HTMLDOMAttribute *attr; HRESULT hres; + DISPID id;
- WARN("(%p)->(%s %p) forwarding to IHTMLElement4\n", This, debugstr_w(strAttributeName), ppretAttribute); + TRACE("(%p)->(%s %p)\n", This, debugstr_w(strAttributeName), ppretAttribute);
- hres = IHTMLElement4_getAttributeNode(&This->IHTMLElement4_iface, strAttributeName, &attr); + hres = HTMLElement_get_attr_col(&This->node, &attrs); if(FAILED(hres)) return hres;
- if(attr) { - hres = IHTMLDOMAttribute_QueryInterface(attr, &IID_IHTMLDOMAttribute2, (void**)ppretAttribute); - IHTMLDOMAttribute_Release(attr); - }else { + hres = get_attr_dispid_by_name(attrs, strAttributeName, TRUE, &id); + if(hres == S_OK) { + hres = get_domattr(attrs, id, NULL, &attr); + if(hres == S_OK) + *ppretAttribute = &attr->IHTMLDOMAttribute2_iface; + }else if(hres == DISP_E_UNKNOWNNAME) { *ppretAttribute = NULL; + hres = S_OK; } + + IHTMLAttributeCollection_Release(&attrs->IHTMLAttributeCollection_iface); return hres; }
@@ -7773,7 +7782,7 @@ static HRESULT create_filters_collection(compat_mode_t compat_mode, IHTMLFilters return S_OK; }
-static inline BOOL is_valid_attr_dispid(HTMLAttributeCollection *col, DISPID id) +static inline BOOL is_valid_attr_dispid(HTMLAttributeCollection *col, DISPID id, BOOL specified_only) { if(get_dispid_type(id) != DISPEXPROP_BUILTIN) return TRUE; @@ -7781,10 +7790,14 @@ static inline BOOL is_valid_attr_dispid(HTMLAttributeCollection *col, DISPID id) if(dispex_builtin_is_noattr(&col->elem->node.event_target.dispex, id)) return FALSE;
+ if(specified_only) + return !!element_has_attribute(col->elem, dispex_builtin_prop_name(&col->elem->node.event_target.dispex, id)); + return TRUE; }
-static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LONG *idx, DISPID start, DISPID *dispid) +static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LONG *idx, DISPID start, + BOOL specified_only, DISPID *dispid) { DISPID id = start; LONG len = -1; @@ -7798,7 +7811,7 @@ static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LO return hres; else if(hres == S_FALSE) break; - else if(!is_valid_attr_dispid(This, id)) + else if(!is_valid_attr_dispid(This, id, specified_only)) continue;
len++; @@ -7815,12 +7828,12 @@ static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LO return S_OK; }
-static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, DISPID *dispid) +static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, BOOL specified_only, DISPID *dispid) { - return get_attr_dispid_by_relative_idx(This, idx, DISPID_STARTENUM, dispid); + return get_attr_dispid_by_relative_idx(This, idx, DISPID_STARTENUM, specified_only, dispid); }
-static inline HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, const WCHAR *name, DISPID *id) +static HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, const WCHAR *name, BOOL specified_only, DISPID *id) { HRESULT hres;
@@ -7830,17 +7843,17 @@ static inline HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, con
idx = wcstoul(name, &end_ptr, 10); if(!*end_ptr) { - hres = get_attr_dispid_by_idx(This, &idx, id); + hres = get_attr_dispid_by_idx(This, &idx, specified_only, id); if(SUCCEEDED(hres)) return hres; } }
hres = dispex_get_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, id); - return (FAILED(hres) || is_valid_attr_dispid(This, *id)) ? hres : DISP_E_UNKNOWNNAME; + return (FAILED(hres) || is_valid_attr_dispid(This, *id, specified_only)) ? hres : DISP_E_UNKNOWNNAME; }
-static inline HRESULT get_domattr(HTMLAttributeCollection *This, DISPID id, LONG *list_pos, HTMLDOMAttribute **attr) +static HRESULT get_domattr(HTMLAttributeCollection *This, DISPID id, LONG *list_pos, HTMLDOMAttribute **attr) { HRESULT hres;
@@ -7925,7 +7938,7 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Next(IEnumVARIANT *iface, ULON 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); + hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, dispid, FALSE, &tmp); if(SUCCEEDED(hres)) { dispid = tmp; hres = get_domattr(This->col, dispid, NULL, &attr); @@ -7962,14 +7975,14 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Skip(IEnumVARIANT *iface, ULON return S_OK;
rel_index = -1; - hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, This->iter_dispid, NULL); + hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, This->iter_dispid, FALSE, 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); + hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, This->iter_dispid, FALSE, &dispid); if(FAILED(hres)) return hres; This->iter_dispid = dispid; @@ -8021,7 +8034,7 @@ static HRESULT WINAPI HTMLAttributeCollection_get_length(IHTMLAttributeCollectio TRACE("(%p)->(%p)\n", This, p);
*p = -1; - hres = get_attr_dispid_by_idx(This, p, NULL); + hres = get_attr_dispid_by_idx(This, p, FALSE, NULL); return hres; }
@@ -8058,10 +8071,10 @@ static HRESULT WINAPI HTMLAttributeCollection_item(IHTMLAttributeCollection *ifa
switch(V_VT(name)) { case VT_I4: - hres = get_attr_dispid_by_idx(This, &V_I4(name), &id); + hres = get_attr_dispid_by_idx(This, &V_I4(name), FALSE, &id); break; case VT_BSTR: - hres = get_attr_dispid_by_name(This, V_BSTR(name), &id); + hres = get_attr_dispid_by_name(This, V_BSTR(name), FALSE, &id); break; default: FIXME("unsupported name %s\n", debugstr_variant(name)); @@ -8111,7 +8124,7 @@ static HRESULT WINAPI HTMLAttributeCollection2_getNamedItem(IHTMLAttributeCollec
TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrName), newretNode);
- hres = get_attr_dispid_by_name(This, bstrName, &id); + hres = get_attr_dispid_by_name(This, bstrName, FALSE, &id); if(hres == DISP_E_UNKNOWNNAME) { *newretNode = NULL; return S_OK; @@ -8196,7 +8209,7 @@ static HRESULT WINAPI HTMLAttributeCollection3_item(IHTMLAttributeCollection3 *i
TRACE("(%p)->(%ld %p)\n", This, index, ppNodeOut);
- hres = get_attr_dispid_by_idx(This, &index, &id); + hres = get_attr_dispid_by_idx(This, &index, FALSE, &id); if(hres == DISP_E_UNKNOWNNAME) return E_INVALIDARG; if(FAILED(hres)) @@ -8292,7 +8305,7 @@ static HRESULT HTMLAttributeCollection_get_dispid(DispatchEx *dispex, const WCHA
TRACE("(%p)->(%s %lx %p)\n", This, debugstr_w(name), flags, dispid);
- hres = get_attr_dispid_by_name(This, name, dispid); + hres = get_attr_dispid_by_name(This, name, FALSE, dispid); if(FAILED(hres)) return hres;
diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 914267c67a1..d7155f32a7c 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -2088,10 +2088,10 @@ static void _test_comment_text(unsigned line, IUnknown *unk, const WCHAR *extext }
#define create_attr(a,b) _create_attr(__LINE__,a,b) -static IHTMLDOMAttribute *_create_attr(unsigned line, IUnknown *unk, const char *name) +static IHTMLDOMAttribute *_create_attr(unsigned line, IUnknown *unk, const WCHAR *name) { IHTMLDocument5 *doc = _get_htmldoc5_iface(line, unk); - BSTR str = SysAllocString(L"Test"); + BSTR str = SysAllocString(name); IHTMLDOMAttribute *attr; HRESULT hres;
@@ -5489,6 +5489,78 @@ static IHTMLDOMAttribute *_get_elem_attr_node(unsigned line, IUnknown *unk, cons return attr; }
+#define get_elem6_attr_node(a,b,c) _get_elem6_attr_node(__LINE__,a,b,c) +static IHTMLDOMAttribute *_get_elem6_attr_node(unsigned line, IUnknown *unk, const WCHAR *attr_name, BOOL expect_success) +{ + BSTR str = SysAllocString(attr_name); + IHTMLDOMAttribute2 *attr2; + IHTMLDOMAttribute *attr; + IHTMLElement6 *elem; + HRESULT hres; + + hres = IUnknown_QueryInterface(unk, &IID_IHTMLElement6, (void**)&elem); + ok_(__FILE__,line)(hres == S_OK, "Could not get IHTMLElement6: %08lx\n", hres); + + attr = (void*)0xdeadbeef; + hres = IHTMLElement6_getAttributeNode(elem, str, &attr2); + ok_(__FILE__,line)(hres == S_OK, "getAttributeNode failed: %08lx\n", hres); + if(expect_success) { + ok_(__FILE__,line)(attr2 != NULL, "attr = NULL\n"); + hres = IHTMLDOMAttribute2_QueryInterface(attr2, &IID_IHTMLDOMAttribute, (void**)&attr); + ok_(__FILE__,line)(hres == S_OK, "Could not get IHTMLDOMAttribute: %08lx\n", hres); + IHTMLDOMAttribute2_Release(attr2); + }else { + ok_(__FILE__,line)(!attr2, "attr = %p\n", attr2); + attr = NULL; + } + + IHTMLElement6_Release(elem); + SysFreeString(str); + return attr; +} + +#define get_elem_attr_node_via_disp(a,b,c) _get_elem_attr_node_via_disp(__LINE__,a,b,c) +static IHTMLDOMAttribute *_get_elem_attr_node_via_disp(unsigned line, IUnknown *unk, const WCHAR *attr_name, BOOL expect_success) +{ + IHTMLDOMAttribute *attr = NULL; + DISPPARAMS dp = { 0 }; + IDispatchEx *dispex; + VARIANT var, arg; + EXCEPINFO ei; + HRESULT hres; + DISPID id; + BSTR str; + + hres = IUnknown_QueryInterface(unk, &IID_IDispatchEx, (void**)&dispex); + ok_(__FILE__,line)(hres == S_OK, "Could not get IDispatchEx: %08lx\n", hres); + + str = SysAllocString(L"getAttributeNode"); + hres = IDispatchEx_GetDispID(dispex, str, fdexNameCaseSensitive, &id); + ok_(__FILE__,line)(hres == S_OK, "GetDispID failed: %08lx\n", hres); + SysFreeString(str); + + dp.cArgs = 1; + dp.rgvarg = &arg; + V_VT(&arg) = VT_BSTR; + V_BSTR(&arg) = SysAllocString(attr_name); + + VariantInit(&var); + hres = IDispatchEx_InvokeEx(dispex, id, LOCALE_NEUTRAL, DISPATCH_METHOD, &dp, &var, &ei, NULL); + ok_(__FILE__,line)(hres == S_OK, "InvokeEx failed: %08lx\n", hres); + if(!expect_success) + ok_(__FILE__,line)(V_VT(&var) == VT_NULL, "V_VT(var) = %d\n", V_VT(&var)); + else { + ok_(__FILE__,line)(V_VT(&var) == VT_DISPATCH, "V_VT(var) = %d\n", V_VT(&var)); + ok_(__FILE__,line)(V_DISPATCH(&var) != NULL, "V_DISPATCH(var) == NULL\n"); + hres = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IHTMLDOMAttribute, (void**)&attr); + ok_(__FILE__,line)(hres == S_OK, "Could not get IHTMLDOMAttribute: %08lx\n", hres); + } + IDispatchEx_Release(dispex); + VariantClear(&arg); + VariantClear(&var); + return attr; +} + #define get_attr_node_value(a,b,c) _get_attr_node_value(__LINE__,a,b,c) static void _get_attr_node_value(unsigned line, IHTMLDOMAttribute *attr, VARIANT *v, VARTYPE vt) { @@ -10690,16 +10762,26 @@ static void test_attr(IHTMLDocument2 *doc, IHTMLElement *elem) test_attr_specified(attr, has_attr ? VARIANT_TRUE : VARIANT_FALSE); test_attr_expando(attr, VARIANT_FALSE); IHTMLDOMAttribute_Release(attr); + attr = get_elem6_attr_node((IUnknown*)elem, elem_attr_props[i], has_attr); + if(attr) IHTMLDOMAttribute_Release(attr); + attr = get_elem_attr_node_via_disp((IUnknown*)elem, elem_attr_props[i], TRUE); + if(attr) IHTMLDOMAttribute_Release(attr); } for(i = 0; i < ARRAY_SIZE(elem_noattr_props); i++) { get_elem_attr_node((IUnknown*)elem, elem_noattr_props[i], FALSE); + get_elem6_attr_node((IUnknown*)elem, elem_noattr_props[i], FALSE); }
ok(elem_has_attr((IUnknown*)elem, L"emptyattr"), "elem does not have emptyattr"); - attr = get_elem_attr_node((IUnknown*)elem, L"emptyattr", TRUE); + attr = get_elem6_attr_node((IUnknown*)elem, L"emptyattr", TRUE); test_attr_specified(attr, VARIANT_TRUE); test_attr_expando(attr, VARIANT_TRUE); test_attr_node(attr, doc); + IHTMLDOMAttribute_Release(attr); + + attr = get_elem_attr_node_via_disp((IUnknown*)elem, L"emptyattr", TRUE); + test_attr_specified(attr, VARIANT_TRUE); + test_attr_expando(attr, VARIANT_TRUE);
bstr = SysAllocString(L"emptyattr"); hres = IHTMLElement_removeAttribute(elem, bstr, 0, &vbool); @@ -10718,7 +10800,7 @@ static void test_attr(IHTMLDocument2 *doc, IHTMLElement *elem) IHTMLDOMAttribute_Release(attr);
/* Test created, detached attribute. */ - attr = create_attr((IUnknown*)doc, "Test"); + attr = create_attr((IUnknown*)doc, L"Test");
test_disp((IUnknown*)attr, &DIID_DispHTMLDOMAttribute, NULL, L"[object]"); test_ifaces((IUnknown*)attr, attr_iids); @@ -10760,7 +10842,7 @@ static void test_attr(IHTMLDocument2 *doc, IHTMLElement *elem) ok(iface_cmp((IUnknown*)attr2, (IUnknown*)attr), "attr2 != attr\n"); IHTMLDOMAttribute_Release(attr2);
- attr3 = create_attr((IUnknown*)doc, "Test"); + attr3 = create_attr((IUnknown*)doc, L"Test"); put_attr_value(attr3, L"replace test");
hres = IHTMLElement4_setAttributeNode(elem4, attr3, &attr2);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlattr.c | 27 +++++++++++---------------- dlls/mshtml/mshtml_private.h | 3 +-- 2 files changed, 12 insertions(+), 18 deletions(-)
diff --git a/dlls/mshtml/htmlattr.c b/dlls/mshtml/htmlattr.c index f98356e19db..7a1f9e88fbf 100644 --- a/dlls/mshtml/htmlattr.c +++ b/dlls/mshtml/htmlattr.c @@ -37,7 +37,7 @@ static inline HTMLDOMAttribute *impl_from_IHTMLDOMAttribute(IHTMLDOMAttribute *i return CONTAINING_RECORD(iface, HTMLDOMAttribute, IHTMLDOMAttribute_iface); }
-DISPEX_IDISPATCH_IMPL(HTMLDOMAttribute, IHTMLDOMAttribute, impl_from_IHTMLDOMAttribute(iface)->dispex) +DISPEX_IDISPATCH_IMPL(HTMLDOMAttribute, IHTMLDOMAttribute, impl_from_IHTMLDOMAttribute(iface)->node.event_target.dispex)
static HRESULT WINAPI HTMLDOMAttribute_get_nodeName(IHTMLDOMAttribute *iface, BSTR *p) { @@ -131,7 +131,7 @@ static inline HTMLDOMAttribute *impl_from_IHTMLDOMAttribute2(IHTMLDOMAttribute2 return CONTAINING_RECORD(iface, HTMLDOMAttribute, IHTMLDOMAttribute2_iface); }
-DISPEX_IDISPATCH_IMPL(HTMLDOMAttribute2, IHTMLDOMAttribute2, impl_from_IHTMLDOMAttribute2(iface)->dispex) +DISPEX_IDISPATCH_IMPL(HTMLDOMAttribute2, IHTMLDOMAttribute2, impl_from_IHTMLDOMAttribute2(iface)->node.event_target.dispex)
static HRESULT WINAPI HTMLDOMAttribute2_get_name(IHTMLDOMAttribute2 *iface, BSTR *p) { @@ -275,7 +275,7 @@ static HRESULT WINAPI HTMLDOMAttribute2_get_ownerDocument(IHTMLDOMAttribute2 *if
TRACE("(%p)->(%p)\n", This, p);
- *p = (IDispatch*)&This->doc->IHTMLDocument2_iface; + *p = (IDispatch*)&This->node.doc->IHTMLDocument2_iface; IDispatch_AddRef(*p); return S_OK; } @@ -352,13 +352,13 @@ static HRESULT WINAPI HTMLDOMAttribute2_cloneNode(IHTMLDOMAttribute2 *iface, VAR hres = dispex_prop_name(&This->elem->node.event_target.dispex, This->dispid, &name); if(FAILED(hres)) return hres; - hres = HTMLDOMAttribute_Create(name, NULL, 0, This->doc, &new_attr); + hres = HTMLDOMAttribute_Create(name, NULL, 0, This->node.doc, &new_attr); SysFreeString(name); if(FAILED(hres)) return hres; hres = get_elem_attr_value_by_dispid(This->elem, This->dispid, &new_attr->value); }else { - hres = HTMLDOMAttribute_Create(This->name, NULL, 0, This->doc, &new_attr); + hres = HTMLDOMAttribute_Create(This->name, NULL, 0, This->node.doc, &new_attr); if(FAILED(hres)) return hres; hres = VariantCopy(&new_attr->value, &This->value); @@ -407,7 +407,7 @@ static inline HTMLDOMAttribute *impl_from_IHTMLDOMAttribute3(IHTMLDOMAttribute3 return CONTAINING_RECORD(iface, HTMLDOMAttribute, IHTMLDOMAttribute3_iface); }
-DISPEX_IDISPATCH_IMPL(HTMLDOMAttribute3, IHTMLDOMAttribute3, impl_from_IHTMLDOMAttribute3(iface)->dispex) +DISPEX_IDISPATCH_IMPL(HTMLDOMAttribute3, IHTMLDOMAttribute3, impl_from_IHTMLDOMAttribute3(iface)->node.event_target.dispex)
static HRESULT WINAPI HTMLDOMAttribute3_put_nodeValue(IHTMLDOMAttribute3 *iface, VARIANT v) { @@ -488,7 +488,7 @@ static const IHTMLDOMAttribute3Vtbl HTMLDOMAttribute3Vtbl = {
static inline HTMLDOMAttribute *impl_from_DispatchEx(DispatchEx *iface) { - return CONTAINING_RECORD(iface, HTMLDOMAttribute, dispex); + return CONTAINING_RECORD(iface, HTMLDOMAttribute, node.event_target.dispex); }
static void *HTMLDOMAttribute_query_interface(DispatchEx *dispex, REFIID riid) @@ -508,9 +508,8 @@ static void *HTMLDOMAttribute_query_interface(DispatchEx *dispex, REFIID riid) static void HTMLDOMAttribute_traverse(DispatchEx *dispex, nsCycleCollectionTraversalCallback *cb) { HTMLDOMAttribute *This = impl_from_DispatchEx(dispex); + HTMLDOMNode_traverse(&This->node.event_target.dispex, cb);
- if(This->doc) - note_cc_edge((nsISupports*)&This->doc->node.IHTMLDOMNode_iface, "doc", cb); if(This->elem) note_cc_edge((nsISupports*)&This->elem->node.IHTMLDOMNode_iface, "elem", cb); traverse_variant(&This->value, "value", cb); @@ -519,12 +518,8 @@ static void HTMLDOMAttribute_traverse(DispatchEx *dispex, nsCycleCollectionTrave static void HTMLDOMAttribute_unlink(DispatchEx *dispex) { HTMLDOMAttribute *This = impl_from_DispatchEx(dispex); + HTMLDOMNode_unlink(&This->node.event_target.dispex);
- if(This->doc) { - HTMLDocumentNode *doc = This->doc; - This->doc = NULL; - IHTMLDOMNode_Release(&doc->node.IHTMLDOMNode_iface); - } if(This->elem) { HTMLElement *elem = This->elem; This->elem = NULL; @@ -590,7 +585,7 @@ HRESULT HTMLDOMAttribute_Create(const WCHAR *name, HTMLElement *elem, DISPID dis ret->dispid = dispid; ret->elem = elem;
- init_dispatch(&ret->dispex, &Attr_dispex, doc->script_global, + init_dispatch(&ret->node.event_target.dispex, &Attr_dispex, doc->script_global, dispex_compat_mode(&doc->script_global->event_target.dispex));
/* For attributes attached to an element, (elem,dispid) pair should be valid used for its operation. */ @@ -616,7 +611,7 @@ HRESULT HTMLDOMAttribute_Create(const WCHAR *name, HTMLElement *elem, DISPID dis } }
- ret->doc = doc; + ret->node.doc = doc; IHTMLDOMNode_AddRef(&doc->node.IHTMLDOMNode_iface);
*attr = ret; diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index fe18f01e553..270990f249a 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1335,7 +1335,7 @@ struct HTMLAttributeCollection { };
typedef struct { - DispatchEx dispex; + HTMLDOMNode node; IHTMLDOMAttribute IHTMLDOMAttribute_iface; IHTMLDOMAttribute2 IHTMLDOMAttribute2_iface; IHTMLDOMAttribute3 IHTMLDOMAttribute3_iface; @@ -1345,7 +1345,6 @@ typedef struct { /* name must be valid for detached attributes */ BSTR name;
- HTMLDocumentNode *doc; HTMLElement *elem; DISPID dispid; struct list entry;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlelem.c | 112 +++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 44 deletions(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index e4afbd42574..661c12428fa 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -41,14 +41,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(mshtml); #define ATTRFLAG_ASSTRING 0x0002 #define ATTRFLAG_EXPANDURL 0x0004
+struct attr_id { + DISPID dispid; +}; + +struct attr_enum_id { + DISPID dispid; +}; + typedef struct { const WCHAR *name; HRESULT (*constructor)(HTMLDocumentNode*,nsIDOMElement*,HTMLElement**); } tag_desc_t;
static HRESULT HTMLElement_Ctor(HTMLDocumentNode*,nsIDOMElement*,HTMLElement**); -static HRESULT get_attr_dispid_by_name(HTMLAttributeCollection*,const WCHAR*,BOOL,DISPID*); -static HRESULT get_domattr(HTMLAttributeCollection*,DISPID,LONG*,HTMLDOMAttribute**); +static HRESULT get_attr_id_by_name(HTMLAttributeCollection*,const WCHAR*,BOOL,struct attr_id*); +static HRESULT get_domattr(HTMLAttributeCollection*,struct attr_id,LONG*,HTMLDOMAttribute**);
static const tag_desc_t tag_descs[] = { {L"A", HTMLAnchorElement_Create}, @@ -4756,8 +4764,8 @@ static HRESULT WINAPI HTMLElement6_getAttributeNode(IHTMLElement6 *iface, BSTR s HTMLElement *This = impl_from_IHTMLElement6(iface); HTMLAttributeCollection *attrs; HTMLDOMAttribute *attr; + struct attr_id attr_id; HRESULT hres; - DISPID id;
TRACE("(%p)->(%s %p)\n", This, debugstr_w(strAttributeName), ppretAttribute);
@@ -4765,9 +4773,9 @@ static HRESULT WINAPI HTMLElement6_getAttributeNode(IHTMLElement6 *iface, BSTR s if(FAILED(hres)) return hres;
- hres = get_attr_dispid_by_name(attrs, strAttributeName, TRUE, &id); + hres = get_attr_id_by_name(attrs, strAttributeName, TRUE, &attr_id); if(hres == S_OK) { - hres = get_domattr(attrs, id, NULL, &attr); + hres = get_domattr(attrs, attr_id, NULL, &attr); if(hres == S_OK) *ppretAttribute = &attr->IHTMLDOMAttribute2_iface; }else if(hres == DISP_E_UNKNOWNNAME) { @@ -7796,10 +7804,16 @@ static inline BOOL is_valid_attr_dispid(HTMLAttributeCollection *col, DISPID id, return TRUE; }
-static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LONG *idx, DISPID start, - BOOL specified_only, DISPID *dispid) +static struct attr_id attr_enum_id_to_attr_id(HTMLAttributeCollection *This, struct attr_enum_id enum_id) +{ + struct attr_id ret = { .dispid = enum_id.dispid }; + return ret; +} + +static HRESULT get_attr_enum_id_by_relative_idx(HTMLAttributeCollection *This, LONG *idx, struct attr_enum_id start, + BOOL specified_only, struct attr_enum_id *ret) { - DISPID id = start; + DISPID id = start.dispid; LONG len = -1; HRESULT hres;
@@ -7819,8 +7833,8 @@ static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LO break; }
- if(dispid) { - *dispid = id; + if(ret) { + ret->dispid = id; return *idx==len ? S_OK : DISP_E_UNKNOWNNAME; }
@@ -7828,12 +7842,21 @@ static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LO return S_OK; }
-static HRESULT get_attr_dispid_by_idx(HTMLAttributeCollection *This, LONG *idx, BOOL specified_only, DISPID *dispid) +static HRESULT get_attr_id_by_idx(HTMLAttributeCollection *This, LONG *idx, BOOL specified_only, struct attr_id *ret) { - return get_attr_dispid_by_relative_idx(This, idx, DISPID_STARTENUM, specified_only, dispid); + struct attr_enum_id enum_id, start = { .dispid = DISPID_STARTENUM }; + HRESULT hres; + + if(!ret) + return get_attr_enum_id_by_relative_idx(This, idx, start, specified_only, NULL); + + hres = get_attr_enum_id_by_relative_idx(This, idx, start, specified_only, &enum_id); + if(SUCCEEDED(hres)) + *ret = attr_enum_id_to_attr_id(This, enum_id); + return hres; }
-static HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, const WCHAR *name, BOOL specified_only, DISPID *id) +static HRESULT get_attr_id_by_name(HTMLAttributeCollection *This, const WCHAR *name, BOOL specified_only, struct attr_id *ret) { HRESULT hres;
@@ -7843,22 +7866,22 @@ static HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, const WCHA
idx = wcstoul(name, &end_ptr, 10); if(!*end_ptr) { - hres = get_attr_dispid_by_idx(This, &idx, specified_only, id); + hres = get_attr_id_by_idx(This, &idx, specified_only, ret); if(SUCCEEDED(hres)) return hres; } }
- hres = dispex_get_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, id); - return (FAILED(hres) || is_valid_attr_dispid(This, *id, specified_only)) ? hres : DISP_E_UNKNOWNNAME; + hres = dispex_get_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, &ret->dispid); + return (FAILED(hres) || is_valid_attr_dispid(This, ret->dispid, specified_only)) ? hres : DISP_E_UNKNOWNNAME; }
-static HRESULT get_domattr(HTMLAttributeCollection *This, DISPID id, LONG *list_pos, HTMLDOMAttribute **attr) +static HRESULT get_domattr(HTMLAttributeCollection *This, struct attr_id attr_id, LONG *list_pos, HTMLDOMAttribute **attr) { HRESULT hres;
- if(!(*attr = find_attr_in_list(This, id, list_pos))) { - hres = HTMLDOMAttribute_Create(NULL, This->elem, id, This->elem->node.doc, attr); + if(!(*attr = find_attr_in_list(This, attr_id.dispid, list_pos))) { + hres = HTMLDOMAttribute_Create(NULL, This->elem, attr_id.dispid, This->elem->node.doc, attr); if(FAILED(hres)) return hres; } @@ -7872,7 +7895,7 @@ typedef struct {
LONG ref;
- DISPID iter_dispid; + struct attr_enum_id iter_attr_id; HTMLAttributeCollection *col; } HTMLAttributeCollectionEnum;
@@ -7929,7 +7952,7 @@ static ULONG WINAPI HTMLAttributeCollectionEnum_Release(IEnumVARIANT *iface) 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; + struct attr_enum_id tmp, enum_id = This->iter_attr_id; HTMLDOMAttribute *attr; LONG rel_index = 0; HRESULT hres; @@ -7938,10 +7961,10 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Next(IEnumVARIANT *iface, ULON 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, FALSE, &tmp); + hres = get_attr_enum_id_by_relative_idx(This->col, &rel_index, enum_id, FALSE, &tmp); if(SUCCEEDED(hres)) { - dispid = tmp; - hres = get_domattr(This->col, dispid, NULL, &attr); + enum_id = tmp; + hres = get_domattr(This->col, attr_enum_id_to_attr_id(This->col, enum_id), NULL, &attr); } else if(hres == DISP_E_UNKNOWNNAME) break; @@ -7956,7 +7979,7 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Next(IEnumVARIANT *iface, ULON V_DISPATCH(&rgVar[i]) = (IDispatch*)&attr->IHTMLDOMAttribute_iface; }
- This->iter_dispid = dispid; + This->iter_attr_id = enum_id; if(pCeltFetched) *pCeltFetched = i; return i == celt ? S_OK : S_FALSE; @@ -7965,8 +7988,8 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Next(IEnumVARIANT *iface, ULON static HRESULT WINAPI HTMLAttributeCollectionEnum_Skip(IEnumVARIANT *iface, ULONG celt) { HTMLAttributeCollectionEnum *This = HTMLAttributeCollectionEnum_from_IEnumVARIANT(iface); + struct attr_enum_id enum_id; LONG remaining, rel_index; - DISPID dispid; HRESULT hres;
TRACE("(%p)->(%lu)\n", This, celt); @@ -7975,17 +7998,17 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Skip(IEnumVARIANT *iface, ULON return S_OK;
rel_index = -1; - hres = get_attr_dispid_by_relative_idx(This->col, &rel_index, This->iter_dispid, FALSE, NULL); + hres = get_attr_enum_id_by_relative_idx(This->col, &rel_index, This->iter_attr_id, FALSE, 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, FALSE, &dispid); + hres = get_attr_enum_id_by_relative_idx(This->col, &rel_index, This->iter_attr_id, FALSE, &enum_id); if(FAILED(hres)) return hres; - This->iter_dispid = dispid; + This->iter_attr_id = enum_id; } return celt > remaining ? S_FALSE : S_OK; } @@ -7996,7 +8019,7 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Reset(IEnumVARIANT *iface)
TRACE("(%p)->()\n", This);
- This->iter_dispid = DISPID_STARTENUM; + This->iter_attr_id.dispid = DISPID_STARTENUM; return S_OK; }
@@ -8034,7 +8057,7 @@ static HRESULT WINAPI HTMLAttributeCollection_get_length(IHTMLAttributeCollectio TRACE("(%p)->(%p)\n", This, p);
*p = -1; - hres = get_attr_dispid_by_idx(This, p, FALSE, NULL); + hres = get_attr_id_by_idx(This, p, FALSE, NULL); return hres; }
@@ -8051,7 +8074,7 @@ static HRESULT WINAPI HTMLAttributeCollection__newEnum(IHTMLAttributeCollection
ret->IEnumVARIANT_iface.lpVtbl = &HTMLAttributeCollectionEnumVtbl; ret->ref = 1; - ret->iter_dispid = DISPID_STARTENUM; + ret->iter_attr_id.dispid = DISPID_STARTENUM;
HTMLAttributeCollection_AddRef(&This->IHTMLAttributeCollection_iface); ret->col = This; @@ -8064,17 +8087,17 @@ static HRESULT WINAPI HTMLAttributeCollection_item(IHTMLAttributeCollection *ifa { HTMLAttributeCollection *This = impl_from_IHTMLAttributeCollection(iface); HTMLDOMAttribute *attr; - DISPID id; + struct attr_id attr_id; HRESULT hres;
TRACE("(%p)->(%s %p)\n", This, debugstr_variant(name), ppItem);
switch(V_VT(name)) { case VT_I4: - hres = get_attr_dispid_by_idx(This, &V_I4(name), FALSE, &id); + hres = get_attr_id_by_idx(This, &V_I4(name), FALSE, &attr_id); break; case VT_BSTR: - hres = get_attr_dispid_by_name(This, V_BSTR(name), FALSE, &id); + hres = get_attr_id_by_name(This, V_BSTR(name), FALSE, &attr_id); break; default: FIXME("unsupported name %s\n", debugstr_variant(name)); @@ -8085,7 +8108,7 @@ static HRESULT WINAPI HTMLAttributeCollection_item(IHTMLAttributeCollection *ifa if(FAILED(hres)) return hres;
- hres = get_domattr(This, id, NULL, &attr); + hres = get_domattr(This, attr_id, NULL, &attr); if(FAILED(hres)) return hres;
@@ -8119,12 +8142,12 @@ static HRESULT WINAPI HTMLAttributeCollection2_getNamedItem(IHTMLAttributeCollec { HTMLAttributeCollection *This = impl_from_IHTMLAttributeCollection2(iface); HTMLDOMAttribute *attr; - DISPID id; + struct attr_id attr_id; HRESULT hres;
TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrName), newretNode);
- hres = get_attr_dispid_by_name(This, bstrName, FALSE, &id); + hres = get_attr_id_by_name(This, bstrName, FALSE, &attr_id); if(hres == DISP_E_UNKNOWNNAME) { *newretNode = NULL; return S_OK; @@ -8132,7 +8155,7 @@ static HRESULT WINAPI HTMLAttributeCollection2_getNamedItem(IHTMLAttributeCollec return hres; }
- hres = get_domattr(This, id, NULL, &attr); + hres = get_domattr(This, attr_id, NULL, &attr); if(FAILED(hres)) return hres;
@@ -8204,18 +8227,18 @@ static HRESULT WINAPI HTMLAttributeCollection3_item(IHTMLAttributeCollection3 *i { HTMLAttributeCollection *This = impl_from_IHTMLAttributeCollection3(iface); HTMLDOMAttribute *attr; - DISPID id; + struct attr_id attr_id; HRESULT hres;
TRACE("(%p)->(%ld %p)\n", This, index, ppNodeOut);
- hres = get_attr_dispid_by_idx(This, &index, FALSE, &id); + hres = get_attr_id_by_idx(This, &index, FALSE, &attr_id); if(hres == DISP_E_UNKNOWNNAME) return E_INVALIDARG; if(FAILED(hres)) return hres;
- hres = get_domattr(This, id, NULL, &attr); + hres = get_domattr(This, attr_id, NULL, &attr); if(FAILED(hres)) return hres;
@@ -8300,16 +8323,17 @@ static HRESULT HTMLAttributeCollection_get_dispid(DispatchEx *dispex, const WCHA { HTMLAttributeCollection *This = HTMLAttributeCollection_from_DispatchEx(dispex); HTMLDOMAttribute *attr; + struct attr_id attr_id; LONG pos; HRESULT hres;
TRACE("(%p)->(%s %lx %p)\n", This, debugstr_w(name), flags, dispid);
- hres = get_attr_dispid_by_name(This, name, FALSE, dispid); + hres = get_attr_id_by_name(This, name, FALSE, &attr_id); if(FAILED(hres)) return hres;
- hres = get_domattr(This, *dispid, &pos, &attr); + hres = get_domattr(This, attr_id, &pos, &attr); if(FAILED(hres)) return hres; IHTMLDOMAttribute_Release(&attr->IHTMLDOMAttribute_iface);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/dispex.c | 5 + dlls/mshtml/htmlattr.c | 184 +++++++++++++++-- dlls/mshtml/htmldoc.c | 15 +- dlls/mshtml/htmlelem.c | 265 +++++++++++++++++++++---- dlls/mshtml/mshtml_private.h | 7 +- dlls/mshtml/tests/documentmode.js | 35 +++- dlls/mshtml/tests/dom.c | 320 +++++++++++++++++++++++++++++- dlls/mshtml/tests/dom.js | 44 +++- 8 files changed, 805 insertions(+), 70 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 92a3746f4e5..00c856afedd 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -1386,6 +1386,11 @@ HRESULT dispex_get_chain_builtin_id(DispatchEx *dispex, const WCHAR *name, DWORD
assert(compat_mode >= COMPAT_MODE_IE9);
+ hres = get_builtin_id(info, name, flags, pid); + if(hres != DISP_E_UNKNOWNNAME) + return hres; + info = info->desc->prototype_info[compat_mode - COMPAT_MODE_IE9]; + for(;;) { hres = get_builtin_id(info, name, flags, pid); if(hres != DISP_E_UNKNOWNNAME) diff --git a/dlls/mshtml/htmlattr.c b/dlls/mshtml/htmlattr.c index 7a1f9e88fbf..dddb4b0838e 100644 --- a/dlls/mshtml/htmlattr.c +++ b/dlls/mshtml/htmlattr.c @@ -55,6 +55,12 @@ static HRESULT WINAPI HTMLDOMAttribute_get_nodeName(IHTMLDOMAttribute *iface, BS return *p ? S_OK : E_OUTOFMEMORY; }
+ if(This->dispid == DISPID_UNKNOWN) { + nsAString nsname; + nsAString_InitDepend(&nsname, NULL); + return return_nsstr(nsIDOMAttr_GetName(This->nsattr, &nsname), &nsname, p); + } + return dispex_prop_name(&This->elem->node.event_target.dispex, This->dispid, p); }
@@ -62,12 +68,29 @@ static HRESULT WINAPI HTMLDOMAttribute_put_nodeValue(IHTMLDOMAttribute *iface, V { HTMLDOMAttribute *This = impl_from_IHTMLDOMAttribute(iface); EXCEPINFO ei; + HRESULT hres;
TRACE("(%p)->(%s)\n", This, debugstr_variant(&v));
+ if(This->nsattr) { + nsAString nsval; + nsresult nsres; + + hres = variant_to_nsstr(&v, FALSE, &nsval); + if(FAILED(hres)) + return hres; + nsres = nsIDOMAttr_SetValue(This->nsattr, &nsval); + nsAString_Finish(&nsval); + if(FAILED(nsres)) + return map_nsresult(nsres); + } + if(!This->elem) return VariantCopy(&This->value, &v);
+ if(This->dispid == DISPID_UNKNOWN) + return S_OK; + memset(&ei, 0, sizeof(ei)); return dispex_prop_put(&This->elem->node.event_target.dispex, This->dispid, LOCALE_SYSTEM_DEFAULT, &v, &ei, NULL); } @@ -81,6 +104,12 @@ static HRESULT WINAPI HTMLDOMAttribute_get_nodeValue(IHTMLDOMAttribute *iface, V if(!This->elem) return VariantCopy(p, &This->value);
+ if(This->dispid == DISPID_UNKNOWN) { + nsAString nsval; + nsAString_InitDepend(&nsval, NULL); + return return_nsstr_variant(nsIDOMAttr_GetValue(This->nsattr, &nsval), &nsval, 0, p); + } + return get_elem_attr_value_by_dispid(This->elem, This->dispid, p); }
@@ -98,13 +127,20 @@ static HRESULT WINAPI HTMLDOMAttribute_get_specified(IHTMLDOMAttribute *iface, V return S_OK; }
- if(get_dispid_type(This->dispid) != DISPEXPROP_BUILTIN) { - *p = VARIANT_TRUE; - return S_OK; + if(This->dispid == DISPID_UNKNOWN) { + nsAString_InitDepend(&nsname, NULL); + nsres = nsIDOMAttr_GetName(This->nsattr, &nsname); + if(NS_FAILED(nsres)) + return map_nsresult(nsres); + }else { + if(get_dispid_type(This->dispid) != DISPEXPROP_BUILTIN) { + *p = VARIANT_TRUE; + return S_OK; + } + nsAString_InitDepend(&nsname, dispex_builtin_prop_name(&This->elem->node.event_target.dispex, This->dispid)); }
/* FIXME: This is not exactly right, we have some attributes that don't map directly to Gecko attributes. */ - nsAString_InitDepend(&nsname, dispex_builtin_prop_name(&This->elem->node.event_target.dispex, This->dispid)); nsres = nsIDOMElement_HasAttribute(This->elem->dom_element, &nsname, &r); nsAString_Finish(&nsname);
@@ -163,10 +199,15 @@ static HRESULT WINAPI HTMLDOMAttribute2_get_value(IHTMLDOMAttribute2 *iface, BST TRACE("(%p)->(%p)\n", This, p);
V_VT(&val) = VT_EMPTY; - if(This->elem) - hres = get_elem_attr_value_by_dispid(This->elem, This->dispid, &val); - else + if(!This->elem) hres = VariantCopy(&val, &This->value); + else if(This->dispid != DISPID_UNKNOWN) + hres = get_elem_attr_value_by_dispid(This->elem, This->dispid, &val); + else { + nsAString nsval; + nsAString_InitDepend(&nsval, NULL); + hres = return_nsstr_variant(nsIDOMAttr_GetValue(This->nsattr, &nsval), &nsval, 0, &val); + } if(SUCCEEDED(hres)) hres = attr_value_to_string(&val); if(FAILED(hres)) @@ -182,10 +223,38 @@ static HRESULT WINAPI HTMLDOMAttribute2_get_value(IHTMLDOMAttribute2 *iface, BST static HRESULT WINAPI HTMLDOMAttribute2_get_expando(IHTMLDOMAttribute2 *iface, VARIANT_BOOL *p) { HTMLDOMAttribute *This = impl_from_IHTMLDOMAttribute2(iface); + const PRUnichar *name; + nsAString nsname; + nsresult nsres; + HRESULT hres; + DISPID id;
TRACE("(%p)->(%p)\n", This, p);
- *p = variant_bool(This->elem && get_dispid_type(This->dispid) != DISPEXPROP_BUILTIN); + if(!This->elem) { + *p = VARIANT_FALSE; + return S_OK; + } + + if(This->dispid == DISPID_UNKNOWN) { + nsAString_InitDepend(&nsname, NULL); + nsres = nsIDOMAttr_GetName(This->nsattr, &nsname); + if(NS_FAILED(nsres)) + return map_nsresult(nsres); + + nsAString_GetData(&nsname, &name); + hres = dispex_get_chain_builtin_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, &id); + nsAString_Finish(&nsname); + if(SUCCEEDED(hres)) + *p = variant_bool(dispex_builtin_is_noattr(&This->elem->node.event_target.dispex, id)); + else if(hres == DISP_E_UNKNOWNNAME) + *p = VARIANT_TRUE; + else + return hres; + return S_OK; + } + + *p = variant_bool(get_dispid_type(This->dispid) != DISPEXPROP_BUILTIN); return S_OK; }
@@ -348,17 +417,27 @@ static HRESULT WINAPI HTMLDOMAttribute2_cloneNode(IHTMLDOMAttribute2 *iface, VAR
TRACE("(%p)->(%x %p)\n", This, fDeep, clonedNode);
+ if(This->node.nsnode) { + IHTMLDOMNode *cloned; + hres = IHTMLDOMNode_cloneNode(&This->node.IHTMLDOMNode_iface, fDeep, &cloned); + if(SUCCEEDED(hres)) { + hres = IHTMLDOMNode_QueryInterface(cloned, &IID_IHTMLDOMAttribute, (void**)clonedNode); + IHTMLDOMNode_Release(cloned); + } + return hres; + } + if(This->elem) { hres = dispex_prop_name(&This->elem->node.event_target.dispex, This->dispid, &name); if(FAILED(hres)) return hres; - hres = HTMLDOMAttribute_Create(name, NULL, 0, This->node.doc, &new_attr); + hres = HTMLDOMAttribute_Create(name, NULL, 0, NULL, This->node.doc, &new_attr); SysFreeString(name); if(FAILED(hres)) return hres; hres = get_elem_attr_value_by_dispid(This->elem, This->dispid, &new_attr->value); }else { - hres = HTMLDOMAttribute_Create(This->name, NULL, 0, This->node.doc, &new_attr); + hres = HTMLDOMAttribute_Create(This->name, NULL, 0, NULL, This->node.doc, &new_attr); if(FAILED(hres)) return hres; hres = VariantCopy(&new_attr->value, &This->value); @@ -486,11 +565,60 @@ static const IHTMLDOMAttribute3Vtbl HTMLDOMAttribute3Vtbl = { HTMLDOMAttribute3_get_ownerElement };
+static inline HTMLDOMAttribute *impl_from_HTMLDOMNode(HTMLDOMNode *iface) +{ + return CONTAINING_RECORD(iface, HTMLDOMAttribute, node); +} + static inline HTMLDOMAttribute *impl_from_DispatchEx(DispatchEx *iface) { return CONTAINING_RECORD(iface, HTMLDOMAttribute, node.event_target.dispex); }
+static HRESULT HTMLDOMAttribute_clone(HTMLDOMNode *iface, nsIDOMNode *nsnode, HTMLDOMNode **ret) +{ + HTMLDOMAttribute *This = impl_from_HTMLDOMNode(iface); + HTMLDOMAttribute *new_attr; + nsIDOMAttr *nsattr; + nsAString nsstr; + nsresult nsres; + HRESULT hres; + + nsres = nsIDOMNode_QueryInterface(nsnode, &IID_nsIDOMAttr, (void**)&nsattr); + if(NS_FAILED(nsres)) { + ERR("no nsIDOMAttr iface\n"); + return E_FAIL; + } + + nsAString_InitDepend(&nsstr, NULL); + nsres = nsIDOMAttr_GetName(nsattr, &nsstr); + if(NS_FAILED(nsres)) + hres = map_nsresult(nsres); + else { + const PRUnichar *name; + nsAString_GetData(&nsstr, &name); + hres = HTMLDOMAttribute_Create(name, NULL, 0, nsattr, This->node.doc, &new_attr); + } + nsAString_Finish(&nsstr); + nsIDOMAttr_Release(nsattr); + if(FAILED(hres)) + return hres; + + hres = IHTMLDOMAttribute_get_nodeValue(&This->IHTMLDOMAttribute_iface, &new_attr->value); + if(FAILED(hres)) + return hres; + + *ret = &new_attr->node; + return S_OK; +} + +static const cpc_entry_t HTMLDOMAttribute_cpc[] = {{NULL}}; + +static const NodeImplVtbl HTMLDOMAttributeImplVtbl = { + .cpc_entries = HTMLDOMAttribute_cpc, + .clone = HTMLDOMAttribute_clone, +}; + static void *HTMLDOMAttribute_query_interface(DispatchEx *dispex, REFIID riid) { HTMLDOMAttribute *This = impl_from_DispatchEx(dispex); @@ -501,6 +629,8 @@ static void *HTMLDOMAttribute_query_interface(DispatchEx *dispex, REFIID riid) return &This->IHTMLDOMAttribute2_iface; if(IsEqualGUID(&IID_IHTMLDOMAttribute3, riid)) return &This->IHTMLDOMAttribute3_iface; + if(This->node.nsnode) + return HTMLDOMNode_query_interface(&This->node.event_target.dispex, riid);
return NULL; } @@ -510,6 +640,8 @@ static void HTMLDOMAttribute_traverse(DispatchEx *dispex, nsCycleCollectionTrave HTMLDOMAttribute *This = impl_from_DispatchEx(dispex); HTMLDOMNode_traverse(&This->node.event_target.dispex, cb);
+ if(This->nsattr) + note_cc_edge((nsISupports*)This->nsattr, "nsattr", cb); if(This->elem) note_cc_edge((nsISupports*)&This->elem->node.IHTMLDOMNode_iface, "elem", cb); traverse_variant(&This->value, "value", cb); @@ -526,6 +658,7 @@ static void HTMLDOMAttribute_unlink(DispatchEx *dispex) IHTMLDOMNode_Release(&elem->node.IHTMLDOMNode_iface); } unlink_variant(&This->value); + unlink_ref(&This->nsattr); }
static void HTMLDOMAttribute_destructor(DispatchEx *dispex) @@ -547,6 +680,8 @@ static void HTMLDOMAttribute_init_dispex_info(dispex_data_t *info, compat_mode_t { if(mode >= COMPAT_MODE_IE8) dispex_info_add_interface(info, IHTMLDOMAttribute3_tid, NULL); + if(mode >= COMPAT_MODE_IE9) + HTMLDOMNode_init_dispex_info(info, mode); }
static const tid_t HTMLDOMAttribute_iface_tids[] = { @@ -568,7 +703,7 @@ HTMLDOMAttribute *unsafe_impl_from_IHTMLDOMAttribute(IHTMLDOMAttribute *iface) return iface->lpVtbl == &HTMLDOMAttributeVtbl ? impl_from_IHTMLDOMAttribute(iface) : NULL; }
-HRESULT HTMLDOMAttribute_Create(const WCHAR *name, HTMLElement *elem, DISPID dispid, +HRESULT HTMLDOMAttribute_Create(const WCHAR *name, HTMLElement *elem, DISPID dispid, nsIDOMAttr *nsattr, HTMLDocumentNode *doc, HTMLDOMAttribute **attr) { HTMLAttributeCollection *col; @@ -585,23 +720,33 @@ HRESULT HTMLDOMAttribute_Create(const WCHAR *name, HTMLElement *elem, DISPID dis ret->dispid = dispid; ret->elem = elem;
- init_dispatch(&ret->node.event_target.dispex, &Attr_dispex, doc->script_global, - dispex_compat_mode(&doc->script_global->event_target.dispex)); - - /* For attributes attached to an element, (elem,dispid) pair should be valid used for its operation. */ + /* For attributes attached to an element, (elem,dispid) pair should + * be valid used for its operation if we don't use a gecko node. */ if(elem) { - IHTMLDOMNode_AddRef(&elem->node.IHTMLDOMNode_iface); - hres = HTMLElement_get_attr_col(&elem->node, &col); if(FAILED(hres)) { - IHTMLDOMAttribute_Release(&ret->IHTMLDOMAttribute_iface); + free(ret); return hres; } + IHTMLDOMNode_AddRef(&elem->node.IHTMLDOMNode_iface); IHTMLAttributeCollection_Release(&col->IHTMLAttributeCollection_iface);
list_add_tail(&elem->attrs->attrs, &ret->entry); }
+ if(nsattr) { + ret->node.vtbl = &HTMLDOMAttributeImplVtbl; + ret->nsattr = nsattr; + nsIDOMAttr_AddRef(nsattr); + HTMLDOMNode_Init(doc, &ret->node, (nsIDOMNode*)nsattr, &Attr_dispex); + }else { + ret->node.doc = doc; + IHTMLDOMNode_AddRef(&doc->node.IHTMLDOMNode_iface); + + init_dispatch(&ret->node.event_target.dispex, &Attr_dispex, doc->script_global, + dispex_compat_mode(&doc->script_global->event_target.dispex)); + } + /* For detached attributes we may still do most operations if we have its name available. */ if(name) { ret->name = SysAllocString(name); @@ -611,9 +756,6 @@ HRESULT HTMLDOMAttribute_Create(const WCHAR *name, HTMLElement *elem, DISPID dis } }
- ret->node.doc = doc; - IHTMLDOMNode_AddRef(&doc->node.IHTMLDOMNode_iface); - *attr = ret; return S_OK; } diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 3c3c344309a..c02b020f39c 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -3089,12 +3089,25 @@ static HRESULT WINAPI HTMLDocument5_createAttribute(IHTMLDocument5 *iface, BSTR IHTMLDOMAttribute **ppattribute) { HTMLDocumentNode *This = impl_from_IHTMLDocument5(iface); + nsIDOMAttr *nsattr = NULL; HTMLDOMAttribute *attr; + nsresult nsres; + nsAString str; HRESULT hres;
TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrattrName), ppattribute);
- hres = HTMLDOMAttribute_Create(bstrattrName, NULL, 0, This, &attr); + if(dispex_compat_mode(&This->node.event_target.dispex) >= COMPAT_MODE_IE9) { + nsAString_InitDepend(&str, bstrattrName); + nsres = nsIDOMDocument_CreateAttribute(This->dom_document, &str, &nsattr); + nsAString_Finish(&str); + if(NS_FAILED(nsres)) + return map_nsresult(nsres); + } + + hres = HTMLDOMAttribute_Create(bstrattrName, NULL, 0, nsattr, This, &attr); + if(nsattr) + nsIDOMAttr_Release(nsattr); if(FAILED(hres)) return hres;
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index 661c12428fa..c31c8299a50 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -43,10 +43,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
struct attr_id { DISPID dispid; + nsIDOMAttr *nsattr; /* used for gecko attrs when it's not NULL */ };
struct attr_enum_id { DISPID dispid; + UINT32 pos; /* used for gecko attrs when it's not ~0 */ };
typedef struct { @@ -331,6 +333,20 @@ static HRESULT element_remove_attribute(HTMLElement *element, const WCHAR *name) return map_nsresult(nsres); }
+static HRESULT fill_attr_name_from_nsattr(HTMLDOMAttribute *attr) +{ + nsAString nsstr; + nsAString_InitDepend(&nsstr, NULL); + return return_nsstr(nsIDOMAttr_GetName(attr->nsattr, &nsstr), &nsstr, &attr->name); +} + +static HRESULT fill_attr_value_from_nsattr(HTMLDOMAttribute *attr) +{ + nsAString nsstr; + nsAString_InitDepend(&nsstr, NULL); + return return_nsstr_variant(nsIDOMAttr_GetValue(attr->nsattr, &nsstr), &nsstr, 0, &attr->value); +} + HRESULT get_readystate_string(READYSTATE readystate, BSTR *p) { static const LPCWSTR readystate_strs[] = { @@ -553,6 +569,19 @@ static HTMLDOMAttribute *find_attr_in_list(HTMLAttributeCollection *attrs, DISPI return ret; }
+static HRESULT find_attr_in_list_by_nsattr(HTMLElement *elem, nsIDOMAttr *nsattr, HTMLDOMAttribute **ret) +{ + HTMLDOMNode *node; + HRESULT hres; + + hres = get_node((nsIDOMNode*)nsattr, FALSE, &node); + if(FAILED(hres)) + return hres; + + *ret = node ? CONTAINING_RECORD(node, HTMLDOMAttribute, node) : NULL; + return S_OK; +} + typedef struct { DispatchEx dispex; IHTMLRect IHTMLRect_iface; @@ -1335,15 +1364,17 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA { HTMLElement *This = impl_from_IHTMLElement(iface); compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex); + HTMLAttributeCollection *attrs; + nsIDOMAttr *nsattr, *oldnsattr; + HTMLDOMAttribute *attr; + nsAString nsstr; + nsresult nsres; DISPID id; HRESULT hres;
TRACE("(%p)->(%s %lx %p)\n", This, debugstr_w(strAttributeName), lFlags, pfSuccess);
if(compat_mode < COMPAT_MODE_IE9 || !This->dom_element) { - HTMLAttributeCollection *attrs; - HTMLDOMAttribute *attr; - hres = dispex_get_id(&This->node.event_target.dispex, translate_attr_name(strAttributeName, compat_mode), lFlags & ATTRFLAG_CASESENSITIVE ? fdexNameCaseSensitive : fdexNameCaseInsensitive, &id); if(hres == DISP_E_UNKNOWNNAME) { @@ -1398,9 +1429,42 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA return remove_attribute(&This->node.event_target.dispex, id, pfSuccess); }
- *pfSuccess = element_has_attribute(This, strAttributeName); - if(*pfSuccess) - return element_remove_attribute(This, strAttributeName); + nsAString_InitDepend(&nsstr, strAttributeName); + nsres = nsIDOMElement_GetAttributeNode(This->dom_element, &nsstr, &nsattr); + nsAString_Finish(&nsstr); + if(NS_FAILED(nsres) || !nsattr) { + *pfSuccess = VARIANT_FALSE; + return S_OK; + } + + hres = find_attr_in_list_by_nsattr(This, nsattr, &attr); + if(FAILED(hres)) + return hres; + + if(attr) { + hres = fill_attr_name_from_nsattr(attr); + if(SUCCEEDED(hres)) + hres = fill_attr_value_from_nsattr(attr); + if(SUCCEEDED(hres)) { + list_remove(&attr->entry); + IHTMLDOMNode_Release(&attr->elem->node.IHTMLDOMNode_iface); + attr->elem = NULL; + } + IHTMLDOMAttribute_Release(&attr->IHTMLDOMAttribute_iface); + if(FAILED(hres)) { + SysFreeString(attr->name); + attr->name = NULL; + return hres; + } + } + + nsres = nsIDOMElement_RemoveAttributeNode(This->dom_element, nsattr, &oldnsattr); + nsIDOMAttr_Release(nsattr); + if(FAILED(nsres)) + return map_nsresult(nsres); + if(oldnsattr) + nsIDOMAttr_Release(oldnsattr); + *pfSuccess = VARIANT_TRUE; return S_OK; }
@@ -4441,9 +4505,10 @@ static HRESULT WINAPI HTMLElement4_setAttributeNode(IHTMLElement4 *iface, IHTMLD IHTMLDOMAttribute **ppretAttribute) { HTMLElement *This = impl_from_IHTMLElement4(iface); - HTMLDOMAttribute *attr, *replace; + HTMLDOMAttribute *attr, *replace = NULL; HTMLAttributeCollection *attrs; - DISPID dispid; + nsIDOMAttr *oldnsattr; + nsresult nsres; HRESULT hres;
TRACE("(%p)->(%p %p)\n", This, pattr, ppretAttribute); @@ -4457,21 +4522,52 @@ static HRESULT WINAPI HTMLElement4_setAttributeNode(IHTMLElement4 *iface, IHTMLD return E_INVALIDARG; }
- hres = dispex_get_id(&This->node.event_target.dispex, attr->name, fdexNameCaseInsensitive | fdexNameEnsure, &dispid); - if(FAILED(hres)) - return hres; - hres = HTMLElement_get_attr_col(&This->node, &attrs); if(FAILED(hres)) return hres;
- replace = find_attr_in_list(attrs, dispid, NULL); - if(replace) { - hres = get_elem_attr_value_by_dispid(This, dispid, &replace->value); + if(attrs->nsattrs) { + nsIDOMAttr *nsattr = attr->nsattr; + if(!nsattr) { + FIXME("Trying to place a legacy attr into the IE9+ collection\n"); + IHTMLAttributeCollection_Release(&attrs->IHTMLAttributeCollection_iface); + return E_NOTIMPL; + } + nsres = nsIDOMMozNamedAttrMap_SetNamedItem(attrs->nsattrs, nsattr, &oldnsattr); + if(NS_FAILED(nsres)) { + IHTMLAttributeCollection_Release(&attrs->IHTMLAttributeCollection_iface); + return map_nsresult(nsres); + } + attr->nsattr = nsattr; + attr->dispid = DISPID_UNKNOWN; + + if(oldnsattr) { + hres = find_attr_in_list_by_nsattr(This, oldnsattr, &replace); + if(SUCCEEDED(hres) && replace) { + hres = fill_attr_value_from_nsattr(replace); + if(FAILED(hres)) { + WARN("could not get attr value: %08lx\n", hres); + V_VT(&replace->value) = VT_EMPTY; + } + } + } + }else { + hres = dispex_get_id(&This->node.event_target.dispex, attr->name, fdexNameCaseInsensitive | fdexNameEnsure, &attr->dispid); if(FAILED(hres)) { - WARN("could not get attr value: %08lx\n", hres); - V_VT(&replace->value) = VT_EMPTY; + IHTMLAttributeCollection_Release(&attrs->IHTMLAttributeCollection_iface); + return hres; + } + replace = find_attr_in_list(attrs, attr->dispid, NULL); + if(replace) { + hres = get_elem_attr_value_by_dispid(This, attr->dispid, &replace->value); + if(FAILED(hres)) { + WARN("could not get attr value: %08lx\n", hres); + V_VT(&replace->value) = VT_EMPTY; + } } + } + + if(replace) { if(!replace->name) { replace->name = attr->name; attr->name = NULL; @@ -4486,13 +4582,14 @@ static HRESULT WINAPI HTMLElement4_setAttributeNode(IHTMLElement4 *iface, IHTMLD
IHTMLDOMAttribute_AddRef(&attr->IHTMLDOMAttribute_iface); attr->elem = This; - attr->dispid = dispid;
IHTMLAttributeCollection_Release(&attrs->IHTMLAttributeCollection_iface);
- hres = set_elem_attr_value_by_dispid(This, dispid, &attr->value); - if(FAILED(hres)) - WARN("Could not set attribute value: %08lx\n", hres); + if(!attrs->nsattrs) { + hres = set_elem_attr_value_by_dispid(This, attr->dispid, &attr->value); + if(FAILED(hres)) + WARN("Could not set attribute value: %08lx\n", hres); + } VariantClear(&attr->value);
*ppretAttribute = replace ? &replace->IHTMLDOMAttribute_iface : NULL; @@ -6729,18 +6826,18 @@ static const dispex_hook_t elem_traversal_noattr_hooks[] = {
void HTMLElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { - static const dispex_hook_t elem6_ie9_hooks[] = { - {DISPID_IHTMLELEMENT6_SETATTRIBUTENS, IHTMLElement6_setAttributeNS_hook}, - {DISPID_UNKNOWN} - }; static const dispex_hook_t elem6_ie10_hooks[] = { {DISPID_IHTMLELEMENT6_HASATTRIBUTENS, IHTMLElement6_hasAttributeNS_hook}, {DISPID_IHTMLELEMENT6_GETATTRIBUTENS, IHTMLElement6_getAttributeNS_hook}, - {DISPID_IHTMLELEMENT6_SETATTRIBUTENS, IHTMLElement6_setAttributeNS_hook}, {DISPID_IHTMLELEMENT6_REMOVEATTRIBUTENS, IHTMLElement6_removeAttributeNS_hook}, {DISPID_IHTMLELEMENT6_IE9_SETATTRIBUTE, IHTMLElement6_setAttribute_hook}, + + /* IE9+ */ + {DISPID_IHTMLELEMENT6_SETATTRIBUTENS, IHTMLElement6_setAttributeNS_hook}, + {DISPID_IHTMLELEMENT6_IE9_TAGNAME, .noattr = TRUE}, {DISPID_UNKNOWN} }; + const dispex_hook_t *const elem6_ie9_hooks = elem6_ie10_hooks + 4; static const dispex_hook_t elem_ie11_hooks[] = { {DISPID_IHTMLELEMENT_ONBEFOREUPDATE}, {DISPID_IHTMLELEMENT_ONAFTERUPDATE}, @@ -7807,34 +7904,62 @@ static inline BOOL is_valid_attr_dispid(HTMLAttributeCollection *col, DISPID id, static struct attr_id attr_enum_id_to_attr_id(HTMLAttributeCollection *This, struct attr_enum_id enum_id) { struct attr_id ret = { .dispid = enum_id.dispid }; + if(enum_id.pos != ~0) { + nsresult nsres = nsIDOMMozNamedAttrMap_Item(This->nsattrs, enum_id.pos, &ret.nsattr); + assert(nsres == NS_OK && ret.nsattr != NULL); + } return ret; }
static HRESULT get_attr_enum_id_by_relative_idx(HTMLAttributeCollection *This, LONG *idx, struct attr_enum_id start, BOOL specified_only, struct attr_enum_id *ret) { + nsIDOMMozNamedAttrMap *nsattrs = This->nsattrs; DISPID id = start.dispid; + UINT32 pos = start.pos; + UINT32 length; LONG len = -1; HRESULT hres;
FIXME("filter non-enumerable attributes out\n");
- while(1) { - hres = dispex_next_id(&This->elem->node.event_target.dispex, id, FALSE, &id); - if(FAILED(hres)) - return hres; - else if(hres == S_FALSE) - break; - else if(!is_valid_attr_dispid(This, id, specified_only)) - continue; + if(pos != ~0) + pos++; + else if(nsattrs) { + if(!specified_only) + FIXME("enumerating non-specified attributes in IE9+ mode not implemented"); + pos = 0; + }else { + while(1) { + hres = dispex_next_id(&This->elem->node.event_target.dispex, id, FALSE, &id); + if(FAILED(hres)) + return hres; + else if(hres == S_FALSE) + break; + else if(!is_valid_attr_dispid(This, id, specified_only)) + continue;
- len++; - if(len == *idx) - break; + len++; + if(len == *idx) + break; + } + } + + if(pos != ~0) { + nsIDOMMozNamedAttrMap_GetLength(nsattrs, &length); + + if(ret) { + ret->pos = pos + *idx; + return (length > ret->pos) ? S_OK : DISP_E_UNKNOWNNAME; + } + + *idx = (length > pos) ? length - pos : 0; + return S_OK; }
if(ret) { ret->dispid = id; + ret->pos = pos; return *idx==len ? S_OK : DISP_E_UNKNOWNNAME; }
@@ -7844,7 +7969,7 @@ static HRESULT get_attr_enum_id_by_relative_idx(HTMLAttributeCollection *This, L
static HRESULT get_attr_id_by_idx(HTMLAttributeCollection *This, LONG *idx, BOOL specified_only, struct attr_id *ret) { - struct attr_enum_id enum_id, start = { .dispid = DISPID_STARTENUM }; + struct attr_enum_id enum_id, start = { .dispid = DISPID_STARTENUM, .pos = ~0 }; HRESULT hres;
if(!ret) @@ -7858,6 +7983,8 @@ static HRESULT get_attr_id_by_idx(HTMLAttributeCollection *This, LONG *idx, BOOL
static HRESULT get_attr_id_by_name(HTMLAttributeCollection *This, const WCHAR *name, BOOL specified_only, struct attr_id *ret) { + nsAString nsstr; + nsresult nsres; HRESULT hres;
if(name[0]>='0' && name[0]<='9') { @@ -7871,17 +7998,59 @@ static HRESULT get_attr_id_by_name(HTMLAttributeCollection *This, const WCHAR *n return hres; } } + ret->nsattr = NULL; + + if(!This->nsattrs) + hres = dispex_get_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, &ret->dispid); + else { + nsAString_InitDepend(&nsstr, name); + nsres = nsIDOMMozNamedAttrMap_GetNamedItem(This->nsattrs, &nsstr, &ret->nsattr); + nsAString_Finish(&nsstr); + if(NS_FAILED(nsres)) + return map_nsresult(nsres); + + if(ret->nsattr) + return S_OK; + if(specified_only) + return DISP_E_UNKNOWNNAME; + + FIXME("retrieving non-specified attributes in IE9+ mode not implemented"); + return DISP_E_UNKNOWNNAME; + }
- hres = dispex_get_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, &ret->dispid); return (FAILED(hres) || is_valid_attr_dispid(This, ret->dispid, specified_only)) ? hres : DISP_E_UNKNOWNNAME; }
+/* Gets the DOMAttribute associated with the attr_id, and releases the attr_id even on failure */ static HRESULT get_domattr(HTMLAttributeCollection *This, struct attr_id attr_id, LONG *list_pos, HTMLDOMAttribute **attr) { - HRESULT hres; + HRESULT hres = S_OK; + HTMLDOMNode *node;
- if(!(*attr = find_attr_in_list(This, attr_id.dispid, list_pos))) { - hres = HTMLDOMAttribute_Create(NULL, This->elem, attr_id.dispid, This->elem->node.doc, attr); + if(attr_id.nsattr) { + hres = get_node((nsIDOMNode*)attr_id.nsattr, FALSE, &node); + if(SUCCEEDED(hres)) { + if(node) + *attr = CONTAINING_RECORD(node, HTMLDOMAttribute, node); + else + hres = HTMLDOMAttribute_Create(NULL, This->elem, DISPID_UNKNOWN, attr_id.nsattr, This->elem->node.doc, attr); + } + nsIDOMAttr_Release(attr_id.nsattr); + if(FAILED(hres)) + return hres; + + if(list_pos) { + HTMLDOMAttribute *iter; + + *list_pos = 0; + LIST_FOR_EACH_ENTRY(iter, &This->attrs, HTMLDOMAttribute, entry) { + if(iter == *attr) + break; + (*list_pos)++; + } + } + }else if(!(*attr = find_attr_in_list(This, attr_id.dispid, list_pos))) { + hres = HTMLDOMAttribute_Create(NULL, This->elem, attr_id.dispid, NULL, This->elem->node.doc, attr); if(FAILED(hres)) return hres; } @@ -8020,6 +8189,7 @@ static HRESULT WINAPI HTMLAttributeCollectionEnum_Reset(IEnumVARIANT *iface) TRACE("(%p)->()\n", This);
This->iter_attr_id.dispid = DISPID_STARTENUM; + This->iter_attr_id.pos = ~0; return S_OK; }
@@ -8075,6 +8245,7 @@ static HRESULT WINAPI HTMLAttributeCollection__newEnum(IHTMLAttributeCollection ret->IEnumVARIANT_iface.lpVtbl = &HTMLAttributeCollectionEnumVtbl; ret->ref = 1; ret->iter_attr_id.dispid = DISPID_STARTENUM; + ret->iter_attr_id.pos = ~0;
HTMLAttributeCollection_AddRef(&This->IHTMLAttributeCollection_iface); ret->col = This; @@ -8295,6 +8466,8 @@ static void HTMLAttributeCollection_traverse(DispatchEx *dispex, nsCycleCollecti note_cc_edge((nsISupports*)&attr->IHTMLDOMAttribute_iface, "attr", cb); if(This->elem) note_cc_edge((nsISupports*)&This->elem->node.IHTMLDOMNode_iface, "elem", cb); + if(This->nsattrs) + note_cc_edge((nsISupports*)This->nsattrs, "nsattrs", cb); }
static void HTMLAttributeCollection_unlink(DispatchEx *dispex) @@ -8311,6 +8484,7 @@ static void HTMLAttributeCollection_unlink(DispatchEx *dispex) This->elem = NULL; IHTMLDOMNode_Release(&elem->node.IHTMLDOMNode_iface); } + unlink_ref(&This->nsattrs); }
static void HTMLAttributeCollection_destructor(DispatchEx *dispex) @@ -8405,6 +8579,7 @@ dispex_static_data_t NamedNodeMap_dispex = { HRESULT HTMLElement_get_attr_col(HTMLDOMNode *iface, HTMLAttributeCollection **ac) { HTMLElement *This = impl_from_HTMLDOMNode(iface); + compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex);
if(This->attrs) { IHTMLAttributeCollection_AddRef(&This->attrs->IHTMLAttributeCollection_iface); @@ -8423,8 +8598,10 @@ HRESULT HTMLElement_get_attr_col(HTMLDOMNode *iface, HTMLAttributeCollection **a IHTMLDOMNode_AddRef(&This->node.IHTMLDOMNode_iface); This->attrs->elem = This; list_init(&This->attrs->attrs); - init_dispatch(&This->attrs->dispex, &NamedNodeMap_dispex, This->node.doc->script_global, - dispex_compat_mode(&This->node.event_target.dispex)); + init_dispatch(&This->attrs->dispex, &NamedNodeMap_dispex, This->node.doc->script_global, compat_mode); + + if(compat_mode >= COMPAT_MODE_IE9 && This->dom_element) + nsIDOMElement_GetAttributes(This->dom_element, &This->attrs->nsattrs);
*ac = This->attrs; IHTMLAttributeCollection_AddRef(&This->attrs->IHTMLAttributeCollection_iface); diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 270990f249a..025805fc3f1 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1330,16 +1330,21 @@ struct HTMLAttributeCollection { IHTMLAttributeCollection2 IHTMLAttributeCollection2_iface; IHTMLAttributeCollection3 IHTMLAttributeCollection3_iface;
+ nsIDOMMozNamedAttrMap *nsattrs; HTMLElement *elem; struct list attrs; };
typedef struct { + /* nsnode is valid when gecko attribute nodes are used in IE9+ attr nodes */ HTMLDOMNode node; IHTMLDOMAttribute IHTMLDOMAttribute_iface; IHTMLDOMAttribute2 IHTMLDOMAttribute2_iface; IHTMLDOMAttribute3 IHTMLDOMAttribute3_iface;
+ /* nsattr is the associated gecko node, always valid on IE9+ collections */ + nsIDOMAttr *nsattr; + /* value is valid only for detached attributes (when elem == NULL). */ VARIANT value; /* name must be valid for detached attributes */ @@ -1352,7 +1357,7 @@ typedef struct {
HTMLDOMAttribute *unsafe_impl_from_IHTMLDOMAttribute(IHTMLDOMAttribute*);
-HRESULT HTMLDOMAttribute_Create(const WCHAR*,HTMLElement*,DISPID,HTMLDocumentNode*,HTMLDOMAttribute**); +HRESULT HTMLDOMAttribute_Create(const WCHAR*,HTMLElement*,DISPID,nsIDOMAttr*,HTMLDocumentNode*,HTMLDOMAttribute**);
HRESULT HTMLElement_Create(HTMLDocumentNode*,nsIDOMNode*,BOOL,HTMLElement**); HRESULT HTMLCommentElement_Create(HTMLDocumentNode*,nsIDOMNode*,HTMLElement**); diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index e231c4c090c..75dd87ebdf2 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -580,7 +580,16 @@ sync_test("attr_props", function() {
elem.innerHTML = '<span id="test"></span>'; elem = elem.getElementsByTagName("span")[0]; - attr = elem.attributes[0]; + attr = elem.getAttributeNode("id"); + + if(v < 8) + ok(elem.attributes.length > 50, "attributes.length = " + elem.attributes.length); + else { + todo_wine_if(v === 8). + ok(elem.attributes.length === 1, "attributes.length = " + elem.attributes.length); + todo_wine_if(v === 8). + ok(elem.attributes[0] === attr, "attributes[0] != attr"); + }
function test_exposed(prop, expect) { if(expect) @@ -589,6 +598,13 @@ sync_test("attr_props", function() { ok(!(prop in attr), prop + " found in attribute."); }
+ function test_attr(expando, specified) { + var r = attr.expando; + ok(r === expando, attr.name + " attr.expando = " + r); + r = attr.specified; + ok(r === specified, attr.name + " attr.specified = " + r); + } + test_exposed("appendChild", true); test_exposed("attributes", true); test_exposed("childNodes", true); @@ -622,6 +638,23 @@ sync_test("attr_props", function() { test_exposed("specified", true); test_exposed("textContent", v >= 9); test_exposed("value", true); + test_attr(false, true); + + elem.setAttribute("test", "wine"); + elem.setAttribute("z-index", "foobar"); + + attr = elem.getAttributeNode("test"); + test_attr(true, true); + + attr = elem.getAttributeNode("z-index"); + test_attr(true, true); + + attr = elem.getAttributeNode("tabIndex"); + if(v < 8) + test_attr(false, false); + else + todo_wine_if(v === 8). + ok(attr === null, "tabIndex attr not null."); });
sync_test("doc_props", function() { diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index d7155f32a7c..5b250c1305c 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -801,7 +801,8 @@ static void _test_disp(unsigned line, IUnknown *unk, const IID *diid, const CLSI static void _set_dispex_value(unsigned line, IUnknown *unk, const WCHAR *name, VARIANT *val) { IDispatchEx *dispex = _get_dispex_iface(line, unk); - DISPPARAMS dp = {val, NULL, 1, 0}; + static DISPID propput_dispid = DISPID_PROPERTYPUT; + DISPPARAMS dp = {val, &propput_dispid, 1, 1}; EXCEPINFO ei; DISPID id; BSTR str; @@ -1310,6 +1311,33 @@ static void _test_elem_attr(unsigned line, IHTMLElement *elem, const WCHAR *name VariantClear(&value); }
+#define test_elem_attr_todo(e,n,v) _test_elem_attr_todo(__LINE__,e,n,v) +static void _test_elem_attr_todo(unsigned line, IHTMLElement *elem, const WCHAR *name, const WCHAR *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: %08lx\n", hres); + + if(exval) { + todo_wine + ok_(__FILE__,line) (V_VT(&value) == VT_BSTR, "vt=%d\n", V_VT(&value)); + todo_wine + ok_(__FILE__,line) (!lstrcmpW(V_BSTR(&value), exval), "unexpected value %s\n", wine_dbgstr_w(V_BSTR(&value))); + }else { + todo_wine + ok_(__FILE__,line) (V_VT(&value) == VT_NULL, "vt=%d\n", V_VT(&value)); + } + + VariantClear(&value); +} + #define test_elem_offset(a,b) _test_elem_offset(__LINE__,a,b) static void _test_elem_offset(unsigned line, IUnknown *unk, const WCHAR *parent_tag) { @@ -2146,6 +2174,26 @@ static void _test_attr_value(unsigned line, IHTMLDOMAttribute *attr, const WCHAR SysFreeString(val); }
+#define test_attr_value_todo(a,b) _test_attr_value_todo(__LINE__,a,b) +static void _test_attr_value_todo(unsigned line, IHTMLDOMAttribute *attr, const WCHAR *exval) +{ + IHTMLDOMAttribute2 *attr2 = _get_attr2_iface(line, (IUnknown*)attr); + BSTR val; + HRESULT hres; + + hres = IHTMLDOMAttribute2_get_value(attr2, &val); + ok_(__FILE__,line)(hres == S_OK, "get_value failed: %08lx\n", hres); + if(exval) + todo_wine + ok_(__FILE__,line)(!lstrcmpW(val, exval), "value = %s, expected %s\n", wine_dbgstr_w(val), wine_dbgstr_w(exval)); + else + todo_wine + ok_(__FILE__,line)(!val, "value = %s, expected NULL\n", wine_dbgstr_w(val)); + + IHTMLDOMAttribute2_Release(attr2); + SysFreeString(val); +} + #define test_comment_attrs(c) _test_comment_attrs(__LINE__,c) static void _test_comment_attrs(unsigned line, IUnknown *unk) { @@ -3733,7 +3781,9 @@ static LONG test_attr_collection_attr(IDispatch *attr, LONG i) } 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); + todo_wine_if(compat_mode >= COMPAT_IE9) ok(V_VT(&val) == VT_I4, "test: V_VT(&val) = %d\n", V_VT(&val)); + todo_wine_if(compat_mode >= COMPAT_IE9) ok(V_I4(&val) == 1, "test: V_I4(&val) = %ld\n", V_I4(&val)); test_attr_value(dom_attr, L"1"); } else @@ -3758,6 +3808,9 @@ static void test_attr_collection_disp(IDispatch *disp, LONG len, IHTMLElement *e HRESULT hres; BSTR bstr;
+ if(compat_mode >= COMPAT_IE9) + return; + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); ok(hres == S_OK, "QueryInterface failed: %08lx\n", hres);
@@ -5841,6 +5894,10 @@ static void test_attr_GetIDsOfNames(IHTMLDOMAttribute *attr) HRESULT hres; BSTR bstr[3];
+ /* IE9+ has jscript integration and non-static DISPIDs for them */ + if(compat_mode >= COMPAT_IE9) + return; + bstr[0] = SysAllocString(L"insertBefore"); bstr[1] = SysAllocString(L"newChild"); bstr[2] = SysAllocString(L"refChild"); @@ -11195,6 +11252,18 @@ static void test_dom_elements(IHTMLDocument2 *doc) IHTMLElement_Release(div); }
+static void test_attr_ie9(IHTMLDocument2 *doc) +{ + IHTMLElement *div; + + set_body_html(doc, L"<div id="attr" attr1="attr1" attr2 attr3="attr3"></div>"); + div = get_doc_elem_by_id(doc, L"attr"); + + test_attr_collection(div); + + IHTMLElement_Release(div); +} + static void test_create_elems(IHTMLDocument2 *doc) { IHTMLElement *elem, *body, *elem2; @@ -13073,6 +13142,253 @@ static void test_document_mode_after_initnew(void) IHTMLDocument2_Release(doc); }
+static void test_attribute_node_across_modes(void) +{ + IHTMLDOMAttribute *attr, *attr_ie9, *old_attr; + IHTMLElement4 *elem4, *elem4_ie9; + IDispatchEx *dispex, *dispex_ie9; + IHTMLDocument2 *doc, *doc_ie9; + IHTMLElement *elem, *elem_ie9; + IPersistStreamInit *init; + DISPPARAMS dp = { 0 }; + IHTMLDocument6 *doc6; + IStream *stream; + BSTR name, bstr; + DISPID dispid; + HRESULT hres; + HGLOBAL mem; + SIZE_T len; + VARIANT v; + MSG msg; + + notif_doc = doc = create_document(); + if(!doc) + return; + doc_complete = FALSE; + + len = strlen(doc_blank); + mem = GlobalAlloc(0, len); + memcpy(mem, doc_blank, len); + hres = CreateStreamOnHGlobal(mem, TRUE, &stream); + ok(hres == S_OK, "Failed to create stream: %08lx.\n", hres); + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IPersistStreamInit, (void**)&init); + ok(hres == S_OK, "QueryInterface(IID_IPersistStreamInit) failed: %08lx.\n", hres); + + IPersistStreamInit_Load(init, stream); + IPersistStreamInit_Release(init); + IStream_Release(stream); + + set_client_site(doc, TRUE); + do_advise((IUnknown*)doc, &IID_IPropertyNotifySink, (IUnknown*)&PropertyNotifySink); + + while(!doc_complete && GetMessageW(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + notif_doc = doc_ie9 = create_document(); + if(!doc_ie9) { + set_client_site(doc, FALSE); + IHTMLDocument2_Release(doc); + return; + } + doc_complete = FALSE; + + len = strlen(doc_blank_ie9); + mem = GlobalAlloc(0, len); + memcpy(mem, doc_blank_ie9, len); + hres = CreateStreamOnHGlobal(mem, TRUE, &stream); + ok(hres == S_OK, "Failed to create stream: %08lx.\n", hres); + + hres = IHTMLDocument2_QueryInterface(doc_ie9, &IID_IPersistStreamInit, (void**)&init); + ok(hres == S_OK, "QueryInterface(IID_IPersistStreamInit) failed: %08lx.\n", hres); + + IPersistStreamInit_Load(init, stream); + IPersistStreamInit_Release(init); + IStream_Release(stream); + + set_client_site(doc_ie9, TRUE); + do_advise((IUnknown*)doc_ie9, &IID_IPropertyNotifySink, (IUnknown*)&PropertyNotifySink); + + while(!doc_complete && GetMessageW(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IHTMLDocument6, (void**)&doc6); + ok(hres == S_OK, "Could not get IHTMLDocument6 interface: %08lx\n", hres); + V_VT(&v) = VT_EMPTY; + hres = IHTMLDocument6_get_documentMode(doc6, &v); + ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres); + ok(V_VT(&v) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&v)); + ok(V_R4(&v) == 5, "documentMode = %f\n", V_R4(&v)); + IHTMLDocument6_Release(doc6); + + hres = IHTMLDocument2_QueryInterface(doc_ie9, &IID_IHTMLDocument6, (void**)&doc6); + ok(hres == S_OK, "Could not get IHTMLDocument6 interface: %08lx\n", hres); + V_VT(&v) = VT_EMPTY; + hres = IHTMLDocument6_get_documentMode(doc6, &v); + ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres); + ok(V_VT(&v) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&v)); + ok(V_R4(&v) == 9, "documentMode = %f\n", V_R4(&v)); + IHTMLDocument6_Release(doc6); + + elem = doc_get_body(doc); + elem4 = get_elem4_iface((IUnknown*)elem); + dispex = get_dispex_iface((IUnknown*)elem); + elem_ie9 = doc_get_body(doc_ie9); + elem4_ie9 = get_elem4_iface((IUnknown*)elem_ie9); + dispex_ie9 = get_dispex_iface((IUnknown*)elem_ie9); + + name = SysAllocString(L"testattr"); + attr = create_attr((IUnknown*)doc, name); + put_attr_value(attr, L"attr"); + + attr_ie9 = create_attr((IUnknown*)doc_ie9, name); + put_attr_value(attr_ie9, L"attr_ie9"); + + test_attr_value(attr, L"attr"); + test_attr_value(attr_ie9, L"attr_ie9"); + + hres = IHTMLElement_GetIDsOfNames(elem, &IID_NULL, &name, 1, 0, &dispid); + ok(hres == DISP_E_UNKNOWNNAME, "GetIDsOfNames returned: %08lx\n", hres); + hres = IHTMLElement_GetIDsOfNames(elem_ie9, &IID_NULL, &name, 1, 0, &dispid); + ok(hres == DISP_E_UNKNOWNNAME, "GetIDsOfNames returned: %08lx\n", hres); + + hres = IHTMLElement4_setAttributeNode(elem4, attr_ie9, &old_attr); + ok(hres == S_OK, "setAttributeNode failed: %08lx\n", hres); + ok(!old_attr, "old_attr != NULL\n"); + + hres = IHTMLElement4_setAttributeNode(elem4_ie9, attr, &old_attr); + todo_wine + ok(hres == S_OK, "setAttributeNode failed: %08lx\n", hres); + ok(!old_attr, "old_attr != NULL\n"); + + test_attr_value(attr, L"attr"); + test_attr_value(attr_ie9, L"attr_ie9"); + test_elem_attr(elem, name, L"attr_ie9"); + test_elem_attr_todo(elem_ie9, name, L"attr"); + + hres = IHTMLElement_GetIDsOfNames(elem, &IID_NULL, &name, 1, 0, &dispid); + ok(hres == S_OK, "GetIDsOfNames returned: %08lx\n", hres); + hres = IDispatchEx_GetMemberName(dispex, dispid, &bstr); + ok(hres == S_OK, "GetMemberName failed: %08lx\n", hres); + ok(!wcscmp(bstr, name), "GetMemberName returned %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + hres = IHTMLElement_GetIDsOfNames(elem_ie9, &IID_NULL, &name, 1, 0, &dispid); + ok(hres == S_OK || hres == DISP_E_UNKNOWNNAME, "GetIDsOfNames returned: %08lx\n", hres); + if(hres == S_OK) { + hres = IDispatchEx_GetMemberName(dispex_ie9, dispid, &bstr); + todo_wine + ok(hres == E_INVALIDARG, "GetMemberName returned: %08lx\n", hres); + } + IHTMLDOMAttribute_Release(attr_ie9); + IHTMLDOMAttribute_Release(attr); + + attr = create_attr((IUnknown*)doc, name); + put_attr_value(attr, L"wine"); + + attr_ie9 = create_attr((IUnknown*)doc_ie9, name); + put_attr_value(attr_ie9, L"wine_ie9"); + + test_attr_value(attr, L"wine"); + test_attr_value(attr_ie9, L"wine_ie9"); + test_elem_attr(elem, name, L"attr_ie9"); + test_elem_attr_todo(elem_ie9, name, L"attr"); + + hres = IHTMLElement4_setAttributeNode(elem4, attr_ie9, &old_attr); + ok(hres == S_OK, "setAttributeNode failed: %08lx\n", hres); + test_attr_value(old_attr, L"attr_ie9"); + test_attr_value(attr_ie9, L"wine_ie9"); + test_elem_attr(elem, name, L"wine_ie9"); + IHTMLDOMAttribute_Release(old_attr); + + hres = IHTMLElement4_setAttributeNode(elem4_ie9, attr, &old_attr); + todo_wine + ok(hres == S_OK, "setAttributeNode failed: %08lx\n", hres); + test_attr_value_todo(old_attr, L"attr"); + test_attr_value(attr, L"wine"); + test_elem_attr_todo(elem_ie9, name, L"wine"); + IHTMLDOMAttribute_Release(old_attr); + + IDispatchEx_Release(dispex_ie9); + IDispatchEx_Release(dispex); + + /* The attributes function in their actual compat modes, regardless of the element's mode */ + bstr = SysAllocString(L"firstChild"); + dispex = get_dispex_iface((IUnknown*)attr); + dispex_ie9 = get_dispex_iface((IUnknown*)attr_ie9); + + hres = IDispatchEx_GetDispID(dispex, bstr, fdexNameCaseSensitive, &dispid); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + hres = IDispatchEx_InvokeEx(dispex, dispid, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "InvokeEx failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(firstChild) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == NULL, "V_DISPATCH(firstChild) != NULL\n"); + + hres = IDispatchEx_GetDispID(dispex_ie9, bstr, fdexNameCaseSensitive, &dispid); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + hres = IDispatchEx_InvokeEx(dispex_ie9, dispid, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "InvokeEx failed: %08lx\n", hres); + todo_wine + ok(V_VT(&v) == VT_DISPATCH, "V_VT(firstChild) = %d\n", V_VT(&v)); + todo_wine + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(firstChild) == NULL\n"); + VariantClear(&v); + + IHTMLDOMAttribute_Release(attr_ie9); + IHTMLDOMAttribute_Release(attr); + IDispatchEx_Release(dispex_ie9); + IDispatchEx_Release(dispex); + SysFreeString(name); + + /* And obtaining unspecified builtins in IE9 elements with IHTMLElement4's getAttributeNode still has proper node */ + name = SysAllocString(L"title"); + hres = IHTMLElement4_getAttributeNode(elem4_ie9, name, &attr_ie9); + ok(hres == S_OK, "getAttributeNode failed: %08lx\n", hres); + todo_wine + ok(attr_ie9 != NULL, "attr_ie9 = NULL\n"); + if(!attr_ie9) + goto end; + + dispex_ie9 = get_dispex_iface((IUnknown*)attr_ie9); + hres = IDispatchEx_GetDispID(dispex_ie9, bstr, fdexNameCaseSensitive, &dispid); + ok(hres == S_OK, "GetDispID failed: %08lx\n", hres); + hres = IDispatchEx_InvokeEx(dispex_ie9, dispid, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "InvokeEx failed: %08lx\n", hres); + ok(V_VT(&v) == VT_NULL, "V_VT(firstChild) = %d\n", V_VT(&v)); + + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = SysAllocString(L"test"); + hres = IHTMLElement_setAttribute(elem_ie9, name, v, 0); + ok(hres == S_OK, "setAttribute failed: %08lx\n", hres); + VariantClear(&v); + + hres = IDispatchEx_InvokeEx(dispex_ie9, dispid, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL); + ok(hres == S_OK, "InvokeEx failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(firstChild) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) != NULL, "V_DISPATCH(firstChild) == NULL\n"); + IHTMLDOMAttribute_Release(attr_ie9); + IDispatchEx_Release(dispex_ie9); + +end: + SysFreeString(bstr); + SysFreeString(name); + VariantClear(&v); + + IHTMLElement4_Release(elem4_ie9); + IHTMLElement4_Release(elem4); + IHTMLElement_Release(elem_ie9); + IHTMLElement_Release(elem); + + set_client_site(doc_ie9, FALSE); + IHTMLDocument2_Release(doc_ie9); + set_client_site(doc, FALSE); + IHTMLDocument2_Release(doc); +} + static DWORD WINAPI create_document_proc(void *param) { IHTMLDocument2 *doc = NULL; @@ -13180,6 +13496,7 @@ START_TEST(dom) run_domtest(doc_blank_ie9, test_doc_open); run_domtest(doc_blank_ie9, test_dom_elements); run_domtest(doc_blank_ie9, test_about_blank_storage); + run_domtest(doc_blank_ie9, test_attr_ie9); compat_mode = COMPAT_NONE; } run_domtest(noscript_str, test_noscript); @@ -13206,6 +13523,7 @@ START_TEST(dom) test_default_content_charset(); test_document_mode_lock(); test_document_mode_after_initnew(); + test_attribute_node_across_modes(); test_threads();
/* Run this last since it messes with the process-wide user agent */ diff --git a/dlls/mshtml/tests/dom.js b/dlls/mshtml/tests/dom.js index 1ca70c5726d..5a7cf2ae1c0 100644 --- a/dlls/mshtml/tests/dom.js +++ b/dlls/mshtml/tests/dom.js @@ -1044,10 +1044,52 @@ sync_test("importNode", function() { sync_test("attributeNode", function() { document.body.innerHTML = '<div id="test" attr="wine"></div>'; var elem = document.getElementById("test"); - var attr = elem.attributes[0]; + var attr = elem.getAttributeNode("attr");
ok(attr.ownerDocument === document, "ownerDocument = " + attr.ownerDocument); ok(attr.ownerElement === elem, "ownerElement = " + attr.ownerElement); + ok(attr.namespaceURI === null, "namespaceURI = " + attr.namespaceURI); + ok(attr.specified === true, "attr is not specified"); + ok(attr.textContent === "wine", "textContent = " + attr.textContent); + todo_wine. + ok(attr.hasChildNodes() === true, "attr doesn't have child nodes"); + + todo_wine. + ok(attr.childNodes.length === 1, "child count = " + attr.childNodes.length); + if(attr.childNodes.length) { + ok(attr.firstChild.nodeType === 3, "child nodeType = " + attr.firstChild.nodeType); + ok(attr.firstChild.textContent === "wine", "child textContent = " + attr.firstChild.textContent); + } + + elem = document.createElement("span"); + try { + attr.appendChild(elem); + ok(false, "appendChild did not throw"); + }catch(e) { + todo_wine. + ok(e.message === "HierarchyRequestError", "appendChild threw " + e.message); + } + try { + attr.insertBefore(elem, null); + ok(false, "insertBefore did not throw"); + }catch(e) { + todo_wine. + ok(e.message === "HierarchyRequestError", "insertBefore threw " + e.message); + } + try { + attr.replaceChild(elem, attr.firstChild); + ok(false, "replaceChild did not throw"); + }catch(e) { + todo_wine. + ok(e.message === "HierarchyRequestError", "replaceChild threw " + e.message); + } + try { + attr.removeChild(attr.firstChild); + ok(false, "removeChild did not throw"); + }catch(e) { + todo_wine. + ok(e.message === "NotFoundError", "removeChild threw " + e.message); + }
attr = document.createAttribute("id"); ok(attr.ownerDocument === document, "detached attr ownerDocument = " + attr.ownerDocument);
this uses the existing infrastructure to keep track of the attributes in a collection (and so, associate DISPIDs for them, etc), since it also matches native (same DISPID broken behavior when removing attrs) and we already have the code for it.
While I don't think we need full compatibility with legacy-in-compat attributes right now, we do know that the collection exposes different sets of attributes depending on how it's queried. That means we'd need two separate sets to handle it. Your patch replaces the existing set instead. IMO, the straightforward solution would be to use Gecko for the compat set and leave the existing architecture for the legacy mess.
Overall, the patch feels like a mix of various approaches. For example, the name `find_attr_in_list_by_nsattr` doesn’t make sense, it refers to a “list” from the “elem”, but `elem` is an unused argument now. More importantly, the refactoring doesn't add clear value at this stage and just makes things more complicated than they need to be.
I still don't see why we can't start with just thin wrapper for the collection, so I gave it a try myself. What do you think about something like this? https://gitlab.winehq.org/jacek/wine/-/commits/html-attr
On Mon Aug 4 17:40:18 2025 +0000, Jacek Caban wrote:
this uses the existing infrastructure to keep track of the attributes
in a collection (and so, associate DISPIDs for them, etc), since it also matches native (same DISPID broken behavior when removing attrs) and we already have the code for it. While I don't think we need full compatibility with legacy-in-compat attributes right now, we do know that the collection exposes different sets of attributes depending on how it's queried. That means we'd need two separate sets to handle it. Your patch replaces the existing set instead. IMO, the straightforward solution would be to use Gecko for the compat set and leave the existing architecture for the legacy mess. Overall, the patch feels like a mix of various approaches. For example, the name `find_attr_in_list_by_nsattr` doesn’t make sense, it refers to a “list” from the “elem”, but `elem` is an unused argument now. More importantly, the refactoring doesn't add clear value at this stage and just makes things more complicated than they need to be. I still don't see why we can't start with just thin wrapper for the collection, so I gave it a try myself. What do you think about something like this? https://gitlab.winehq.org/jacek/wine/-/commits/html-attr
Sorry about the name, I forgot to rename it from my previous attempt which also looked it up in the list (for legacy attributes mixed in the collection).
The reason I went with my approach instead of a thin wrapper is mostly because it re-uses the existing infrastructure, so that whenever we implement new methods (as your branch does, btw), we won't have to add `if(This->dom_attr)` or checking for IE9 code paths everywhere and deal with all that entails. It's also subtly wrong as in you're not actually testing the COM methods' behavior itself.
For instance, it seems you're using new interface methods to separate legacy/IE9 nodes, but are you sure it's how it's supposed to work? getAttributeNode has changed behavior between interfaces, true, but that also applies to legacy attribute nodes, as well as IE9 nodes. IHTMLElement4's getAttributeNode enumerates "unspecified" builtins in both modes (though it's a FIXME even in my patches for IE9, that's fine), so does IHTMLElement6's not enumerate them in both modes.
In other words, your branch will fail the dom.c attr node tests I added (which checks behavior of the different methods). Sure, that's a simple fix for legacy attrs, but at that point we're duplicating code paths again. And if we ever implement IHTMLElement4's getAttributeNode for IE9 attrs too, it will also require a different code path yet again. (contrast this with my patch which just uses a flag "specified_only")
Another example is test_attr_collection for example in dom.c, if you run a limited version of it in IE9 mode (like in my patches) it will fail some tests since it uses the "older" interface methods, which in your case assume it's legacy collection/nodes, even though they're not.
What I'm trying to say is I don't see where native draws such a "big" or hard line between the COM methods?
That's why I'm not sure if it's a good idea to "separate" implementations based on the interface method. Note that it's also probably the reason it doesn't look like your branch has too many code paths in every method, because you've separated them by the COM method, so instead of having 2 code paths in every method, you now have a separate code path in each method. But that's not correct, in native it seems those COM methods affect both type of attr nodes (legacy/IE9) equally.
So while I think it may seem simpler because of the separation, it's kind of obviously wrong compared to native. And the problem is, it can't be used as a "starting block" either because it will have to be rewritten if that will be a problem at some point.
Lastly, the other thing I'm worried about is because of how you separated the impls, it will be very hard to debug if an app depends on native's behavior at any of those points. In my patches it was easy to test (and in one code path) and I just added FIXMEs where applicable, which will make it obvious (there's 3 of them actually: placing legacy attrs into IE9 collection, enumerating non-specified builtins, and obtaining non-specified builtins, in IE9 modes). You can also mix and match attribute nodes in native in a collection (either legacy or IE9 collection)—and while it's obviously a FIXME even in my patches, at least it does give an idea that native doesn't draw such a hard line between them.
I don't mind a simpler implementation don't get me wrong, but the problem with separate method impls like in your branch is that I can't see how to expand on it to make it more correct in the future. Wouldn't that be a problem?
What I'm trying to say is I don't see where native draws such a "big" or hard line between the COM methods?
Yes, it does. This behavior isn’t new and is easy to test. I believe this addresses most of your concerns. I’ll include the test and prepare an MR, we can discuss the details there.
I don't mind a simpler implementation don't get me wrong, but the problem with separate method impls like in your branch is that I can't see how to expand on it to make it more correct in the future. Wouldn't that be a problem?
We’re talking about a few trivial functions, right? So replacing them would not be of much waste. Even if, hypothetically, we wanted something closer to your solution, we could get there incrementally instead of complicating a single change that’s already large.
Yes, the branch is missing synchronization between the legacy attribute set and the standards-compliant one. I can imagine various ways to address that. My preferred approach would be to keep the main code path simple and efficient and avoid relying on tracking every possible place we think may change, and instead find a solution that doesn’t depend on hoping things won’t change behind our backs. That may require listening to more mutation events, I'm not sure.
Note that with this patch, the synchronization issue isn’t visible to scripts (which make up the overwhelming majority of consumers). They will use only one model at a time. It’s only visible when mixing C APIs and even then, only has minor impact if interfaces are used consistently. That makes the issue non-urgent, IMO.
What I like about this solution is that it keeps the code as simple as possible, and its impact is mostly limited to IE9+, the legacy complicated code is barely changed. Since IE9+ aims to be standards-compliant, using the well-proven Gecko implementation seems like a good fit and a safer bet. And if that’s not enough, we can increment on top of that.
Superseded by !8714
This merge request was closed by Gabriel Ivăncescu.