sbBaseDeviceEventTarget.cpp
Go to the documentation of this file.
1 /* vim: set sw=2 :miv */
2 /*
3 //
4 // BEGIN SONGBIRD GPL
5 //
6 // This file is part of the Songbird web player.
7 //
8 // Copyright(c) 2005-2008 POTI, Inc.
9 // http://songbirdnest.com
10 //
11 // This file may be licensed under the terms of of the
12 // GNU General Public License Version 2 (the "GPL").
13 //
14 // Software distributed under the License is distributed
15 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
16 // express or implied. See the GPL for the specific language
17 // governing rights and limitations.
18 //
19 // You should have received a copy of the GPL along with this
20 // program. If not, go to http://www.gnu.org/licenses/gpl.html
21 // or write to the Free Software Foundation, Inc.,
22 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 //
24 // END SONGBIRD GPL
25 //
26 */
27 
29 
30 #include <nsIThread.h>
31 #include <nsAutoLock.h>
32 #include <nsAutoPtr.h>
33 #include <nsCOMPtr.h>
34 #include <nsDeque.h>
35 #include <nsThreadUtils.h>
36 
37 #include "sbIDeviceEventListener.h"
38 #include "sbDeviceEvent.h"
40 #include <sbThreadUtils.h>
41 
42 
43 class sbDeviceEventTargetRemovalHelper : public nsDequeFunctor {
44  public:
46  : mIndexToRemove(aIndex) {}
47  virtual void* operator()(void* aObject)
48  {
51  if (state->length > mIndexToRemove) {
52  --state->length;
53  }
54  if (state->index >= mIndexToRemove) {
55  --state->index;
56  }
57  /* no return value (it's only useful for FirstThat, not ForEach) */
58  return nsnull;
59  }
60  protected:
61  PRInt32 mIndexToRemove;
62 };
63 
65 {
66  /* member initializers and constructor code */
67  mMonitor = nsAutoMonitor::NewMonitor(__FILE__);
68  NS_ASSERTION(mMonitor, "Failed to create monitor");
69 }
70 
72 {
73  /* destructor code */
74  if (mMonitor) {
75  nsAutoMonitor::DestroyMonitor(mMonitor);
76  mMonitor = nsnull;
77  }
78 }
79 
80 /* boolean dispatchEvent (in sbIDeviceEvent aEvent, [optional] PRBool aAsync); */
81 NS_IMETHODIMP sbBaseDeviceEventTarget::DispatchEvent(sbIDeviceEvent *aEvent,
82  PRBool aAsync,
83  PRBool* _retval)
84 {
85  nsresult rv;
86 
87  PRUint32 const listeners = mListeners.Count();
88  if (listeners == 0) {
89  if (_retval) {
90  *_retval = PR_FALSE;
91  }
92  return NS_OK;
93  }
94 
95  // This will be released inside of DispatchEventInternal. In the case of
96  // an async dispatch we need to hold on till dispatched.
97  if (aAsync) {
98  nsCOMPtr<sbIDeviceEvent> event(aEvent);
100  *this,
102  NS_ERROR_FAILURE,
103  event);
104  // don't have a return value if dispatching asynchronously
105  // (since the variable is likely to be dead by that point)
106  }
107  else {
108  if (!NS_IsMainThread()) {
109  nsCOMPtr<sbIDeviceEvent> event(aEvent);
111  *this,
113  NS_ERROR_FAILURE,
114  event);
115  }
116  else {
117  rv = DispatchEventInternal(aEvent);
118  }
119  }
120  if (_retval) {
121  *_retval = PR_TRUE;
122  }
123  return rv;
124 }
125 
126 /* Dispatch an event, assuming we're already on the main thread */
127 nsresult sbBaseDeviceEventTarget::DispatchEventInternal(nsCOMPtr<sbIDeviceEvent> aEvent)
128 {
129  DispatchState state;
130  state.length = mListeners.Count();
131 
132  nsresult rv;
133 
134  // make sure the event has not already been dispatched
135  nsCOMPtr<sbDeviceEvent> event = do_QueryInterface(aEvent, &rv);
136 
137  // Now release the addref our caller did since we are now holding a reference
138  NS_ENSURE_SUCCESS(rv, rv);
139  NS_ENSURE_FALSE(event->WasDispatched(), NS_ERROR_ALREADY_INITIALIZED);
140 
141  // set the event target
142  rv = event->SetTarget(this);
143  NS_ENSURE_SUCCESS(rv, rv);
144 
145  // store the state into our state stack, so if any listener removes a
146  // listener we get updated
147  mStates.Push(&state);
148 
149  for (state.index = 0; state.index < state.length; ++state.index) {
150  rv = mListeners[state.index]->OnDeviceEvent(aEvent);
151  /* the return value is only checked on debug builds */
152  #if DEBUG
153  if (NS_FAILED(rv)) {
154  NS_WARNING("Device event listener returned error");
155  }
156  #endif
157  }
158 
159  // pop the stored state, to ensure we don't end up with a pointer to a stack
160  // variable dangling
161  mStates.Pop();
162 
163  // bubble this event upwards
164  if (!mParentEventTarget) {
165  // no other event target, just return early
166  return NS_OK;
167  }
168 
169  nsCOMPtr<sbIDeviceEventTarget> parentEventTarget =
170  do_QueryReferent(mParentEventTarget, &rv);
171  if (NS_FAILED(rv) || !parentEventTarget) {
172  // the parent's gone, we don't care all that much
173  return NS_OK;
174  }
175 
176  // always dispatch as sync, since if we wanted to be async, we're already on
177  // that path and the caller's already gone.
178  rv = parentEventTarget->DispatchEvent(aEvent, PR_FALSE, nsnull);
179  NS_ENSURE_SUCCESS(rv, rv);
180 
181  return NS_OK;
182 }
183 
184 /* void addEventListener (in sbIDeviceEventListener aListener); */
185 NS_IMETHODIMP sbBaseDeviceEventTarget::AddEventListener(sbIDeviceEventListener *aListener)
186 {
187  nsresult rv;
188 
189  // we need to access the listeners from the main thread only
190  if (!NS_IsMainThread()) {
191  // we need to proxy to the main thread
192  // because we can't just use a monitor (that'll deadlock if we're in the
193  // middle of a listener, then got proxied onto a second thread)
194  nsCOMPtr<sbIDeviceEventTarget> proxiedSelf;
195  { /* scope the monitor */
196  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
197  nsAutoMonitor mon(mMonitor);
198  rv = do_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
199  NS_GET_IID(sbIDeviceEventTarget),
200  this,
201  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
202  getter_AddRefs(proxiedSelf));
203  NS_ENSURE_SUCCESS(rv, rv);
204  }
205 
206  // XXX Mook: consider wrapping the listener in a proxy
207 
208  return proxiedSelf->AddEventListener(aListener);
209  }
210 
211  PRInt32 index = mListeners.IndexOf(aListener);
212  if (index >= 0) {
213  // the listener already exists, do not re-add
214  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
215  }
216  PRBool succeeded = mListeners.AppendObject(aListener);
217  return succeeded ? NS_OK : NS_ERROR_FAILURE;
218 }
219 
220 /* void removeEventListener (in sbIDeviceEventListener aListener); */
221 NS_IMETHODIMP sbBaseDeviceEventTarget::RemoveEventListener(sbIDeviceEventListener *aListener)
222 {
223  nsresult rv;
224 
225  // we need to access the listeners from the main thread only
226  if (!NS_IsMainThread()) {
227  // we need to proxy to the main thread
228  nsCOMPtr<sbIDeviceEventTarget> proxiedSelf;
229  { /* scope the monitor */
230  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
231  nsAutoMonitor mon(mMonitor);
232  rv = do_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
233  NS_GET_IID(sbIDeviceEventTarget),
234  this,
235  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
236  getter_AddRefs(proxiedSelf));
237  NS_ENSURE_SUCCESS(rv, rv);
238  }
239  return proxiedSelf->RemoveEventListener(aListener);
240  }
241 
242  // XXX Mook: if we wrapped listeners, watch for equality!
243  PRInt32 indexToRemove = mListeners.IndexOf(aListener);
244  if (indexToRemove < 0) {
245  // err, no such listener
246  return NS_OK;
247  }
248 
249  // remove the listener
250  PRBool succeeded = mListeners.RemoveObjectAt(indexToRemove);
251  NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
252 
253  // fix up the stack to account for the removed listener
254  // (decrease the stored length of the listener array)
255  sbDeviceEventTargetRemovalHelper helper(indexToRemove);
256  mStates.ForEach(helper);
257 
258  return NS_OK;
259 }
nsCOMPtr< nsIWeakReference > mParentEventTarget
nsresult sbInvokeOnMainThread1Async(T &aObject, MT aMethod, RT aFailureReturnValue, A1 aArg1)
return NS_OK
function succeeded(ch, cx, status, data)
nsresult DispatchEventInternal(nsCOMPtr< sbIDeviceEvent > aEvent)
var event
nsCOMArray< sbIDeviceEventListener > mListeners
nsresult do_GetProxyForObject(nsIEventTarget *aTarget, REFNSIID aIID, nsISupports *aObj, PRInt32 aProxyType, void **aProxyObject)
Songbird Thread Utilities Definitions.
virtual void * operator()(void *aObject)
RT sbInvokeOnMainThread1(T &aObject, MT aMethod, RT aFailureReturnValue, A1 aArg1)