sbLocalDatabaseLibrary.cpp
Go to the documentation of this file.
1 /* vim: set sw=2 :miv */
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 
26 #include "sbLocalDatabaseLibrary.h"
27 
28 #include <nsArrayUtils.h>
29 #include <nsIArray.h>
30 #include <nsIClassInfo.h>
31 #include <nsIClassInfoImpl.h>
32 #include <nsIFile.h>
33 #include <nsIFileURL.h>
34 #include <nsIMutableArray.h>
35 #include <nsIObserverService.h>
36 #include <nsIPrefBranch.h>
37 #include <nsIPrefService.h>
38 #include <nsIProgrammingLanguage.h>
39 #include <nsIPropertyBag2.h>
40 #include <nsIProxyObjectManager.h>
41 #include <nsISimpleEnumerator.h>
42 #include <nsIStringEnumerator.h>
43 #include <nsISupportsPrimitives.h>
44 #include <nsIThread.h>
45 #include <nsIURI.h>
46 #include <nsIUUIDGenerator.h>
47 #include <nsIWritablePropertyBag2.h>
48 #include <sbIDatabaseQuery.h>
49 #include <sbIDatabaseResult.h>
50 #include <sbIDevice.h>
51 #include <sbIDeviceManager.h>
52 #include <sbILibraryFactory.h>
53 #include <sbILibraryManager.h>
54 #include <sbILibraryResource.h>
55 #include <sbILocalDatabaseLibraryCopyListener.h>
56 #include <sbILocalDatabaseGUIDArray.h>
57 #include <sbILocalDatabaseMigrationHelper.h>
58 #include <sbILocalDatabasePropertyCache.h>
59 #include <sbILocalDatabaseSimpleMediaList.h>
60 #include <sbIMediacoreTypeSniffer.h>
61 #include <sbIMediaItem.h>
62 #include <sbIMediaList.h>
63 #include <sbIMediaListView.h>
64 #include <sbISQLBuilder.h>
65 #include <nsITimer.h>
66 
67 #include <DatabaseQuery.h>
68 #include <sbIDatabaseEngine.h>
69 #include <nsAutoLock.h>
70 #include <nsAutoPtr.h>
71 #include <nsCOMPtr.h>
72 #include <nsComponentManagerUtils.h>
73 #include <nsTHashtable.h>
74 #include <nsHashKeys.h>
75 #include <nsID.h>
76 #include <nsIInputStreamPump.h>
77 #include <nsMemory.h>
78 #include <nsNetUtil.h>
79 #include <nsServiceManagerUtils.h>
80 #include <nsThreadUtils.h>
81 #include <nsXPCOM.h>
82 #include <nsWeakReference.h>
83 #include <prinrval.h>
84 #include <prlog.h>
85 #include <prprf.h>
86 #include <prtime.h>
87 #include "sbLocalDatabaseCID.h"
96 #include <sbStandardProperties.h>
97 #include <sbSQLBuilderCID.h>
99 #include <sbVariantUtils.h>
100 #include <nsIVariant.h>
101 #include <nsUnicharUtils.h>
103 
104 #define NS_UUID_GENERATOR_CONTRACTID "@mozilla.org/uuid-generator;1"
105 
106 #define SB_MEDIAITEM_TYPEID 0
107 
108 #define DEFAULT_ANALYZE_COUNT_LIMIT 1000
109 #define ANALYZE_COUNT_PREF "songbird.library.localdatabase.analyzeCountLimit"
110 #define INVALID_COLLATION_INDEX_PREF_PREFIX "songbird.databaseengine."
111 #define INVALID_COLLATION_INDEX_PREF_SUFFIX ".invalidCollationIndex"
112 
113 #define DEFAULT_MEDIAITEM_CACHE_SIZE 2500
114 #define DEFAULT_MEDIALIST_CACHE_SIZE 25
115 
116 #define SB_MEDIALIST_FACTORY_DEFAULT_TYPE 1
117 #define SB_MEDIALIST_FACTORY_URI_PREFIX "medialist('"
118 #define SB_MEDIALIST_FACTORY_URI_SUFFIX "')"
119 
120 // The number of milliseconds that the library could possibly wait before
121 // checking to see if all the async batch create timers have finished on
122 // shutdown. This number isn't exact, see the documentation for
123 // NS_ProcessPendingEvents.
124 #define SHUTDOWN_ASYNC_GRANULARITY_MS 1000
125 
126 // How often to send progress notifications / check for completion
127 // when running a BatchCreateMediaItemsAsync request.
128 #define BATCHCREATE_NOTIFICATION_INTERVAL_MS 100
129 
130 // These macros need to stay in sync with the QueryInterface macro
131 #define SB_ILIBRESOURCE_CAST(_ptr) \
132  static_cast<sbILibraryResource*>(static_cast<sbIMediaItem*>(static_cast<sbLocalDatabaseMediaItem*>(_ptr)))
133 #define SB_IMEDIALIST_CAST(_ptr) \
134  static_cast<sbIMediaList*>(static_cast<sbLocalDatabaseMediaListBase*>(_ptr))
135 #define SB_IMEDIAITEM_CAST(_ptr) \
136  static_cast<sbIMediaItem*>(static_cast<sbLocalDatabaseMediaItem*>(_ptr))
137 
138 #define DEFAULT_SORT_PROPERTY SB_PROPERTY_CREATED
139 
140 #define DEFAULT_FETCH_SIZE 1000
141 
146 #ifdef PR_LOGGING
147 static PRLogModuleInfo* gLibraryLog = nsnull;
148 #endif /* PR_LOGGING */
149 
150 #define TRACE(args) PR_LOG(gLibraryLog, PR_LOG_DEBUG, args)
151 #define LOG(args) PR_LOG(gLibraryLog, PR_LOG_WARN, args)
152 
153 // Makes some of the logging a little easier to read
154 #define LOG_SUBMESSAGE_SPACE " - "
155 
156 // Some constants used by RemoveIfNotList
157 const PRUint32 REMOVE_ALL_TYPES = 0;
158 const PRUint32 REMOVE_AUDIO_TYPE_ONLY = 1;
159 const PRUint32 REMOVE_VIDEO_TYPE_ONLY = 2;
160 
164 template<class V, typename T>
165 PLDHashOperator PR_CALLBACK
166 CopyInterfaceHashtableEntry(typename V::KeyType aKey,
167  T* aData,
168  void* aUserData)
169 {
170  nsInterfaceHashtableMT<V, T> *newHash =
171  reinterpret_cast<nsInterfaceHashtableMT<V, T>*>(aUserData);
172 
173  NS_ASSERTION(newHash->IsInitialized(), "copying to uninitialized hashtable!");
174 
175  PRBool success = newHash->Put(aKey, aData);
176 
177  return success ? PL_DHASH_NEXT : PL_DHASH_STOP;
178 }
179 
184 class sbLocalDatabaseLibraryAsyncRunner : public nsIRunnable
185 {
186 public:
188 
190  sbLocalDatabaseLibrary* aLocalDatabaseLibrary,
191  nsISimpleEnumerator* aMediaItems,
192  sbIAddMediaItemsListener* aListener)
193  : mLocalDatabaseLibrary(aLocalDatabaseLibrary)
194  , mAddItemsListener(aListener)
195  , mMediaItems(aMediaItems) {}
196  NS_IMETHOD Run() {
197  nsresult rv;
198  // Call may not have provided a listener so just do the work
199  rv = mLocalDatabaseLibrary->AddMediaItems(mMediaItems,
200  mAddItemsListener,
201  PR_FALSE);
202  NS_ENSURE_SUCCESS(rv, rv);
203  return NS_OK;
204  }
205 
206 private:
207  nsRefPtr<sbLocalDatabaseLibrary> mLocalDatabaseLibrary;
208 
213  nsCOMPtr<sbIAddMediaItemsListener> mAddItemsListener;
214 
218  nsCOMPtr<nsISimpleEnumerator> mMediaItems;
219 };
220 
222  nsIRunnable);
223 
226 
227 
230 NS_IMETHODIMP
231 sbLibraryInsertingEnumerationListener::OnEnumerationBegin(sbIMediaList* aMediaList,
232  PRUint16* _retval)
233 {
234  NS_ENSURE_ARG_POINTER(_retval);
235 
236  // Remember the length for notifications
237  nsresult rv = mFriendLibrary->GetLength(&mLength);
238  NS_ENSURE_SUCCESS(rv, rv);
239 
241 
242  return NS_OK;
243 }
244 
248 NS_IMETHODIMP
249 sbLibraryInsertingEnumerationListener::OnEnumeratedItem(sbIMediaList* aMediaList,
250  sbIMediaItem* aMediaItem,
251  PRUint16* _retval)
252 {
253  NS_ENSURE_ARG_POINTER(aMediaItem);
254  NS_ENSURE_ARG_POINTER(_retval);
255 
256  PRBool containsCopy;
257  nsresult rv = mFriendLibrary->ContainsCopy(aMediaItem, &containsCopy);
258  NS_ENSURE_SUCCESS(rv, rv);
259  if (!containsCopy) {
260  nsCOMPtr<sbIMediaItem> newMediaItem;
261  rv = mFriendLibrary->AddItemToLocalDatabase(aMediaItem,
262  getter_AddRefs(newMediaItem));
263  NS_ENSURE_SUCCESS(rv, rv);
264 
265  // Notify our add listener if any
266  if (mListener) {
267  rv = mListener->OnItemAdded(newMediaItem);
268  // Listener told us to stop so let's abort
269  if (rv == NS_ERROR_ABORT) {
271  return NS_OK;
272  }
273  NS_ENSURE_SUCCESS(rv, rv);
274  }
275  // Remember this media item for later so we can notify with it
276  PRBool success = mNotificationList.AppendObject(newMediaItem);
277  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
278 
279  success = mOriginalItemList.AppendObject(aMediaItem);
280  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
281 
282  mShouldInvalidate = PR_TRUE;
283  }
284 
286  return NS_OK;
287 }
288 
292 NS_IMETHODIMP
293 sbLibraryInsertingEnumerationListener::OnEnumerationEnd(sbIMediaList* aMediaList,
294  nsresult aStatusCode)
295 {
296  nsresult rv;
297 
298  if (mShouldInvalidate) {
299  NS_ASSERTION(mFriendLibrary->GetArray(), "Uh, no full array?!");
300 
301  // Inserting items will definitely invalidate length values in the
302  // underlying GUID array.
303  rv = mFriendLibrary->GetArray()->Invalidate(PR_TRUE);
304  NS_ENSURE_SUCCESS(rv, rv);
305  }
306  // If we have an add listener we need to notify we're done
307  if (mListener) {
308  rv = mListener->OnComplete();
309  NS_ENSURE_SUCCESS(rv, rv);
310  }
311 
312  nsCOMPtr<sbIMediaList> libraryList =
313  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseLibrary*, mFriendLibrary), &rv);
314  NS_ENSURE_SUCCESS(rv, rv);
315 
316  // Notify our listeners
317  PRUint32 count = mNotificationList.Count();
318 
319  PRUint32 originalItemsCount = mOriginalItemList.Count();
320 
321  // Ensure the newly created item list (mNotificationList)
322  // has the same amount of items as the original items list.
323  NS_ENSURE_TRUE(count == originalItemsCount, NS_ERROR_UNEXPECTED);
324 
325  nsCOMPtr<sbILibrary> originalLibrary;
326  nsCOMPtr<sbILocalDatabaseLibrary> originalLocalDatabaseLibrary;
327 
328  for (PRUint32 i = 0; i < count; i++) {
329  // First, normal notifications.
330  mFriendLibrary->NotifyListenersItemAdded(libraryList,
331  mNotificationList[i],
332  mLength + i);
333 
334  // Then, we notify the owning library that an item was copied from it
335  // into another library.
336  rv = mOriginalItemList[i]->GetLibrary(getter_AddRefs(originalLibrary));
337  NS_ENSURE_SUCCESS(rv, rv);
338 
339  originalLocalDatabaseLibrary = do_QueryInterface(originalLibrary, &rv);
340  NS_ENSURE_SUCCESS(rv, rv);
341 
342  // It's better here to continue rather than halt on error.
343  originalLocalDatabaseLibrary->NotifyCopyListenersItemCopied(mOriginalItemList[i],
344  mNotificationList[i]);
345  }
346 
347  return NS_OK;
348 }
349 
352 
353 
356 NS_IMETHODIMP
357 sbLibraryRemovingEnumerationListener::OnEnumerationBegin(sbIMediaList* aMediaList,
358  PRUint16* _retval)
359 {
360  NS_ENSURE_ARG_POINTER(_retval);
361 
362  // Prep the query
363  nsresult rv = mFriendLibrary->MakeStandardQuery(getter_AddRefs(mDBQuery));
364  NS_ENSURE_SUCCESS(rv, rv);
365 
366  rv = mDBQuery->AddQuery(NS_LITERAL_STRING("begin"));
367  NS_ENSURE_SUCCESS(rv, rv);
368 
370 
371  return NS_OK;
372 }
373 
377 NS_IMETHODIMP
378 sbLibraryRemovingEnumerationListener::OnEnumeratedItem(sbIMediaList* aMediaList,
379  sbIMediaItem* aMediaItem,
380  PRUint16* _retval)
381 {
382  NS_ENSURE_ARG_POINTER(aMediaItem);
383  NS_ENSURE_ARG_POINTER(_retval);
384 
385  PRUint32 index;
386  nsresult rv = mFriendLibrary->IndexOf(aMediaItem, 0, &index);
387  // If the item is not in the list, ignore it
388  if (rv == NS_ERROR_NOT_AVAILABLE) {
389  return NS_OK;
390  }
391  NS_ENSURE_SUCCESS(rv, rv);
392 
393  // Remember this media item for later so we can notify with it
394  PRBool success = mNotificationList.AppendObject(aMediaItem);
395  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
396 
397  PRUint32* added = mNotificationIndexes.AppendElement(index);
398  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
399 
400  mItemEnumerated = PR_TRUE;
401 
403 
404  return NS_OK;
405 }
406 
410 NS_IMETHODIMP
411 sbLibraryRemovingEnumerationListener::OnEnumerationEnd(sbIMediaList* aMediaList,
412  nsresult aStatusCode)
413 {
414  if (!mItemEnumerated) {
415  NS_WARNING("OnEnumerationEnd called with no items enumerated");
416  return NS_OK;
417  }
418 
419  nsresult rv;
420  nsCOMPtr<sbIMediaList> libraryList =
421  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseLibrary*, mFriendLibrary), &rv);
422  NS_ENSURE_SUCCESS(rv, rv);
423 
424  // First we notify any simple media lists that contain this item
426  PRBool success = map.Init();
427  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
428 
429  sbMediaListArray lists;
430  rv = mFriendLibrary->GetContainingLists(&mNotificationList, &lists, &map);
431  NS_ENSURE_SUCCESS(rv, rv);
432 
433  sbListItemIndexMap itemIndexes;
434  success = itemIndexes.Init();
435  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
436 
437  map.EnumerateRead(sbLocalDatabaseLibrary::NotifyListsBeforeItemRemoved,
438  &itemIndexes);
439 
440  nsCOMPtr<sbIDatabasePreparedStatement> deleteItemPreparedStatement;
441  rv = mDBQuery->PrepareQuery(
442  NS_LITERAL_STRING("DELETE FROM media_items WHERE guid = ?"),
443  getter_AddRefs(deleteItemPreparedStatement));
444  NS_ENSURE_SUCCESS(rv, rv);
445 
446  PRUint32 totalOffset = 1;
447  PRUint32 count = mNotificationList.Count();
448 
449  for (PRUint32 i = 0; i < count; i++) {
450  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(mNotificationList[i], &rv);
451  NS_ENSURE_SUCCESS(rv, rv);
452 
453  // Notify explicitly registered listeners for each item removal
454  mFriendLibrary->NotifyListenersBeforeItemRemoved(libraryList,
455  item,
456  mNotificationIndexes[i]);
457 
458  // Shift indexes of items that come after the deleted index. This may
459  // seem like a very odd thing to do but we can do this safely because
460  // this method only applies to _libraries_. Medialists shift the indexes
461  // as well but they do it more sanely. What we are doing here is only
462  // possible because the items are guaranteed to be in the right order
463  // because of how they are fetched!
464  PRUint32 const j = i + 1;
465  if(j < count && mNotificationIndexes[j] > mNotificationIndexes[i]) {
466  mNotificationIndexes[j] -= totalOffset++;
467  }
468 
469  nsAutoString guid;
470  rv = item->GetGuid(guid);
471  NS_ENSURE_SUCCESS(rv, rv);
472 
473  // Remove from our cache.
474  mFriendLibrary->mMediaItemTable.Remove(guid);
475 
476  // And set up the database query to actually remove the item
477  rv = mDBQuery->AddPreparedStatement(deleteItemPreparedStatement);
478  NS_ENSURE_SUCCESS(rv, rv);
479 
480  rv = mDBQuery->BindStringParameter(0, guid);
481  NS_ENSURE_SUCCESS(rv, rv);
482  }
483 
484  rv = mDBQuery->AddQuery(NS_LITERAL_STRING("commit"));
485  NS_ENSURE_SUCCESS(rv, rv);
486 
487  PRInt32 dbSuccess;
488  rv = mDBQuery->Execute(&dbSuccess);
489  NS_ENSURE_SUCCESS(rv, rv);
490  NS_ENSURE_TRUE(dbSuccess == 0, NS_ERROR_FAILURE);
491 
492  // Invalidate our guid array. Removing items definitely influences
493  // length so we must invalidate that cached information too.
494  rv = mFriendLibrary->GetArray()->Invalidate(PR_TRUE);
495  NS_ENSURE_SUCCESS(rv, rv);
496 
497  // Invalidate all of the simple media lists we notified
498  for (PRInt32 i = 0; i < lists.Count(); i++) {
499  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
500  do_QueryInterface(lists[i], &rv);
501  NS_ENSURE_SUCCESS(rv, rv);
502 
503  // Again, since we removed items, it's highly likely the length has
504  // changed so we must invalidate the cached value.
505  rv = simple->Invalidate(PR_TRUE);
506  NS_ENSURE_SUCCESS(rv, rv);
507  }
508 
509  // Notify simple media lists after removal
510  map.EnumerateRead(sbLocalDatabaseLibrary::NotifyListsAfterItemRemoved,
511  &itemIndexes);
512 
513  // Notify our listeners of after removal
514  for (PRUint32 i = 0; i < count; i++) {
515  mFriendLibrary->NotifyListenersAfterItemRemoved(libraryList,
516  mNotificationList[i],
517  mNotificationIndexes[i]);
518  }
519 
520  return NS_OK;
521 }
522 
524  nsIClassInfo,
525  nsIObserver,
526  sbILibrary,
529 
532  nsIObserver,
534  sbILibrary,
536  sbIMediaItem,
537  sbIMediaList,
539 
540 sbLocalDatabaseLibrary::sbLocalDatabaseLibrary()
541 : mAnalyzeCountLimit(DEFAULT_ANALYZE_COUNT_LIMIT),
542  mPreventAddedNotification(PR_FALSE),
543  mMonitor(nsnull)
544 {
545 #ifdef PR_LOGGING
546  if (!gLibraryLog) {
547  gLibraryLog = PR_NewLogModule("sbLocalDatabaseLibrary");
548  }
549 #endif
550  TRACE(("LocalDatabaseLibrary[0x%.8x] - Constructed", this));
551 }
552 
554 {
555  TRACE(("LocalDatabaseLibrary[0x%.8x] - Destructed", this));
556  if(mMonitor) {
557  nsAutoMonitor::DestroyMonitor(mMonitor);
558  }
559 }
560 
561 nsresult
562 sbLocalDatabaseLibrary::Init(const nsAString& aDatabaseGuid,
563  nsIPropertyBag2* aCreationParameters,
564  sbILibraryFactory* aFactory,
565  nsIURI* aDatabaseLocation)
566 {
567  TRACE(("LocalDatabaseLibrary[0x%.8x] - Init()", this));
568  NS_ENSURE_FALSE(aDatabaseGuid.IsEmpty(), NS_ERROR_INVALID_ARG);
569  NS_ENSURE_ARG_POINTER(aCreationParameters);
570  NS_ENSURE_ARG_POINTER(aFactory);
571 
572  mDatabaseGuid = aDatabaseGuid;
573  mCreationParameters = aCreationParameters;
574  mFactory = aFactory;
575 
576  // This may be null.
577  mDatabaseLocation = aDatabaseLocation;
578 
579  // Check version and migrate if needed.
580  PRBool needsMigration = PR_FALSE;
581 
582  PRUint32 fromVersion = 0;
583  PRUint32 toVersion = 0;
584 
585  nsresult rv = NeedsMigration(&needsMigration, &fromVersion, &toVersion);
586  NS_ENSURE_SUCCESS(rv, rv);
587 
588  if(needsMigration) {
589  rv = MigrateLibrary(fromVersion, toVersion);
590  NS_ENSURE_SUCCESS(rv, rv);
591  }
592 
593  // Check locale and reindex collated indices if needed
594  PRBool needsReindexCollations = PR_FALSE;
595  rv = NeedsReindexCollations(&needsReindexCollations);
596  NS_ENSURE_SUCCESS(rv, rv);
597 
598  if (needsReindexCollations) {
599  rv = ReindexCollations();
600  NS_ENSURE_SUCCESS(rv, rv);
601  }
602 
603  PRBool success = mCopyListeners.Init();
604  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
605 
606  // Find our resource GUID. This identifies us within the library (as opposed
607  // to the database file used by the DBEngine).
608  nsCOMPtr<sbIDatabaseQuery> query;
609  rv = MakeStandardQuery(getter_AddRefs(query));
610  NS_ENSURE_SUCCESS(rv, rv);
611 
612  rv = query->AddQuery(NS_LITERAL_STRING("SELECT value FROM library_metadata WHERE name = 'resource-guid'"));
613  NS_ENSURE_SUCCESS(rv, rv);
614 
615  PRInt32 dbOk = 0;
616  rv = query->Execute(&dbOk);
617  NS_ENSURE_SUCCESS(rv, rv);
618  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
619 
620  nsCOMPtr<sbIDatabaseResult> result;
621  rv = query->GetResultObject(getter_AddRefs(result));
622  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
623 
624  PRUint32 rowCount = 0;
625  rv = result->GetRowCount(&rowCount);
626  NS_ENSURE_SUCCESS(rv, rv);
627 
628  NS_ENSURE_TRUE(rowCount == 1, NS_ERROR_UNEXPECTED);
629 
630  nsAutoString guid;
631  rv = result->GetRowCell(0, 0, guid);
632  NS_ENSURE_SUCCESS(rv, rv);
633 
634  // Set up our property cache
635  nsRefPtr<sbLocalDatabasePropertyCache>
636  propCache(new sbLocalDatabasePropertyCache());
637  NS_ENSURE_TRUE(propCache, NS_ERROR_OUT_OF_MEMORY);
638 
639  rv = propCache->Init(this, guid);
640  NS_ENSURE_SUCCESS(rv, rv);
641 
642  mPropertyCache = propCache;
643 
644  mLengthCache = new sbLocalDatabaseGUIDArrayLengthCache();
645  NS_ENSURE_TRUE (mLengthCache, NS_ERROR_OUT_OF_MEMORY);
646 
648  NS_ENSURE_TRUE(GetArray(), NS_ERROR_OUT_OF_MEMORY);
649 
650  rv = GetArray()->SetDatabaseGUID(aDatabaseGuid);
651  NS_ENSURE_SUCCESS(rv, rv);
652 
653  if (mDatabaseLocation) {
654  rv = GetArray()->SetDatabaseLocation(aDatabaseLocation);
655  NS_ENSURE_SUCCESS(rv, rv);
656  }
657 
658  rv = GetArray()->SetBaseTable(NS_LITERAL_STRING("media_items"));
659  NS_ENSURE_SUCCESS(rv, rv);
660 
661  rv = GetArray()->AddSort(NS_LITERAL_STRING(DEFAULT_SORT_PROPERTY), PR_TRUE);
662  NS_ENSURE_SUCCESS(rv, rv);
663 
664  rv = GetArray()->SetFetchSize(DEFAULT_FETCH_SIZE);
665  NS_ENSURE_SUCCESS(rv, rv);
666 
667  rv = GetArray()->SetPropertyCache(mPropertyCache);
668  NS_ENSURE_SUCCESS(rv, rv);
669 
670  rv = GetArray()->SetLengthCache(mLengthCache);
671  NS_ENSURE_SUCCESS(rv, rv);
672 
673  rv = CreateQueries();
674  NS_ENSURE_SUCCESS(rv, rv);
675 
676  // Initialize our base classes. Note that we tell sbLocalDatabaseMediaListBase
677  // that we do not want an owning reference to be kept to the library since
678  // we are the library. This is done to prevent a cycle.
679  // XXXben You can't call Init here unless this library's mPropertyCache has
680  // been created.
681  rv = sbLocalDatabaseMediaListBase::Init(this, guid, PR_FALSE);
682  NS_ENSURE_SUCCESS(rv, rv);
683 
684  // Initialize the media list factory table.
685  success = mMediaListFactoryTable.Init();
686  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
687 
688  rv = RegisterDefaultMediaListFactories();
689  NS_ENSURE_SUCCESS(rv, rv);
690 
691  // Initialize the media item table.
692  success = mMediaItemTable.Init(DEFAULT_MEDIAITEM_CACHE_SIZE);
693  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
694 
695  // Initialize the list of instantiated medialists
696  success = mMediaListTable.Init(DEFAULT_MEDIALIST_CACHE_SIZE);
697  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
698 
699  InitializeLibraryStatistics();
700 
701  // See if the user has specified a different analyze count limit. We don't
702  // care if any of this fails.
703  nsCOMPtr<nsIPrefBranch> prefBranch =
704  do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
705  if (NS_SUCCEEDED(rv)) {
706  PRInt32 prefValue;
707  rv = prefBranch->GetIntPref(ANALYZE_COUNT_PREF, &prefValue);
708  if (NS_SUCCEEDED(rv)) {
709  mAnalyzeCountLimit = PR_MAX(1, prefValue);
710  }
711  }
712 
713  // Register for library manager shutdown so we know when to write all
714  // pending changes.
715  nsCOMPtr<nsIObserverService> observerService =
716  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
717  NS_ENSURE_SUCCESS(rv, rv);
718  rv = observerService->AddObserver(this,
720  PR_FALSE);
721  NS_ENSURE_SUCCESS(rv, rv);
722 
723  mMonitor = nsAutoMonitor::NewMonitor("sbLocalDatabaseLibrary::mMonitor");
724  NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
725 
726  // Library initialized, ensure others can get notifications
727  nsCOMPtr<sbILocalDatabaseMediaItem> item =
728  do_QueryInterface(NS_ISUPPORTS_CAST(sbILibrary *, this), &rv);
729  NS_ENSURE_SUCCESS(rv, rv);
730 
731  item->SetSuppressNotifications(PR_FALSE);
732 
733  return NS_OK;
734 }
735 
739 nsresult sbLocalDatabaseLibrary::CreateQueries()
740 {
741  nsCOMPtr<sbIDatabaseQuery> query;
742  nsresult rv = MakeStandardQuery(getter_AddRefs(query), PR_FALSE);
743  NS_ENSURE_SUCCESS(rv, rv);
744 
745  rv = query->PrepareQuery(NS_LITERAL_STRING("\
746  INSERT INTO media_items \
747  (guid, created, updated, content_url, hidden, media_list_type_id, is_list) \
748  values (?, ?, ?, ?, ?, ?, ?)"), getter_AddRefs(mCreateMediaItemPreparedStatement));
749  NS_ENSURE_SUCCESS(rv, rv);
750 
751  query->PrepareQuery(NS_LITERAL_STRING("\
752  SELECT _mlt.type, _mi.content_mime_type \
753  FROM media_items as _mi \
754  LEFT JOIN media_list_types as _mlt ON _mi.media_list_type_id = _mlt.media_list_type_id \
755  WHERE _mi.guid = ?"),
756  getter_AddRefs(mGetTypeForGUID));
757  NS_ENSURE_SUCCESS(rv, rv);
758 
759  query->PrepareQuery(NS_LITERAL_STRING("\
760  SELECT guid \
761  FROM media_items \
762  WHERE metadata_hash_identity = ? and guid != ?"),
763  getter_AddRefs(mGetGUIDForIdentity));
764  NS_ENSURE_SUCCESS(rv, rv);
765 
766  query->PrepareQuery(NS_LITERAL_STRING("\
767  SELECT count(media_item_id) \
768  FROM media_items \
769  WHERE metadata_hash_identity = ? and guid != ?"),
770  getter_AddRefs(mGetCountForIdentity));
771  NS_ENSURE_SUCCESS(rv, rv);
772 
773  return NS_OK;
774 }
779 /* inline */ nsresult
780 sbLocalDatabaseLibrary::MakeStandardQuery(sbIDatabaseQuery** _retval,
781  PRBool aRunAsync)
782 {
783  TRACE(("LocalDatabaseLibrary[0x%.8x] - MakeStandardQuery()", this));
784  nsresult rv;
785  nsCOMPtr<sbIDatabaseQuery> query =
786  do_CreateInstance(SONGBIRD_DATABASEQUERY_CONTRACTID, &rv);
787  NS_ENSURE_SUCCESS(rv, rv);
788 
789  rv = query->SetDatabaseGUID(mDatabaseGuid);
790  NS_ENSURE_SUCCESS(rv, rv);
791 
792  // Set the location (if it was specified in the constructor)
793  if (mDatabaseLocation) {
794  rv = query->SetDatabaseLocation(mDatabaseLocation);
795  NS_ENSURE_SUCCESS(rv, rv);
796  }
797 
798  rv = query->SetAsyncQuery(aRunAsync);
799  NS_ENSURE_SUCCESS(rv, rv);
800 
801  NS_ADDREF(*_retval = query);
802  return NS_OK;
803 }
804 
808 /* static */ void
810  char buf[30];
811  PRUint32 len = PR_snprintf(buf, sizeof(buf), "%lld",
812  (PRUint64)(PR_Now() / PR_USEC_PER_MSEC));
813  _retval.Assign(NS_ConvertASCIItoUTF16(buf, len));
814 }
815 
827 nsresult
828 sbLocalDatabaseLibrary::AddNewItemQuery(sbIDatabaseQuery* aQuery,
829  const PRUint32 aMediaItemTypeID,
830  const nsAString& aURISpec,
831  nsAString& _retval)
832 {
833  NS_ENSURE_ARG_POINTER(aQuery);
834 
835  TRACE(("LocalDatabaseLibrary[0x%.8x] - AddNewItemQuery(%d, %s)", this,
836  aMediaItemTypeID, NS_LossyConvertUTF16toASCII(aURISpec).get()));
837 
838  nsresult rv = aQuery->AddPreparedStatement(mCreateMediaItemPreparedStatement);
839  NS_ENSURE_SUCCESS(rv, rv);
840 
841  // Make a new GUID for the new media list.
842  nsCOMPtr<nsIUUIDGenerator> uuidGen =
843  do_GetService(NS_UUID_GENERATOR_CONTRACTID, &rv);
844  NS_ENSURE_SUCCESS(rv, rv);
845 
846  nsID id;
847  rv = uuidGen->GenerateUUIDInPlace(&id);
848  NS_ENSURE_SUCCESS(rv, rv);
849 
850  char guidChars[NSID_LENGTH];
851  id.ToProvidedString(guidChars);
852 
853  nsString guid(NS_ConvertASCIItoUTF16(nsDependentCString(guidChars + 1,
854  NSID_LENGTH - 3)));
855 
856  // ToString adds curly braces to the GUID which we don't want.
857  rv = aQuery->BindStringParameter(0, guid);
858  NS_ENSURE_SUCCESS(rv, rv);
859 
860  // Set created and updated timestamps.
861  nsAutoString createdTimeString;
862  GetNowString(createdTimeString);
863 
864  rv = aQuery->BindStringParameter(1, createdTimeString);
865  NS_ENSURE_SUCCESS(rv, rv);
866 
867  rv = aQuery->BindStringParameter(2, createdTimeString);
868  NS_ENSURE_SUCCESS(rv, rv);
869 
870  // Set the new URI spec and media item type.
871  if (aMediaItemTypeID == SB_MEDIAITEM_TYPEID) {
872  // This is a regular media item so use the spec that was passed in.
873  rv = aQuery->BindStringParameter(3, aURISpec);
874  NS_ENSURE_SUCCESS(rv, rv);
875 
876  // Not hidden by default
877  rv = aQuery->BindInt32Parameter(4, 0);
878  NS_ENSURE_SUCCESS(rv, rv);
879 
880  // Media items don't have a media_list_type_id.
881  rv = aQuery->BindNullParameter(5);
882  NS_ENSURE_SUCCESS(rv, rv);
883 
884  // Media items aren't media lists. Set isList to 0.
885  rv = aQuery->BindInt32Parameter(6, 0);
886  NS_ENSURE_SUCCESS(rv, rv);
887  }
888  else {
889  // This is a media list, create its url in the form
890  // songbird-medialist://<library guid>/<item guid>
891  nsAutoString newSpec;
892  newSpec.AssignLiteral("songbird-medialist://");
893  newSpec.Append(mGuid);
894  newSpec.AppendLiteral("/");
895  newSpec.Append(guid);
896 
897  rv = aQuery->BindStringParameter(3, newSpec);
898  NS_ENSURE_SUCCESS(rv, rv);
899 
900  // Not hidden by default
901  rv = aQuery->BindInt32Parameter(4, 0);
902  NS_ENSURE_SUCCESS(rv, rv);
903 
904  // Record the media list type.
905  rv = aQuery->BindInt32Parameter(5, aMediaItemTypeID);
906  NS_ENSURE_SUCCESS(rv, rv);
907 
908  // Set isList to 1.
909  rv = aQuery->BindInt32Parameter(6, 1);
910  NS_ENSURE_SUCCESS(rv, rv);
911  }
912 
913  _retval.Assign(guid);
914  return NS_OK;
915 }
916 
917 nsresult
918 sbLocalDatabaseLibrary::SetDefaultItemProperties(sbIMediaItem* aItem,
919  sbIPropertyArray* aProperties,
920  sbMediaItemInfo* aItemInfo)
921 {
922  NS_ASSERTION(aItem, "aItem is null");
923 
924  nsresult rv;
925  nsCOMPtr<sbIPropertyArray> properties(aProperties);
926 
927  if (!properties) {
928  // we still need to pass in an empty property array so we can add missing
929  // properties as well
930  properties =
931  do_CreateInstance("@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1", &rv);
932  NS_ENSURE_SUCCESS(rv, rv);
933  }
934 
935  nsString url;
936  rv = aItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
937  url);
938  NS_ENSURE_SUCCESS(rv, rv);
939 
940  nsCOMPtr<nsIURI> uri;
941  if (!NS_IsMainThread()) {
942  // we need to proxy to the main thread
943  nsCOMPtr<nsIIOService> ioService =
944  do_ProxiedGetService("@mozilla.org/network/io-service;1", &rv);
945  NS_ENSURE_SUCCESS(rv, rv);
946 
947  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(url), nsnull,
948  nsnull, getter_AddRefs(uri));
949  NS_ENSURE_SUCCESS(rv, rv);
950 
951  nsCOMPtr<nsIThread> target;
952  rv = NS_GetMainThread(getter_AddRefs(target));
953  NS_ENSURE_SUCCESS(rv, rv);
954 
955  nsCOMPtr<nsIURI> proxiedURI;
956  rv = do_GetProxyForObject(target,
957  NS_GET_IID(nsIURI),
958  uri,
959  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
960  getter_AddRefs(proxiedURI));
961  NS_ENSURE_SUCCESS(rv, rv);
962  uri = proxiedURI;
963  }
964  else {
965  nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
966  NS_ENSURE_SUCCESS(rv, rv);
967 
968  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(url), nsnull,
969  nsnull, getter_AddRefs(uri));
970  NS_ENSURE_SUCCESS(rv, rv);
971  }
972 
973  // Only sniff out the contentType if the property currently doesn't already
974  // exist on the base item.
975  nsString contentType;
976  rv = properties->GetPropertyValue(
977  NS_LITERAL_STRING(SB_PROPERTY_CONTENTTYPE), contentType);
978  if (NS_FAILED(rv) || contentType.IsEmpty()) {
979  nsCOMPtr<sbIMediacoreTypeSniffer> typeSniffer =
980  do_CreateInstance("@songbirdnest.com/Songbird/Mediacore/TypeSniffer;1", &rv);
981  NS_ENSURE_SUCCESS(rv, rv);
982 
983  PRBool isVideo = PR_FALSE;
984  rv = typeSniffer->IsValidVideoURL(uri, &isVideo);
985  if (NS_SUCCEEDED(rv) && isVideo) {
986  nsCOMPtr<sbIMutablePropertyArray> mutableProperties =
987  do_QueryInterface(properties, &rv);
988  NS_ENSURE_SUCCESS(rv, rv);
989 
990  rv = mutableProperties->AppendProperty(
991  NS_LITERAL_STRING(SB_PROPERTY_CONTENTTYPE),
992  NS_LITERAL_STRING("video"));
993  NS_ENSURE_SUCCESS(rv, rv);
994  }
995  }
996 
997  nsCOMPtr<sbIPropertyArray> filteredProperties;
998 
999  rv = GetFilteredPropertiesForNewItem(properties,
1000  getter_AddRefs(filteredProperties));
1001  NS_ENSURE_SUCCESS(rv, rv);
1002 
1003  aItemInfo->hasAudioType = contentType.EqualsLiteral("audio");
1004  aItemInfo->hasVideoType = contentType.EqualsLiteral("video");
1005 
1006  // Set the new properties, but do not send notifications,
1007  // since we assume aItem was only just created, and at
1008  // this point nobody cares.
1009  nsCOMPtr<sbILocalDatabaseMediaItem> item =
1010  do_QueryInterface(aItem, &rv);
1011  NS_ENSURE_SUCCESS(rv, rv);
1012  item->SetSuppressNotifications(PR_TRUE);
1013  rv = aItem->SetProperties(filteredProperties);
1014  NS_ENSURE_SUCCESS(rv, rv);
1015  item->SetSuppressNotifications(PR_FALSE);
1016  return NS_OK;
1017 }
1018 
1027 nsresult
1028 sbLocalDatabaseLibrary::GetTypeForGUID(const nsAString& aGUID,
1029  nsAString& _retval)
1030 {
1031  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetTypeForGUID(%s)", this,
1032  NS_LossyConvertUTF16toASCII(aGUID).get()));
1033 
1034  // See if we have already cached this GUID's type.
1035  sbMediaItemInfo* itemInfo;
1036  if (mMediaItemTable.Get(aGUID, &itemInfo) && itemInfo->hasListType) {
1037  LOG((LOG_SUBMESSAGE_SPACE "Found type in cache!"));
1038 
1039  _retval.Assign(itemInfo->listType);
1040  return NS_OK;
1041  }
1042 
1043  // Make sure this GUID actually belongs in our library.
1044  nsCOMPtr<sbIDatabaseQuery> query;
1045  nsresult rv = MakeStandardQuery(getter_AddRefs(query));
1046  NS_ENSURE_SUCCESS(rv, rv);
1047 
1048  rv = query->AddPreparedStatement(mGetTypeForGUID);
1049  NS_ENSURE_SUCCESS(rv, rv);
1050 
1051  rv = query->BindStringParameter(0, aGUID);
1052  NS_ENSURE_SUCCESS(rv, rv);
1053 
1054  PRInt32 dbresult;
1055  rv = query->Execute(&dbresult);
1056  NS_ENSURE_SUCCESS(rv, rv);
1057  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
1058 
1059  nsCOMPtr<sbIDatabaseResult> result;
1060  rv = query->GetResultObject(getter_AddRefs(result));
1061  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1062 
1063  PRUint32 rowCount;
1064  rv = result->GetRowCount(&rowCount);
1065  NS_ENSURE_SUCCESS(rv, rv);
1066 
1067  if (rowCount == 0) {
1068  // This GUID isn't part of our library.
1069  return NS_ERROR_NOT_AVAILABLE;
1070  }
1071 
1072  nsString type;
1073  rv = result->GetRowCell(0, 0, type);
1074  NS_ENSURE_SUCCESS(rv, rv);
1075 
1076  nsString contentType;
1077  rv = result->GetRowCell(0, 1, contentType);
1078  NS_ENSURE_SUCCESS(rv, rv);
1079 
1080  if (!itemInfo) {
1081  // Make a new itemInfo for this GUID.
1082  nsAutoPtr<sbMediaItemInfo> newItemInfo(new sbMediaItemInfo());
1083  NS_ENSURE_TRUE(newItemInfo, NS_ERROR_OUT_OF_MEMORY);
1084 
1085  PRBool success = mMediaItemTable.Put(aGUID, newItemInfo);
1086  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
1087 
1088  itemInfo = newItemInfo.forget();
1089  }
1090 
1091  itemInfo->listType.Assign(type);
1092  itemInfo->hasListType = PR_TRUE;
1093  itemInfo->hasAudioType = contentType.EqualsLiteral("audio");
1094  itemInfo->hasVideoType = contentType.EqualsLiteral("video");
1095 
1096  _retval.Assign(type);
1097  return NS_OK;
1098 }
1099 
1103 /* static */ PLDHashOperator PR_CALLBACK
1104 sbLocalDatabaseLibrary::AddTypesToArrayCallback(nsStringHashKey::KeyType aKey,
1105  sbMediaListFactoryInfo* aEntry,
1106  void* aUserData)
1107 {
1108  NS_ASSERTION(aEntry, "Null entry in the hash?!");
1109  NS_ASSERTION(aUserData, "Null userData!");
1110 
1111 #ifdef PR_LOGGING
1112  NS_ASSERTION(aEntry->factory, "Null factory!");
1113 
1114  nsCAutoString contractID;
1115  aEntry->factory->GetContractID(contractID);
1116 
1117  TRACE(("LocalDatabaseLibrary - AddTypesToArrayCallback(%s, %s)",
1118  NS_LossyConvertUTF16toASCII(aKey).get(), contractID.get()));
1119 #endif
1120 
1121  // Make a string enumerator for the string array.
1122  nsTArray<nsString>* array =
1123  static_cast<nsTArray<nsString>*>(aUserData);
1124  NS_ENSURE_TRUE(array, PL_DHASH_STOP);
1125 
1126  nsString* newElement = array->AppendElement(aKey);
1127  NS_ENSURE_TRUE(newElement, PL_DHASH_STOP);
1128 
1129  return PL_DHASH_NEXT;
1130 }
1131 
1135 nsresult
1136 sbLocalDatabaseLibrary::RegisterDefaultMediaListFactories()
1137 {
1138  nsCOMPtr<sbIMediaListFactory> factory;
1139  NS_NEWXPCOM(factory, sbLocalDatabaseSimpleMediaListFactory);
1140  NS_ENSURE_TRUE(factory, NS_ERROR_OUT_OF_MEMORY);
1141 
1142  nsresult rv = RegisterMediaListFactory(factory);
1143  NS_ENSURE_SUCCESS(rv, rv);
1144 
1145  NS_NEWXPCOM(factory, sbLocalDatabaseSmartMediaListFactory);
1146  NS_ENSURE_TRUE(factory, NS_ERROR_OUT_OF_MEMORY);
1147 
1148  rv = RegisterMediaListFactory(factory);
1149  NS_ENSURE_SUCCESS(rv, rv);
1150 
1151  factory =
1153  NS_ENSURE_SUCCESS(rv, rv);
1154 
1155  rv = RegisterMediaListFactory(factory);
1156  NS_ENSURE_SUCCESS(rv, rv);
1157 
1158  return NS_OK;
1159 }
1160 
1166 nsresult
1167 sbLocalDatabaseLibrary::DeleteDatabaseItem(const nsAString& aGuid)
1168 {
1169  nsCOMPtr<sbIDatabaseQuery> query;
1170  nsresult rv = MakeStandardQuery(getter_AddRefs(query));
1171  NS_ENSURE_SUCCESS(rv, rv);
1172 
1173  rv = query->AddQuery(NS_LITERAL_STRING("DELETE FROM media_items WHERE guid = ?"));
1174  NS_ENSURE_SUCCESS(rv, rv);
1175 
1176  rv = query->BindStringParameter(0, aGuid);
1177  NS_ENSURE_SUCCESS(rv, rv);
1178 
1179  PRInt32 dbOk;
1180  rv = query->Execute(&dbOk);
1181  NS_ENSURE_SUCCESS(rv, rv);
1182  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1183 
1184  return NS_OK;
1185 }
1186 
1190 static PRBool
1192 {
1193  nsresult rv;
1194  nsCOMPtr<sbIMediaItem> libraryAsItem = do_QueryInterface(aLibrary);
1195  if (!libraryAsItem) {
1196  return PR_FALSE;
1197  }
1198 
1199  nsString innderDeviceLibraryGuid;
1200  rv = libraryAsItem->GetProperty
1201  (NS_LITERAL_STRING(SB_PROPERTY_DEVICE_LIBRARY_GUID),
1202  innderDeviceLibraryGuid);
1203  return NS_SUCCEEDED(rv) && !innderDeviceLibraryGuid.IsEmpty();
1204 }
1208 nsresult
1209 sbLocalDatabaseLibrary::AddItemToLocalDatabase(sbIMediaItem* aMediaItem,
1210  sbIMediaItem** _retval)
1211 {
1212  NS_ASSERTION(aMediaItem, "Null pointer!");
1213  NS_ASSERTION(_retval, "Null pointer!");
1214 
1215  // Create a new media item and copy all the properties
1216  nsCOMPtr<nsIURI> contentUri;
1217  nsresult rv = aMediaItem->GetContentSrc(getter_AddRefs(contentUri));
1218  NS_ENSURE_SUCCESS(rv, rv);
1219 
1220  nsCOMPtr<sbIPropertyArray> properties;
1221  rv = aMediaItem->GetProperties(nsnull, getter_AddRefs(properties));
1222  NS_ENSURE_SUCCESS(rv, rv);
1223 
1224  nsCOMPtr<sbIMutablePropertyArray> mutableProperties =
1225  do_QueryInterface(properties, &rv);
1226  NS_ENSURE_SUCCESS(rv, rv);
1227 
1228  nsCOMPtr<sbILibrary> oldLibrary;
1229  rv = aMediaItem->GetLibrary(getter_AddRefs(oldLibrary));
1230  NS_ENSURE_SUCCESS(rv, rv);
1231 
1232  // Track the origin of the copied item:
1233  rv = GetOriginProperties(aMediaItem, mutableProperties);
1234  NS_ENSURE_SUCCESS(rv, rv);
1235 
1236  nsCOMPtr<sbIMediaItem> newItem;
1237 
1238  // Is this a list?
1239  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
1240 
1241  if (NS_SUCCEEDED(rv)) {
1242  nsString type;
1243  rv = itemAsList->GetType(type);
1244  NS_ENSURE_SUCCESS(rv, rv);
1245 
1246  NS_ASSERTION(!type.IsEmpty(), "GetType returned an empty type!");
1247 
1248  PRUint32 otherListLength;
1249  rv = itemAsList->GetLength(&otherListLength);
1250  NS_ENSURE_SUCCESS(rv, rv);
1251 
1252  // CreateMediaList usually notify listeners that an item was added to the
1253  // media list. That's not what we want in this case because our callers
1254  // (Add and OnEnumeratedItem) will notify instead.
1255  mPreventAddedNotification = PR_TRUE;
1256 
1257  // Don't return after this without resetting mPreventAddedNotification!
1258 
1259  // If the list is from a different library, we want to copy the list as
1260  // type "simple", because other types can carry logic that points at some
1261  // of its library's resources.
1262  nsCOMPtr<sbILibrary> itemLibrary;
1263  nsresult rv = aMediaItem->GetLibrary(getter_AddRefs(itemLibrary));
1264  NS_ENSURE_SUCCESS(rv, rv);
1265 
1266  PRBool equals;
1267  rv = itemLibrary->Equals(SB_ILIBRESOURCE_CAST(this), &equals);
1268  NS_ENSURE_SUCCESS(rv, rv);
1269 
1270  nsCOMPtr<sbIMediaList> newList;
1271  PRBool forceCreateAsSimple = !equals;
1272 
1273  if (!forceCreateAsSimple) {
1274  rv = CreateMediaList(type, properties, getter_AddRefs(newList));
1275  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't create media list!");
1276  // Hm, try to create a simple list if that failed.
1277  if (NS_FAILED(rv))
1278  forceCreateAsSimple = PR_TRUE;
1279  }
1280 
1281  if (forceCreateAsSimple) {
1282  nsCOMPtr<sbIPropertyArray> simpleProperties;
1283  rv = GetSimpleMediaListCopyProperties(itemAsList,
1284  getter_AddRefs(simpleProperties));
1285  NS_ENSURE_SUCCESS(rv, rv);
1286 
1287  rv = CreateMediaList(NS_LITERAL_STRING("simple"), simpleProperties,
1288  getter_AddRefs(newList));
1289  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't create simple media list!");
1290  }
1291 
1292  if (NS_SUCCEEDED(rv) && otherListLength) {
1293  rv = newList->AddAll(itemAsList);
1294  }
1295 
1296  if (NS_FAILED(rv)) {
1297  // Crap, clean up the new media list.
1298  NS_WARNING("AddAll failed!");
1299 
1300  nsresult rvOther;
1301  nsCOMPtr<sbIMediaItem> newListAsItem =
1302  do_QueryInterface(newList, &rvOther);
1303  NS_WARN_IF_FALSE(NS_SUCCEEDED(rvOther), "Couldn't QI!");
1304 
1305  if (NS_SUCCEEDED(rvOther)) {
1306  rvOther = Remove(newListAsItem);
1307  NS_WARN_IF_FALSE(NS_SUCCEEDED(rvOther), "Couldn't remove new list!");
1308  }
1309  }
1310 
1311  mPreventAddedNotification = PR_FALSE;
1312 
1313  NS_ENSURE_SUCCESS(rv, rv);
1314 
1315  newItem = do_QueryInterface(newList, &rv);
1316  NS_ENSURE_SUCCESS(rv, rv);
1317  }
1318  else { // It's not a list
1319  // keep track of the library/item guid that we just copied from
1320  NS_NAMED_LITERAL_STRING(PROP_ORIGINURL, SB_PROPERTY_ORIGINURL);
1321  nsString originURL;
1322 
1323  rv = properties->GetPropertyValue(PROP_ORIGINURL, originURL);
1324  if (rv == NS_ERROR_NOT_AVAILABLE) {
1325  nsCString spec;
1326  rv = contentUri->GetSpec(spec);
1327  NS_ENSURE_SUCCESS(rv, rv);
1328 
1329  rv = mutableProperties->AppendProperty(PROP_ORIGINURL,
1330  NS_ConvertUTF8toUTF16(spec));
1331  NS_ENSURE_SUCCESS(rv, rv);
1332  }
1333 
1334  // If we're copying from the device, Hide the item till the copy is
1335  // complete. Code processing the read request will unhide.
1336  PRBool const isCopyingFromDevice = IsDeviceLibrary(oldLibrary);
1337  PRBool const isCopyingToDevice = IsDeviceLibrary(this);
1338  if (isCopyingFromDevice || isCopyingToDevice) {
1339  mutableProperties->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
1340  NS_LITERAL_STRING("1"));
1341  }
1342 
1343  // CreateMediaItem usually notify listeners that an item was added to the
1344  // media list. That's not what we want in this case because our callers
1345  // (Add and OnEnumeratedItem) will notify instead.
1346  mPreventAddedNotification = PR_TRUE;
1347  rv = CreateMediaItem(contentUri, properties, PR_TRUE,
1348  getter_AddRefs(newItem));
1349  mPreventAddedNotification = PR_FALSE;
1350  NS_ENSURE_SUCCESS(rv, rv);
1351 
1352  rv = sbLibraryUtils::LinkCopy(aMediaItem, newItem);
1353  NS_ENSURE_SUCCESS(rv, rv);
1354 
1355  if (isCopyingFromDevice) {
1356  SubmitCopyRequest(aMediaItem, newItem);
1357  }
1358  }
1359 
1360  newItem.swap(*_retval);
1361  return NS_OK;
1362 }
1363 
1372 nsresult
1373 sbLocalDatabaseLibrary::GetSimpleMediaListCopyProperties
1374  (sbIMediaList* aMediaList,
1375  sbIPropertyArray** aSimpleProperties)
1376 {
1377  NS_ASSERTION(aMediaList, "Null pointer!");
1378  NS_ASSERTION(aSimpleProperties, "Null pointer!");
1379 
1380  nsresult rv;
1381 
1382  // Get the media list properties
1383  nsCOMPtr<sbIPropertyArray> properties;
1384  rv = aMediaList->GetProperties(nsnull, getter_AddRefs(properties));
1385  NS_ENSURE_SUCCESS(rv, rv);
1386 
1387  // Create an array of simple media list properties
1388  nsCOMPtr<sbIMutablePropertyArray> simpleProperties =
1389  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1390  NS_ENSURE_SUCCESS(rv, rv);
1391 
1392  // Get the simple media list properties
1393  PRUint32 propCount;
1394  rv = properties->GetLength(&propCount);
1395  NS_ENSURE_SUCCESS(rv, rv);
1396  for (PRUint32 i = 0; i < propCount; i++) {
1397  // Get the next property.
1398  nsCOMPtr<sbIProperty> property;
1399  rv = properties->GetPropertyAt(i, getter_AddRefs(property));
1400  NS_ENSURE_SUCCESS(rv, rv);
1401 
1402  // Filter out some properties from the simple media list
1403  // - storageGuid and outerGuid are stripped because a simple list stands
1404  // on its own
1405  // - isContentReadOnly is stripped because the simple list has its own
1406  // content and will inherit isReadOnly
1407  // - listType and customType are stripped because they will be set when the
1408  // simple media list is created
1409  // - mediaListName is stripped because we're going to set it manually
1410  // using the value from list->GetName, so that the name is kept
1411  // even if it was stored as a property of a storage list
1412  nsAutoString id;
1413  rv = property->GetId(id);
1414  NS_ENSURE_SUCCESS(rv, rv);
1415  if (id.EqualsLiteral(SB_PROPERTY_STORAGEGUID) ||
1416  id.EqualsLiteral(SB_PROPERTY_OUTERGUID) ||
1417  id.EqualsLiteral(SB_PROPERTY_ISCONTENTREADONLY) ||
1418  id.EqualsLiteral(SB_PROPERTY_LISTTYPE) ||
1419  id.EqualsLiteral(SB_PROPERTY_CUSTOMTYPE) ||
1420  id.EqualsLiteral(SB_PROPERTY_MEDIALISTNAME)) {
1421  continue;
1422  }
1423 
1424  // Add the property to the simple media list property array
1425  nsAutoString value;
1426  rv = property->GetValue(value);
1427  NS_ENSURE_SUCCESS(rv, rv);
1428  rv = simpleProperties->AppendProperty(id, value);
1429  NS_ENSURE_SUCCESS(rv, rv);
1430  }
1431 
1432  // Get the simple media list name
1433  nsAutoString listName;
1434  rv = aMediaList->GetName(listName);
1435  NS_ENSURE_SUCCESS(rv, rv);
1436  rv = simpleProperties->AppendProperty
1437  (NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME),
1438  listName);
1439  NS_ENSURE_SUCCESS(rv, rv);
1440 
1441  // Track the origin of the copied item:
1442  rv = GetOriginProperties(aMediaList, simpleProperties);
1443  NS_ENSURE_SUCCESS(rv, rv);
1444 
1445  // Return results
1446  nsCOMPtr<sbIPropertyArray>
1447  returnSimpleProperties = do_QueryInterface(simpleProperties, &rv);
1448  NS_ENSURE_SUCCESS(rv, rv);
1449  returnSimpleProperties.forget(aSimpleProperties);
1450 
1451  return NS_OK;
1452 }
1453 
1458 nsresult
1459 sbLocalDatabaseLibrary::GetContainingLists(sbMediaItemArray* aItems,
1460  sbMediaListArray* aLists,
1461  sbMediaItemToListsMap* aMap)
1462 {
1463 
1464  NS_ASSERTION(aItems, "aItems is null");
1465  NS_ASSERTION(aLists, "aLists is null");
1466  NS_ASSERTION(aMap, "aMap is null");
1467  NS_ASSERTION(aMap->IsInitialized(), "aMap not initalized");
1468 
1469  nsresult rv;
1470 
1471  nsTHashtable<nsISupportsHashKey> distinctLists;
1472  PRBool success = distinctLists.Init();
1473  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1474 
1475  nsCOMPtr<sbIDatabaseQuery> query;
1476  rv = MakeStandardQuery(getter_AddRefs(query), PR_FALSE);
1477  NS_ENSURE_SUCCESS(rv, rv);
1478 
1479  // do a select by the member item's media_item_id in the simple_media_lists table
1480  // then translate the media_item_ids into guids for use with GetMediaItem
1481  nsCOMPtr<sbIDatabasePreparedStatement> preparedStatement;
1482  rv = query->PrepareQuery(NS_LITERAL_STRING("\
1483  SELECT list.guid, item.guid FROM simple_media_lists as sml\
1484  JOIN media_items AS list ON list.media_item_id = sml.media_item_id\
1485  JOIN media_items AS item ON item.media_item_id = sml.member_media_item_id\
1486  WHERE sml.member_media_item_id = ?"), getter_AddRefs(preparedStatement));
1487  NS_ENSURE_SUCCESS(rv,rv);
1488 
1489  PRUint32 count = aItems->Count();
1490  for (PRUint32 i = 0; i < count; i++) {
1491  // The library can never be a member of a simple media list, so skip it.
1492  // Without this, GetMediaItemId will fail on the library
1493  PRBool isLibrary;
1494  rv = this->Equals(aItems->ObjectAt(i), &isLibrary);
1495  NS_ENSURE_SUCCESS(rv, rv);
1496  if (isLibrary) {
1497  continue;
1498  }
1499 
1500  nsCOMPtr<sbILocalDatabaseMediaItem> item =
1501  do_QueryInterface(aItems->ObjectAt(i), &rv);
1502  if (rv == NS_NOINTERFACE) {
1503  // not a localdatabase mediaitem, continue enumeration
1504  continue;
1505  }
1506 
1507  PRUint32 mediaItemId;
1508  rv = item->GetMediaItemId(&mediaItemId);
1509  NS_ENSURE_SUCCESS(rv, rv);
1510 
1511  rv = query->AddPreparedStatement(preparedStatement);
1512  NS_ENSURE_SUCCESS(rv, rv);
1513 
1514  rv = query->BindInt32Parameter(0, mediaItemId);
1515  NS_ENSURE_SUCCESS(rv, rv);
1516  }
1517 
1518  PRInt32 dbresult;
1519  rv = query->Execute(&dbresult);
1520  NS_ENSURE_SUCCESS(rv, rv);
1521  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
1522 
1523  nsCOMPtr<sbIDatabaseResult> result;
1524  rv = query->GetResultObject(getter_AddRefs(result));
1525  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1526 
1527  PRUint32 rowCount;
1528  rv = result->GetRowCount(&rowCount);
1529  NS_ENSURE_SUCCESS(rv, rv);
1530 
1531  for (PRUint32 i = 0; i < rowCount; i++) {
1532  nsAutoString listGuid;
1533  rv = result->GetRowCell(i, 0, listGuid);
1534  NS_ENSURE_SUCCESS(rv, rv);
1535 
1536  nsCOMPtr<sbIMediaItem> list;
1537  rv = GetMediaItem(listGuid, getter_AddRefs(list));
1538  NS_ENSURE_SUCCESS(rv, rv);
1539 
1540  nsAutoString itemGuid;
1541  rv = result->GetRowCell(i, 1, itemGuid);
1542  NS_ENSURE_SUCCESS(rv, rv);
1543 
1544  nsCOMPtr<sbIMediaItem> containedItem;
1545  rv = GetMediaItem(itemGuid, getter_AddRefs(containedItem));
1546  NS_ENSURE_SUCCESS(rv, rv);
1547 
1548  // start a new media item array for us if this is the first item.
1549  sbMediaItemArray* lists;
1550  if (!aMap->Get(containedItem, &lists)) {
1551  nsAutoPtr<sbMediaItemArray> newLists(new sbMediaItemArray());
1552  NS_ENSURE_TRUE(newLists, NS_ERROR_OUT_OF_MEMORY);
1553  PRBool success = aMap->Put(containedItem, newLists);
1554  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1555  lists = newLists.forget();
1556  }
1557 
1558  success = lists->AppendObject(list);
1559  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1560 
1561  nsISupportsHashKey* addedList = distinctLists.PutEntry(list);
1562  NS_ENSURE_TRUE(addedList, NS_ERROR_OUT_OF_MEMORY);
1563  }
1564  rv = query->ResetQuery();
1565  NS_ENSURE_SUCCESS(rv, rv);
1566 
1567  distinctLists.EnumerateEntries(EntriesToMediaListArray, aLists);
1568 
1569  return NS_OK;
1570 }
1571 
1572 nsresult
1573 sbLocalDatabaseLibrary::GetAllListsByType(const nsAString& aType,
1574  sbMediaListArray* aArray)
1575 {
1576  NS_ASSERTION(aArray, "aArray is null");
1577 
1578  nsresult rv;
1579 
1580  sbMediaListFactoryInfo* factoryInfo;
1581  PRBool success = mMediaListFactoryTable.Get(aType, &factoryInfo);
1582  NS_ENSURE_TRUE(success, NS_ERROR_INVALID_ARG);
1583 
1584  nsCOMPtr<sbIDatabaseQuery> query;
1585  rv = MakeStandardQuery(getter_AddRefs(query), PR_FALSE);
1586  NS_ENSURE_SUCCESS(rv, rv);
1587 
1588  rv = query->AddQuery(NS_LITERAL_STRING(
1589  "SELECT guid FROM media_items WHERE media_list_type_id = ?"));
1590  NS_ENSURE_SUCCESS(rv, rv);
1591 
1592  rv = query->BindInt32Parameter(0, factoryInfo->typeID);
1593  NS_ENSURE_SUCCESS(rv, rv);
1594 
1595  PRInt32 dbresult;
1596  rv = query->Execute(&dbresult);
1597  NS_ENSURE_SUCCESS(rv, rv);
1598  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
1599 
1600  nsCOMPtr<sbIDatabaseResult> result;
1601  rv = query->GetResultObject(getter_AddRefs(result));
1602  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1603 
1604  PRUint32 rowCount;
1605  rv = result->GetRowCount(&rowCount);
1606  NS_ENSURE_SUCCESS(rv, rv);
1607 
1608  for (PRUint32 i = 0; i < rowCount; i++) {
1609  nsAutoString guid;
1610  rv = result->GetRowCell(i, 0, guid);
1611  NS_ENSURE_SUCCESS(rv, rv);
1612 
1613  nsCOMPtr<sbIMediaItem> item;
1614  rv = GetMediaItem(guid, getter_AddRefs(item));
1615  NS_ENSURE_SUCCESS(rv, rv);
1616 
1617  nsCOMPtr<sbIMediaList> list = do_QueryInterface(item, &rv);
1618  NS_ENSURE_SUCCESS(rv, rv);
1619 
1620  PRBool success = aArray->AppendObject(list);
1621  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1622  }
1623 
1624  return NS_OK;
1625 }
1626 
1641 nsresult
1642 sbLocalDatabaseLibrary::ConvertURIsToStrings(nsIArray* aURIs, nsStringArray** aStringArray)
1643 {
1644  TRACE(("ConvertURIsToStrings[0x%.8x] - ConvertURIsToStrings()", this));
1645 
1646  NS_ENSURE_ARG_POINTER(aURIs);
1647  NS_ENSURE_ARG_POINTER(aStringArray);
1648 
1649  nsresult rv;
1650  PRUint32 length = 0;
1651  rv = aURIs->GetLength(&length);
1652  NS_ENSURE_SUCCESS(rv, rv);
1653 
1654  nsAutoPtr<nsStringArray> strArray(new nsStringArray(length));
1655 
1656  for (PRUint32 i = 0; i < length; i++) {
1657  nsAutoString uriSpec;
1658  nsCOMPtr<nsISupportsString> uriStr = do_QueryElementAt(aURIs, i, &rv);
1659  if (!NS_SUCCEEDED(rv)) {
1660  // aURIs contains the nsIURI objects.
1661  nsCOMPtr<nsIURI> uri = do_QueryElementAt(aURIs, i, &rv);
1662  NS_ENSURE_SUCCESS(rv, rv);
1663 
1664  nsCAutoString spec;
1665  rv = uri->GetSpec(spec);
1666  NS_ENSURE_SUCCESS(rv, rv);
1667  uriSpec = NS_ConvertUTF8toUTF16(spec);
1668  } else {
1669  rv = uriStr->GetData(uriSpec);
1670  NS_ENSURE_SUCCESS(rv, rv);
1671  }
1672  strArray->AppendString(uriSpec);
1673  }
1674  *aStringArray = strArray.forget();
1675  return NS_OK;
1676 }
1677 
1687 nsresult
1688 sbLocalDatabaseLibrary::ContainsCopy(sbIMediaItem* aMediaItem,
1689  PRBool* aContainsCopy)
1690 {
1691  NS_ENSURE_ARG_POINTER(aMediaItem);
1692  NS_ENSURE_ARG_POINTER(aContainsCopy);
1693 
1694  nsresult rv;
1695 
1696  // Shortcut, if the media item's library and this list's library are the same,
1697  // this item must already be in this database.
1698  nsCOMPtr<sbILibrary> itemLibrary;
1699  rv = aMediaItem->GetLibrary(getter_AddRefs(itemLibrary));
1700  NS_ENSURE_SUCCESS(rv, rv);
1701 
1702  PRBool equals;
1703  rv = itemLibrary->Equals(SB_ILIBRESOURCE_CAST(this), &equals);
1704  NS_ENSURE_SUCCESS(rv, rv);
1705 
1706  if (equals) {
1707  *aContainsCopy = PR_TRUE;
1708  return NS_OK;
1709  }
1710 
1712  aMediaItem,
1713  static_cast<sbLocalDatabaseMediaListBase*>(this),
1714  nsnull);
1715  if (rv != NS_ERROR_NOT_AVAILABLE) {
1716  NS_ENSURE_SUCCESS(rv, rv);
1717  *aContainsCopy = PR_TRUE;
1718  return NS_OK;
1719  }
1720 
1722  aMediaItem,
1723  static_cast<sbLocalDatabaseMediaListBase*>(this),
1724  nsnull);
1725  if (rv != NS_ERROR_NOT_AVAILABLE) {
1726  NS_ENSURE_SUCCESS(rv, rv);
1727  *aContainsCopy = PR_TRUE;
1728  return NS_OK;
1729  }
1730 
1731  *aContainsCopy = PR_FALSE;
1732  return NS_OK;
1733 }
1734 
1735 nsresult
1736 sbLocalDatabaseLibrary::FilterExistingItems
1737  (nsStringArray* aURIs,
1738  nsIArray* aPropertyArrayArray,
1739  nsTArray<PRUint32>* aFilteredIndexArray,
1740  nsStringArray** aFilteredURIs,
1741  nsIArray** aFilteredPropertyArrayArray)
1742 {
1743  TRACE(("LocalDatabaseLibrary[0x%.8x] - FilterExistingItems()", this));
1744 
1745  NS_ENSURE_ARG_POINTER(aURIs);
1746  NS_ENSURE_ARG_POINTER(aFilteredURIs);
1747 
1748  if (aFilteredIndexArray)
1749  aFilteredIndexArray->Clear();
1750 
1751  PRUint32 length = aURIs->Count();
1752  // If the incoming array is empty, do nothing
1753  if (length == 0) {
1754  *aFilteredURIs = aURIs;
1755  if (aPropertyArrayArray)
1756  NS_IF_ADDREF(*aFilteredPropertyArrayArray = aPropertyArrayArray);
1757  return NS_OK;
1758  }
1759 
1760  nsTHashtable<nsStringHashKey> uniques;
1761  PRBool success = uniques.Init(length);
1762  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1763 
1764  nsresult rv;
1765  nsCOMPtr<sbIDatabaseQuery> query;
1766  rv = MakeStandardQuery(getter_AddRefs(query), PR_FALSE);
1767  NS_ENSURE_SUCCESS(rv, rv);
1768 
1769  nsCOMPtr<sbISQLSelectBuilder> builder =
1770  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
1771  NS_ENSURE_SUCCESS(rv, rv);
1772 
1773  rv = builder->AddColumn(EmptyString(), NS_LITERAL_STRING("content_url"));
1774  NS_ENSURE_SUCCESS(rv, rv);
1775 
1776  rv = builder->SetBaseTableName(NS_LITERAL_STRING("media_items"));
1777  NS_ENSURE_SUCCESS(rv, rv);
1778 
1779  nsCOMPtr<sbISQLBuilderCriterionIn> inCriterionContentURL;
1780  rv = builder->CreateMatchCriterionIn(EmptyString(),
1781  NS_LITERAL_STRING("content_url"),
1782  getter_AddRefs(inCriterionContentURL));
1783  NS_ENSURE_SUCCESS(rv, rv);
1784 
1785  rv = builder->AddCriterion(inCriterionContentURL);
1786  NS_ENSURE_SUCCESS(rv, rv);
1787 
1788  // Add url's to inCriterionContentURL
1789  PRUint32 incount = 0;
1790  // noOfDups tracks how many passed in URI objects have the same spec strings.
1791  PRUint32 noOfDups = 0;
1792  for (PRUint32 i = 0; i < length; i++) {
1793  nsAutoString uriSpec;
1794  aURIs->StringAt(i, uriSpec);
1795  // We want to build a list of unique URIs, and also only add these unique
1796  // URIs to the query
1797  if (!uniques.GetEntry(uriSpec)) {
1798  nsStringHashKey* hashKey = uniques.PutEntry(uriSpec);
1799  NS_ENSURE_TRUE(hashKey != nsnull, NS_ERROR_OUT_OF_MEMORY);
1800 
1801  rv = inCriterionContentURL->AddString(uriSpec);
1802  NS_ENSURE_SUCCESS(rv, rv);
1803 
1804  incount++;
1805  } else {
1806  noOfDups++;
1807  }
1808 
1809  if (incount > MAX_IN_LENGTH || i + 1 == length) {
1810 
1811  nsAutoString sql;
1812  rv = builder->ToString(sql);
1813  NS_ENSURE_SUCCESS(rv, rv);
1814 
1815  // TODO: can i make this faster?
1816  rv = query->AddQuery(sql);
1817  NS_ENSURE_SUCCESS(rv, rv);
1818 
1819  rv = inCriterionContentURL->Clear();
1820  NS_ENSURE_SUCCESS(rv, rv);
1821 
1822  incount = 0;
1823  }
1824  }
1825 
1826  PRInt32 dbresult;
1827  rv = query->Execute(&dbresult);
1828  NS_ENSURE_SUCCESS(rv, rv);
1829  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
1830 
1831  nsCOMPtr<sbIDatabaseResult> result;
1832  rv = query->GetResultObject(getter_AddRefs(result));
1833  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1834 
1835  PRUint32 rowCount;
1836  rv = result->GetRowCount(&rowCount);
1837  NS_ENSURE_SUCCESS(rv, rv);
1838 
1839  if (rowCount == 0 && noOfDups == 0) {
1840  *aFilteredURIs = aURIs;
1841  if (aPropertyArrayArray)
1842  NS_IF_ADDREF(*aFilteredPropertyArrayArray = aPropertyArrayArray);
1843  if (aFilteredIndexArray) {
1844  for (PRUint32 i = 0; i < length; i++) {
1845  NS_ENSURE_TRUE(aFilteredIndexArray->AppendElement(i),
1846  NS_ERROR_OUT_OF_MEMORY);
1847  }
1848  }
1849  return NS_OK;
1850  }
1851  // Remove any found URIs from the unique list since they are duplicates
1852  for (PRUint32 i = 0; i < rowCount; i++) {
1853  nsAutoString value;
1854 
1855  // Remove URI.
1856  rv = result->GetRowCell(i, 0, value);
1857  NS_ENSURE_SUCCESS(rv, rv);
1858 
1859  uniques.RemoveEntry(value);
1860  }
1861  // Now uniques should contain all the final items we need to insert to the
1862  // destination arrays.
1863  nsAutoPtr<nsStringArray> filteredURIs(new nsStringArray(length - rowCount - noOfDups));
1864  nsCOMPtr<nsIMutableArray> filteredPropertyArrayArray;
1865  if (aPropertyArrayArray) {
1866  filteredPropertyArrayArray =
1867  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1868  NS_ENSURE_SUCCESS(rv, rv);
1869  }
1870 
1871  for (PRUint32 i = 0; i < length; i++) {
1872  nsAutoString uriSpec;
1873  aURIs->StringAt(i, uriSpec);
1874  if (uniques.GetEntry(uriSpec)) {
1875  if (aFilteredIndexArray) {
1876  NS_ENSURE_TRUE(aFilteredIndexArray->AppendElement(i),
1877  NS_ERROR_OUT_OF_MEMORY);
1878  }
1879 
1880  PRBool success = filteredURIs->AppendString(uriSpec);
1881  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1882 
1883  if (aPropertyArrayArray && filteredPropertyArrayArray) {
1884  nsCOMPtr<sbIPropertyArray> properties =
1885  do_QueryElementAt(aPropertyArrayArray, i, &rv);
1886  NS_ENSURE_SUCCESS(rv, rv);
1887  rv = filteredPropertyArrayArray->AppendElement(properties, PR_FALSE);
1888  NS_ENSURE_SUCCESS(rv, rv);
1889  }
1890 
1891  // Remove them as we find them so we don't include duplicates from the
1892  // original array
1893  uniques.RemoveEntry(uriSpec);
1894  }
1895  }
1896 
1897  *aFilteredURIs = filteredURIs.forget();
1898 
1899  if (aFilteredPropertyArrayArray)
1900  NS_IF_ADDREF(*aFilteredPropertyArrayArray = filteredPropertyArrayArray);
1901 
1902  return NS_OK;
1903 }
1904 
1905 nsresult
1906 sbLocalDatabaseLibrary::GetGuidFromContentURI(nsIURI* aURI, nsAString& aGUID)
1907 {
1908  NS_ASSERTION(aURI, "aURIs is null");
1909 
1910  nsresult rv;
1911 
1912  nsCString spec;
1913  rv = aURI->GetSpec(spec);
1914  NS_ENSURE_SUCCESS(rv, rv);
1915 
1916  nsCOMPtr<sbIDatabaseQuery> query;
1917  rv = MakeStandardQuery(getter_AddRefs(query));
1918  NS_ENSURE_SUCCESS(rv, rv);
1919 
1920  rv = query->AddQuery(NS_LITERAL_STRING(
1921  "SELECT guid FROM media_items WHERE content_url = ?"
1922  ));
1923  NS_ENSURE_SUCCESS(rv, rv);
1924 
1925  rv = query->BindStringParameter(0, NS_ConvertUTF8toUTF16(spec));
1926  NS_ENSURE_SUCCESS(rv, rv);
1927 
1928  PRInt32 dbresult;
1929  rv = query->Execute(&dbresult);
1930  NS_ENSURE_SUCCESS(rv, rv);
1931  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
1932 
1933  nsCOMPtr<sbIDatabaseResult> result;
1934  rv = query->GetResultObject(getter_AddRefs(result));
1935  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1936 
1937  PRUint32 rowCount;
1938  rv = result->GetRowCount(&rowCount);
1939  NS_ENSURE_SUCCESS(rv, rv);
1940 
1941  if (rowCount == 0) {
1942  // The URI was not found
1943  return NS_ERROR_NOT_AVAILABLE;
1944  }
1945 
1946  rv = result->GetRowCell(0, 0, aGUID);
1947  NS_ENSURE_SUCCESS(rv, rv);
1948 
1949  return NS_OK;
1950 }
1951 
1952 nsresult
1953 sbLocalDatabaseLibrary::Shutdown()
1954 {
1955  TRACE(("LocalDatabaseLibrary[0x%.8x] - Shutdown()", this));
1956 
1957  // Pump events until all of our async queries have returned.
1958  PRUint32 timerCount = (PRUint32)mBatchCreateTimers.Count();
1959  if (timerCount) {
1960  LOG((LOG_SUBMESSAGE_SPACE "waiting on %u timers", timerCount));
1961 
1962  nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
1963  NS_ABORT_IF_FALSE(currentThread, "Failed to get current thread!");
1964 
1965  if (currentThread) {
1966  while (mBatchCreateTimers.Count()) {
1967  LOG((LOG_SUBMESSAGE_SPACE "processing events for %u milliseconds",
1969 #ifdef DEBUG
1970  nsresult rv =
1971 #endif
1972  NS_ProcessPendingEvents(currentThread,
1973  PR_MillisecondsToInterval(SHUTDOWN_ASYNC_GRANULARITY_MS));
1974  NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ProcessPendingEvents failed!");
1975  }
1976  }
1977  LOG((LOG_SUBMESSAGE_SPACE "all timers have died"));
1978  }
1979 
1980  // Explicitly release our property cache here so we make sure to write all
1981  // changes to disk (regardless of whether or not this library will be leaked)
1982  // to prevent data loss.
1983  mPropertyCache = nsnull;
1984 
1985  mCreateMediaItemPreparedStatement = nsnull;
1986  mGetTypeForGUID = nsnull;
1987  mStatisticsSumPreparedStatement = nsnull;
1988 
1989  return NS_OK;
1990 }
1991 
1992 nsresult
1994 {
1995  NS_ENSURE_ARG_POINTER(aLengthCache);
1996  NS_ENSURE_TRUE(mLengthCache, NS_ERROR_NOT_INITIALIZED);
1997 
1998  NS_ADDREF(*aLengthCache = mLengthCache);
1999  return NS_OK;
2000 }
2001 
2005 NS_IMETHODIMP
2006 sbLocalDatabaseLibrary::GetContractIdForGuid(const nsAString& aGUID,
2007  nsACString& aContractID)
2008 {
2009  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetContractIdForGuid(%s)", this,
2010  NS_LossyConvertUTF16toASCII(aGUID).get()));
2011  nsAutoString mediaType;
2012  nsresult rv = GetTypeForGUID(aGUID, mediaType);
2013  NS_ENSURE_SUCCESS(rv, rv);
2014 
2015  if (mediaType.IsEmpty()) {
2016  // This is a regular media item with no contractID.
2017  aContractID.Truncate();
2018  return NS_OK;
2019  }
2020 
2021  sbMediaListFactoryInfo* factoryInfo;
2022  PRBool typeRegistered = mMediaListFactoryTable.Get(mediaType, &factoryInfo);
2023  NS_ENSURE_TRUE(typeRegistered, NS_ERROR_UNEXPECTED);
2024 
2025  NS_ASSERTION(factoryInfo->factory, "Null factory pointer!");
2026  nsCAutoString contractID;
2027  rv = factoryInfo->factory->GetContractID(contractID);
2028  NS_ENSURE_SUCCESS(rv, rv);
2029 
2030  aContractID.Assign(contractID);
2031  return NS_OK;
2032 }
2033 
2037 NS_IMETHODIMP
2038 sbLocalDatabaseLibrary::GetMediaItemIdForGuid(const nsAString& aGUID,
2039  PRUint32* aMediaItemID)
2040 {
2041  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetMediaItemIdForGuid(%s)", this,
2042  NS_LossyConvertUTF16toASCII(aGUID).get()));
2043  NS_ENSURE_ARG_POINTER(aMediaItemID);
2044 
2045  sbMediaItemInfo* itemInfo;
2046  if (!mMediaItemTable.Get(aGUID, &itemInfo)) {
2047  // Make a new itemInfo for this GUID.
2048  nsAutoPtr<sbMediaItemInfo> newItemInfo(new sbMediaItemInfo());
2049  NS_ENSURE_TRUE(newItemInfo, NS_ERROR_OUT_OF_MEMORY);
2050 
2051  PRBool success = mMediaItemTable.Put(aGUID, newItemInfo);
2052  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
2053 
2054  itemInfo = newItemInfo.forget();
2055  }
2056  else if (itemInfo->hasItemID) {
2057  LOG((" - Found ID in cache!"));
2058  *aMediaItemID = itemInfo->itemID;
2059  return NS_OK;
2060  }
2061 
2062  nsCOMPtr<sbIDatabaseQuery> query;
2063  nsresult rv = MakeStandardQuery(getter_AddRefs(query));
2064  NS_ENSURE_SUCCESS(rv, rv);
2065 
2066  rv = query->AddQuery(NS_LITERAL_STRING("SELECT media_item_id, content_mime_type FROM \
2067  media_items WHERE guid = ?"));
2068  NS_ENSURE_SUCCESS(rv, rv);
2069 
2070  rv = query->BindStringParameter(0, aGUID);
2071  NS_ENSURE_SUCCESS(rv, rv);
2072 
2073  PRInt32 dbresult;
2074  rv = query->Execute(&dbresult);
2075  NS_ENSURE_SUCCESS(rv, rv);
2076  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
2077 
2078  nsCOMPtr<sbIDatabaseResult> result;
2079  rv = query->GetResultObject(getter_AddRefs(result));
2080  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2081 
2082  PRUint32 rowCount;
2083  rv = result->GetRowCount(&rowCount);
2084  NS_ENSURE_SUCCESS(rv, rv);
2085 
2086  // Some callers (e.g. Contains) listen specifically for
2087  // NS_ERROR_NOT_AVAILABLE to mean that the item does not exist in the table.
2088  NS_ENSURE_TRUE(rowCount, NS_ERROR_NOT_AVAILABLE);
2089 
2090  nsAutoString idString;
2091  rv = result->GetRowCell(0, 0, idString);
2092  NS_ENSURE_SUCCESS(rv, rv);
2093 
2094  PRUint32 id = idString.ToInteger(&rv);
2095  NS_ENSURE_SUCCESS(rv, rv);
2096 
2097  nsString contentType;
2098  rv = result->GetRowCell(0, 1, contentType);
2099  NS_ENSURE_SUCCESS(rv, rv);
2100 
2101  itemInfo->itemID = id;
2102  itemInfo->hasItemID = PR_TRUE;
2103  itemInfo->hasAudioType = contentType.EqualsLiteral("audio");
2104  itemInfo->hasVideoType = contentType.EqualsLiteral("video");
2105 
2106  *aMediaItemID = id;
2107 
2108  return NS_OK;
2109 }
2110 
2114 NS_IMETHODIMP
2115 sbLocalDatabaseLibrary::GetDatabaseGuid(nsAString& aDatabaseGuid)
2116 {
2117  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetDatabaseGuid()", this));
2118  aDatabaseGuid = mDatabaseGuid;
2119  return NS_OK;
2120 }
2121 
2125 NS_IMETHODIMP
2126 sbLocalDatabaseLibrary::GetDatabaseLocation(nsIURI** aDatabaseLocation)
2127 {
2128  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetDatabaseLocation()", this));
2129  NS_ENSURE_ARG_POINTER(aDatabaseLocation);
2130 
2131  if (!mDatabaseLocation) {
2132  *aDatabaseLocation = nsnull;
2133  return NS_OK;
2134  }
2135 
2136  nsresult rv = mDatabaseLocation->Clone(aDatabaseLocation);
2137  NS_ENSURE_SUCCESS(rv, rv);
2138 
2139  return NS_OK;
2140 }
2141 
2145 NS_IMETHODIMP
2146 sbLocalDatabaseLibrary::GetPropertyCache(sbILocalDatabasePropertyCache** aPropertyCache)
2147 {
2148  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetPropertyCache()", this));
2149  NS_ENSURE_ARG_POINTER(aPropertyCache);
2150  NS_ENSURE_TRUE(mPropertyCache, NS_ERROR_NOT_INITIALIZED);
2151  NS_ADDREF(*aPropertyCache = mPropertyCache);
2152  return NS_OK;
2153 }
2154 
2158 NS_IMETHODIMP
2159 sbLocalDatabaseLibrary::AddCopyListener(sbILocalDatabaseLibraryCopyListener *aCopyListener)
2160 {
2161  NS_ENSURE_ARG_POINTER(aCopyListener);
2162 
2163  nsCOMPtr<sbILocalDatabaseLibraryCopyListener> proxiedListener;
2164 
2165  nsresult rv = do_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
2167  aCopyListener,
2168  NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
2169  getter_AddRefs(proxiedListener));
2170  NS_ENSURE_SUCCESS(rv, rv);
2171 
2172  PRBool success = mCopyListeners.Put(aCopyListener, proxiedListener);
2173  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2174 
2175  return NS_OK;
2176 }
2177 
2181 NS_IMETHODIMP
2182 sbLocalDatabaseLibrary::RemoveCopyListener(sbILocalDatabaseLibraryCopyListener *aCopyListener)
2183 {
2184  NS_ENSURE_ARG_POINTER(aCopyListener);
2185  mCopyListeners.Remove(aCopyListener);
2186  return NS_OK;
2187 }
2188 
2192 NS_IMETHODIMP
2193 sbLocalDatabaseLibrary::CreateQuery(sbIDatabaseQuery** _retval)
2194 {
2195  return MakeStandardQuery(_retval);
2196 }
2197 
2201 NS_IMETHODIMP
2203  sbIPropertyArray* aProperties)
2204 {
2205  NS_ENSURE_ARG_POINTER(aItem);
2206  NS_ENSURE_ARG_POINTER(aProperties);
2207 
2208 #ifdef PR_LOGGING
2209  PRTime timer = PR_Now();
2210 #endif
2211 
2212  // Check all instantiated media lists and notify them if
2213  // they contain the item
2214  // use a snapshot because the listeners may do arbitary things, including
2215  // re-entering this method
2216  sbMediaItemUpdatedInfo info(aItem, aProperties, &mMediaListTable);
2217  nsInterfaceHashtableMT<nsStringHashKey, nsIWeakReference> tableSnapshot;
2218  tableSnapshot.Init(mMediaListTable.Count());
2219  mMediaListTable.EnumerateRead(CopyInterfaceHashtableEntry<nsStringHashKey,
2220  nsIWeakReference>,
2221  &tableSnapshot);
2222 
2223  tableSnapshot.Enumerate(sbLocalDatabaseLibrary::NotifyListItemUpdated,
2224  &info);
2225 
2226  // the enumeration might have changed the entries
2227  tableSnapshot.EnumerateRead(CopyInterfaceHashtableEntry<nsStringHashKey,
2228  nsIWeakReference>,
2229  &mMediaListTable);
2230 
2231  // Also notify explicity registered listeners
2233  aItem,
2234  aProperties);
2235 
2236  TRACE(("LocalDatabaseLibrary[0x%.8x] - NotifyListenersItemUpdated %d usec",
2237  this, PR_Now() - timer));
2238 
2239  return NS_OK;
2240 }
2241 
2245 NS_IMETHODIMP
2246 sbLocalDatabaseLibrary::NotifyCopyListenersItemCopied(sbIMediaItem *aSourceItem,
2247  sbIMediaItem *aDestinationItem)
2248 {
2249  NS_ENSURE_ARG_POINTER(aSourceItem);
2250  NS_ENSURE_ARG_POINTER(aDestinationItem);
2251 
2252  nsAutoPtr<sbMediaItemPair>
2253  mediaItemPair(new sbMediaItemPair(aSourceItem, aDestinationItem));
2254 
2255  // use a snapshot because the listeners may do arbitary things, including
2256  // re-entering this method
2257  nsInterfaceHashtableMT<nsISupportsHashKey, sbILocalDatabaseLibraryCopyListener> tableSnapshot;
2258  tableSnapshot.Init(mCopyListeners.Count());
2259  mCopyListeners.EnumerateRead(CopyInterfaceHashtableEntry<nsISupportsHashKey,
2261  &tableSnapshot);
2262 
2263  mCopyListeners.EnumerateRead(sbLocalDatabaseLibrary::NotifyCopyListeners,
2264  mediaItemPair);
2265 
2266  return NS_OK;
2267 }
2268 
2269 /* static */PLDHashOperator PR_CALLBACK
2270 sbLocalDatabaseLibrary::NotifyCopyListeners(nsISupportsHashKey::KeyType aKey,
2271  sbILocalDatabaseLibraryCopyListener *aCopyListener,
2272  void* aUserData)
2273 {
2274  NS_ASSERTION(aCopyListener, "Null entry in the hash?!");
2275  NS_ASSERTION(aUserData, "Null user data!");
2276 
2277  sbMediaItemPair *items = static_cast<sbMediaItemPair *>(aUserData);
2278  NS_ENSURE_TRUE(items, PL_DHASH_STOP);
2279 
2280  nsresult rv = aCopyListener->OnItemCopied(items->sourceItem,
2281  items->destinationItem);
2282  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2283 
2284  return PL_DHASH_NEXT;
2285 }
2286 
2287 /* static */ PLDHashOperator PR_CALLBACK
2288 sbLocalDatabaseLibrary::NotifyListItemUpdated(nsStringHashKey::KeyType aKey,
2289  nsCOMPtr<nsIWeakReference>& aEntry,
2290  void* aUserData)
2291 {
2292  NS_ASSERTION(aEntry, "Null entry in the hash?!");
2293  NS_ASSERTION(aUserData, "Null userData!");
2294  nsresult rv;
2295 
2296  sbMediaItemUpdatedInfo* info =
2297  static_cast<sbMediaItemUpdatedInfo*>(aUserData);
2298  NS_ENSURE_TRUE(info, PL_DHASH_STOP);
2299 
2300  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simpleList;
2301  simpleList = do_QueryReferent(aEntry, &rv);
2302  if (NS_SUCCEEDED(rv)) {
2303  // If we can get a strong reference that means someone is
2304  // actively holding on to this list, and may care for
2305  // item updated notifications.
2306 
2307  // Find out if the list contains the item that has been updated.
2308  PRBool containsItem = PR_FALSE;
2309  nsCOMPtr<sbIMediaList> list = do_QueryInterface(simpleList, &rv);
2310  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2311  rv = list->Contains(info->item, &containsItem);
2312  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2313 
2314  // If so, announce update
2315  if (containsItem) {
2316  rv = simpleList->NotifyListenersItemUpdated(
2317  info->item, 0, info->newProperties);
2318  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2319  }
2320  } else {
2321  // If no weak ref, then this list has gone away and we
2322  // can forget about it
2323  info->mediaListTable->Remove(aKey);
2324  return PL_DHASH_REMOVE;
2325  }
2326 
2327  return PL_DHASH_NEXT;
2328 }
2329 
2330 /* static */ PLDHashOperator PR_CALLBACK
2331 sbLocalDatabaseLibrary::NotifyListsBeforeItemRemoved(nsISupportsHashKey::KeyType aKey,
2332  sbMediaItemArray* aEntry,
2333  void* aUserData)
2334 {
2335  NS_ASSERTION(aEntry, "Null entry in the hash?!");
2336  NS_ASSERTION(aUserData, "Null userData!");
2337 
2338  sbListItemIndexMap* indexMap = static_cast<sbListItemIndexMap*>(aUserData);
2339  NS_ENSURE_TRUE(indexMap, PL_DHASH_STOP);
2340 
2341  nsresult rv;
2342  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(aKey, &rv);
2343  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2344 
2345  nsString itemGuid;
2346  rv = item->GetGuid(itemGuid);
2347  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2348 
2349  PRUint32 count = aEntry->Count();
2350  for (PRUint32 i = 0; i < count; i++) {
2351  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
2352  do_QueryInterface(aEntry->ObjectAt(i), &rv);
2353  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2354 
2355  nsCOMPtr<sbIMediaList> list = do_QueryInterface(simple, &rv);
2356  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2357 
2358  nsString listGuid;
2359  rv = list->GetGuid(listGuid);
2360  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2361 
2362  PRUint32 index;
2363  rv = list->IndexOf(item, 0, &index);
2364  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2365 
2366  listGuid.Append(itemGuid);
2367  PRBool success = indexMap->Put(listGuid, index);
2368  NS_ENSURE_TRUE(success, PL_DHASH_STOP);
2369 
2370  rv = simple->NotifyListenersBeforeItemRemoved(list, item, index);
2371  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2372  }
2373 
2374  return PL_DHASH_NEXT;
2375 }
2376 
2377 /* static */ PLDHashOperator PR_CALLBACK
2378 sbLocalDatabaseLibrary::NotifyListsAfterItemRemoved(nsISupportsHashKey::KeyType aKey,
2379  sbMediaItemArray* aEntry,
2380  void* aUserData)
2381 {
2382  NS_ASSERTION(aEntry, "Null entry in the hash?!");
2383  NS_ASSERTION(aUserData, "Null userData!");
2384 
2385  sbListItemIndexMap* indexMap = static_cast<sbListItemIndexMap*>(aUserData);
2386  NS_ENSURE_TRUE(indexMap, PL_DHASH_STOP);
2387 
2388  nsresult rv;
2389  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(aKey, &rv);
2390  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2391 
2392  nsString itemGuid;
2393  rv = item->GetGuid(itemGuid);
2394  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2395 
2396  PRUint32 count = aEntry->Count();
2397  for (PRUint32 i = 0; i < count; i++) {
2398  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
2399  do_QueryInterface(aEntry->ObjectAt(i), &rv);
2400  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2401 
2402  nsCOMPtr<sbIMediaList> list = do_QueryInterface(simple, &rv);
2403  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2404 
2405  nsString listGuid;
2406  rv = list->GetGuid(listGuid);
2407  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2408 
2409  PRUint32 index = 0;
2410  listGuid.Append(itemGuid);
2411  PRBool success = indexMap->Get(listGuid, &index);
2412  NS_ENSURE_TRUE(success, PL_DHASH_STOP);
2413 
2414  rv = simple->NotifyListenersAfterItemRemoved(list, item, index);
2415  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2416  }
2417 
2418  return PL_DHASH_NEXT;
2419 }
2420 
2421 /*static*/ PLDHashOperator PR_CALLBACK
2422 sbLocalDatabaseLibrary::NotifyListsBeforeAfterItemRemoved(nsISupportsHashKey::KeyType aKey,
2423  sbMediaItemArray* aEntry,
2424  void* aUserData)
2425 {
2426  NS_PRECONDITION(aEntry, "Null entry in the hash?!");
2427  NS_PRECONDITION(aUserData, "Null userData!");
2428 
2429  sbMediaItemInfoTable* infoTable = static_cast<sbMediaItemInfoTable*>(aUserData);
2430  NS_ENSURE_TRUE(infoTable, PL_DHASH_STOP);
2431 
2432  nsresult rv;
2433  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(aKey, &rv);
2434  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2435 
2436  nsString itemGuid;
2437  rv = item->GetGuid(itemGuid);
2438  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2439 
2440  PRUint32 count = aEntry->Count();
2441  for (PRUint32 i = 0; i < count; i++) {
2442  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
2443  do_QueryInterface(aEntry->ObjectAt(i), &rv);
2444  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2445 
2446  nsCOMPtr<sbIMediaList> list = do_QueryInterface(simple, &rv);
2447  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2448 
2449  PRUint32 index;
2450  rv = list->IndexOf(item, 0, &index);
2451  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2452 
2453  rv = simple->NotifyListenersBeforeItemRemoved(list, item, index);
2454  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2455 
2456  rv = simple->NotifyListenersAfterItemRemoved(list, item, index);
2457  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2458 
2459  infoTable->Remove(itemGuid);
2460  }
2461 
2462  return PL_DHASH_NEXT;
2463 }
2464 
2465 
2466 /* static */ PLDHashOperator PR_CALLBACK
2467 sbLocalDatabaseLibrary::EntriesToMediaListArray(nsISupportsHashKey* aEntry,
2468  void* aUserData)
2469 {
2470  NS_ASSERTION(aEntry, "Null entry in the hash?!");
2471  NS_ASSERTION(aUserData, "Null entry in the hash?!");
2472 
2473  sbMediaListArray* array =
2474  static_cast<sbMediaListArray*>(aUserData);
2475 
2476  nsresult rv;
2477  nsCOMPtr<sbIMediaList> list = do_QueryInterface(aEntry->GetKey(), &rv);
2478  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
2479 
2480  PRBool success = array->AppendObject(list);
2481  NS_ENSURE_TRUE(success, PL_DHASH_STOP);
2482 
2483  return PL_DHASH_NEXT;
2484 }
2485 
2486 /* static */ PLDHashOperator PR_CALLBACK
2487 sbLocalDatabaseLibrary::RemoveIfNotList(nsStringHashKey::KeyType aKey,
2488  nsAutoPtr<sbMediaItemInfo> &aEntry,
2489  void *aUserData)
2490 {
2491  PRUint32 removeType = *static_cast<PRUint32 *>(aUserData);
2492 
2493  if (aEntry->hasListType && !aEntry->listType.IsEmpty())
2494  return PL_DHASH_NEXT;
2495  else if (removeType == REMOVE_ALL_TYPES)
2496  return PL_DHASH_REMOVE;
2497  else if (removeType == REMOVE_AUDIO_TYPE_ONLY && aEntry->hasAudioType)
2498  return PL_DHASH_REMOVE;
2499  else if (removeType == REMOVE_VIDEO_TYPE_ONLY && aEntry->hasVideoType)
2500  return PL_DHASH_REMOVE;
2501  else
2502  return PL_DHASH_NEXT;
2503 }
2504 
2508 NS_IMETHODIMP
2509 sbLocalDatabaseLibrary::GetDevice(sbIDevice** aDevice)
2510 {
2511  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetDevice()", this));
2512  return NS_ERROR_NOT_IMPLEMENTED;
2513 }
2514 
2518 NS_IMETHODIMP
2519 sbLocalDatabaseLibrary::GetSupportsForeignMediaItems(PRBool* aSupportsForeignMediaItems)
2520 {
2521  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetSupportsForeignMediaItems()", this));
2522  return NS_ERROR_NOT_IMPLEMENTED;
2523 }
2524 
2528 NS_IMETHODIMP
2529 sbLocalDatabaseLibrary::GetCreationParameters(nsIPropertyBag2** aCreationParameters)
2530 {
2531  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetCreationParameters()", this));
2532  NS_ENSURE_ARG_POINTER(aCreationParameters);
2533  NS_ENSURE_STATE(mCreationParameters);
2534 
2535  NS_ADDREF(*aCreationParameters = mCreationParameters);
2536  return NS_OK;
2537 }
2538 
2542 NS_IMETHODIMP
2543 sbLocalDatabaseLibrary::GetFactory(sbILibraryFactory** aFactory)
2544 {
2545  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetFactory()", this));
2546  NS_ENSURE_ARG_POINTER(aFactory);
2547  NS_ENSURE_STATE(mFactory);
2548 
2549  NS_ADDREF(*aFactory = mFactory);
2550  return NS_OK;
2551 }
2552 
2556 NS_IMETHODIMP
2557 sbLocalDatabaseLibrary::Resolve(nsIURI* aUri,
2558  nsIChannel** _retval)
2559 {
2560  NS_ENSURE_ARG_POINTER(aUri);
2561  NS_ENSURE_ARG_POINTER(_retval);
2562 
2563  nsCAutoString spec;
2564  nsresult rv = aUri->GetSpec(spec);
2565  NS_ENSURE_SUCCESS(rv, rv);
2566 
2567  TRACE(("LocalDatabaseLibrary[0x%.8x] - Resolve(%s)", this, spec.get()));
2568  return NS_ERROR_NOT_IMPLEMENTED;
2569 }
2570 
2574 NS_IMETHODIMP
2575 sbLocalDatabaseLibrary::ContainsItemWithSameIdentity
2576  (sbIMediaItem* aMediaItem,
2577  PRBool* _retval)
2578 {
2579  NS_ENSURE_ARG_POINTER(aMediaItem);
2580  NS_ENSURE_ARG_POINTER(_retval);
2581 
2582  nsresult rv;
2583  nsCOMPtr<sbIIdentityService> idService =
2584  do_GetService("@songbirdnest.com/Songbird/IdentityService;1", &rv);
2585  NS_ENSURE_SUCCESS(rv, rv);
2586 
2587  /* flush the property cache so that we know the identity is based on current
2588  * properties */
2589  rv = Flush();
2590  NS_ENSURE_SUCCESS(rv, rv);
2591 
2592  // find the identity for the param mediaitem
2593  nsString identity;
2594  rv = idService->CalculateIdentityForMediaItem(aMediaItem, identity);
2595  NS_ENSURE_SUCCESS(rv, rv);
2596 
2597  nsCOMPtr<sbIDatabaseQuery> query;
2598  rv = MakeStandardQuery(getter_AddRefs(query));
2599  NS_ENSURE_SUCCESS(rv, rv);
2600 
2601  /* construct the query to count the number of mediaitems with the same
2602  * identity that aren't the same mediaitem (dont have the same guid) */
2603  rv = query->AddPreparedStatement(mGetCountForIdentity);
2604  NS_ENSURE_SUCCESS(rv, rv);
2605 
2606  rv = query->BindStringParameter(0, identity);
2607  NS_ENSURE_SUCCESS(rv, rv);
2608 
2609  nsString paramsGuid;
2610  rv = aMediaItem->GetGuid(paramsGuid);
2611  NS_ENSURE_SUCCESS(rv, rv);
2612 
2613  rv = query->BindStringParameter(1, paramsGuid);
2614  NS_ENSURE_SUCCESS(rv, rv);
2615 
2616  PRInt32 dbOk;
2617  rv = query->Execute(&dbOk);
2618  NS_ENSURE_SUCCESS(rv, rv);
2619  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2620 
2621  nsCOMPtr<sbIDatabaseResult> result;
2622  rv = query->GetResultObject(getter_AddRefs(result));
2623  NS_ENSURE_SUCCESS(rv, rv);
2624  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2625 
2626  nsString foundCount;
2627  rv = result->GetRowCell(0, 0, foundCount);
2628  NS_ENSURE_SUCCESS(rv, rv);
2629 
2630  *_retval = !foundCount.EqualsLiteral("0");
2631  return NS_OK;
2632 }
2633 
2637 NS_IMETHODIMP
2638 sbLocalDatabaseLibrary::GetItemsWithSameIdentity(sbIMediaItem* aMediaItem,
2639  nsIArray** _retval)
2640 {
2641  NS_ENSURE_ARG_POINTER(aMediaItem);
2642  NS_ENSURE_ARG_POINTER(_retval);
2643 
2644  nsresult rv;
2645  nsCOMPtr<sbIIdentityService> idService =
2646  do_GetService("@songbirdnest.com/Songbird/IdentityService;1", &rv);
2647  NS_ENSURE_SUCCESS(rv, rv);
2648 
2649  /* flush the property cache so that we know the identity is based on current
2650  * properties */
2651  rv = Flush();
2652  NS_ENSURE_SUCCESS(rv, rv);
2653 
2654  // find the identity for the param mediaitem
2655  nsString identity;
2656  rv = idService->CalculateIdentityForMediaItem(aMediaItem, identity);
2657  NS_ENSURE_SUCCESS(rv, rv);
2658 
2659  nsCOMPtr<sbIDatabaseQuery> query;
2660  rv = MakeStandardQuery(getter_AddRefs(query));
2661  NS_ENSURE_SUCCESS(rv, rv);
2662 
2663  /* construct the query to determine if there are any mediaitems with the same
2664  * identity that aren't the same mediaitem (dont have the same guid) */
2665  rv = query->AddPreparedStatement(mGetGUIDForIdentity);
2666  NS_ENSURE_SUCCESS(rv, rv);
2667 
2668  rv = query->BindStringParameter(0, identity);
2669  NS_ENSURE_SUCCESS(rv, rv);
2670 
2671  nsString paramsGuid;
2672  rv = aMediaItem->GetGuid(paramsGuid);
2673  NS_ENSURE_SUCCESS(rv, rv);
2674 
2675  rv = query->BindStringParameter(1, paramsGuid);
2676  NS_ENSURE_SUCCESS(rv, rv);
2677 
2678  PRInt32 dbOk;
2679  rv = query->Execute(&dbOk);
2680  NS_ENSURE_SUCCESS(rv, rv);
2681  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2682 
2683  nsCOMPtr<sbIDatabaseResult> result;
2684  rv = query->GetResultObject(getter_AddRefs(result));
2685  NS_ENSURE_SUCCESS(rv, rv);
2686  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2687 
2688  /* check if we got any results, if we did push them to the array and return.
2689  * If not, we'll return the empty array */
2690  nsCOMPtr<nsIMutableArray> sameIdentityItems =
2691  do_CreateInstance(NS_ARRAY_CONTRACTID);
2692 
2693  PRUint32 rowCount = 0;
2694  rv = result->GetRowCount(&rowCount);
2695  NS_ENSURE_SUCCESS(rv, rv);
2696 
2697  for (PRUint32 i = 0; i < rowCount; i++)
2698  {
2699  nsString foundGUID;
2700  rv = result->GetRowCell(i, 0, foundGUID);
2701  if (NS_SUCCEEDED(rv) && !foundGUID.IsEmpty())
2702  {
2703  nsCOMPtr<sbIMediaItem> foundMediaItem;
2704  rv = GetMediaItem(foundGUID, getter_AddRefs(foundMediaItem));
2705  if (NS_SUCCEEDED(rv) && foundMediaItem)
2706  {
2707  rv = sameIdentityItems->AppendElement(foundMediaItem, PR_FALSE);
2708  NS_ENSURE_SUCCESS(rv, rv);
2709  }
2710  }
2711  }
2712 
2713  NS_ADDREF(*_retval = sameIdentityItems.get());
2714  return NS_OK;
2715 }
2716 
2717 
2721 NS_IMETHODIMP
2722 sbLocalDatabaseLibrary::CreateMediaItem(nsIURI* aUri,
2723  sbIPropertyArray* aProperties,
2724  PRBool aAllowDuplicates,
2725  sbIMediaItem** _retval)
2726 {
2727  NS_ENSURE_ARG_POINTER(aUri);
2728  NS_ENSURE_ARG_POINTER(_retval);
2729 
2730  PRBool wasCreated; /* ignored */
2731  return CreateMediaItemInternal(aUri,
2732  aProperties,
2733  aAllowDuplicates,
2734  &wasCreated,
2735  _retval);
2736 }
2737 
2741 NS_IMETHODIMP
2742 sbLocalDatabaseLibrary::CreateMediaItemIfNotExist(nsIURI *aContentUri,
2743  sbIPropertyArray *aProperties,
2744  sbIMediaItem **aResultItem,
2745  PRBool *_retval)
2746 {
2747  NS_ENSURE_ARG_POINTER(aContentUri);
2748  NS_ENSURE_ARG_POINTER(_retval);
2749 
2750  nsresult rv;
2751  nsCOMPtr<sbIMediaItem> resultItem;
2752 
2753  rv = CreateMediaItemInternal(aContentUri,
2754  aProperties,
2755  PR_FALSE, /* never allow duplicates */
2756  _retval,
2757  getter_AddRefs(resultItem));
2758  NS_ENSURE_SUCCESS(rv, rv);
2759 
2760  if (aResultItem) {
2761  resultItem.forget(aResultItem);
2762  }
2763  return NS_OK;
2764 }
2765 
2766 nsresult
2767 sbLocalDatabaseLibrary::CreateMediaItemInternal(nsIURI* aUri,
2768  sbIPropertyArray* aProperties,
2769  PRBool aAllowDuplicates,
2770  PRBool* aWasCreated,
2771  sbIMediaItem** _retval)
2772 {
2773  NS_ENSURE_ARG_POINTER(aUri);
2774  NS_ENSURE_ARG_POINTER(aWasCreated);
2775  NS_ENSURE_ARG_POINTER(_retval);
2776 
2777  nsCAutoString spec;
2778  nsresult rv = aUri->GetSpec(spec);
2779  NS_ENSURE_SUCCESS(rv, rv);
2780 
2781  TRACE(("LocalDatabaseLibrary[0x%.8x] - CreateMediaItem(%s)", this,
2782  spec.get()));
2783 
2784  // If we don't allow duplicates, check to see if there is already a media
2785  // item with this uri. If so, return it.
2786  if (!aAllowDuplicates) {
2787  nsAutoPtr<nsStringArray> strArray(new nsStringArray());
2788  PRBool success = strArray->AppendString(NS_ConvertUTF8toUTF16(spec));
2789  NS_ENSURE_SUCCESS(success, NS_ERROR_OUT_OF_MEMORY);
2790 
2791  nsAutoPtr<nsStringArray> filtered;
2792 
2793  rv = FilterExistingItems(strArray,
2794  nsnull,
2795  nsnull,
2796  getter_Transfers(filtered),
2797  nsnull);
2798 
2799  NS_ENSURE_SUCCESS(rv, rv);
2800 
2801  PRUint32 length = filtered->Count();
2802  NS_ENSURE_SUCCESS(rv, rv);
2803 
2804  // The uri was filtered out, therefore it exists. Get it and return it
2805  if (length == 0) {
2806  nsString guid;
2807  rv = GetGuidFromContentURI(aUri, guid);
2808  NS_ENSURE_SUCCESS(rv, rv);
2809 
2810  rv = GetMediaItem(guid, _retval);
2811  NS_ENSURE_SUCCESS(rv, rv);
2812 
2813  *aWasCreated = PR_FALSE;
2814 
2815  return NS_OK;
2816  }
2817  if (filtered == strArray)
2818  strArray.forget();
2819  }
2820 
2821  // Remember the length so we can use it in the notification
2822  PRUint32 length;
2823  rv = GetArray()->GetLength(&length);
2824  NS_ENSURE_SUCCESS(rv, rv);
2825 
2826  nsCOMPtr<sbIDatabaseQuery> query;
2827  rv = MakeStandardQuery(getter_AddRefs(query));
2828  NS_ENSURE_SUCCESS(rv, rv);
2829 
2830  nsAutoString guid;
2831  rv = AddNewItemQuery(query,
2833  NS_ConvertUTF8toUTF16(spec),
2834  guid);
2835  NS_ENSURE_SUCCESS(rv, rv);
2836 
2837  PRInt32 dbOk;
2838  rv = query->Execute(&dbOk);
2839  NS_ENSURE_SUCCESS(rv, rv);
2840  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2841 
2842  // Add the new media item into cache
2843  nsAutoPtr<sbMediaItemInfo> newItemInfo(new sbMediaItemInfo());
2844  NS_ENSURE_TRUE(newItemInfo, NS_ERROR_OUT_OF_MEMORY);
2845 
2846  newItemInfo->hasListType = PR_TRUE;
2847 
2848  NS_ASSERTION(!mMediaItemTable.Get(guid, nsnull),
2849  "Guid already exists!");
2850 
2851  PRBool success = mMediaItemTable.Put(guid, newItemInfo);
2852  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
2853 
2854  nsCOMPtr<sbIMediaItem> mediaItem;
2855  rv = GetMediaItem(guid, getter_AddRefs(mediaItem));
2856  NS_ENSURE_SUCCESS(rv, rv);
2857 
2858  // Set up properties for the new item
2859  rv = SetDefaultItemProperties(mediaItem, aProperties, newItemInfo);
2860  NS_ENSURE_SUCCESS(rv, rv);
2861 
2862  newItemInfo.forget();
2863 
2864  // Invalidate our array. Creating items definitely changes length.
2865  rv = GetArray()->Invalidate(PR_TRUE);
2866  NS_ENSURE_SUCCESS(rv, rv);
2867 
2868  // Don't do this while we're receiving items through the Insertinglistener.
2869  if (!mPreventAddedNotification) {
2870  NotifyListenersItemAdded(SB_IMEDIALIST_CAST(this), mediaItem, length);
2871  }
2872 
2873  *aWasCreated = PR_TRUE;
2874  NS_ADDREF(*_retval = mediaItem);
2875  return NS_OK;
2876 }
2877 
2882 nsresult
2883 sbLocalDatabaseLibrary::SubmitCopyRequest(sbIMediaItem * aSourceItem,
2884  sbIMediaItem * aDestinationItem) {
2885  nsresult rv;
2886 
2887  // Ask the device manager for the device the item belongs to
2888  nsCOMPtr<sbIDeviceManager2> deviceManager =
2889  do_GetService("@songbirdnest.com/Songbird/DeviceManager;2", &rv);
2890  NS_ENSURE_SUCCESS(rv, rv);
2891 
2892  nsCOMPtr<sbIDevice> device;
2893  rv = deviceManager->GetDeviceForItem(aSourceItem, getter_AddRefs(device));
2894  if (NS_FAILED(rv) || !device) {
2895  // Errors are OK, means there's no device and we don't need to do
2896  // a copy request to the device
2897  return NS_OK;
2898  }
2899 
2900  // Setup the property bag and submit the request
2901  nsCOMPtr<nsIWritablePropertyBag2> requestParams =
2902  do_CreateInstance(NS_HASH_PROPERTY_BAG_CONTRACTID, &rv);
2903  NS_ENSURE_SUCCESS(rv, rv);
2904 
2905  rv = requestParams->SetPropertyAsInterface(NS_LITERAL_STRING("item"),
2906  aDestinationItem);
2907  NS_ENSURE_SUCCESS(rv, rv);
2908 
2909  rv = requestParams->SetPropertyAsInterface(NS_LITERAL_STRING("list"),
2910  NS_ISUPPORTS_CAST(sbILibrary*, this));
2911  NS_ENSURE_SUCCESS(rv, rv);
2912 
2913  rv = requestParams->SetPropertyAsInterface(NS_LITERAL_STRING("data"),
2914  aSourceItem);
2915  NS_ENSURE_SUCCESS(rv, rv);
2916 
2917  rv = device->SubmitRequest(sbIDevice::REQUEST_READ, requestParams);
2918  NS_ENSURE_SUCCESS(rv, rv);
2919 
2920  return NS_OK;
2921 }
2922 
2926 NS_IMETHODIMP
2927 sbLocalDatabaseLibrary::CreateMediaList(const nsAString& aType,
2928  sbIPropertyArray* aProperties,
2929  sbIMediaList** _retval)
2930 {
2931  TRACE(("LocalDatabaseLibrary[0x%.8x] - CreateMediaList(%s)", this,
2932  NS_LossyConvertUTF16toASCII(aType).get()));
2933  NS_ENSURE_ARG_POINTER(_retval);
2934 
2935  sbMediaListFactoryInfo* factoryInfo;
2936  PRBool validType = mMediaListFactoryTable.Get(aType, &factoryInfo);
2937  NS_ENSURE_TRUE(validType, NS_ERROR_INVALID_ARG);
2938 
2939  nsCOMPtr<sbIDatabaseQuery> query;
2940  nsresult rv = MakeStandardQuery(getter_AddRefs(query));
2941  NS_ENSURE_SUCCESS(rv, rv);
2942 
2943  nsAutoString guid;
2944  rv = AddNewItemQuery(query, factoryInfo->typeID, aType, guid);
2945  NS_ENSURE_SUCCESS(rv, rv);
2946 
2947  // Remember the length for notification
2948  PRUint32 length;
2949  rv = GetArray()->GetLength(&length);
2950  NS_ENSURE_SUCCESS(rv, rv);
2951 
2952  PRInt32 dbOk;
2953  rv = query->Execute(&dbOk);
2954  NS_ENSURE_SUCCESS(rv, rv);
2955  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2956 
2957  // Add the new media list into cache
2958  nsAutoPtr<sbMediaItemInfo> newItemInfo(new sbMediaItemInfo());
2959  NS_ENSURE_TRUE(newItemInfo, NS_ERROR_OUT_OF_MEMORY);
2960 
2961  newItemInfo->listType.Assign(aType);
2962  newItemInfo->hasListType = PR_TRUE;
2963 
2964  NS_ASSERTION(!mMediaItemTable.Get(guid, nsnull),
2965  "Guid already exists!");
2966 
2967  PRBool success = mMediaItemTable.Put(guid, newItemInfo);
2968  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
2969 
2970  nsCOMPtr<sbIMediaItem> mediaItem;
2971  rv = GetMediaItem(guid, getter_AddRefs(mediaItem));
2972  NS_ENSURE_SUCCESS(rv, rv);
2973 
2974  nsCOMPtr<sbIMediaList> mediaList = do_QueryInterface(mediaItem, &rv);
2975  NS_ENSURE_SUCCESS(rv, rv);
2976  if (aProperties) {
2977  rv = SetDefaultItemProperties(mediaItem, aProperties, newItemInfo);
2978  NS_ENSURE_SUCCESS(rv, rv);
2979 
2980  // Set the name for media list properly, but do not send notifications,
2981  // since we assume mediaItem was only just created, and at this point
2982  // nobody cares.
2983  nsString name;
2984  rv = aProperties->GetPropertyValue(
2985  NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME), name);
2986  if (NS_SUCCEEDED(rv) && !name.IsEmpty() && mediaList) {
2987  nsCOMPtr<sbILocalDatabaseMediaItem> item =
2988  do_QueryInterface(mediaItem, &rv);
2989  NS_ENSURE_SUCCESS(rv, rv);
2990 
2991  item->SetSuppressNotifications(PR_TRUE);
2992  mediaList->SetName(name);
2993  item->SetSuppressNotifications(PR_FALSE);
2994  }
2995  }
2996 
2997  newItemInfo.forget();
2998 
2999  // Invalidate our array. Creating lists also creates items so invalidate
3000  // the cached length values.
3001  rv = GetArray()->Invalidate(PR_TRUE);
3002  NS_ENSURE_SUCCESS(rv, rv);
3003 
3004  if (!mPreventAddedNotification) {
3005  NotifyListenersItemAdded(SB_IMEDIALIST_CAST(this), mediaItem, length);
3006  }
3007 
3008  NS_ADDREF(*_retval = mediaList);
3009  return NS_OK;
3010 }
3011 
3015 NS_IMETHODIMP
3016 sbLocalDatabaseLibrary::CopyMediaList(const nsAString& aType,
3017  sbIMediaList* aSource,
3018  PRBool aDontCopyContent,
3019  sbIMediaList** _retval)
3020 {
3021  NS_ENSURE_FALSE(aType.IsEmpty(), NS_ERROR_INVALID_ARG);
3022  NS_ENSURE_ARG_POINTER(aSource);
3023  NS_ENSURE_ARG_POINTER(_retval);
3024 
3025  nsresult rv;
3026 
3027  // Get the source media list properties. If copying as a simple media list,
3028  // get the simple media list copy properties.
3029  nsCOMPtr<sbIPropertyArray> properties;
3030  if (aType.EqualsLiteral("simple"))
3031  rv = GetSimpleMediaListCopyProperties(aSource, getter_AddRefs(properties));
3032  else
3033  rv = aSource->GetProperties(nsnull, getter_AddRefs(properties));
3034  NS_ENSURE_SUCCESS(rv, rv);
3035 
3036  nsCOMPtr<sbIMediaList> newList;
3037  rv = CreateMediaList(aType, properties, getter_AddRefs(newList));
3038  NS_ENSURE_SUCCESS(rv, rv);
3039 
3040  rv = sbLibraryUtils::LinkCopy(aSource, newList);
3041  NS_ENSURE_SUCCESS(rv, rv);
3042 
3043  if (!aDontCopyContent) {
3044  // XXXben This will probably fail for types other than "simple"... For now
3045  // we won't automatically lock other types out (by returning early)
3046  // just in case another media list type implements this behavior.
3047  rv = newList->AddAll(aSource);
3048  if (NS_FAILED(rv)) {
3049  nsresult rvOther;
3050  // Ick, sucks that that failed... Clean up the new media list.
3051  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(newList, &rvOther);
3052  NS_ENSURE_SUCCESS(rvOther, rvOther);
3053 
3054  rvOther = Remove(item);
3055  NS_ENSURE_SUCCESS(rvOther, rvOther);
3056  }
3057  NS_ENSURE_SUCCESS(rv, rv);
3058  }
3059 
3060  NS_ADDREF(*_retval = newList);
3061  return NS_OK;
3062 }
3063 
3067 NS_IMETHODIMP
3068 sbLocalDatabaseLibrary::GetMediaItem(const nsAString& aGUID,
3069  sbIMediaItem** _retval)
3070 {
3071  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetMediaItem(%s)", this,
3072  NS_LossyConvertUTF16toASCII(aGUID).get()));
3073  NS_ENSURE_ARG_POINTER(_retval);
3074 
3075  nsAutoMonitor mon(mMonitor);
3076 
3077  nsresult rv;
3078  nsCOMPtr<sbIMediaItem> strongMediaItem;
3079 
3080  // If the requested guid is the library, return ourselves
3081  if (mGuid.Equals(aGUID)) {
3082  nsCOMPtr<sbIMediaItem> item = SB_IMEDIAITEM_CAST(this);
3083  NS_ADDREF(*_retval = item);
3084  return NS_OK;
3085  }
3086 
3087  sbMediaItemInfo* itemInfo;
3088  if (!mMediaItemTable.Get(aGUID, &itemInfo) ||
3089  !itemInfo->hasListType) {
3090  // Try to get the type from the database. This will also tell us if this
3091  // GUID actually belongs to this library.
3092  nsString type;
3093  rv = GetTypeForGUID(aGUID, type);
3094  if (rv == NS_ERROR_NOT_AVAILABLE) {
3095  // Hmm, this GUID doesn't belong to this library. Someone passed in a bad
3096  // GUID.
3097  NS_WARNING("GetMediaItem called with a bad GUID!");
3098  return NS_ERROR_NOT_AVAILABLE;
3099  }
3100  NS_ENSURE_SUCCESS(rv, rv);
3101 
3102  if (!itemInfo) {
3103  // Now there should definitely be an entry for this GUID in our table.
3104  mMediaItemTable.Get(aGUID, &itemInfo);
3105  NS_ASSERTION(itemInfo, "No entry in the table for this GUID!");
3106  }
3107  }
3108  else if (itemInfo->weakRef) {
3109  strongMediaItem = do_QueryReferent(itemInfo->weakRef, &rv);
3110  if (NS_SUCCEEDED(rv)) {
3111  // This item is still owned by someone so we don't have to create it
3112  // again. Add a ref and let it live a little longer.
3113  LOG((LOG_SUBMESSAGE_SPACE "Found live weak reference in cache"));
3114  NS_ADDREF(*_retval = strongMediaItem);
3115 
3116  // It is possible for items to get in the cache without being gotten
3117  // via "GetMediaItem". Therefore, it is possible for their mSuppressNotification
3118  // flag to remain TRUE despite the fact that it should be FALSE.
3119  // This ensures that items gotten from the cache have proper notification
3120  // via media list listeners.
3121  nsCOMPtr<sbILocalDatabaseMediaItem> strongLocalItem =
3122  do_QueryInterface(strongMediaItem, &rv);
3123  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to SetSuppressNotifications!");
3124 
3125  if (NS_SUCCEEDED(rv)) {
3126  strongLocalItem->SetSuppressNotifications(PR_FALSE);
3127  }
3128 
3129  // That's all we have to do here.
3130  return NS_OK;
3131  }
3132 
3133  // Our cached item has died... Remove it from the hash table and create it
3134  // all over again.
3135  LOG((LOG_SUBMESSAGE_SPACE "Found dead weak reference in cache"));
3136  itemInfo->weakRef = nsnull;
3137  }
3138 
3139  NS_ASSERTION(itemInfo->hasListType, "Should have set a list type there!");
3140 
3141  nsRefPtr<sbLocalDatabaseMediaItem>
3142  newMediaItem(new sbLocalDatabaseMediaItem());
3143  NS_ENSURE_TRUE(newMediaItem, NS_ERROR_OUT_OF_MEMORY);
3144 
3145  rv = newMediaItem->Init(this, aGUID);
3146  NS_ENSURE_SUCCESS(rv, rv);
3147 
3148  strongMediaItem = newMediaItem;
3149 
3150  if (!itemInfo->listType.IsEmpty()) {
3151  // This must be a media list, so get the appropriate factory.
3152  sbMediaListFactoryInfo* factoryInfo;
3153  PRBool success = mMediaListFactoryTable.Get(itemInfo->listType,
3154  &factoryInfo);
3155  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
3156 
3157  NS_ASSERTION(factoryInfo->factory, "Null factory!");
3158 
3159  nsCOMPtr<sbIMediaList> mediaList;
3160  rv = factoryInfo->factory->CreateMediaList(strongMediaItem,
3161  getter_AddRefs(mediaList));
3162  NS_ENSURE_SUCCESS(rv, rv);
3163 
3164  // Replace the new media item. If the media list cared about it then it
3165  // should have added a reference to keep it alive.
3166  strongMediaItem = do_QueryInterface(mediaList, &rv);
3167  NS_ENSURE_SUCCESS(rv, rv);
3168  }
3169 
3170  itemInfo->weakRef = do_GetWeakReference(strongMediaItem, &rv);
3171  NS_ENSURE_SUCCESS(rv, rv);
3172 
3173  // Remember that this GUID maps to a MediaList, and that it
3174  // may be instantiated. We'll use this information for fast
3175  // notification.
3176  if (!itemInfo->listType.IsEmpty()) {
3177  PRBool success = mMediaListTable.Put(aGUID, itemInfo->weakRef);
3178  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
3179  }
3180 
3181  nsCOMPtr<sbILocalDatabaseMediaItem> strongLocalItem =
3182  do_QueryInterface(strongMediaItem, &rv);
3183  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetSuppressNotifications will not get called!");
3184 
3185  if (NS_SUCCEEDED(rv)) {
3186  strongLocalItem->SetSuppressNotifications(PR_FALSE);
3187  }
3188 
3189  NS_ADDREF(*_retval = strongMediaItem);
3190  return NS_OK;
3191 }
3192 
3196 NS_IMETHODIMP
3197 sbLocalDatabaseLibrary::GetDuplicate(sbIMediaItem* aMediaItem,
3198  sbIMediaItem** _retval)
3199 {
3200  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetDuplicate()", this));
3201  NS_ENSURE_ARG_POINTER(aMediaItem);
3202  NS_ENSURE_ARG_POINTER(_retval);
3203 
3204  // Search for a duplicate item
3205  nsresult rv = sbLibraryUtils::GetItemInLibrary(aMediaItem, this, _retval);
3206 
3207  // If we found it, just return.
3208  if(NS_SUCCEEDED(rv) && *_retval) {
3209  return NS_OK;
3210  }
3211 
3212  // Search for URL's
3213  nsCOMPtr<nsIMutableArray> dupeItems =
3214  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
3215  NS_ENSURE_SUCCESS(rv, rv);
3216  rv = sbLibraryUtils::FindItemsWithSameURL(aMediaItem,
3217  static_cast<sbILibrary*>(this),
3218  dupeItems);
3219  // If not found return null
3220  if (NS_FAILED(rv)) {
3221  *_retval = nsnull;
3222  return NS_OK;
3223  }
3224 
3225  // If dupes found, just return the first one
3226  PRUint32 length;
3227  rv = dupeItems->GetLength(&length);
3228  NS_ENSURE_SUCCESS(rv, rv);
3229 
3230  if (length == 0) {
3231  *_retval = nsnull;
3232  return NS_OK;
3233  }
3234 
3235  rv = dupeItems->QueryElementAt(0,
3236  NS_GET_IID(sbIMediaItem),
3237  reinterpret_cast<void**>(_retval));
3238  NS_ENSURE_SUCCESS(rv, rv);
3239 
3240  return NS_OK;
3241 }
3242 
3246 NS_IMETHODIMP
3247 sbLocalDatabaseLibrary::ClearItems()
3248 {
3249  return ClearInternal(PR_TRUE);
3250 }
3251 
3255 NS_IMETHODIMP
3256 sbLocalDatabaseLibrary::ClearItemsByType(const nsAString &aContentType)
3257 {
3258  return ClearInternal(PR_TRUE, aContentType);
3259 }
3260 
3264 NS_IMETHODIMP
3265 sbLocalDatabaseLibrary::GetMediaListTypes(nsIStringEnumerator** aMediaListTypes)
3266 {
3267  TRACE(("LocalDatabaseLibrary[0x%.8x] - GetMediaListTypes()", this));
3268  NS_ENSURE_ARG_POINTER(aMediaListTypes);
3269 
3270  // Array to hold all the registered types.
3271  nsTArray<nsString> typeArray;
3272 
3273  PRUint32 keyCount = mMediaListFactoryTable.Count();
3274  PRUint32 enumCount =
3275  mMediaListFactoryTable.EnumerateRead(AddTypesToArrayCallback, &typeArray);
3276  NS_ENSURE_TRUE(enumCount == keyCount, NS_ERROR_FAILURE);
3277 
3278  nsCOMPtr<nsIStringEnumerator> enumerator =
3279  new sbTArrayStringEnumerator(&typeArray);
3280  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
3281 
3282  NS_ADDREF(*aMediaListTypes = enumerator);
3283  return NS_OK;
3284 }
3285 
3289 NS_IMETHODIMP
3290 sbLocalDatabaseLibrary::RegisterMediaListFactory(sbIMediaListFactory* aFactory)
3291 {
3292  NS_ENSURE_ARG_POINTER(aFactory);
3293 
3294  nsAutoString type;
3295  nsresult rv = aFactory->GetType(type);
3296  NS_ENSURE_SUCCESS(rv, rv);
3297 
3298  TRACE(("LocalDatabaseLibrary[0x%.8x] - RegisterMediaListFactory(%s)", this,
3299  NS_LossyConvertUTF16toASCII(type).get()));
3300 
3301  // Bail if we've already registered this type.
3302  PRBool alreadyRegistered = mMediaListFactoryTable.Get(type, nsnull);
3303  if (alreadyRegistered) {
3304  NS_WARNING("Registering a media list factory that was already registered!");
3305  return NS_OK;
3306  }
3307 
3308  // See if this type has already been registered into the database.
3309  nsCOMPtr<sbIDatabaseQuery> query;
3310  rv = MakeStandardQuery(getter_AddRefs(query));
3311  NS_ENSURE_SUCCESS(rv, rv);
3312 
3313  rv = query->AddQuery(NS_LITERAL_STRING(
3314  "SELECT media_list_type_id FROM media_list_types WHERE type = ?"));
3315  NS_ENSURE_SUCCESS(rv, rv);
3316 
3317  rv = query->BindStringParameter(0, type);
3318  NS_ENSURE_SUCCESS(rv, rv);
3319 
3320  PRInt32 dbresult;
3321  rv = query->Execute(&dbresult);
3322  NS_ENSURE_SUCCESS(rv, rv);
3323  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
3324 
3325  nsCOMPtr<sbIDatabaseResult> result;
3326  rv = query->GetResultObject(getter_AddRefs(result));
3327  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
3328 
3329  PRUint32 rowCount;
3330  rv = result->GetRowCount(&rowCount);
3331  NS_ENSURE_SUCCESS(rv, rv);
3332 
3333  // Make sure that there is no more than one entry for this factory type.
3334  NS_ASSERTION(rowCount <= 1,
3335  "More than one entry for this type in the database!");
3336 
3337  if (!rowCount) {
3338  // This is a brand new media list type that our database has never seen.
3339  // Make a query to insert it into the database.
3340  rv = query->ResetQuery();
3341  NS_ENSURE_SUCCESS(rv, rv);
3342 
3343  rv = query->AddQuery(NS_LITERAL_STRING(
3344  "INSERT into media_list_types (type, factory_contractid) values (?, ?)"));
3345  NS_ENSURE_SUCCESS(rv, rv);
3346 
3347  rv = query->BindStringParameter(0, type);
3348  NS_ENSURE_SUCCESS(rv, rv);
3349 
3350  nsCAutoString contractID;
3351  rv = aFactory->GetContractID(contractID);
3352  NS_ENSURE_SUCCESS(rv, rv);
3353 
3354  rv = query->BindStringParameter(1, NS_ConvertASCIItoUTF16(contractID));
3355  NS_ENSURE_SUCCESS(rv, rv);
3356 
3357  rv = query->Execute(&dbresult);
3358  NS_ENSURE_SUCCESS(rv, rv);
3359  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
3360 
3361  // Get the newly created typeID for the factory.
3362  rv = query->ResetQuery();
3363  NS_ENSURE_SUCCESS(rv, rv);
3364 
3365  rv = query->AddQuery(NS_LITERAL_STRING("select last_insert_rowid()"));
3366  NS_ENSURE_SUCCESS(rv, rv);
3367 
3368  rv = query->Execute(&dbresult);
3369  NS_ENSURE_SUCCESS(rv, rv);
3370  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
3371 
3372  rv = query->GetResultObject(getter_AddRefs(result));
3373  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
3374  }
3375 
3376  nsAutoString newTypeIDString;
3377  rv = result->GetRowCell(0, 0, newTypeIDString);
3378  NS_ENSURE_SUCCESS(rv, rv);
3379 
3380  PRUint32 newTypeID = newTypeIDString.ToInteger(&rv);
3381  NS_ENSURE_SUCCESS(rv, rv);
3382 
3383  // Make a new object to hold this info.
3384  nsAutoPtr<sbMediaListFactoryInfo>
3385  factoryInfo(new sbMediaListFactoryInfo(newTypeID, aFactory));
3386  NS_ENSURE_TRUE(factoryInfo, NS_ERROR_OUT_OF_MEMORY);
3387 
3388  // And add it to our hash table.
3389  PRBool success = mMediaListFactoryTable.Put(type, factoryInfo);
3390  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
3391 
3392  factoryInfo.forget();
3393  return NS_OK;
3394 }
3395 
3399 NS_IMETHODIMP
3400 sbLocalDatabaseLibrary::Optimize(PRBool aAnalyzeOnly)
3401 {
3402  TRACE(("LocalDatabaseLibrary[0x%.8x] - Optimize()", this));
3403 
3404  nsCOMPtr<sbIDatabaseQuery> query;
3405  nsresult rv = MakeStandardQuery(getter_AddRefs(query), PR_TRUE);
3406  NS_ENSURE_SUCCESS(rv, rv);
3407 
3408  if (!aAnalyzeOnly) {
3409  rv = query->AddQuery(NS_LITERAL_STRING("VACUUM"));
3410  NS_ENSURE_SUCCESS(rv, rv);
3411  }
3412 
3413  rv = query->AddQuery(NS_LITERAL_STRING("ANALYZE"));
3414  NS_ENSURE_SUCCESS(rv, rv);
3415 
3416  PRInt32 dbresult;
3417  rv = query->Execute(&dbresult);
3418  NS_ENSURE_SUCCESS(rv, rv);
3419  NS_ENSURE_TRUE(dbresult == 0, NS_ERROR_FAILURE);
3420 
3421  return NS_OK;
3422 }
3423 
3427 NS_IMETHODIMP
3428 sbLocalDatabaseLibrary::Flush()
3429 {
3430  TRACE(("LocalDatabaseLibrary[0x%.8x] - Sync()", this));
3431  NS_ENSURE_TRUE(mPropertyCache, NS_ERROR_NOT_INITIALIZED);
3432 
3433  nsresult rv = mPropertyCache->Write();
3434  NS_ENSURE_SUCCESS(rv, rv);
3435 
3436  return NS_OK;
3437 }
3438 
3442 NS_IMETHODIMP
3443 sbLocalDatabaseLibrary::BatchCreateMediaItems(nsIArray* aURIArray,
3444  nsIArray* aPropertyArrayArray,
3445  PRBool aAllowDuplicates,
3446  nsIArray** _retval)
3447 {
3448  NS_ENSURE_ARG_POINTER(aURIArray);
3449  NS_ENSURE_ARG_POINTER(_retval);
3450 
3451  TRACE(("LocalDatabaseLibrary[0x%.8x] - BatchCreateMediaItems()", this));
3452 
3453  return BatchCreateMediaItemsInternal(aURIArray,
3454  aPropertyArrayArray,
3455  aAllowDuplicates,
3456  nsnull,
3457  nsnull,
3458  _retval);
3459 }
3460 
3464 NS_IMETHODIMP
3465 sbLocalDatabaseLibrary::BatchCreateMediaItemsIfNotExist
3466  (nsIArray* aURIArray,
3467  nsIArray* aPropertyArrayArray,
3468  nsIArray** aResultItemArray,
3469  nsIArray** _retval)
3470 {
3471  NS_ENSURE_ARG_POINTER(aURIArray);
3472  NS_ENSURE_ARG_POINTER(_retval);
3473 
3474  TRACE(("LocalDatabaseLibrary[0x%.8x] - BatchCreateMediaItemsIfNotExist()",
3475  this));
3476 
3477  return BatchCreateMediaItemsInternal(aURIArray,
3478  aPropertyArrayArray,
3479  PR_FALSE,
3480  _retval,
3481  nsnull,
3482  aResultItemArray);
3483 }
3484 
3488 NS_IMETHODIMP
3489 sbLocalDatabaseLibrary::BatchCreateMediaItemsAsync(sbIBatchCreateMediaItemsListener* aListener,
3490  nsIArray* aURIArray,
3491  nsIArray* aPropertyArrayArray,
3492  PRBool aAllowDuplicates)
3493 {
3494  NS_ENSURE_ARG_POINTER(aListener);
3495  NS_ENSURE_ARG_POINTER(aURIArray);
3496 
3497  TRACE(("LocalDatabaseLibrary[0x%.8x] - BatchCreateMediaItemsAsync()", this));
3498 
3499  return BatchCreateMediaItemsInternal(aURIArray,
3500  aPropertyArrayArray,
3501  aAllowDuplicates,
3502  nsnull,
3503  aListener,
3504  nsnull);
3505 }
3506 
3507 nsresult
3508 sbLocalDatabaseLibrary::BatchCreateMediaItemsInternal(nsIArray* aURIArray,
3509  nsIArray* aPropertyArrayArray,
3510  PRBool aAllowDuplicates,
3511  nsIArray** aMediaItemCreatedArray,
3513  nsIArray** _retval)
3514 {
3515  NS_ASSERTION((aListener && !_retval) || (!aListener && _retval),
3516  "Only one of |aListener| and |_retval| should be set!");
3517 
3518  TRACE(("LocalDatabaseLibrary[0x%.8x] - BatchCreateMediaItemsInternal()",
3519  this));
3520 
3521  nsresult rv;
3522 
3523  nsAutoPtr<nsStringArray> strArray;
3524  // Convert URI objects into String objects
3525  rv = ConvertURIsToStrings(aURIArray, getter_Transfers(strArray));
3526  NS_ENSURE_SUCCESS(rv, rv);
3527 
3528  nsAutoPtr<nsStringArray> filteredArray;
3529  nsCOMPtr<nsIArray> filteredPropertyArrayArray;
3530  nsTArray<PRUint32> createdMediaItemIndexArray;
3531  if (aAllowDuplicates) {
3532  filteredArray = strArray.forget();
3533  filteredPropertyArrayArray = aPropertyArrayArray;
3534  }
3535  else {
3536  rv = FilterExistingItems(strArray,
3537  aPropertyArrayArray,
3538  &createdMediaItemIndexArray,
3539  getter_Transfers(filteredArray),
3540  getter_AddRefs(filteredPropertyArrayArray));
3541  NS_ENSURE_SUCCESS(rv, rv);
3542  if (strArray == filteredArray)
3543  strArray.forget();
3544  }
3545 
3546  PRBool runAsync = aListener ? PR_TRUE : PR_FALSE;
3547 
3548  nsCOMPtr<sbIDatabaseQuery> query;
3549  rv = MakeStandardQuery(getter_AddRefs(query), runAsync);
3550  NS_ENSURE_SUCCESS(rv, rv);
3551 
3552  nsRefPtr<sbBatchCreateTimerCallback> callback;
3553  nsRefPtr<sbBatchCreateHelper> helper;
3554 
3555  if (runAsync) {
3556  callback = new sbBatchCreateTimerCallback(this, aListener, query);
3557  NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
3558 
3559  rv = callback->Init();
3560  NS_ENSURE_SUCCESS(rv, rv);
3561 
3562  helper = callback->BatchHelper();
3563  }
3564  else {
3565  helper = new sbBatchCreateHelper(this);
3566  NS_ENSURE_TRUE(helper, NS_ERROR_OUT_OF_MEMORY);
3567  }
3568 
3569  // Set up the batch add query
3570  rv = helper->InitQuery(query, filteredArray.forget(), filteredPropertyArrayArray);
3571  NS_ENSURE_SUCCESS(rv, rv);
3572 
3573  PRInt32 dbResult;
3574  rv = query->Execute(&dbResult);
3575  NS_ENSURE_SUCCESS(rv, rv);
3576  NS_ENSURE_TRUE(dbResult == 0, NS_ERROR_FAILURE);
3577 
3578  if (runAsync) {
3579  // Start polling the query for completion
3580  nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
3581  NS_ENSURE_SUCCESS(rv, rv);
3582 
3583  // Stick the timer into a member array so we keep it alive while it does
3584  // its thing. It never gets removed from this array until this library
3585  // gets deleted.
3586  PRBool success = mBatchCreateTimers.AppendObject(timer);
3587  NS_ENSURE_TRUE(success, rv);
3588 
3589  rv = timer->InitWithCallback(callback, BATCHCREATE_NOTIFICATION_INTERVAL_MS,
3590  nsITimer::TYPE_REPEATING_SLACK);
3591  if (NS_FAILED(rv)) {
3592  NS_WARNING("InitWithCallback failed!");
3593  success = mBatchCreateTimers.RemoveObject(timer);
3594  NS_ASSERTION(success, "Failed to remove a failed timer... Armageddon?");
3595  return rv;
3596  }
3597  }
3598  else {
3599  // Notify and get the new items
3600  nsCOMPtr<nsIArray> array;
3601  rv = helper->NotifyAndGetItems(getter_AddRefs(array));
3602  NS_ENSURE_SUCCESS(rv, rv);
3603 
3604  // Return the array of media items
3605  if (aMediaItemCreatedArray) {
3606  // Create the list of all items and the list of which items were created.
3607  nsCOMPtr<nsIMutableArray> allItems =
3608  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1",
3609  &rv);
3610  nsCOMPtr<nsIMutableArray> mediaItemCreatedArray =
3611  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1",
3612  &rv);
3613  NS_ENSURE_SUCCESS(rv, rv);
3614 
3615  // Fill in the lists for the created media items.
3616  for (PRUint32 i = 0; i < createdMediaItemIndexArray.Length(); i++) {
3617  // Get the full item list index of the created media item.
3618  PRUint32 createdMediaItemIndex = createdMediaItemIndexArray[i];
3619 
3620  // Get the created media item.
3621  nsCOMPtr<sbIMediaItem> mediaItem = do_QueryElementAt(array, i, &rv);
3622  NS_ENSURE_SUCCESS(rv, rv);
3623 
3624  // Add the created media item to the list of all items.
3625  rv = allItems->ReplaceElementAt(mediaItem,
3626  createdMediaItemIndex,
3627  PR_FALSE);
3628  NS_ENSURE_SUCCESS(rv, rv);
3629 
3630  // Set the media item as created in the created list.
3631  rv = mediaItemCreatedArray->ReplaceElementAt
3632  (sbNewVariant(PR_TRUE, nsIDataType::VTYPE_BOOL),
3633  createdMediaItemIndex,
3634  PR_FALSE);
3635  NS_ENSURE_SUCCESS(rv, rv);
3636  }
3637 
3638  // Fill in the lists for the existing media items.
3639  PRUint32 length;
3640  rv = aURIArray->GetLength(&length);
3641  NS_ENSURE_SUCCESS(rv, rv);
3642  for (PRUint32 i = 0; i < length; i++) {
3643  // Skip items that were newly created.
3644  if (createdMediaItemIndexArray.Contains(i))
3645  continue;
3646 
3647  // Get the media item from its URI (same as CreateMediaItemIfNotExist).
3648  nsCOMPtr<sbIMediaItem> mediaItem;
3649  nsString guid;
3650  nsCOMPtr<nsIURI> uri = do_QueryElementAt(aURIArray, i, &rv);
3651  NS_ENSURE_SUCCESS(rv, rv);
3652  rv = GetGuidFromContentURI(uri, guid);
3653  NS_ENSURE_SUCCESS(rv, rv);
3654  rv = GetMediaItem(guid, getter_AddRefs(mediaItem));
3655  NS_ENSURE_SUCCESS(rv, rv);
3656 
3657  // Add the item to the list of all items.
3658  rv = allItems->ReplaceElementAt(mediaItem, i, PR_FALSE);
3659  NS_ENSURE_SUCCESS(rv, rv);
3660 
3661  // Set the media item as not created in the created list.
3662  rv = mediaItemCreatedArray->ReplaceElementAt
3663  (sbNewVariant(PR_FALSE, nsIDataType::VTYPE_BOOL),
3664  i,
3665  PR_FALSE);
3666  NS_ENSURE_SUCCESS(rv, rv);
3667  }
3668 
3669  rv = CallQueryInterface(allItems, _retval);
3670  NS_ENSURE_SUCCESS(rv, rv);
3671  rv = CallQueryInterface(mediaItemCreatedArray, aMediaItemCreatedArray);
3672  NS_ENSURE_SUCCESS(rv, rv);
3673  }
3674  else {
3675  NS_ADDREF(*_retval = array);
3676  }
3677  }
3678 
3679  return NS_OK;
3680 }
3681 
3682 nsresult
3683 sbLocalDatabaseLibrary::ClearInternal(PRBool aExcludeLists /*= PR_FALSE*/,
3684  const nsAString &aContentType /*= EmptyString()*/)
3685 {
3687  NS_ENSURE_TRUE(mPropertyCache, NS_ERROR_NOT_INITIALIZED);
3688 
3689  sbAutoBatchHelper batchHelper(*this);
3690 
3691  // Notify the library's listeners that the library is about to be cleared
3693 
3694  nsresult rv = mPropertyCache->Write();
3695  NS_ENSURE_SUCCESS(rv, rv);
3696 
3697  // We only have to get the simple media lists because those are
3698  // the lists that are used for storage for all other media list types.
3699  // The outer implementation (ie, the actual smart, dynamic media list)
3700  // will pick up the changes made to it's storage list.
3701  sbMediaListArray lists;
3702  rv = GetAllListsByType(NS_LITERAL_STRING("simple"), &lists);
3703  NS_ENSURE_SUCCESS(rv, rv);
3704 
3705  // Notify simple media lists that they are getting cleared
3706  for (PRInt32 i = 0; i < lists.Count(); i++) {
3707  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
3708  do_QueryInterface(lists[i], &rv);
3709  NS_ENSURE_SUCCESS(rv, rv);
3710 
3711  rv = simple->NotifyListenersBeforeListCleared(lists[i], aExcludeLists);
3712  NS_ENSURE_SUCCESS(rv, rv);
3713  }
3714 
3715  nsCOMPtr<sbIDatabaseQuery> query;
3716  rv = MakeStandardQuery(getter_AddRefs(query));
3717  NS_ENSURE_SUCCESS(rv, rv);
3718 
3719  // Type of media items to remove from media item table. Only used when
3720  // excluding lists from the delete operation.
3721  PRUint32 removeType = REMOVE_ALL_TYPES;
3722 
3723  if(!aExcludeLists) {
3724  // Clear our caches
3725  mMediaListTable.Clear();
3726 
3727  rv = query->AddQuery(NS_LITERAL_STRING("DELETE FROM media_items"));
3728  NS_ENSURE_SUCCESS(rv, rv);
3729  }
3730  else {
3731  if(aContentType.IsEmpty()) {
3732  rv = query->AddQuery(NS_LITERAL_STRING("DELETE FROM media_items WHERE is_list = 0"));
3733  NS_ENSURE_SUCCESS(rv, rv);
3734  }
3735  else {
3736 
3737  // Only accept 'audio' and 'video' content types for now.
3738  if(aContentType.EqualsLiteral("audio")) {
3739  removeType = REMOVE_AUDIO_TYPE_ONLY;
3740  }
3741  else if(aContentType.EqualsLiteral("video")) {
3742  removeType = REMOVE_VIDEO_TYPE_ONLY;
3743  }
3744  else {
3745  return NS_ERROR_INVALID_ARG;
3746  }
3747 
3748  rv = query->AddQuery(NS_LITERAL_STRING("DELETE FROM media_items WHERE \
3749  is_list = 0 AND \
3750  content_mime_type = ?"));
3751  NS_ENSURE_SUCCESS(rv, rv);
3752 
3753  rv = query->BindStringParameter(0, aContentType);
3754  NS_ENSURE_SUCCESS(rv, rv);
3755  }
3756  }
3757 
3758  if (aExcludeLists) {
3759  // Remove only non-lists from mMediaItemTable and specific content type.
3760  //
3761  // RemoveIfNotList with removal type for media items is only implemented for
3762  // sbILibrary::ClearItems and sbILibrary::ClearItemsByType. Calling
3763  // sbLocalDatabaseLibrary::ClearInternal with aExcludeLists set to FALSE
3764  // with a content type will ALWAYS DELETE ALL ITEMS INCLUDING LISTS!
3765  //
3766  mMediaItemTable.Enumerate(sbLocalDatabaseLibrary::RemoveIfNotList, &removeType);
3767  }
3768  else {
3769  mMediaItemTable.Clear();
3770  }
3771 
3772  PRInt32 dbOk;
3773  rv = query->Execute(&dbOk);
3774  NS_ENSURE_SUCCESS(rv, rv);
3775  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
3776 
3777  // Invalidate the cached list. Clearing the library obviously influences
3778  // cached count values.
3779  rv = GetArray()->Invalidate(PR_TRUE);
3780  NS_ENSURE_SUCCESS(rv, rv);
3781 
3782  // Invalidate all simple media lists
3783  for (PRInt32 i = 0; i < lists.Count(); i++) {
3784  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
3785  do_QueryInterface(lists[i], &rv);
3786  NS_ENSURE_SUCCESS(rv, rv);
3787 
3788  // Invalidate the list's item array because its content are gone.
3789  rv = simple->Invalidate(PR_TRUE);
3790  NS_ENSURE_SUCCESS(rv, rv);
3791 
3792  // Notify the list's listeners that the list was cleared
3793  rv = simple->NotifyListenersListCleared(lists[i], aExcludeLists);
3794  NS_ENSURE_SUCCESS(rv, rv);
3795  }
3796 
3797  // Notify the library's listeners that the library was cleared
3798  NotifyListenersListCleared(SB_IMEDIALIST_CAST(this), aExcludeLists);
3799 
3800  return NS_OK;
3801 }
3802 
3803 nsresult
3804 sbLocalDatabaseLibrary::NeedsMigration(PRBool *aNeedsMigration,
3805  PRUint32 *aFromVersion,
3806  PRUint32 *aToVersion)
3807 {
3808  NS_ENSURE_ARG_POINTER(aNeedsMigration);
3809  NS_ENSURE_ARG_POINTER(aFromVersion);
3810  NS_ENSURE_ARG_POINTER(aToVersion);
3811 
3812  *aNeedsMigration = PR_FALSE;
3813  *aFromVersion = 0;
3814  *aToVersion = 0;
3815 
3816  nsresult rv = NS_ERROR_UNEXPECTED;
3817 
3818  nsCOMPtr<sbIDatabaseQuery> query;
3819  rv = MakeStandardQuery(getter_AddRefs(query));
3820  NS_ENSURE_SUCCESS(rv, rv);
3821 
3822  rv = query->AddQuery(
3823  NS_LITERAL_STRING("SELECT value FROM library_metadata WHERE name = 'version'"));
3824  NS_ENSURE_SUCCESS(rv, rv);
3825 
3826  PRInt32 dbOk;
3827  rv = query->Execute(&dbOk);
3828  NS_ENSURE_SUCCESS(rv, rv);
3829  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
3830 
3831  nsCOMPtr<sbIDatabaseResult> result;
3832  rv = query->GetResultObject(getter_AddRefs(result));
3833  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
3834 
3835  PRUint32 rowCount;
3836  rv = result->GetRowCount(&rowCount);
3837  NS_ENSURE_SUCCESS(rv, rv);
3838 
3839  if(rowCount > 0) {
3840  NS_ENSURE_TRUE(rowCount == 1, NS_ERROR_UNEXPECTED);
3841 
3842  nsAutoString strCurrentVersion;
3843  rv = result->GetRowCell(0, 0, strCurrentVersion);
3844  NS_ENSURE_SUCCESS(rv, rv);
3845 
3846  PRUint32 currentVersion = strCurrentVersion.ToInteger(&rv);
3847  NS_ENSURE_SUCCESS(rv, rv);
3848 
3849  nsCOMPtr<sbILocalDatabaseMigrationHelper> migration =
3850  do_CreateInstance("@songbirdnest.com/Songbird/Library/LocalDatabase/MigrationHelper;1", &rv);
3851  NS_ENSURE_SUCCESS(rv, rv);
3852 
3853  PRUint32 latestVersion = 0;
3854  rv = migration->GetLatestSchemaVersion(&latestVersion);
3855  NS_ENSURE_SUCCESS(rv, rv);
3856 
3857  *aFromVersion = currentVersion;
3858  *aToVersion = latestVersion;
3859 
3860  *aNeedsMigration = currentVersion < latestVersion;
3861 
3862  LOG(("++++----++++\nlatest version: %i\ncurrent schema version: %i\nneeds migration: %i\n\n",
3863  latestVersion,
3864  currentVersion,
3865  *aNeedsMigration));
3866  }
3867 
3868  return NS_OK;
3869 }
3870 
3871 nsresult
3872 sbLocalDatabaseLibrary::MigrateLibrary(PRUint32 aFromVersion,
3873  PRUint32 aToVersion)
3874 {
3875  nsresult rv = NS_ERROR_UNEXPECTED;
3876 
3877  nsCOMPtr<sbILocalDatabaseMigrationHelper> migration =
3878  do_CreateInstance("@songbirdnest.com/Songbird/Library/LocalDatabase/MigrationHelper;1", &rv);
3879  NS_ENSURE_SUCCESS(rv, rv);
3880 
3881  rv = migration->Migrate(aFromVersion, aToVersion, this);
3882  NS_ENSURE_SUCCESS(rv, rv);
3883 
3884  nsCOMPtr<sbIDatabaseEngine> dbEngine =
3885  do_GetService("@songbirdnest.com/Songbird/DatabaseEngine;1", &rv);
3886  NS_ENSURE_SUCCESS(rv, rv);
3887 
3888  rv = dbEngine->ReleaseMemory();
3889  NS_ENSURE_SUCCESS(rv, rv);
3890 
3891  return NS_OK;
3892 }
3893 
3894 nsresult
3897 {
3898  NS_ENSURE_ARG_POINTER(aSelection);
3899  NS_ENSURE_ARG_POINTER(aView);
3900 
3901  nsresult rv;
3902 
3903  nsRefPtr<sbLocalDatabaseMediaListBase> viewMediaList =
3904  aView->GetNativeMediaList();
3905 
3906  // Get the full guid array for the view. This is needed to find the
3907  // indexes for notifications
3908  nsCOMPtr<sbILocalDatabaseGUIDArray> fullArray = viewMediaList->GetArray();
3909 
3910  // Keep track of removed item's indexes for notifications
3911  sbILocalDatabaseGUIDArray* viewArray = aView->GetGUIDArray();
3912 
3913  // Figure out if we have a library or a simple media list
3914  nsCOMPtr<sbIMediaList> list =
3915  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaList*, viewMediaList), &rv);
3916  NS_ENSURE_SUCCESS(rv, rv);
3917 
3918  PRBool isLibrary;
3919  rv = this->Equals(list, &isLibrary);
3920  NS_ENSURE_SUCCESS(rv, rv);
3921 
3922  // Start building a query that we will use to delete the items from the
3923  // database. This query will be slightly different depending on if we are
3924  // deleting from the library or a list
3925 
3926  nsCOMPtr<sbIDatabaseQuery> query;
3927  rv = MakeStandardQuery(getter_AddRefs(query));
3928  NS_ENSURE_SUCCESS(rv, rv);
3929 
3930  rv = query->AddQuery(NS_LITERAL_STRING("BEGIN"));
3931  NS_ENSURE_SUCCESS(rv, rv);
3932 
3933  // Loop through the selected media items and build the in criterion for the
3934  // query as well as keep a list of the items were deleting so we can send
3935  // notifications
3936  sbMediaItemArray selectedItems;
3937  nsCOMPtr<sbIIndexedMediaItem> indexedMediaItem;
3938 
3939  // If we are a library, we can delete things by media item id
3940  if (isLibrary) {
3941  sbAutoBatchHelper batchHelper(*this);
3942 
3943  nsString deleteQuery;
3944  deleteQuery.SetLength(1048576); // This ends up being 2M of RAM.
3945  deleteQuery.Assign(NS_LITERAL_STRING("DELETE FROM media_items WHERE media_item_id IN ("));
3946 
3947  while (NS_SUCCEEDED(aSelection->GetNext(getter_AddRefs(indexedMediaItem)))) {
3948  nsCOMPtr<sbIMediaItem> item;
3949  rv = indexedMediaItem->GetMediaItem(getter_AddRefs(item));
3950  NS_ENSURE_SUCCESS(rv, rv);
3951 
3952  PRUint32 index;
3953  rv = indexedMediaItem->GetIndex(&index);
3954  NS_ENSURE_SUCCESS(rv, rv);
3955 
3956  PRUint32 mediaItemId;
3957  rv = viewArray->GetMediaItemIdByIndex(index, &mediaItemId);
3958  NS_ENSURE_SUCCESS(rv, rv);
3959 
3960  deleteQuery.AppendInt(mediaItemId);
3961  deleteQuery.AppendLiteral(",");
3962 
3963  // Finally, remember this media item so we can send notifications
3964  PRBool success = selectedItems.AppendObject(item);
3965  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
3966 
3967  // Get the index of this item in the full array
3968  PRUint64 rowid;
3969  rv = viewArray->GetRowidByIndex(index, &rowid);
3970  NS_ENSURE_SUCCESS(rv, rv);
3971 
3972  nsAutoString viewItemUID;
3973  AppendInt(viewItemUID, rowid);
3974  viewItemUID.Append('-');
3975  viewItemUID.AppendInt(mediaItemId);
3976 
3977  PRUint32 fullArrayIndex;
3978  rv = fullArray->GetIndexByViewItemUID(viewItemUID, &fullArrayIndex);
3979  NS_ENSURE_SUCCESS(rv, rv);
3980 
3982  item,
3983  fullArrayIndex);
3984 
3986  item,
3987  fullArrayIndex);
3988  }
3989 
3990  PRUint32 count = selectedItems.Count();
3991 
3992  // If we are removing from the library, we need to notify all the simple
3993  // media lists that contains the items as well as the library's listeners
3995  PRBool success = map.Init(count);
3996  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
3997 
3998  sbMediaListArray lists;
3999 
4000  rv = GetContainingLists(&selectedItems, &lists, &map);
4001  NS_ENSURE_SUCCESS(rv, rv);
4002 
4003  // Start a batch in both the library and all the lists that we are
4004  // removing from
4005  sbAutoSimpleMediaListBatchHelper listsBatchHelper(&lists);
4006  map.EnumerateRead(NotifyListsBeforeAfterItemRemoved, &mMediaItemTable);
4007 
4008  // Removing items definitely influences cached count values.
4009  rv = GetArray()->Invalidate(PR_TRUE);
4010  NS_ENSURE_SUCCESS(rv, rv);
4011 
4012  // Invalidate all of the simple media lists we notified
4013  for (PRInt32 i = 0; i < lists.Count(); i++) {
4014  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
4015  do_QueryInterface(lists[i], &rv);
4016  NS_ENSURE_SUCCESS(rv, rv);
4017 
4018  // Cached lengths are likely to be affected since we're removing items.
4019  rv = simple->Invalidate(PR_TRUE);
4020  NS_ENSURE_SUCCESS(rv, rv);
4021  }
4022 
4023  deleteQuery.Replace(deleteQuery.Length() - 1, 1, NS_LITERAL_STRING(")"));
4024 
4025  rv = query->AddQuery(deleteQuery);
4026  NS_ENSURE_SUCCESS(rv, rv);
4027 
4028  rv = query->AddQuery(NS_LITERAL_STRING("COMMIT"));
4029  NS_ENSURE_SUCCESS(rv, rv);
4030 
4031  // Now actually delete the items
4032  PRInt32 dbSuccess;
4033  rv = query->Execute(&dbSuccess);
4034  NS_ENSURE_SUCCESS(rv, rv);
4035  NS_ENSURE_TRUE(dbSuccess == 0, NS_ERROR_FAILURE);
4036  }
4037  else { // !isLibrary
4038  nsCOMPtr<sbIDatabasePreparedStatement> deletePreparedStatement;
4039  query->PrepareQuery(NS_LITERAL_STRING("DELETE FROM simple_media_lists WHERE media_item_id = ? AND ordinal = ?"), getter_AddRefs(deletePreparedStatement));
4040 
4041  PRUint32 mediaListId;
4042  rv = viewMediaList->GetMediaItemId(&mediaListId);
4043  NS_ENSURE_SUCCESS(rv, rv);
4044 
4045  sbAutoBatchHelper batchHelper(*viewMediaList);
4046 
4047  // If this is a media list, just notify the list
4048  nsCOMPtr<sbILocalDatabaseSimpleMediaList> simple =
4049  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaList*, viewMediaList), &rv);
4050  NS_ENSURE_SUCCESS(rv, rv);
4051 
4052  while (NS_SUCCEEDED(aSelection->GetNext(getter_AddRefs(indexedMediaItem)))) {
4053  nsCOMPtr<sbIMediaItem> item;
4054  rv = indexedMediaItem->GetMediaItem(getter_AddRefs(item));
4055  NS_ENSURE_SUCCESS(rv, rv);
4056 
4057  PRUint32 index;
4058  rv = indexedMediaItem->GetIndex(&index);
4059  NS_ENSURE_SUCCESS(rv, rv);
4060 
4061  // we always bind the same parameter here -- the media_item_id in this case is the media list!
4062  rv = query->AddPreparedStatement(deletePreparedStatement);
4063  NS_ENSURE_SUCCESS(rv, rv);
4064 
4065  query->BindInt32Parameter(0, mediaListId);
4066  NS_ENSURE_SUCCESS(rv, rv);
4067 
4068  nsAutoString ordinal;
4069  rv = viewArray->GetOrdinalByIndex(index, ordinal);
4070  NS_ENSURE_SUCCESS(rv, rv);
4071  query->BindStringParameter(1, ordinal);
4072  NS_ENSURE_SUCCESS(rv, rv);
4073 
4074  nsString viewItemUID;
4075  rv = viewArray->GetViewItemUIDByIndex(index, viewItemUID);
4076  NS_ENSURE_SUCCESS(rv, rv);
4077 
4078  PRUint32 fullArrayIndex;
4079  rv = fullArray->GetIndexByViewItemUID(viewItemUID, &fullArrayIndex);
4080  NS_ENSURE_SUCCESS(rv, rv);
4081 
4082  rv = simple->NotifyListenersBeforeItemRemoved(viewMediaList,
4083  item,
4084  fullArrayIndex);
4085  NS_ENSURE_SUCCESS(rv, rv);
4086 
4087  nsString guid;
4088  rv = simple->NotifyListenersAfterItemRemoved(viewMediaList,
4089  item,
4090  fullArrayIndex);
4091  NS_ENSURE_SUCCESS(rv, rv);
4092 
4093  rv = item->GetGuid(guid);
4094  NS_ENSURE_SUCCESS(rv, rv);
4095 
4096  // Remove from our cache.
4097  mMediaItemTable.Remove(guid);
4098  }
4099 
4100  // Invalidate length too since we're removing items.
4101  rv = simple->Invalidate(PR_TRUE);
4102  NS_ENSURE_SUCCESS(rv, rv);
4103 
4104  // Now actually delete the items
4105  rv = query->AddQuery(NS_LITERAL_STRING("COMMIT"));
4106  NS_ENSURE_SUCCESS(rv, rv);
4107 
4108  PRInt32 dbSuccess;
4109  rv = query->Execute(&dbSuccess);
4110  NS_ENSURE_SUCCESS(rv, rv);
4111  NS_ENSURE_TRUE(dbSuccess == 0, NS_ERROR_FAILURE);
4112 
4113  sbAutoString now((PRUint64)(PR_Now()/PR_MSEC_PER_SEC));
4114  nsCOMPtr<sbIMediaList> list =
4115  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseSimpleMediaList*, simple), &rv);
4116  rv = list->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_UPDATED), now);
4117  NS_ENSURE_SUCCESS(rv, rv);
4118  }
4119 
4120  return NS_OK;
4121 }
4122 
4123 NS_IMETHODIMP
4124 sbLocalDatabaseLibrary::ForceBeginUpdateBatch() {
4126  return NS_OK;
4127 };
4128 
4129 NS_IMETHODIMP
4130 sbLocalDatabaseLibrary::ForceEndUpdateBatch() {
4132  return NS_OK;
4133 };
4134 
4135 NS_IMETHODIMP
4137 {
4138  NS_ENSURE_ARG_POINTER(aLocalDatabaseLibrary);
4139  *aLocalDatabaseLibrary = this;
4140  return NS_OK;
4141 }
4142 
4146 NS_IMETHODIMP
4148 {
4149  // The contentSrc attribute cannot be modified on a library.
4150  return NS_ERROR_FAILURE;
4151 }
4152 
4156 NS_IMETHODIMP
4158 {
4159  aType.Assign(NS_LITERAL_STRING("library"));
4160  return NS_OK;
4161 }
4162 
4166 NS_IMETHODIMP
4168  sbIMediaItem** _retval)
4169 {
4170  NS_ENSURE_ARG_POINTER(_retval);
4171 
4172  nsresult rv;
4173 
4174  nsCOMPtr<sbIMediaItem> item;
4175  rv = GetMediaItem(aGuid, getter_AddRefs(item));
4176  NS_ENSURE_SUCCESS(rv, rv);
4177 
4178  NS_ADDREF(*_retval = item);
4179  return NS_OK;
4180 }
4181 
4185 NS_IMETHODIMP
4187  PRBool* _retval)
4188 {
4189  NS_ENSURE_ARG_POINTER(aMediaItem);
4190  NS_ENSURE_ARG_POINTER(_retval);
4191 
4192  nsAutoString guid;
4193  nsresult rv = aMediaItem->GetGuid(guid);
4194  NS_ENSURE_SUCCESS(rv, rv);
4195 
4196  PRUint32 mediaItemId;
4197  rv = GetMediaItemIdForGuid(guid, &mediaItemId);
4198  if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_AVAILABLE)) {
4199  NS_WARNING("GetMediaItemIdForGuid failed");
4200  return rv;
4201  }
4202 
4203  *_retval = NS_SUCCEEDED(rv);
4204  return NS_OK;
4205 }
4206 
4210 NS_IMETHODIMP
4212 {
4213  return AddItem(aMediaItem, nsnull);
4214 }
4215 
4216 NS_IMETHODIMP
4218  sbIMediaItem ** aNewMediaItem)
4219 {
4220  NS_ENSURE_ARG_POINTER(aMediaItem);
4221 
4223 
4224  PRBool containsCopy;
4225  nsresult rv = ContainsCopy(aMediaItem, &containsCopy);
4226  NS_ENSURE_SUCCESS(rv, rv);
4227  if (containsCopy)
4228  return NS_OK;
4229 
4230  // Remember length for notification
4231  PRUint32 length;
4232  rv = GetArray()->GetLength(&length);
4233  NS_ENSURE_SUCCESS(rv, rv);
4234 
4235  nsCOMPtr<sbIMediaItem> newMediaItem;
4236 
4237  // Import this item into this library
4238  rv = AddItemToLocalDatabase(aMediaItem, getter_AddRefs(newMediaItem));
4239  NS_ENSURE_SUCCESS(rv, rv);
4240 
4241  // Invalidate the cached list. Adding an item will
4242  // definitely invalidate the length.
4243  rv = GetArray()->Invalidate(PR_TRUE);
4244  NS_ENSURE_SUCCESS(rv, rv);
4245 
4246  // And let everyone know about it.
4247  NotifyListenersItemAdded(SB_IMEDIALIST_CAST(this), newMediaItem, length);
4248 
4249  // Also let the owning library of the original item know that
4250  // an item was copied from it into another library.
4251  nsCOMPtr<sbILibrary> itemLibrary;
4252  rv = aMediaItem->GetLibrary(getter_AddRefs(itemLibrary));
4253  NS_ENSURE_SUCCESS(rv, rv);
4254  nsCOMPtr<sbILocalDatabaseLibrary> originalLocalDatabaseLibrary;
4255  originalLocalDatabaseLibrary = do_QueryInterface(itemLibrary, &rv);
4256  NS_ENSURE_SUCCESS(rv, rv);
4257 
4258  // It's better here to continue rather than halt on error.
4259  originalLocalDatabaseLibrary->NotifyCopyListenersItemCopied(aMediaItem,
4260  newMediaItem);
4261  if (aNewMediaItem) {
4262  newMediaItem.forget(aNewMediaItem);
4263  }
4264 
4265  return NS_OK;
4266 }
4267 
4271 NS_IMETHODIMP
4273 {
4274  NS_ENSURE_ARG_POINTER(aMediaList);
4275 
4277 
4278  sbAutoBatchHelper batchHelper(*this);
4279 
4281  nsresult rv = aMediaList->EnumerateAllItems(&listener,
4283  NS_ENSURE_SUCCESS(rv, rv);
4284 
4285  return NS_OK;
4286 }
4287 
4288 NS_IMETHODIMP
4290 {
4291  return AddMediaItems(aMediaItems, nsnull, PR_FALSE);
4292 }
4296 NS_IMETHODIMP
4298  nsISimpleEnumerator* aMediaItems,
4299  sbIAddMediaItemsListener * aListener,
4300  PRBool aAsync)
4301 {
4302  NS_ENSURE_ARG_POINTER(aMediaItems);
4303 
4305 
4306  if (aAsync) {
4307  nsCOMPtr<nsIThread> target;
4308  nsresult rv = NS_GetMainThread(getter_AddRefs(target));
4309  NS_ENSURE_SUCCESS(rv, rv);
4310 
4311  nsCOMPtr<sbIAddMediaItemsListener> proxiedListener;
4312  if (aListener) {
4313  rv = do_GetProxyForObject(target,
4314  NS_GET_IID(sbIAddMediaItemsListener),
4315  aListener,
4316  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
4317  getter_AddRefs(proxiedListener));
4318  NS_ENSURE_SUCCESS(rv, rv);
4319  }
4320  nsRefPtr<sbLocalDatabaseLibraryAsyncRunner> runner =
4321  new sbLocalDatabaseLibraryAsyncRunner(this, aMediaItems, proxiedListener);
4322  NS_ENSURE_TRUE(runner, NS_ERROR_OUT_OF_MEMORY);
4323 
4324  nsCOMPtr<nsIThreadPool> threadPoolService =
4325  do_GetService("@songbirdnest.com/Songbird/ThreadPoolService;1", &rv);
4326  NS_ENSURE_SUCCESS(rv, rv);
4327 
4328  rv = threadPoolService->Dispatch(runner, NS_DISPATCH_NORMAL);
4329  NS_ENSURE_SUCCESS(rv, rv);
4330  }
4331  else {
4333 
4334  PRUint16 stepResult;
4335  nsresult rv = listener.OnEnumerationBegin(nsnull, &stepResult);
4336  NS_ENSURE_SUCCESS(rv, rv);
4337  NS_ENSURE_TRUE(stepResult == sbIMediaListEnumerationListener::CONTINUE,
4338  NS_ERROR_ABORT);
4339 
4340  sbAutoBatchHelper batchHelper(*this);
4341 
4342  PRBool hasMore;
4343  while (NS_SUCCEEDED(aMediaItems->HasMoreElements(&hasMore)) && hasMore) {
4344  nsCOMPtr<nsISupports> supports;
4345  rv = aMediaItems->GetNext(getter_AddRefs(supports));
4347 
4348  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(supports, &rv);
4350 
4351  PRUint16 stepResult;
4352  rv = listener.OnEnumeratedItem(nsnull, item, &stepResult);
4353  if (NS_FAILED(rv) ||
4355  break;
4356  }
4357  }
4358 
4359  rv = listener.OnEnumerationEnd(nsnull, NS_OK);
4360  NS_ENSURE_SUCCESS(rv, rv);
4361  }
4362  return NS_OK;
4363 }
4364 
4365 nsresult
4367  sbIAddMediaItemsListener* aListener)
4368 {
4369  NS_ENSURE_ARG_POINTER(aMediaItems);
4370  NS_ENSURE_ARG_POINTER(aListener);
4371 
4373 
4375 
4376  PRUint16 stepResult;
4377  nsresult rv = listener.OnEnumerationBegin(nsnull, &stepResult);
4378  NS_ENSURE_SUCCESS(rv, rv);
4379  NS_ENSURE_TRUE(stepResult == sbIMediaListEnumerationListener::CONTINUE,
4380  NS_ERROR_ABORT);
4381 
4382  sbAutoBatchHelper batchHelper(*this);
4383 
4384  PRBool hasMore;
4385  PRUint32 itemsProcessed = 0;
4386 
4387  while (NS_SUCCEEDED(aMediaItems->HasMoreElements(&hasMore)) && hasMore) {
4388 
4389  nsCOMPtr<nsISupports> supports;
4390  rv = aMediaItems->GetNext(getter_AddRefs(supports));
4392 
4393  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(supports, &rv);
4395 
4396  PRUint16 stepResult;
4397  rv = listener.OnEnumeratedItem(nsnull, item, &stepResult);
4398  if (NS_FAILED(rv) ||
4400  break;
4401  }
4402 
4403  ++itemsProcessed;
4404 
4405  // only send notifications every SB_ASYNC_NOTIFICATION_ITEMS items or
4406  // when it's finished if < SB_ASYNC_NOTIFICATION_ITEMS items.
4407  if (itemsProcessed % SB_ASYNC_NOTIFICATION_ITEMS == 0) {
4408  rv = aListener->OnProgress(itemsProcessed, PR_FALSE);
4409  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to call async listener.");
4410  }
4411 
4412  // Yield to other threads.
4413  PR_Sleep(0);
4414  }
4415 
4416  rv = listener.OnEnumerationEnd(nsnull, NS_OK);
4417  NS_ENSURE_SUCCESS(rv, rv);
4418 
4419  rv = aListener->OnProgress(itemsProcessed, PR_TRUE);
4420  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to call async listener.");
4421 
4422  return NS_OK;
4423 }
4424 
4428 NS_IMETHODIMP
4430 {
4431  NS_ENSURE_ARG_POINTER(aMediaItem);
4432 
4434 
4435  // Use our enumeration listener to make this use the same code as all the
4436  // other remove methods.
4438 
4439  PRUint16 stepResult;
4440  nsresult rv = listener.OnEnumerationBegin(nsnull, &stepResult);
4441  NS_ENSURE_SUCCESS(rv, rv);
4442 
4443  rv = listener.OnEnumeratedItem(nsnull, aMediaItem, &stepResult);
4444  NS_ENSURE_SUCCESS(rv, rv);
4445 
4446  rv = listener.OnEnumerationEnd(nsnull, NS_OK);
4447  NS_ENSURE_SUCCESS(rv, rv);
4448 
4449  return NS_OK;
4450 }
4451 
4455 NS_IMETHODIMP
4457 {
4459 
4460  // Hrm, we actually have to instantiate an item to notify the listeners here.
4461  // Hopefully it's cached.
4462  nsAutoString guid;
4463  nsresult rv = GetArray()->GetGuidByIndex(aIndex, guid);
4464  NS_ENSURE_SUCCESS(rv, rv);
4465 
4466  nsCOMPtr<sbIMediaItem> mediaItem;
4467  rv = GetMediaItem(guid, getter_AddRefs(mediaItem));
4468  NS_ENSURE_SUCCESS(rv, rv);
4469 
4470  rv = Remove(mediaItem);
4471  NS_ENSURE_SUCCESS(rv, rv);
4472 
4473  return NS_OK;
4474 }
4475 
4479 NS_IMETHODIMP
4481 {
4482  NS_ENSURE_ARG_POINTER(aMediaItems);
4483 
4485 
4486  sbAutoBatchHelper batchHelper(*this);
4487 
4488  // Use our enumeration listener to make this use the same code as all the
4489  // other remove methods.
4491 
4492  PRUint16 stepResult;
4493  nsresult rv = listener.OnEnumerationBegin(nsnull, &stepResult);
4494  NS_ENSURE_SUCCESS(rv, rv);
4495 
4496  PRBool hasMore;
4497  while (NS_SUCCEEDED(aMediaItems->HasMoreElements(&hasMore)) && hasMore) {
4498 
4499  nsCOMPtr<nsISupports> supports;
4500  rv = aMediaItems->GetNext(getter_AddRefs(supports));
4501  NS_ENSURE_SUCCESS(rv, rv);
4502 
4503  nsCOMPtr<sbIMediaItem> item = do_QueryInterface(supports, &rv);
4504  NS_ENSURE_SUCCESS(rv, rv);
4505 
4506  rv = listener.OnEnumeratedItem(nsnull, item, &stepResult);
4507  NS_ENSURE_SUCCESS(rv, rv);
4508  }
4509 
4510  rv = listener.OnEnumerationEnd(nsnull, NS_OK);
4511  NS_ENSURE_SUCCESS(rv, rv);
4512 
4513  return NS_OK;
4514 }
4515 
4519 NS_IMETHODIMP
4521 {
4522  return ClearInternal(PR_FALSE);
4523 }
4524 
4528 NS_IMETHODIMP
4530  sbIMediaListView** _retval)
4531 {
4532  NS_ENSURE_ARG_POINTER(_retval);
4533 
4534  nsresult rv;
4535 
4536  nsAutoString prop;
4537  prop.AssignLiteral(DEFAULT_SORT_PROPERTY);
4538 
4539  nsRefPtr<sbLocalDatabaseMediaListView>
4540  view(new sbLocalDatabaseMediaListView(this, this, prop, 0));
4541  NS_ENSURE_TRUE(view, NS_ERROR_OUT_OF_MEMORY);
4542 
4543  rv = view->Init(aState);
4544  NS_ENSURE_SUCCESS(rv, rv);
4545 
4546  NS_ADDREF(*_retval = view);
4547  return NS_OK;
4548 }
4549 
4553 NS_IMETHODIMP
4555 {
4556  aProperty.AssignLiteral(DEFAULT_SORT_PROPERTY);
4557  return NS_OK;
4558 }
4559 
4563 NS_IMETHODIMP
4564 sbLocalDatabaseLibrary::Observe(nsISupports *aSubject,
4565  const char *aTopic,
4566  const PRUnichar *aData)
4567 {
4568  if (!strcmp(aTopic, SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC)) {
4569  nsresult rv;
4570  nsCOMPtr<nsIObserverService> observerService =
4571  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
4572  if (NS_SUCCEEDED(rv)) {
4573  observerService->RemoveObserver(this, SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC);
4574  }
4575 
4576  rv = Shutdown();
4577  NS_ENSURE_SUCCESS(rv, rv);
4578  }
4579  else {
4580  NS_NOTREACHED("Observing a topic we don't care about!");
4581  }
4582 
4583  return NS_OK;
4584 }
4585 
4589 NS_IMETHODIMP
4590 sbLocalDatabaseLibrary::GetInterfaces(PRUint32* count, nsIID*** array)
4591 {
4592  return NS_CI_INTERFACE_GETTER_NAME(sbLocalDatabaseLibrary)(count, array);
4593 }
4594 
4598 NS_IMETHODIMP
4599 sbLocalDatabaseLibrary::GetHelperForLanguage(PRUint32 language,
4600  nsISupports** _retval)
4601 {
4602  *_retval = nsnull;
4603  return NS_OK;
4604 }
4605 
4609 NS_IMETHODIMP
4610 sbLocalDatabaseLibrary::GetContractID(char** aContractID)
4611 {
4612  *aContractID = nsnull;
4613  return NS_OK;
4614 }
4615 
4619 NS_IMETHODIMP
4620 sbLocalDatabaseLibrary::GetClassDescription(char** aClassDescription)
4621 {
4622  *aClassDescription = nsnull;
4623  return NS_OK;
4624 }
4625 
4629 NS_IMETHODIMP
4630 sbLocalDatabaseLibrary::GetClassID(nsCID** aClassID)
4631 {
4632  *aClassID = nsnull;
4633  return NS_OK;
4634 }
4635 
4639 NS_IMETHODIMP
4640 sbLocalDatabaseLibrary::GetImplementationLanguage(PRUint32* aImplementationLanguage)
4641 {
4642  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
4643  return NS_OK;
4644 }
4645 
4649 NS_IMETHODIMP
4650 sbLocalDatabaseLibrary::GetFlags(PRUint32 *aFlags)
4651 {
4652  *aFlags = nsIClassInfo::THREADSAFE;
4653  return NS_OK;
4654 }
4655 
4659 NS_IMETHODIMP
4660 sbLocalDatabaseLibrary::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
4661 {
4662  return NS_ERROR_NOT_AVAILABLE;
4663 }
4664 
4665 
4667 
4669  sbLocalDatabaseLibrary* aLibrary,
4671  sbIDatabaseQuery* aQuery) :
4672  mLibrary(aLibrary),
4673  mListener(aListener),
4674  mQuery(aQuery),
4675  mTimer(nsnull),
4676  mQueryCount(0)
4677 {
4678  NS_ASSERTION(aLibrary, "Null library!");
4679  NS_ASSERTION(aListener, "Null listener!");
4680  NS_ASSERTION(aQuery, "Null query!");
4681 }
4682 
4683 nsresult
4685 {
4686  mBatchHelper = new sbBatchCreateHelper(mLibrary, this);
4687  NS_ENSURE_TRUE(mBatchHelper, NS_ERROR_OUT_OF_MEMORY);
4688 
4689  return NS_OK;
4690 }
4691 
4692 nsresult
4694 {
4695  mQueryCount = aQueryCount;
4696  return NS_OK;
4697 }
4698 
4701 {
4702  NS_ASSERTION(mBatchHelper, "This shouldn't be null, did you call Init?!");
4703  return mBatchHelper;
4704 }
4705 
4706 NS_IMETHODIMP
4707 sbBatchCreateTimerCallback::Notify(nsITimer* aTimer)
4708 {
4709  NS_ENSURE_ARG_POINTER(aTimer);
4710 
4711  PRBool complete;
4712  nsresult rv = NotifyInternal(&complete);
4713  if (NS_SUCCEEDED(rv) && !complete) {
4714  // Everything looks fine, let the timer continue.
4715  return NS_OK;
4716  }
4717 
4718  // The library won't shut down until all the async timers are cleared, so
4719  // cancel this timer if we're done or if there was some kind of error.
4720 
4721  aTimer->Cancel();
4722  mLibrary->mBatchCreateTimers.RemoveObject(aTimer);
4723 
4724  // Gather the media items we added and call the listener.
4725  nsCOMPtr<nsIArray> array;
4726  if (NS_SUCCEEDED(rv)) {
4727  rv = mBatchHelper->NotifyAndGetItems(getter_AddRefs(array));
4728  }
4729 
4730  mListener->OnComplete(array, rv);
4731 
4732  // Report the earlier error, if any.
4733  NS_ENSURE_SUCCESS(rv, rv);
4734 
4735  return NS_OK;
4736 }
4737 
4738 nsresult
4740 {
4741  NS_ASSERTION(_retval, "Null retval!");
4742 
4743  nsresult rv;
4744  *_retval = PR_TRUE;
4745 
4746  // Exit early if there's nothing to do.
4747  if (!mQueryCount) {
4748  return NS_OK;
4749  }
4750 
4751  // Check to see if the query is complete.
4752  PRBool isExecuting = PR_FALSE;
4753  rv = mQuery->IsExecuting(&isExecuting);
4754  NS_ENSURE_SUCCESS(rv, rv);
4755 
4756  PRUint32 currentQuery;
4757  rv = mQuery->CurrentQuery(&currentQuery);
4758  NS_ENSURE_SUCCESS(rv, rv);
4759 
4760  NS_ASSERTION(currentQuery <= mQueryCount, "Invalid position!");
4761 
4762  // FIXME: This is wrong for several reasons. First, the mQueryCount we get is not meaningful anymore
4763  // since we have a queue that we pop as we work, so this length is always decreasing.
4764  // Second, we seem to be getting one past the end of the *real* query count in the DBEngine somehow.
4765  // Still, if we are at this point, we should basically be within a timer's length or so of the end,
4766  // (assuming incorrectly that all queries take about the same length of time.)
4767 
4768  if (currentQuery <= mQueryCount &&
4769  isExecuting) {
4770 
4771  // Notify listener of progress.
4772  // There is one query per item, plus a BEGIN and COMMIT.
4773  PRUint32 itemIndex = (currentQuery > 2) ? currentQuery - 2 : 0;
4774  mListener->OnProgress(itemIndex);
4775 
4776  *_retval = PR_FALSE;
4777  }
4778 
4779  return NS_OK;
4780 }
4781 
4782 
4783 
4786 
4788  sbBatchCreateTimerCallback* aCallback) :
4789  mLibrary(aLibrary),
4790  mCallback(aCallback),
4791  mLength(0)
4792 {
4793  NS_ASSERTION(aLibrary, "Null library!");
4794 }
4795 
4796 nsresult
4798  nsStringArray* aURIArray,
4799  nsIArray* aPropertyArrayArray)
4800 {
4801  TRACE(("sbBatchCreateHelper[0x%.8x] - InitQuery()", this));
4802  NS_ASSERTION(aQuery, "aQuery is null");
4803 
4804  mURIArray = aURIArray;
4805  mPropertiesArray = aPropertyArrayArray;
4806 
4807  nsresult rv = aQuery->AddQuery(NS_LITERAL_STRING("begin"));
4808  NS_ENSURE_SUCCESS(rv, rv);
4809 
4810  // Iterate over all items in the URI array, creating media items.
4811  PRUint32 listLength = mURIArray->Count();
4812 
4813  // Add a query to insert each new item and record the guids that were
4814  // generated for the future inserts
4815  for (PRUint32 i = 0; i < listLength; i++) {
4816  nsAutoString uriSpec;
4817  mURIArray->StringAt(i, uriSpec);
4818 
4819  nsAutoString guid;
4820  rv = mLibrary->AddNewItemQuery(aQuery,
4822  uriSpec,
4823  guid);
4824  NS_ENSURE_SUCCESS(rv, rv);
4825  TRACE(("sbBatchCreateHelper[0x%.8x] - InitQuery() -- added new itemQuery", this));
4826 
4827  nsString* success = mGuids.AppendElement(guid);
4828  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
4829  }
4830 
4831 
4832  rv = aQuery->AddQuery(NS_LITERAL_STRING("commit"));
4833  NS_ENSURE_SUCCESS(rv, rv);
4834 
4835  if (mCallback) {
4836  PRUint32 queryCount = 0;
4837  aQuery->GetQueryCount(&queryCount);
4838  mCallback->SetQueryCount(queryCount);
4839  }
4840 
4841  // Remember length for notifications
4842  rv = mLibrary->GetLength(&mLength);
4843  NS_ENSURE_SUCCESS(rv, rv);
4844 
4845  return NS_OK;
4846 }
4847 
4848 nsresult
4850 {
4851  TRACE(("sbBatchCreateTimerCallback[0x%.8x] - NotifyAndGetItems()", this ));
4852  NS_ASSERTION(_retval, "_retval is null");
4853 
4854  nsresult rv;
4855  nsCOMPtr<nsIMutableArray> array =
4856  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
4857  NS_ENSURE_SUCCESS(rv, rv);
4858 
4859  PRUint32 length = mGuids.Length();
4860  if (length > 0) {
4861  sbAutoBatchHelper batchHelper(*mLibrary);
4862 
4863  PRUint32 notifiedCount = 0;
4864  // Bulk get all the property bags for the newly added items
4865  nsTArray<const PRUnichar*> guidArray(length);
4866  for (PRUint32 i = 0; i < length; i++) {
4867  const PRUnichar** addedPtr = guidArray.AppendElement(mGuids[i].get());
4868  NS_ENSURE_TRUE(addedPtr, NS_ERROR_OUT_OF_MEMORY);
4869  }
4870 
4871  PRUint32 count = 0;
4872  sbILocalDatabaseResourcePropertyBag** bags = nsnull;
4873  rv = mLibrary->mPropertyCache->GetProperties(guidArray.Elements(),
4874  length,
4875  &count,
4876  &bags);
4877  NS_ENSURE_SUCCESS(rv, rv);
4878 
4879  for (PRUint32 i = 0; i < length; i++) {
4880  // We know the GUID and the type of these new media items so preload
4881  // the cache with this information
4882  nsAutoPtr<sbLocalDatabaseLibrary::sbMediaItemInfo>
4883  newItemInfo(new sbLocalDatabaseLibrary::sbMediaItemInfo(PR_TRUE));
4884  NS_ENSURE_TRUE(newItemInfo, NS_ERROR_OUT_OF_MEMORY);
4885 
4886  NS_ASSERTION(!mLibrary->mMediaItemTable.Get(mGuids[i], nsnull),
4887  "Guid already exists!");
4888 
4889  PRBool success = mLibrary->mMediaItemTable.Put(mGuids[i], newItemInfo);
4890  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
4891 
4892  nsCOMPtr<sbIMediaItem> mediaItem;
4893  rv = mLibrary->GetMediaItem(mGuids[i], getter_AddRefs(mediaItem));
4894  NS_ENSURE_SUCCESS(rv, rv);
4895 
4896  // Set the new media item with the property bag we got earlier
4897  nsCOMPtr<sbILocalDatabaseMediaItem> ldbmi =
4898  do_QueryInterface(mediaItem, &rv);
4899  NS_ENSURE_SUCCESS(rv, rv);
4900 
4901  NS_ENSURE_TRUE(bags[i], NS_ERROR_NULL_POINTER);
4902  rv = ldbmi->SetPropertyBag(bags[i]);
4903  NS_ENSURE_SUCCESS(rv, rv);
4904 
4905  // Now that we have a media item, set up the initial
4906  // properties
4907  nsCOMPtr<sbIPropertyArray> properties = nsnull;
4908  if (mPropertiesArray) {
4909  properties = do_QueryElementAt(mPropertiesArray, i, &rv);
4910  NS_ENSURE_SUCCESS(rv, rv);
4911  } else {
4912  // we still need to pass in an empty property array so we can add missing
4913  // properties as well
4914  properties =
4915  do_CreateInstance("@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1", &rv);
4916  NS_ENSURE_SUCCESS(rv, rv);
4917  }
4918  rv = mLibrary->SetDefaultItemProperties(mediaItem, properties, newItemInfo);
4919  NS_ENSURE_SUCCESS(rv, rv);
4920 
4921  newItemInfo.forget();
4922 
4923  rv = array->AppendElement(mediaItem, PR_FALSE);
4924  NS_ENSURE_SUCCESS(rv, rv);
4925 
4926  mLibrary->NotifyListenersItemAdded(SB_IMEDIALIST_CAST(mLibrary),
4927  mediaItem,
4928  mLength + notifiedCount);
4929  notifiedCount++;
4930  }
4931  // Items added, count must be invalidated too.
4932  rv = mLibrary->GetArray()->Invalidate(PR_TRUE);
4933  NS_ENSURE_SUCCESS(rv, rv);
4934  NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, bags);
4935  }
4936 
4937  NS_ADDREF(*_retval = array);
4938  return NS_OK;
4939 }
4940 
4941 
4942 // library statistics implementation
4943 nsresult
4944 sbLocalDatabaseLibrary::InitializeLibraryStatistics() {
4945  nsresult rv = NS_OK;
4946 
4947  // make the SUM query
4948  rv = MakeStandardQuery(getter_AddRefs(mStatisticsSumQuery));
4949  NS_ENSURE_SUCCESS(rv, rv);
4950  // what's the SQL?
4951  NS_NAMED_MULTILINE_LITERAL_STRING(sumQuery,
4952  NS_LL("SELECT value1.obj, SUM(value2.obj)")
4953  NS_LL(" FROM properties AS property1")
4954  NS_LL(" INNER JOIN resource_properties AS value1")
4955  NS_LL(" ON value1.property_id = property1.property_id")
4956  NS_LL(" INNER JOIN resource_properties AS value2")
4957  NS_LL(" ON value1.media_item_id = value2.media_item_id")
4958  NS_LL(" INNER JOIN properties AS property2")
4959  NS_LL(" ON value2.property_id = property2.property_id")
4960  NS_LL(" WHERE property1.property_name = ?")
4961  NS_LL(" AND property2.property_name = ?")
4962  NS_LL(" GROUP BY value1.obj")
4963  NS_LL(" ORDER BY ? * SUM(value2.obj)")
4964  NS_LL(" LIMIT ?;"));
4965  // add the SQL to the query object
4966  rv = mStatisticsSumQuery->PrepareQuery(sumQuery, getter_AddRefs(mStatisticsSumPreparedStatement));
4967  NS_ENSURE_SUCCESS(rv, rv);
4968 
4969  return NS_OK;
4970 }
4971 
4972 NS_IMETHODIMP
4973 sbLocalDatabaseLibrary::CollectDistinctValues(const nsAString & aProperty,
4974  PRUint32 aCollectionMethod,
4975  const nsAString & aOtherProperty,
4976  PRBool aAscending,
4977  PRUint32 aMaxResults,
4978  nsIArray **_retval)
4979 {
4980  NS_ENSURE_ARG_POINTER(_retval);
4981 
4982  // main thread only, thanks!
4983  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_FAILURE);
4984 
4985  nsresult rv = NS_OK;
4986 
4987  nsCOMPtr<sbIDatabaseQuery> query;
4988 
4989  switch(aCollectionMethod) {
4990  case COLLECT_SUM:
4991  query = mStatisticsSumQuery;
4992  query->AddPreparedStatement(mStatisticsSumPreparedStatement);
4993  query->BindStringParameter(0, aProperty);
4994  query->BindStringParameter(1, aOtherProperty);
4995  query->BindInt32Parameter(2, aAscending ? 1 : -1);
4996  query->BindInt32Parameter(3, aMaxResults);
4997  break;
4998  default:
4999  return NS_ERROR_INVALID_ARG;
5000  }
5001 
5002  PRInt32 dbOk = 0;
5003  rv = query->Execute(&dbOk);
5004  NS_ENSURE_SUCCESS(rv, rv);
5005  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
5006 
5007  nsCOMPtr<sbIDatabaseResult> result;
5008  rv = query->GetResultObject(getter_AddRefs(result));
5009  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
5010 
5011  PRUint32 rowCount = 0;
5012  rv = result->GetRowCount(&rowCount);
5013  NS_ENSURE_SUCCESS(rv, rv);
5014 
5015  nsCOMPtr<nsIMutableArray> array =
5016  do_CreateInstance("@mozilla.org/array;1", &rv);
5017  NS_ENSURE_SUCCESS(rv, rv);
5018 
5019  for (PRUint32 i=0; i<rowCount; i++) {
5020  // create a variant to hold this string value
5021  nsCOMPtr<nsIWritableVariant> variant =
5022  do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
5023  NS_ENSURE_SUCCESS(rv, rv);
5024 
5025  // get the value
5026  nsString name;
5027  rv = result->GetRowCell(i, 0, name);
5028  NS_ENSURE_SUCCESS(rv, rv);
5029 
5030  variant->SetAsAString(name);
5031  array->AppendElement(variant, PR_FALSE);
5032  }
5033 
5034  return CallQueryInterface(array, _retval);
5035 }
5036 
5037 nsresult
5038 sbLocalDatabaseLibrary::NeedsReindexCollations(PRBool *aNeedsReindexCollations) {
5039 
5040  nsresult rv;
5041 
5042  // See if we've specifically been asked to reindex the collations
5043  nsCOMPtr<nsIPrefBranch> prefBranch =
5044  do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
5045  if (NS_SUCCEEDED(rv)) {
5046  PRBool prefValue;
5048  key.Append(NS_ConvertUTF16toUTF8(mDatabaseGuid));
5050  rv = prefBranch->GetBoolPref(key.get(), &prefValue);
5051  if (NS_SUCCEEDED(rv)) {
5052  if (prefValue) {
5053  *aNeedsReindexCollations = PR_TRUE;
5054  return NS_OK;
5055  }
5056  }
5057  }
5058 
5059  // Read the identifier for the collation locale
5060  nsCOMPtr<sbIDatabaseQuery> query;
5061  rv = MakeStandardQuery(getter_AddRefs(query));
5062  NS_ENSURE_SUCCESS(rv, rv);
5063 
5064  rv = query->AddQuery(NS_LITERAL_STRING("SELECT value FROM library_metadata WHERE name = 'collation-locale'"));
5065  NS_ENSURE_SUCCESS(rv, rv);
5066 
5067  PRInt32 dbOk = 0;
5068  rv = query->Execute(&dbOk);
5069  NS_ENSURE_SUCCESS(rv, rv);
5070  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
5071 
5072  nsCOMPtr<sbIDatabaseResult> result;
5073  rv = query->GetResultObject(getter_AddRefs(result));
5074  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
5075 
5076  PRUint32 rowCount = 0;
5077  rv = result->GetRowCount(&rowCount);
5078  NS_ENSURE_SUCCESS(rv, rv);
5079 
5080  // If the collation-locale has not been set yet, cause a reindex
5081  if (rowCount == 0) {
5082  *aNeedsReindexCollations = PR_TRUE;
5083  return NS_OK;
5084  }
5085 
5086  NS_ENSURE_TRUE(rowCount == 1, NS_ERROR_UNEXPECTED);
5087 
5088  nsAutoString previousCollationLocale;
5089  rv = result->GetRowCell(0, 0, previousCollationLocale);
5090  NS_ENSURE_SUCCESS(rv, rv);
5091 
5092  nsCOMPtr<sbIDatabaseEngine> dbEngine =
5093  do_GetService("@songbirdnest.com/Songbird/DatabaseEngine;1", &rv);
5094  NS_ENSURE_SUCCESS(rv, rv);
5095 
5096  nsString currentCollationLocale;
5097  dbEngine->GetLocaleCollationID(currentCollationLocale);
5098  NS_ENSURE_SUCCESS(rv, rv);
5099 
5100  *aNeedsReindexCollations =
5101  !currentCollationLocale.Equals(previousCollationLocale);
5102 
5103  return NS_OK;
5104 }
5105 
5106 nsresult
5107 sbLocalDatabaseLibrary::ReindexCollations() {
5108 
5109  nsCOMPtr<sbIDatabaseQuery> query;
5110  nsresult rv = MakeStandardQuery(getter_AddRefs(query));
5111  NS_ENSURE_SUCCESS(rv, rv);
5112 
5113  // Reindex all library_collate sequences
5114 
5115  nsAutoString queryStr;
5116  queryStr = NS_LITERAL_STRING("REINDEX 'library_collate'");
5117 
5118  rv = query->AddQuery(queryStr);
5119  NS_ENSURE_SUCCESS(rv, rv);
5120 
5121  // Remove the flag that forces reindexing for this library
5122 
5123  nsCOMPtr<nsIPrefBranch> prefBranch =
5124  do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
5125  if (NS_SUCCEEDED(rv)) {
5126  PRBool prefValue;
5128  key.Append(NS_ConvertUTF16toUTF8(mDatabaseGuid));
5130  rv = prefBranch->PrefHasUserValue(key.get(), &prefValue);
5131  NS_ENSURE_SUCCESS(rv, rv);
5132  if (prefValue) {
5133  rv = prefBranch->ClearUserPref(key.get());
5134  NS_ENSURE_SUCCESS(rv, rv);
5135  }
5136  }
5137 
5138  // Write the new collation locale identifier in the db
5139 
5140  nsCOMPtr<sbIDatabaseEngine> dbEngine =
5141  do_GetService("@songbirdnest.com/Songbird/DatabaseEngine;1", &rv);
5142  NS_ENSURE_SUCCESS(rv, rv);
5143 
5144  nsString currentCollationLocale;
5145  dbEngine->GetLocaleCollationID(currentCollationLocale);
5146  NS_ENSURE_SUCCESS(rv, rv);
5147 
5148  queryStr =
5149  NS_LITERAL_STRING("INSERT OR REPLACE INTO library_metadata VALUES('collation-locale', '");
5150 
5151  queryStr += currentCollationLocale;
5152 
5153  queryStr += NS_LITERAL_STRING("')");
5154 
5155  rv = query->AddQuery(queryStr);
5156  NS_ENSURE_SUCCESS(rv, rv);
5157 
5158  PRInt32 dbOk = 0;
5159  rv = query->Execute(&dbOk);
5160  NS_ENSURE_SUCCESS(rv, rv);
5161  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
5162 
5163  return NS_OK;
5164 }
5165 
5166 
GeneratorThread currentThread
nsresult GetOriginProperties(sbIMediaItem *aSourceItem, sbIMutablePropertyArray *aProperties)
nsDataHashtable< nsStringHashKey, PRUint32 > sbListItemIndexMap
NS_IMETHOD IndexOf(sbIMediaItem *aMediaItem, PRUint32 aStartFrom, PRUint32 *_retval)
sbILocalDatabaseGUIDArray * GetGUIDArray()
NS_IMETHOD AddAll(sbIMediaList *aMediaList)
return NS_OK
#define SB_PROPERTY_MEDIALISTNAME
static nsresult LinkCopy(sbIMediaItem *aOriginal, sbIMediaItem *aCopy)
Links a copy to its original. It will take into account the libraries the items belong to...
#define SONGBIRD_DATABASEQUERY_CONTRACTID
Definition: DatabaseQuery.h:63
static nsCOMPtr< nsIObserverService > observerService
Definition: UnityProxy.cpp:6
PLDHashOperator PR_CALLBACK CopyInterfaceHashtableEntry(typename V::KeyType aKey, T *aData, void *aUserData)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
static nsresult GetItemInLibrary(sbIMediaItem *aItem, sbILibrary *aLibrary, sbIMediaItem **_retval)
#define SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC
#define SB_IMEDIAITEM_CAST(_ptr)
#define SB_PROPERTY_ORIGINURL
readonly attribute sbIDevice device
Reference to the device that this library is stored on.
Definition: sbILibrary.idl:87
void NotifyListenersAfterItemRemoved(sbIMediaList *aList, sbIMediaItem *aItem, PRUint32 aIndex)
Notifies all listeners that an item has been removed from the list.
Interface used to enumerate the items in a media list.
void NotifyListenersBeforeListCleared(sbIMediaList *aList, PRBool aExcludeLists)
Notifies all listeners before the list has been cleared.
menuItem id
Definition: FeedWriter.js:971
NS_IMETHOD AddItem(sbIMediaItem *aMediaItem, sbIMediaItem **aNewMediaItem)
#define BATCHCREATE_NOTIFICATION_INTERVAL_MS
inArray array
nsresult RemoveSelected(nsISimpleEnumerator *aSelection, sbLocalDatabaseMediaListView *aView)
Bulk removes the selected items specified in aSelection from the view aView.
const NS_PREFSERVICE_CONTRACTID
nsClassHashtable< nsISupportsHashKey, sbMediaItemArray > sbMediaItemToListsMap
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
attribute AString contentType
Content type of the content of the media item, typically a mime type (should this be renamed...
#define SB_PROPERTY_ISCONTENTREADONLY
NS_IMETHOD AddMediaItems(nsISimpleEnumerator *aMediaItems, sbIAddMediaItemsListener *aListener, PRBool aAsync)
NS_DECL_ISUPPORTS NS_DECL_NSITIMERCALLBACK sbBatchCreateTimerCallback(sbLocalDatabaseLibrary *aLibrary, sbIBatchCreateMediaItemsListener *aListener, sbIDatabaseQuery *aQuery)
#define DEFAULT_MEDIALIST_CACHE_SIZE
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
Extract statistics from a media library.
static nsresult FindCopiesByID(sbIMediaItem *aMediaItem, sbIMediaList *aList, nsIMutableArray *aCopies)
const NS_ERROR_ABORT
NS_IMETHOD GetDefaultSortProperty(nsAString &aProperty)
#define DEFAULT_MEDIAITEM_CACHE_SIZE
void NotifyListenersItemAdded(sbIMediaList *aList, sbIMediaItem *aItem, PRUint32 aIndex)
Notifies all listeners that an item has been added to the list.
Factory for new library instances.
#define TRACE(args)
#define SB_PROPERTY_HIDDEN
nsresult NotifyAndGetItems(nsIArray **_retval)
Factory for new media list instances.
#define SB_IMEDIALIST_CAST(_ptr)
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
Interface used to listen for items being copied from a library.
nsISupportsWeakReference
#define INVALID_COLLATION_INDEX_PREF_PREFIX
NS_IMPL_CI_INTERFACE_GETTER8(sbLocalDatabaseSmartMediaList, nsIClassInfo, nsISupportsWeakReference, sbILibraryResource, sbILocalDatabaseSmartMediaList, sbIMediaItem, sbIMediaList, sbIMediaListListener, nsIObserver)
nsresult do_GetProxyForObject(nsIEventTarget *aTarget, REFNSIID aIID, nsISupports *aObj, PRInt32 aProxyType, void **aProxyObject)
#define SB_MEDIALIST_LOCK_FULLARRAY_AND_ENSURE_MUTABLE()
void SetArray(sbILocalDatabaseGUIDArray *aArray)
Songbird Variant Utility Definitions.
const PRUint32 REMOVE_VIDEO_TYPE_ONLY
var ioService
var language
Definition: Info.js:44
A brief description of the contents of this interface.
static PRBool IsDeviceLibrary(sbILibrary *aLibrary)
NS_IMPL_THREADSAFE_ISUPPORTS1(sbLocalDatabaseLibraryAsyncRunner, nsIRunnable)
NS_IMPL_THREADSAFE_RELEASE(sbRequestItem)
A distinct view on a given media list.
Simple class to make sure we notify listeners that a batch operation has completed every time they ar...
void NotifyListenersItemUpdated(in sbIMediaItem aItem, in sbIPropertyArray aProperties)
void NotifyListenersItemUpdated(sbIMediaList *aList, sbIMediaItem *aItem, sbIPropertyArray *aProperties)
Notifies all listeners that an item has been updated.
NS_IMPL_THREADSAFE_ADDREF(sbRequestItem)
readonly attribute AString guid
The guid of this resource.
const unsigned long REQUEST_READ
Definition: sbIDevice.idl:258
readonly attribute AString type
The type of media list (eg "simple")
#define SB_PROPERTY_STORAGEGUID
#define LOG(args)
nsresult NotifyInternal(PRBool *_retval)
#define INVALID_COLLATION_INDEX_PREF_SUFFIX
readonly attribute unsigned long length
Returns the length of the list.
NS_IMETHOD AddSome(nsISimpleEnumerator *aMediaItems)
Saved state of a media list view.
#define SB_SQLBUILDER_SELECT_CONTRACTID
var count
Definition: test_bug7406.js:32
boolean equals(in sbILibraryResource aOtherLibraryResource)
Tests the equality of two library resources.
void NotifyListenersBeforeItemRemoved(sbIMediaList *aList, sbIMediaItem *aItem, PRUint32 aIndex)
Notifies all listeners that an item is about to be removed from the list.
#define SB_PROPERTY_OUTERGUID
NS_IMETHOD Add(sbIMediaItem *aMediaItem)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
const sbCreateProxiedComponent do_ProxiedGetService(const nsCID &aCID, nsresult *error=0)
#define ANALYZE_COUNT_PREF
#define SB_PROPERTY_UPDATED
#define SB_PROPERTY_CONTENTTYPE
NS_IMETHOD CreateView(sbIMediaListViewState *aState, sbIMediaListView **_retval)
const unsigned short ENUMERATIONTYPE_SNAPSHOT
This flag means that the list being enumerated is a copy that may become out of date.
const nsIChannel
NS_IMETHOD Remove(sbIMediaItem *aMediaItem)
General interface to data resources.
attribute AString name
The name of the media list.
#define SB_CONTINUE_IF_FAILED(_rv)
void NotifyListenersListCleared(sbIMediaList *aList, PRBool aExcludeLists)
Notifies all listeners that the list has been cleared.
#define SB_ILIBRESOURCE_CAST(_ptr)
grep callback
NS_IMETHOD GetLength(PRUint32 *aLength)
already_AddRefed< sbLocalDatabaseLibrary > GetNativeLibrary()
const PRUint32 REMOVE_AUDIO_TYPE_ONLY
#define SB_PROPERTY_DEVICE_LIBRARY_GUID
NS_IMETHOD RemoveByIndex(PRUint32 aIndex)
NS_IMETHOD Contains(sbIMediaItem *aMediaItem, PRBool *_retval)
#define MAX_IN_LENGTH
Media library abstraction.
Definition: sbILibrary.idl:82
static nsresult FindOriginalsByID(sbIMediaItem *aMediaItem, sbIMediaList *aList, nsIMutableArray *aCopies)
NS_IMETHOD SetContentSrc(nsIURI *aContentSrc)
#define LOG_SUBMESSAGE_SPACE
sbILocalDatabaseGUIDArray * GetArray()
static nsresult FindItemsWithSameURL(sbIMediaItem *aMediaItem, sbIMediaList *aMediaList, nsIMutableArray *aCopies)
#define DEFAULT_ANALYZE_COUNT_LIMIT
nsresult SetQueryCount(PRUint32 aQueryCount)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
Callback for batch media create items.
Definition: sbILibrary.idl:50
nsresult AddSomeAsyncInternal(nsISimpleEnumerator *aMediaItems, sbIAddMediaItemsListener *aListener)
var uri
Definition: FeedWriter.js:1135
function url(spec)
StringArrayEnumerator prototype hasMore
nsresult GetLengthCache(sbILocalDatabaseGUIDArrayLengthCache **aLengthCache)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
countRef value
Definition: FeedWriter.js:1423
static void AppendInt(nsAString &str, PRInt64 val)
#define DEFAULT_SORT_PROPERTY
already_AddRefed< sbLocalDatabaseMediaListBase > GetNativeMediaList()
An object responsible for executing SQL queries on the database.
sbBatchCreateHelper * BatchHelper()
#define SB_ASYNC_NOTIFICATION_ITEMS
#define SB_PROPERTY_LISTTYPE
#define SHUTDOWN_ASYNC_GRANULARITY_MS
readonly attribute unsigned long mediaItemId
NS_IMETHOD RemoveSome(nsISimpleEnumerator *aMediaItems)
function now()
Interface that defines a single item of media in the system.
nsCOMArray< sbIMediaList > sbMediaListArray
nsCOMArray< sbIMediaItem > sbMediaItemArray
static void GetNowString(nsAString &_retval)
Make a string of the current time in milliseconds.
#define SB_PROPERTY_CUSTOMTYPE
restoreWindow aState
#define SB_MEDIAITEM_TYPEID
#define SB_PROPERTY_CONTENTURL
const PRUint32 REMOVE_ALL_TYPES
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSICLASSINFO NS_DECL_SBILIBRARYRESOURCE NS_DECL_SBILOCALDATABASEMEDIAITEM NS_DECL_SBIMEDIAITEM sbLocalDatabaseMediaItem()
#define DEFAULT_FETCH_SIZE
_getSelectedPageStyle s i
readonly attribute sbILibraryFactory factory
Holds the factory that created this library.
Definition: sbILibrary.idl:102
NS_IMPL_ISUPPORTS1(sbLibraryRemovingEnumerationListener, sbIMediaListEnumerationListener) NS_IMETHODIMP sbLibraryRemovingEnumerationListener
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
nsITimerCallback
#define SB_LOCALDATABASE_DYNAMICMEDIALISTFACTORY_CONTRACTID
NS_IMPL_ISUPPORTS_INHERITED5(sbLocalDatabaseLibrary, sbLocalDatabaseMediaListBase, nsIClassInfo, nsIObserver, sbILibrary, sbILocalDatabaseLibrary, sbILibraryStatistics) NS_IMPL_CI_INTERFACE_GETTER8(sbLocalDatabaseLibrary
_updateTextAndScrollDataForFrame aData
nsresult InitQuery(sbIDatabaseQuery *aQuery, nsStringArray *aURIArray, nsIArray *aPropertyArrayArray)
#define NS_UUID_GENERATOR_CONTRACTID
nsresult GetFilteredPropertiesForNewItem(sbIPropertyArray *aProperties, sbIPropertyArray **_retval)
NS_DECL_ISUPPORTS sbLocalDatabaseLibraryAsyncRunner(sbLocalDatabaseLibrary *aLocalDatabaseLibrary, nsISimpleEnumerator *aMediaItems, sbIAddMediaItemsListener *aListener)
NS_IMETHOD GetItemByGuid(const nsAString &aGuid, sbIMediaItem **_retval)
NS_IMETHOD GetType(nsAString &aType)
Songbird Database Object Definition.