mshtml: COM cleanup for the IHTMLDOMNode2 iface.
[wine] / dlls / mshtml / htmlnode.c
1 /*
2  * Copyright 2006 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "ole2.h"
27
28 #include "wine/debug.h"
29
30 #include "mshtml_private.h"
31 #include "htmlevent.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
34
35 static HTMLDOMNode *get_node_obj(HTMLDocumentNode*,IUnknown*);
36 static HRESULT create_node(HTMLDocumentNode*,nsIDOMNode*,HTMLDOMNode**);
37
38 typedef struct {
39     DispatchEx dispex;
40     const IHTMLDOMChildrenCollectionVtbl  *lpIHTMLDOMChildrenCollectionVtbl;
41
42     LONG ref;
43
44     /* FIXME: implement weak reference */
45     HTMLDocumentNode *doc;
46
47     nsIDOMNodeList *nslist;
48 } HTMLDOMChildrenCollection;
49
50 #define HTMLCHILDCOL(x)  ((IHTMLDOMChildrenCollection*)  &(x)->lpIHTMLDOMChildrenCollectionVtbl)
51
52 #define HTMLCHILDCOL_THIS(iface) DEFINE_THIS(HTMLDOMChildrenCollection, IHTMLDOMChildrenCollection, iface)
53
54 static HRESULT WINAPI HTMLDOMChildrenCollection_QueryInterface(IHTMLDOMChildrenCollection *iface, REFIID riid, void **ppv)
55 {
56     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
57
58     *ppv = NULL;
59
60     if(IsEqualGUID(&IID_IUnknown, riid)) {
61         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
62         *ppv = HTMLCHILDCOL(This);
63     }else if(IsEqualGUID(&IID_IHTMLDOMChildrenCollection, riid)) {
64         TRACE("(%p)->(IID_IHTMLDOMChildrenCollection %p)\n", This, ppv);
65         *ppv = HTMLCHILDCOL(This);
66     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
67         return *ppv ? S_OK : E_NOINTERFACE;
68     }
69
70     if(*ppv) {
71         IUnknown_AddRef((IUnknown*)*ppv);
72         return S_OK;
73     }
74
75     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
76     return E_NOINTERFACE;
77 }
78
79 static ULONG WINAPI HTMLDOMChildrenCollection_AddRef(IHTMLDOMChildrenCollection *iface)
80 {
81     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
82     LONG ref = InterlockedIncrement(&This->ref);
83
84     TRACE("(%p) ref=%d\n", This, ref);
85
86     return ref;
87 }
88
89 static ULONG WINAPI HTMLDOMChildrenCollection_Release(IHTMLDOMChildrenCollection *iface)
90 {
91     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
92     LONG ref = InterlockedDecrement(&This->ref);
93
94     TRACE("(%p) ref=%d\n", This, ref);
95
96     if(!ref) {
97         nsIDOMNodeList_Release(This->nslist);
98         heap_free(This);
99     }
100
101     return ref;
102 }
103
104 static HRESULT WINAPI HTMLDOMChildrenCollection_GetTypeInfoCount(IHTMLDOMChildrenCollection *iface, UINT *pctinfo)
105 {
106     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
107     return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo);
108 }
109
110 static HRESULT WINAPI HTMLDOMChildrenCollection_GetTypeInfo(IHTMLDOMChildrenCollection *iface, UINT iTInfo,
111         LCID lcid, ITypeInfo **ppTInfo)
112 {
113     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
114     return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo);
115 }
116
117 static HRESULT WINAPI HTMLDOMChildrenCollection_GetIDsOfNames(IHTMLDOMChildrenCollection *iface, REFIID riid,
118         LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
119 {
120     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
121     return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId);
122 }
123
124 static HRESULT WINAPI HTMLDOMChildrenCollection_Invoke(IHTMLDOMChildrenCollection *iface, DISPID dispIdMember,
125         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
126         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
127 {
128     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
129     return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid,
130             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
131 }
132
133 static HRESULT WINAPI HTMLDOMChildrenCollection_get_length(IHTMLDOMChildrenCollection *iface, LONG *p)
134 {
135     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
136     PRUint32 length=0;
137
138     TRACE("(%p)->(%p)\n", This, p);
139
140     nsIDOMNodeList_GetLength(This->nslist, &length);
141     *p = length;
142     return S_OK;
143 }
144
145 static HRESULT WINAPI HTMLDOMChildrenCollection__newEnum(IHTMLDOMChildrenCollection *iface, IUnknown **p)
146 {
147     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
148     FIXME("(%p)->(%p)\n", This, p);
149     return E_NOTIMPL;
150 }
151
152 static HRESULT WINAPI HTMLDOMChildrenCollection_item(IHTMLDOMChildrenCollection *iface, LONG index, IDispatch **ppItem)
153 {
154     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
155     nsIDOMNode *nsnode = NULL;
156     HTMLDOMNode *node;
157     PRUint32 length=0;
158     nsresult nsres;
159     HRESULT hres;
160
161     TRACE("(%p)->(%d %p)\n", This, index, ppItem);
162
163     if (ppItem)
164         *ppItem = NULL;
165     else
166         return E_POINTER;
167
168     nsIDOMNodeList_GetLength(This->nslist, &length);
169     if(index < 0 || index >= length)
170         return E_INVALIDARG;
171
172     nsres = nsIDOMNodeList_Item(This->nslist, index, &nsnode);
173     if(NS_FAILED(nsres) || !nsnode) {
174         ERR("Item failed: %08x\n", nsres);
175         return E_FAIL;
176     }
177
178     hres = get_node(This->doc, nsnode, TRUE, &node);
179     if(FAILED(hres))
180         return hres;
181
182     *ppItem = (IDispatch*)&node->IHTMLDOMNode_iface;
183     IDispatch_AddRef(*ppItem);
184     return S_OK;
185 }
186
187 #undef HTMLCHILDCOL_THIS
188
189 static const IHTMLDOMChildrenCollectionVtbl HTMLDOMChildrenCollectionVtbl = {
190     HTMLDOMChildrenCollection_QueryInterface,
191     HTMLDOMChildrenCollection_AddRef,
192     HTMLDOMChildrenCollection_Release,
193     HTMLDOMChildrenCollection_GetTypeInfoCount,
194     HTMLDOMChildrenCollection_GetTypeInfo,
195     HTMLDOMChildrenCollection_GetIDsOfNames,
196     HTMLDOMChildrenCollection_Invoke,
197     HTMLDOMChildrenCollection_get_length,
198     HTMLDOMChildrenCollection__newEnum,
199     HTMLDOMChildrenCollection_item
200 };
201
202 static inline HTMLDOMChildrenCollection *impl_from_DispatchEx(DispatchEx *iface)
203 {
204     return CONTAINING_RECORD(iface, HTMLDOMChildrenCollection, dispex);
205 }
206
207 #define DISPID_CHILDCOL_0 MSHTML_DISPID_CUSTOM_MIN
208
209 static HRESULT HTMLDOMChildrenCollection_get_dispid(DispatchEx *dispex, BSTR name, DWORD flags, DISPID *dispid)
210 {
211     HTMLDOMChildrenCollection *This = impl_from_DispatchEx(dispex);
212     WCHAR *ptr;
213     DWORD idx=0;
214     PRUint32 len = 0;
215
216     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
217         idx = idx*10 + (*ptr-'0');
218     if(*ptr)
219         return DISP_E_UNKNOWNNAME;
220
221     nsIDOMNodeList_GetLength(This->nslist, &len);
222     if(idx >= len)
223         return DISP_E_UNKNOWNNAME;
224
225     *dispid = DISPID_CHILDCOL_0 + idx;
226     TRACE("ret %x\n", *dispid);
227     return S_OK;
228 }
229
230 static HRESULT HTMLDOMChildrenCollection_invoke(DispatchEx *dispex, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
231         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
232 {
233     HTMLDOMChildrenCollection *This = impl_from_DispatchEx(dispex);
234
235     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, flags, params, res, ei, caller);
236
237     switch(flags) {
238     case DISPATCH_PROPERTYGET: {
239         IDispatch *disp = NULL;
240         HRESULT hres;
241
242         hres = IHTMLDOMChildrenCollection_item(HTMLCHILDCOL(This), id - DISPID_CHILDCOL_0, &disp);
243         if(0&&FAILED(hres))
244             return hres;
245
246         V_VT(res) = VT_DISPATCH;
247         V_DISPATCH(res) = disp;
248         break;
249     }
250
251     default:
252         FIXME("unimplemented flags %x\n", flags);
253         return E_NOTIMPL;
254     }
255
256     return S_OK;
257 }
258
259 static const dispex_static_data_vtbl_t HTMLDOMChildrenCollection_dispex_vtbl = {
260     NULL,
261     HTMLDOMChildrenCollection_get_dispid,
262     HTMLDOMChildrenCollection_invoke
263 };
264
265 static const tid_t HTMLDOMChildrenCollection_iface_tids[] = {
266     IHTMLDOMChildrenCollection_tid,
267     0
268 };
269
270 static dispex_static_data_t HTMLDOMChildrenCollection_dispex = {
271     &HTMLDOMChildrenCollection_dispex_vtbl,
272     DispDOMChildrenCollection_tid,
273     NULL,
274     HTMLDOMChildrenCollection_iface_tids
275 };
276
277 static IHTMLDOMChildrenCollection *create_child_collection(HTMLDocumentNode *doc, nsIDOMNodeList *nslist)
278 {
279     HTMLDOMChildrenCollection *ret;
280
281     ret = heap_alloc_zero(sizeof(*ret));
282     ret->lpIHTMLDOMChildrenCollectionVtbl = &HTMLDOMChildrenCollectionVtbl;
283     ret->ref = 1;
284
285     nsIDOMNodeList_AddRef(nslist);
286     ret->nslist = nslist;
287     ret->doc = doc;
288
289     init_dispex(&ret->dispex, (IUnknown*)HTMLCHILDCOL(ret), &HTMLDOMChildrenCollection_dispex);
290
291     return HTMLCHILDCOL(ret);
292 }
293
294 static inline HTMLDOMNode *impl_from_IHTMLDOMNode(IHTMLDOMNode *iface)
295 {
296     return CONTAINING_RECORD(iface, HTMLDOMNode, IHTMLDOMNode_iface);
297 }
298
299 static HRESULT WINAPI HTMLDOMNode_QueryInterface(IHTMLDOMNode *iface,
300                                                  REFIID riid, void **ppv)
301 {
302     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
303
304     return This->vtbl->qi(This, riid, ppv);
305 }
306
307 static ULONG WINAPI HTMLDOMNode_AddRef(IHTMLDOMNode *iface)
308 {
309     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
310     LONG ref = InterlockedIncrement(&This->ref);
311
312     TRACE("(%p) ref=%d\n", This, ref);
313
314     return ref;
315 }
316
317 static ULONG WINAPI HTMLDOMNode_Release(IHTMLDOMNode *iface)
318 {
319     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
320     LONG ref = InterlockedDecrement(&This->ref);
321
322     TRACE("(%p) ref=%d\n", This, ref);
323
324     if(!ref) {
325         This->vtbl->destructor(This);
326         release_dispex(&This->dispex);
327         heap_free(This);
328     }
329
330     return ref;
331 }
332
333 static HRESULT WINAPI HTMLDOMNode_GetTypeInfoCount(IHTMLDOMNode *iface, UINT *pctinfo)
334 {
335     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
336     return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo);
337 }
338
339 static HRESULT WINAPI HTMLDOMNode_GetTypeInfo(IHTMLDOMNode *iface, UINT iTInfo,
340                                               LCID lcid, ITypeInfo **ppTInfo)
341 {
342     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
343     return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo);
344 }
345
346 static HRESULT WINAPI HTMLDOMNode_GetIDsOfNames(IHTMLDOMNode *iface, REFIID riid,
347                                                 LPOLESTR *rgszNames, UINT cNames,
348                                                 LCID lcid, DISPID *rgDispId)
349 {
350     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
351     return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId);
352 }
353
354 static HRESULT WINAPI HTMLDOMNode_Invoke(IHTMLDOMNode *iface, DISPID dispIdMember,
355                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
356                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
357 {
358     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
359     return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid,
360             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
361 }
362
363 static HRESULT WINAPI HTMLDOMNode_get_nodeType(IHTMLDOMNode *iface, LONG *p)
364 {
365     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
366     PRUint16 type = -1;
367
368     TRACE("(%p)->(%p)\n", This, p);
369
370     nsIDOMNode_GetNodeType(This->nsnode, &type);
371
372     switch(type) {
373     case ELEMENT_NODE:
374         *p = 1;
375         break;
376     case TEXT_NODE:
377         *p = 3;
378         break;
379     case COMMENT_NODE:
380         *p = 8;
381         break;
382     case DOCUMENT_NODE:
383         *p = 9;
384         break;
385     case DOCUMENT_FRAGMENT_NODE:
386         *p = 11;
387         break;
388     default:
389         /*
390          * FIXME:
391          * According to MSDN only ELEMENT_NODE and TEXT_NODE are supported.
392          * It needs more tests.
393          */
394         FIXME("type %u\n", type);
395         *p = 0;
396     }
397
398     return S_OK;
399 }
400
401 static HRESULT WINAPI HTMLDOMNode_get_parentNode(IHTMLDOMNode *iface, IHTMLDOMNode **p)
402 {
403     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
404     HTMLDOMNode *node;
405     nsIDOMNode *nsnode;
406     nsresult nsres;
407     HRESULT hres;
408
409     TRACE("(%p)->(%p)\n", This, p);
410
411     nsres = nsIDOMNode_GetParentNode(This->nsnode, &nsnode);
412     if(NS_FAILED(nsres)) {
413         ERR("GetParentNode failed: %08x\n", nsres);
414         return E_FAIL;
415     }
416
417     if(!nsnode) {
418         *p = NULL;
419         return S_OK;
420     }
421
422     hres = get_node(This->doc, nsnode, TRUE, &node);
423     nsIDOMNode_Release(nsnode);
424     if(FAILED(hres))
425         return hres;
426
427     *p = &node->IHTMLDOMNode_iface;
428     IHTMLDOMNode_AddRef(*p);
429     return S_OK;
430 }
431
432 static HRESULT WINAPI HTMLDOMNode_hasChildNodes(IHTMLDOMNode *iface, VARIANT_BOOL *fChildren)
433 {
434     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
435     PRBool has_child = FALSE;
436     nsresult nsres;
437
438     TRACE("(%p)->(%p)\n", This, fChildren);
439
440     nsres = nsIDOMNode_HasChildNodes(This->nsnode, &has_child);
441     if(NS_FAILED(nsres))
442         ERR("HasChildNodes failed: %08x\n", nsres);
443
444     *fChildren = has_child ? VARIANT_TRUE : VARIANT_FALSE;
445     return S_OK;
446 }
447
448 static HRESULT WINAPI HTMLDOMNode_get_childNodes(IHTMLDOMNode *iface, IDispatch **p)
449 {
450     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
451     nsIDOMNodeList *nslist;
452     nsresult nsres;
453
454     TRACE("(%p)->(%p)\n", This, p);
455
456     nsres = nsIDOMNode_GetChildNodes(This->nsnode, &nslist);
457     if(NS_FAILED(nsres)) {
458         ERR("GetChildNodes failed: %08x\n", nsres);
459         return E_FAIL;
460     }
461
462     *p = (IDispatch*)create_child_collection(This->doc, nslist);
463     nsIDOMNodeList_Release(nslist);
464
465     return S_OK;
466 }
467
468 static HRESULT WINAPI HTMLDOMNode_get_attributes(IHTMLDOMNode *iface, IDispatch **p)
469 {
470     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
471     FIXME("(%p)->(%p)\n", This, p);
472     return E_NOTIMPL;
473 }
474
475 static HRESULT WINAPI HTMLDOMNode_insertBefore(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
476                                                VARIANT refChild, IHTMLDOMNode **node)
477 {
478     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
479     nsIDOMNode *nsnode, *nsref = NULL;
480     HTMLDOMNode *new_child;
481     HTMLDOMNode *node_obj;
482     nsresult nsres;
483     HRESULT hres;
484
485     TRACE("(%p)->(%p %s %p)\n", This, newChild, debugstr_variant(&refChild), node);
486
487     new_child = get_node_obj(This->doc, (IUnknown*)newChild);
488     if(!new_child) {
489         ERR("invalid newChild\n");
490         return E_INVALIDARG;
491     }
492
493     switch(V_VT(&refChild)) {
494     case VT_NULL:
495         break;
496     case VT_DISPATCH: {
497         HTMLDOMNode *ref_node;
498
499         ref_node = get_node_obj(This->doc, (IUnknown*)V_DISPATCH(&refChild));
500         if(!ref_node) {
501             ERR("unvalid node\n");
502             return E_FAIL;
503         }
504
505         nsref = ref_node->nsnode;
506         break;
507     }
508     default:
509         FIXME("unimplemented vt %d\n", V_VT(&refChild));
510         return E_NOTIMPL;
511     }
512
513     nsres = nsIDOMNode_InsertBefore(This->nsnode, new_child->nsnode, nsref, &nsnode);
514     if(NS_FAILED(nsres)) {
515         ERR("InsertBefore failed: %08x\n", nsres);
516         return E_FAIL;
517     }
518
519     hres = get_node(This->doc, nsnode, TRUE, &node_obj);
520     nsIDOMNode_Release(nsnode);
521     if(FAILED(hres))
522         return hres;
523
524     *node = &node_obj->IHTMLDOMNode_iface;
525     IHTMLDOMNode_AddRef(*node);
526     return S_OK;
527 }
528
529 static HRESULT WINAPI HTMLDOMNode_removeChild(IHTMLDOMNode *iface, IHTMLDOMNode *oldChild,
530                                               IHTMLDOMNode **node)
531 {
532     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
533     HTMLDOMNode *node_obj;
534     nsIDOMNode *nsnode;
535     nsresult nsres;
536     HRESULT hres;
537
538     TRACE("(%p)->(%p %p)\n", This, oldChild, node);
539
540     node_obj = get_node_obj(This->doc, (IUnknown*)oldChild);
541     if(!node_obj)
542         return E_FAIL;
543
544     nsres = nsIDOMNode_RemoveChild(This->nsnode, node_obj->nsnode, &nsnode);
545     if(NS_FAILED(nsres)) {
546         ERR("RemoveChild failed: %08x\n", nsres);
547         return E_FAIL;
548     }
549
550     hres = get_node(This->doc, nsnode, TRUE, &node_obj);
551     nsIDOMNode_Release(nsnode);
552     if(FAILED(hres))
553         return hres;
554
555     /* FIXME: Make sure that node != newChild */
556     *node = &node_obj->IHTMLDOMNode_iface;
557     IHTMLDOMNode_AddRef(*node);
558     return S_OK;
559 }
560
561 static HRESULT WINAPI HTMLDOMNode_replaceChild(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
562                                                IHTMLDOMNode *oldChild, IHTMLDOMNode **node)
563 {
564     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
565     FIXME("(%p)->(%p %p %p)\n", This, newChild, oldChild, node);
566     return E_NOTIMPL;
567 }
568
569 static HRESULT WINAPI HTMLDOMNode_cloneNode(IHTMLDOMNode *iface, VARIANT_BOOL fDeep,
570                                             IHTMLDOMNode **clonedNode)
571 {
572     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
573     HTMLDOMNode *new_node;
574     nsIDOMNode *nsnode;
575     nsresult nsres;
576     HRESULT hres;
577
578     TRACE("(%p)->(%x %p)\n", This, fDeep, clonedNode);
579
580     nsres = nsIDOMNode_CloneNode(This->nsnode, fDeep != VARIANT_FALSE, &nsnode);
581     if(NS_FAILED(nsres) || !nsnode) {
582         ERR("CloneNode failed: %08x\n", nsres);
583         return E_FAIL;
584     }
585
586     hres = This->vtbl->clone(This, nsnode, &new_node);
587     if(FAILED(hres))
588         return hres;
589
590     *clonedNode = &new_node->IHTMLDOMNode_iface;
591     return S_OK;
592 }
593
594 static HRESULT WINAPI HTMLDOMNode_removeNode(IHTMLDOMNode *iface, VARIANT_BOOL fDeep,
595                                              IHTMLDOMNode **removed)
596 {
597     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
598     FIXME("(%p)->(%x %p)\n", This, fDeep, removed);
599     return E_NOTIMPL;
600 }
601
602 static HRESULT WINAPI HTMLDOMNode_swapNode(IHTMLDOMNode *iface, IHTMLDOMNode *otherNode,
603                                            IHTMLDOMNode **swappedNode)
604 {
605     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
606     FIXME("(%p)->(%p %p)\n", This, otherNode, swappedNode);
607     return E_NOTIMPL;
608 }
609
610 static HRESULT WINAPI HTMLDOMNode_replaceNode(IHTMLDOMNode *iface, IHTMLDOMNode *replacement,
611                                               IHTMLDOMNode **replaced)
612 {
613     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
614     FIXME("(%p)->(%p %p)\n", This, replacement, replaced);
615     return E_NOTIMPL;
616 }
617
618 static HRESULT WINAPI HTMLDOMNode_appendChild(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
619                                               IHTMLDOMNode **node)
620 {
621     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
622     HTMLDOMNode *node_obj;
623     nsIDOMNode *nsnode;
624     nsresult nsres;
625     HRESULT hres;
626
627     TRACE("(%p)->(%p %p)\n", This, newChild, node);
628
629     node_obj = get_node_obj(This->doc, (IUnknown*)newChild);
630     if(!node_obj)
631         return E_FAIL;
632
633     nsres = nsIDOMNode_AppendChild(This->nsnode, node_obj->nsnode, &nsnode);
634     if(NS_FAILED(nsres)) {
635         WARN("AppendChild failed: %08x\n", nsres);
636         nsnode = node_obj->nsnode;
637     }
638
639     hres = get_node(This->doc, nsnode, TRUE, &node_obj);
640     nsIDOMNode_Release(nsnode);
641     if(FAILED(hres))
642         return hres;
643
644     /* FIXME: Make sure that node != newChild */
645     *node = &node_obj->IHTMLDOMNode_iface;
646     IHTMLDOMNode_AddRef(*node);
647     return S_OK;
648 }
649
650 static HRESULT WINAPI HTMLDOMNode_get_nodeName(IHTMLDOMNode *iface, BSTR *p)
651 {
652     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
653
654     TRACE("(%p)->(%p)\n", This, p);
655
656     *p = NULL;
657
658     if(This->nsnode) {
659         nsAString name_str;
660         const PRUnichar *name;
661         nsresult nsres;
662
663         nsAString_Init(&name_str, NULL);
664         nsres = nsIDOMNode_GetNodeName(This->nsnode, &name_str);
665
666         if(NS_SUCCEEDED(nsres)) {
667             nsAString_GetData(&name_str, &name);
668             *p = SysAllocString(name);
669         }else {
670             ERR("GetNodeName failed: %08x\n", nsres);
671         }
672
673         nsAString_Finish(&name_str);
674     }
675
676     return S_OK;
677 }
678
679 static HRESULT WINAPI HTMLDOMNode_put_nodeValue(IHTMLDOMNode *iface, VARIANT v)
680 {
681     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
682
683     TRACE("(%p)->()\n", This);
684
685     switch(V_VT(&v)) {
686     case VT_BSTR: {
687         nsAString val_str;
688
689         TRACE("bstr %s\n", debugstr_w(V_BSTR(&v)));
690
691         nsAString_InitDepend(&val_str, V_BSTR(&v));
692         nsIDOMNode_SetNodeValue(This->nsnode, &val_str);
693         nsAString_Finish(&val_str);
694
695         return S_OK;
696     }
697
698     default:
699         FIXME("unsupported vt %d\n", V_VT(&v));
700     }
701
702     return E_NOTIMPL;
703 }
704
705 static HRESULT WINAPI HTMLDOMNode_get_nodeValue(IHTMLDOMNode *iface, VARIANT *p)
706 {
707     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
708     const PRUnichar *val;
709     nsAString val_str;
710
711     TRACE("(%p)->(%p)\n", This, p);
712
713     nsAString_Init(&val_str, NULL);
714     nsIDOMNode_GetNodeValue(This->nsnode, &val_str);
715     nsAString_GetData(&val_str, &val);
716
717     if(*val) {
718         V_VT(p) = VT_BSTR;
719         V_BSTR(p) = SysAllocString(val);
720     }else {
721         V_VT(p) = VT_NULL;
722     }
723
724     nsAString_Finish(&val_str);
725
726     return S_OK;
727 }
728
729 static HRESULT WINAPI HTMLDOMNode_get_firstChild(IHTMLDOMNode *iface, IHTMLDOMNode **p)
730 {
731     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
732     nsIDOMNode *nschild = NULL;
733     HTMLDOMNode *node;
734     HRESULT hres;
735
736     TRACE("(%p)->(%p)\n", This, p);
737
738     nsIDOMNode_GetFirstChild(This->nsnode, &nschild);
739     if(!nschild) {
740         *p = NULL;
741         return S_OK;
742     }
743
744     hres = get_node(This->doc, nschild, TRUE, &node);
745     nsIDOMNode_Release(nschild);
746     if(FAILED(hres))
747         return hres;
748
749     *p = &node->IHTMLDOMNode_iface;
750     IHTMLDOMNode_AddRef(*p);
751     return S_OK;
752 }
753
754 static HRESULT WINAPI HTMLDOMNode_get_lastChild(IHTMLDOMNode *iface, IHTMLDOMNode **p)
755 {
756     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
757     nsIDOMNode *nschild = NULL;
758     HTMLDOMNode *node;
759     HRESULT hres;
760
761     TRACE("(%p)->(%p)\n", This, p);
762
763     nsIDOMNode_GetLastChild(This->nsnode, &nschild);
764     if(!nschild) {
765         *p = NULL;
766         return S_OK;
767     }
768
769     hres = get_node(This->doc, nschild, TRUE, &node);
770     nsIDOMNode_Release(nschild);
771     if(FAILED(hres))
772         return hres;
773
774     *p = &node->IHTMLDOMNode_iface;
775     IHTMLDOMNode_AddRef(*p);
776     return S_OK;
777 }
778
779 static HRESULT WINAPI HTMLDOMNode_get_previousSibling(IHTMLDOMNode *iface, IHTMLDOMNode **p)
780 {
781     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
782     FIXME("(%p)->(%p)\n", This, p);
783     return E_NOTIMPL;
784 }
785
786 static HRESULT WINAPI HTMLDOMNode_get_nextSibling(IHTMLDOMNode *iface, IHTMLDOMNode **p)
787 {
788     HTMLDOMNode *This = impl_from_IHTMLDOMNode(iface);
789     nsIDOMNode *nssibling = NULL;
790     HTMLDOMNode *node;
791     HRESULT hres;
792
793     TRACE("(%p)->(%p)\n", This, p);
794
795     nsIDOMNode_GetNextSibling(This->nsnode, &nssibling);
796     if(!nssibling) {
797         *p = NULL;
798         return S_OK;
799     }
800
801     hres = get_node(This->doc, nssibling, TRUE, &node);
802     nsIDOMNode_Release(nssibling);
803     if(FAILED(hres))
804         return hres;
805
806     *p = &node->IHTMLDOMNode_iface;
807     IHTMLDOMNode_AddRef(*p);
808     return S_OK;
809 }
810
811 static const IHTMLDOMNodeVtbl HTMLDOMNodeVtbl = {
812     HTMLDOMNode_QueryInterface,
813     HTMLDOMNode_AddRef,
814     HTMLDOMNode_Release,
815     HTMLDOMNode_GetTypeInfoCount,
816     HTMLDOMNode_GetTypeInfo,
817     HTMLDOMNode_GetIDsOfNames,
818     HTMLDOMNode_Invoke,
819     HTMLDOMNode_get_nodeType,
820     HTMLDOMNode_get_parentNode,
821     HTMLDOMNode_hasChildNodes,
822     HTMLDOMNode_get_childNodes,
823     HTMLDOMNode_get_attributes,
824     HTMLDOMNode_insertBefore,
825     HTMLDOMNode_removeChild,
826     HTMLDOMNode_replaceChild,
827     HTMLDOMNode_cloneNode,
828     HTMLDOMNode_removeNode,
829     HTMLDOMNode_swapNode,
830     HTMLDOMNode_replaceNode,
831     HTMLDOMNode_appendChild,
832     HTMLDOMNode_get_nodeName,
833     HTMLDOMNode_put_nodeValue,
834     HTMLDOMNode_get_nodeValue,
835     HTMLDOMNode_get_firstChild,
836     HTMLDOMNode_get_lastChild,
837     HTMLDOMNode_get_previousSibling,
838     HTMLDOMNode_get_nextSibling
839 };
840
841 static inline HTMLDOMNode *impl_from_IHTMLDOMNode2(IHTMLDOMNode2 *iface)
842 {
843     return CONTAINING_RECORD(iface, HTMLDOMNode, IHTMLDOMNode2_iface);
844 }
845
846 static HRESULT WINAPI HTMLDOMNode2_QueryInterface(IHTMLDOMNode2 *iface,
847         REFIID riid, void **ppv)
848 {
849     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
850
851     return IHTMLDOMNode_QueryInterface(&This->IHTMLDOMNode_iface, riid, ppv);
852 }
853
854 static ULONG WINAPI HTMLDOMNode2_AddRef(IHTMLDOMNode2 *iface)
855 {
856     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
857
858     return IHTMLDOMNode_AddRef(&This->IHTMLDOMNode_iface);
859 }
860
861 static ULONG WINAPI HTMLDOMNode2_Release(IHTMLDOMNode2 *iface)
862 {
863     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
864
865     return IHTMLDOMNode_Release(&This->IHTMLDOMNode_iface);
866 }
867
868 static HRESULT WINAPI HTMLDOMNode2_GetTypeInfoCount(IHTMLDOMNode2 *iface, UINT *pctinfo)
869 {
870     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
871     return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo);
872 }
873
874 static HRESULT WINAPI HTMLDOMNode2_GetTypeInfo(IHTMLDOMNode2 *iface, UINT iTInfo,
875         LCID lcid, ITypeInfo **ppTInfo)
876 {
877     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
878     return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo);
879 }
880
881 static HRESULT WINAPI HTMLDOMNode2_GetIDsOfNames(IHTMLDOMNode2 *iface, REFIID riid,
882                                                 LPOLESTR *rgszNames, UINT cNames,
883                                                 LCID lcid, DISPID *rgDispId)
884 {
885     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
886     return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId);
887 }
888
889 static HRESULT WINAPI HTMLDOMNode2_Invoke(IHTMLDOMNode2 *iface, DISPID dispIdMember,
890         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
891         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
892 {
893     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
894     return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid,
895             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
896 }
897
898 static HRESULT WINAPI HTMLDOMNode2_get_ownerDocument(IHTMLDOMNode2 *iface, IDispatch **p)
899 {
900     HTMLDOMNode *This = impl_from_IHTMLDOMNode2(iface);
901
902     TRACE("(%p)->(%p)\n", This, p);
903
904     /* FIXME: Better check for document node */
905     if(This == &This->doc->node) {
906         *p = NULL;
907     }else {
908         *p = (IDispatch*)&This->doc->basedoc.IHTMLDocument2_iface;
909         IDispatch_AddRef(*p);
910     }
911     return S_OK;
912 }
913
914 static const IHTMLDOMNode2Vtbl HTMLDOMNode2Vtbl = {
915     HTMLDOMNode2_QueryInterface,
916     HTMLDOMNode2_AddRef,
917     HTMLDOMNode2_Release,
918     HTMLDOMNode2_GetTypeInfoCount,
919     HTMLDOMNode2_GetTypeInfo,
920     HTMLDOMNode2_GetIDsOfNames,
921     HTMLDOMNode2_Invoke,
922     HTMLDOMNode2_get_ownerDocument
923 };
924
925 HRESULT HTMLDOMNode_QI(HTMLDOMNode *This, REFIID riid, void **ppv)
926 {
927     *ppv = NULL;
928
929     if(IsEqualGUID(&IID_IUnknown, riid)) {
930         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
931         *ppv = &This->IHTMLDOMNode_iface;
932     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
933         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
934         *ppv = &This->IHTMLDOMNode_iface;
935     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
936         if(This->dispex.data) {
937             TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
938             *ppv = DISPATCHEX(&This->dispex);
939         }else {
940             FIXME("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
941             return E_NOINTERFACE;
942         }
943     }else if(IsEqualGUID(&IID_IHTMLDOMNode, riid)) {
944         TRACE("(%p)->(IID_IHTMLDOMNode %p)\n", This, ppv);
945         *ppv = &This->IHTMLDOMNode_iface;
946     }else if(IsEqualGUID(&IID_IHTMLDOMNode2, riid)) {
947         TRACE("(%p)->(IID_IHTMLDOMNode2 %p)\n", This, ppv);
948         *ppv = &This->IHTMLDOMNode2_iface;
949     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
950         return *ppv ? S_OK : E_NOINTERFACE;
951     }
952
953     if(*ppv) {
954         IUnknown_AddRef((IUnknown*)*ppv);
955         return S_OK;
956     }
957
958     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
959     return E_NOINTERFACE;
960 }
961
962 void HTMLDOMNode_destructor(HTMLDOMNode *This)
963 {
964     if(This->nsnode)
965         nsIDOMNode_Release(This->nsnode);
966     if(This->event_target)
967         release_event_target(This->event_target);
968 }
969
970 static HRESULT HTMLDOMNode_clone(HTMLDOMNode *This, nsIDOMNode *nsnode, HTMLDOMNode **ret)
971 {
972     HRESULT hres;
973
974     hres = create_node(This->doc, nsnode, ret);
975     if(FAILED(hres))
976         return hres;
977
978     IHTMLDOMNode_AddRef(&(*ret)->IHTMLDOMNode_iface);
979     return S_OK;
980 }
981
982 static const NodeImplVtbl HTMLDOMNodeImplVtbl = {
983     HTMLDOMNode_QI,
984     HTMLDOMNode_destructor,
985     HTMLDOMNode_clone
986 };
987
988 void HTMLDOMNode_Init(HTMLDocumentNode *doc, HTMLDOMNode *node, nsIDOMNode *nsnode)
989 {
990     node->IHTMLDOMNode_iface.lpVtbl = &HTMLDOMNodeVtbl;
991     node->IHTMLDOMNode2_iface.lpVtbl = &HTMLDOMNode2Vtbl;
992     node->ref = 1;
993     node->doc = doc;
994
995     if(nsnode)
996         nsIDOMNode_AddRef(nsnode);
997     node->nsnode = nsnode;
998
999     node->next = doc->nodes;
1000     doc->nodes = node;
1001 }
1002
1003 static HRESULT create_node(HTMLDocumentNode *doc, nsIDOMNode *nsnode, HTMLDOMNode **ret)
1004 {
1005     PRUint16 node_type;
1006     HRESULT hres;
1007
1008     nsIDOMNode_GetNodeType(nsnode, &node_type);
1009
1010     switch(node_type) {
1011     case ELEMENT_NODE: {
1012         HTMLElement *elem;
1013         hres = HTMLElement_Create(doc, nsnode, FALSE, &elem);
1014         if(FAILED(hres))
1015             return hres;
1016         *ret = &elem->node;
1017         break;
1018     }
1019     case TEXT_NODE:
1020         hres = HTMLDOMTextNode_Create(doc, nsnode, ret);
1021         if(FAILED(hres))
1022             return hres;
1023         break;
1024     case COMMENT_NODE: {
1025         HTMLElement *comment;
1026         hres = HTMLCommentElement_Create(doc, nsnode, &comment);
1027         if(FAILED(hres))
1028             return hres;
1029         *ret = &comment->node;
1030         break;
1031     }
1032     default: {
1033         HTMLDOMNode *node;
1034
1035         node = heap_alloc_zero(sizeof(HTMLDOMNode));
1036         if(!node)
1037             return E_OUTOFMEMORY;
1038
1039         node->vtbl = &HTMLDOMNodeImplVtbl;
1040         HTMLDOMNode_Init(doc, node, nsnode);
1041         *ret = node;
1042     }
1043     }
1044
1045     TRACE("type %d ret %p\n", node_type, *ret);
1046     return S_OK;
1047 }
1048
1049 /*
1050  * FIXME
1051  * List looks really ugly here. We should use a better data structure or
1052  * (better) find a way to store HTMLDOMelement pointer in nsIDOMNode.
1053  */
1054
1055 HRESULT get_node(HTMLDocumentNode *This, nsIDOMNode *nsnode, BOOL create, HTMLDOMNode **ret)
1056 {
1057     HTMLDOMNode *iter = This->nodes;
1058
1059     while(iter) {
1060         if(iter->nsnode == nsnode)
1061             break;
1062         iter = iter->next;
1063     }
1064
1065     if(iter || !create) {
1066         *ret = iter;
1067         return S_OK;
1068     }
1069
1070     return create_node(This, nsnode, ret);
1071 }
1072
1073 /*
1074  * FIXME
1075  * We should use better way for getting node object (like private interface)
1076  * or avoid it at all.
1077  */
1078 static HTMLDOMNode *get_node_obj(HTMLDocumentNode *This, IUnknown *iface)
1079 {
1080     HTMLDOMNode *iter = This->nodes;
1081     IHTMLDOMNode *node;
1082
1083     IUnknown_QueryInterface(iface, &IID_IHTMLDOMNode, (void**)&node);
1084     IHTMLDOMNode_Release(node);
1085
1086     while(iter) {
1087         if(&iter->IHTMLDOMNode_iface == node)
1088             return iter;
1089         iter = iter->next;
1090     }
1091
1092     FIXME("Not found %p\n", iface);
1093     return NULL;
1094 }
1095
1096 void release_nodes(HTMLDocumentNode *This)
1097 {
1098     HTMLDOMNode *iter, *next;
1099
1100     if(!This->nodes)
1101         return;
1102
1103     for(iter = This->nodes; iter; iter = next) {
1104         next = iter->next;
1105         iter->doc = NULL;
1106         if(&This->node != iter)
1107             IHTMLDOMNode_Release(&iter->IHTMLDOMNode_iface);
1108     }
1109 }