mshtml: Associate props with attributes for IE8 mode, but keep them stringified.
For non-builtin props, getAttribute retrieves the stringified value of the prop. setAttribute also stringifies the value if it's a builtin only. Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com> Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
d224a2a881
commit
6f37e50a1d
|
@ -1067,6 +1067,16 @@ static HRESULT WINAPI HTMLElement_Invoke(IHTMLElement *iface, DISPID dispIdMembe
|
|||
wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
|
||||
}
|
||||
|
||||
static inline WCHAR *translate_attr_name(WCHAR *attr_name, compat_mode_t compat_mode)
|
||||
{
|
||||
static WCHAR classNameW[] = L"className";
|
||||
WCHAR *ret = attr_name;
|
||||
|
||||
if(compat_mode >= COMPAT_MODE_IE8 && !wcsicmp(attr_name, L"class"))
|
||||
ret = classNameW;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static HRESULT set_elem_attr_value_by_dispid(HTMLElement *elem, DISPID dispid, VARIANT *v)
|
||||
{
|
||||
DISPID propput_dispid = DISPID_PROPERTYPUT;
|
||||
|
@ -1088,24 +1098,47 @@ static HRESULT WINAPI HTMLElement_setAttribute(IHTMLElement *iface, BSTR strAttr
|
|||
HTMLElement *This = impl_from_IHTMLElement(iface);
|
||||
compat_mode_t compat_mode = dispex_compat_mode(&This->node.event_target.dispex);
|
||||
nsAString name_str, value_str;
|
||||
VARIANT val = AttributeValue;
|
||||
BOOL needs_free = FALSE;
|
||||
nsresult nsres;
|
||||
DISPID dispid;
|
||||
HRESULT hres;
|
||||
|
||||
TRACE("(%p)->(%s %s %08x)\n", This, debugstr_w(strAttributeName), debugstr_variant(&AttributeValue), lFlags);
|
||||
|
||||
if(compat_mode < COMPAT_MODE_IE8 || !This->dom_element) {
|
||||
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, strAttributeName,
|
||||
if(compat_mode < COMPAT_MODE_IE9 || !This->dom_element) {
|
||||
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, translate_attr_name(strAttributeName, compat_mode),
|
||||
(lFlags&ATTRFLAG_CASESENSITIVE ? fdexNameCaseSensitive : fdexNameCaseInsensitive) | fdexNameEnsure, &dispid);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
return set_elem_attr_value_by_dispid(This, dispid, &AttributeValue);
|
||||
if(compat_mode >= COMPAT_MODE_IE8 && get_dispid_type(dispid) == DISPEXPROP_BUILTIN) {
|
||||
if(V_VT(&val) != VT_BSTR && V_VT(&val) != VT_NULL) {
|
||||
LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT);
|
||||
|
||||
V_VT(&val) = VT_EMPTY;
|
||||
hres = VariantChangeTypeEx(&val, &AttributeValue, lcid, 0, VT_BSTR);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
if(V_BSTR(&val))
|
||||
needs_free = TRUE;
|
||||
else
|
||||
V_VT(&val) = VT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* className and style are special cases */
|
||||
if(compat_mode != COMPAT_MODE_IE8 || !This->dom_element ||
|
||||
(dispid != DISPID_IHTMLELEMENT_CLASSNAME && dispid != DISPID_IHTMLELEMENT_STYLE)) {
|
||||
hres = set_elem_attr_value_by_dispid(This, dispid, &val);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
hres = variant_to_nsstr(&AttributeValue, FALSE, &value_str);
|
||||
hres = variant_to_nsstr(&val, FALSE, &value_str);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
goto done;
|
||||
|
||||
nsAString_InitDepend(&name_str, strAttributeName);
|
||||
nsres = nsIDOMElement_SetAttribute(This->dom_element, &name_str, &value_str);
|
||||
|
@ -1115,6 +1148,9 @@ static HRESULT WINAPI HTMLElement_setAttribute(IHTMLElement *iface, BSTR strAttr
|
|||
WARN("SetAttribute failed: %08x\n", nsres);
|
||||
hres = map_nsresult(nsres);
|
||||
|
||||
done:
|
||||
if(needs_free)
|
||||
SysFreeString(V_BSTR(&val));
|
||||
return hres;
|
||||
}
|
||||
|
||||
|
@ -1169,21 +1205,35 @@ static HRESULT WINAPI HTMLElement_getAttribute(IHTMLElement *iface, BSTR strAttr
|
|||
if(lFlags & ~(ATTRFLAG_CASESENSITIVE|ATTRFLAG_ASSTRING))
|
||||
FIXME("Unsupported flags %x\n", lFlags);
|
||||
|
||||
if(compat_mode < COMPAT_MODE_IE8 || !This->dom_element) {
|
||||
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, strAttributeName,
|
||||
if(compat_mode < COMPAT_MODE_IE9 || !This->dom_element) {
|
||||
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, translate_attr_name(strAttributeName, compat_mode),
|
||||
lFlags&ATTRFLAG_CASESENSITIVE ? fdexNameCaseSensitive : fdexNameCaseInsensitive, &dispid);
|
||||
if(FAILED(hres)) {
|
||||
V_VT(AttributeValue) = VT_NULL;
|
||||
return (hres == DISP_E_UNKNOWNNAME) ? S_OK : hres;
|
||||
}
|
||||
|
||||
hres = get_elem_attr_value_by_dispid(This, dispid, AttributeValue);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
/* className and style are special cases */
|
||||
if(compat_mode != COMPAT_MODE_IE8 || !This->dom_element ||
|
||||
(dispid != DISPID_IHTMLELEMENT_CLASSNAME && dispid != DISPID_IHTMLELEMENT_STYLE)) {
|
||||
hres = get_elem_attr_value_by_dispid(This, dispid, AttributeValue);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
if(lFlags & ATTRFLAG_ASSTRING)
|
||||
hres = attr_value_to_string(AttributeValue);
|
||||
return hres;
|
||||
if(compat_mode >= COMPAT_MODE_IE8 && V_VT(AttributeValue) != VT_BSTR && V_VT(AttributeValue) != VT_NULL) {
|
||||
LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT);
|
||||
|
||||
hres = VariantChangeTypeEx(AttributeValue, AttributeValue, lcid, 0, VT_BSTR);
|
||||
if(FAILED(hres)) {
|
||||
VariantClear(AttributeValue);
|
||||
return hres;
|
||||
}
|
||||
if(!V_BSTR(AttributeValue))
|
||||
V_VT(AttributeValue) = VT_NULL;
|
||||
}else if(lFlags & ATTRFLAG_ASSTRING)
|
||||
hres = attr_value_to_string(AttributeValue);
|
||||
return hres;
|
||||
}
|
||||
}
|
||||
|
||||
nsAString_InitDepend(&name_str, strAttributeName);
|
||||
|
@ -1203,8 +1253,8 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA
|
|||
|
||||
TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(strAttributeName), lFlags, pfSuccess);
|
||||
|
||||
if(compat_mode < COMPAT_MODE_IE8 || !This->dom_element) {
|
||||
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, strAttributeName,
|
||||
if(compat_mode < COMPAT_MODE_IE9 || !This->dom_element) {
|
||||
hres = IDispatchEx_GetDispID(&This->node.event_target.dispex.IDispatchEx_iface, translate_attr_name(strAttributeName, compat_mode),
|
||||
lFlags&ATTRFLAG_CASESENSITIVE ? fdexNameCaseSensitive : fdexNameCaseInsensitive, &id);
|
||||
if(hres == DISP_E_UNKNOWNNAME) {
|
||||
*pfSuccess = VARIANT_FALSE;
|
||||
|
@ -1231,7 +1281,8 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
return remove_attribute(&This->node.event_target.dispex, id, pfSuccess);
|
||||
if(compat_mode != COMPAT_MODE_IE8 || !This->dom_element || id != DISPID_IHTMLELEMENT_CLASSNAME)
|
||||
return remove_attribute(&This->node.event_target.dispex, id, pfSuccess);
|
||||
}
|
||||
|
||||
*pfSuccess = element_has_attribute(This, strAttributeName);
|
||||
|
|
|
@ -518,9 +518,7 @@ sync_test("createElement_inline_attr", function() {
|
|||
for(var i = 0; i < tags.length; i++) {
|
||||
e = document.createElement("<" + tags[i] + " test='a\"' abcd=\""b"\">");
|
||||
ok(e.tagName === tags[i].toUpperCase(), "<" + tags[i] + " test=\"a\" abcd=\"b\">.tagName returned " + e.tagName);
|
||||
todo_wine_if(v == 8).
|
||||
ok(e.test === "a\"", "<" + tags[i] + " test='a\"' abcd=\""b"\">.test returned " + e.test);
|
||||
todo_wine_if(v == 8).
|
||||
ok(e.abcd === "\"b\"", "<" + tags[i] + " test='a\"' abcd=\""b"\">.abcd returned " + e.abcd);
|
||||
}
|
||||
}else {
|
||||
|
@ -1063,6 +1061,13 @@ sync_test("elem_attr", function() {
|
|||
var v = document.documentMode;
|
||||
var elem = document.createElement("div"), r;
|
||||
|
||||
function test_exposed(prop, expect) {
|
||||
if(expect)
|
||||
ok(prop in elem, prop + " is not exposed from elem");
|
||||
else
|
||||
ok(!(prop in elem), prop + " is exposed from elem");
|
||||
}
|
||||
|
||||
r = elem.getAttribute("class");
|
||||
ok(r === null, "class attr = " + r);
|
||||
r = elem.getAttribute("className");
|
||||
|
@ -1088,11 +1093,37 @@ sync_test("elem_attr", function() {
|
|||
r = elem.getAttribute("className");
|
||||
ok(r === "cls3", "className attr = " + r);
|
||||
|
||||
elem.htmlFor = "for";
|
||||
r = elem.getAttribute("for");
|
||||
ok(r === null, "for attr = " + r);
|
||||
r = elem.getAttribute("htmlFor");
|
||||
ok(r === (v < 9 ? "for" : null), "htmlFor attr = " + r);
|
||||
|
||||
elem.setAttribute("for", "for2");
|
||||
ok(elem.htmlFor === "for", "elem.htmlFor = " + elem.htmlFor);
|
||||
r = elem.getAttribute("for");
|
||||
ok(r === "for2", "for attr = " + r);
|
||||
r = elem.getAttribute("htmlFor");
|
||||
ok(r === (v < 9 ? "for" : null), "htmlFor attr = " + r);
|
||||
|
||||
elem.setAttribute("htmlFor", "for3");
|
||||
ok(elem.htmlFor === (v < 9 ? "for3" : "for"), "elem.htmlFor = " + elem.htmlFor);
|
||||
r = elem.getAttribute("for");
|
||||
ok(r === "for2", "for attr = " + r);
|
||||
r = elem.getAttribute("htmlFor");
|
||||
ok(r === "for3", "htmlFor attr = " + r);
|
||||
|
||||
elem.setAttribute("testattr", "test");
|
||||
test_exposed("class", v < 8);
|
||||
test_exposed("className", true);
|
||||
test_exposed("for", v < 9);
|
||||
test_exposed("htmlFor", true);
|
||||
test_exposed("testattr", v < 9);
|
||||
|
||||
var arr = [3];
|
||||
elem.setAttribute("testattr", arr);
|
||||
r = elem.getAttribute("testattr");
|
||||
ok(r === (v < 8 ? arr : "3"), "testattr = " + r);
|
||||
todo_wine_if(v === 8).
|
||||
ok(elem.testattr === (v < 9 ? arr : undefined), "elem.testattr = " + elem.testattr);
|
||||
r = elem.removeAttribute("testattr");
|
||||
ok(r === (v < 9 ? true : undefined), "testattr removeAttribute returned " + r);
|
||||
|
@ -1102,23 +1133,19 @@ sync_test("elem_attr", function() {
|
|||
elem.setAttribute("testattr", "string");
|
||||
elem.testattr = arr;
|
||||
r = elem.getAttribute("testattr");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 8 ? arr : (v < 9 ? "9" : "string")), "testattr = " + r);
|
||||
ok(elem.testattr === arr, "elem.testattr = " + elem.testattr);
|
||||
arr[0] = 3;
|
||||
r = elem.getAttribute("testattr");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 8 ? arr : (v < 9 ? "3" : "string")), "testattr = " + r);
|
||||
ok(elem.testattr === arr, "elem.testattr = " + elem.testattr);
|
||||
r = elem.removeAttribute("testattr");
|
||||
ok(r === (v < 9 ? true : undefined), "testattr removeAttribute returned " + r);
|
||||
todo_wine_if(v === 8).
|
||||
ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr = " + elem.testattr);
|
||||
|
||||
arr.toString = function() { return 42; }
|
||||
elem.testattr = arr;
|
||||
r = elem.getAttribute("testattr");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 8 ? arr : (v < 9 ? "42" : null)), "testattr with custom toString = " + r);
|
||||
elem.setAttribute("testattr", arr);
|
||||
r = elem.getAttribute("testattr");
|
||||
|
@ -1126,13 +1153,11 @@ sync_test("elem_attr", function() {
|
|||
ok(elem.testattr === arr, "elem.testattr after setAttribute with custom toString = " + elem.testattr);
|
||||
r = elem.removeAttribute("testattr");
|
||||
ok(r === (v < 9 ? true : undefined), "testattr removeAttribute with custom toString returned " + r);
|
||||
todo_wine_if(v === 8).
|
||||
ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr with custom toString = " + elem.testattr);
|
||||
|
||||
arr.valueOf = function() { return "arrval"; }
|
||||
elem.testattr = arr;
|
||||
r = elem.getAttribute("testattr");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 8 ? arr : (v < 9 ? "arrval" : null)), "testattr with custom valueOf = " + r);
|
||||
elem.setAttribute("testattr", arr);
|
||||
r = elem.getAttribute("testattr");
|
||||
|
@ -1140,7 +1165,6 @@ sync_test("elem_attr", function() {
|
|||
ok(elem.testattr === arr, "elem.testattr after setAttribute with custom valueOf = " + elem.testattr);
|
||||
r = elem.removeAttribute("testattr");
|
||||
ok(r === (v < 9 ? true : undefined), "testattr removeAttribute with custom valueOf returned " + r);
|
||||
todo_wine_if(v === 8).
|
||||
ok(elem.testattr === (v < 9 ? undefined : arr), "removed testattr with custom valueOf = " + elem.testattr);
|
||||
delete arr.valueOf;
|
||||
delete arr.toString;
|
||||
|
@ -1157,6 +1181,7 @@ sync_test("elem_attr", function() {
|
|||
elem.onclick = func;
|
||||
ok(elem.onclick === func, "onclick = " + elem.onclick);
|
||||
r = elem.getAttribute("onclick");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 8 ? func : null), "onclick attr = " + r);
|
||||
r = elem.removeAttribute("onclick");
|
||||
ok(r === (v < 9 ? false : undefined), "removeAttribute returned " + r);
|
||||
|
@ -1166,7 +1191,6 @@ sync_test("elem_attr", function() {
|
|||
elem.onclick_test = func;
|
||||
ok(elem.onclick_test === func, "onclick_test = " + elem.onclick_test);
|
||||
r = elem.getAttribute("onclick_test");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 8 ? func : (v < 9 ? func.toString() : null)), "onclick_test attr = " + r);
|
||||
|
||||
elem.setAttribute("onclick", "test");
|
||||
|
@ -1176,11 +1200,11 @@ sync_test("elem_attr", function() {
|
|||
ok(r === (v < 9 ? true : undefined), "removeAttribute after setAttribute returned " + r);
|
||||
|
||||
/* IE11 returns an empty function, which we can't check directly */
|
||||
todo_wine_if(v >= 8).
|
||||
todo_wine_if(v >= 9).
|
||||
ok((v < 11) ? (elem.onclick === null) : (elem.onclick !== func), "removed onclick after setAttribute = " + elem.onclick);
|
||||
|
||||
r = Object.prototype.toString.call(elem.onclick);
|
||||
todo_wine_if(v >= 8 && v < 11).
|
||||
todo_wine_if(v >= 9 && v < 11).
|
||||
ok(r === (v < 9 ? "[object Object]" : (v < 11 ? "[object Null]" : "[object Function]")),
|
||||
"removed onclick after setAttribute Object.toString returned " + r);
|
||||
|
||||
|
@ -1194,11 +1218,10 @@ sync_test("elem_attr", function() {
|
|||
ok(r === (v < 8 ? func : (v < 9 ? null : "string")), "onclick attr = " + r);
|
||||
elem.onclick = "test";
|
||||
r = elem.getAttribute("onclick");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 9 ? "test" : "string"), "onclick attr = " + r);
|
||||
r = elem.removeAttribute("onclick");
|
||||
ok(r === (v < 9 ? true : undefined), "removeAttribute returned " + r);
|
||||
todo_wine_if(v >= 8).
|
||||
todo_wine_if(v >= 9).
|
||||
ok(elem.onclick === null, "removed onclick = " + elem.onclick);
|
||||
|
||||
elem.setAttribute("ondblclick", arr);
|
||||
|
@ -1218,6 +1241,30 @@ sync_test("elem_attr", function() {
|
|||
r = elem.removeAttribute("ondblclick");
|
||||
ok(r === (v < 9 ? true : undefined), "ondblclick string removeAttribute returned " + r);
|
||||
ok(elem.ondblclick === null, "removed ondblclick string = " + elem.ondblclick);
|
||||
|
||||
if(v < 9) {
|
||||
/* style is a special case */
|
||||
try {
|
||||
elem.style = "opacity: 1.0";
|
||||
ok(false, "expected exception setting elem.style");
|
||||
}catch(ex) { }
|
||||
|
||||
var style = elem.style;
|
||||
r = elem.getAttribute("style");
|
||||
ok(r === (v < 8 ? style : null), "style attr = " + r);
|
||||
r = elem.removeAttribute("style");
|
||||
ok(r === true, "removeAttribute('style') returned " + r);
|
||||
r = elem.style;
|
||||
ok(r === style, "removed elem.style = " + r);
|
||||
r = elem.getAttribute("style");
|
||||
todo_wine_if(v === 8).
|
||||
ok(r === (v < 8 ? style : null), "style attr after removal = " + r);
|
||||
elem.setAttribute("style", "opacity: 1.0");
|
||||
r = elem.getAttribute("style");
|
||||
ok(r === (v < 8 ? style : "opacity: 1.0"), "style attr after setAttribute = " + r);
|
||||
r = elem.style;
|
||||
ok(r === style, "elem.style after setAttribute = " + r);
|
||||
}
|
||||
});
|
||||
|
||||
sync_test("__proto__", function() {
|
||||
|
|
Loading…
Reference in New Issue