sbLocalDatabaseDiffingService.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
26 
27 #include <vector>
28 #include <algorithm>
29 
30 #include "sbLocalDatabaseCID.h"
31 
32 #include <nsIAppStartupNotifier.h>
33 #include <nsICategoryManager.h>
34 #include <nsIClassInfoImpl.h>
35 #include <nsIURI.h>
36 #include <nsIMutableArray.h>
37 #include <nsIObserverService.h>
38 #include <nsIProgrammingLanguage.h>
39 #include <nsIStringEnumerator.h>
40 
41 #include <sbIMediaListView.h>
42 
43 #include <nsArrayUtils.h>
44 #include <nsComponentManagerUtils.h>
45 #include <nsDataHashtable.h>
46 #include <nsHashKeys.h>
47 #include <nsTHashtable.h>
48 #include <nsMemory.h>
49 #include <nsServiceManagerUtils.h>
50 #include <nsTArray.h>
51 #include <nsXPCOMCID.h>
52 
53 #include <sbIndex.h>
54 #include <sbLibraryChangeset.h>
55 #include <sbLibraryUtils.h>
56 #include <sbPropertiesCID.h>
57 #include <sbStandardProperties.h>
58 #include <sbStringUtils.h>
59 
60 static nsID const NULL_GUID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0 } };
61 
62 #ifdef PR_LOGGING
63  static PRLogModuleInfo* gsbLocalDatabaseDiffingLog = nsnull;
64 # define TRACE(args) \
65  PR_BEGIN_MACRO \
66  if (!gsbLocalDatabaseDiffingLog) \
67  gsbLocalDatabaseDiffingLog = PR_NewLogModule("sbLocalDatabaseDiffingService"); \
68  PR_LOG(gsbLocalDatabaseDiffingLog, PR_LOG_DEBUG, args); \
69  PR_END_MACRO
70 # define LOG(args) \
71  PR_BEGIN_MACRO \
72  if (!gsbLocalDatabaseDiffingLog) \
73  gsbLocalDatabaseDiffingLog = PR_NewLogModule("sbLocalDatabaseDiffingService"); \
74  PR_LOG(gsbLocalDatabaseDiffingLog, PR_LOG_WARN, args); \
75  PR_END_MACRO
76 void LogMediaItem(char const * aMessage, sbIMediaItem * aMediaItem)
77 {
78  nsCOMPtr<nsIURI> uri;
79  aMediaItem->GetContentSrc(getter_AddRefs(uri));
80  nsString sourceGUID;
81  aMediaItem->GetGuid(sourceGUID);
82  nsString sourceOriginGUID;
83  aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID),
84  sourceOriginGUID);
85  nsCString spec;
86  if (uri) {
87  uri->GetSpec(spec);
88  }
89  LOG(("%s\n URL=%s\n ID=%s\n Origin=%s",
90  aMessage,
91  spec.BeginReading(),
92  NS_LossyConvertUTF16toASCII(sourceGUID).BeginReading(),
93  NS_LossyConvertUTF16toASCII(sourceOriginGUID).BeginReading()));
94 }
95 #else
96 # define TRACE(args) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
97 # define LOG(args) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
98 inline
99 void LogMediaItem(char const *, sbIMediaItem *)
100 {
101 
102 }
103 #endif
104 
105 nsString sbGUIDToString(nsID const & aID)
106 {
107  char guidBuffer[NSID_LENGTH];
108  aID.ToProvidedString(guidBuffer);
109  guidBuffer[NSID_LENGTH - 2] = '\0';
110  nsString retval;
111  retval.AssignLiteral(guidBuffer + 1);
112  return retval;
113 }
114 
116 {
117  nsID returnedID;
118  nsString itemGUID;
119  nsresult rv = aItem->GetGuid(itemGUID);
120  NS_ENSURE_SUCCESS(rv, NULL_GUID);
121  PRBool success =
122  returnedID.Parse(NS_LossyConvertUTF16toASCII(itemGUID).BeginReading());
123  NS_ENSURE_TRUE(success, NULL_GUID);
124 
125  return returnedID;
126 }
127 
128 nsID GetGUIDProperty(sbIMediaItem * aItem, nsAString const & aProperty)
129 {
130  NS_ASSERTION(aItem, "aItem is null");
131  NS_ASSERTION(!aProperty.IsEmpty(), "aProperty is empty");
132  nsID returnedID;
133  nsString guid;
134  nsresult rv = aItem->GetProperty(aProperty, guid);
135  if (rv == NS_ERROR_NOT_AVAILABLE || guid.IsEmpty()) {
136  return NULL_GUID;
137  }
138  NS_ENSURE_SUCCESS(rv, NULL_GUID);
139  PRBool success =
140  returnedID.Parse(NS_LossyConvertUTF16toASCII(guid).BeginReading());
141  NS_ENSURE_TRUE(success, NULL_GUID);
142 
143  return returnedID;
144 }
145 
152 {
153 // Public types
154 public:
155 
156  struct ItemInfo
157  {
158  static bool CompareID(ItemInfo const & aLeft, ItemInfo const & aRight)
159  {
160  return aLeft.mID.Equals(aRight.mID) != PR_FALSE;
161  }
162  enum Action
163  {
169  };
173  mPosition(0)
174  {
175  }
176  nsID mID;
177  nsID mOriginID;
179  PRUint32 mPosition;
180  };
181  static bool lessThan(nsID const & aLeftID, nsID const & aRightID)
182  {
183  if (aLeftID.m0 < aRightID.m0) {
184  return true;
185  }
186  else if (aLeftID.m0 > aRightID.m0) {
187  return false;
188  }
189 
190  if (aLeftID.m1 < aRightID.m1) {
191  return true;
192  }
193  else if (aLeftID.m1 > aRightID.m1) {
194  return false;
195  }
196 
197  if (aLeftID.m2 < aRightID.m2) {
198  return true;
199  }
200  else if (aLeftID.m2 > aRightID.m2) {
201  return false;
202  }
203 
204  if (aLeftID.m2 < aRightID.m2) {
205  for (PRUint32 index = 0; index < 8; ++index) {
206  if (aLeftID.m3[index] < aRightID.m3[index]) {
207  return true;
208  }
209  else if (aLeftID.m3[index] > aRightID.m3[index]) {
210  return false;
211  }
212  }
213  }
214  return false;
215  }
216  class IDCompare;
218  // We use a vector to conserve memory, since stl based sets and maps are
219  // tree based. Sorting the vector gives us comparable speed without the
220  // overhead
221  typedef std::vector<ItemInfo> ItemInfos;
222  typedef nsID const & (*IDExtractorFunc)(ItemInfos::const_iterator);
225  class IDCompare
226  {
227  public:
228  bool operator()(ItemInfos::const_iterator aLeft, nsID const & aID) const
229  {
230  return lessThan(aLeft->mID, aID);
231  }
232  bool operator()(nsID const & aID, ItemInfos::const_iterator aRight) const
233  {
234  return lessThan(aID, aRight->mID);
235  }
236  bool operator()(ItemInfos::const_iterator aLeft,
237  ItemInfos::const_iterator aRight) const
238  {
239  return lessThan(aLeft->mID, aRight->mID);
240  }
241  bool operator()(ItemInfos::iterator aLeft, nsID const & aID) const
242  {
243  return lessThan(aLeft->mID, aID);
244  }
245  bool operator()(nsID const & aID, ItemInfos::iterator aRight) const
246  {
247  return lessThan(aID, aRight->mID);
248  }
249  bool operator()(ItemInfos::iterator aLeft,
250  ItemInfos::iterator aRight) const
251  {
252  return lessThan(aLeft->mID, aRight->mID);
253  }
254  };
256  {
257  public:
258  bool operator()(ItemInfos::const_iterator aLeft, nsID const & aID) const
259  {
260  return lessThan(aLeft->mOriginID, aID);
261  }
262  bool operator()(nsID const & aID, ItemInfos::const_iterator aRight) const
263  {
264  return lessThan(aID, aRight->mOriginID);
265  }
266  bool operator()(ItemInfos::const_iterator aLeft,
267  ItemInfos::const_iterator aRight) const
268  {
269  return lessThan(aLeft->mOriginID, aRight->mOriginID);
270  }
271 
272  bool operator()(ItemInfos::iterator aLeft, nsID const & aID) const
273  {
274  return lessThan(aLeft->mOriginID, aID);
275  }
276  bool operator()(nsID const & aID, ItemInfos::iterator aRight) const
277  {
278  return lessThan(aID, aRight->mOriginID);
279  }
280  bool operator()(ItemInfos::iterator aLeft,
281  ItemInfos::iterator aRight) const
282  {
283  return lessThan(aLeft->mOriginID, aRight->mOriginID);
284  }
285  };
286 
287 public:
289  NS_DECL_SBIMEDIALISTENUMERATIONLISTENER
290 
291  enum Index {
295  };
296  typedef ItemInfos::const_iterator const_iterator;
297  typedef ItemInfos::iterator iterator;
302 
304 
305  void Sort()
306  {
307  mIDIndex.Build(mItemInfos.begin(),
308  mItemInfos.end());
309  mOriginIndex.Build(mItemInfos.begin(),
310  mItemInfos.end());
311  }
312  IDIterator FindByID(nsID const & aID) {
313  return mIDIndex.find(aID);
314  }
315  ConstIDIterator FindByID(nsID const & aID) const {
316  return mIDIndex.find(aID);
317  }
318  OriginIterator FindByOrigin(nsID const & aID) {
319  return mOriginIndex.find(aID);
320  }
321  ConstOriginIterator FindByOrigin(nsID const & aID) const {
322  return mOriginIndex.find(aID);
323  }
325  {
326  return mItemInfos.begin();
327  }
329  {
330  return mItemInfos.begin();
331  }
333  {
334  return mItemInfos.end();
335  }
337  {
338  return mItemInfos.end();
339  }
340 
342  {
343  return mIDIndex.begin();
344  }
346  {
347  return mIDIndex.begin();
348  }
350  {
351  return mIDIndex.end();
352  }
354  {
355  return mIDIndex.end();
356  }
357 
359  {
360  return mOriginIndex.begin();
361  }
363  {
364  return mOriginIndex.begin();
365  }
367  {
368  return mOriginIndex.end();
369  }
371  {
372  return mOriginIndex.end();
373  }
374 protected:
376 
377 private:
378  ItemInfos mItemInfos;
379  IDIndex mIDIndex;
380  OriginIDIndex mOriginIndex;
381  PRUint32 mItemIndex;
382 };
383 
384 //-----------------------------------------------------------------------------
385 // sbLocalDatabaseDiffingServiceComparator
386 //-----------------------------------------------------------------------------
389 
391  mItemIndex(0)
392 {
393 }
394 
396 {
397 }
398 
399 /* unsigned short onEnumerationBegin (in sbIMediaList aMediaList); */
400 NS_IMETHODIMP
401 sbLDBDSEnumerator::OnEnumerationBegin(sbIMediaList *aMediaList,
402  PRUint16 *_retval)
403 {
404  NS_ENSURE_ARG_POINTER(aMediaList);
405  NS_ENSURE_ARG_POINTER(_retval);
406 
407  LOG(("Scanning for items"));
408  mItemIndex = 0;
409  PRUint32 length;
410  nsresult rv = aMediaList->GetLength(&length);
411  NS_ENSURE_SUCCESS(rv, rv);
412 
413  mItemInfos.reserve(length);
414 
416 
417  return NS_OK;
418 }
419 
420 /* unsigned short onEnumeratedItem (in sbIMediaList aMediaList, in sbIMediaItem aMediaItem); */
421 NS_IMETHODIMP
422 sbLDBDSEnumerator::OnEnumeratedItem(sbIMediaList *aMediaList,
423  sbIMediaItem *aMediaItem,
424  PRUint16 *_retval)
425 {
426  NS_ENSURE_ARG_POINTER(aMediaList);
427  NS_ENSURE_ARG_POINTER(aMediaItem);
428  NS_ENSURE_ARG_POINTER(_retval);
429 
430  nsresult rv;
431 
432  ItemInfo info;
433  info.mID = GetItemGUID(aMediaItem);
434  NS_ENSURE_TRUE(!info.mID.Equals(NULL_GUID), NS_ERROR_FAILURE);
435 
436  nsString originID;
437  rv = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID),
438  originID);
439  if (rv != NS_ERROR_NOT_AVAILABLE) {
440  NS_ENSURE_SUCCESS(rv, rv);
441  }
442  PRBool parsed = PR_FALSE;
443  nsID originGUID;
444  if (!originID.IsEmpty()) {
445  parsed = originGUID.Parse(
446  NS_LossyConvertUTF16toASCII(originID).BeginReading());
447  }
448  if (parsed) {
449  info.mOriginID = originGUID;
450  }
451  info.mPosition = mItemIndex++;
452 #ifdef PR_LOGGING
453  nsCOMPtr<nsIURI> uri;
454  aMediaItem->GetContentSrc(getter_AddRefs(uri));
455  nsCString spec;
456  if (uri) {
457  uri->GetSpec(spec);
458  }
459  LOG((" URL=%s\n ID=%s\n Origin=%s",
460  spec.BeginReading(),
461  NS_LossyConvertUTF16toASCII(sbGUIDToString(info.mID)).BeginReading(),
462  NS_LossyConvertUTF16toASCII(sbGUIDToString(info.mOriginID)).BeginReading()));
463 #endif
464  mItemInfos.push_back(info);
466 
467  return NS_OK;
468 }
469 
470 /* void onEnumerationEnd (in sbIMediaList aMediaList, in nsresult aStatusCode); */
471 NS_IMETHODIMP
472 sbLDBDSEnumerator::OnEnumerationEnd(sbIMediaList *aMediaList,
473  nsresult aStatusCode)
474 {
475  LOG(("Scanning complete, sorting"));
476  Sort();
477  return NS_OK;
478 }
479 
482 
483 NS_INTERFACE_MAP_BEGIN(sbLocalDatabaseDiffingService)
484  NS_IMPL_QUERY_CLASSINFO(sbLocalDatabaseDiffingService)
485  NS_INTERFACE_MAP_ENTRY(sbILibraryDiffingService)
486  NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
487  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, sbILibraryDiffingService)
488 NS_INTERFACE_MAP_END
489 
492 
493 NS_DECL_CLASSINFO(sbLocalDatabaseDiffingService)
494 NS_IMPL_THREADSAFE_CI(sbLocalDatabaseDiffingService)
495 
496 sbLocalDatabaseDiffingService::sbLocalDatabaseDiffingService()
497 {
498 }
499 
500 sbLocalDatabaseDiffingService::~sbLocalDatabaseDiffingService()
501 {
502 }
503 
504 /*static*/
506  nsIComponentManager* aCompMgr,
507  nsIFile* aPath,
508  const char* aLoaderStr,
509  const char* aType,
510  const nsModuleComponentInfo *aInfo)
511 {
512  nsresult rv;
513  nsCOMPtr<nsICategoryManager> categoryManager =
514  do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
515  NS_ENSURE_SUCCESS(rv, rv);
516 
517  rv = categoryManager->AddCategoryEntry(APPSTARTUP_CATEGORY,
520  PR_TRUE,
521  PR_TRUE,
522  nsnull);
523  NS_ENSURE_SUCCESS(rv, rv);
524 
525  return NS_OK;
526 }
527 
528 template <class T, class M>
529 T FindNextUsable(T aIter, T aEnd, M aMember)
530 {
531  if (aIter != aEnd) {
532  nsID const id = (**aIter).*aMember;
533  while (aIter != aEnd &&
534  (*aIter)->mAction ==
536  ((**aIter).*aMember).Equals(id)) {
537  ++aIter;
538  }
539  if (aIter != aEnd &&
540  (!((**aIter).*aMember).Equals(id) ||
541  ((*aIter)->mAction == sbLDBDSEnumerator::ItemInfo::ACTION_UPDATE))) {
542  return aEnd;
543  }
544  }
545  return aIter;
546 }
547 
548 static void
550 {
551  LOG(("Marking source and destination"));
552  sbLDBDSEnumerator::iterator const srcEnd = aSrc->end();
553  sbLDBDSEnumerator::OriginIterator const destOriginEnd = aDest->OriginEnd();
554  sbLDBDSEnumerator::IDIterator const destIDEnd = aDest->IDEnd();
555  for (sbLDBDSEnumerator::iterator srcIter = aSrc->begin();
556  srcIter != srcEnd;
557  ++srcIter) {
558  // Find the first non-marked destination item with the origin ID of our
559  // source's ID
560  sbLDBDSEnumerator::OriginIterator destOriginIter =
561  aDest->FindByOrigin(srcIter->mID);
562  destOriginIter = FindNextUsable(destOriginIter,
563  destOriginEnd,
565 
566  // If found then we're mark the destination as an update and get its ID
567  nsID foundDestID;
568  bool found = false;
569  if (destOriginIter != destOriginEnd) {
570  (*destOriginIter)->mAction = sbLDBDSEnumerator::ItemInfo::ACTION_UPDATE;
571  foundDestID = (*destOriginIter)->mID;
572  found = true;
573  }
574  // Not found, so look at other copy traits
575  else {
576  // If the destination and source have the same ID then obviously they
577  // are copies
578  sbLDBDSEnumerator::IDIterator destIDIter = aDest->FindByID(srcIter->mID);
579  destIDIter = FindNextUsable(destIDIter,
580  destIDEnd,
582  // Not found so see if a destination item's ID is the same as our
583  // origin, if it is set
584  if (destIDIter == destIDEnd && !srcIter->mOriginID.Equals(NULL_GUID)) {
585  destIDIter = aDest->FindByID(srcIter->mOriginID);
586  destIDIter = FindNextUsable(destIDIter,
587  destIDEnd,
589  }
590  // if we found one then mark it and set the found flag
591  if (destIDIter != destIDEnd) {
592  (*destIDIter)->mAction = sbLDBDSEnumerator::ItemInfo::ACTION_UPDATE;
593  foundDestID = (*destIDIter)->mID;
594  found = true;
595  }
596  }
597  // If we found a match in the destination then we need to update the
598  // source, setting its mAction and setting the origin back to the
599  // to the destination
600  if (found) {
601  // Origin in source becomes destination GUID
602  srcIter->mOriginID = foundDestID;
604  LOG((" Updating src=%s, dest=%s",
605  NS_LossyConvertUTF16toASCII(
606  sbGUIDToString(srcIter->mID)).BeginReading(),
607  NS_LossyConvertUTF16toASCII(
608  sbGUIDToString(foundDestID)).BeginReading()));
609  }
610  // Not found, mark the source as new
611  else {
612  LOG((" New src=%s",
613  NS_LossyConvertUTF16toASCII(
614  sbGUIDToString(srcIter->mID)).BeginReading()));
615  srcIter->mAction = sbLDBDSEnumerator::ItemInfo::ACTION_NEW;
616  }
617  }
618 }
619 
620 nsresult
622  sbIMediaList * aDestList,
623  sbLDBDSEnumerator * aSrcEnum,
624  sbLDBDSEnumerator * aDestEnum,
625  nsIArray ** aChanges)
626 {
627  NS_ENSURE_ARG_POINTER(aSrcList);
628  NS_ENSURE_ARG_POINTER(aDestList);
629  NS_ENSURE_ARG_POINTER(aChanges);
630 
631  nsresult rv;
632 
633  LOG(("Creating changes"));
634  nsCOMPtr<nsIMutableArray> libraryChanges =
635  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
636  NS_ENSURE_SUCCESS(rv, rv);
637 
638  nsCOMPtr<sbIMediaItem> srcItem;
639  nsCOMPtr<sbIMediaItem> destItem;
640  nsCOMPtr<sbILibraryChange> libraryChange;
641 
642  // For each item in the source, verify presence in destination library.
643  sbLDBDSEnumerator::const_iterator const srcEnd = aSrcEnum->end();
644  for(sbLDBDSEnumerator::const_iterator srcIter = aSrcEnum->begin();
645  srcIter != srcEnd;
646  ++srcIter) {
647 
648  rv = aSrcList->GetItemByGuid(sbGUIDToString(srcIter->mID),
649  getter_AddRefs(srcItem));
650  if (NS_FAILED(rv) || !srcItem) {
651  continue;
652  }
653 
654  switch (srcIter->mAction) {
656 #ifdef PR_LOGGING
657  nsCOMPtr<nsIURI> uri;
658  srcItem->GetContentSrc(getter_AddRefs(uri));
659  nsCString spec;
660  if (uri) {
661  uri->GetSpec(spec);
662  }
663  LOG((" New %s-%s",
664  NS_LossyConvertUTF16toASCII(
665  sbGUIDToString(srcIter->mID)).BeginReading(),
666  spec.BeginReading()));
667 #endif
668  rv = CreateItemAddedLibraryChange(srcItem,
669  getter_AddRefs(libraryChange));
670  NS_ENSURE_SUCCESS(rv, rv);
671  break;
672  }
674  // The origin is the destination ID as updated in MarkLists
675  rv = aDestList->GetItemByGuid(sbGUIDToString(srcIter->mOriginID),
676  getter_AddRefs(destItem));
677  if (NS_FAILED(rv) || !destItem) {
678  continue;
679  }
680 #ifdef PR_LOGGING
681  nsCOMPtr<nsIURI> uri;
682  srcItem->GetContentSrc(getter_AddRefs(uri));
683  nsCString srcSpec;
684  if (uri) {
685  uri->GetSpec(srcSpec);
686  }
687  destItem->GetContentSrc(getter_AddRefs(uri));
688  nsCString destSpec;
689  if (uri) {
690  uri->GetSpec(destSpec);
691  }
692  LOG((" Updating"));
693  LOG((" %s-%s",
694  NS_LossyConvertUTF16toASCII(
695  sbGUIDToString(srcIter->mID)).BeginReading(),
696  srcSpec.BeginReading()));
697  LOG((" %s-%s",
698  NS_LossyConvertUTF16toASCII(
699  sbGUIDToString(srcIter->mOriginID)).BeginReading(),
700  destSpec.BeginReading()));
701 #endif
702  rv = CreateLibraryChangeFromItems(srcItem,
703  destItem,
704  getter_AddRefs(libraryChange));
705  // Handle no changes error
706  if (rv == NS_ERROR_NOT_AVAILABLE) {
707  continue;
708  }
709  NS_ENSURE_SUCCESS(rv, rv);
710  break;
711  }
712  default: {
713  NS_WARNING("Unexpected action in diffing source, skipping");
714  break;
715  }
716  }
717  if (libraryChange) {
718  rv = libraryChanges->AppendElement(libraryChange, PR_FALSE);
719  NS_ENSURE_SUCCESS(rv, rv);
720  }
721  }
722 
723  sbLDBDSEnumerator::const_iterator const destEnd = aDestEnum->end();
724  // For each item in the destination
725  for(sbLDBDSEnumerator::const_iterator destIter =
726  aDestEnum->begin();
727  destIter != destEnd;
728  destIter++) {
729 
730  // Add delete change if not marked as used
731  if (destIter->mAction ==
733  rv = aDestList->GetItemByGuid(sbGUIDToString(destIter->mID),
734  getter_AddRefs(destItem));
735  // If we can't find it now, just skip it
736  if (rv == NS_ERROR_NOT_AVAILABLE || !destItem) {
737  continue;
738  }
739  NS_ENSURE_SUCCESS(rv, rv);
740 
741 #ifdef PR_LOGGING
742  nsCOMPtr<nsIURI> uri;
743  destItem->GetContentSrc(getter_AddRefs(uri));
744  nsCString spec;
745  if (uri) {
746  uri->GetSpec(spec);
747  }
748  LOG((" Delete %s-%s",
749  NS_LossyConvertUTF16toASCII(
750  sbGUIDToString(destIter->mID)).BeginReading(),
751  spec.BeginReading()));
752 #endif
753  rv = CreateItemDeletedLibraryChange(destItem,
754  getter_AddRefs(libraryChange));
755  NS_ENSURE_SUCCESS(rv, rv);
756 
757  rv = libraryChanges->AppendElement(libraryChange, PR_FALSE);
758  NS_ENSURE_SUCCESS(rv, rv);
759  }
760  }
761 
762  rv = CallQueryInterface(libraryChanges.get(), aChanges);
763  NS_ENSURE_SUCCESS(rv, rv);
764 
765  return rv;
766 }
767 
768 nsresult
770 {
771  nsresult rv;
772 
773  mPropertyManager = do_CreateInstance(SB_PROPERTYMANAGER_CONTRACTID, &rv);
774  NS_ENSURE_SUCCESS(rv, rv);
775 
776  return NS_OK;
777 }
778 
779 nsresult
781 {
782  NS_ENSURE_ARG_POINTER(aPropertyIDs);
783  NS_ENSURE_STATE(mPropertyManager);
784 
785  nsCOMPtr<nsIStringEnumerator> ids;
786 
787  nsresult rv = mPropertyManager->GetPropertyIDs(getter_AddRefs(ids));
788  NS_ENSURE_SUCCESS(rv, rv);
789 
790  ids.forget(aPropertyIDs);
791 
792  return NS_OK;
793 }
794 
795 nsresult
797  sbIMediaItem *aSourceItem,
798  sbIMediaItem *aDestinationItem,
799  sbILibraryChange **aLibraryChange)
800 {
801  NS_ENSURE_ARG_POINTER(aSourceItem);
802  NS_ENSURE_ARG_POINTER(aDestinationItem);
803  NS_ENSURE_ARG_POINTER(aLibraryChange);
804 
805  nsCOMPtr<sbIPropertyArray> sourceProperties;
806  nsCOMPtr<sbIPropertyArray> destinationProperties;
807 
808  nsresult rv = aSourceItem->GetProperties(nsnull,
809  getter_AddRefs(sourceProperties));
810  NS_ENSURE_SUCCESS(rv, rv);
811 
812  rv = aDestinationItem->GetProperties(nsnull,
813  getter_AddRefs(destinationProperties));
814  NS_ENSURE_SUCCESS(rv, rv);
815 
816  nsCOMPtr<nsIArray> propertyChanges;
817  rv = CreatePropertyChangesFromProperties(sourceProperties,
818  destinationProperties,
819  getter_AddRefs(propertyChanges));
820  if (rv == NS_ERROR_NOT_AVAILABLE) {
821  return NS_ERROR_NOT_AVAILABLE;
822  }
823  NS_ENSURE_SUCCESS(rv, rv);
824 
825  nsRefPtr<sbLibraryChange> libraryChange;
826  NS_NEWXPCOM(libraryChange, sbLibraryChange);
827  NS_ENSURE_TRUE(libraryChange, NS_ERROR_OUT_OF_MEMORY);
828 
829  rv = libraryChange->InitWithValues(sbIChangeOperation::MODIFIED,
830  0,
831  aSourceItem,
832  aDestinationItem,
833  propertyChanges,
834  nsnull);
835  NS_ENSURE_SUCCESS(rv, rv);
836 
837  return CallQueryInterface(libraryChange.get(), aLibraryChange);
838 }
839 
840 nsresult
842  sbIMediaItem *aSourceItem,
843  sbILibraryChange **aLibraryChange)
844 {
845  NS_ENSURE_ARG_POINTER(aSourceItem);
846  NS_ENSURE_ARG_POINTER(aLibraryChange);
847 
848  nsRefPtr<sbLibraryChange> libraryChange;
849  NS_NEWXPCOM(libraryChange, sbLibraryChange);
850  NS_ENSURE_TRUE(libraryChange, NS_ERROR_OUT_OF_MEMORY);
851 
852  nsCOMPtr<sbIPropertyArray> properties;
853  nsresult rv = aSourceItem->GetProperties(nsnull, getter_AddRefs(properties));
854  NS_ENSURE_SUCCESS(rv, rv);
855 
856  nsCOMPtr<nsIMutableArray> propertyChanges =
857  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
858  NS_ENSURE_SUCCESS(rv, rv);
859 
860  PRUint32 propertyCount = 0;
861  rv = properties->GetLength(&propertyCount);
862  NS_ENSURE_SUCCESS(rv, rv);
863 
864  nsString strPropertyID;
865  nsString strPropertyValue;
866  nsCOMPtr<sbIProperty> property;
867 
868  for(PRUint32 current = 0; current < propertyCount; ++current) {
869 
870  rv = properties->GetPropertyAt(current, getter_AddRefs(property));
871  NS_ENSURE_SUCCESS(rv, rv);
872 
873  rv = property->GetId(strPropertyID);
874  NS_ENSURE_SUCCESS(rv, rv);
875 
876  rv = property->GetValue(strPropertyValue);
877  NS_ENSURE_SUCCESS(rv, rv);
878 
879  nsRefPtr<sbPropertyChange> propertyChange;
880  NS_NEWXPCOM(propertyChange, sbPropertyChange);
881  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
882 
883  rv = propertyChange->InitWithValues(sbIChangeOperation::ADDED,
884  strPropertyID,
885  EmptyString(),
886  strPropertyValue);
887  NS_ENSURE_SUCCESS(rv, rv);
888 
889  nsCOMPtr<nsISupports> element =
890  do_QueryInterface(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange), &rv);
891  NS_ENSURE_SUCCESS(rv, rv);
892 
893  rv = propertyChanges->AppendElement(element,
894  PR_FALSE);
895  NS_ENSURE_SUCCESS(rv, rv);
896  }
897 
898  rv = libraryChange->InitWithValues(sbIChangeOperation::ADDED,
899  0,
900  aSourceItem,
901  nsnull,
902  propertyChanges,
903  nsnull);
904  NS_ENSURE_SUCCESS(rv, rv);
905 
906  return CallQueryInterface(libraryChange.get(), aLibraryChange);
907 }
908 
909 nsresult
911  PRUint32 aItemOrdinal,
912  sbILibraryChange **aLibraryChange)
913 {
914  NS_ENSURE_ARG_POINTER(aSourceItem);
915  NS_ENSURE_ARG_POINTER(aLibraryChange);
916 
917  nsresult rv = NS_ERROR_UNEXPECTED;
918 
919  nsRefPtr<sbLibraryChange> libraryChange;
920  NS_NEWXPCOM(libraryChange, sbLibraryChange);
921  NS_ENSURE_TRUE(libraryChange, NS_ERROR_OUT_OF_MEMORY);
922 
923  nsCOMPtr<nsIMutableArray> propertyChanges =
924  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
925  NS_ENSURE_SUCCESS(rv, rv);
926 
927  nsRefPtr<sbPropertyChange> propertyChange;
928  NS_NEWXPCOM(propertyChange, sbPropertyChange);
929  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
930 
931  nsString strPropertyValue;
932  strPropertyValue.AppendInt(aItemOrdinal);
933 
934  rv = propertyChange->InitWithValues(sbIChangeOperation::MODIFIED,
935  NS_LITERAL_STRING(SB_PROPERTY_ORDINAL),
936  EmptyString(),
937  strPropertyValue);
938  NS_ENSURE_SUCCESS(rv, rv);
939 
940  nsCOMPtr<nsISupports> element =
941  do_QueryInterface(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange), &rv);
942  NS_ENSURE_SUCCESS(rv, rv);
943 
944  rv = propertyChanges->AppendElement(element,
945  PR_FALSE);
946  NS_ENSURE_SUCCESS(rv, rv);
947 
948  rv = libraryChange->InitWithValues(sbIChangeOperation::MOVED,
949  0,
950  aSourceItem,
951  nsnull,
952  propertyChanges,
953  nsnull);
954  NS_ENSURE_SUCCESS(rv, rv);
955 
956  return CallQueryInterface(libraryChange.get(), aLibraryChange);
957 }
958 
959 nsresult
961  sbILibraryChange **aLibraryChange)
962 {
963  NS_ENSURE_ARG_POINTER(aDestinationItem);
964  NS_ENSURE_ARG_POINTER(aLibraryChange);
965 
966  nsRefPtr<sbLibraryChange> libraryChange;
967  NS_NEWXPCOM(libraryChange, sbLibraryChange);
968  NS_ENSURE_TRUE(libraryChange, NS_ERROR_OUT_OF_MEMORY);
969 
970  nsresult rv = libraryChange->InitWithValues(sbIChangeOperation::DELETED,
971  0,
972  nsnull,
973  aDestinationItem,
974  nsnull,
975  nsnull);
976  NS_ENSURE_SUCCESS(rv, rv);
977 
978  return CallQueryInterface(libraryChange.get(), aLibraryChange);
979 }
980 
981 nsresult
983  sbIPropertyArray *aSourceProperties,
984  sbIPropertyArray *aDestinationProperties,
985  nsIArray **aPropertyChanges)
986 {
987  NS_ENSURE_ARG_POINTER(aSourceProperties);
988  NS_ENSURE_ARG_POINTER(aDestinationProperties);
989  NS_ENSURE_ARG_POINTER(aPropertyChanges);
990 
991  nsresult rv;
992  nsCOMPtr<nsIMutableArray> propertyChanges =
993  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
994  NS_ENSURE_SUCCESS(rv, rv);
995 
996  PRUint32 sourceLength;
997  rv = aSourceProperties->GetLength(&sourceLength);
998  NS_ENSURE_SUCCESS(rv, rv);
999 
1000  PRUint32 destinationLength;
1001  rv = aDestinationProperties->GetLength(&destinationLength);
1002 
1003  nsCOMPtr<sbIProperty> property;
1004  nsTHashtable<nsStringHashKey> sourcePropertyNamesFoundInDestination;
1005 
1006  PRBool success = sourcePropertyNamesFoundInDestination.Init();
1007  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1008 
1009  // These properties are excluded from checking since they
1010  // are automatically maintained and do not reflect actual
1011  // metadata differences in the items.
1012  nsTHashtable<nsStringHashKey> propertyExclusionList;
1013  success = propertyExclusionList.Init();
1014  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1015 
1016  nsStringHashKey* successHashkey =
1017  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_CREATED));
1018  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1019 
1020  successHashkey =
1021  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_UPDATED));
1022  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1023 
1024  successHashkey =
1025  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_GUID));
1026  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1027 
1028  successHashkey =
1029  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID));
1030  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1031 
1032  successHashkey =
1033  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_ORIGINLIBRARYGUID));
1034  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1035 
1036  successHashkey =
1037  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_ORIGINURL));
1038  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1039 
1040  // Sadly we can't use content length because on a device the length may be
1041  // different
1042  successHashkey =
1043  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_CONTENTLENGTH));
1044  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1045 
1046  nsString propertyId;
1047  nsString propertyValue;
1048  nsString propertyDestinationValue;
1049 
1050  // First, we process the sourceProperties.
1051  for(PRUint32 current = 0; current < sourceLength; ++current)
1052  {
1053  rv = aSourceProperties->GetPropertyAt(current, getter_AddRefs(property));
1054  NS_ENSURE_SUCCESS(rv, rv);
1055 
1056  rv = property->GetId(propertyId);
1057  NS_ENSURE_SUCCESS(rv, rv);
1058 
1059  rv = property->GetValue(propertyValue);
1060  NS_ENSURE_SUCCESS(rv, rv);
1061 
1062  if(propertyExclusionList.GetEntry(propertyId)) {
1063  continue;
1064  }
1065 
1066  rv = aDestinationProperties->GetPropertyValue(propertyId,
1067  propertyDestinationValue);
1068  // Property has been added.
1069  if(rv == NS_ERROR_NOT_AVAILABLE) {
1070  nsRefPtr<sbPropertyChange> propertyChange;
1071  NS_NEWXPCOM(propertyChange, sbPropertyChange);
1072  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
1073 
1074  rv = propertyChange->InitWithValues(sbIChangeOperation::ADDED,
1075  propertyId,
1076  EmptyString(),
1077  propertyValue);
1078  NS_ENSURE_SUCCESS(rv, rv);
1079 
1080  nsCOMPtr<nsISupports> element =
1081  do_QueryInterface(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange), &rv);
1082 
1083  rv = propertyChanges->AppendElement(element,
1084  PR_FALSE);
1085  NS_ENSURE_SUCCESS(rv, rv);
1086  }
1087  else {
1088 
1089  NS_ENSURE_SUCCESS(rv, rv);
1090 
1091  // Didn't fail, it should be present in both source and destination.
1092  successHashkey = sourcePropertyNamesFoundInDestination.PutEntry(propertyId);
1093  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
1094 
1095  if (propertyId.EqualsLiteral(SB_PROPERTY_CONTENTURL)) {
1096  nsString originURL;
1097  rv = aDestinationProperties->GetPropertyValue(
1098  NS_LITERAL_STRING(SB_PROPERTY_ORIGINURL),
1099  originURL);
1100  if (NS_SUCCEEDED(rv) && !originURL.IsEmpty()) {
1101  if (propertyValue.Equals(originURL)) {
1102  continue;
1103  }
1104  }
1105  }
1106  // Check if the duration is the same in seconds, that's good enough
1107  else if (propertyId.EqualsLiteral(SB_PROPERTY_DURATION)) {
1108  PRUint64 const sourceDuration = nsString_ToUint64(propertyValue, &rv);
1109  if (NS_SUCCEEDED(rv)) {
1110  PRUint64 const destDuration =
1111  nsString_ToUint64(propertyDestinationValue, &rv);
1112  // If the duration was parsed and the difference less than a second
1113  // then treat it as unchanged
1114  if (NS_SUCCEEDED(rv)) {
1115  // MSVC has no llabs(), so do it this way.
1116  PRInt64 durationDiff = sourceDuration - destDuration;
1117  if ((durationDiff < 0 && -durationDiff < PR_USEC_PER_SEC) ||
1118  durationDiff < PR_USEC_PER_SEC)
1119  continue;
1120  }
1121  }
1122  }
1123  // Property values are the same, nothing changed,
1124  // continue onto the next property.
1125  if(propertyValue.Equals(propertyDestinationValue)) {
1126  continue;
1127  }
1128 
1129  nsRefPtr<sbPropertyChange> propertyChange;
1130  NS_NEWXPCOM(propertyChange, sbPropertyChange);
1131  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
1132 
1133  rv = propertyChange->InitWithValues(sbIChangeOperation::MODIFIED,
1134  propertyId,
1135  propertyDestinationValue,
1136  propertyValue);
1137  NS_ENSURE_SUCCESS(rv, rv);
1138 
1139  nsCOMPtr<nsISupports> element =
1140  do_QueryInterface(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange), &rv);
1141  rv = propertyChanges->AppendElement(element,
1142  PR_FALSE);
1143  NS_ENSURE_SUCCESS(rv, rv);
1144  }
1145  }
1146 
1147  // Second, we process the destinationProperties.
1148  // This will enable us to determine which properties were removed
1149  // from the source.
1150  for(PRUint32 current = 0; current < destinationLength; ++current) {
1151  rv = aDestinationProperties->GetPropertyAt(current, getter_AddRefs(property));
1152  NS_ENSURE_SUCCESS(rv, rv);
1153 
1154  rv = property->GetId(propertyId);
1155  NS_ENSURE_SUCCESS(rv, rv);
1156 
1157  rv = property->GetValue(propertyDestinationValue);
1158  NS_ENSURE_SUCCESS(rv, rv);
1159 
1160  if(propertyExclusionList.GetEntry(propertyId)) {
1161  continue;
1162  }
1163 
1164  if(!sourcePropertyNamesFoundInDestination.GetEntry(propertyId)) {
1165 
1166  // We couldn't find the property in the source properties, this means
1167  // the property must've been removed.
1168  nsRefPtr<sbPropertyChange> propertyChange;
1169  NS_NEWXPCOM(propertyChange, sbPropertyChange);
1170  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
1171 
1172  rv = propertyChange->InitWithValues(sbIChangeOperation::DELETED,
1173  propertyId,
1174  propertyDestinationValue,
1175  EmptyString());
1176  NS_ENSURE_SUCCESS(rv, rv);
1177 
1178  rv = propertyChanges->AppendElement(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange),
1179  PR_FALSE);
1180  NS_ENSURE_SUCCESS(rv, rv);
1181  }
1182  }
1183 
1184  PRUint32 propertyChangesCount = 0;
1185  rv = propertyChanges->GetLength(&propertyChangesCount);
1186  NS_ENSURE_SUCCESS(rv, rv);
1187 
1188  if (propertyChangesCount == 0) {
1189  return NS_ERROR_NOT_AVAILABLE;
1190  }
1191 
1192  return CallQueryInterface(propertyChanges.get(), aPropertyChanges);
1193 }
1194 
1195 nsresult
1197  sbIMediaList *aSourceList,
1198  sbIMediaList *aDestinationList,
1199  sbILibraryChangeset **aLibraryChangeset)
1200 {
1201  NS_ENSURE_ARG_POINTER(aSourceList);
1202  NS_ENSURE_ARG_POINTER(aDestinationList);
1203  NS_ENSURE_ARG_POINTER(aLibraryChangeset);
1204 
1205  nsRefPtr<sbLibraryChangeset> libraryChangeset;
1206  NS_NEWXPCOM(libraryChangeset, sbLibraryChangeset);
1207  NS_ENSURE_TRUE(libraryChangeset, NS_ERROR_OUT_OF_MEMORY);
1208 
1209  nsresult rv;
1210 
1211  nsRefPtr<sbLDBDSEnumerator> sourceEnum;
1212  NS_NEWXPCOM(sourceEnum, sbLDBDSEnumerator);
1213  NS_ENSURE_TRUE(sourceEnum, NS_ERROR_OUT_OF_MEMORY);
1214 
1215  nsRefPtr<sbLDBDSEnumerator> destinationEnum;
1216  NS_NEWXPCOM(destinationEnum, sbLDBDSEnumerator);
1217  NS_ENSURE_TRUE(destinationEnum, NS_ERROR_OUT_OF_MEMORY);
1218 
1219  rv = aSourceList->EnumerateAllItems(sourceEnum,
1221  NS_ENSURE_SUCCESS(rv, rv);
1222 
1223  rv = aDestinationList->EnumerateAllItems(destinationEnum,
1225  NS_ENSURE_SUCCESS(rv, rv);
1226 
1227  MarkLists(sourceEnum, destinationEnum);
1228 
1229  nsCOMPtr<nsIArray> libraryChanges;
1230  rv = CreateChanges(aSourceList,
1231  aDestinationList,
1232  sourceEnum,
1233  destinationEnum,
1234  getter_AddRefs(libraryChanges));
1235  NS_ENSURE_SUCCESS(rv, rv);
1236 
1237  nsCOMPtr<nsIMutableArray> libraryChangesMutable =
1238  do_QueryInterface(libraryChanges, &rv);
1239  NS_ENSURE_SUCCESS(rv, rv);
1240  // Ensure that all items present will be in the correct order
1241  // by explicitly including a move operation for each item present
1242  // the source. sourceEnum items are in playlist order
1243  PRInt32 listIndex = 0;
1244  sbLDBDSEnumerator::const_iterator const sourceEnd = sourceEnum->end();
1245  for (sbLDBDSEnumerator::const_iterator sourceIter = sourceEnum->begin();
1246  sourceIter != sourceEnd;
1247  ++sourceIter) {
1248  // Skip delete ones.
1249  if (sourceIter->mAction == sbLDBDSEnumerator::ItemInfo::ACTION_DELETE) {
1250  continue;
1251  }
1252  nsCOMPtr<sbIMediaItem> sourceItem;
1253  rv = aSourceList->GetItemByIndex(sourceIter->mPosition,
1254  getter_AddRefs(sourceItem));
1255  if (NS_SUCCEEDED(rv)) {
1256  nsCOMPtr<sbILibraryChange> libraryChange;
1257  rv = CreateItemMovedLibraryChange(sourceItem,
1258  listIndex++,
1259  getter_AddRefs(libraryChange));
1260  NS_ENSURE_SUCCESS(rv, rv);
1261 
1262  rv = libraryChangesMutable->AppendElement(libraryChange, PR_FALSE);
1263  NS_ENSURE_SUCCESS(rv, rv);
1264  }
1265  }
1266 
1267  // That's it, we should have a valid changeset.
1268  nsCOMPtr<nsIMutableArray> sources =
1269  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1270  NS_ENSURE_SUCCESS(rv, rv);
1271 
1272  rv = sources->AppendElement(aSourceList, PR_FALSE);
1273  NS_ENSURE_SUCCESS(rv, rv);
1274 
1275  rv = libraryChangeset->InitWithValues(sources,
1276  aDestinationList,
1277  libraryChanges);
1278  NS_ENSURE_SUCCESS(rv, rv);
1279 
1280  return CallQueryInterface(libraryChangeset.get(), aLibraryChangeset);
1281 }
1282 
1283 nsresult
1285  sbILibrary *aSourceLibrary,
1286  sbILibrary *aDestinationLibrary,
1287  sbILibraryChangeset **aLibraryChangeset)
1288 {
1289  NS_ENSURE_ARG_POINTER(aSourceLibrary);
1290  NS_ENSURE_ARG_POINTER(aDestinationLibrary);
1291  NS_ENSURE_ARG_POINTER(aLibraryChangeset);
1292 
1293  NS_NAMED_LITERAL_STRING(ORIGIN_ID, SB_PROPERTY_ORIGINITEMGUID);
1294 
1295  nsRefPtr<sbLibraryChangeset> libraryChangeset;
1296  NS_NEWXPCOM(libraryChangeset, sbLibraryChangeset);
1297  NS_ENSURE_TRUE(libraryChangeset, NS_ERROR_OUT_OF_MEMORY);
1298 
1299  nsresult rv;
1300 
1301  nsRefPtr<sbLDBDSEnumerator> sourceEnum;
1302  NS_NEWXPCOM(sourceEnum, sbLDBDSEnumerator);
1303  NS_ENSURE_TRUE(sourceEnum, NS_ERROR_OUT_OF_MEMORY);
1304 
1305  nsRefPtr<sbLDBDSEnumerator> destinationEnum;
1306  NS_NEWXPCOM(destinationEnum, sbLDBDSEnumerator);
1307  NS_ENSURE_TRUE(destinationEnum, NS_ERROR_OUT_OF_MEMORY);
1308 
1309  rv = aSourceLibrary->EnumerateAllItems(sourceEnum,
1311  NS_ENSURE_SUCCESS(rv, rv);
1312 
1313  rv = aDestinationLibrary->EnumerateAllItems(destinationEnum,
1315  NS_ENSURE_SUCCESS(rv, rv);
1316 
1317  MarkLists(sourceEnum, destinationEnum);
1318 
1319  nsCOMPtr<nsIArray> libraryChanges;
1320  rv = CreateChanges(aSourceLibrary,
1321  aDestinationLibrary,
1322  sourceEnum,
1323  destinationEnum,
1324  getter_AddRefs(libraryChanges));
1325  NS_ENSURE_SUCCESS(rv, rv);
1326 
1327  // That's it, we should have a valid changeset.
1328  nsCOMPtr<nsIMutableArray> sources =
1329  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1330  NS_ENSURE_SUCCESS(rv, rv);
1331 
1332  rv = sources->AppendElement(aSourceLibrary, PR_FALSE);
1333  NS_ENSURE_SUCCESS(rv, rv);
1334 
1335  rv = libraryChangeset->InitWithValues(sources,
1336  aDestinationLibrary,
1337  libraryChanges);
1338  NS_ENSURE_SUCCESS(rv, rv);
1339 
1340  return CallQueryInterface(libraryChangeset.get(), aLibraryChangeset);
1341 }
1342 
1343 inline
1344 nsresult AddUniqueItem(nsTHashtable<nsIDHashKey> & aItems, sbIMediaItem * aItem)
1345 {
1346  nsID const & itemID = GetItemGUID(aItem);
1347  NS_ENSURE_SUCCESS(!itemID.Equals(NULL_GUID), NS_ERROR_FAILURE);
1348 
1349  // Already in the list, done.
1350  if (!aItems.GetEntry(itemID)) {
1351  nsIDHashKey *hashKey = aItems.PutEntry(itemID);
1352  NS_ENSURE_TRUE(hashKey, NS_ERROR_OUT_OF_MEMORY);
1353  }
1354 
1355  return NS_OK;
1356 }
1357 
1359 {
1362  nsIMutableArray * mLibraryChanges;
1365 };
1366 
1367 PLDHashOperator
1368 sbLocalDatabaseDiffingService::Enumerator(nsIDHashKey* aEntry, void* userArg)
1369 {
1370  nsresult rv;
1371 
1372  EnumeratorArgs * args = static_cast<EnumeratorArgs*>(userArg);
1373 
1374  nsCOMPtr<sbIMediaItem> sourceItem;
1375 
1376  rv = args->mSourceLibrary->GetItemByGuid(
1377  sbGUIDToString(aEntry->GetKey()),
1378  getter_AddRefs(sourceItem));
1379  NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
1380 
1381  nsCOMPtr<sbILibraryChange> libraryChange;
1382 
1384  args->mDestinationEnum->FindByOrigin(aEntry->GetKey());
1385  nsCOMPtr<sbIMediaItem> destinationItem;
1386  if (iter != args->mDestinationEnum->OriginEnd()) {
1387  (*iter)->mAction = sbLDBDSEnumerator::ItemInfo::ACTION_UPDATE;
1388  args->mDestinationLibrary->GetItemByGuid(
1389  sbGUIDToString((*iter)->mID),
1390  getter_AddRefs(destinationItem));
1391  NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
1392  }
1393  else {
1395  args->mDestinationEnum->FindByID(aEntry->GetKey());
1396  if (idIter == args->mDestinationEnum->IDEnd()) {
1397  nsID originID = GetGUIDProperty(
1398  sourceItem,
1399  NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID));
1400  if (!originID.Equals(NULL_GUID)) {
1401  idIter = args->mDestinationEnum->FindByID(originID);
1402  }
1403  }
1404  if (idIter != args->mDestinationEnum->IDEnd()) {
1405  (*idIter)->mAction = sbLDBDSEnumerator::ItemInfo::ACTION_UPDATE;
1406  args->mDestinationLibrary->GetItemByGuid(
1407  sbGUIDToString((*idIter)->mID),
1408  getter_AddRefs(destinationItem));
1409  NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
1410  }
1411  }
1412  if (destinationItem) {
1413  // Do not update playlist
1414  nsString isList;
1415  rv = sourceItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ISLIST),
1416  isList);
1417  NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
1418 
1419  if (isList.EqualsLiteral("1"))
1420  return PL_DHASH_NEXT;
1421 
1422  LogMediaItem("Source Item", sourceItem);
1423  LogMediaItem("Destination item", destinationItem);
1425  sourceItem,
1426  destinationItem,
1427  getter_AddRefs(libraryChange));
1428 
1429  // Item did not change.
1430  if (rv == NS_ERROR_NOT_AVAILABLE) {
1431  return PL_DHASH_NEXT;
1432  }
1433  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
1434 
1435  }
1436  else {
1437  LogMediaItem("New item", sourceItem);
1439  sourceItem,
1440  getter_AddRefs(libraryChange));
1441  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
1442  }
1443  rv = args->mLibraryChanges->AppendElement(libraryChange, PR_FALSE);
1444  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
1445  return PL_DHASH_NEXT;
1446 }
1447 
1448 nsresult
1450  nsIArray *aSourceLists,
1451  sbILibrary *aDestinationLibrary,
1452  sbILibraryChangeset **aLibraryChangeset)
1453 {
1454  NS_ENSURE_ARG_POINTER(aSourceLists);
1455  NS_ENSURE_ARG_POINTER(aDestinationLibrary);
1456  NS_ENSURE_ARG_POINTER(aLibraryChangeset);
1457 
1458  // First, we build a hashtable of all the unique GUIDs.
1459  PRUint32 sourcesLength = 0;
1460  nsresult rv = aSourceLists->GetLength(&sourcesLength);
1461  NS_ENSURE_SUCCESS(rv, rv);
1462 
1463  nsCOMPtr<sbIMediaItem> sourceItem;
1464  nsCOMPtr<sbIMediaList> sourceList;
1465  nsCOMPtr<sbILibrary> sourceLibrary;
1466 
1467  nsTHashtable<nsIDHashKey> sourceItemIDs;
1468  sourceItemIDs.Init();
1469  for(PRUint32 currentSource = 0;
1470  currentSource < sourcesLength;
1471  ++currentSource) {
1472 
1473  sourceItem = do_QueryElementAt(aSourceLists, currentSource, &rv);
1474  NS_ENSURE_SUCCESS(rv, rv);
1475 
1476  // TODO: XXX a bit of a hack, we're assuming all lists are from the same
1477  // library
1478  if (currentSource == 0) {
1479  rv = sourceItem->GetLibrary(getter_AddRefs(sourceLibrary));
1480  NS_ENSURE_SUCCESS(rv, rv);
1481  }
1482 #ifdef DEBUG
1483  // Ensure that we're seeing items from the same library
1484  else {
1485  nsCOMPtr<sbILibrary> testLibrary;
1486  PRBool sameLibrary = PR_FALSE;
1487  rv = sourceItem->GetLibrary(getter_AddRefs(testLibrary));
1488  if (NS_SUCCEEDED(rv)) {
1489 
1490  rv = testLibrary->Equals(sourceLibrary, &sameLibrary);
1491  sameLibrary = sameLibrary && NS_SUCCEEDED(rv);
1492  }
1493  NS_ASSERTION(sameLibrary,
1494  "Source cannot contain items from different libraries");
1495  }
1496 #endif
1497  sourceList = do_QueryInterface(sourceItem, &rv);
1498  // Not media list.
1499  if (NS_FAILED(rv)) {
1500  rv = AddUniqueItem(sourceItemIDs, sourceItem);
1501  NS_ENSURE_SUCCESS(rv, rv);
1502  }
1503  else {
1504  PRUint32 sourceLength = 0;
1505  rv = sourceList->GetLength(&sourceLength);
1506  NS_ENSURE_SUCCESS(rv, rv);
1507 
1508  for(PRUint32 currentItem = 0; currentItem < sourceLength; ++currentItem) {
1509  rv = sourceList->GetItemByIndex(currentItem,
1510  getter_AddRefs(sourceItem));
1511  NS_ENSURE_SUCCESS(rv, rv);
1512 
1513  rv = AddUniqueItem(sourceItemIDs,
1514  sourceItem);
1515  NS_ENSURE_SUCCESS(rv, rv);
1516  }
1517 
1518  // Add source list to the unique item list if it's not a library.
1519  nsCOMPtr<sbILibrary> sourceListIsLibrary = do_QueryInterface(sourceList);
1520  if (!sourceListIsLibrary) {
1521  rv = AddUniqueItem(sourceItemIDs,
1522  sourceList);
1523  NS_ENSURE_SUCCESS(rv, rv);
1524  }
1525  }
1526  }
1527 
1528  nsRefPtr<sbLibraryChangeset> libraryChangeset;
1529  NS_NEWXPCOM(libraryChangeset, sbLibraryChangeset);
1530  NS_ENSURE_TRUE(libraryChangeset, NS_ERROR_OUT_OF_MEMORY);
1531 
1532  nsCOMPtr<nsIMutableArray> libraryChanges =
1533  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1534  NS_ENSURE_SUCCESS(rv, rv);
1535 
1536  nsRefPtr<sbLDBDSEnumerator> destinationEnum;
1537  NS_NEWXPCOM(destinationEnum, sbLDBDSEnumerator);
1538  NS_ENSURE_TRUE(destinationEnum, NS_ERROR_OUT_OF_MEMORY);
1539 
1540  rv = aDestinationLibrary->EnumerateAllItems(destinationEnum,
1542  NS_ENSURE_SUCCESS(rv, rv);
1543 
1544  // Create a unique list of media items in the source lists
1545  EnumeratorArgs args = {
1546  destinationEnum,
1547  this,
1548  libraryChanges,
1549  sourceLibrary,
1550  aDestinationLibrary
1551  };
1552  sourceItemIDs.EnumerateEntries(Enumerator, &args);
1553 
1554  // For each item in the destination
1555  sbLDBDSEnumerator::const_iterator const end = destinationEnum->end();
1556  for (sbLDBDSEnumerator::const_iterator iter = destinationEnum->begin();
1557  iter != end;
1558  ++iter) {
1559  if (iter->mAction == sbLDBDSEnumerator::ItemInfo::ACTION_NONE) {
1560  // Not present in source, indicate that the item was removed from the
1561  // source.
1562  nsCOMPtr<sbIMediaItem> destItem;
1563  rv = aDestinationLibrary->GetItemByGuid(sbGUIDToString(iter->mID),
1564  getter_AddRefs(destItem));
1565  // If we can't find it now, just skip it
1566  if (rv == NS_ERROR_NOT_AVAILABLE || !destItem) {
1567  continue;
1568  }
1569  NS_ENSURE_SUCCESS(rv, rv);
1570 
1571  nsCOMPtr<sbILibraryChange> libraryChange;
1572  rv = CreateItemDeletedLibraryChange(destItem, getter_AddRefs(libraryChange));
1573  NS_ENSURE_SUCCESS(rv, rv);
1574 
1575  rv = libraryChanges->AppendElement(libraryChange, PR_FALSE);
1576  NS_ENSURE_SUCCESS(rv, rv);
1577  }
1578  }
1579 
1580  // That's it, we should have a valid changeset.
1581  rv = libraryChangeset->InitWithValues(aSourceLists,
1582  aDestinationLibrary,
1583  libraryChanges);
1584  NS_ENSURE_SUCCESS(rv, rv);
1585 
1586  return CallQueryInterface(libraryChangeset.get(), aLibraryChangeset);
1587 }
1588 
1589 /* sbILibraryChangeset createChangeset (in sbIMediaList aSource, in sbIMediaList aDestination); */
1590 NS_IMETHODIMP sbLocalDatabaseDiffingService::CreateChangeset(sbIMediaList *aSource,
1591  sbIMediaList *aDestination,
1592  sbILibraryChangeset **_retval)
1593 {
1594  NS_ENSURE_ARG_POINTER(aSource);
1595  NS_ENSURE_ARG_POINTER(aDestination);
1596  NS_ENSURE_ARG_POINTER(_retval);
1597 
1598  nsCOMPtr<sbILibrary> sourceLibrary = do_QueryInterface(aSource);
1599  nsCOMPtr<sbILibrary> destinationLibrary = do_QueryInterface(aDestination);
1600 
1601  nsresult rv = NS_ERROR_FAILURE;
1602  nsCOMPtr<sbILibraryChangeset> changeset;
1603 
1604  if(sourceLibrary && destinationLibrary) {
1605 
1606  rv = CreateLibraryChangesetFromLibraries(sourceLibrary,
1607  destinationLibrary,
1608  getter_AddRefs(changeset));
1609 
1610  }
1611  else {
1612  rv = CreateLibraryChangesetFromLists(aSource,
1613  aDestination,
1614  getter_AddRefs(changeset));
1615  }
1616 
1617  NS_ENSURE_SUCCESS(rv, rv);
1618  changeset.forget(_retval);
1619 
1620  return NS_OK;
1621 }
1622 
1623 /* sbILibraryChangeset createMultiChangeset (in nsIArray aSources, in sbIMediaList aDestination); */
1624 NS_IMETHODIMP sbLocalDatabaseDiffingService::CreateMultiChangeset(nsIArray *aSources,
1625  sbIMediaList *aDestination,
1626  sbILibraryChangeset **_retval)
1627 {
1628  NS_ENSURE_ARG_POINTER(aSources);
1629  NS_ENSURE_ARG_POINTER(aDestination);
1630  NS_ENSURE_ARG_POINTER(_retval);
1631 
1632  nsresult rv = NS_ERROR_UNEXPECTED;
1633 
1634  // Destination must be a library.
1635  nsCOMPtr<sbILibrary> destinationLibrary =
1636  do_QueryInterface(aDestination);
1637  NS_ENSURE_TRUE(destinationLibrary, NS_ERROR_INVALID_ARG);
1638 
1639  nsCOMPtr<sbILibraryChangeset> changeset;
1641  destinationLibrary,
1642  getter_AddRefs(changeset));
1643  NS_ENSURE_SUCCESS(rv, rv);
1644 
1645  changeset.forget(_retval);
1646 
1647  return NS_OK;
1648 }
1649 
1650 /* AString createChangesetAsync (in sbIMediaList aSource, in sbIMediaList aDestination, [optional] in nsIObserver aObserver); */
1651 NS_IMETHODIMP sbLocalDatabaseDiffingService::CreateChangesetAsync(sbIMediaList *aSource,
1652  sbIMediaList *aDestination,
1653  nsIObserver *aObserver,
1654  nsAString & _retval)
1655 {
1656  NS_ENSURE_ARG_POINTER(aSource);
1657  NS_ENSURE_ARG_POINTER(aDestination);
1658 
1659  return NS_ERROR_NOT_IMPLEMENTED;
1660 }
1661 
1662 /* sbILibraryChangeset createMultiChangesetAsync (in nsIArray aSources, in sbIMediaList aDestination, [optional] in nsIObserver aObserver); */
1663 NS_IMETHODIMP sbLocalDatabaseDiffingService::CreateMultiChangesetAsync(nsIArray *aSources,
1664  sbIMediaList *aDestination,
1665  nsIObserver *aObserver,
1666  sbILibraryChangeset **_retval)
1667 {
1668  NS_ENSURE_ARG_POINTER(aSources);
1669  NS_ENSURE_ARG_POINTER(aDestination);
1670  NS_ENSURE_ARG_POINTER(_retval);
1671 
1672  return NS_ERROR_NOT_IMPLEMENTED;
1673 }
1674 
1675 /* sbILibraryChangeset getChangeset (in AString aChangesetCookie); */
1676 NS_IMETHODIMP sbLocalDatabaseDiffingService::GetChangeset(const nsAString & aChangesetCookie,
1677  sbILibraryChangeset **_retval)
1678 {
1679  NS_ENSURE_ARG_POINTER(_retval);
1680 
1681  return NS_ERROR_NOT_IMPLEMENTED;
1682 }
1683 
IDIndex::const_iterator ConstOriginIterator
NS_IMPL_THREADSAFE_ISUPPORTS1(sbLDBDSEnumerator, sbIMediaListEnumerationListener) sbLDBDSEnumerator
const unsigned long DELETED
var args
Definition: alert.js:8
return NS_OK
_updateCookies aPath
ConstIDIterator IDEnd() const
IDIndex::const_iterator ConstIDIterator
bool operator()(ItemInfos::iterator aLeft, nsID const &aID) const
#define SB_LOCALDATABASE_DIFFINGSERVICE_CONTRACTID
bool operator()(nsID const &aID, ItemInfos::const_iterator aRight) const
#define SB_PROPERTY_ORIGINLIBRARYGUID
bool operator()(nsID const &aID, ItemInfos::iterator aRight) const
#define SB_PROPERTY_ORIGINURL
nsresult CreatePropertyChangesFromProperties(sbIPropertyArray *aSourceProperties, sbIPropertyArray *aDestinationProperties, nsIArray **aPropertyChanges)
Interface used to enumerate the items in a media list.
nsCOMPtr< sbIPropertyManager > mPropertyManager
static NS_METHOD RegisterSelf(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *aLoaderStr, const char *aType, const nsModuleComponentInfo *aInfo)
PRUint64 nsString_ToUint64(const nsAString &str, nsresult *rv)
nsresult CreateLibraryChangesetFromLists(sbIMediaList *aSourceList, sbIMediaList *aDestinationList, sbILibraryChangeset **aLibraryChangeset)
nsID GetItemGUID(sbIMediaItem *aItem)
bool operator()(ItemInfos::const_iterator aLeft, ItemInfos::const_iterator aRight) const
T FindNextUsable(T aIter, T aEnd, M aMember)
Library and media list diffing service.
bool operator()(ItemInfos::iterator aLeft, ItemInfos::iterator aRight) const
nsresult CreateItemDeletedLibraryChange(sbIMediaItem *aDestinationItem, sbILibraryChange **aLibraryChange)
#define SB_PROPERTY_ORDINAL
static bool lessThan(nsID const &aLeftID, nsID const &aRightID)
IDIterator FindByID(nsID const &aID)
bool operator()(ItemInfos::const_iterator aLeft, nsID const &aID) const
nsresult CreateItemMovedLibraryChange(sbIMediaItem *aSourceItem, PRUint32 aItemOrdinal, sbILibraryChange **aLibraryChange)
bool operator()(ItemInfos::iterator aLeft, ItemInfos::iterator aRight) const
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
sbLibraryChangeset Definition.
static bool CompareID(ItemInfo const &aLeft, ItemInfo const &aRight)
A brief description of the contents of this interface.
bool operator()(ItemInfos::iterator aLeft, nsID const &aID) const
static nsID const NULL_GUID
std::vector< ItemInfo > ItemInfos
nsresult AddUniqueItem(nsTHashtable< nsIDHashKey > &aItems, sbIMediaItem *aItem)
NS_IMPL_THREADSAFE_RELEASE(sbRequestItem)
restoreDimensions aLeft
NS_IMPL_THREADSAFE_ADDREF(sbRequestItem)
ItemInfos::const_iterator const_iterator
#define SB_PROPERTYMANAGER_CONTRACTID
nsresult CreateLibraryChangesetFromLibraries(sbILibrary *aSourceLibrary, sbILibrary *aDestinationLibrary, sbILibraryChangeset **aLibraryChangeset)
void Build(IterT aBegin, IterT aEnd)
Definition: sbIndex.h:54
const_iterator find(KeyT const &aData) const
Definition: sbIndex.h:68
bool operator()(ItemInfos::const_iterator aLeft, ItemInfos::const_iterator aRight) const
#define SB_PROPERTY_CONTENTLENGTH
nsresult CreateLibraryChangesetFromListsToLibrary(nsIArray *aSourceLists, sbILibrary *aDestinationLibrary, sbILibraryChangeset **aLibraryChangeset)
sbLDBDSEnumerator * mDestinationEnum
#define SB_PROPERTY_CREATED
sbIndex< nsID, ItemInfos::iterator, IDCompare > IDIndex
#define SB_PROPERTY_UPDATED
OriginIterator FindByOrigin(nsID const &aID)
const unsigned short ENUMERATIONTYPE_SNAPSHOT
This flag means that the list being enumerated is a copy that may become out of date.
ConstOriginIterator FindByOrigin(nsID const &aID) const
const_iterator begin() const
Definition: sbIndex.h:94
nsresult CreateItemAddedLibraryChange(sbIMediaItem *aSourceItem, sbILibraryChange **aLibraryChange)
nsresult CreateChanges(sbIMediaList *aSrcLibrary, sbIMediaList *aDestLibrary, sbLDBDSEnumerator *aSrcEnum, sbLDBDSEnumerator *aDestEnum, nsIArray **aChanges)
sbIndex< nsID, ItemInfos::iterator, OriginIDCompare > OriginIDIndex
ConstOriginIterator OriginBegin() const
#define SB_PROPERTY_DURATION
nsString sbGUIDToString(nsID const &aID)
#define SB_PROPERTY_GUID
const unsigned long MODIFIED
Media library abstraction.
Definition: sbILibrary.idl:82
const_iterator end() const
Definition: sbIndex.h:98
#define LOG(args)
ConstOriginIterator OriginEnd() const
sbLocalDatabaseDiffingService * mDiffService
var uri
Definition: FeedWriter.js:1135
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
static void MarkLists(sbLDBDSEnumerator *aSrc, sbLDBDSEnumerator *aDest)
const_iterator begin() const
nsID GetGUIDProperty(sbIMediaItem *aItem, nsAString const &aProperty)
Interface that defines a single item of media in the system.
#define SB_LOCALDATABASE_DIFFINGSERVICE_DESCRIPTION
nsresult CreateLibraryChangeFromItems(sbIMediaItem *aSourceItem, sbIMediaItem *aDestinationItem, sbILibraryChange **aLibraryChange)
#define SB_PROPERTY_ISLIST
bool operator()(nsID const &aID, ItemInfos::const_iterator aRight) const
#define SB_PROPERTY_CONTENTURL
const unsigned long MOVED
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
static PLDHashOperator Enumerator(nsIDHashKey *aEntry, void *userArg)
#define SB_PROPERTY_ORIGINITEMGUID
NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER1(sbLocalDatabaseDiffingService, sbILibraryDiffingService) sbLocalDatabaseDiffingService
void LogMediaItem(char const *, sbIMediaItem *)
nsresult GetPropertyIDs(nsIStringEnumerator **aPropertyIDs)
Indexes::const_iterator const_iterator
Definition: sbIndex.h:38
const unsigned long ADDED
ConstIDIterator FindByID(nsID const &aID) const
bool operator()(ItemInfos::const_iterator aLeft, nsID const &aID) const
bool operator()(nsID const &aID, ItemInfos::iterator aRight) const
ConstIDIterator IDBegin() const