mshtml: Moved cloneNode implementation to vtbl.
[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 HTMLDOMNode *create_node(HTMLDocumentNode*,nsIDOMNode*);
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     PRUint32 length=0;
157     nsresult nsres;
158
159     TRACE("(%p)->(%d %p)\n", This, index, ppItem);
160
161     if (ppItem)
162         *ppItem = NULL;
163     else
164         return E_POINTER;
165
166     nsIDOMNodeList_GetLength(This->nslist, &length);
167     if(index < 0 || index >= length)
168         return E_INVALIDARG;
169
170     nsres = nsIDOMNodeList_Item(This->nslist, index, &nsnode);
171     if(NS_FAILED(nsres) || !nsnode) {
172         ERR("Item failed: %08x\n", nsres);
173         return E_FAIL;
174     }
175
176     *ppItem = (IDispatch*)get_node(This->doc, nsnode, TRUE);
177     IDispatch_AddRef(*ppItem);
178     return S_OK;
179 }
180
181 #define DISPID_CHILDCOL_0 MSHTML_DISPID_CUSTOM_MIN
182
183 static HRESULT HTMLDOMChildrenCollection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
184 {
185     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
186     WCHAR *ptr;
187     DWORD idx=0;
188     PRUint32 len = 0;
189
190     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
191         idx = idx*10 + (*ptr-'0');
192     if(*ptr)
193         return DISP_E_UNKNOWNNAME;
194
195     nsIDOMNodeList_GetLength(This->nslist, &len);
196     if(idx >= len)
197         return DISP_E_UNKNOWNNAME;
198
199     *dispid = DISPID_CHILDCOL_0 + idx;
200     TRACE("ret %x\n", *dispid);
201     return S_OK;
202 }
203
204 static HRESULT HTMLDOMChildrenCollection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
205         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
206 {
207     HTMLDOMChildrenCollection *This = HTMLCHILDCOL_THIS(iface);
208
209     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, flags, params, res, ei, caller);
210
211     switch(flags) {
212     case DISPATCH_PROPERTYGET: {
213         IDispatch *disp = NULL;
214         HRESULT hres;
215
216         hres = IHTMLDOMChildrenCollection_item(HTMLCHILDCOL(This), id - DISPID_CHILDCOL_0, &disp);
217         if(0&&FAILED(hres))
218             return hres;
219
220         V_VT(res) = VT_DISPATCH;
221         V_DISPATCH(res) = disp;
222         break;
223     }
224
225     default:
226         FIXME("unimplemented flags %x\n", flags);
227         return E_NOTIMPL;
228     }
229
230     return S_OK;
231 }
232
233 #undef HTMLCHILDCOL_THIS
234
235 static const IHTMLDOMChildrenCollectionVtbl HTMLDOMChildrenCollectionVtbl = {
236     HTMLDOMChildrenCollection_QueryInterface,
237     HTMLDOMChildrenCollection_AddRef,
238     HTMLDOMChildrenCollection_Release,
239     HTMLDOMChildrenCollection_GetTypeInfoCount,
240     HTMLDOMChildrenCollection_GetTypeInfo,
241     HTMLDOMChildrenCollection_GetIDsOfNames,
242     HTMLDOMChildrenCollection_Invoke,
243     HTMLDOMChildrenCollection_get_length,
244     HTMLDOMChildrenCollection__newEnum,
245     HTMLDOMChildrenCollection_item
246 };
247
248 static const tid_t HTMLDOMChildrenCollection_iface_tids[] = {
249     IHTMLDOMChildrenCollection_tid,
250     0
251 };
252
253 static const dispex_static_data_vtbl_t HTMLDOMChildrenCollection_dispex_vtbl = {
254     NULL,
255     HTMLDOMChildrenCollection_get_dispid,
256     HTMLDOMChildrenCollection_invoke
257 };
258
259 static dispex_static_data_t HTMLDOMChildrenCollection_dispex = {
260     &HTMLDOMChildrenCollection_dispex_vtbl,
261     DispDOMChildrenCollection_tid,
262     NULL,
263     HTMLDOMChildrenCollection_iface_tids
264 };
265
266 static IHTMLDOMChildrenCollection *create_child_collection(HTMLDocumentNode *doc, nsIDOMNodeList *nslist)
267 {
268     HTMLDOMChildrenCollection *ret;
269
270     ret = heap_alloc_zero(sizeof(*ret));
271     ret->lpIHTMLDOMChildrenCollectionVtbl = &HTMLDOMChildrenCollectionVtbl;
272     ret->ref = 1;
273
274     nsIDOMNodeList_AddRef(nslist);
275     ret->nslist = nslist;
276     ret->doc = doc;
277
278     init_dispex(&ret->dispex, (IUnknown*)HTMLCHILDCOL(ret), &HTMLDOMChildrenCollection_dispex);
279
280     return HTMLCHILDCOL(ret);
281 }
282
283 #define HTMLDOMNODE_THIS(iface) DEFINE_THIS(HTMLDOMNode, HTMLDOMNode, iface)
284
285 static HRESULT WINAPI HTMLDOMNode_QueryInterface(IHTMLDOMNode *iface,
286                                                  REFIID riid, void **ppv)
287 {
288     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
289
290     return This->vtbl->qi(This, riid, ppv);
291 }
292
293 static ULONG WINAPI HTMLDOMNode_AddRef(IHTMLDOMNode *iface)
294 {
295     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
296     LONG ref = InterlockedIncrement(&This->ref);
297
298     TRACE("(%p) ref=%d\n", This, ref);
299
300     return ref;
301 }
302
303 static ULONG WINAPI HTMLDOMNode_Release(IHTMLDOMNode *iface)
304 {
305     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
306     LONG ref = InterlockedDecrement(&This->ref);
307
308     TRACE("(%p) ref=%d\n", This, ref);
309
310     if(!ref) {
311         This->vtbl->destructor(This);
312         release_dispex(&This->dispex);
313         heap_free(This);
314     }
315
316     return ref;
317 }
318
319 static HRESULT WINAPI HTMLDOMNode_GetTypeInfoCount(IHTMLDOMNode *iface, UINT *pctinfo)
320 {
321     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
322     return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo);
323 }
324
325 static HRESULT WINAPI HTMLDOMNode_GetTypeInfo(IHTMLDOMNode *iface, UINT iTInfo,
326                                               LCID lcid, ITypeInfo **ppTInfo)
327 {
328     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
329     return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo);
330 }
331
332 static HRESULT WINAPI HTMLDOMNode_GetIDsOfNames(IHTMLDOMNode *iface, REFIID riid,
333                                                 LPOLESTR *rgszNames, UINT cNames,
334                                                 LCID lcid, DISPID *rgDispId)
335 {
336     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
337     return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId);
338 }
339
340 static HRESULT WINAPI HTMLDOMNode_Invoke(IHTMLDOMNode *iface, DISPID dispIdMember,
341                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
342                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
343 {
344     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
345     return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid,
346             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
347 }
348
349 static HRESULT WINAPI HTMLDOMNode_get_nodeType(IHTMLDOMNode *iface, LONG *p)
350 {
351     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
352     PRUint16 type = -1;
353
354     TRACE("(%p)->(%p)\n", This, p);
355
356     nsIDOMNode_GetNodeType(This->nsnode, &type);
357
358     switch(type) {
359     case ELEMENT_NODE:
360         *p = 1;
361         break;
362     case TEXT_NODE:
363         *p = 3;
364         break;
365     case COMMENT_NODE:
366         *p = 8;
367         break;
368     case DOCUMENT_NODE:
369         *p = 9;
370         break;
371     default:
372         /*
373          * FIXME:
374          * According to MSDN only ELEMENT_NODE and TEXT_NODE are supported.
375          * It needs more tests.
376          */
377         FIXME("type %u\n", type);
378         *p = 0;
379     }
380
381     return S_OK;
382 }
383
384 static HRESULT WINAPI HTMLDOMNode_get_parentNode(IHTMLDOMNode *iface, IHTMLDOMNode **p)
385 {
386     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
387     HTMLDOMNode *node;
388     nsIDOMNode *nsnode;
389     nsresult nsres;
390
391     TRACE("(%p)->(%p)\n", This, p);
392
393     nsres = nsIDOMNode_GetParentNode(This->nsnode, &nsnode);
394     if(NS_FAILED(nsres)) {
395         ERR("GetParentNode failed: %08x\n", nsres);
396         return E_FAIL;
397     }
398
399     if(!nsnode) {
400         *p = NULL;
401         return S_OK;
402     }
403
404     node = get_node(This->doc, nsnode, TRUE);
405     *p = HTMLDOMNODE(node);
406     IHTMLDOMNode_AddRef(*p);
407     return S_OK;
408 }
409
410 static HRESULT WINAPI HTMLDOMNode_hasChildNodes(IHTMLDOMNode *iface, VARIANT_BOOL *fChildren)
411 {
412     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
413     PRBool has_child = FALSE;
414     nsresult nsres;
415
416     TRACE("(%p)->(%p)\n", This, fChildren);
417
418     nsres = nsIDOMNode_HasChildNodes(This->nsnode, &has_child);
419     if(NS_FAILED(nsres))
420         ERR("HasChildNodes failed: %08x\n", nsres);
421
422     *fChildren = has_child ? VARIANT_TRUE : VARIANT_FALSE;
423     return S_OK;
424 }
425
426 static HRESULT WINAPI HTMLDOMNode_get_childNodes(IHTMLDOMNode *iface, IDispatch **p)
427 {
428     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
429     nsIDOMNodeList *nslist;
430     nsresult nsres;
431
432     TRACE("(%p)->(%p)\n", This, p);
433
434     nsres = nsIDOMNode_GetChildNodes(This->nsnode, &nslist);
435     if(NS_FAILED(nsres)) {
436         ERR("GetChildNodes failed: %08x\n", nsres);
437         return E_FAIL;
438     }
439
440     *p = (IDispatch*)create_child_collection(This->doc, nslist);
441     nsIDOMNodeList_Release(nslist);
442
443     return S_OK;
444 }
445
446 static HRESULT WINAPI HTMLDOMNode_get_attributes(IHTMLDOMNode *iface, IDispatch **p)
447 {
448     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
449     FIXME("(%p)->(%p)\n", This, p);
450     return E_NOTIMPL;
451 }
452
453 static HRESULT WINAPI HTMLDOMNode_insertBefore(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
454                                                VARIANT refChild, IHTMLDOMNode **node)
455 {
456     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
457     nsIDOMNode *nsnode, *nsref = NULL;
458     HTMLDOMNode *new_child;
459     nsresult nsres;
460
461     TRACE("(%p)->(%p %s %p)\n", This, newChild, debugstr_variant(&refChild), node);
462
463     new_child = get_node_obj(This->doc, (IUnknown*)newChild);
464     if(!new_child) {
465         ERR("invalid newChild\n");
466         return E_INVALIDARG;
467     }
468
469     switch(V_VT(&refChild)) {
470     case VT_NULL:
471         break;
472     case VT_DISPATCH: {
473         HTMLDOMNode *ref_node;
474
475         ref_node = get_node_obj(This->doc, (IUnknown*)V_DISPATCH(&refChild));
476         if(!ref_node) {
477             ERR("unvalid node\n");
478             return E_FAIL;
479         }
480
481         nsref = ref_node->nsnode;
482         break;
483     }
484     default:
485         FIXME("unimplemented vt %d\n", V_VT(&refChild));
486         return E_NOTIMPL;
487     }
488
489     nsres = nsIDOMNode_InsertBefore(This->nsnode, new_child->nsnode, nsref, &nsnode);
490     if(NS_FAILED(nsres)) {
491         ERR("InsertBefore failed: %08x\n", nsres);
492         return E_FAIL;
493     }
494
495     *node = HTMLDOMNODE(get_node(This->doc, nsnode, TRUE));
496     nsIDOMNode_Release(nsnode);
497     IHTMLDOMNode_AddRef(*node);
498     return S_OK;
499 }
500
501 static HRESULT WINAPI HTMLDOMNode_removeChild(IHTMLDOMNode *iface, IHTMLDOMNode *oldChild,
502                                               IHTMLDOMNode **node)
503 {
504     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
505     HTMLDOMNode *node_obj;
506     nsIDOMNode *nsnode;
507     nsresult nsres;
508
509     TRACE("(%p)->(%p %p)\n", This, oldChild, node);
510
511     node_obj = get_node_obj(This->doc, (IUnknown*)oldChild);
512     if(!node_obj)
513         return E_FAIL;
514
515     nsres = nsIDOMNode_RemoveChild(This->nsnode, node_obj->nsnode, &nsnode);
516     if(NS_FAILED(nsres)) {
517         ERR("RemoveChild failed: %08x\n", nsres);
518         return E_FAIL;
519     }
520
521     /* FIXME: Make sure that node != newChild */
522     *node = HTMLDOMNODE(get_node(This->doc, nsnode, TRUE));
523     nsIDOMNode_Release(nsnode);
524     IHTMLDOMNode_AddRef(*node);
525     return S_OK;
526 }
527
528 static HRESULT WINAPI HTMLDOMNode_replaceChild(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
529                                                IHTMLDOMNode *oldChild, IHTMLDOMNode **node)
530 {
531     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
532     FIXME("(%p)->(%p %p %p)\n", This, newChild, oldChild, node);
533     return E_NOTIMPL;
534 }
535
536 static HRESULT WINAPI HTMLDOMNode_cloneNode(IHTMLDOMNode *iface, VARIANT_BOOL fDeep,
537                                             IHTMLDOMNode **clonedNode)
538 {
539     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
540     HTMLDOMNode *new_node;
541     nsIDOMNode *nsnode;
542     nsresult nsres;
543     HRESULT hres;
544
545     TRACE("(%p)->(%x %p)\n", This, fDeep, clonedNode);
546
547     nsres = nsIDOMNode_CloneNode(This->nsnode, fDeep != VARIANT_FALSE, &nsnode);
548     if(NS_FAILED(nsres) || !nsnode) {
549         ERR("CloneNode failed: %08x\n", nsres);
550         return E_FAIL;
551     }
552
553     hres = This->vtbl->clone(This, nsnode, &new_node);
554     if(FAILED(hres))
555         return hres;
556
557     *clonedNode = HTMLDOMNODE(new_node);
558     return S_OK;
559 }
560
561 static HRESULT WINAPI HTMLDOMNode_removeNode(IHTMLDOMNode *iface, VARIANT_BOOL fDeep,
562                                              IHTMLDOMNode **removed)
563 {
564     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
565     FIXME("(%p)->(%x %p)\n", This, fDeep, removed);
566     return E_NOTIMPL;
567 }
568
569 static HRESULT WINAPI HTMLDOMNode_swapNode(IHTMLDOMNode *iface, IHTMLDOMNode *otherNode,
570                                            IHTMLDOMNode **swappedNode)
571 {
572     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
573     FIXME("(%p)->(%p %p)\n", This, otherNode, swappedNode);
574     return E_NOTIMPL;
575 }
576
577 static HRESULT WINAPI HTMLDOMNode_replaceNode(IHTMLDOMNode *iface, IHTMLDOMNode *replacement,
578                                               IHTMLDOMNode **replaced)
579 {
580     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
581     FIXME("(%p)->(%p %p)\n", This, replacement, replaced);
582     return E_NOTIMPL;
583 }
584
585 static HRESULT WINAPI HTMLDOMNode_appendChild(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
586                                               IHTMLDOMNode **node)
587 {
588     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
589     HTMLDOMNode *node_obj;
590     nsIDOMNode *nsnode;
591     nsresult nsres;
592
593     TRACE("(%p)->(%p %p)\n", This, newChild, node);
594
595     node_obj = get_node_obj(This->doc, (IUnknown*)newChild);
596     if(!node_obj)
597         return E_FAIL;
598
599     nsres = nsIDOMNode_AppendChild(This->nsnode, node_obj->nsnode, &nsnode);
600     if(NS_FAILED(nsres)) {
601         WARN("AppendChild failed: %08x\n", nsres);
602         nsnode = node_obj->nsnode;
603     }
604
605     /* FIXME: Make sure that node != newChild */
606     *node = HTMLDOMNODE(get_node(This->doc, nsnode, TRUE));
607     IHTMLDOMNode_AddRef(*node);
608     return S_OK;
609 }
610
611 static HRESULT WINAPI HTMLDOMNode_get_nodeName(IHTMLDOMNode *iface, BSTR *p)
612 {
613     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
614
615     TRACE("(%p)->(%p)\n", This, p);
616
617     *p = NULL;
618
619     if(This->nsnode) {
620         nsAString name_str;
621         const PRUnichar *name;
622         nsresult nsres;
623
624         nsAString_Init(&name_str, NULL);
625         nsres = nsIDOMNode_GetNodeName(This->nsnode, &name_str);
626
627         if(NS_SUCCEEDED(nsres)) {
628             nsAString_GetData(&name_str, &name);
629             *p = SysAllocString(name);
630         }else {
631             ERR("GetNodeName failed: %08x\n", nsres);
632         }
633
634         nsAString_Finish(&name_str);
635     }
636
637     return S_OK;
638 }
639
640 static HRESULT WINAPI HTMLDOMNode_put_nodeValue(IHTMLDOMNode *iface, VARIANT v)
641 {
642     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
643
644     TRACE("(%p)->()\n", This);
645
646     switch(V_VT(&v)) {
647     case VT_BSTR: {
648         nsAString val_str;
649
650         TRACE("bstr %s\n", debugstr_w(V_BSTR(&v)));
651
652         nsAString_InitDepend(&val_str, V_BSTR(&v));
653         nsIDOMNode_SetNodeValue(This->nsnode, &val_str);
654         nsAString_Finish(&val_str);
655
656         return S_OK;
657     }
658
659     default:
660         FIXME("unsupported vt %d\n", V_VT(&v));
661     }
662
663     return E_NOTIMPL;
664 }
665
666 static HRESULT WINAPI HTMLDOMNode_get_nodeValue(IHTMLDOMNode *iface, VARIANT *p)
667 {
668     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
669     const PRUnichar *val;
670     nsAString val_str;
671
672     TRACE("(%p)->(%p)\n", This, p);
673
674     nsAString_Init(&val_str, NULL);
675     nsIDOMNode_GetNodeValue(This->nsnode, &val_str);
676     nsAString_GetData(&val_str, &val);
677
678     if(*val) {
679         V_VT(p) = VT_BSTR;
680         V_BSTR(p) = SysAllocString(val);
681     }else {
682         V_VT(p) = VT_NULL;
683     }
684
685     nsAString_Finish(&val_str);
686
687     return S_OK;
688 }
689
690 static HRESULT WINAPI HTMLDOMNode_get_firstChild(IHTMLDOMNode *iface, IHTMLDOMNode **p)
691 {
692     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
693     nsIDOMNode *nschild = NULL;
694
695     TRACE("(%p)->(%p)\n", This, p);
696
697     nsIDOMNode_GetFirstChild(This->nsnode, &nschild);
698     if(nschild) {
699         *p = HTMLDOMNODE(get_node(This->doc, nschild, TRUE));
700         IHTMLDOMNode_AddRef(*p);
701     }else {
702         *p = NULL;
703     }
704
705     return S_OK;
706 }
707
708 static HRESULT WINAPI HTMLDOMNode_get_lastChild(IHTMLDOMNode *iface, IHTMLDOMNode **p)
709 {
710     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
711     nsIDOMNode *nschild = NULL;
712
713     TRACE("(%p)->(%p)\n", This, p);
714
715     nsIDOMNode_GetLastChild(This->nsnode, &nschild);
716     if(nschild) {
717         *p = HTMLDOMNODE(get_node(This->doc, nschild, TRUE));
718         IHTMLDOMNode_AddRef(*p);
719     }else {
720         *p = NULL;
721     }
722
723     return S_OK;
724 }
725
726 static HRESULT WINAPI HTMLDOMNode_get_previousSibling(IHTMLDOMNode *iface, IHTMLDOMNode **p)
727 {
728     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
729     FIXME("(%p)->(%p)\n", This, p);
730     return E_NOTIMPL;
731 }
732
733 static HRESULT WINAPI HTMLDOMNode_get_nextSibling(IHTMLDOMNode *iface, IHTMLDOMNode **p)
734 {
735     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
736     nsIDOMNode *nssibling = NULL;
737
738     TRACE("(%p)->(%p)\n", This, p);
739
740     nsIDOMNode_GetNextSibling(This->nsnode, &nssibling);
741     if(nssibling) {
742         *p = HTMLDOMNODE(get_node(This->doc, nssibling, TRUE));
743         IHTMLDOMNode_AddRef(*p);
744     }else {
745         *p = NULL;
746     }
747
748     return S_OK;
749 }
750
751 #undef HTMLDOMNODE_THIS
752
753 static const IHTMLDOMNodeVtbl HTMLDOMNodeVtbl = {
754     HTMLDOMNode_QueryInterface,
755     HTMLDOMNode_AddRef,
756     HTMLDOMNode_Release,
757     HTMLDOMNode_GetTypeInfoCount,
758     HTMLDOMNode_GetTypeInfo,
759     HTMLDOMNode_GetIDsOfNames,
760     HTMLDOMNode_Invoke,
761     HTMLDOMNode_get_nodeType,
762     HTMLDOMNode_get_parentNode,
763     HTMLDOMNode_hasChildNodes,
764     HTMLDOMNode_get_childNodes,
765     HTMLDOMNode_get_attributes,
766     HTMLDOMNode_insertBefore,
767     HTMLDOMNode_removeChild,
768     HTMLDOMNode_replaceChild,
769     HTMLDOMNode_cloneNode,
770     HTMLDOMNode_removeNode,
771     HTMLDOMNode_swapNode,
772     HTMLDOMNode_replaceNode,
773     HTMLDOMNode_appendChild,
774     HTMLDOMNode_get_nodeName,
775     HTMLDOMNode_put_nodeValue,
776     HTMLDOMNode_get_nodeValue,
777     HTMLDOMNode_get_firstChild,
778     HTMLDOMNode_get_lastChild,
779     HTMLDOMNode_get_previousSibling,
780     HTMLDOMNode_get_nextSibling
781 };
782
783 #define HTMLDOMNODE2_THIS(iface) DEFINE_THIS(HTMLDOMNode, HTMLDOMNode2, iface)
784
785 static HRESULT WINAPI HTMLDOMNode2_QueryInterface(IHTMLDOMNode2 *iface,
786         REFIID riid, void **ppv)
787 {
788     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
789
790     return IHTMLDOMNode_QueryInterface(HTMLDOMNODE(This), riid, ppv);
791 }
792
793 static ULONG WINAPI HTMLDOMNode2_AddRef(IHTMLDOMNode2 *iface)
794 {
795     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
796
797     return IHTMLDOMNode_AddRef(HTMLDOMNODE(This));
798 }
799
800 static ULONG WINAPI HTMLDOMNode2_Release(IHTMLDOMNode2 *iface)
801 {
802     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
803
804     return IHTMLDOMNode_Release(HTMLDOMNODE(This));
805 }
806
807 static HRESULT WINAPI HTMLDOMNode2_GetTypeInfoCount(IHTMLDOMNode2 *iface, UINT *pctinfo)
808 {
809     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
810     return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo);
811 }
812
813 static HRESULT WINAPI HTMLDOMNode2_GetTypeInfo(IHTMLDOMNode2 *iface, UINT iTInfo,
814         LCID lcid, ITypeInfo **ppTInfo)
815 {
816     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
817     return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo);
818 }
819
820 static HRESULT WINAPI HTMLDOMNode2_GetIDsOfNames(IHTMLDOMNode2 *iface, REFIID riid,
821                                                 LPOLESTR *rgszNames, UINT cNames,
822                                                 LCID lcid, DISPID *rgDispId)
823 {
824     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
825     return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId);
826 }
827
828 static HRESULT WINAPI HTMLDOMNode2_Invoke(IHTMLDOMNode2 *iface, DISPID dispIdMember,
829         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
830         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
831 {
832     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
833     return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid,
834             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
835 }
836
837 static HRESULT WINAPI HTMLDOMNode2_get_ownerDocument(IHTMLDOMNode2 *iface, IDispatch **p)
838 {
839     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
840
841     TRACE("(%p)->(%p)\n", This, p);
842
843     /* FIXME: Better check for document node */
844     if(This == &This->doc->node) {
845         *p = NULL;
846     }else {
847         *p = (IDispatch*)HTMLDOC(&This->doc->basedoc);
848         IDispatch_AddRef(*p);
849     }
850     return S_OK;
851 }
852
853 #undef HTMLDOMNODE2_THIS
854
855 static const IHTMLDOMNode2Vtbl HTMLDOMNode2Vtbl = {
856     HTMLDOMNode2_QueryInterface,
857     HTMLDOMNode2_AddRef,
858     HTMLDOMNode2_Release,
859     HTMLDOMNode2_GetTypeInfoCount,
860     HTMLDOMNode2_GetTypeInfo,
861     HTMLDOMNode2_GetIDsOfNames,
862     HTMLDOMNode2_Invoke,
863     HTMLDOMNode2_get_ownerDocument
864 };
865
866 HRESULT HTMLDOMNode_QI(HTMLDOMNode *This, REFIID riid, void **ppv)
867 {
868     *ppv = NULL;
869
870     if(IsEqualGUID(&IID_IUnknown, riid)) {
871         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
872         *ppv = HTMLDOMNODE(This);
873     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
874         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
875         *ppv = HTMLDOMNODE(This);
876     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
877         if(This->dispex.data) {
878             TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
879             *ppv = DISPATCHEX(&This->dispex);
880         }else {
881             FIXME("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
882             return E_NOINTERFACE;
883         }
884     }else if(IsEqualGUID(&IID_IHTMLDOMNode, riid)) {
885         TRACE("(%p)->(IID_IHTMLDOMNode %p)\n", This, ppv);
886         *ppv = HTMLDOMNODE(This);
887     }else if(IsEqualGUID(&IID_IHTMLDOMNode2, riid)) {
888         TRACE("(%p)->(IID_IHTMLDOMNode2 %p)\n", This, ppv);
889         *ppv = HTMLDOMNODE2(This);
890     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
891         return *ppv ? S_OK : E_NOINTERFACE;
892     }
893
894     if(*ppv) {
895         IUnknown_AddRef((IUnknown*)*ppv);
896         return S_OK;
897     }
898
899     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
900     return E_NOINTERFACE;
901 }
902
903 void HTMLDOMNode_destructor(HTMLDOMNode *This)
904 {
905     if(This->nsnode)
906         nsIDOMNode_Release(This->nsnode);
907     if(This->event_target)
908         release_event_target(This->event_target);
909 }
910
911 static HRESULT HTMLDOMNode_clone(HTMLDOMNode *This, nsIDOMNode *nsnode, HTMLDOMNode **ret)
912 {
913     *ret = create_node(This->doc, nsnode);
914     IHTMLDOMNode_AddRef(HTMLDOMNODE(*ret));
915     return S_OK;
916 }
917
918 static const NodeImplVtbl HTMLDOMNodeImplVtbl = {
919     HTMLDOMNode_QI,
920     HTMLDOMNode_destructor,
921     HTMLDOMNode_clone
922 };
923
924 void HTMLDOMNode_Init(HTMLDocumentNode *doc, HTMLDOMNode *node, nsIDOMNode *nsnode)
925 {
926     node->lpHTMLDOMNodeVtbl = &HTMLDOMNodeVtbl;
927     node->lpHTMLDOMNode2Vtbl = &HTMLDOMNode2Vtbl;
928     node->ref = 1;
929     node->doc = doc;
930
931     if(nsnode)
932         nsIDOMNode_AddRef(nsnode);
933     node->nsnode = nsnode;
934
935     node->next = doc->nodes;
936     doc->nodes = node;
937 }
938
939 static HTMLDOMNode *create_node(HTMLDocumentNode *doc, nsIDOMNode *nsnode)
940 {
941     HTMLDOMNode *ret;
942     PRUint16 node_type;
943
944     nsIDOMNode_GetNodeType(nsnode, &node_type);
945
946     switch(node_type) {
947     case ELEMENT_NODE:
948         ret = &HTMLElement_Create(doc, nsnode, FALSE)->node;
949         break;
950     case TEXT_NODE:
951         ret = HTMLDOMTextNode_Create(doc, nsnode);
952         break;
953     case COMMENT_NODE:
954         ret = &HTMLCommentElement_Create(doc, nsnode)->node;
955         break;
956     default:
957         ret = heap_alloc_zero(sizeof(HTMLDOMNode));
958         ret->vtbl = &HTMLDOMNodeImplVtbl;
959         HTMLDOMNode_Init(doc, ret, nsnode);
960     }
961
962     TRACE("type %d ret %p\n", node_type, ret);
963
964     return ret;
965 }
966
967 /*
968  * FIXME
969  * List looks really ugly here. We should use a better data structure or
970  * (better) find a way to store HTMLDOMelement pointer in nsIDOMNode.
971  */
972
973 HTMLDOMNode *get_node(HTMLDocumentNode *This, nsIDOMNode *nsnode, BOOL create)
974 {
975     HTMLDOMNode *iter = This->nodes;
976
977     while(iter) {
978         if(iter->nsnode == nsnode)
979             break;
980         iter = iter->next;
981     }
982
983     if(iter || !create)
984         return iter;
985
986     return create_node(This, nsnode);
987 }
988
989 /*
990  * FIXME
991  * We should use better way for getting node object (like private interface)
992  * or avoid it at all.
993  */
994 static HTMLDOMNode *get_node_obj(HTMLDocumentNode *This, IUnknown *iface)
995 {
996     HTMLDOMNode *iter = This->nodes;
997     IHTMLDOMNode *node;
998
999     IUnknown_QueryInterface(iface, &IID_IHTMLDOMNode, (void**)&node);
1000     IHTMLDOMNode_Release(node);
1001
1002     while(iter) {
1003         if(HTMLDOMNODE(iter) == node)
1004             return iter;
1005         iter = iter->next;
1006     }
1007
1008     FIXME("Not found %p\n", iface);
1009     return NULL;
1010 }
1011
1012 void release_nodes(HTMLDocumentNode *This)
1013 {
1014     HTMLDOMNode *iter, *next;
1015
1016     if(!This->nodes)
1017         return;
1018
1019     for(iter = This->nodes; iter; iter = next) {
1020         next = iter->next;
1021         iter->doc = NULL;
1022         if(&This->node != iter)
1023             IHTMLDOMNode_Release(HTMLDOMNODE(iter));
1024     }
1025 }