/* * Node implementation * * Copyright 2005 Mike McCormack * * 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 */ #include "config.h" #define COBJMACROS #include #include #include "windef.h" #include "winbase.h" #include "winuser.h" #include "winnls.h" #include "ole2.h" #include "msxml2.h" #include "msxml_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(msxml); #ifdef HAVE_LIBXML2 typedef struct _xmlnode { const struct IXMLDOMNodeVtbl *lpVtbl; const struct IUnknownVtbl *lpInternalUnkVtbl; IUnknown *pUnkOuter; LONG ref; xmlNodePtr node; } xmlnode; static inline xmlnode *impl_from_IXMLDOMNode( IXMLDOMNode *iface ) { return (xmlnode *)((char*)iface - FIELD_OFFSET(xmlnode, lpVtbl)); } static inline xmlnode *impl_from_InternalUnknown( IUnknown *iface ) { return (xmlnode *)((char*)iface - FIELD_OFFSET(xmlnode, lpInternalUnkVtbl)); } xmlNodePtr xmlNodePtr_from_domnode( IXMLDOMNode *iface, xmlElementType type ) { xmlnode *This; if ( !iface ) return NULL; This = impl_from_IXMLDOMNode( iface ); if ( !This->node ) return NULL; if ( type && This->node->type != type ) return NULL; return This->node; } void attach_xmlnode( IXMLDOMNode *node, xmlNodePtr xml ) { xmlnode *This = impl_from_IXMLDOMNode( node ); if(This->node) xmldoc_release(This->node->doc); This->node = xml; if(This->node) xmldoc_add_ref(This->node->doc); return; } static HRESULT WINAPI xmlnode_QueryInterface( IXMLDOMNode *iface, REFIID riid, void** ppvObject ) { xmlnode *This = impl_from_IXMLDOMNode( iface ); TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); return IUnknown_QueryInterface(This->pUnkOuter, riid, ppvObject); } static ULONG WINAPI xmlnode_AddRef( IXMLDOMNode *iface ) { xmlnode *This = impl_from_IXMLDOMNode( iface ); return IUnknown_AddRef(This->pUnkOuter); } static ULONG WINAPI xmlnode_Release( IXMLDOMNode *iface ) { xmlnode *This = impl_from_IXMLDOMNode( iface ); return IUnknown_Release(This->pUnkOuter); } static HRESULT WINAPI xmlnode_GetTypeInfoCount( IXMLDOMNode *iface, UINT* pctinfo ) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_GetTypeInfo( IXMLDOMNode *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo ) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_GetIDsOfNames( IXMLDOMNode *iface, REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId ) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_Invoke( IXMLDOMNode *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 xmlnode_get_nodeName( IXMLDOMNode *iface, BSTR* name) { xmlnode *This = impl_from_IXMLDOMNode( iface ); const xmlChar *str; TRACE("%p\n", This ); if (!name) return E_INVALIDARG; if ( !This->node ) return E_FAIL; switch( This->node->type ) { case XML_TEXT_NODE: str = (const xmlChar*) "#text"; break; case XML_DOCUMENT_NODE: str = (const xmlChar*) "#document"; break; default: str = This->node->name; break; } *name = bstr_from_xmlChar( str ); if (!*name) return S_FALSE; return S_OK; } BSTR bstr_from_xmlChar( const xmlChar *buf ) { DWORD len; LPWSTR str; BSTR bstr; if ( !buf ) return NULL; len = MultiByteToWideChar( CP_UTF8, 0, (LPCSTR) buf, -1, NULL, 0 ); str = (LPWSTR) HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) ); if ( !str ) return NULL; MultiByteToWideChar( CP_UTF8, 0, (LPCSTR) buf, -1, str, len ); bstr = SysAllocString( str ); HeapFree( GetProcessHeap(), 0, str ); return bstr; } static HRESULT WINAPI xmlnode_get_nodeValue( IXMLDOMNode *iface, VARIANT* value) { xmlnode *This = impl_from_IXMLDOMNode( iface ); HRESULT r = S_FALSE; TRACE("%p %p\n", This, value); V_BSTR(value) = NULL; V_VT(value) = VT_NULL; switch ( This->node->type ) { case XML_ATTRIBUTE_NODE: { xmlChar *content = xmlNodeGetContent(This->node); V_VT(value) = VT_BSTR; V_BSTR(value) = bstr_from_xmlChar( content ); xmlFree(content); r = S_OK; break; } case XML_TEXT_NODE: V_VT(value) = VT_BSTR; V_BSTR(value) = bstr_from_xmlChar( This->node->content ); r = S_OK; break; case XML_ELEMENT_NODE: case XML_DOCUMENT_NODE: /* these seem to return NULL */ break; case XML_PI_NODE: default: FIXME("node %p type %d\n", This, This->node->type); } TRACE("%p returned %s\n", This, debugstr_w( V_BSTR(value) ) ); return r; } static HRESULT WINAPI xmlnode_put_nodeValue( IXMLDOMNode *iface, VARIANT value) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_nodeType( IXMLDOMNode *iface, DOMNodeType* type) { xmlnode *This = impl_from_IXMLDOMNode( iface ); TRACE("%p %p\n", This, type); assert( NODE_ELEMENT == XML_ELEMENT_NODE ); assert( NODE_NOTATION == XML_NOTATION_NODE ); *type = This->node->type; return S_OK; } static HRESULT get_node( xmlnode *This, const char *name, xmlNodePtr node, IXMLDOMNode **out ) { TRACE("%p->%s %p\n", This, name, node ); if ( !out ) return E_INVALIDARG; *out = create_node( node ); if (!*out) return S_FALSE; return S_OK; } static HRESULT WINAPI xmlnode_get_parentNode( IXMLDOMNode *iface, IXMLDOMNode** parent) { xmlnode *This = impl_from_IXMLDOMNode( iface ); return get_node( This, "parent", This->node->parent, parent ); } static HRESULT WINAPI xmlnode_get_childNodes( IXMLDOMNode *iface, IXMLDOMNodeList** childList) { xmlnode *This = impl_from_IXMLDOMNode( iface ); TRACE("%p %p\n", This, childList ); if ( !childList ) return E_INVALIDARG; switch(This->node->type) { case XML_ELEMENT_NODE: *childList = create_filtered_nodelist( This->node->children, (const xmlChar *)"*", FALSE ); break; case XML_ATTRIBUTE_NODE: *childList = create_filtered_nodelist( This->node->children, (const xmlChar *)"node()", FALSE ); break; default: FIXME("unhandled node type %d\n", This->node->type); break; } if (!*childList) return S_FALSE; return S_OK; } static HRESULT WINAPI xmlnode_get_firstChild( IXMLDOMNode *iface, IXMLDOMNode** firstChild) { xmlnode *This = impl_from_IXMLDOMNode( iface ); return get_node( This, "firstChild", This->node->children, firstChild ); } static HRESULT WINAPI xmlnode_get_lastChild( IXMLDOMNode *iface, IXMLDOMNode** lastChild) { xmlnode *This = impl_from_IXMLDOMNode( iface ); return get_node( This, "lastChild", This->node->last, lastChild ); } static HRESULT WINAPI xmlnode_get_previousSibling( IXMLDOMNode *iface, IXMLDOMNode** previousSibling) { xmlnode *This = impl_from_IXMLDOMNode( iface ); return get_node( This, "previous", This->node->prev, previousSibling ); } static HRESULT WINAPI xmlnode_get_nextSibling( IXMLDOMNode *iface, IXMLDOMNode** nextSibling) { xmlnode *This = impl_from_IXMLDOMNode( iface ); return get_node( This, "next", This->node->next, nextSibling ); } static HRESULT WINAPI xmlnode_get_attributes( IXMLDOMNode *iface, IXMLDOMNamedNodeMap** attributeMap) { xmlnode *This = impl_from_IXMLDOMNode( iface ); TRACE("%p\n", This); *attributeMap = create_nodemap( iface ); return S_OK; } static HRESULT WINAPI xmlnode_insertBefore( IXMLDOMNode *iface, IXMLDOMNode* newChild, VARIANT refChild, IXMLDOMNode** outNewChild) { xmlnode *This = impl_from_IXMLDOMNode( iface ); xmlNodePtr before_node, new_child_node; IXMLDOMNode *before = NULL, *new; HRESULT hr; TRACE("(%p)->(%p,var,%p)\n",This,newChild,outNewChild); switch(V_VT(&refChild)) { case VT_EMPTY: case VT_NULL: break; case VT_UNKNOWN: hr = IUnknown_QueryInterface(V_UNKNOWN(&refChild), &IID_IXMLDOMNode, (LPVOID)&before); if(FAILED(hr)) return hr; break; case VT_DISPATCH: hr = IDispatch_QueryInterface(V_DISPATCH(&refChild), &IID_IXMLDOMNode, (LPVOID)&before); if(FAILED(hr)) return hr; break; default: FIXME("refChild var type %x\n", V_VT(&refChild)); return E_FAIL; } IXMLDOMNode_QueryInterface(newChild, &IID_IXMLDOMNode, (LPVOID)&new); new_child_node = impl_from_IXMLDOMNode(new)->node; TRACE("new_child_node %p This->node %p\n", new_child_node, This->node); if(before) { before_node = impl_from_IXMLDOMNode(before)->node; xmlAddPrevSibling(before_node, new_child_node); IXMLDOMNode_Release(before); } else { xmlAddChild(This->node, new_child_node); } IXMLDOMNode_Release(new); IXMLDOMNode_AddRef(newChild); *outNewChild = newChild; TRACE("ret S_OK\n"); return S_OK; } static HRESULT WINAPI xmlnode_replaceChild( IXMLDOMNode *iface, IXMLDOMNode* newChild, IXMLDOMNode* oldChild, IXMLDOMNode** outOldChild) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_removeChild( IXMLDOMNode *iface, IXMLDOMNode* childNode, IXMLDOMNode** oldChild) { xmlnode *This = impl_from_IXMLDOMNode( iface ); xmlNode *ancestor, *child_node_ptr; HRESULT hr; IXMLDOMNode *child; TRACE("%p->(%p, %p)\n", This, childNode, oldChild); *oldChild = NULL; hr = IXMLDOMNode_QueryInterface(childNode, &IID_IXMLDOMNode, (LPVOID)&child); if(FAILED(hr)) return hr; child_node_ptr = ancestor = impl_from_IXMLDOMNode(child)->node; while(ancestor->parent) { if(ancestor->parent == This->node) break; ancestor = ancestor->parent; } if(!ancestor->parent) { WARN("childNode %p is not a child of %p\n", childNode, iface); IXMLDOMNode_Release(child); return E_INVALIDARG; } xmlUnlinkNode(child_node_ptr); IXMLDOMNode_Release(child); IXMLDOMNode_AddRef(childNode); *oldChild = childNode; return S_OK; } static HRESULT WINAPI xmlnode_appendChild( IXMLDOMNode *iface, IXMLDOMNode* newChild, IXMLDOMNode** outNewChild) { xmlnode *This = impl_from_IXMLDOMNode( iface ); VARIANT var; TRACE("(%p)->(%p,%p)\n", This, newChild, outNewChild); VariantInit(&var); return IXMLDOMNode_insertBefore(iface, newChild, var, outNewChild); } static HRESULT WINAPI xmlnode_hasChildNodes( IXMLDOMNode *iface, VARIANT_BOOL* hasChild) { xmlnode *This = impl_from_IXMLDOMNode( iface ); TRACE("%p\n", This); if (!hasChild) return E_INVALIDARG; if (!This->node->children) { *hasChild = VARIANT_FALSE; return S_FALSE; } *hasChild = VARIANT_TRUE; return S_OK; } static HRESULT WINAPI xmlnode_get_ownerDocument( IXMLDOMNode *iface, IXMLDOMDocument** DOMDocument) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_cloneNode( IXMLDOMNode *iface, VARIANT_BOOL deep, IXMLDOMNode** cloneRoot) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_nodeTypeString( IXMLDOMNode *iface, BSTR* xmlnodeType) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_text( IXMLDOMNode *iface, BSTR* text) { xmlnode *This = impl_from_IXMLDOMNode( iface ); BSTR str = NULL; TRACE("%p\n", This); if ( !text ) return E_INVALIDARG; switch(This->node->type) { case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: { xmlNodePtr child = This->node->children; if ( child && child->type == XML_TEXT_NODE ) str = bstr_from_xmlChar( child->content ); break; } case XML_TEXT_NODE: str = bstr_from_xmlChar( This->node->content ); break; default: FIXME("Unhandled node type %d\n", This->node->type); } TRACE("%p %s\n", This, debugstr_w(str) ); *text = str; return S_OK; } static HRESULT WINAPI xmlnode_put_text( IXMLDOMNode *iface, BSTR text) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_specified( IXMLDOMNode *iface, VARIANT_BOOL* isSpecified) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_definition( IXMLDOMNode *iface, IXMLDOMNode** definitionNode) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_nodeTypedValue( IXMLDOMNode *iface, VARIANT* typedValue) { FIXME("ignoring data type\n"); return xmlnode_get_nodeValue(iface, typedValue); } static HRESULT WINAPI xmlnode_put_nodeTypedValue( IXMLDOMNode *iface, VARIANT typedValue) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_dataType( IXMLDOMNode *iface, VARIANT* dataTypeName) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_put_dataType( IXMLDOMNode *iface, BSTR dataTypeName) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_xml( IXMLDOMNode *iface, BSTR* xmlString) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_transformNode( IXMLDOMNode *iface, IXMLDOMNode* styleSheet, BSTR* xmlString) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_selectNodes( IXMLDOMNode *iface, BSTR queryString, IXMLDOMNodeList** resultList) { xmlnode *This = impl_from_IXMLDOMNode( iface ); xmlChar *str = NULL; HRESULT r = E_FAIL; TRACE("%p %s %p\n", This, debugstr_w(queryString), resultList ); str = xmlChar_from_wchar( queryString ); if (!str) return r; if( !This->node->children ) return S_FALSE; *resultList = create_filtered_nodelist( This->node->children, str, FALSE ); HeapFree( GetProcessHeap(), 0, str ); return S_OK; } static HRESULT WINAPI xmlnode_selectSingleNode( IXMLDOMNode *iface, BSTR queryString, IXMLDOMNode** resultNode) { xmlnode *This = impl_from_IXMLDOMNode( iface ); IXMLDOMNodeList *list; HRESULT r; TRACE("%p %s %p\n", This, debugstr_w(queryString), resultNode ); r = IXMLDOMNode_selectNodes(iface, queryString, &list); if(r == S_OK) { r = IXMLDOMNodeList_nextNode(list, resultNode); IXMLDOMNodeList_Release(list); } return r; } static HRESULT WINAPI xmlnode_get_parsed( IXMLDOMNode *iface, VARIANT_BOOL* isParsed) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_namespaceURI( IXMLDOMNode *iface, BSTR* namespaceURI) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_prefix( IXMLDOMNode *iface, BSTR* prefixString) { FIXME("\n"); return E_NOTIMPL; } static HRESULT WINAPI xmlnode_get_baseName( IXMLDOMNode *iface, BSTR* nameString) { xmlnode *This = impl_from_IXMLDOMNode( iface ); BSTR str = NULL; HRESULT r = S_FALSE; TRACE("%p %p\n", This, nameString ); if ( !nameString ) return E_INVALIDARG; switch ( This->node->type ) { case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: str = bstr_from_xmlChar( This->node->name ); r = S_OK; break; case XML_TEXT_NODE: break; default: ERR("Unhandled type %d\n", This->node->type ); break; } TRACE("returning %08lx str = %s\n", r, debugstr_w( str ) ); *nameString = str; return r; } static HRESULT WINAPI xmlnode_transformNodeToObject( IXMLDOMNode *iface, IXMLDOMNode* stylesheet, VARIANT outputObject) { FIXME("\n"); return E_NOTIMPL; } static const struct IXMLDOMNodeVtbl xmlnode_vtbl = { xmlnode_QueryInterface, xmlnode_AddRef, xmlnode_Release, xmlnode_GetTypeInfoCount, xmlnode_GetTypeInfo, xmlnode_GetIDsOfNames, xmlnode_Invoke, xmlnode_get_nodeName, xmlnode_get_nodeValue, xmlnode_put_nodeValue, xmlnode_get_nodeType, xmlnode_get_parentNode, xmlnode_get_childNodes, xmlnode_get_firstChild, xmlnode_get_lastChild, xmlnode_get_previousSibling, xmlnode_get_nextSibling, xmlnode_get_attributes, xmlnode_insertBefore, xmlnode_replaceChild, xmlnode_removeChild, xmlnode_appendChild, xmlnode_hasChildNodes, xmlnode_get_ownerDocument, xmlnode_cloneNode, xmlnode_get_nodeTypeString, xmlnode_get_text, xmlnode_put_text, xmlnode_get_specified, xmlnode_get_definition, xmlnode_get_nodeTypedValue, xmlnode_put_nodeTypedValue, xmlnode_get_dataType, xmlnode_put_dataType, xmlnode_get_xml, xmlnode_transformNode, xmlnode_selectNodes, xmlnode_selectSingleNode, xmlnode_get_parsed, xmlnode_get_namespaceURI, xmlnode_get_prefix, xmlnode_get_baseName, xmlnode_transformNodeToObject, }; static HRESULT WINAPI Internal_QueryInterface( IUnknown *iface, REFIID riid, void** ppvObject ) { xmlnode *This = impl_from_InternalUnknown( iface ); TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject); if ( IsEqualGUID( riid, &IID_IUnknown )) *ppvObject = iface; else if ( IsEqualGUID( riid, &IID_IDispatch ) || IsEqualGUID( riid, &IID_IXMLDOMNode ) ) *ppvObject = &This->lpVtbl; else { FIXME("interface %s not implemented\n", debugstr_guid(riid)); *ppvObject = NULL; return E_NOINTERFACE; } IUnknown_AddRef( (IUnknown*)*ppvObject ); return S_OK; } static ULONG WINAPI Internal_AddRef( IUnknown *iface ) { xmlnode *This = impl_from_InternalUnknown( iface ); return InterlockedIncrement( &This->ref ); } static ULONG WINAPI Internal_Release( IUnknown *iface ) { xmlnode *This = impl_from_InternalUnknown( iface ); ULONG ref; ref = InterlockedDecrement( &This->ref ); if ( ref == 0 ) { if( This->node ) xmldoc_release( This->node->doc ); HeapFree( GetProcessHeap(), 0, This ); } return ref; } static const struct IUnknownVtbl internal_unk_vtbl = { Internal_QueryInterface, Internal_AddRef, Internal_Release }; IUnknown *create_basic_node( xmlNodePtr node, IUnknown *pUnkOuter ) { xmlnode *This; This = HeapAlloc( GetProcessHeap(), 0, sizeof *This ); if ( !This ) return NULL; if(node) xmldoc_add_ref( node->doc ); This->lpVtbl = &xmlnode_vtbl; This->lpInternalUnkVtbl = &internal_unk_vtbl; if(pUnkOuter) This->pUnkOuter = pUnkOuter; /* Don't take a ref on outer Unknown */ else This->pUnkOuter = (IUnknown *)&This->lpInternalUnkVtbl; This->ref = 1; This->node = node; return (IUnknown*)&This->lpInternalUnkVtbl; } IXMLDOMNode *create_node( xmlNodePtr node ) { IUnknown *pUnk; IXMLDOMNode *ret; HRESULT hr; if ( !node ) return NULL; TRACE("type %d\n", node->type); switch(node->type) { case XML_ELEMENT_NODE: pUnk = create_element( node, NULL ); break; case XML_ATTRIBUTE_NODE: pUnk = create_attribute( node ); break; case XML_TEXT_NODE: pUnk = create_text( node ); break; case XML_COMMENT_NODE: pUnk = create_comment( node ); break; case XML_DOCUMENT_NODE: ERR("shouldn't be here!\n"); return NULL; default: FIXME("only creating basic node for type %d\n", node->type); pUnk = create_basic_node( node, NULL ); } hr = IUnknown_QueryInterface(pUnk, &IID_IXMLDOMNode, (LPVOID*)&ret); IUnknown_Release(pUnk); if(FAILED(hr)) return NULL; return ret; } #endif