1/*
2 * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebKitDLL.h"
28#include "WebNotificationCenter.h"
29
30#include "WebNotification.h"
31#include <WebCore/COMPtr.h>
32#include <WebCore/PlatformString.h>
33#include <wtf/HashMap.h>
34#include <wtf/HashTraits.h>
35#include <wtf/Vector.h>
36#include <wtf/text/StringHash.h>
37#include <utility>
38#include <wchar.h>
39
40using namespace WebCore;
41
42typedef std::pair<COMPtr<IUnknown>, COMPtr<IWebNotificationObserver> > ObjectObserverPair;
43typedef Vector<ObjectObserverPair> ObjectObserverList;
44typedef ObjectObserverList::iterator ObserverListIterator;
45typedef HashMap<String, ObjectObserverList> MappedObservers;
46
47struct WebNotificationCenterPrivate {
48    MappedObservers m_mappedObservers;
49};
50
51// WebNotificationCenter ----------------------------------------------------------------
52
53IWebNotificationCenter* WebNotificationCenter::m_defaultCenter = 0;
54
55WebNotificationCenter::WebNotificationCenter()
56    : m_refCount(0)
57    , d(new WebNotificationCenterPrivate)
58{
59    gClassCount++;
60    gClassNameCount.add("WebNotificationCenter");
61}
62
63WebNotificationCenter::~WebNotificationCenter()
64{
65    gClassCount--;
66    gClassNameCount.remove("WebNotificationCenter");
67}
68
69WebNotificationCenter* WebNotificationCenter::createInstance()
70{
71    WebNotificationCenter* instance = new WebNotificationCenter();
72    instance->AddRef();
73    return instance;
74}
75
76// IUnknown -------------------------------------------------------------------
77
78HRESULT STDMETHODCALLTYPE WebNotificationCenter::QueryInterface(REFIID riid, void** ppvObject)
79{
80    *ppvObject = 0;
81    if (IsEqualGUID(riid, IID_IUnknown))
82        *ppvObject = static_cast<IWebNotificationCenter*>(this);
83    else if (IsEqualGUID(riid, IID_IWebNotificationCenter))
84        *ppvObject = static_cast<IWebNotificationCenter*>(this);
85    else
86        return E_NOINTERFACE;
87
88    AddRef();
89    return S_OK;
90}
91
92ULONG STDMETHODCALLTYPE WebNotificationCenter::AddRef(void)
93{
94    return ++m_refCount;
95}
96
97ULONG STDMETHODCALLTYPE WebNotificationCenter::Release(void)
98{
99    ULONG newRef = --m_refCount;
100    if (!newRef)
101        delete(this);
102
103    return newRef;
104}
105
106IWebNotificationCenter* WebNotificationCenter::defaultCenterInternal()
107{
108    if (!m_defaultCenter)
109        m_defaultCenter = WebNotificationCenter::createInstance();
110    return m_defaultCenter;
111}
112
113void WebNotificationCenter::postNotificationInternal(IWebNotification* notification, BSTR notificationName, IUnknown* anObject)
114{
115    String name(notificationName, SysStringLen(notificationName));
116    MappedObservers::iterator it = d->m_mappedObservers.find(name);
117    if (it == d->m_mappedObservers.end())
118        return;
119
120    // Intentionally make a copy of the list to avoid the possibility of errors
121    // from a mutation of the list in the onNotify callback.
122    ObjectObserverList list = it->second;
123
124    ObserverListIterator end = list.end();
125    for (ObserverListIterator it2 = list.begin(); it2 != end; ++it2) {
126        IUnknown* observedObject = it2->first.get();
127        IWebNotificationObserver* observer = it2->second.get();
128        if (!observedObject || !anObject || observedObject == anObject)
129            observer->onNotify(notification);
130    }
131}
132
133// IWebNotificationCenter -----------------------------------------------------
134
135HRESULT STDMETHODCALLTYPE WebNotificationCenter::defaultCenter(
136    /* [retval][out] */ IWebNotificationCenter** center)
137{
138    *center = defaultCenterInternal();
139    (*center)->AddRef();
140    return S_OK;
141}
142
143HRESULT STDMETHODCALLTYPE WebNotificationCenter::addObserver(
144    /* [in] */ IWebNotificationObserver* observer,
145    /* [in] */ BSTR notificationName,
146    /* [in] */ IUnknown* anObject)
147{
148    String name(notificationName, SysStringLen(notificationName));
149    MappedObservers::iterator it = d->m_mappedObservers.find(name);
150    if (it != d->m_mappedObservers.end())
151        it->second.append(ObjectObserverPair(anObject, observer));
152    else {
153        ObjectObserverList list;
154        list.append(ObjectObserverPair(anObject, observer));
155        d->m_mappedObservers.add(name, list);
156    }
157
158    return S_OK;
159}
160
161HRESULT STDMETHODCALLTYPE WebNotificationCenter::postNotification(
162    /* [in] */ IWebNotification* notification)
163{
164    BSTR name;
165    HRESULT hr = notification->name(&name);
166    if (FAILED(hr))
167        return hr;
168
169    COMPtr<IUnknown> obj;
170    hr = notification->getObject(&obj);
171    if (FAILED(hr))
172        return hr;
173
174    postNotificationInternal(notification, name, obj.get());
175    SysFreeString(name);
176
177    return hr;
178}
179
180HRESULT STDMETHODCALLTYPE WebNotificationCenter::postNotificationName(
181    /* [in] */ BSTR notificationName,
182    /* [in] */ IUnknown* anObject,
183    /* [optional][in] */ IPropertyBag* userInfo)
184{
185    COMPtr<WebNotification> notification(AdoptCOM, WebNotification::createInstance(notificationName, anObject, userInfo));
186    postNotificationInternal(notification.get(), notificationName, anObject);
187    return S_OK;
188}
189
190HRESULT STDMETHODCALLTYPE WebNotificationCenter::removeObserver(
191    /* [in] */ IWebNotificationObserver* anObserver,
192    /* [in] */ BSTR notificationName,
193    /* [optional][in] */ IUnknown* anObject)
194{
195    String name(notificationName, SysStringLen(notificationName));
196    MappedObservers::iterator it = d->m_mappedObservers.find(name);
197    if (it == d->m_mappedObservers.end())
198        return E_FAIL;
199
200    ObjectObserverList& observerList = it->second;
201    ObserverListIterator end = observerList.end();
202
203    int i = 0;
204    for (ObserverListIterator it2 = observerList.begin(); it2 != end; ++it2, ++i) {
205        IUnknown* observedObject = it2->first.get();
206        IWebNotificationObserver* observer = it2->second.get();
207        if (observer == anObserver && (!anObject || anObject == observedObject)) {
208            observerList.remove(i);
209            break;
210        }
211    }
212
213    if (observerList.isEmpty())
214        d->m_mappedObservers.remove(name);
215
216    return S_OK;
217}
218