diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c
index b2024d4b3d..614c9d54b8 100644
--- a/dlls/mshtml/dispex.c
+++ b/dlls/mshtml/dispex.c
@@ -385,6 +385,15 @@ static inline BOOL is_dynamic_dispid(DISPID id)
return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
}
+dispex_prop_type_t get_dispid_type(DISPID id)
+{
+ if(is_dynamic_dispid(id))
+ return DISPEXPROP_DYNAMIC;
+ if(is_custom_dispid(id))
+ return DISPEXPROP_CUSTOM;
+ return DISPEXPROP_BUILTIN;
+}
+
static HRESULT variant_copy(VARIANT *dest, VARIANT *src)
{
if(V_VT(src) == VT_BSTR && !V_BSTR(src)) {
diff --git a/dlls/mshtml/htmlattr.c b/dlls/mshtml/htmlattr.c
index d8203fef48..5d4c2b46de 100644
--- a/dlls/mshtml/htmlattr.c
+++ b/dlls/mshtml/htmlattr.c
@@ -155,8 +155,45 @@ static HRESULT WINAPI HTMLDOMAttribute_get_nodeValue(IHTMLDOMAttribute *iface, V
static HRESULT WINAPI HTMLDOMAttribute_get_specified(IHTMLDOMAttribute *iface, VARIANT_BOOL *p)
{
HTMLDOMAttribute *This = impl_from_IHTMLDOMAttribute(iface);
- FIXME("(%p)->(%p)\n", This, p);
- return E_NOTIMPL;
+ nsIDOMAttr *nsattr;
+ nsAString nsname;
+ BSTR name;
+ nsresult nsres;
+ HRESULT hres;
+
+ TRACE("(%p)->(%p)\n", This, p);
+
+ if(get_dispid_type(This->dispid) != DISPEXPROP_BUILTIN) {
+ *p = VARIANT_TRUE;
+ return S_OK;
+ }
+
+ if(!This->elem || !This->elem->nselem) {
+ FIXME("NULL This->elem\n");
+ return E_UNEXPECTED;
+ }
+
+ hres = IDispatchEx_GetMemberName(&This->elem->node.dispex.IDispatchEx_iface, This->dispid, &name);
+ if(FAILED(hres))
+ return hres;
+
+ /* FIXME: This is not exactly right, we have some attributes that don't map directly to Gecko attributes. */
+ nsAString_InitDepend(&nsname, name);
+ nsres = nsIDOMHTMLElement_GetAttributeNode(This->elem->nselem, &nsname, &nsattr);
+ nsAString_Finish(&nsname);
+ SysFreeString(name);
+ if(NS_FAILED(nsres))
+ return E_FAIL;
+
+ /* If the Gecko attribute node can be found, we know that the attribute is specified.
+ There is no point in calling GetSpecified */
+ if(nsattr) {
+ nsIDOMAttr_Release(nsattr);
+ *p = VARIANT_TRUE;
+ }else {
+ *p = VARIANT_FALSE;
+ }
+ return S_OK;
}
static const IHTMLDOMAttributeVtbl HTMLDOMAttributeVtbl = {
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h
index 04b5b1ef84..1f51804cc5 100644
--- a/dlls/mshtml/mshtml_private.h
+++ b/dlls/mshtml/mshtml_private.h
@@ -263,6 +263,14 @@ void dispex_unlink(DispatchEx*) DECLSPEC_HIDDEN;
void release_typelib(void) DECLSPEC_HIDDEN;
HRESULT get_htmldoc_classinfo(ITypeInfo **typeinfo) DECLSPEC_HIDDEN;
+typedef enum {
+ DISPEXPROP_CUSTOM,
+ DISPEXPROP_DYNAMIC,
+ DISPEXPROP_BUILTIN
+} dispex_prop_type_t;
+
+dispex_prop_type_t get_dispid_type(DISPID) DECLSPEC_HIDDEN;
+
typedef struct HTMLWindow HTMLWindow;
typedef struct HTMLInnerWindow HTMLInnerWindow;
typedef struct HTMLOuterWindow HTMLOuterWindow;
diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c
index 1a1732f549..b0c5335cb5 100644
--- a/dlls/mshtml/tests/dom.c
+++ b/dlls/mshtml/tests/dom.c
@@ -2632,6 +2632,17 @@ static void test_attr_collection(IHTMLElement *elem)
IHTMLAttributeCollection_Release(attr_col);
}
+#define test_attr_specified(a,b) _test_attr_specified(__LINE__,a,b)
+static void _test_attr_specified(unsigned line, IHTMLDOMAttribute *attr, VARIANT_BOOL expected)
+{
+ VARIANT_BOOL specified;
+ HRESULT hres;
+
+ hres = IHTMLDOMAttribute_get_specified(attr, &specified);
+ ok_(__FILE__,line)(hres == S_OK, "get_specified failed: %08x\n", hres);
+ ok_(__FILE__,line)(specified == expected, "specified = %x, expected %x\n", specified, expected);
+}
+
#define test_input_type(i,t) _test_input_type(__LINE__,i,t)
static void _test_input_type(unsigned line, IHTMLInputElement *input, const char *extype)
{
@@ -5721,6 +5732,7 @@ static void test_attr(IHTMLElement *elem)
test_disp((IUnknown*)attr, &DIID_DispHTMLDOMAttribute, "[object]");
test_ifaces((IUnknown*)attr, attr_iids);
test_no_iface((IUnknown*)attr, &IID_IHTMLDOMNode);
+ test_attr_specified(attr, VARIANT_TRUE);
attr2 = get_elem_attr_node((IUnknown*)elem, "id", TRUE);
ok(iface_cmp((IUnknown*)attr, (IUnknown*)attr2), "attr != attr2\n");
@@ -5736,6 +5748,7 @@ static void test_attr(IHTMLElement *elem)
get_attr_node_value(attr, &v, VT_BSTR);
ok(!V_BSTR(&v), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v)));
VariantClear(&v);
+ test_attr_specified(attr, VARIANT_TRUE);
IHTMLDOMAttribute_Release(attr);
V_VT(&v) = VT_I4;
@@ -5744,6 +5757,11 @@ static void test_attr(IHTMLElement *elem)
attr = get_elem_attr_node((IUnknown*)elem, "dispprop", TRUE);
get_attr_node_value(attr, &v, VT_I4);
ok(V_I4(&v) == 100, "V_I4(v) = %d\n", V_I4(&v));
+ test_attr_specified(attr, VARIANT_TRUE);
+ IHTMLDOMAttribute_Release(attr);
+
+ attr = get_elem_attr_node((IUnknown*)elem, "tabIndex", TRUE);
+ test_attr_specified(attr, VARIANT_FALSE);
IHTMLDOMAttribute_Release(attr);
}