sbLocalDatabaseMediaListListener.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
29 
30 #include <sbIMediaItem.h>
31 #include <sbIMediaList.h>
32 #include <sbIMediaListListener.h>
33 
34 #include <nsIWeakReference.h>
35 #include <nsIWeakReferenceUtils.h>
36 
37 #include <nsAutoLock.h>
38 #include <nsHashKeys.h>
39 #include <nsThreadUtils.h>
40 #include <nsServiceManagerUtils.h>
41 
43 
44 #ifdef DEBUG
45 #include <nsIXPConnect.h>
46 #endif
47 #ifdef PR_LOGGING
48 #include <prprf.h>
49 #endif
50 
51 #define SB_ENSURE_TRUE_VOID(_arg) \
52  NS_ENSURE_TRUE((_arg), /* void */)
53 
58 #ifdef PR_LOGGING
59 
60 static PRLogModuleInfo* gMediaListListenerLog = nsnull;
61 #define TRACE(args) if (gMediaListListenerLog) PR_LOG(gMediaListListenerLog, PR_LOG_DEBUG, args)
62 #define LOG(args) if (gMediaListListenerLog) PR_LOG(gMediaListListenerLog, PR_LOG_WARN, args)
63 
64 #else /* PR_LOGGING */
65 
66 #define TRACE(args) /* nothing */
67 #define LOG(args) /* nothing */
68 
69 #endif /* PR_LOGGING */
70 
72 : mListenerArrayLock(nsnull),
73  mBatchDepth(0)
74 {
75  MOZ_COUNT_CTOR(sbLocalDatabaseMediaListListener);
76 #ifdef PR_LOGGING
77  if (!gMediaListListenerLog) {
78  gMediaListListenerLog = PR_NewLogModule("sbLocalDatabaseMediaListListener");
79  }
80 #endif
81  TRACE(("LocalDatabaseMediaListListener[0x%.8x] - Constructed", this));
82 }
83 
85 {
86  if (mListenerArrayLock) {
87  nsAutoLock::DestroyLock(mListenerArrayLock);
88  }
89  TRACE(("LocalDatabaseMediaListListener[0x%.8x] - Destructed", this));
90  MOZ_COUNT_DTOR(sbLocalDatabaseMediaListListener);
91 }
92 
93 nsresult
95 {
96  NS_WARN_IF_FALSE(!mListenerArrayLock, "You're calling Init more than once!");
97 
98  // Initialize our lock.
99  if (!mListenerArrayLock) {
100  mListenerArrayLock =
101  nsAutoLock::NewLock("sbLocalDatabaseMediaListListener::"
102  "mListenerArrayLock");
103  NS_ENSURE_TRUE(mListenerArrayLock, NS_ERROR_OUT_OF_MEMORY);
104  }
105 
106  return NS_OK;
107 }
108 
109 nsresult
111  sbIMediaListListener* aListener,
112  PRBool aOwnsWeak,
113  PRUint32 aFlags,
114  sbIPropertyArray* aPropertyFilter)
115 {
116 #ifdef DEBUG
117  nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
118  if(xpc) {
119  nsCAutoString objtype;
120  nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(aListener);
121  if (wrapped) {
122  objtype.AssignLiteral("wrapped");
123  }
124  else {
125  objtype.AssignLiteral("native");
126  }
127 
128  nsCAutoString stack;
129  stack.AssignLiteral("no js stack");
130 
131  nsCOMPtr<nsIStackFrame> current;
132  xpc->GetCurrentJSStack(getter_AddRefs(current));
133  if (current) {
134  current->ToString(getter_Copies(stack));
135  }
136  TRACE(("LocalDatabaseMediaListListener[0x%.8x] - "
137  "AddListener 0x%.8x %d %s %s",
138  this, aListener, aOwnsWeak, objtype.get(), stack.get()));
139  }
140 #endif
141 
142  NS_ASSERTION(mListenerArrayLock, "You haven't called Init yet!");
143  NS_ENSURE_ARG_POINTER(aListener);
144 
145  nsresult rv;
146 
147  // When aFlags is 0, this means that it was unspecified by the caller and
148  // should get the default value
149  if (aFlags == 0) {
151  }
152 
153  // Acquiring the proxied proxy object manager will spin the event loop, so
154  // we need to do it before acquiring mListenerArrayLock
155  nsCOMPtr<nsIProxyObjectManager> proxyObjMgr =
156  do_ProxiedGetService(NS_XPCOMPROXY_CONTRACTID, &rv);
157  NS_ENSURE_SUCCESS(rv, rv);
158 
159  nsAutoLock lock(mListenerArrayLock);
160 
161  // See if we have already added this listener.
162  PRUint32 length = mListenerArray.Length();
163  nsCOMPtr<nsISupports> ref = do_QueryInterface(aListener, &rv);
164  NS_ENSURE_SUCCESS(rv, rv);
165 
166  if (aOwnsWeak) {
167  nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aListener, &rv);
168  NS_ENSURE_SUCCESS(rv, rv);
169  for (PRUint32 i = 0; i < length; i++) {
170  if(mListenerArray[i]->mRef == weak) {
171  NS_WARNING("Attempted to add a weak listener twice!");
172  return NS_OK;
173  }
174  }
175  }
176  else {
177  for (PRUint32 i = 0; i < length; i++) {
178  if(mListenerArray[i]->mRef == ref) {
179  NS_WARNING("Attempted to add a strong listener twice!");
180  return NS_OK;
181  }
182  }
183  }
184 
185  sbListenerInfoAutoPtr info(new sbListenerInfo());
186  NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
187 
188  if (aOwnsWeak) {
189  nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aListener, &rv);
190  NS_ENSURE_SUCCESS(rv, rv);
191  rv = info->Init(proxyObjMgr, weak, mBatchDepth, aFlags, aPropertyFilter);
192  NS_ENSURE_SUCCESS(rv, rv);
193  }
194  else {
195  rv = info->Init(proxyObjMgr, aListener, mBatchDepth, aFlags, aPropertyFilter);
196  NS_ENSURE_SUCCESS(rv, rv);
197  }
198 
199  sbListenerInfoAutoPtr* added = mListenerArray.AppendElement(info.forget());
200  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
201 
202  // If this list is in a batch, the newly added listener should be notified
203  // immediately of this fact
204  if (mBatchDepth > 0) {
205  nsCOMPtr<sbIMediaList> list =
206  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaList*, aList), &rv);
207  NS_ENSURE_SUCCESS(rv, rv);
208 
209  for (PRUint32 i = 0; i < mBatchDepth; i++) {
210  (*added)->BeginBatch();
211  rv = aListener->OnBatchBegin(aList);
212  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "OnBatchBegin returned a failure code");
213  }
214  }
215 
216  return NS_OK;
217 }
218 
219 nsresult
221  sbIMediaListListener* aListener)
222 {
223  TRACE(("LocalDatabaseMediaListListener[0x%.8x] - RemoveListener 0x%.8x",
224  this, aListener));
225  NS_ASSERTION(mListenerArrayLock, "You haven't called Init yet!");
226  NS_ENSURE_ARG_POINTER(aListener);
227  nsresult rv;
228 
229  PRUint32 batchDepth = 0;
230  {
231  nsAutoLock lock(mListenerArrayLock);
232 
233  PRUint32 length = mListenerArray.Length();
234 
235  nsCOMPtr<nsISupports> ref = do_QueryInterface(aListener, &rv);
236  for (PRUint32 i = 0; i < length; i++) {
237  if(mListenerArray[i]->mRef == ref) {
238  for (PRUint32 j = 0; j < mBatchDepth; j++) {
239  mListenerArray[i]->EndBatch();
240  }
241  mListenerArray.RemoveElementAt(i);
242  return NS_OK;
243  }
244  }
245 
246  nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aListener, &rv);
247  if (NS_SUCCEEDED(rv)) {
248  for (PRUint32 i = 0; i < length; i++) {
249  if(mListenerArray[i]->mRef == weak) {
250  mListenerArray.RemoveElementAt(i);
251  return NS_OK;
252  }
253  }
254  NS_WARNING("Attempted to remove a weak listener that was never added!");
255  }
256  else {
257  NS_WARNING("Attempted to remove a strong listener that was never added!");
258  }
259 
260  // Snapshot batchdepth at the time of removal (while still in the lock)
261  batchDepth = mBatchDepth;
262  }
263 
264  // If this list is in a batch, the listener being removed should be
265  // sent the batch end callback mBatchDepth times, otherwise, if that
266  // listener is re-registered immediately (like, for instance, in
267  // sbLocalDatabaseMediaListView::UpdateListener, it will receive the
268  // onBatchBegin callback mBatchDepth times again, which will throw
269  // its batch count out of balance. Technically, the batch has not
270  // actually ended, but preventing an out-of-balance batch count any
271  // other way is going to be very hard, and very inelegant. This at
272  // least keeps everything in balance at all times, and it can be
273  // argued that the batch has indeed ended for that listener, since
274  // no more callbacks will be issued to it after that (unless it is
275  // re-registered, of course)
276 
277  // Do this AFTER removing from the array, and after releasing the lock
278  // since the onbatchend listener should be free to add/modify other
279  // listeners without causing a deadlock
280 
281  if (batchDepth > 0) {
282  nsCOMPtr<sbIMediaList> list =
283  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaList*, aList), &rv);
284  NS_ENSURE_SUCCESS(rv, rv);
285 
286  for (PRUint32 i = 0; i < batchDepth; i++) {
287  rv = aListener->OnBatchEnd(aList);
288  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "OnBatchEnd returned a failure code");
289  }
290  }
291 
292  return NS_OK;
293 }
294 
295 PRUint32
297 {
298  NS_ASSERTION(mListenerArrayLock, "You haven't called Init yet!");
299 
300  nsAutoLock lock(mListenerArrayLock);
301  return mListenerArray.Length();
302 }
303 
304 nsresult
305 sbLocalDatabaseMediaListListener::SnapshotListenerArray(sbMediaListListenersArray& aArray,
306  PRUint32 aFlags,
307  sbIPropertyArray* aProperties)
308 {
309  nsAutoLock lock(mListenerArrayLock);
310 
311  PRUint32 length = mListenerArray.Length();
312  for (PRUint32 i = 0; i < length; i++) {
313  if (mListenerArray[i]->ShouldNotify(aFlags, aProperties)) {
314  nsString debugAddress;
315  mListenerArray[i]->GetDebugAddress(debugAddress);
316  ListenerAndDebugAddress* added =
317  aArray.AppendElement(ListenerAndDebugAddress(mListenerArray[i]->mProxy,
318  debugAddress));
319  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
320  }
321  }
322 
323  return NS_OK;
324 }
325 
326 void
327 sbLocalDatabaseMediaListListener::SweepListenerArray(sbStopNotifyArray& aStopNotifying)
328 {
329  nsAutoLock lock(mListenerArrayLock);
330 
331  PRUint32 numStopNotifying = aStopNotifying.Length();
332 
333  // The aStopNotifying array tells us what listeners are "gone" (weak
334  // listeners that have gone away) as well as which listeners should no
335  // longer be notified in the current batch. Match up the listeners in this
336  // array with the main listener array by comparing the listener comptr
337 
338  for (PRInt32 i = numStopNotifying - 1; i >= 0; i--) {
339  const StopNotifyFlags& stop = aStopNotifying[i];
340  // Don't cache the length of mListenerArray because it can change between
341  // iterations of the outer loop
342  for (PRInt32 j = mListenerArray.Length() - 1; j >= 0; j--) {
343  if (stop.listener == mListenerArray[j]->mProxy) {
344  if (stop.isGone) {
345  mListenerArray.RemoveElementAt(j);
346  }
347  else {
348  if (stop.listenerFlags > 0) {
349  mListenerArray[j]->SetShouldStopNotifying(stop.listenerFlags);
350  }
351  }
352  }
353  }
354  }
355 
356 }
357 
358 #ifdef PR_LOGGING
359 
360 #define SB_NOTIFY_LOG_COUNT(method) \
361  nsString __notifiedList; \
362  TRACE(("LocalDatabaseMediaListListener[0x%.8x] - " #method " %d of %d", \
363  this, snapshot.Length(), mListenerArray.Length()));
364 
365 #define SB_NOTIFY_LOG_REMEMBER_NOTIFIED \
366  PR_BEGIN_MACRO \
367  __notifiedList.AppendLiteral(" "); \
368  __notifiedList.Append(snapshot[i].debugAddress); \
369  __notifiedList.AppendLiteral(" ("); \
370  __notifiedList.AppendInt(__delta); \
371  __notifiedList.AppendLiteral(")"); \
372  PR_END_MACRO
373 
374 #define SB_NOTIFY_LOG_NOTIFIED \
375  TRACE(("LocalDatabaseMediaListListener[0x%.8x] - Notified%s", \
376  this, NS_LossyConvertUTF16toASCII(__notifiedList).get()));
377 
378 #define SB_NOTIFY_LOG_START_TIMER \
379  PRTime __now = PR_Now();
380 
381 #define SB_NOTIFY_LOG_STOP_TIMER \
382  PRUint32 __delta = (PRUint32)(PR_Now() - __now);
383 #else
384 
385 #define SB_NOTIFY_LOG_COUNT(method)
386 #define SB_NOTIFY_LOG_REMEMBER_NOTIFIED
387 #define SB_NOTIFY_LOG_NOTIFIED
388 #define SB_NOTIFY_LOG_START_TIMER
389 #define SB_NOTIFY_LOG_STOP_TIMER
390 
391 #endif
392 
393 #define SB_NOTIFY_LISTENERS_HEAD \
394  NS_ASSERTION(mListenerArrayLock, "You haven't called Init yet!"); \
395  \
396  sbMediaListListenersArray snapshot;
397 
398 #define SB_NOTIFY_LISTENERS_TAIL(method, call, flag) \
399  SB_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv)); \
400  \
401  PRUint32 length = snapshot.Length(); \
402  sbStopNotifyArray stopNotifying(length); \
403  \
404  SB_NOTIFY_LOG_COUNT(method) \
405  \
406  for (PRUint32 i = 0; i < length; i++) { \
407  PRBool noMoreForBatch = PR_FALSE; \
408  SB_NOTIFY_LOG_START_TIMER \
409  rv = snapshot[i].listener->call; \
410  SB_NOTIFY_LOG_STOP_TIMER \
411  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), #call " returned a failure code"); \
412  PRBool isGone = rv == NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA; \
413  PRUint32 listenerFlags = 0; \
414  if (noMoreForBatch) { \
415  listenerFlags = sbIMediaList::flag; \
416  } \
417  StopNotifyFlags* added = \
418  stopNotifying.AppendElement(StopNotifyFlags(snapshot[i].listener, \
419  listenerFlags, \
420  isGone)); \
421  SB_ENSURE_TRUE_VOID(added); \
422  SB_NOTIFY_LOG_REMEMBER_NOTIFIED; \
423  } \
424  \
425  SB_NOTIFY_LOG_NOTIFIED \
426  SweepListenerArray(stopNotifying);
427 
428 #define SB_NOTIFY_LISTENERS(method, call, flag) \
429  SB_NOTIFY_LISTENERS_HEAD \
430  nsresult rv = SnapshotListenerArray(snapshot, sbIMediaList::flag); \
431  SB_NOTIFY_LISTENERS_TAIL(method, call, flag)
432 
433 #define SB_NOTIFY_LISTENERS_PROPERTIES(method, call, flag) \
434  SB_NOTIFY_LISTENERS_HEAD \
435  nsresult rv = SnapshotListenerArray(snapshot, \
436  sbIMediaList::flag, \
437  aProperties); \
438  SB_NOTIFY_LISTENERS_TAIL(method, call, flag)
439 
443 void
445  sbIMediaItem* aItem,
446  PRUint32 aIndex)
447 {
448  SB_ENSURE_TRUE_VOID(aList);
449  SB_ENSURE_TRUE_VOID(aItem);
450 
452  OnItemAdded(aList, aItem, aIndex, &noMoreForBatch),
453  LISTENER_FLAGS_ITEMADDED);
454 }
455 
460 void
462  sbIMediaItem* aItem,
463  PRUint32 aIndex)
464 {
465  SB_ENSURE_TRUE_VOID(aList);
466  SB_ENSURE_TRUE_VOID(aItem);
467 
469  OnBeforeItemRemoved(aList,
470  aItem,
471  aIndex,
472  &noMoreForBatch),
473  LISTENER_FLAGS_BEFOREITEMREMOVED);
474 }
475 
479 void
481  sbIMediaItem* aItem,
482  PRUint32 aIndex)
483 {
484  SB_ENSURE_TRUE_VOID(aList);
485  SB_ENSURE_TRUE_VOID(aItem);
486 
488  OnAfterItemRemoved(aList,
489  aItem,
490  aIndex,
491  &noMoreForBatch),
492  LISTENER_FLAGS_AFTERITEMREMOVED);
493 }
494 
498 void
500  sbIMediaItem* aItem,
501  sbIPropertyArray* aProperties)
502 {
503  SB_ENSURE_TRUE_VOID(aList);
504  SB_ENSURE_TRUE_VOID(aItem);
505  SB_ENSURE_TRUE_VOID(aProperties);
506 
507 #if 0
508  nsAutoString list;
509  aList->ToString(list);
510  nsAutoString item;
511  aItem->ToString(item);
512  nsAutoString props;
513  aProperties->ToString(props);
514  TRACE(("LocalDatabaseMediaListListener[0x%.8x] - "
515  "NotifyListenersItemUpdated %s %s %d %s", this,
516  NS_LossyConvertUTF16toASCII(list).get(),
517  NS_LossyConvertUTF16toASCII(item).get(),
518  aIndex,
519  NS_LossyConvertUTF16toASCII(props).get()));
520 #endif
521 
523  OnItemUpdated(aList,
524  aItem,
525  aProperties,
526  &noMoreForBatch),
527  LISTENER_FLAGS_ITEMUPDATED);
528 }
529 
533 void
535  PRUint32 aFromIndex,
536  PRUint32 aToIndex)
537 {
538  SB_ENSURE_TRUE_VOID(aList);
539 
541  OnItemMoved(aList,
542  aFromIndex,
543  aToIndex,
544  &noMoreForBatch),
545  LISTENER_FLAGS_ITEMMOVED);
546 }
547 
551 void
553  (sbIMediaList* aList,
554  PRBool aExcludeLists)
555 {
556  SB_ENSURE_TRUE_VOID(aList);
557 
558  SB_NOTIFY_LISTENERS(NotifyListenersBeforeListCleared,
559  OnBeforeListCleared(aList, aExcludeLists, &noMoreForBatch),
560  LISTENER_FLAGS_BEFORELISTCLEARED);
561 }
562 
566 void
568  (sbIMediaList* aList,
569  PRBool aExcludeLists)
570 {
571  SB_ENSURE_TRUE_VOID(aList);
572 
573  SB_NOTIFY_LISTENERS(NotifyListenersListCleared,
574  OnListCleared(aList, aExcludeLists, &noMoreForBatch),
575  LISTENER_FLAGS_LISTCLEARED);
576 }
577 
581 void
583 {
584  SB_ENSURE_TRUE_VOID(aList);
585 
586  // Tell all of our listener infos that we have started a batch
587  {
588  nsAutoLock lock(mListenerArrayLock);
589  mBatchDepth++;
590  PRUint32 length = mListenerArray.Length();
591  for (PRUint32 i = 0; i < length; i++) {
592  mListenerArray[i]->BeginBatch();
593  }
594  }
595 
597  OnBatchBegin(aList),
598  LISTENER_FLAGS_BATCHBEGIN);
599 }
600 
604 void
606 {
607  SB_ENSURE_TRUE_VOID(aList);
608 
609  // Tell all of our listener infos that we have ended a batch
610  {
611  nsAutoLock lock(mListenerArrayLock);
612 
613  if (mBatchDepth == 0) {
614  NS_ERROR("Batch begin/end imbalance");
615  return;
616  }
617 
618  mBatchDepth--;
619  PRUint32 length = mListenerArray.Length();
620  for (PRUint32 i = 0; i < length; i++) {
621  mListenerArray[i]->EndBatch();
622  }
623  }
624 
626  OnBatchEnd(aList),
627  LISTENER_FLAGS_BATCHEND);
628 }
629 
631  mIsGone(PR_FALSE)
632 {
633  MOZ_COUNT_CTOR(sbListenerInfo);
634 }
635 
637 {
638  MOZ_COUNT_DTOR(sbListenerInfo);
639 }
640 
641 nsresult
642 sbListenerInfo::Init(nsIProxyObjectManager *aProxyObjMgr,
643  sbIMediaListListener* aListener,
644  PRUint32 aCurrentBatchDepth,
645  PRUint32 aFlags,
646  sbIPropertyArray* aPropertyFilter)
647 {
648  NS_ENSURE_ARG_POINTER(aProxyObjMgr);
649  NS_ASSERTION(aListener, "aListener is null");
650  NS_ASSERTION(!mProxy, "Init called twice");
651  nsresult rv;
652 
653  mRef = do_QueryInterface(aListener, &rv);
654  NS_ENSURE_SUCCESS(rv, rv);
655 
656  mFlags = aFlags;
657 
658  PRBool success = mStopNotifiyingStack.SetLength(aCurrentBatchDepth);
659  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
660  for (PRUint32 i = 0; i < aCurrentBatchDepth; i++) {
661  mStopNotifiyingStack[i] = 0;
662  }
663 
664  InitPropertyFilter(aPropertyFilter);
665 
666  /* This function is called with a lock held, so we must not spin the event
667  loop - so we use this variant of do_GetProxyForObject that does not */
668  rv = do_GetProxyForObjectWithManager(aProxyObjMgr,
669  NS_PROXY_TO_CURRENT_THREAD,
670  NS_GET_IID(sbIMediaListListener),
671  aListener,
672  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
673  getter_AddRefs(mProxy));
674  NS_ENSURE_SUCCESS(rv, rv);
675 
676 #ifdef PR_LOGGING
677  char buf[256];
678  PRUint32 len = PR_snprintf(buf, sizeof(buf), "0x%.8x", aListener);
679  mDebugAddress.Assign(NS_ConvertASCIItoUTF16(buf, len));
680 #endif
681  return NS_OK;
682 }
683 
684 nsresult
685 sbListenerInfo::Init(nsIProxyObjectManager *aProxyObjMgr,
686  nsIWeakReference* aWeakListener,
687  PRUint32 aCurrentBatchDepth,
688  PRUint32 aFlags,
689  sbIPropertyArray* aPropertyFilter)
690 {
691  NS_ENSURE_ARG_POINTER(aProxyObjMgr);
692  NS_ASSERTION(aWeakListener, "aWeakListener is null");
693  NS_ASSERTION(!mProxy, "Init called twice");
694  nsresult rv;
695 
696  mRef = do_QueryInterface(aWeakListener, &rv);
697  NS_ENSURE_SUCCESS(rv, rv);
698 
699  mWeak = aWeakListener;
700  mFlags = aFlags;
701 
702  PRBool success = mStopNotifiyingStack.SetLength(aCurrentBatchDepth);
703  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
704  for (PRUint32 i = 0; i < aCurrentBatchDepth; i++) {
705  mStopNotifiyingStack[i] = 0;
706  }
707 
708  InitPropertyFilter(aPropertyFilter);
709 
710  nsCOMPtr<sbIMediaListListener> wrapped =
712  NS_ENSURE_TRUE(wrapped, NS_ERROR_OUT_OF_MEMORY);
713 
714  /* This function is called with a lock held, so we must not spin the event
715  loop - so we use this variant of do_GetProxyForObject that guarantees we
716  don't */
717  rv = do_GetProxyForObjectWithManager(aProxyObjMgr,
718  NS_PROXY_TO_CURRENT_THREAD,
719  NS_GET_IID(sbIMediaListListener),
720  wrapped,
721  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
722  getter_AddRefs(mProxy));
723  NS_ENSURE_SUCCESS(rv, rv);
724 
725 #ifdef PR_LOGGING
726  nsCOMPtr<sbIMediaListListener> listener = do_QueryReferent(aWeakListener);
727  char buf[256];
728  PRUint32 len = PR_snprintf(buf, sizeof(buf), "0x%.8x",
729  listener ? listener.get() : 0);
730  mDebugAddress.Assign(NS_ConvertASCIItoUTF16(buf, len));
731 #endif
732 
733  return NS_OK;
734 }
735 
736 void
737 sbListenerInfo::GetDebugAddress(nsAString& aDebugAddress)
738 {
739  aDebugAddress = mDebugAddress;
740 }
741 
742 PRBool
743 sbListenerInfo::ShouldNotify(PRUint32 aFlag, sbIPropertyArray* aProperties)
744 {
745  // Check the flags to see if we should be notifying
746  if ((mFlags & aFlag) == 0) {
747  return PR_FALSE;
748  }
749 
750  // Check if we are set to stop notifying for this batch
751  if (mStopNotifiyingStack.Length() > 0 && mStopNotifiyingStack[0] & aFlag) {
752  return PR_FALSE;
753  }
754 
755  // If there are properties, check them
756  if (mHasPropertyFilter && aProperties) {
757  nsresult rv;
758 
759  PRUint32 length;
760  rv = aProperties->GetLength(&length);
761  NS_ENSURE_SUCCESS(rv, PR_FALSE);
762 
763  for (PRUint32 i = 0; i < length; i++) {
764  nsCOMPtr<sbIProperty> property;
765  rv = aProperties->GetPropertyAt(i, getter_AddRefs(property));
766  NS_ENSURE_SUCCESS(rv, PR_FALSE);
767 
768  nsString id;
769  rv = property->GetId(id);
770  NS_ENSURE_SUCCESS(rv, PR_FALSE);
771 
772  if (mPropertyFilter.GetEntry(id)) {
773  // Found, we should notify
774  return PR_TRUE;
775  }
776  }
777 
778  // Not found, don't notify
779  return PR_FALSE;
780  }
781 
782  // If we get here, this means we should notify
783  return PR_TRUE;
784 }
785 
786 void
788 {
789  PRUint32* success = mStopNotifiyingStack.InsertElementAt(0, 0);
790  SB_ENSURE_TRUE_VOID(success);
791 }
792 
793 void
795 {
796  if (mStopNotifiyingStack.Length() == 0) {
797  NS_ERROR("BeginBatch/EndBatch out of balance");
798  return;
799  }
800 
801  mStopNotifiyingStack.RemoveElementAt(0);
802 }
803 
804 void
806 {
807  // If there is no batch, just ignore this
808  if (mStopNotifiyingStack.Length() == 0) {
809  return;
810  }
811 
812  mStopNotifiyingStack[0] |= aFlag;
813 }
814 
815 nsresult
816 sbListenerInfo::InitPropertyFilter(sbIPropertyArray* aPropertyFilter)
817 {
818  nsresult rv;
819 
820  if (aPropertyFilter) {
821  mHasPropertyFilter = PR_TRUE;
822 
823  PRUint32 length = 0;
824  rv = aPropertyFilter->GetLength(&length);
825  NS_ENSURE_SUCCESS(rv, rv);
826 
827  PRBool success = mPropertyFilter.Init(length);
828  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
829 
830  for (PRUint32 i = 0; i < length; i++) {
831  nsCOMPtr<sbIProperty> property;
832  rv = aPropertyFilter->GetPropertyAt(i, getter_AddRefs(property));
833  NS_ENSURE_SUCCESS(rv, rv);
834 
835  nsString id;
836  rv = property->GetId(id);
837  NS_ENSURE_SUCCESS(rv, rv);
838 
839  nsStringHashKey* added = mPropertyFilter.PutEntry(id);
840  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
841  }
842  }
843  else {
844  mHasPropertyFilter = PR_FALSE;
845  }
846 
847  return NS_OK;
848 }
849 
852 
853 sbWeakMediaListListenerWrapper::sbWeakMediaListListenerWrapper(nsIWeakReference* aWeakListener) :
854  mWrappedWeak(aWeakListener)
855 {
856  NS_ASSERTION(mWrappedWeak, "aWeakListener is null");
857  MOZ_COUNT_CTOR(sbWeakMediaListListenerWrapper);
858 }
859 
861 {
862  MOZ_COUNT_DTOR(sbWeakMediaListListenerWrapper);
863 }
864 
865 already_AddRefed<sbIMediaListListener>
866 sbWeakMediaListListenerWrapper::GetListener()
867 {
868  nsCOMPtr<sbIMediaListListener> strongListener = do_QueryReferent(mWrappedWeak);
869  if (!strongListener) {
870  LOG(("sbWeakMediaListListenerWrapper[0x%.8x] - Weak listener is gone",
871  this));
872  return nsnull;
873  }
874 
875  sbIMediaListListener* listenerPtr = strongListener;
876  NS_ADDREF(listenerPtr);
877  return listenerPtr;
878 }
879 
880 // Try to notify the listener if it still exists. If it no longer exists,
881 // return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA as a sentinel to report this
882 // situation (see SB_NOTIFY_LISTENERS_TAIL)
883 #define SB_TRY_NOTIFY(call) \
884  nsCOMPtr<sbIMediaListListener> listener = GetListener(); \
885  if (listener) { \
886  return listener->call; \
887  } \
888  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
889 
890 NS_IMETHODIMP
891 sbWeakMediaListListenerWrapper::OnItemAdded(sbIMediaList* aMediaList,
892  sbIMediaItem* aMediaItem,
893  PRUint32 aIndex,
894  PRBool* aNoMoreForBatch)
895 {
896  SB_TRY_NOTIFY(OnItemAdded(aMediaList, aMediaItem, aIndex, aNoMoreForBatch))
897 }
898 
899 NS_IMETHODIMP
900 sbWeakMediaListListenerWrapper::OnBeforeItemRemoved(sbIMediaList* aMediaList,
901  sbIMediaItem* aMediaItem,
902  PRUint32 aIndex,
903  PRBool* aNoMoreForBatch)
904 {
905  SB_TRY_NOTIFY(OnBeforeItemRemoved(aMediaList,
906  aMediaItem,
907  aIndex,
908  aNoMoreForBatch))
909 }
910 
911 NS_IMETHODIMP
912 sbWeakMediaListListenerWrapper::OnAfterItemRemoved(sbIMediaList* aMediaList,
913  sbIMediaItem* aMediaItem,
914  PRUint32 aIndex,
915  PRBool* aNoMoreForBatch)
916 {
917  SB_TRY_NOTIFY(OnAfterItemRemoved(aMediaList,
918  aMediaItem,
919  aIndex,
920  aNoMoreForBatch))
921 }
922 
923 NS_IMETHODIMP
924 sbWeakMediaListListenerWrapper::OnItemUpdated(sbIMediaList* aMediaList,
925  sbIMediaItem* aMediaItem,
926  sbIPropertyArray* aProperties,
927  PRBool* aNoMoreForBatch)
928 {
929  SB_TRY_NOTIFY(OnItemUpdated(aMediaList,
930  aMediaItem,
931  aProperties,
932  aNoMoreForBatch))
933 }
934 
935 NS_IMETHODIMP
936 sbWeakMediaListListenerWrapper::OnItemMoved(sbIMediaList* aMediaList,
937  PRUint32 aFromIndex,
938  PRUint32 aToIndex,
939  PRBool* aNoMoreForBatch)
940 {
941  SB_TRY_NOTIFY(OnItemMoved(aMediaList,
942  aFromIndex,
943  aToIndex,
944  aNoMoreForBatch))
945 }
946 
947 NS_IMETHODIMP
948 sbWeakMediaListListenerWrapper::OnBeforeListCleared(sbIMediaList* aMediaList,
949  PRBool aExcludeLists,
950  PRBool* aNoMoreForBatch)
951 {
952  SB_TRY_NOTIFY(OnBeforeListCleared(aMediaList, aExcludeLists, aNoMoreForBatch))
953 }
954 
955 NS_IMETHODIMP
956 sbWeakMediaListListenerWrapper::OnListCleared(sbIMediaList* aMediaList,
957  PRBool aExcludeLists,
958  PRBool* aNoMoreForBatch)
959 {
960  SB_TRY_NOTIFY(OnListCleared(aMediaList, aExcludeLists, aNoMoreForBatch))
961 }
962 
963 NS_IMETHODIMP
964 sbWeakMediaListListenerWrapper::OnBatchBegin(sbIMediaList* aMediaList)
965 {
966  SB_TRY_NOTIFY(OnBatchBegin(aMediaList))
967 }
968 
969 NS_IMETHODIMP
970 sbWeakMediaListListenerWrapper::OnBatchEnd(sbIMediaList* aMediaList)
971 {
972  SB_TRY_NOTIFY(OnBatchEnd(aMediaList))
973 }
#define LOG(args)
function stop(ch, cx, status, data)
return NS_OK
void NotifyListenersAfterItemRemoved(sbIMediaList *aList, sbIMediaItem *aItem, PRUint32 aIndex)
Notifies all listeners that an item has been removed from the list.
void NotifyListenersBeforeListCleared(sbIMediaList *aList, PRBool aExcludeLists)
Notifies all listeners before the list has been cleared.
menuItem id
Definition: FeedWriter.js:971
void NotifyListenersItemAdded(sbIMediaList *aList, sbIMediaItem *aItem, PRUint32 aIndex)
Notifies all listeners that an item has been added to the list.
A brief description of the contents of this interface.
nsresult RemoveListener(sbLocalDatabaseMediaListBase *aList, sbIMediaListListener *aListener)
void NotifyListenersItemUpdated(sbIMediaList *aList, sbIMediaItem *aItem, sbIPropertyArray *aProperties)
Notifies all listeners that an item has been updated.
nsresult AddListener(sbLocalDatabaseMediaListBase *aList, sbIMediaListListener *aListener, PRBool aOwnsWeak=PR_FALSE, PRUint32 aFlags=sbIMediaList::LISTENER_FLAGS_ALL, sbIPropertyArray *aPropertyFilter=nsnull)
Interface used to listen to changes to a media list.
#define SB_NOTIFY_LISTENERS_PROPERTIES(method, call, flag)
void GetDebugAddress(nsAString &mDebugAddress)
void NotifyListenersBeforeItemRemoved(sbIMediaList *aList, sbIMediaItem *aItem, PRUint32 aIndex)
Notifies all listeners that an item is about to be removed from the list.
void SetShouldStopNotifying(PRUint32 aFlag)
const sbCreateProxiedComponent do_ProxiedGetService(const nsCID &aCID, nsresult *error=0)
#define SB_NOTIFY_LISTENERS(method, call, flag)
PRBool ShouldNotify(PRUint32 aFlag, sbIPropertyArray *aProperties=nsnull)
void NotifyListenersListCleared(sbIMediaList *aList, PRBool aExcludeLists)
Notifies all listeners that the list has been cleared.
#define SB_ENSURE_TRUE_VOID(_arg)
void NotifyListenersBatchEnd(sbIMediaList *aList)
Notifies all listeners that multiple items have been changed.
void NotifyListenersBatchBegin(sbIMediaList *aList)
Notifies all listeners that multiple items are about to be changed.
const unsigned long LISTENER_FLAGS_ALL
Interface that defines a single item of media in the system.
#define SB_TRY_NOTIFY(call)
void NotifyListenersItemMoved(sbIMediaList *aList, PRUint32 aFromIndex, PRUint32 aToIndex)
Notifies all listeners that an item has been moved.
nsresult do_GetProxyForObjectWithManager(nsIProxyObjectManager *aProxyObjMgr, nsIEventTarget *aTarget, REFNSIID aIID, nsISupports *aObj, PRInt32 aProxyType, void **aProxyObject)
#define TRACE(args)
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
NS_IMPL_THREADSAFE_ISUPPORTS1(sbWeakMediaListListenerWrapper, sbIMediaListListener) sbWeakMediaListListenerWrapper
_getSelectedPageStyle s i
nsresult Init(nsIProxyObjectManager *aProxyObjMgr, sbIMediaListListener *aListener, PRUint32 aCurrentBatchDepth, PRUint32 aFlags, sbIPropertyArray *aPropertyFilter)