Release 1.5.29.
[wine] / dlls / mshtml / conpoint.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
32 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
33
34 static const char *debugstr_cp_guid(REFIID riid)
35 {
36 #define X(x) \
37     if(IsEqualGUID(riid, &x)) \
38         return #x
39
40     X(IID_IPropertyNotifySink);
41     X(DIID_HTMLDocumentEvents);
42     X(DIID_HTMLDocumentEvents2);
43     X(DIID_HTMLTableEvents);
44     X(DIID_HTMLTextContainerEvents);
45
46 #undef X
47
48     return debugstr_guid(riid);
49 }
50
51 static inline ConnectionPoint *impl_from_IConnectionPoint(IConnectionPoint *iface)
52 {
53     return CONTAINING_RECORD(iface, ConnectionPoint, IConnectionPoint_iface);
54 }
55
56 static HRESULT WINAPI ConnectionPoint_QueryInterface(IConnectionPoint *iface,
57                                                      REFIID riid, LPVOID *ppv)
58 {
59     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
60
61     *ppv = NULL;
62
63     if(IsEqualGUID(&IID_IUnknown, riid)) {
64         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
65         *ppv = &This->IConnectionPoint_iface;
66     }else if(IsEqualGUID(&IID_IConnectionPoint, riid)) {
67         TRACE("(%p)->(IID_IConnectionPoint %p)\n", This, ppv);
68         *ppv = &This->IConnectionPoint_iface;
69     }
70
71     if(*ppv) {
72         IUnknown_AddRef((IUnknown*)*ppv);
73         return S_OK;
74     }
75
76     WARN("Unsupported interface %s\n", debugstr_guid(riid));
77     return E_NOINTERFACE;
78 }
79
80 static ULONG WINAPI ConnectionPoint_AddRef(IConnectionPoint *iface)
81 {
82     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
83     return IConnectionPointContainer_AddRef(&This->container->IConnectionPointContainer_iface);
84 }
85
86 static ULONG WINAPI ConnectionPoint_Release(IConnectionPoint *iface)
87 {
88     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
89     return IConnectionPointContainer_Release(&This->container->IConnectionPointContainer_iface);
90 }
91
92 static HRESULT WINAPI ConnectionPoint_GetConnectionInterface(IConnectionPoint *iface, IID *pIID)
93 {
94     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
95
96     TRACE("(%p)->(%p)\n", This, pIID);
97
98     if(!pIID)
99         return E_POINTER;
100
101     *pIID = *This->iid;
102     return S_OK;
103 }
104
105 static HRESULT WINAPI ConnectionPoint_GetConnectionPointContainer(IConnectionPoint *iface,
106         IConnectionPointContainer **ppCPC)
107 {
108     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
109
110     TRACE("(%p)->(%p)\n", This, ppCPC);
111
112     if(!ppCPC)
113         return E_POINTER;
114
115     *ppCPC = &This->container->IConnectionPointContainer_iface;
116     IConnectionPointContainer_AddRef(*ppCPC);
117     return S_OK;
118 }
119
120 static HRESULT WINAPI ConnectionPoint_Advise(IConnectionPoint *iface, IUnknown *pUnkSink,
121                                              DWORD *pdwCookie)
122 {
123     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
124     IUnknown *sink;
125     DWORD i;
126     HRESULT hres;
127
128     TRACE("(%p)->(%p %p)\n", This, pUnkSink, pdwCookie);
129
130     hres = IUnknown_QueryInterface(pUnkSink, This->iid, (void**)&sink);
131     if(FAILED(hres) && !IsEqualGUID(&IID_IPropertyNotifySink, This->iid))
132         hres = IUnknown_QueryInterface(pUnkSink, &IID_IDispatch, (void**)&sink);
133     if(FAILED(hres))
134         return CONNECT_E_CANNOTCONNECT;
135
136     if(This->sinks) {
137         for(i=0; i<This->sinks_size; i++) {
138             if(!This->sinks[i].unk)
139                 break;
140         }
141
142         if(i == This->sinks_size)
143             This->sinks = heap_realloc(This->sinks,(++This->sinks_size)*sizeof(*This->sinks));
144     }else {
145         This->sinks = heap_alloc(sizeof(*This->sinks));
146         This->sinks_size = 1;
147         i = 0;
148     }
149
150     This->sinks[i].unk = sink;
151     if(pdwCookie)
152         *pdwCookie = i+1;
153
154     if(!i && This->data && This->data->on_advise)
155         This->data->on_advise(This->container->outer, This->data);
156
157     return S_OK;
158 }
159
160 static HRESULT WINAPI ConnectionPoint_Unadvise(IConnectionPoint *iface, DWORD dwCookie)
161 {
162     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
163     TRACE("(%p)->(%d)\n", This, dwCookie);
164
165     if(!dwCookie || dwCookie > This->sinks_size || !This->sinks[dwCookie-1].unk)
166         return CONNECT_E_NOCONNECTION;
167
168     IUnknown_Release(This->sinks[dwCookie-1].unk);
169     This->sinks[dwCookie-1].unk = NULL;
170
171     return S_OK;
172 }
173
174 static HRESULT WINAPI ConnectionPoint_EnumConnections(IConnectionPoint *iface,
175                                                       IEnumConnections **ppEnum)
176 {
177     ConnectionPoint *This = impl_from_IConnectionPoint(iface);
178     FIXME("(%p)->(%p)\n", This, ppEnum);
179     return E_NOTIMPL;
180 }
181
182 static const IConnectionPointVtbl ConnectionPointVtbl =
183 {
184     ConnectionPoint_QueryInterface,
185     ConnectionPoint_AddRef,
186     ConnectionPoint_Release,
187     ConnectionPoint_GetConnectionInterface,
188     ConnectionPoint_GetConnectionPointContainer,
189     ConnectionPoint_Advise,
190     ConnectionPoint_Unadvise,
191     ConnectionPoint_EnumConnections
192 };
193
194 static void ConnectionPoint_Init(ConnectionPoint *cp, ConnectionPointContainer *container, REFIID riid, cp_static_data_t *data)
195 {
196     cp->IConnectionPoint_iface.lpVtbl = &ConnectionPointVtbl;
197     cp->container = container;
198     cp->sinks = NULL;
199     cp->sinks_size = 0;
200     cp->iid = riid;
201     cp->data = data;
202 }
203
204 static void ConnectionPoint_Destroy(ConnectionPoint *This)
205 {
206     DWORD i;
207
208     for(i=0; i<This->sinks_size; i++) {
209         if(This->sinks[i].unk)
210             IUnknown_Release(This->sinks[i].unk);
211     }
212
213     heap_free(This->sinks);
214 }
215
216 static ConnectionPoint *get_cp(ConnectionPointContainer *container, REFIID riid, BOOL do_create)
217 {
218     const cpc_entry_t *iter;
219     unsigned idx, i;
220
221     for(iter = container->cp_entries; iter->riid; iter++) {
222         if(IsEqualGUID(iter->riid, riid))
223             break;
224     }
225     if(!iter->riid)
226         return NULL;
227     idx = iter - container->cp_entries;
228
229     if(!container->cps) {
230         if(!do_create)
231             return NULL;
232
233         while(iter->riid)
234             iter++;
235         container->cps = heap_alloc((iter - container->cp_entries) * sizeof(*container->cps));
236         if(!container->cps)
237             return NULL;
238
239         for(i=0; container->cp_entries[i].riid; i++)
240             ConnectionPoint_Init(container->cps+i, container, container->cp_entries[i].riid, container->cp_entries[i].desc);
241     }
242
243     return container->cps+idx;
244 }
245
246 void call_property_onchanged(ConnectionPointContainer *container, DISPID dispid)
247 {
248     ConnectionPoint *cp;
249     DWORD i;
250
251     cp = get_cp(container, &IID_IPropertyNotifySink, FALSE);
252     if(!cp)
253         return;
254
255     for(i=0; i<cp->sinks_size; i++) {
256         if(cp->sinks[i].propnotif)
257             IPropertyNotifySink_OnChanged(cp->sinks[i].propnotif, dispid);
258     }
259 }
260
261 static inline ConnectionPointContainer *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
262 {
263     return CONTAINING_RECORD(iface, ConnectionPointContainer, IConnectionPointContainer_iface);
264 }
265
266 static HRESULT WINAPI ConnectionPointContainer_QueryInterface(IConnectionPointContainer *iface,
267                                                               REFIID riid, void **ppv)
268 {
269     ConnectionPointContainer *This = impl_from_IConnectionPointContainer(iface);
270     return IUnknown_QueryInterface(This->outer, riid, ppv);
271 }
272
273 static ULONG WINAPI ConnectionPointContainer_AddRef(IConnectionPointContainer *iface)
274 {
275     ConnectionPointContainer *This = impl_from_IConnectionPointContainer(iface);
276     return IUnknown_AddRef(This->outer);
277 }
278
279 static ULONG WINAPI ConnectionPointContainer_Release(IConnectionPointContainer *iface)
280 {
281     ConnectionPointContainer *This = impl_from_IConnectionPointContainer(iface);
282     return IUnknown_Release(This->outer);
283 }
284
285 static HRESULT WINAPI ConnectionPointContainer_EnumConnectionPoints(IConnectionPointContainer *iface,
286         IEnumConnectionPoints **ppEnum)
287 {
288     ConnectionPointContainer *This = impl_from_IConnectionPointContainer(iface);
289     FIXME("(%p)->(%p)\n", This, ppEnum);
290     return E_NOTIMPL;
291 }
292
293 static HRESULT WINAPI ConnectionPointContainer_FindConnectionPoint(IConnectionPointContainer *iface,
294         REFIID riid, IConnectionPoint **ppCP)
295 {
296     ConnectionPointContainer *This = impl_from_IConnectionPointContainer(iface);
297     ConnectionPoint *cp;
298
299     TRACE("(%p)->(%s %p)\n", This, debugstr_cp_guid(riid), ppCP);
300
301     if(This->forward_container)
302         return IConnectionPointContainer_FindConnectionPoint(&This->forward_container->IConnectionPointContainer_iface,
303                 riid, ppCP);
304
305     cp = get_cp(This, riid, TRUE);
306     if(!cp) {
307         FIXME("unsupported riid %s\n", debugstr_cp_guid(riid));
308         *ppCP = NULL;
309         return CONNECT_E_NOCONNECTION;
310     }
311
312     *ppCP = &cp->IConnectionPoint_iface;
313     IConnectionPoint_AddRef(*ppCP);
314     return S_OK;
315 }
316
317 static const IConnectionPointContainerVtbl ConnectionPointContainerVtbl = {
318     ConnectionPointContainer_QueryInterface,
319     ConnectionPointContainer_AddRef,
320     ConnectionPointContainer_Release,
321     ConnectionPointContainer_EnumConnectionPoints,
322     ConnectionPointContainer_FindConnectionPoint
323 };
324
325 void ConnectionPointContainer_Init(ConnectionPointContainer *This, IUnknown *outer, const cpc_entry_t *cp_entries)
326 {
327     This->IConnectionPointContainer_iface.lpVtbl = &ConnectionPointContainerVtbl;
328     This->cp_entries = cp_entries;
329     This->cps = NULL;
330     This->outer = outer;
331     This->forward_container = NULL;
332 }
333
334 void ConnectionPointContainer_Destroy(ConnectionPointContainer *This)
335 {
336     unsigned i;
337
338     if(!This->cps)
339         return;
340
341     for(i=0; This->cp_entries[i].riid; i++)
342         ConnectionPoint_Destroy(This->cps+i);
343     heap_free(This->cps);
344 }