/* * XML Element implementation * * Copyright 2007 James Hawkins * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #include "config.h" #include #ifdef HAVE_LIBXML2 # include # include #endif #include "windef.h" #include "winbase.h" #include "winuser.h" #include "ole2.h" #include "msxml6.h" #include "ocidl.h" #include "wine/debug.h" #include "msxml_private.h" WINE_DEFAULT_DEBUG_CHANNEL(msxml); #ifdef HAVE_LIBXML2 static HRESULT XMLElementCollection_create( IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj ); /********************************************************************** * IXMLElement */ typedef struct _xmlelem { IXMLElement IXMLElement_iface; LONG ref; xmlNodePtr node; BOOL own; } xmlelem; static inline xmlelem *impl_from_IXMLElement(IXMLElement *iface) { return CONTAINING_RECORD(iface, xmlelem, IXMLElement_iface); } static HRESULT WINAPI xmlelem_QueryInterface(IXMLElement *iface, REFIID riid, void** ppvObject) { xmlelem *This = impl_from_IXMLElement(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject); if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IXMLElement)) { *ppvObject = iface; } else { FIXME("interface %s not implemented\n", debugstr_guid(riid)); *ppvObject = NULL; return E_NOINTERFACE; } IXMLElement_AddRef(iface); return S_OK; } static ULONG WINAPI xmlelem_AddRef(IXMLElement *iface) { xmlelem *This = impl_from_IXMLElement(iface); TRACE("%p\n", This); return InterlockedIncrement(&This->ref); } static ULONG WINAPI xmlelem_Release(IXMLElement *iface) { xmlelem *This = impl_from_IXMLElement(iface); LONG ref; TRACE("%p\n", This); ref = InterlockedDecrement(&This->ref); if (ref == 0) { if (This->own) xmlFreeNode(This->node); heap_free(This); } return ref; } static HRESULT WINAPI xmlelem_GetTypeInfoCount(IXMLElement *iface, UINT* pctinfo) { xmlelem *This = impl_from_IXMLElement(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI xmlelem_GetTypeInfo(IXMLElement *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { xmlelem *This = impl_from_IXMLElement(iface); HRESULT hr; TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); hr = get_typeinfo(IXMLElement_tid, ppTInfo); return hr; } static HRESULT WINAPI xmlelem_GetIDsOfNames(IXMLElement *iface, REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { xmlelem *This = impl_from_IXMLElement(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); if(!rgszNames || cNames == 0 || !rgDispId) return E_INVALIDARG; hr = get_typeinfo(IXMLElement_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI xmlelem_Invoke(IXMLElement *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { xmlelem *This = impl_from_IXMLElement(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IXMLElement_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, &This->IXMLElement_iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI xmlelem_get_tagName(IXMLElement *iface, BSTR *p) { xmlelem *This = impl_from_IXMLElement(iface); TRACE("(%p)->(%p)\n", This, p); if (!p) return E_INVALIDARG; *p = bstr_from_xmlChar(This->node->name); CharUpperBuffW(*p, SysStringLen(*p)); TRACE("returning %s\n", debugstr_w(*p)); return S_OK; } static HRESULT WINAPI xmlelem_put_tagName(IXMLElement *iface, BSTR p) { xmlelem *This = impl_from_IXMLElement(iface); FIXME("(%p)->(%s): stub\n", This, debugstr_w(p)); if (!p) return E_INVALIDARG; return E_NOTIMPL; } static HRESULT WINAPI xmlelem_get_parent(IXMLElement *iface, IXMLElement **parent) { xmlelem *This = impl_from_IXMLElement(iface); TRACE("(%p)->(%p)\n", This, parent); if (!parent) return E_INVALIDARG; *parent = NULL; if (!This->node->parent) return S_FALSE; return XMLElement_create((IUnknown *)iface, This->node->parent, (LPVOID *)parent, FALSE); } static HRESULT WINAPI xmlelem_setAttribute(IXMLElement *iface, BSTR strPropertyName, VARIANT PropertyValue) { xmlelem *This = impl_from_IXMLElement(iface); xmlChar *name, *value; xmlAttrPtr attr; TRACE("(%p)->(%s %s)\n", This, debugstr_w(strPropertyName), debugstr_variant(&PropertyValue)); if (!strPropertyName || V_VT(&PropertyValue) != VT_BSTR) return E_INVALIDARG; name = xmlChar_from_wchar(strPropertyName); value = xmlChar_from_wchar(V_BSTR(&PropertyValue)); attr = xmlSetProp(This->node, name, value); heap_free(name); heap_free(value); return (attr) ? S_OK : S_FALSE; } static HRESULT WINAPI xmlelem_getAttribute(IXMLElement *iface, BSTR name, VARIANT *value) { static const WCHAR xmllangW[] = { 'x','m','l',':','l','a','n','g',0 }; xmlelem *This = impl_from_IXMLElement(iface); xmlChar *val = NULL; TRACE("(%p)->(%s, %p)\n", This, debugstr_w(name), value); if (!value) return E_INVALIDARG; VariantInit(value); V_BSTR(value) = NULL; if (!name) return E_INVALIDARG; /* case for xml:lang attribute */ if (!lstrcmpiW(name, xmllangW)) { xmlNsPtr ns; ns = xmlSearchNs(This->node->doc, This->node, (xmlChar*)"xml"); val = xmlGetNsProp(This->node, (xmlChar*)"lang", ns->href); } else { xmlAttrPtr attr; xmlChar *xml_name; xml_name = xmlChar_from_wchar(name); attr = This->node->properties; while (attr) { BSTR attr_name; attr_name = bstr_from_xmlChar(attr->name); if (!lstrcmpiW(name, attr_name)) { val = xmlNodeListGetString(attr->doc, attr->children, 1); SysFreeString(attr_name); break; } attr = attr->next; SysFreeString(attr_name); } heap_free(xml_name); } if (val) { V_VT(value) = VT_BSTR; V_BSTR(value) = bstr_from_xmlChar(val); } xmlFree(val); TRACE("returning %s\n", debugstr_w(V_BSTR(value))); return (val) ? S_OK : S_FALSE; } static HRESULT WINAPI xmlelem_removeAttribute(IXMLElement *iface, BSTR strPropertyName) { xmlelem *This = impl_from_IXMLElement(iface); xmlChar *name; xmlAttrPtr attr; int res; HRESULT hr = S_FALSE; TRACE("(%p)->(%s)\n", This, debugstr_w(strPropertyName)); if (!strPropertyName) return E_INVALIDARG; name = xmlChar_from_wchar(strPropertyName); attr = xmlHasProp(This->node, name); if (!attr) goto done; res = xmlRemoveProp(attr); if (res == 0) hr = S_OK; done: heap_free(name); return hr; } static HRESULT WINAPI xmlelem_get_children(IXMLElement *iface, IXMLElementCollection **p) { xmlelem *This = impl_from_IXMLElement(iface); TRACE("(%p)->(%p)\n", This, p); if (!p) return E_INVALIDARG; return XMLElementCollection_create((IUnknown *)iface, This->node, (LPVOID *)p); } static LONG type_libxml_to_msxml(xmlElementType type) { switch (type) { case XML_ELEMENT_NODE: return XMLELEMTYPE_ELEMENT; case XML_TEXT_NODE: return XMLELEMTYPE_TEXT; case XML_COMMENT_NODE: return XMLELEMTYPE_COMMENT; case XML_DOCUMENT_NODE: return XMLELEMTYPE_DOCUMENT; case XML_DTD_NODE: return XMLELEMTYPE_DTD; case XML_PI_NODE: return XMLELEMTYPE_PI; default: break; } return XMLELEMTYPE_OTHER; } static HRESULT WINAPI xmlelem_get_type(IXMLElement *iface, LONG *p) { xmlelem *This = impl_from_IXMLElement(iface); TRACE("(%p)->(%p)\n", This, p); if (!p) return E_INVALIDARG; *p = type_libxml_to_msxml(This->node->type); TRACE("returning %d\n", *p); return S_OK; } static HRESULT WINAPI xmlelem_get_text(IXMLElement *iface, BSTR *p) { xmlelem *This = impl_from_IXMLElement(iface); xmlChar *content; TRACE("(%p)->(%p)\n", This, p); if (!p) return E_INVALIDARG; content = xmlNodeGetContent(This->node); *p = bstr_from_xmlChar(content); TRACE("returning %s\n", debugstr_w(*p)); xmlFree(content); return S_OK; } static HRESULT WINAPI xmlelem_put_text(IXMLElement *iface, BSTR p) { xmlelem *This = impl_from_IXMLElement(iface); xmlChar *content; TRACE("(%p)->(%s)\n", This, debugstr_w(p)); /* FIXME: test which types can be used */ if (This->node->type == XML_ELEMENT_NODE) return E_NOTIMPL; content = xmlChar_from_wchar(p); xmlNodeSetContent(This->node, content); heap_free(content); return S_OK; } static HRESULT WINAPI xmlelem_addChild(IXMLElement *iface, IXMLElement *pChildElem, LONG lIndex, LONG lreserved) { xmlelem *This = impl_from_IXMLElement(iface); xmlelem *childElem = impl_from_IXMLElement(pChildElem); xmlNodePtr child; TRACE("(%p)->(%p %d %d)\n", This, pChildElem, lIndex, lreserved); if (lIndex == 0) child = xmlAddChild(This->node, childElem->node); else child = xmlAddNextSibling(This->node, childElem->node->last); /* parent is responsible for child data */ if (child) childElem->own = FALSE; return (child) ? S_OK : S_FALSE; } static HRESULT WINAPI xmlelem_removeChild(IXMLElement *iface, IXMLElement *pChildElem) { xmlelem *This = impl_from_IXMLElement(iface); xmlelem *childElem = impl_from_IXMLElement(pChildElem); TRACE("(%p)->(%p)\n", This, childElem); if (!pChildElem) return E_INVALIDARG; /* only supported for This is childElem parent case */ if (This->node != childElem->node->parent) return E_INVALIDARG; xmlUnlinkNode(childElem->node); /* standalone element now */ childElem->own = TRUE; return S_OK; } static const struct IXMLElementVtbl xmlelem_vtbl = { xmlelem_QueryInterface, xmlelem_AddRef, xmlelem_Release, xmlelem_GetTypeInfoCount, xmlelem_GetTypeInfo, xmlelem_GetIDsOfNames, xmlelem_Invoke, xmlelem_get_tagName, xmlelem_put_tagName, xmlelem_get_parent, xmlelem_setAttribute, xmlelem_getAttribute, xmlelem_removeAttribute, xmlelem_get_children, xmlelem_get_type, xmlelem_get_text, xmlelem_put_text, xmlelem_addChild, xmlelem_removeChild }; HRESULT XMLElement_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj, BOOL own) { xmlelem *elem; TRACE("(%p,%p)\n", pUnkOuter, ppObj); if (!ppObj) return E_INVALIDARG; *ppObj = NULL; elem = heap_alloc(sizeof (*elem)); if(!elem) return E_OUTOFMEMORY; elem->IXMLElement_iface.lpVtbl = &xmlelem_vtbl; elem->ref = 1; elem->node = node; elem->own = own; *ppObj = &elem->IXMLElement_iface; TRACE("returning iface %p\n", *ppObj); return S_OK; } /************************************************************************ * IXMLElementCollection */ typedef struct _xmlelem_collection { IXMLElementCollection IXMLElementCollection_iface; IEnumVARIANT IEnumVARIANT_iface; LONG ref; LONG length; xmlNodePtr node; /* IEnumVARIANT members */ xmlNodePtr current; } xmlelem_collection; static inline LONG xmlelem_collection_updatelength(xmlelem_collection *collection) { xmlNodePtr ptr = collection->node->children; collection->length = 0; while (ptr) { collection->length++; ptr = ptr->next; } return collection->length; } static inline xmlelem_collection *impl_from_IXMLElementCollection(IXMLElementCollection *iface) { return CONTAINING_RECORD(iface, xmlelem_collection, IXMLElementCollection_iface); } static inline xmlelem_collection *impl_from_IEnumVARIANT(IEnumVARIANT *iface) { return CONTAINING_RECORD(iface, xmlelem_collection, IEnumVARIANT_iface); } static HRESULT WINAPI xmlelem_collection_QueryInterface(IXMLElementCollection *iface, REFIID riid, void** ppvObject) { xmlelem_collection *This = impl_from_IXMLElementCollection(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject); if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IXMLElementCollection)) { *ppvObject = iface; } else if (IsEqualGUID(riid, &IID_IEnumVARIANT)) { *ppvObject = &This->IEnumVARIANT_iface; } else { FIXME("interface %s not implemented\n", debugstr_guid(riid)); return E_NOINTERFACE; } IXMLElementCollection_AddRef(iface); return S_OK; } static ULONG WINAPI xmlelem_collection_AddRef(IXMLElementCollection *iface) { xmlelem_collection *This = impl_from_IXMLElementCollection(iface); TRACE("(%p)\n", This); return InterlockedIncrement(&This->ref); } static ULONG WINAPI xmlelem_collection_Release(IXMLElementCollection *iface) { xmlelem_collection *This = impl_from_IXMLElementCollection(iface); LONG ref; TRACE("(%p)\n", This); ref = InterlockedDecrement(&This->ref); if (ref == 0) { heap_free(This); } return ref; } static HRESULT WINAPI xmlelem_collection_GetTypeInfoCount(IXMLElementCollection *iface, UINT* pctinfo) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlelem_collection_GetTypeInfo(IXMLElementCollection *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlelem_collection_GetIDsOfNames(IXMLElementCollection *iface, REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlelem_collection_Invoke(IXMLElementCollection *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlelem_collection_put_length(IXMLElementCollection *iface, LONG v) { xmlelem_collection *This = impl_from_IXMLElementCollection(iface); TRACE("(%p)->(%d)\n", This, v); return E_FAIL; } static HRESULT WINAPI xmlelem_collection_get_length(IXMLElementCollection *iface, LONG *p) { xmlelem_collection *This = impl_from_IXMLElementCollection(iface); TRACE("(%p)->(%p)\n", This, p); if (!p) return E_INVALIDARG; *p = xmlelem_collection_updatelength(This); return S_OK; } static HRESULT WINAPI xmlelem_collection_get__newEnum(IXMLElementCollection *iface, IUnknown **ppUnk) { xmlelem_collection *This = impl_from_IXMLElementCollection(iface); TRACE("(%p)->(%p)\n", This, ppUnk); if (!ppUnk) return E_INVALIDARG; *ppUnk = (IUnknown *)This; IUnknown_AddRef(*ppUnk); return S_OK; } static HRESULT WINAPI xmlelem_collection_item(IXMLElementCollection *iface, VARIANT var1, VARIANT var2, IDispatch **ppDisp) { xmlelem_collection *This = impl_from_IXMLElementCollection(iface); xmlNodePtr ptr = This->node->children; int index, i; TRACE("(%p)->(%s %s %p)\n", This, debugstr_variant(&var1), debugstr_variant(&var2), ppDisp); if (!ppDisp) return E_INVALIDARG; *ppDisp = NULL; index = V_I4(&var1); if (index < 0) return E_INVALIDARG; xmlelem_collection_updatelength(This); if (index >= This->length) return E_FAIL; for (i = 0; i < index; i++) ptr = ptr->next; return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)ppDisp, FALSE); } static const struct IXMLElementCollectionVtbl xmlelem_collection_vtbl = { xmlelem_collection_QueryInterface, xmlelem_collection_AddRef, xmlelem_collection_Release, xmlelem_collection_GetTypeInfoCount, xmlelem_collection_GetTypeInfo, xmlelem_collection_GetIDsOfNames, xmlelem_collection_Invoke, xmlelem_collection_put_length, xmlelem_collection_get_length, xmlelem_collection_get__newEnum, xmlelem_collection_item }; /************************************************************************ * xmlelem_collection implementation of IEnumVARIANT. */ static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_QueryInterface( IEnumVARIANT *iface, REFIID riid, LPVOID *ppvObj) { xmlelem_collection *this = impl_from_IEnumVARIANT(iface); return IXMLDocument_QueryInterface((IXMLDocument *)this, riid, ppvObj); } static ULONG WINAPI xmlelem_collection_IEnumVARIANT_AddRef( IEnumVARIANT *iface) { xmlelem_collection *this = impl_from_IEnumVARIANT(iface); return IXMLDocument_AddRef((IXMLDocument *)this); } static ULONG WINAPI xmlelem_collection_IEnumVARIANT_Release( IEnumVARIANT *iface) { xmlelem_collection *this = impl_from_IEnumVARIANT(iface); return IXMLDocument_Release((IXMLDocument *)this); } static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Next( IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *fetched) { xmlelem_collection *This = impl_from_IEnumVARIANT(iface); xmlNodePtr ptr = This->current; TRACE("(%p)->(%d %p %p)\n", This, celt, rgVar, fetched); if (!rgVar) return E_INVALIDARG; /* FIXME: handle celt */ if (fetched) *fetched = 1; if (This->current) This->current = This->current->next; else { V_VT(rgVar) = VT_EMPTY; if (fetched) *fetched = 0; return S_FALSE; } V_VT(rgVar) = VT_DISPATCH; return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)&V_DISPATCH(rgVar), FALSE); } static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Skip( IEnumVARIANT *iface, ULONG celt) { xmlelem_collection *This = impl_from_IEnumVARIANT(iface); FIXME("(%p)->(%d): stub\n", This, celt); return E_NOTIMPL; } static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Reset( IEnumVARIANT *iface) { xmlelem_collection *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)\n", This); This->current = This->node->children; return S_OK; } static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Clone( IEnumVARIANT *iface, IEnumVARIANT **ppEnum) { xmlelem_collection *This = impl_from_IEnumVARIANT(iface); FIXME("(%p)->(%p): stub\n", This, ppEnum); return E_NOTIMPL; } static const struct IEnumVARIANTVtbl xmlelem_collection_IEnumVARIANTvtbl = { xmlelem_collection_IEnumVARIANT_QueryInterface, xmlelem_collection_IEnumVARIANT_AddRef, xmlelem_collection_IEnumVARIANT_Release, xmlelem_collection_IEnumVARIANT_Next, xmlelem_collection_IEnumVARIANT_Skip, xmlelem_collection_IEnumVARIANT_Reset, xmlelem_collection_IEnumVARIANT_Clone }; static HRESULT XMLElementCollection_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj) { xmlelem_collection *collection; TRACE("(%p,%p)\n", pUnkOuter, ppObj); *ppObj = NULL; if (!node->children) return S_FALSE; collection = heap_alloc(sizeof (*collection)); if(!collection) return E_OUTOFMEMORY; collection->IXMLElementCollection_iface.lpVtbl = &xmlelem_collection_vtbl; collection->IEnumVARIANT_iface.lpVtbl = &xmlelem_collection_IEnumVARIANTvtbl; collection->ref = 1; collection->length = 0; collection->node = node; collection->current = node->children; xmlelem_collection_updatelength(collection); *ppObj = &collection->IXMLElementCollection_iface; TRACE("returning iface %p\n", *ppObj); return S_OK; } #endif