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);