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