From: Gabriel Ivăncescu gabrielopcode@gmail.com
Surprisingly, this applies even in IE9+ modes when using IHTMLElement4's methods, but we can't test that yet because it's too broken in wine.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlelem.c | 152 +++++++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/dom.c | 59 ++++++++++++++++ 2 files changed, 210 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index fcc6394c0d2..621bc705da3 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -7677,6 +7677,153 @@ static HRESULT create_filters_collection(compat_mode_t compat_mode, IHTMLFilters return S_OK; }
+static inline BOOL is_valid_attr_dispid(DISPID id) +{ + /* Builtin props that can be treated as attributes; MUST be sorted by underlying dispid value. */ + static const DISPID attr_dispids[] = { + DISPID_IHTMLELEMENT2_TABINDEX, + DISPID_IHTMLELEMENT_TITLE, + DISPID_IHTMLELEMENT_STYLE, + DISPID_IHTMLELEMENT3_DISABLED, + DISPID_IHTMLELEMENT_ID, + DISPID_IHTMLELEMENT2_ACCESSKEY, + DISPID_IHTMLELEMENT_LANG, + DISPID_IHTMLELEMENT_LANGUAGE, + DISPID_IHTMLELEMENT2_DIR, + DISPID_IHTMLELEMENT3_CONTENTEDITABLE, + DISPID_IHTMLELEMENT3_HIDEFOCUS, + DISPID_IHTMLELEMENT7_SPELLCHECK, + DISPID_IHTMLELEMENT_ONMOUSEOVER, + DISPID_IHTMLELEMENT_ONMOUSEOUT, + DISPID_IHTMLELEMENT_ONMOUSEDOWN, + DISPID_IHTMLELEMENT_ONMOUSEUP, + DISPID_IHTMLELEMENT_ONMOUSEMOVE, + DISPID_IHTMLELEMENT_ONKEYDOWN, + DISPID_IHTMLELEMENT_ONKEYUP, + DISPID_IHTMLELEMENT_ONKEYPRESS, + DISPID_IHTMLELEMENT_ONCLICK, + DISPID_IHTMLELEMENT_ONDBLCLICK, + DISPID_IHTMLELEMENT6_ONSELECT, + DISPID_IHTMLELEMENT6_ONSUBMIT, + DISPID_IHTMLELEMENT6_ONRESET, + DISPID_IHTMLELEMENT_ONHELP, + DISPID_IHTMLELEMENT2_ONFOCUS, + DISPID_IHTMLELEMENT2_ONBLUR, + DISPID_IHTMLELEMENT_ONROWEXIT, + DISPID_IHTMLELEMENT_ONROWENTER, + DISPID_IHTMLELEMENT_ONBEFOREUPDATE, + DISPID_IHTMLELEMENT_ONAFTERUPDATE, + DISPID_IHTMLELEMENT2_ONREADYSTATECHANGE, + DISPID_IHTMLELEMENT6_ONABORT, + DISPID_IHTMLELEMENT6_ONERROR, + DISPID_IHTMLELEMENT6_ONCHANGE, + DISPID_IHTMLELEMENT2_ONSCROLL, + DISPID_IHTMLELEMENT6_ONLOAD, + DISPID_IHTMLELEMENT_ONDRAGSTART, + DISPID_IHTMLELEMENT2_ONRESIZE, + DISPID_IHTMLELEMENT_ONSELECTSTART, + DISPID_IHTMLELEMENT_ONERRORUPDATE, + DISPID_IHTMLELEMENT_ONDATASETCHANGED, + DISPID_IHTMLELEMENT_ONDATAAVAILABLE, + DISPID_IHTMLELEMENT_ONDATASETCOMPLETE, + DISPID_IHTMLELEMENT_ONFILTERCHANGE, + DISPID_IHTMLELEMENT2_ONLOSECAPTURE, + DISPID_IHTMLELEMENT2_ONPROPERTYCHANGE, + DISPID_IHTMLELEMENT2_ONDRAG, + DISPID_IHTMLELEMENT2_ONDRAGEND, + DISPID_IHTMLELEMENT2_ONDRAGENTER, + DISPID_IHTMLELEMENT2_ONDRAGOVER, + DISPID_IHTMLELEMENT2_ONDRAGLEAVE, + DISPID_IHTMLELEMENT2_ONDROP, + DISPID_IHTMLELEMENT2_ONCUT, + DISPID_IHTMLELEMENT2_ONCOPY, + DISPID_IHTMLELEMENT2_ONPASTE, + DISPID_IHTMLELEMENT2_ONBEFORECUT, + DISPID_IHTMLELEMENT2_ONBEFORECOPY, + DISPID_IHTMLELEMENT2_ONBEFOREPASTE, + DISPID_IHTMLELEMENT2_ONROWSDELETE, + DISPID_IHTMLELEMENT2_ONROWSINSERTED, + DISPID_IHTMLELEMENT2_ONCELLCHANGE, + DISPID_IHTMLELEMENT2_ONCONTEXTMENU, + DISPID_IHTMLELEMENT2_ONBEFOREEDITFOCUS, + DISPID_IHTMLELEMENT3_ONLAYOUTCOMPLETE, + DISPID_IHTMLELEMENT3_ONPAGE, + DISPID_IHTMLELEMENT4_ONMOUSEWHEEL, + DISPID_IHTMLELEMENT3_ONBEFOREDEACTIVATE, + DISPID_IHTMLELEMENT3_ONMOVE, + DISPID_IHTMLELEMENT3_ONCONTROLSELECT, + DISPID_IHTMLELEMENT3_ONMOVESTART, + DISPID_IHTMLELEMENT3_ONMOVEEND, + DISPID_IHTMLELEMENT3_ONRESIZESTART, + DISPID_IHTMLELEMENT3_ONRESIZEEND, + DISPID_IHTMLELEMENT3_ONMOUSELEAVE, + DISPID_IHTMLELEMENT3_ONACTIVATE, + DISPID_IHTMLELEMENT3_ONDEACTIVATE, + DISPID_IHTMLELEMENT4_ONBEFOREACTIVATE, + DISPID_IHTMLELEMENT4_ONFOCUSIN, + DISPID_IHTMLELEMENT4_ONFOCUSOUT, + DISPID_IHTMLELEMENT6_ONINPUT, + DISPID_IHTMLELEMENT6_ONCANPLAY, + DISPID_IHTMLELEMENT6_ONCANPLAYTHROUGH, + DISPID_IHTMLELEMENT6_ONDURATIONCHANGE, + DISPID_IHTMLELEMENT6_ONEMPTIED, + DISPID_IHTMLELEMENT6_ONENDED, + DISPID_IHTMLELEMENT6_ONLOADEDDATA, + DISPID_IHTMLELEMENT6_ONLOADEDMETADATA, + DISPID_IHTMLELEMENT6_ONLOADSTART, + DISPID_IHTMLELEMENT6_ONPAUSE, + DISPID_IHTMLELEMENT6_ONPLAY, + DISPID_IHTMLELEMENT6_ONPLAYING, + DISPID_IHTMLELEMENT6_ONPROGRESS, + DISPID_IHTMLELEMENT6_ONRATECHANGE, + DISPID_IHTMLELEMENT6_ONSEEKED, + DISPID_IHTMLELEMENT6_ONSEEKING, + DISPID_IHTMLELEMENT6_ONSTALLED, + DISPID_IHTMLELEMENT6_ONSUSPEND, + DISPID_IHTMLELEMENT6_ONTIMEUPDATE, + DISPID_IHTMLELEMENT6_ONVOLUMECHANGE, + DISPID_IHTMLELEMENT6_ONWAITING, + DISPID_IHTMLELEMENT7_ONMSPOINTERDOWN, + DISPID_IHTMLELEMENT7_ONMSPOINTERMOVE, + DISPID_IHTMLELEMENT7_ONMSPOINTERUP, + DISPID_IHTMLELEMENT7_ONMSPOINTEROVER, + DISPID_IHTMLELEMENT7_ONMSPOINTEROUT, + DISPID_IHTMLELEMENT7_ONMSPOINTERCANCEL, + DISPID_IHTMLELEMENT7_ONMSPOINTERHOVER, + DISPID_IHTMLELEMENT7_ONMSGESTURESTART, + DISPID_IHTMLELEMENT7_ONMSGESTURECHANGE, + DISPID_IHTMLELEMENT7_ONMSGESTUREEND, + DISPID_IHTMLELEMENT7_ONMSGESTUREHOLD, + DISPID_IHTMLELEMENT7_ONMSGESTURETAP, + DISPID_IHTMLELEMENT7_ONMSGESTUREDOUBLETAP, + DISPID_IHTMLELEMENT7_ONMSINERTIASTART, + DISPID_IHTMLELEMENT7_ONMSLOSTPOINTERCAPTURE, + DISPID_IHTMLELEMENT7_ONMSGOTPOINTERCAPTURE, + DISPID_IHTMLELEMENT7_ONMSTRANSITIONSTART, + DISPID_IHTMLELEMENT7_ONMSTRANSITIONEND, + DISPID_IHTMLELEMENT7_ONMSANIMATIONSTART, + DISPID_IHTMLELEMENT7_ONMSANIMATIONEND, + DISPID_IHTMLELEMENT7_ONMSANIMATIONITERATION, + DISPID_IHTMLELEMENT7_ONMSMANIPULATIONSTATECHANGED, + DISPID_IHTMLELEMENT7_ONINVALID, + DISPID_IHTMLELEMENT7_ONCUECHANGE + }; + unsigned i, a = 0, b = ARRAY_SIZE(attr_dispids) - 1; + + if(get_dispid_type(id) != DISPEXPROP_BUILTIN) + return TRUE; + + while(a < b) { + i = (a + b) / 2; + if(id == attr_dispids[i]) + return TRUE; + if(id <= attr_dispids[i]) b = i; + else a = i + 1; + } + + return FALSE; +} + static HRESULT get_attr_dispid_by_relative_idx(HTMLAttributeCollection *This, LONG *idx, DISPID start, DISPID *dispid) { DISPID id = start; @@ -7691,6 +7838,8 @@ 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(id)) + continue;
len++; if(len == *idx) @@ -7727,7 +7876,8 @@ static inline HRESULT get_attr_dispid_by_name(HTMLAttributeCollection *This, con } }
- return dispex_get_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, id); + hres = dispex_get_id(&This->elem->node.event_target.dispex, name, fdexNameCaseInsensitive, id); + return (FAILED(hres) || is_valid_attr_dispid(*id)) ? hres : DISP_E_UNKNOWNNAME; }
static inline HRESULT get_domattr(HTMLAttributeCollection *This, DISPID id, LONG *list_pos, HTMLDOMAttribute **attr) diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 9eb7bac9a11..b67ed183d67 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -5116,6 +5116,24 @@ static void _link_put_href(unsigned line, IHTMLElement *elem, const WCHAR *v) _test_link_href(line, elem, v); }
+#define elem_has_attr(a,b) _elem_has_attr(__LINE__,a,b) +static BOOL _elem_has_attr(unsigned line, IUnknown *unk, const WCHAR *attr_name) +{ + BSTR str = SysAllocString(attr_name); + IHTMLElement6 *elem; + VARIANT_BOOL b; + HRESULT hres; + + hres = IUnknown_QueryInterface(unk, &IID_IHTMLElement6, (void**)&elem); + ok_(__FILE__,line)(hres == S_OK, "Could not get IHTMLElement6: %08lx\n", hres); + hres = IHTMLElement6_hasAttribute(elem, str, &b); + ok_(__FILE__,line)(hres == S_OK, "hasAttribute failed: %08lx\n", hres); + IHTMLElement6_Release(elem); + SysFreeString(str); + + return b != VARIANT_FALSE; +} + #define get_elem_doc(e) _get_elem_doc(__LINE__,e) static IHTMLDocument2 *_get_elem_doc(unsigned line, IUnknown *unk) { @@ -10076,8 +10094,31 @@ static void test_attr_node(IHTMLDOMAttribute *test_attr, IHTMLDocument2 *doc)
static void test_attr(IHTMLDocument2 *doc, IHTMLElement *elem) { + static const WCHAR *elem_attr_props[] = { + L"accessKey", L"contentEditable", L"dir", L"disabled", L"hideFocus", L"id", L"lang", L"language", L"onactivate", L"onafterupdate", L"onbeforeactivate", + L"onbeforecopy", L"onbeforecut", L"onbeforedeactivate", L"onbeforeeditfocus", L"onbeforepaste", L"onbeforeupdate", L"onblur", L"oncellchange", L"onclick", + L"oncontextmenu", L"oncontrolselect", L"oncopy", L"oncut", L"ondataavailable", L"ondatasetchanged", L"ondatasetcomplete", L"ondblclick", L"ondeactivate", + L"ondrag", L"ondragend", L"ondragenter", L"ondragleave", L"ondragover", L"ondragstart", L"ondrop", L"onerrorupdate", L"onfilterchange", L"onfocus", + L"onfocusin", L"onfocusout", L"onhelp", L"onkeydown", L"onkeypress", L"onkeyup", L"onlayoutcomplete", L"onlosecapture", L"onmousedown", L"onmouseleave", + L"onmousemove", L"onmouseout", L"onmouseover", L"onmouseup", L"onmousewheel", L"onmove", L"onmoveend", L"onmovestart", L"onpage", L"onpaste", + L"onpropertychange", L"onreadystatechange", L"onresize", L"onresizeend", L"onresizestart", L"onrowenter", L"onrowexit", L"onrowsdelete", + L"onrowsinserted", L"onscroll", L"onselectstart", L"style", L"tabIndex", L"title" + }; + static const WCHAR *elem_noattr_props[] = { + L"addBehavior", L"all", L"applyElement", L"attachEvent", L"attributes", L"behaviorUrns", L"blur", L"canHaveHTML", L"childNodes", L"children", L"className", + L"clearAttributes", L"click", L"clientHeight", L"clientLeft", L"clientTop", L"clientWidth", L"componentFromPoint", L"contains", L"createControlRange", + L"currentStyle", L"detachEvent", L"document", L"doScroll", L"dragDrop", L"filters", L"fireEvent", L"firstChild", L"focus", L"getAdjacentText", L"getAttribute", + L"getAttributeNode", L"getBoundingClientRect", L"getClientRects", L"getElementsByTagName", L"getExpression", L"innerHTML", L"innerText", L"insertAdjacentElement", + L"insertAdjacentHTML", L"insertAdjacentText", L"isContentEditable", L"isDisabled", L"isMultiLine", L"isTextEdit", L"lastChild", L"mergeAttributes", L"nextSibling", + L"nodeName", L"nodeName", L"nodeType", L"nodeValue", L"normalize", L"offsetHeight", L"offsetLeft", L"offsetParent", L"offsetTop", L"offsetWidth", L"outerHTML", + L"outerText", L"ownerDocument", L"parentElement", L"parentNode", L"parentTextEdit", L"previousSibling", L"readyState", L"recordNumber", L"removeAttribute", + L"removeAttributeNode", L"removeBehavior", L"removeExpression", L"replaceAdjacentText", L"runtimeStyle", L"scopeName", L"scrollHeight", L"scrollIntoView", + L"scrollLeft", L"scrollTop", L"scrollWidth", L"setActive", L"setAttribute", L"setAttributeNode", L"setExpression", L"sourceIndex", L"tagName", L"tagUrn", + L"toString", L"uniqueID", L"uniqueNumber" + }; IHTMLDOMAttribute *attr, *attr2, *attr3; IHTMLElement4 *elem4; + unsigned i; VARIANT v; HRESULT hres;
@@ -10165,6 +10206,24 @@ static void test_attr(IHTMLDocument2 *doc, IHTMLElement *elem) test_attr_specified(attr, VARIANT_FALSE); test_attr_expando(attr, VARIANT_FALSE); test_attr_node(attr, doc); + + for(i = 0; i < ARRAY_SIZE(elem_attr_props); i++) { + BOOL has_attr = elem_has_attr((IUnknown*)elem, elem_attr_props[i]); + + attr = get_elem_attr_node((IUnknown*)elem, elem_attr_props[i], TRUE); + test_attr_specified(attr, has_attr ? VARIANT_TRUE : VARIANT_FALSE); + test_attr_expando(attr, VARIANT_FALSE); + IHTMLDOMAttribute_Release(attr); + } + for(i = 0; i < ARRAY_SIZE(elem_noattr_props); i++) { + get_elem_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); + test_attr_specified(attr, VARIANT_TRUE); + test_attr_expando(attr, VARIANT_TRUE); + test_attr_node(attr, doc); IHTMLDOMAttribute_Release(attr);
/* Test created, detached attribute. */