sbMediaItemControllerCleanup.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  *=BEGIN SONGBIRD GPL
4  *
5  * This file is part of the Songbird web player.
6  *
7  * Copyright(c) 2005-2010 POTI, Inc.
8  * http://www.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 
27 
28 #include <nsIAppStartupNotifier.h>
29 #include <nsICategoryManager.h>
30 #include <nsIComponentManager.h>
31 #include <nsIComponentRegistrar.h>
32 #include <nsIEventTarget.h>
33 #include <nsIFile.h>
34 #include <nsIIdleService.h>
35 #include <nsILocalFile.h>
36 #include <nsIObserverService.h>
37 #include <nsIPropertyBag2.h>
38 #include <nsISimpleEnumerator.h>
39 #include <nsISupportsPrimitives.h>
40 #include <nsIURI.h>
41 #include <nsIURL.h>
42 
43 #include <sbILibrary.h>
44 #include <sbILibraryManager.h>
45 #include <sbIMediaItemController.h>
46 
47 #include <nsAutoLock.h>
48 #include <nsCOMPtr.h>
49 #include <nsServiceManagerUtils.h>
50 #include <nsStringAPI.h>
51 #include <nsTextFormatter.h>
52 #include <nsThreadUtils.h>
53 
54 #include <sbDebugUtils.h>
55 #include <sbStandardProperties.h>
56 #include <sbStringUtils.h>
58 
59 #include <algorithm>
60 
66 // The property name on the library we use to store the last seen set of
67 // track types
68 #define K_LAST_SEEN_TYPES_PROPERTY \
69  "http://songbirdnest.com/data/1.0#libraryItemControllerLastSeenTypes"
70 #define K_LAST_SEEN_TYPES_SEPARATOR '\x7F'
71 // the property name was use to flag individual items as having been hidden
72 // due to their controller going away (so we can un-hide them again)
73 #define K_HIDDEN_FOR_CONTROLLER_PROPERTY \
74  "http://songbirdnest.com/data/1.0#libraryItemControllerTypeDisappeared"
75 
76 // the observer topic we fire when we're done
77 #define K_CLEANUP_COMPLETE_OBSERVER_TOPIC \
78  "songbird-media-item-controller-cleanup-complete"
79 #define K_CLEANUP_INTERRUPTED_OBSERVER_TOPIC \
80  "songbird-media-item-controller-cleanup-interrupted"
81 #define K_CLEANUP_IDLE_OBSERVER_TOPIC \
82  "songbird-media-item-controller-cleanup-idle"
83 #define K_QUIT_APP_OBSERVER_TOPIC \
84  "quit-application"
85 
86 const PRUint32 K_IDLE_TIMEOUT = 5; // seconds
87 
88 #define SB_THREADPOOLSERVICE_CONTRACTID \
89  "@songbirdnest.com/Songbird/ThreadPoolService;1"
90 
91 
94  nsIRunnable,
96 
98  : mAvailableTypesInitialized(false),
99  mIdleServiceRegistered(false),
100  mState(STATE_IDLE)
101 {
103  TRACE_FUNCTION("");
104  NS_PRECONDITION(NS_IsMainThread(),
105  "sbMediaItemControllerCleanup::sbMediaItemControllerCleanup"
106  " should be called on the main thread!");
107 }
108 
110 {
111  TRACE_FUNCTION("");
112  NS_PRECONDITION(mState == STATE_IDLE,
113  "sbMediaItemControllerCleanup being destroyed while not idle");
114  // since, at this point, the thread manager has already shut down, we can't
115  // check that this was called on the main thread :(
116 }
117 
119 /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
120 NS_IMETHODIMP
121 sbMediaItemControllerCleanup::Observe(nsISupports *aSubject,
122  const char *aTopic,
123  const PRUnichar *aData)
124 {
125  TRACE_FUNCTION("topic=\"%s\"", aTopic);
126  NS_PRECONDITION(NS_IsMainThread(),
127  "sbMediaItemControllerCleanup::Observe"
128  " should be called on the main thread!");
129  nsresult rv;
130 
131  if (!strcmp(aTopic, "idle")) {
132  // idle observer: we are now idle
133  nsAutoLock lock(mLock);
134  if (mState == STATE_QUEUED) {
135  TRACE("preparing to run: STATE->RUNNING");
137  rv = mBackgroundEventTarget->Dispatch(static_cast<nsIRunnable*>(this),
138  NS_DISPATCH_NORMAL);
139  NS_ENSURE_SUCCESS(rv, rv);
140  }
141  else if (mState == STATE_STOPPING) {
142  // we can un-pause
143  if (mListener) {
144  mListener->Resume();
145  }
146  TRACE("Resuming: STATE->RUNNING");
148  }
149  else {
150  // we no longer need to hold on to the lock for anything; let it go now
151  // because do_ProxiedGetService may cause a nested event loop
152  lock.unlock();
153  TRACE("nothing to do, ignoring idle notification");
154  nsCOMPtr<nsIObserverService> obs =
155  do_ProxiedGetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
156  NS_ENSURE_SUCCESS(rv, rv);
157 
158  nsString notificationData;
159  #if PR_LOGGING
160  notificationData.Adopt(nsTextFormatter::smprintf((PRUnichar*)NS_LL("%llx"),
161  PR_Now()));
162  TRACE("Notifying observers (%s) [%s]",
164  NS_ConvertUTF16toUTF8(notificationData).get());
165  #endif /* PR_LOGGING */
166  rv = obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIObserver*, this),
168  notificationData.get());
169  NS_ENSURE_SUCCESS(rv, rv);
170  }
171  }
172  else if (!strcmp(aTopic, "back")) {
173  // idle observer: we are busy again
174  nsAutoLock lock(mLock);
175  if (mState == STATE_RUNNING) {
176  TRACE("Stopping due to activity: STATE->STOPPING");
178  }
179  if (mListener) {
180  // ask the processor to stop
181  mListener->Stop();
182  }
183  }
184  else if (!strcmp(aTopic, APPSTARTUP_TOPIC)) {
185  nsCOMPtr<nsIObserverService> obs =
186  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
187  NS_ENSURE_SUCCESS(rv, rv);
188  rv = obs->AddObserver(static_cast<nsIObserver*>(this),
190  false);
191  NS_ENSURE_SUCCESS(rv, rv);
192  rv = obs->AddObserver(static_cast<nsIObserver*>(this),
194  false);
195  NS_ENSURE_SUCCESS(rv, rv);
197  NS_ENSURE_SUCCESS(rv, rv);
198  mLock = nsAutoLock::NewLock(__FUNCTION__);
199  NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
200  }
201  else if (!strcmp(aTopic, SB_LIBRARY_MANAGER_READY_TOPIC)) {
202  nsCOMPtr<sbILibraryManager> libManager =
203  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
204  NS_ENSURE_SUCCESS(rv, rv);
205  rv = libManager->AddListener(static_cast<sbILibraryManagerListener*>(this));
206  NS_ENSURE_SUCCESS(rv, rv);
207 
208  nsCOMPtr<nsIObserverService> obs =
209  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
210  NS_ENSURE_SUCCESS(rv, rv);
211  rv = obs->RemoveObserver(static_cast<nsIObserver*>(this),
213  NS_ENSURE_SUCCESS(rv, rv);
214 
215  // since the library manager doesn't tell us about libraries loaded before
216  // this point, we have to tell ourself about them :|
217  nsCOMPtr<nsISimpleEnumerator> libs;
218  rv = libManager->GetLibraries(getter_AddRefs(libs));
219  NS_ENSURE_SUCCESS(rv, rv);
220  PRBool hasLib;
221  while (NS_SUCCEEDED(libs->HasMoreElements(&hasLib)) && hasLib) {
222  nsCOMPtr<nsISupports> supports;
223  rv = libs->GetNext(getter_AddRefs(supports));
224  NS_ENSURE_SUCCESS(rv, rv);
225  nsCOMPtr<sbILibrary> lib = do_QueryInterface(supports, &rv);
226  NS_ENSURE_SUCCESS(rv, rv);
227  rv = OnLibraryRegistered(lib);
228  NS_ENSURE_SUCCESS(rv, rv);
229  }
230  }
231  else if (!strcmp(aTopic, K_QUIT_APP_OBSERVER_TOPIC)) {
232  nsCOMPtr<nsIIdleService> idleService =
233  do_GetService("@mozilla.org/widget/idleservice;1", &rv);
234  NS_ENSURE_SUCCESS(rv, rv);
235  (void)idleService->RemoveIdleObserver(static_cast<nsIObserver*>(this),
237  // we don't care about failures to remove because we may have done it already
238  mIdleServiceRegistered = false;
239 
240  nsCOMPtr<sbILibraryManager> libManager =
241  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
242  NS_ENSURE_SUCCESS(rv, rv);
243  rv = libManager->RemoveListener(static_cast<sbILibraryManagerListener*>(this));
244  NS_ENSURE_SUCCESS(rv, rv);
245 
246  nsCOMPtr<nsIObserverService> obs =
247  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
248  NS_ENSURE_SUCCESS(rv, rv);
249  rv = obs->RemoveObserver(static_cast<nsIObserver*>(this),
251  NS_ENSURE_SUCCESS(rv, rv);
252  }
253  else {
254  NS_WARNING("unexpected topic");
255  }
256  return NS_OK;
257 }
258 
260 /* void run (); */
261 NS_IMETHODIMP
262 sbMediaItemControllerCleanup::Run()
263 {
264  TRACE_FUNCTION("");
265  NS_PRECONDITION(!NS_IsMainThread(),
266  "sbMediaItemControllerCleanup::Run"
267  " should not be called on the main thread!");
268 
269  nsresult rv = NS_OK;
270  bool stateOK = true;
271  { /* scope */
272  nsAutoLock lock(mLock);
273  NS_PRECONDITION(mState == STATE_RUNNING || mState == STATE_STOPPING,
274  "the act of getting things queued should have put"
275  " us into the running state");
276  if (mState == STATE_STOPPING) {
277  // the idle service noticed the user came back or something, before
278  // we got a chance to run anything; skip the run completely
279  stateOK = false;
280  }
281  }
282 
283  if (stateOK) {
284  rv = ProcessLibraries();
285  }
286 
287  PRBool complete = true;
288  { /* scope */
289  nsAutoLock lock(mLock);
290  mListener = nsnull;
291  NS_POSTCONDITION(mState == STATE_RUNNING || mState == STATE_STOPPING,
292  "should not have exit running!?");
293  if (mLibraries.empty()) {
294  TRACE("Run complete: STATE->IDLE");
295  mState = STATE_IDLE;
296  complete = true;
297  }
298  else {
299  // we can get here if the machine gets out of the idle state before the
300  // jobs are all done
301  TRACE("Run incomplete: STATE->QUEUED");
303  complete = false;
304  }
305  }
306  NS_ENSURE_SUCCESS(rv, rv);
307 
308  // Getting here means one of two things:
309  // 1) all libraries have been processed
310  // 2) we were interrupted when the idle service reports the user is back
311 
312  NS_ASSERTION(mState == STATE_IDLE || mState == STATE_QUEUED,
313  "Unexpected state while stopping process!");
314  nsCString topic;
315  if (complete) {
316  topic.AssignLiteral(K_CLEANUP_COMPLETE_OBSERVER_TOPIC);
317  }
318  else {
319  topic.AssignLiteral(K_CLEANUP_INTERRUPTED_OBSERVER_TOPIC);
320  }
321  nsString notificationData;
322  #if PR_LOGGING
323  notificationData.Adopt(nsTextFormatter::smprintf((PRUnichar*)NS_LL("%llx"),
324  PR_Now()));
325  TRACE("Notifying observers (%s) [%s]",
326  topic.get(),
327  NS_ConvertUTF16toUTF8(notificationData).get());
328  #endif /* PR_LOGGING */
329  nsCOMPtr<nsIObserverService> obs =
330  do_ProxiedGetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
331  NS_ENSURE_SUCCESS(rv, rv);
332 
333  rv = obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIObserver*, this),
334  topic.get(),
335  notificationData.get());
336  NS_ENSURE_SUCCESS(rv, rv);
337 
338  return NS_OK;
339 }
340 
341 
342 // helper class for onLibraryRegistered logging
343 #if PR_LOGGING
344 namespace {
345  template <typename T>
346  struct STLIteratorValueIs {
347  STLIteratorValueIs(typename T::second_type v) :m(v) {}
348  bool operator() (const T& t) {
349  return t.second == m;
350  }
351  typename T::second_type m;
352  };
353 }
354 #endif
355 
357 /* void onLibraryRegistered (in sbILibrary aLibrary); */
358 NS_IMETHODIMP
359 sbMediaItemControllerCleanup::OnLibraryRegistered(sbILibrary *aLibrary)
360 {
361  TRACE_FUNCTION("");
362  NS_PRECONDITION(NS_IsMainThread(),
363  "sbMediaItemControllerCleanup::OnLibraryRegistered"
364  " should be called on the main thread!");
365  NS_ENSURE_ARG_POINTER(aLibrary);
366 
367  #if PR_LOGGING
368  do {
369  nsresult rv;
370  nsString contentSrc;
371  rv = aLibrary->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
372  contentSrc);
373  NS_ENSURE_SUCCESS(rv, rv);
374  contentSrc.Cut(0, contentSrc.RFindChar(PRUnichar('/')) + 1);
375  TRACE("library: %s", NS_ConvertUTF16toUTF8(contentSrc).BeginReading());
376  } while(false);
377  #endif /* PR_LOGGING */
378 
379  nsresult rv;
380 
382  rv = EnsureAvailableTypes();
383  NS_ENSURE_SUCCESS(rv, rv);
384 
385  nsString cachedTypesUnicode;
386  rv = aLibrary->GetProperty(NS_LITERAL_STRING(K_LAST_SEEN_TYPES_PROPERTY),
387  cachedTypesUnicode);
388  NS_ENSURE_SUCCESS(rv, rv);
389 
390  nsCString cachedTypes;
391  LossyCopyUTF16toASCII(cachedTypesUnicode, cachedTypes);
392  cachedTypes.Append(K_LAST_SEEN_TYPES_SEPARATOR);
393  TRACE("cached types: %s", cachedTypes.BeginReading());
394 
395  PRInt32 start = 0, end;
396 
398 
399  while ((end = cachedTypes.FindChar(K_LAST_SEEN_TYPES_SEPARATOR, start)) >= 0) {
400  std::string type(cachedTypes.BeginReading() + start, end - start);
401  start = end + 1;
402  if (type.empty()) {
403  continue;
404  }
405  TRACE("found cached type %s", type.c_str());
406  if (mAvailableTypes.find(type) != mAvailableTypes.end()) {
407  // this type used to exist, and still does
408  types.erase(type);
409  }
410  else {
411  // this type used to exist, but doe not any more
412  types.insert(types_t::value_type(type, false));
413  }
414  }
415 
416  TRACE("cache update check: removed types? (%i) added types? (%i)",
417  std::count_if(types.begin(),
418  types.end(),
419  STLIteratorValueIs<types_t::value_type>(false)),
420  std::count_if(types.begin(),
421  types.end(),
422  STLIteratorValueIs<types_t::value_type>(true)));
423  #if PR_LOGGING
424  {
425  types_t::const_iterator it;
426  for (it = types.begin(); it != types.end(); ++it) {
427  if (!it->second) {
428  TRACE("type removed: %s", it->first.c_str());
429  }
430  }
431  for (it = types.begin(); it != types.end(); ++it) {
432  if (it->second) {
433  TRACE("type added: %s", it->first.c_str());
434  }
435  }
436  }
437  #endif /* PR_LOGGING */
438 
439  if (!types.empty()) {
440  // getting here means we have a type that was previously available but no
441  // longer is, or types that didn't exist but now do.
442 
444  // we don't need to worry about purely removing items yet; we do it after
445  // we actually hide the items there. so put the union of (available types)
446  // and (types removed) in the cache.
447  nsCString newCache;
448  types_t::const_iterator it;
449  for (it = mAvailableTypes.begin(); it != mAvailableTypes.end(); ++it) {
450  newCache.Append(it->first.c_str(), it->first.length());
451  newCache.Append(K_LAST_SEEN_TYPES_SEPARATOR);
452  }
453  for (it = types.begin(); it != types.end(); ++it) {
454  if (it->second) {
455  // this type was not a removed type, it's still available
456  continue;
457  }
458  newCache.Append(it->first.c_str(), it->first.length());
459  newCache.Append(K_LAST_SEEN_TYPES_SEPARATOR);
460  }
461  newCache.SetLength(newCache.Length() - 1); // trailing separator
462  TRACE("updating cache to %s", newCache.BeginReading());
463  rv = aLibrary->SetProperty(NS_LITERAL_STRING(K_LAST_SEEN_TYPES_PROPERTY),
464  NS_ConvertASCIItoUTF16(newCache));
465  NS_ENSURE_SUCCESS(rv, rv);
466 
468  // We need to either hide items (when types are removed), or possibly
469  // un-hide items (when types are added).
470 
471  nsCOMPtr<nsIIdleService> idleService =
472  do_GetService("@mozilla.org/widget/idleservice;1", &rv);
473  NS_ENSURE_SUCCESS(rv, rv);
474 
475  if (!mIdleServiceRegistered) {
476  rv = idleService->AddIdleObserver(static_cast<nsIObserver*>(this),
478  NS_ENSURE_SUCCESS(rv, rv);
479  mIdleServiceRegistered = true;
480  }
481  { /* scope */
482  nsAutoLock lock(mLock);
483  mLibraries[aLibrary] = types;
484  if (mState == STATE_IDLE) {
485  TRACE("Libraries added: STATE->QUEUED");
487  }
488  }
489 
490  #if PR_LOGGING
491  {
492  nsString fileName((const PRUnichar*)NS_LL("<ERROR>"));
493  nsCOMPtr<nsIPropertyBag2> creationParams;
494  rv = aLibrary->GetCreationParameters(getter_AddRefs(creationParams));
495  NS_ENSURE_SUCCESS(rv, rv);
496  NS_NAMED_LITERAL_STRING(fileKey, "databaseFile");
497  nsCOMPtr<nsILocalFile> databaseFile;
498  rv = creationParams->GetPropertyAsInterface(fileKey,
499  NS_GET_IID(nsILocalFile),
500  getter_AddRefs(databaseFile));
501  if (NS_SUCCEEDED(rv)) {
502  rv = databaseFile->GetLeafName(fileName);
503  }
504  TRACE("queued library %s for processing",
505  NS_ConvertUTF16toUTF8(fileName).get());
506  }
507  #endif /* PR_LOGGING */
508 
509  PRUint32 idleTime;
510  rv = idleService->GetIdleTime(&idleTime);
511  NS_ENSURE_SUCCESS(rv, rv);
512  if (idleTime > K_IDLE_TIMEOUT * PR_MSEC_PER_SEC) {
513  // we've been idle for long enough already
514  rv = Observe(idleService, "idle", nsnull);
515  NS_ENSURE_SUCCESS(rv, rv);
516  }
517  }
518 
519  return NS_OK;
520 }
521 
522 /* void onLibraryUnregistered (in sbILibrary aLibrary); */
523 NS_IMETHODIMP
524 sbMediaItemControllerCleanup::OnLibraryUnregistered(sbILibrary *aLibrary)
525 {
526  TRACE_FUNCTION("");
527  NS_PRECONDITION(NS_IsMainThread(),
528  "sbMediaItemControllerCleanup::OnLibraryUnregistered"
529  " should be called on the main thread!");
530  NS_ENSURE_ARG_POINTER(aLibrary);
531 
532  nsAutoLock lock(mLock);
533 
534  if (mListener) {
535  nsCOMPtr<sbIMediaList> list = mListener->GetMediaList();
536  PRBool equals;
537  if (list && NS_SUCCEEDED(list->Equals(aLibrary, &equals)) && equals) {
538  mListener->Stop();
539  }
540  }
541 
542  libraries_t::iterator it = mLibraries.find(aLibrary);
543  if (it != mLibraries.end()) {
544  mLibraries.erase(it);
545  }
546 
547  return NS_OK;
548 }
549 
551 nsresult
553 {
554  TRACE_FUNCTION("");
555  NS_PRECONDITION(NS_IsMainThread(),
556  "sbMediaItemControllerCleanup::EnsureAvailableTypes"
557  " should be called on the main thread!");
559  return NS_OK;
560  }
561 
562  nsresult rv;
563 
564  nsCOMPtr<nsIComponentRegistrar> registrar;
565  rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
566  NS_ENSURE_SUCCESS(rv, rv);
567 
568  nsCOMPtr<nsISimpleEnumerator> enumerator;
569  rv = registrar->EnumerateContractIDs(getter_AddRefs(enumerator));
570  NS_ENSURE_SUCCESS(rv, rv);
571 
572  PRBool hasMore;
573  NS_NAMED_LITERAL_CSTRING(CONTRACT_PREFIX,
575  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
576  nsCOMPtr<nsISupports> supports;
577  rv = enumerator->GetNext(getter_AddRefs(supports));
578  NS_ENSURE_SUCCESS(rv, rv);
579  nsCOMPtr<nsISupportsCString> string = do_QueryInterface(supports, &rv);
580  NS_ENSURE_SUCCESS(rv, rv);
581  nsCString value;
582  rv = string->GetData(value);
583  NS_ENSURE_SUCCESS(rv, rv);
584  if (StringBeginsWith(value, CONTRACT_PREFIX))
585  {
586  std::string type(value.BeginReading() + CONTRACT_PREFIX.Length(),
587  value.Length() - CONTRACT_PREFIX.Length());
588  mAvailableTypes.insert(types_t::value_type(type, true));
589  }
590  }
591 
593  return NS_OK;
594 }
595 
596 nsresult
598 {
599  TRACE_FUNCTION("");
600  NS_PRECONDITION(!NS_IsMainThread(),
601  "sbMediaItemControllerCleanup::ProcessLibraries"
602  " was expected to run on a background thread!");
603 
604  nsresult rv;
605 
606  while (true) {
607  // grab the next library to look at.
608  // note that mLibraries may be modified from elsewhere during our run, so
609  // we don't hold on to the iterator beyond the lock.
610  nsCOMPtr<sbILibrary> lib;
611  types_t types;
612  { /* scope */
613  nsAutoLock lock(mLock);
614  if (mLibraries.empty()) {
615  break;
616  }
617  libraries_t::const_iterator it = mLibraries.begin();
618  lib = it->first;
619  types = it->second;
620  }
621  NS_ASSERTION(!types.empty(),
622  "Attempted to process a library with neither added nor removed types!");
623  #if PR_LOGGING
624  {
625  nsString fileName((const PRUnichar*)NS_LL("<ERROR>"));
626  nsCOMPtr<nsIPropertyBag2> creationParams;
627  rv = lib->GetCreationParameters(getter_AddRefs(creationParams));
628  NS_ENSURE_SUCCESS(rv, rv);
629  NS_NAMED_LITERAL_STRING(fileKey, "databaseFile");
630  nsCOMPtr<nsILocalFile> databaseFile;
631  rv = creationParams->GetPropertyAsInterface(fileKey,
632  NS_GET_IID(nsILocalFile),
633  getter_AddRefs(databaseFile));
634  if (NS_SUCCEEDED(rv)) {
635  rv = databaseFile->GetLeafName(fileName);
636  }
637  TRACE("got new library %p to process", NS_ConvertUTF16toUTF8(fileName).get());
638  }
639  #endif /* PR_LOGGING */
640 
641  types_t::const_iterator it;
642 
643  for (it = types.begin(); it != types.end(); ++it) {
644  TRACE("%s type %s",
645  (it->second ? "adding" : "removing"),
646  it->first.c_str());
647  NS_ConvertASCIItoUTF16 type(it->first.c_str(), it->first.length());
648 
649  nsCOMPtr<sbIMutablePropertyArray> mutableProp =
650  do_CreateInstance("@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1", &rv);
651  NS_ENSURE_SUCCESS(rv, rv);
652  rv = mutableProp->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
653  it->second ? NS_LITERAL_STRING("0")
654  : NS_LITERAL_STRING("1"));
655  NS_ENSURE_SUCCESS(rv, rv);
656  rv = mutableProp->AppendProperty(NS_LITERAL_STRING(K_HIDDEN_FOR_CONTROLLER_PROPERTY),
657  it->second ? SBVoidString()
658  : NS_LITERAL_STRING("1"));
659  NS_ENSURE_SUCCESS(rv, rv);
660  nsCOMPtr<sbIPropertyArray> propsToSet = do_QueryInterface(mutableProp, &rv);
661  NS_ENSURE_SUCCESS(rv, rv);
662 
663  mutableProp =
664  do_CreateInstance("@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1", &rv);
665  NS_ENSURE_SUCCESS(rv, rv);
666  rv = mutableProp->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_TRACKTYPE),
667  type);
668  NS_ENSURE_SUCCESS(rv, rv);
669  if (it->second) {
670  // turning things back on; only do it if we had hidden it in the first place
671  rv = mutableProp->AppendProperty(NS_LITERAL_STRING(K_HIDDEN_FOR_CONTROLLER_PROPERTY),
672  NS_LITERAL_STRING("1"));
673  NS_ENSURE_SUCCESS(rv, rv);
674  }
675  nsCOMPtr<sbIPropertyArray> propsToFilter =
676  do_QueryInterface(mutableProp, &rv);
677  NS_ENSURE_SUCCESS(rv, rv);
678 
679  nsCOMPtr<sbIMediaList> list = do_QueryInterface(lib, &rv);
680  NS_ENSURE_SUCCESS(rv, rv);
681  { /* scope */
682  nsAutoLock lock(mLock);
683  mListener = new sbEnumerationHelper(list, propsToFilter, propsToSet);
684  NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
685  }
686 
687  rv = lib->RunInBatchMode(mListener, nsnull);
688  NS_ENSURE_SUCCESS(rv, rv);
689  TRACE("batch complete");
690 
691  { /* scope */
692  nsAutoLock lock(mLock);
693  if (!mListener->Completed() && mLibraries.find(lib) != mLibraries.end()) {
694  // abort the run; since we didn't actually finish this type,
695  // do not remove it from the list, nor do we remove the library from
696  // the queue of things to process
697  TRACE("run aborted");
698  return NS_OK;
699  }
700  }
701 
702  if (!it->second) {
703  // the type was removed; get rid of it from the cache
704  nsString cachedTypes;
705  rv = lib->GetProperty(NS_LITERAL_STRING(K_LAST_SEEN_TYPES_PROPERTY),
706  cachedTypes);
707  NS_ENSURE_SUCCESS(rv, rv);
708  cachedTypes.Insert(K_LAST_SEEN_TYPES_SEPARATOR, 0);
709  cachedTypes.Append(K_LAST_SEEN_TYPES_SEPARATOR);
710 
711  type.Insert(K_LAST_SEEN_TYPES_SEPARATOR, 0);
712  type.Append(K_LAST_SEEN_TYPES_SEPARATOR);
713 
714  PRInt32 pos = cachedTypes.Find(type);
715  if (pos >= 0) {
716  cachedTypes.Cut(pos, type.Length() - 1);
717  char SEP[2] = {K_LAST_SEEN_TYPES_SEPARATOR, 0};
718  cachedTypes.Trim(SEP);
719  TRACE("setting types to \"%s\"",
720  NS_LossyConvertUTF16toASCII(cachedTypes).BeginReading());
721  rv = lib->SetProperty(NS_LITERAL_STRING(K_LAST_SEEN_TYPES_PROPERTY),
722  cachedTypes);
723  NS_ENSURE_SUCCESS(rv, rv);
724  }
725  else {
726  TRACE("Failed to find \"%s\" in \"%s\"",
727  NS_LossyConvertUTF16toASCII(type).BeginReading(),
728  NS_LossyConvertUTF16toASCII(cachedTypes).BeginReading());
729  }
730  }
731  }
732 
733  // all types in this library are processed
734  { /* scope */
735  nsAutoLock lock(mLock);
736  mLibraries.erase(lib);
737  }
738  }
739 
740  #if PR_LOGGING
741  {
742  nsAutoLock lock(mLock);
743  TRACE("completed? %s",
744  mLibraries.empty() ? "yes" : "no");
745  }
746  #endif /* PR_LOGGING */
747 
748  return NS_OK;
749 }
750 
752 /* static */
753 NS_METHOD
754 sbMediaItemControllerCleanup::RegisterSelf(nsIComponentManager* aCompMgr,
755  nsIFile* aPath,
756  const char* aLoaderStr,
757  const char* aType,
758  const nsModuleComponentInfo *aInfo)
759 {
761  TRACE_FUNCTION("");
762 
763  nsresult rv = NS_ERROR_UNEXPECTED;
764  nsCOMPtr<nsICategoryManager> categoryManager =
765  do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
766  NS_ENSURE_SUCCESS(rv, rv);
767 
768  rv = categoryManager->AddCategoryEntry(APPSTARTUP_TOPIC,
770  "service,"
772  PR_TRUE, PR_TRUE, nsnull);
773  NS_ENSURE_SUCCESS(rv, rv);
774 
775  return NS_OK;
776 }
777 
778 
780 
784 
785 sbMediaItemControllerCleanup::sbEnumerationHelper::sbEnumerationHelper(
786  sbIMediaList *aMediaList,
787  sbIPropertyArray *aPropertiesToFilter,
788  sbIPropertyArray *aPropertiesToSet)
789  : mStop(false),
790  mCompleted(false),
791  mCount(0)
792 {
793  TRACE_FUNCTION("");
794  mList = aMediaList;
795  mPropsToFilter = aPropertiesToFilter;
796  mPropsToSet = aPropertiesToSet;
797 }
798 
799 /* unsigned short onEnumerationBegin (in sbIMediaList aMediaList); */
800 NS_IMETHODIMP
801 sbMediaItemControllerCleanup::sbEnumerationHelper::OnEnumerationBegin(
802  sbIMediaList *aMediaList,
803  PRUint16 *_retval NS_OUTPARAM)
804 {
805  TRACE_FUNCTION("");
806  NS_PRECONDITION(!NS_IsMainThread(),
807  "sbEnumerationHelper::OnEnumerationBegin"
808  " should be called on the background thread!");
809  NS_ENSURE_ARG_POINTER(aMediaList);
810  NS_ENSURE_ARG_POINTER(_retval);
812  return NS_OK;
813 }
814 
815 /* unsigned short onEnumeratedItem (in sbIMediaList aMediaList, in sbIMediaItem aMediaItem); */
816 NS_IMETHODIMP
817 sbMediaItemControllerCleanup::sbEnumerationHelper::OnEnumeratedItem(
818  sbIMediaList *aMediaList,
819  sbIMediaItem *aMediaItem,
820  PRUint16 *_retval NS_OUTPARAM)
821 {
822  TRACE_FUNCTION("count: %i", ++mCount);
823  NS_PRECONDITION(!NS_IsMainThread(),
824  "sbEnumerationHelper::OnEnumeratedItem"
825  " should be called on the background thread!");
826  NS_ENSURE_ARG_POINTER(aMediaList);
827  NS_ENSURE_ARG_POINTER(aMediaItem);
828  NS_ENSURE_ARG_POINTER(_retval);
829 
830  // don't process any items beyond this one if we were asked to stop
831  if (mStop)
833  else
835 
836  nsresult rv;
837  rv = aMediaItem->SetProperties(mPropsToSet);
838  NS_ENSURE_SUCCESS(rv, rv);
839 
840  return NS_OK;
841 }
842 
843 /* void onEnumerationEnd (in sbIMediaList aMediaList, in nsresult aStatusCode); */
844 NS_IMETHODIMP
845 sbMediaItemControllerCleanup::sbEnumerationHelper::OnEnumerationEnd(
846  sbIMediaList *aMediaList,
847  nsresult aStatusCode)
848 {
849  TRACE_FUNCTION("total: %i", mCount);
850  NS_PRECONDITION(!NS_IsMainThread(),
851  "sbEnumerationHelper::OnEnumerationEnd"
852  " should be called on the background thread!");
853  mCompleted = NS_SUCCEEDED(aStatusCode);
854  #if PR_LOGGING
855  { /* scope */
856  nsresult rv;
857  nsString filter, set;
858  rv = mPropsToFilter->ToString(filter);
859  if (NS_FAILED(rv)) filter.AssignLiteral("<error>");
860  rv = mPropsToSet->ToString(filter);
861  if (NS_FAILED(rv)) filter.AssignLiteral("<error>");
862  TRACE("filter: %s set: %s complete? %s",
863  NS_ConvertUTF16toUTF8(filter).get(),
864  NS_ConvertUTF16toUTF8(set).get(),
865  mCompleted ? "yes" : "no");
866  }
867  #endif
868  return NS_OK;
869 }
870 
871 /* void runBatched ([optional] in nsISupports aUserData); */
872 NS_IMETHODIMP
873 sbMediaItemControllerCleanup::sbEnumerationHelper::RunBatched(
874  nsISupports *aUserData)
875 {
876  TRACE_FUNCTION("stop? %s", mStop ? "yes" : "no");
877  if (!mStop) {
878  nsresult rv;
879  rv = mList->EnumerateItemsByProperties(mPropsToFilter,
880  this,
882  NS_ENSURE_SUCCESS(rv, rv);
883  }
884 
885  return NS_OK;
886 }
887 
888 void
890 {
891  TRACE_FUNCTION("");
892  mStop = true;
893 }
894 
895 void
897 {
898  TRACE_FUNCTION("");
899  mStop = false;
900 }
901 
902 bool
904 {
905  TRACE_FUNCTION("");
906  return mCompleted;
907 }
908 
909 already_AddRefed<sbIMediaList>
911 {
912  TRACE_FUNCTION("");
913  return nsCOMPtr<sbIMediaList>(mList).forget();
914 }
function start(ch)
Manages the lifecycle of libraries in the system.
#define K_CLEANUP_INTERRUPTED_OBSERVER_TOPIC
#define K_QUIT_APP_OBSERVER_TOPIC
const SB_MEDIAITEMCONTROLLER_PARTIALCONTRACTID
#define K_LAST_SEEN_TYPES_SEPARATOR
#define SB_PRLOG_SETUP(x)
Definition: sbDebugUtils.h:115
return NS_OK
_updateCookies aPath
var registrar
const SB_LIBRARY_MANAGER_READY_TOPIC
#define SB_PROPERTY_TRACKTYPE
_dialogDatepicker pos
Interface used to enumerate the items in a media list.
static NS_METHOD RegisterSelf(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *aLoaderStr, const char *aType, const nsModuleComponentInfo *aInfo)
enum sbMediaItemControllerCleanup::@5 mState
#define SB_PROPERTY_HIDDEN
std::map< std::string, bool > types_t
const char * types
A brief description of the contents of this interface.
nsCOMPtr< nsIEventTarget > mBackgroundEventTarget
const STATE_IDLE
#define SB_THREADPOOLSERVICE_CONTRACTID
#define SONGBIRD_MEDIAITEMCONTROLLERCLEANUP_CONTRACTID
var t
const sbCreateProxiedComponent do_ProxiedGetService(const nsCID &aCID, nsresult *error=0)
const unsigned short ENUMERATIONTYPE_SNAPSHOT
This flag means that the list being enumerated is a copy that may become out of date.
function TRACE(s)
this _document false
Definition: FeedWriter.js:1085
#define SONGBIRD_MEDIAITEMCONTROLLERCLEANUP_CLASSNAME
#define TRACE_FUNCTION(...)
Definition: sbDebugUtils.h:118
Media library abstraction.
Definition: sbILibrary.idl:82
A component used to automatically hide items that require media item controllers which are no longer ...
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
observe topic
Definition: FeedWriter.js:1326
#define K_HIDDEN_FOR_CONTROLLER_PROPERTY
Interface that defines a single item of media in the system.
NS_IMPL_THREADSAFE_ISUPPORTS2(sbMediaItemControllerCleanup::sbEnumerationHelper, sbIMediaListEnumerationListener, sbIMediaListBatchCallback) sbMediaItemControllerCleanup
#define K_CLEANUP_IDLE_OBSERVER_TOPIC
const PRUint32 K_IDLE_TIMEOUT
nsRefPtr< sbEnumerationHelper > mListener
#define SB_PROPERTY_CONTENTURL
NS_IMPL_THREADSAFE_ISUPPORTS3(sbMediaItemControllerCleanup, nsIObserver, nsIRunnable, sbILibraryManagerListener) sbMediaItemControllerCleanup
#define K_CLEANUP_COMPLETE_OBSERVER_TOPIC
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
Array filter(tab.attributes, function(aAttr){return(_this.xulAttributes.indexOf(aAttr.name) >-1);}).forEach(tab.removeAttribute
#define K_LAST_SEEN_TYPES_PROPERTY
_updateTextAndScrollDataForFrame aData