Sync with trunk r64222.
[reactos.git] / dll / win32 / msctf / compartmentmgr.c
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
50 typedef struct tagCompartmentSink {
51 struct list entry;
52 union {
53 IUnknown *pIUnknown;
54 ITfCompartmentEventSink *pITfCompartmentEventSink;
55 } interfaces;
56 } CompartmentSink;
57
58 typedef struct tagCompartment {
59 ITfCompartment ITfCompartment_iface;
60 ITfSource ITfSource_iface;
61 LONG refCount;
62
63 /* Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed */
64 VARIANT variant;
65 CompartmentValue *valueData;
66 struct list CompartmentEventSink;
67 } Compartment;
68
69 static HRESULT CompartmentEnumGuid_Constructor(struct list* values, IEnumGUID **ppOut);
70 static HRESULT Compartment_Constructor(CompartmentValue *value, ITfCompartment **ppOut);
71
72 static inline CompartmentMgr *impl_from_ITfCompartmentMgr(ITfCompartmentMgr *iface)
73 {
74 return CONTAINING_RECORD(iface, CompartmentMgr, ITfCompartmentMgr_iface);
75 }
76
77 static inline Compartment *impl_from_ITfCompartment(ITfCompartment *iface)
78 {
79 return CONTAINING_RECORD(iface, Compartment, ITfCompartment_iface);
80 }
81
82 static inline Compartment *impl_from_ITfSource(ITfSource *iface)
83 {
84 return CONTAINING_RECORD(iface, Compartment, ITfSource_iface);
85 }
86
87 static inline CompartmentEnumGuid *impl_from_IEnumGUID(IEnumGUID *iface)
88 {
89 return CONTAINING_RECORD(iface, CompartmentEnumGuid, IEnumGUID_iface);
90 }
91
92 HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface)
93 {
94 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
95 struct list *cursor, *cursor2;
96
97 LIST_FOR_EACH_SAFE(cursor, cursor2, &This->values)
98 {
99 CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
100 list_remove(cursor);
101 ITfCompartment_Release(value->compartment);
102 HeapFree(GetProcessHeap(),0,value);
103 }
104
105 HeapFree(GetProcessHeap(),0,This);
106 return S_OK;
107 }
108
109 /*****************************************************
110 * ITfCompartmentMgr functions
111 *****************************************************/
112 static HRESULT WINAPI CompartmentMgr_QueryInterface(ITfCompartmentMgr *iface, REFIID iid, LPVOID *ppvOut)
113 {
114 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
115 if (This->pUnkOuter)
116 return IUnknown_QueryInterface(This->pUnkOuter, iid, ppvOut);
117 else
118 {
119 *ppvOut = NULL;
120
121 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartmentMgr))
122 {
123 *ppvOut = &This->ITfCompartmentMgr_iface;
124 }
125
126 if (*ppvOut)
127 {
128 ITfCompartmentMgr_AddRef(iface);
129 return S_OK;
130 }
131
132 WARN("unsupported interface: %s\n", debugstr_guid(iid));
133 return E_NOINTERFACE;
134 }
135 }
136
137 static ULONG WINAPI CompartmentMgr_AddRef(ITfCompartmentMgr *iface)
138 {
139 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
140 if (This->pUnkOuter)
141 return IUnknown_AddRef(This->pUnkOuter);
142 else
143 return InterlockedIncrement(&This->refCount);
144 }
145
146 static ULONG WINAPI CompartmentMgr_Release(ITfCompartmentMgr *iface)
147 {
148 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
149 if (This->pUnkOuter)
150 return IUnknown_Release(This->pUnkOuter);
151 else
152 {
153 ULONG ret;
154
155 ret = InterlockedDecrement(&This->refCount);
156 if (ret == 0)
157 CompartmentMgr_Destructor(iface);
158 return ret;
159 }
160 }
161
162 static HRESULT WINAPI CompartmentMgr_GetCompartment(ITfCompartmentMgr *iface,
163 REFGUID rguid, ITfCompartment **ppcomp)
164 {
165 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
166 CompartmentValue* value;
167 struct list *cursor;
168 HRESULT hr;
169
170 TRACE("(%p) %s %p\n",This,debugstr_guid(rguid),ppcomp);
171
172 LIST_FOR_EACH(cursor, &This->values)
173 {
174 value = LIST_ENTRY(cursor,CompartmentValue,entry);
175 if (IsEqualGUID(rguid,&value->guid))
176 {
177 ITfCompartment_AddRef(value->compartment);
178 *ppcomp = value->compartment;
179 return S_OK;
180 }
181 }
182
183 value = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentValue));
184 value->guid = *rguid;
185 value->owner = 0;
186 hr = Compartment_Constructor(value,&value->compartment);
187 if (SUCCEEDED(hr))
188 {
189 list_add_head(&This->values,&value->entry);
190 ITfCompartment_AddRef(value->compartment);
191 *ppcomp = value->compartment;
192 }
193 else
194 {
195 HeapFree(GetProcessHeap(),0,value);
196 *ppcomp = NULL;
197 }
198 return hr;
199 }
200
201 static HRESULT WINAPI CompartmentMgr_ClearCompartment(ITfCompartmentMgr *iface,
202 TfClientId tid, REFGUID rguid)
203 {
204 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
205 struct list *cursor;
206
207 TRACE("(%p) %i %s\n",This,tid,debugstr_guid(rguid));
208
209 LIST_FOR_EACH(cursor, &This->values)
210 {
211 CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
212 if (IsEqualGUID(rguid,&value->guid))
213 {
214 if (value->owner && tid != value->owner)
215 return E_UNEXPECTED;
216 list_remove(cursor);
217 ITfCompartment_Release(value->compartment);
218 HeapFree(GetProcessHeap(),0,value);
219 return S_OK;
220 }
221 }
222
223 return CONNECT_E_NOCONNECTION;
224 }
225
226 static HRESULT WINAPI CompartmentMgr_EnumCompartments(ITfCompartmentMgr *iface,
227 IEnumGUID **ppEnum)
228 {
229 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
230
231 TRACE("(%p) %p\n",This,ppEnum);
232 if (!ppEnum)
233 return E_INVALIDARG;
234 return CompartmentEnumGuid_Constructor(&This->values, ppEnum);
235 }
236
237 static const ITfCompartmentMgrVtbl CompartmentMgrVtbl =
238 {
239 CompartmentMgr_QueryInterface,
240 CompartmentMgr_AddRef,
241 CompartmentMgr_Release,
242 CompartmentMgr_GetCompartment,
243 CompartmentMgr_ClearCompartment,
244 CompartmentMgr_EnumCompartments
245 };
246
247 HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut)
248 {
249 CompartmentMgr *This;
250
251 if (!ppOut)
252 return E_POINTER;
253
254 if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
255 return CLASS_E_NOAGGREGATION;
256
257 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentMgr));
258 if (This == NULL)
259 return E_OUTOFMEMORY;
260
261 This->ITfCompartmentMgr_iface.lpVtbl = &CompartmentMgrVtbl;
262 This->pUnkOuter = pUnkOuter;
263 list_init(&This->values);
264
265 if (pUnkOuter)
266 {
267 *ppOut = (IUnknown*)&This->ITfCompartmentMgr_iface;
268 TRACE("returning %p\n", *ppOut);
269 return S_OK;
270 }
271 else
272 {
273 HRESULT hr;
274 hr = ITfCompartmentMgr_QueryInterface(&This->ITfCompartmentMgr_iface, riid, (void**)ppOut);
275 if (FAILED(hr))
276 HeapFree(GetProcessHeap(),0,This);
277 return hr;
278 }
279 }
280
281 /**************************************************
282 * IEnumGUID implementation for ITfCompartmentMgr::EnumCompartments
283 **************************************************/
284 static void CompartmentEnumGuid_Destructor(CompartmentEnumGuid *This)
285 {
286 TRACE("destroying %p\n", This);
287 HeapFree(GetProcessHeap(),0,This);
288 }
289
290 static HRESULT WINAPI CompartmentEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut)
291 {
292 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
293 *ppvOut = NULL;
294
295 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID))
296 {
297 *ppvOut = &This->IEnumGUID_iface;
298 }
299
300 if (*ppvOut)
301 {
302 IEnumGUID_AddRef(iface);
303 return S_OK;
304 }
305
306 WARN("unsupported interface: %s\n", debugstr_guid(iid));
307 return E_NOINTERFACE;
308 }
309
310 static ULONG WINAPI CompartmentEnumGuid_AddRef(IEnumGUID *iface)
311 {
312 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
313 return InterlockedIncrement(&This->refCount);
314 }
315
316 static ULONG WINAPI CompartmentEnumGuid_Release(IEnumGUID *iface)
317 {
318 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
319 ULONG ret;
320
321 ret = InterlockedDecrement(&This->refCount);
322 if (ret == 0)
323 CompartmentEnumGuid_Destructor(This);
324 return ret;
325 }
326
327 /*****************************************************
328 * IEnumGuid functions
329 *****************************************************/
330 static HRESULT WINAPI CompartmentEnumGuid_Next(IEnumGUID *iface,
331 ULONG celt, GUID *rgelt, ULONG *pceltFetched)
332 {
333 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
334 ULONG fetched = 0;
335
336 TRACE("(%p)\n",This);
337
338 if (rgelt == NULL) return E_POINTER;
339
340 while (fetched < celt && This->cursor)
341 {
342 CompartmentValue* value = LIST_ENTRY(This->cursor,CompartmentValue,entry);
343 if (!value)
344 break;
345
346 This->cursor = list_next(This->values,This->cursor);
347 *rgelt = value->guid;
348
349 ++fetched;
350 ++rgelt;
351 }
352
353 if (pceltFetched) *pceltFetched = fetched;
354 return fetched == celt ? S_OK : S_FALSE;
355 }
356
357 static HRESULT WINAPI CompartmentEnumGuid_Skip(IEnumGUID *iface, ULONG celt)
358 {
359 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
360 TRACE("(%p)\n",This);
361
362 This->cursor = list_next(This->values,This->cursor);
363 return S_OK;
364 }
365
366 static HRESULT WINAPI CompartmentEnumGuid_Reset(IEnumGUID *iface)
367 {
368 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
369 TRACE("(%p)\n",This);
370 This->cursor = list_head(This->values);
371 return S_OK;
372 }
373
374 static HRESULT WINAPI CompartmentEnumGuid_Clone(IEnumGUID *iface,
375 IEnumGUID **ppenum)
376 {
377 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
378 HRESULT res;
379
380 TRACE("(%p)\n",This);
381
382 if (ppenum == NULL) return E_POINTER;
383
384 res = CompartmentEnumGuid_Constructor(This->values, ppenum);
385 if (SUCCEEDED(res))
386 {
387 CompartmentEnumGuid *new_This = impl_from_IEnumGUID(*ppenum);
388 new_This->cursor = This->cursor;
389 }
390 return res;
391 }
392
393 static const IEnumGUIDVtbl EnumGUIDVtbl =
394 {
395 CompartmentEnumGuid_QueryInterface,
396 CompartmentEnumGuid_AddRef,
397 CompartmentEnumGuid_Release,
398 CompartmentEnumGuid_Next,
399 CompartmentEnumGuid_Skip,
400 CompartmentEnumGuid_Reset,
401 CompartmentEnumGuid_Clone
402 };
403
404 static HRESULT CompartmentEnumGuid_Constructor(struct list *values, IEnumGUID **ppOut)
405 {
406 CompartmentEnumGuid *This;
407
408 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentEnumGuid));
409 if (This == NULL)
410 return E_OUTOFMEMORY;
411
412 This->IEnumGUID_iface.lpVtbl= &EnumGUIDVtbl;
413 This->refCount = 1;
414
415 This->values = values;
416 This->cursor = list_head(values);
417
418 *ppOut = &This->IEnumGUID_iface;
419 TRACE("returning %p\n", *ppOut);
420 return S_OK;
421 }
422
423 /**************************************************
424 * ITfCompartment
425 **************************************************/
426 static void free_sink(CompartmentSink *sink)
427 {
428 IUnknown_Release(sink->interfaces.pIUnknown);
429 HeapFree(GetProcessHeap(),0,sink);
430 }
431
432 static void Compartment_Destructor(Compartment *This)
433 {
434 struct list *cursor, *cursor2;
435 TRACE("destroying %p\n", This);
436 VariantClear(&This->variant);
437 LIST_FOR_EACH_SAFE(cursor, cursor2, &This->CompartmentEventSink)
438 {
439 CompartmentSink* sink = LIST_ENTRY(cursor,CompartmentSink,entry);
440 list_remove(cursor);
441 free_sink(sink);
442 }
443 HeapFree(GetProcessHeap(),0,This);
444 }
445
446 static HRESULT WINAPI Compartment_QueryInterface(ITfCompartment *iface, REFIID iid, LPVOID *ppvOut)
447 {
448 Compartment *This = impl_from_ITfCompartment(iface);
449
450 *ppvOut = NULL;
451
452 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartment))
453 {
454 *ppvOut = &This->ITfCompartment_iface;
455 }
456 else if (IsEqualIID(iid, &IID_ITfSource))
457 {
458 *ppvOut = &This->ITfSource_iface;
459 }
460
461 if (*ppvOut)
462 {
463 ITfCompartment_AddRef(iface);
464 return S_OK;
465 }
466
467 WARN("unsupported interface: %s\n", debugstr_guid(iid));
468 return E_NOINTERFACE;
469 }
470
471 static ULONG WINAPI Compartment_AddRef(ITfCompartment *iface)
472 {
473 Compartment *This = impl_from_ITfCompartment(iface);
474 return InterlockedIncrement(&This->refCount);
475 }
476
477 static ULONG WINAPI Compartment_Release(ITfCompartment *iface)
478 {
479 Compartment *This = impl_from_ITfCompartment(iface);
480 ULONG ret;
481
482 ret = InterlockedDecrement(&This->refCount);
483 if (ret == 0)
484 Compartment_Destructor(This);
485 return ret;
486 }
487
488 static HRESULT WINAPI Compartment_SetValue(ITfCompartment *iface,
489 TfClientId tid, const VARIANT *pvarValue)
490 {
491 Compartment *This = impl_from_ITfCompartment(iface);
492 struct list *cursor;
493
494 TRACE("(%p) %i %p\n",This,tid,pvarValue);
495
496 if (!pvarValue)
497 return E_INVALIDARG;
498
499 if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 ||
500 V_VT(pvarValue) == VT_UNKNOWN))
501 return E_INVALIDARG;
502
503 if (!This->valueData->owner)
504 This->valueData->owner = tid;
505
506 VariantClear(&This->variant);
507
508 /* Shallow copy of value and type */
509 This->variant = *pvarValue;
510
511 if (V_VT(pvarValue) == VT_BSTR)
512 V_BSTR(&This->variant) = SysAllocStringByteLen((char*)V_BSTR(pvarValue),
513 SysStringByteLen(V_BSTR(pvarValue)));
514 else if (V_VT(pvarValue) == VT_UNKNOWN)
515 IUnknown_AddRef(V_UNKNOWN(&This->variant));
516
517 LIST_FOR_EACH(cursor, &This->CompartmentEventSink)
518 {
519 CompartmentSink* sink = LIST_ENTRY(cursor,CompartmentSink,entry);
520 ITfCompartmentEventSink_OnChange(sink->interfaces.pITfCompartmentEventSink,&This->valueData->guid);
521 }
522
523 return S_OK;
524 }
525
526 static HRESULT WINAPI Compartment_GetValue(ITfCompartment *iface,
527 VARIANT *pvarValue)
528 {
529 Compartment *This = impl_from_ITfCompartment(iface);
530 TRACE("(%p) %p\n",This, pvarValue);
531
532 if (!pvarValue)
533 return E_INVALIDARG;
534
535 VariantInit(pvarValue);
536 if (V_VT(&This->variant) == VT_EMPTY) return S_FALSE;
537 return VariantCopy(pvarValue,&This->variant);
538 }
539
540 static const ITfCompartmentVtbl CompartmentVtbl =
541 {
542 Compartment_QueryInterface,
543 Compartment_AddRef,
544 Compartment_Release,
545 Compartment_SetValue,
546 Compartment_GetValue
547 };
548
549 /*****************************************************
550 * ITfSource functions
551 *****************************************************/
552
553 static HRESULT WINAPI CompartmentSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
554 {
555 Compartment *This = impl_from_ITfSource(iface);
556 return ITfCompartment_QueryInterface(&This->ITfCompartment_iface, iid, ppvOut);
557 }
558
559 static ULONG WINAPI CompartmentSource_AddRef(ITfSource *iface)
560 {
561 Compartment *This = impl_from_ITfSource(iface);
562 return ITfCompartment_AddRef(&This->ITfCompartment_iface);
563 }
564
565 static ULONG WINAPI CompartmentSource_Release(ITfSource *iface)
566 {
567 Compartment *This = impl_from_ITfSource(iface);
568 return ITfCompartment_Release(&This->ITfCompartment_iface);
569 }
570
571 static HRESULT WINAPI CompartmentSource_AdviseSink(ITfSource *iface,
572 REFIID riid, IUnknown *punk, DWORD *pdwCookie)
573 {
574 Compartment *This = impl_from_ITfSource(iface);
575 CompartmentSink *cs;
576
577 TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
578
579 if (!riid || !punk || !pdwCookie)
580 return E_INVALIDARG;
581
582 if (IsEqualIID(riid, &IID_ITfCompartmentEventSink))
583 {
584 cs = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentSink));
585 if (!cs)
586 return E_OUTOFMEMORY;
587 if (FAILED(IUnknown_QueryInterface(punk, riid, (LPVOID *)&cs->interfaces.pITfCompartmentEventSink)))
588 {
589 HeapFree(GetProcessHeap(),0,cs);
590 return CONNECT_E_CANNOTCONNECT;
591 }
592 list_add_head(&This->CompartmentEventSink,&cs->entry);
593 *pdwCookie = generate_Cookie(COOKIE_MAGIC_COMPARTMENTSINK , cs);
594 }
595 else
596 {
597 FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
598 return E_NOTIMPL;
599 }
600
601 TRACE("cookie %x\n",*pdwCookie);
602
603 return S_OK;
604 }
605
606 static HRESULT WINAPI CompartmentSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
607 {
608 Compartment *This = impl_from_ITfSource(iface);
609 CompartmentSink *sink;
610
611 TRACE("(%p) %x\n",This,pdwCookie);
612
613 if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_COMPARTMENTSINK)
614 return E_INVALIDARG;
615
616 sink = remove_Cookie(pdwCookie);
617 if (!sink)
618 return CONNECT_E_NOCONNECTION;
619
620 list_remove(&sink->entry);
621 free_sink(sink);
622
623 return S_OK;
624 }
625
626 static const ITfSourceVtbl CompartmentSourceVtbl =
627 {
628 CompartmentSource_QueryInterface,
629 CompartmentSource_AddRef,
630 CompartmentSource_Release,
631 CompartmentSource_AdviseSink,
632 CompartmentSource_UnadviseSink,
633 };
634
635 static HRESULT Compartment_Constructor(CompartmentValue *valueData, ITfCompartment **ppOut)
636 {
637 Compartment *This;
638
639 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Compartment));
640 if (This == NULL)
641 return E_OUTOFMEMORY;
642
643 This->ITfCompartment_iface.lpVtbl= &CompartmentVtbl;
644 This->ITfSource_iface.lpVtbl = &CompartmentSourceVtbl;
645 This->refCount = 1;
646
647 This->valueData = valueData;
648 VariantInit(&This->variant);
649
650 list_init(&This->CompartmentEventSink);
651
652 *ppOut = &This->ITfCompartment_iface;
653 TRACE("returning %p\n", *ppOut);
654 return S_OK;
655 }