xref: /reactos/dll/win32/msctf/compartmentmgr.c (revision c2c66aff)
1 /*
2  *  ITfCompartmentMgr implementation
3  *
4  *  Copyright 2009 Aric Stewart, CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "msctf_internal.h"
22 
23 #include <oleauto.h>
24 
25 typedef struct tagCompartmentValue {
26     struct list entry;
27     GUID guid;
28     TfClientId owner;
29     ITfCompartment *compartment;
30 } CompartmentValue;
31 
32 typedef struct tagCompartmentMgr {
33     ITfCompartmentMgr ITfCompartmentMgr_iface;
34     LONG refCount;
35 
36     IUnknown *pUnkOuter;
37 
38     struct list values;
39 } CompartmentMgr;
40 
41 typedef struct tagCompartmentEnumGuid {
42     IEnumGUID IEnumGUID_iface;
43     LONG refCount;
44 
45     struct list *values;
46     struct list *cursor;
47 } CompartmentEnumGuid;
48 
49 typedef struct tagCompartment {
50     ITfCompartment ITfCompartment_iface;
51     ITfSource ITfSource_iface;
52     LONG refCount;
53 
54     /* Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed */
55     VARIANT variant;
56     CompartmentValue *valueData;
57     struct list CompartmentEventSink;
58 } Compartment;
59 
60 static HRESULT CompartmentEnumGuid_Constructor(struct list* values, IEnumGUID **ppOut);
61 static HRESULT Compartment_Constructor(CompartmentValue *value, ITfCompartment **ppOut);
62 
63 static inline CompartmentMgr *impl_from_ITfCompartmentMgr(ITfCompartmentMgr *iface)
64 {
65     return CONTAINING_RECORD(iface, CompartmentMgr, ITfCompartmentMgr_iface);
66 }
67 
68 static inline Compartment *impl_from_ITfCompartment(ITfCompartment *iface)
69 {
70     return CONTAINING_RECORD(iface, Compartment, ITfCompartment_iface);
71 }
72 
73 static inline Compartment *impl_from_ITfSource(ITfSource *iface)
74 {
75     return CONTAINING_RECORD(iface, Compartment, ITfSource_iface);
76 }
77 
78 static inline CompartmentEnumGuid *impl_from_IEnumGUID(IEnumGUID *iface)
79 {
80     return CONTAINING_RECORD(iface, CompartmentEnumGuid, IEnumGUID_iface);
81 }
82 
83 HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface)
84 {
85     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
86     struct list *cursor, *cursor2;
87 
88     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->values)
89     {
90         CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
91         list_remove(cursor);
92         ITfCompartment_Release(value->compartment);
93         HeapFree(GetProcessHeap(),0,value);
94     }
95 
96     HeapFree(GetProcessHeap(),0,This);
97     return S_OK;
98 }
99 
100 /*****************************************************
101  * ITfCompartmentMgr functions
102  *****************************************************/
103 static HRESULT WINAPI CompartmentMgr_QueryInterface(ITfCompartmentMgr *iface, REFIID iid, LPVOID *ppvOut)
104 {
105     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
106     if (This->pUnkOuter)
107         return IUnknown_QueryInterface(This->pUnkOuter, iid, ppvOut);
108     else
109     {
110         *ppvOut = NULL;
111 
112         if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartmentMgr))
113         {
114             *ppvOut = &This->ITfCompartmentMgr_iface;
115         }
116 
117         if (*ppvOut)
118         {
119             ITfCompartmentMgr_AddRef(iface);
120             return S_OK;
121         }
122 
123         WARN("unsupported interface: %s\n", debugstr_guid(iid));
124         return E_NOINTERFACE;
125     }
126 }
127 
128 static ULONG WINAPI CompartmentMgr_AddRef(ITfCompartmentMgr *iface)
129 {
130     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
131     if (This->pUnkOuter)
132         return IUnknown_AddRef(This->pUnkOuter);
133     else
134         return InterlockedIncrement(&This->refCount);
135 }
136 
137 static ULONG WINAPI CompartmentMgr_Release(ITfCompartmentMgr *iface)
138 {
139     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
140     if (This->pUnkOuter)
141         return IUnknown_Release(This->pUnkOuter);
142     else
143     {
144         ULONG ret;
145 
146         ret = InterlockedDecrement(&This->refCount);
147         if (ret == 0)
148             CompartmentMgr_Destructor(iface);
149         return ret;
150     }
151 }
152 
153 static HRESULT WINAPI CompartmentMgr_GetCompartment(ITfCompartmentMgr *iface,
154         REFGUID rguid, ITfCompartment **ppcomp)
155 {
156     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
157     CompartmentValue* value;
158     struct list *cursor;
159     HRESULT hr;
160 
161     TRACE("(%p) %s  %p\n",This,debugstr_guid(rguid),ppcomp);
162 
163     LIST_FOR_EACH(cursor, &This->values)
164     {
165         value = LIST_ENTRY(cursor,CompartmentValue,entry);
166         if (IsEqualGUID(rguid,&value->guid))
167         {
168             ITfCompartment_AddRef(value->compartment);
169             *ppcomp = value->compartment;
170             return S_OK;
171         }
172     }
173 
174     value = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentValue));
175     value->guid = *rguid;
176     value->owner = 0;
177     hr = Compartment_Constructor(value,&value->compartment);
178     if (SUCCEEDED(hr))
179     {
180         list_add_head(&This->values,&value->entry);
181         ITfCompartment_AddRef(value->compartment);
182         *ppcomp = value->compartment;
183     }
184     else
185     {
186         HeapFree(GetProcessHeap(),0,value);
187         *ppcomp = NULL;
188     }
189     return hr;
190 }
191 
192 static HRESULT WINAPI CompartmentMgr_ClearCompartment(ITfCompartmentMgr *iface,
193     TfClientId tid, REFGUID rguid)
194 {
195     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
196     struct list *cursor;
197 
198     TRACE("(%p) %i %s\n",This,tid,debugstr_guid(rguid));
199 
200     LIST_FOR_EACH(cursor, &This->values)
201     {
202         CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
203         if (IsEqualGUID(rguid,&value->guid))
204         {
205             if (value->owner && tid != value->owner)
206                 return E_UNEXPECTED;
207             list_remove(cursor);
208             ITfCompartment_Release(value->compartment);
209             HeapFree(GetProcessHeap(),0,value);
210             return S_OK;
211         }
212     }
213 
214     return CONNECT_E_NOCONNECTION;
215 }
216 
217 static HRESULT WINAPI CompartmentMgr_EnumCompartments(ITfCompartmentMgr *iface,
218  IEnumGUID **ppEnum)
219 {
220     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
221 
222     TRACE("(%p) %p\n",This,ppEnum);
223     if (!ppEnum)
224         return E_INVALIDARG;
225     return CompartmentEnumGuid_Constructor(&This->values, ppEnum);
226 }
227 
228 static const ITfCompartmentMgrVtbl CompartmentMgrVtbl =
229 {
230     CompartmentMgr_QueryInterface,
231     CompartmentMgr_AddRef,
232     CompartmentMgr_Release,
233     CompartmentMgr_GetCompartment,
234     CompartmentMgr_ClearCompartment,
235     CompartmentMgr_EnumCompartments
236 };
237 
238 HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut)
239 {
240     CompartmentMgr *This;
241 
242     if (!ppOut)
243         return E_POINTER;
244 
245     if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
246         return CLASS_E_NOAGGREGATION;
247 
248     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentMgr));
249     if (This == NULL)
250         return E_OUTOFMEMORY;
251 
252     This->ITfCompartmentMgr_iface.lpVtbl = &CompartmentMgrVtbl;
253     This->pUnkOuter = pUnkOuter;
254     list_init(&This->values);
255 
256     if (pUnkOuter)
257     {
258         *ppOut = (IUnknown*)&This->ITfCompartmentMgr_iface;
259         TRACE("returning %p\n", *ppOut);
260         return S_OK;
261     }
262     else
263     {
264         HRESULT hr;
265         hr = ITfCompartmentMgr_QueryInterface(&This->ITfCompartmentMgr_iface, riid, (void**)ppOut);
266         if (FAILED(hr))
267             HeapFree(GetProcessHeap(),0,This);
268         return hr;
269     }
270 }
271 
272 /**************************************************
273  * IEnumGUID implementation for ITfCompartmentMgr::EnumCompartments
274  **************************************************/
275 static void CompartmentEnumGuid_Destructor(CompartmentEnumGuid *This)
276 {
277     TRACE("destroying %p\n", This);
278     HeapFree(GetProcessHeap(),0,This);
279 }
280 
281 static HRESULT WINAPI CompartmentEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut)
282 {
283     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
284     *ppvOut = NULL;
285 
286     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID))
287     {
288         *ppvOut = &This->IEnumGUID_iface;
289     }
290 
291     if (*ppvOut)
292     {
293         IEnumGUID_AddRef(iface);
294         return S_OK;
295     }
296 
297     WARN("unsupported interface: %s\n", debugstr_guid(iid));
298     return E_NOINTERFACE;
299 }
300 
301 static ULONG WINAPI CompartmentEnumGuid_AddRef(IEnumGUID *iface)
302 {
303     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
304     return InterlockedIncrement(&This->refCount);
305 }
306 
307 static ULONG WINAPI CompartmentEnumGuid_Release(IEnumGUID *iface)
308 {
309     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
310     ULONG ret;
311 
312     ret = InterlockedDecrement(&This->refCount);
313     if (ret == 0)
314         CompartmentEnumGuid_Destructor(This);
315     return ret;
316 }
317 
318 /*****************************************************
319  * IEnumGuid functions
320  *****************************************************/
321 static HRESULT WINAPI CompartmentEnumGuid_Next(IEnumGUID *iface,
322     ULONG celt, GUID *rgelt, ULONG *pceltFetched)
323 {
324     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
325     ULONG fetched = 0;
326 
327     TRACE("(%p)\n",This);
328 
329     if (rgelt == NULL) return E_POINTER;
330 
331     while (fetched < celt && This->cursor)
332     {
333         CompartmentValue* value = LIST_ENTRY(This->cursor,CompartmentValue,entry);
334         if (!value)
335             break;
336 
337         This->cursor = list_next(This->values,This->cursor);
338         *rgelt = value->guid;
339 
340         ++fetched;
341         ++rgelt;
342     }
343 
344     if (pceltFetched) *pceltFetched = fetched;
345     return fetched == celt ? S_OK : S_FALSE;
346 }
347 
348 static HRESULT WINAPI CompartmentEnumGuid_Skip(IEnumGUID *iface, ULONG celt)
349 {
350     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
351     TRACE("(%p)\n",This);
352 
353     This->cursor = list_next(This->values,This->cursor);
354     return S_OK;
355 }
356 
357 static HRESULT WINAPI CompartmentEnumGuid_Reset(IEnumGUID *iface)
358 {
359     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
360     TRACE("(%p)\n",This);
361     This->cursor = list_head(This->values);
362     return S_OK;
363 }
364 
365 static HRESULT WINAPI CompartmentEnumGuid_Clone(IEnumGUID *iface,
366     IEnumGUID **ppenum)
367 {
368     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
369     HRESULT res;
370 
371     TRACE("(%p)\n",This);
372 
373     if (ppenum == NULL) return E_POINTER;
374 
375     res = CompartmentEnumGuid_Constructor(This->values, ppenum);
376     if (SUCCEEDED(res))
377     {
378         CompartmentEnumGuid *new_This = impl_from_IEnumGUID(*ppenum);
379         new_This->cursor = This->cursor;
380     }
381     return res;
382 }
383 
384 static const IEnumGUIDVtbl EnumGUIDVtbl =
385 {
386     CompartmentEnumGuid_QueryInterface,
387     CompartmentEnumGuid_AddRef,
388     CompartmentEnumGuid_Release,
389     CompartmentEnumGuid_Next,
390     CompartmentEnumGuid_Skip,
391     CompartmentEnumGuid_Reset,
392     CompartmentEnumGuid_Clone
393 };
394 
395 static HRESULT CompartmentEnumGuid_Constructor(struct list *values, IEnumGUID **ppOut)
396 {
397     CompartmentEnumGuid *This;
398 
399     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentEnumGuid));
400     if (This == NULL)
401         return E_OUTOFMEMORY;
402 
403     This->IEnumGUID_iface.lpVtbl= &EnumGUIDVtbl;
404     This->refCount = 1;
405 
406     This->values = values;
407     This->cursor = list_head(values);
408 
409     *ppOut = &This->IEnumGUID_iface;
410     TRACE("returning %p\n", *ppOut);
411     return S_OK;
412 }
413 
414 /**************************************************
415  * ITfCompartment
416  **************************************************/
417 static void Compartment_Destructor(Compartment *This)
418 {
419     TRACE("destroying %p\n", This);
420     VariantClear(&This->variant);
421     free_sinks(&This->CompartmentEventSink);
422     HeapFree(GetProcessHeap(),0,This);
423 }
424 
425 static HRESULT WINAPI Compartment_QueryInterface(ITfCompartment *iface, REFIID iid, LPVOID *ppvOut)
426 {
427     Compartment *This = impl_from_ITfCompartment(iface);
428 
429     *ppvOut = NULL;
430 
431     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartment))
432     {
433         *ppvOut = &This->ITfCompartment_iface;
434     }
435     else if (IsEqualIID(iid, &IID_ITfSource))
436     {
437         *ppvOut = &This->ITfSource_iface;
438     }
439 
440     if (*ppvOut)
441     {
442         ITfCompartment_AddRef(iface);
443         return S_OK;
444     }
445 
446     WARN("unsupported interface: %s\n", debugstr_guid(iid));
447     return E_NOINTERFACE;
448 }
449 
450 static ULONG WINAPI Compartment_AddRef(ITfCompartment *iface)
451 {
452     Compartment *This = impl_from_ITfCompartment(iface);
453     return InterlockedIncrement(&This->refCount);
454 }
455 
456 static ULONG WINAPI Compartment_Release(ITfCompartment *iface)
457 {
458     Compartment *This = impl_from_ITfCompartment(iface);
459     ULONG ret;
460 
461     ret = InterlockedDecrement(&This->refCount);
462     if (ret == 0)
463         Compartment_Destructor(This);
464     return ret;
465 }
466 
467 static HRESULT WINAPI Compartment_SetValue(ITfCompartment *iface,
468     TfClientId tid, const VARIANT *pvarValue)
469 {
470     Compartment *This = impl_from_ITfCompartment(iface);
471     ITfCompartmentEventSink *sink;
472     struct list *cursor;
473 
474     TRACE("(%p) %i %p\n",This,tid,pvarValue);
475 
476     if (!pvarValue)
477         return E_INVALIDARG;
478 
479     if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 ||
480           V_VT(pvarValue) == VT_UNKNOWN))
481         return E_INVALIDARG;
482 
483     if (!This->valueData->owner)
484         This->valueData->owner = tid;
485 
486     VariantClear(&This->variant);
487 
488     /* Shallow copy of value and type */
489     This->variant = *pvarValue;
490 
491     if (V_VT(pvarValue) == VT_BSTR)
492         V_BSTR(&This->variant) = SysAllocStringByteLen((char*)V_BSTR(pvarValue),
493                 SysStringByteLen(V_BSTR(pvarValue)));
494     else if (V_VT(pvarValue) == VT_UNKNOWN)
495         IUnknown_AddRef(V_UNKNOWN(&This->variant));
496 
497     SINK_FOR_EACH(cursor, &This->CompartmentEventSink, ITfCompartmentEventSink, sink)
498     {
499         ITfCompartmentEventSink_OnChange(sink, &This->valueData->guid);
500     }
501 
502     return S_OK;
503 }
504 
505 static HRESULT WINAPI Compartment_GetValue(ITfCompartment *iface,
506     VARIANT *pvarValue)
507 {
508     Compartment *This = impl_from_ITfCompartment(iface);
509     TRACE("(%p) %p\n",This, pvarValue);
510 
511     if (!pvarValue)
512         return E_INVALIDARG;
513 
514     VariantInit(pvarValue);
515     if (V_VT(&This->variant) == VT_EMPTY) return S_FALSE;
516     return VariantCopy(pvarValue,&This->variant);
517 }
518 
519 static const ITfCompartmentVtbl CompartmentVtbl =
520 {
521     Compartment_QueryInterface,
522     Compartment_AddRef,
523     Compartment_Release,
524     Compartment_SetValue,
525     Compartment_GetValue
526 };
527 
528 /*****************************************************
529  * ITfSource functions
530  *****************************************************/
531 
532 static HRESULT WINAPI CompartmentSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
533 {
534     Compartment *This = impl_from_ITfSource(iface);
535     return ITfCompartment_QueryInterface(&This->ITfCompartment_iface, iid, ppvOut);
536 }
537 
538 static ULONG WINAPI CompartmentSource_AddRef(ITfSource *iface)
539 {
540     Compartment *This = impl_from_ITfSource(iface);
541     return ITfCompartment_AddRef(&This->ITfCompartment_iface);
542 }
543 
544 static ULONG WINAPI CompartmentSource_Release(ITfSource *iface)
545 {
546     Compartment *This = impl_from_ITfSource(iface);
547     return ITfCompartment_Release(&This->ITfCompartment_iface);
548 }
549 
550 static HRESULT WINAPI CompartmentSource_AdviseSink(ITfSource *iface,
551         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
552 {
553     Compartment *This = impl_from_ITfSource(iface);
554 
555     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
556 
557     if (!riid || !punk || !pdwCookie)
558         return E_INVALIDARG;
559 
560     if (IsEqualIID(riid, &IID_ITfCompartmentEventSink))
561         return advise_sink(&This->CompartmentEventSink, &IID_ITfCompartmentEventSink,
562                            COOKIE_MAGIC_COMPARTMENTSINK, punk, pdwCookie);
563 
564     FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
565     return E_NOTIMPL;
566 }
567 
568 static HRESULT WINAPI CompartmentSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
569 {
570     Compartment *This = impl_from_ITfSource(iface);
571 
572     TRACE("(%p) %x\n",This,pdwCookie);
573 
574     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_COMPARTMENTSINK)
575         return E_INVALIDARG;
576 
577     return unadvise_sink(pdwCookie);
578 }
579 
580 static const ITfSourceVtbl CompartmentSourceVtbl =
581 {
582     CompartmentSource_QueryInterface,
583     CompartmentSource_AddRef,
584     CompartmentSource_Release,
585     CompartmentSource_AdviseSink,
586     CompartmentSource_UnadviseSink,
587 };
588 
589 static HRESULT Compartment_Constructor(CompartmentValue *valueData, ITfCompartment **ppOut)
590 {
591     Compartment *This;
592 
593     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Compartment));
594     if (This == NULL)
595         return E_OUTOFMEMORY;
596 
597     This->ITfCompartment_iface.lpVtbl= &CompartmentVtbl;
598     This->ITfSource_iface.lpVtbl = &CompartmentSourceVtbl;
599     This->refCount = 1;
600 
601     This->valueData = valueData;
602     VariantInit(&This->variant);
603 
604     list_init(&This->CompartmentEventSink);
605 
606     *ppOut = &This->ITfCompartment_iface;
607     TRACE("returning %p\n", *ppOut);
608     return S_OK;
609 }
610