mshtml: Added SetUIHandler tests.
[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     case DOCUMENT_FRAGMENT_NODE:
372         *p = 11;
373         break;
374     default:
375         /*
376          * FIXME:
377          * According to MSDN only ELEMENT_NODE and TEXT_NODE are supported.
378          * It needs more tests.
379          */
380         FIXME("type %u\n", type);
381         *p = 0;
382     }
383
384     return S_OK;
385 }
386
387 static HRESULT WINAPI HTMLDOMNode_get_parentNode(IHTMLDOMNode *iface, IHTMLDOMNode **p)
388 {
389     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
390     HTMLDOMNode *node;
391     nsIDOMNode *nsnode;
392     nsresult nsres;
393
394     TRACE("(%p)->(%p)\n", This, p);
395
396     nsres = nsIDOMNode_GetParentNode(This->nsnode, &nsnode);
397     if(NS_FAILED(nsres)) {
398         ERR("GetParentNode failed: %08x\n", nsres);
399         return E_FAIL;
400     }
401
402     if(!nsnode) {
403         *p = NULL;
404         return S_OK;
405     }
406
407     node = get_node(This->doc, nsnode, TRUE);
408     *p = HTMLDOMNODE(node);
409     IHTMLDOMNode_AddRef(*p);
410     return S_OK;
411 }
412
413 static HRESULT WINAPI HTMLDOMNode_hasChildNodes(IHTMLDOMNode *iface, VARIANT_BOOL *fChildren)
414 {
415     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
416     PRBool has_child = FALSE;
417     nsresult nsres;
418
419     TRACE("(%p)->(%p)\n", This, fChildren);
420
421     nsres = nsIDOMNode_HasChildNodes(This->nsnode, &has_child);
422     if(NS_FAILED(nsres))
423         ERR("HasChildNodes failed: %08x\n", nsres);
424
425     *fChildren = has_child ? VARIANT_TRUE : VARIANT_FALSE;
426     return S_OK;
427 }
428
429 static HRESULT WINAPI HTMLDOMNode_get_childNodes(IHTMLDOMNode *iface, IDispatch **p)
430 {
431     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
432     nsIDOMNodeList *nslist;
433     nsresult nsres;
434
435     TRACE("(%p)->(%p)\n", This, p);
436
437     nsres = nsIDOMNode_GetChildNodes(This->nsnode, &nslist);
438     if(NS_FAILED(nsres)) {
439         ERR("GetChildNodes failed: %08x\n", nsres);
440         return E_FAIL;
441     }
442
443     *p = (IDispatch*)create_child_collection(This->doc, nslist);
444     nsIDOMNodeList_Release(nslist);
445
446     return S_OK;
447 }
448
449 static HRESULT WINAPI HTMLDOMNode_get_attributes(IHTMLDOMNode *iface, IDispatch **p)
450 {
451     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
452     FIXME("(%p)->(%p)\n", This, p);
453     return E_NOTIMPL;
454 }
455
456 static HRESULT WINAPI HTMLDOMNode_insertBefore(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
457                                                VARIANT refChild, IHTMLDOMNode **node)
458 {
459     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
460     nsIDOMNode *nsnode, *nsref = NULL;
461     HTMLDOMNode *new_child;
462     nsresult nsres;
463
464     TRACE("(%p)->(%p %s %p)\n", This, newChild, debugstr_variant(&refChild), node);
465
466     new_child = get_node_obj(This->doc, (IUnknown*)newChild);
467     if(!new_child) {
468         ERR("invalid newChild\n");
469         return E_INVALIDARG;
470     }
471
472     switch(V_VT(&refChild)) {
473     case VT_NULL:
474         break;
475     case VT_DISPATCH: {
476         HTMLDOMNode *ref_node;
477
478         ref_node = get_node_obj(This->doc, (IUnknown*)V_DISPATCH(&refChild));
479         if(!ref_node) {
480             ERR("unvalid node\n");
481             return E_FAIL;
482         }
483
484         nsref = ref_node->nsnode;
485         break;
486     }
487     default:
488         FIXME("unimplemented vt %d\n", V_VT(&refChild));
489         return E_NOTIMPL;
490     }
491
492     nsres = nsIDOMNode_InsertBefore(This->nsnode, new_child->nsnode, nsref, &nsnode);
493     if(NS_FAILED(nsres)) {
494         ERR("InsertBefore failed: %08x\n", nsres);
495         return E_FAIL;
496     }
497
498     *node = HTMLDOMNODE(get_node(This->doc, nsnode, TRUE));
499     nsIDOMNode_Release(nsnode);
500     IHTMLDOMNode_AddRef(*node);
501     return S_OK;
502 }
503
504 static HRESULT WINAPI HTMLDOMNode_removeChild(IHTMLDOMNode *iface, IHTMLDOMNode *oldChild,
505                                               IHTMLDOMNode **node)
506 {
507     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
508     HTMLDOMNode *node_obj;
509     nsIDOMNode *nsnode;
510     nsresult nsres;
511
512     TRACE("(%p)->(%p %p)\n", This, oldChild, node);
513
514     node_obj = get_node_obj(This->doc, (IUnknown*)oldChild);
515     if(!node_obj)
516         return E_FAIL;
517
518     nsres = nsIDOMNode_RemoveChild(This->nsnode, node_obj->nsnode, &nsnode);
519     if(NS_FAILED(nsres)) {
520         ERR("RemoveChild failed: %08x\n", nsres);
521         return E_FAIL;
522     }
523
524     /* FIXME: Make sure that node != newChild */
525     *node = HTMLDOMNODE(get_node(This->doc, nsnode, TRUE));
526     nsIDOMNode_Release(nsnode);
527     IHTMLDOMNode_AddRef(*node);
528     return S_OK;
529 }
530
531 static HRESULT WINAPI HTMLDOMNode_replaceChild(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
532                                                IHTMLDOMNode *oldChild, IHTMLDOMNode **node)
533 {
534     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
535     FIXME("(%p)->(%p %p %p)\n", This, newChild, oldChild, node);
536     return E_NOTIMPL;
537 }
538
539 static HRESULT WINAPI HTMLDOMNode_cloneNode(IHTMLDOMNode *iface, VARIANT_BOOL fDeep,
540                                             IHTMLDOMNode **clonedNode)
541 {
542     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
543     HTMLDOMNode *new_node;
544     nsIDOMNode *nsnode;
545     nsresult nsres;
546     HRESULT hres;
547
548     TRACE("(%p)->(%x %p)\n", This, fDeep, clonedNode);
549
550     nsres = nsIDOMNode_CloneNode(This->nsnode, fDeep != VARIANT_FALSE, &nsnode);
551     if(NS_FAILED(nsres) || !nsnode) {
552         ERR("CloneNode failed: %08x\n", nsres);
553         return E_FAIL;
554     }
555
556     hres = This->vtbl->clone(This, nsnode, &new_node);
557     if(FAILED(hres))
558         return hres;
559
560     *clonedNode = HTMLDOMNODE(new_node);
561     return S_OK;
562 }
563
564 static HRESULT WINAPI HTMLDOMNode_removeNode(IHTMLDOMNode *iface, VARIANT_BOOL fDeep,
565                                              IHTMLDOMNode **removed)
566 {
567     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
568     FIXME("(%p)->(%x %p)\n", This, fDeep, removed);
569     return E_NOTIMPL;
570 }
571
572 static HRESULT WINAPI HTMLDOMNode_swapNode(IHTMLDOMNode *iface, IHTMLDOMNode *otherNode,
573                                            IHTMLDOMNode **swappedNode)
574 {
575     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
576     FIXME("(%p)->(%p %p)\n", This, otherNode, swappedNode);
577     return E_NOTIMPL;
578 }
579
580 static HRESULT WINAPI HTMLDOMNode_replaceNode(IHTMLDOMNode *iface, IHTMLDOMNode *replacement,
581                                               IHTMLDOMNode **replaced)
582 {
583     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
584     FIXME("(%p)->(%p %p)\n", This, replacement, replaced);
585     return E_NOTIMPL;
586 }
587
588 static HRESULT WINAPI HTMLDOMNode_appendChild(IHTMLDOMNode *iface, IHTMLDOMNode *newChild,
589                                               IHTMLDOMNode **node)
590 {
591     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
592     HTMLDOMNode *node_obj;
593     nsIDOMNode *nsnode;
594     nsresult nsres;
595
596     TRACE("(%p)->(%p %p)\n", This, newChild, node);
597
598     node_obj = get_node_obj(This->doc, (IUnknown*)newChild);
599     if(!node_obj)
600         return E_FAIL;
601
602     nsres = nsIDOMNode_AppendChild(This->nsnode, node_obj->nsnode, &nsnode);
603     if(NS_FAILED(nsres)) {
604         WARN("AppendChild failed: %08x\n", nsres);
605         nsnode = node_obj->nsnode;
606     }
607
608     /* FIXME: Make sure that node != newChild */
609     *node = HTMLDOMNODE(get_node(This->doc, nsnode, TRUE));
610     IHTMLDOMNode_AddRef(*node);
611     return S_OK;
612 }
613
614 static HRESULT WINAPI HTMLDOMNode_get_nodeName(IHTMLDOMNode *iface, BSTR *p)
615 {
616     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
617
618     TRACE("(%p)->(%p)\n", This, p);
619
620     *p = NULL;
621
622     if(This->nsnode) {
623         nsAString name_str;
624         const PRUnichar *name;
625         nsresult nsres;
626
627         nsAString_Init(&name_str, NULL);
628         nsres = nsIDOMNode_GetNodeName(This->nsnode, &name_str);
629
630         if(NS_SUCCEEDED(nsres)) {
631             nsAString_GetData(&name_str, &name);
632             *p = SysAllocString(name);
633         }else {
634             ERR("GetNodeName failed: %08x\n", nsres);
635         }
636
637         nsAString_Finish(&name_str);
638     }
639
640     return S_OK;
641 }
642
643 static HRESULT WINAPI HTMLDOMNode_put_nodeValue(IHTMLDOMNode *iface, VARIANT v)
644 {
645     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
646
647     TRACE("(%p)->()\n", This);
648
649     switch(V_VT(&v)) {
650     case VT_BSTR: {
651         nsAString val_str;
652
653         TRACE("bstr %s\n", debugstr_w(V_BSTR(&v)));
654
655         nsAString_InitDepend(&val_str, V_BSTR(&v));
656         nsIDOMNode_SetNodeValue(This->nsnode, &val_str);
657         nsAString_Finish(&val_str);
658
659         return S_OK;
660     }
661
662     default:
663         FIXME("unsupported vt %d\n", V_VT(&v));
664     }
665
666     return E_NOTIMPL;
667 }
668
669 static HRESULT WINAPI HTMLDOMNode_get_nodeValue(IHTMLDOMNode *iface, VARIANT *p)
670 {
671     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
672     const PRUnichar *val;
673     nsAString val_str;
674
675     TRACE("(%p)->(%p)\n", This, p);
676
677     nsAString_Init(&val_str, NULL);
678     nsIDOMNode_GetNodeValue(This->nsnode, &val_str);
679     nsAString_GetData(&val_str, &val);
680
681     if(*val) {
682         V_VT(p) = VT_BSTR;
683         V_BSTR(p) = SysAllocString(val);
684     }else {
685         V_VT(p) = VT_NULL;
686     }
687
688     nsAString_Finish(&val_str);
689
690     return S_OK;
691 }
692
693 static HRESULT WINAPI HTMLDOMNode_get_firstChild(IHTMLDOMNode *iface, IHTMLDOMNode **p)
694 {
695     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
696     nsIDOMNode *nschild = NULL;
697
698     TRACE("(%p)->(%p)\n", This, p);
699
700     nsIDOMNode_GetFirstChild(This->nsnode, &nschild);
701     if(nschild) {
702         *p = HTMLDOMNODE(get_node(This->doc, nschild, TRUE));
703         IHTMLDOMNode_AddRef(*p);
704     }else {
705         *p = NULL;
706     }
707
708     return S_OK;
709 }
710
711 static HRESULT WINAPI HTMLDOMNode_get_lastChild(IHTMLDOMNode *iface, IHTMLDOMNode **p)
712 {
713     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
714     nsIDOMNode *nschild = NULL;
715
716     TRACE("(%p)->(%p)\n", This, p);
717
718     nsIDOMNode_GetLastChild(This->nsnode, &nschild);
719     if(nschild) {
720         *p = HTMLDOMNODE(get_node(This->doc, nschild, TRUE));
721         IHTMLDOMNode_AddRef(*p);
722     }else {
723         *p = NULL;
724     }
725
726     return S_OK;
727 }
728
729 static HRESULT WINAPI HTMLDOMNode_get_previousSibling(IHTMLDOMNode *iface, IHTMLDOMNode **p)
730 {
731     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
732     FIXME("(%p)->(%p)\n", This, p);
733     return E_NOTIMPL;
734 }
735
736 static HRESULT WINAPI HTMLDOMNode_get_nextSibling(IHTMLDOMNode *iface, IHTMLDOMNode **p)
737 {
738     HTMLDOMNode *This = HTMLDOMNODE_THIS(iface);
739     nsIDOMNode *nssibling = NULL;
740
741     TRACE("(%p)->(%p)\n", This, p);
742
743     nsIDOMNode_GetNextSibling(This->nsnode, &nssibling);
744     if(nssibling) {
745         *p = HTMLDOMNODE(get_node(This->doc, nssibling, TRUE));
746         IHTMLDOMNode_AddRef(*p);
747     }else {
748         *p = NULL;
749     }
750
751     return S_OK;
752 }
753
754 #undef HTMLDOMNODE_THIS
755
756 static const IHTMLDOMNodeVtbl HTMLDOMNodeVtbl = {
757     HTMLDOMNode_QueryInterface,
758     HTMLDOMNode_AddRef,
759     HTMLDOMNode_Release,
760     HTMLDOMNode_GetTypeInfoCount,
761     HTMLDOMNode_GetTypeInfo,
762     HTMLDOMNode_GetIDsOfNames,
763     HTMLDOMNode_Invoke,
764     HTMLDOMNode_get_nodeType,
765     HTMLDOMNode_get_parentNode,
766     HTMLDOMNode_hasChildNodes,
767     HTMLDOMNode_get_childNodes,
768     HTMLDOMNode_get_attributes,
769     HTMLDOMNode_insertBefore,
770     HTMLDOMNode_removeChild,
771     HTMLDOMNode_replaceChild,
772     HTMLDOMNode_cloneNode,
773     HTMLDOMNode_removeNode,
774     HTMLDOMNode_swapNode,
775     HTMLDOMNode_replaceNode,
776     HTMLDOMNode_appendChild,
777     HTMLDOMNode_get_nodeName,
778     HTMLDOMNode_put_nodeValue,
779     HTMLDOMNode_get_nodeValue,
780     HTMLDOMNode_get_firstChild,
781     HTMLDOMNode_get_lastChild,
782     HTMLDOMNode_get_previousSibling,
783     HTMLDOMNode_get_nextSibling
784 };
785
786 #define HTMLDOMNODE2_THIS(iface) DEFINE_THIS(HTMLDOMNode, HTMLDOMNode2, iface)
787
788 static HRESULT WINAPI HTMLDOMNode2_QueryInterface(IHTMLDOMNode2 *iface,
789         REFIID riid, void **ppv)
790 {
791     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
792
793     return IHTMLDOMNode_QueryInterface(HTMLDOMNODE(This), riid, ppv);
794 }
795
796 static ULONG WINAPI HTMLDOMNode2_AddRef(IHTMLDOMNode2 *iface)
797 {
798     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
799
800     return IHTMLDOMNode_AddRef(HTMLDOMNODE(This));
801 }
802
803 static ULONG WINAPI HTMLDOMNode2_Release(IHTMLDOMNode2 *iface)
804 {
805     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
806
807     return IHTMLDOMNode_Release(HTMLDOMNODE(This));
808 }
809
810 static HRESULT WINAPI HTMLDOMNode2_GetTypeInfoCount(IHTMLDOMNode2 *iface, UINT *pctinfo)
811 {
812     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
813     return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo);
814 }
815
816 static HRESULT WINAPI HTMLDOMNode2_GetTypeInfo(IHTMLDOMNode2 *iface, UINT iTInfo,
817         LCID lcid, ITypeInfo **ppTInfo)
818 {
819     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
820     return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo);
821 }
822
823 static HRESULT WINAPI HTMLDOMNode2_GetIDsOfNames(IHTMLDOMNode2 *iface, REFIID riid,
824                                                 LPOLESTR *rgszNames, UINT cNames,
825                                                 LCID lcid, DISPID *rgDispId)
826 {
827     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
828     return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId);
829 }
830
831 static HRESULT WINAPI HTMLDOMNode2_Invoke(IHTMLDOMNode2 *iface, DISPID dispIdMember,
832         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
833         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
834 {
835     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
836     return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid,
837             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
838 }
839
840 static HRESULT WINAPI HTMLDOMNode2_get_ownerDocument(IHTMLDOMNode2 *iface, IDispatch **p)
841 {
842     HTMLDOMNode *This = HTMLDOMNODE2_THIS(iface);
843
844     TRACE("(%p)->(%p)\n", This, p);
845
846     /* FIXME: Better check for document node */
847     if(This == &This->doc->node) {
848         *p = NULL;
849     }else {
850         *p = (IDispatch*)HTMLDOC(&This->doc->basedoc);
851         IDispatch_AddRef(*p);
852     }
853     return S_OK;
854 }
855
856 #undef HTMLDOMNODE2_THIS
857
858 static const IHTMLDOMNode2Vtbl HTMLDOMNode2Vtbl = {
859     HTMLDOMNode2_QueryInterface,
860     HTMLDOMNode2_AddRef,
861     HTMLDOMNode2_Release,
862     HTMLDOMNode2_GetTypeInfoCount,
863     HTMLDOMNode2_GetTypeInfo,
864     HTMLDOMNode2_GetIDsOfNames,
865     HTMLDOMNode2_Invoke,
866     HTMLDOMNode2_get_ownerDocument
867 };
868
869 HRESULT HTMLDOMNode_QI(HTMLDOMNode *This, REFIID riid, void **ppv)
870 {
871     *ppv = NULL;
872
873     if(IsEqualGUID(&IID_IUnknown, riid)) {
874         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
875         *ppv = HTMLDOMNODE(This);
876     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
877         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
878         *ppv = HTMLDOMNODE(This);
879     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
880         if(This->dispex.data) {
881             TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
882             *ppv = DISPATCHEX(&This->dispex);
883         }else {
884             FIXME("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
885             return E_NOINTERFACE;
886         }
887     }else if(IsEqualGUID(&IID_IHTMLDOMNode, riid)) {
888         TRACE("(%p)->(IID_IHTMLDOMNode %p)\n", This, ppv);
889         *ppv = HTMLDOMNODE(This);
890     }else if(IsEqualGUID(&IID_IHTMLDOMNode2, riid)) {
891         TRACE("(%p)->(IID_IHTMLDOMNode2 %p)\n", This, ppv);
892         *ppv = HTMLDOMNODE2(This);
893     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
894         return *ppv ? S_OK : E_NOINTERFACE;
895     }
896
897     if(*ppv) {
898         IUnknown_AddRef((IUnknown*)*ppv);
899         return S_OK;
900     }
901
902     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
903     return E_NOINTERFACE;
904 }
905
906 void HTMLDOMNode_destructor(HTMLDOMNode *This)
907 {
908     if(This->nsnode)
909         nsIDOMNode_Release(This->nsnode);
910     if(This->event_target)
911         release_event_target(This->event_target);
912 }
913
914 static HRESULT HTMLDOMNode_clone(HTMLDOMNode *This, nsIDOMNode *nsnode, HTMLDOMNode **ret)
915 {
916     *ret = create_node(This->doc, nsnode);
917     IHTMLDOMNode_AddRef(HTMLDOMNODE(*ret));
918     return S_OK;
919 }
920
921 static const NodeImplVtbl HTMLDOMNodeImplVtbl = {
922     HTMLDOMNode_QI,
923     HTMLDOMNode_destructor,
924     HTMLDOMNode_clone
925 };
926
927 void HTMLDOMNode_Init(HTMLDocumentNode *doc, HTMLDOMNode *node, nsIDOMNode *nsnode)
928 {
929     node->lpHTMLDOMNodeVtbl = &HTMLDOMNodeVtbl;
930     node->lpHTMLDOMNode2Vtbl = &HTMLDOMNode2Vtbl;
931     node->ref = 1;
932     node->doc = doc;
933
934     if(nsnode)
935         nsIDOMNode_AddRef(nsnode);
936     node->nsnode = nsnode;
937
938     node->next = doc->nodes;
939     doc->nodes = node;
940 }
941
942 static HTMLDOMNode *create_node(HTMLDocumentNode *doc, nsIDOMNode *nsnode)
943 {
944     HTMLDOMNode *ret;
945     PRUint16 node_type;
946
947     nsIDOMNode_GetNodeType(nsnode, &node_type);
948
949     switch(node_type) {
950     case ELEMENT_NODE:
951         ret = &HTMLElement_Create(doc, nsnode, FALSE)->node;
952         break;
953     case TEXT_NODE:
954         ret = HTMLDOMTextNode_Create(doc, nsnode);
955         break;
956     case COMMENT_NODE:
957         ret = &HTMLCommentElement_Create(doc, nsnode)->node;
958         break;
959     default:
960         ret = heap_alloc_zero(sizeof(HTMLDOMNode));
961         ret->vtbl = &HTMLDOMNodeImplVtbl;
962         HTMLDOMNode_Init(doc, ret, nsnode);
963     }
964
965     TRACE("type %d ret %p\n", node_type, ret);
966
967     return ret;
968 }
969
970 /*
971  * FIXME
972  * List looks really ugly here. We should use a better data structure or
973  * (better) find a way to store HTMLDOMelement pointer in nsIDOMNode.
974  */
975
976 HTMLDOMNode *get_node(HTMLDocumentNode *This, nsIDOMNode *nsnode, BOOL create)
977 {
978     HTMLDOMNode *iter = This->nodes;
979
980     while(iter) {
981         if(iter->nsnode == nsnode)
982             break;
983         iter = iter->next;
984     }
985
986     if(iter || !create)
987         return iter;
988
989     return create_node(This, nsnode);
990 }
991
992 /*
993  * FIXME
994  * We should use better way for getting node object (like private interface)
995  * or avoid it at all.
996  */
997 static HTMLDOMNode *get_node_obj(HTMLDocumentNode *This, IUnknown *iface)
998 {
999     HTMLDOMNode *iter = This->nodes;
1000     IHTMLDOMNode *node;
1001
1002     IUnknown_QueryInterface(iface, &IID_IHTMLDOMNode, (void**)&node);
1003     IHTMLDOMNode_Release(node);
1004
1005     while(iter) {
1006         if(HTMLDOMNODE(iter) == node)
1007             return iter;
1008         iter = iter->next;
1009     }
1010
1011     FIXME("Not found %p\n", iface);
1012     return NULL;
1013 }
1014
1015 void release_nodes(HTMLDocumentNode *This)
1016 {
1017     HTMLDOMNode *iter, *next;
1018
1019     if(!This->nodes)
1020         return;
1021
1022     for(iter = This->nodes; iter; iter = next) {
1023         next = iter->next;
1024         iter->doc = NULL;
1025         if(&This->node != iter)
1026             IHTMLDOMNode_Release(HTMLDOMNODE(iter));
1027     }
1028 }