sbDeviceLibrarySyncDiff.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-2011 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 <nsIClassInfoImpl.h>
28 #include <nsIMutableArray.h>
29 
30 #include <sbIMediaListView.h>
31 #include <sbIDevice.h>
32 #include <sbIDeviceLibrary.h>
33 #include <sbIDeviceManager.h>
34 #include <sbILibraryChangeset.h>
35 #include <sbILibraryManager.h>
36 
37 #include <nsArrayUtils.h>
38 #include <nsTArray.h>
39 #include <nsISupportsUtils.h>
40 
41 #include <sbLibraryChangeset.h>
42 #include <sbLibraryUtils.h>
43 #include <sbStandardProperties.h>
44 #include <sbStringUtils.h>
45 #include <sbDebugUtils.h>
46 
47 // Sync2 core implementation.
48 //
49 // The 'Sync2' behaviour is driven by changesets (sbILibraryChangeset) created
50 // by the classes in this file. These describe the changes to be made to the
51 // target library. The changeset interfaces are somewhat limited - we've only
52 // added to the interface where needed to implement the current desired
53 // behaviour, which is somewhat constrained by the existing UI - e.g. there is
54 // no mechanism currently to select multiple playlists simultaneously, so drag
55 // and drop of multiple playlists is not currently implemented here. This sort
56 // of thing could be added fairly easily if desired.
57 //
58 // The changesets can be applied to devices via the sbIDevice.importFromDevice
59 // and sbIDevice.exportToDevice methods. In certain cases callers may wish to
60 // directly examine the contents of the changeset also.
61 //
62 // The public functions here are implemented in sbDeviceLibrarySyncDiff, the
63 // implementation of sbIDeviceLibrarySyncDiff. This drives the collection of
64 // objects to add/modify/etc. The bulk of the decision logic exists in the
65 // enumeration listeners (SyncDiffExportListener or SyncDiffImportListener); see
66 // those if you wish to change behaviour of what we do with a particular item.
67 //
68 // Comments here try to describe what behaviour we're implementing, but for
69 // information about _why_ we've chosen the particular behaviour at hand, the
70 // reader should instead see:
71 // http://wiki.songbirdnest.com/Releases/Ratatat/Device_Import_and_Sync
72 // In particular, the four attached images on that page show the decision in
73 // a relatively easily understood form.
74 
75 
76 // Library enumeration listener base class to collect items to sync
77 // This implements a variety of useful methods and basic functionality that is
78 // common to both export and import, but delegates all the actual
79 // decision-making about what to do for any given item to pure-virtual methods
80 // implemented in subclasses for import/export.
83 {
84 public:
86  NS_DECL_SBIMEDIALISTENUMERATIONLISTENER
87 
89  {
93  };
94  // Mode for enumeration - handle all items, only non-list items, or only
95  // list items.
96  enum HandleMode {
100  };
101 
102  // Type of change. This is used to indicate what action for sync behaviour
103  // is desired for a single item.
104  enum ChangeType {
105  CHANGE_NONE, // Item has been ignored.
106  CHANGE_ADD, // Item should be added to the destination
107  CHANGE_CLOBBER, // Item should be used to replace the existing destination
108  CHANGE_RETAIN // Existing destination item should be used
109  };
110 
111 
113  mMediaTypes(0),
116  {
117  }
118 
119  virtual nsresult Init(DropAction aDropAction,
120  sbILibrary *aMainLibrary,
121  sbILibrary *aDeviceLibrary);
122 
123  // Process any item (list or non-list), and act on it appropriately. This
124  // will add any desired changes to the computed changeset object.
125  virtual nsresult ProcessItem(sbIMediaList *aMediaList,
126  sbIMediaItem *aMediaItem) = 0;
127 
128  // Process a non-list item, determining what to do with it. Does not modify
129  // any state. This allows a caller to determine the behaviour desired without
130  // necessarily adding that action to the changeset.
131  virtual nsresult SelectChangeForItem(sbIMediaItem *aMediaItem,
132  ChangeType *aChangeType,
133  sbIMediaItem **aDestMediaItem) = 0;
134 
135  // Process a list item, determining what to do with it. Does not modify
136  // any state. As above, allows a caller to determine desired behaviour.
137  virtual nsresult SelectChangeForList(sbIMediaList *aMediaList,
138  ChangeType *aChangeType,
139  sbIMediaList **aDestMediaList) = 0;
140 
141  // Add a change (sbILibraryChange) to the changeset we're computing.
142  // If aSrcItem is a list, aListItems should contain the items we're adding to
143  // the list. See the documentation for sbILibraryChange.listItems for details
144  // on the semantics of this.
145  nsresult AddChange(PRUint32 aChangeType,
146  sbIMediaItem *aSrcItem,
147  sbIMediaItem *aDstItem,
148  nsIArray *aListItems = NULL);
149 
150  // Add a list change. Computes the items that need to go into the destination
151  // list according to the semantics of sbILibraryChange.listItems.
152  nsresult AddListChange(PRUint32 aChangeType,
153  sbIMediaList *aSrcList,
154  sbIMediaList *aDstList);
155 
156  // Set the media types to process. aMediaType is a bitfield, so multiple
157  // types may be selected. Any item NOT of a type indicated by a call to this
158  // function will be ignored - that is, the SelectChangeFor*() functions will
159  // return CHANGE_NONE for all such items.
160  void SetMediaTypes(PRUint32 aMediaTypes) {
161  mMediaTypes = aMediaTypes;
162  }
163 
164  // Set the mode for enumerating over the library - whether we're processing
165  // lists, non-lists, or both.
166  void SetHandleMode(HandleMode aMode) {
167  mHandleMode = aMode;
168  }
169 
170  // Call to finish processing. This will create the result mChangeset member.
171  // After calling this, no other functions should be called on this object
172  // (note that this constraint is not currently checked internally).
173  nsresult Finish();
174 
175 protected:
177 
178  // Helper function to create array of sbIPropertyChange objects for an
179  // item being added to the destination.
180  nsresult CreatePropertyChangesForItemAdded(sbIMediaItem *aSourceItem,
181  nsIArray **aPropertyChanges);
182  // Helper function to create array of sbIPropertyChange objects for an
183  // item being modified.
185  sbIMediaItem *aSourceItem,
186  sbIMediaItem *aDestinationItem,
187  nsIArray **aPropertyChanges);
188 
189  // Get a property on a media item and convert to a 64 bit integer; this is
190  // useful for our time properties such as the last modified time
191  // (SB_PROPERTY_UPDATED).
192  nsresult GetTimeProperty(sbIMediaItem *aMediaItem,
193  nsString aPropertyName,
194  PRInt64 *_result);
195 
196  // Get a playlist from aLibrary that matches aList. Returns null in
197  // aMatchingList is not found. Matching is done based on GUID, but since
198  // origin GUIDs are one-way, we need to implement this differently for export
199  // and import.
200  virtual nsresult GetMatchingPlaylist(
201  sbILibrary *aLibrary,
202  sbIMediaList *aList,
203  sbIMediaList **aMatchingList) = 0;
204 
205  // Returns true if the item aItem has a content type that we're currently
206  // processing.
207  bool HasCorrectContentType(sbIMediaItem *aItem);
208  // Returns true is aList is a mixed-content (audio and video) playlist.
209  bool ListIsMixed(sbIMediaList *aList);
210  // Returns true if we should process aList based on the current settings
211  // for content types to process.
213 
214  bool IsDrop() const
215  {
216  return mDropAction != NOT_DROP;
217  }
218  PRUint32 mMediaTypes;
221 
222  nsTHashtable<nsStringHashKey> mSeenMediaItems;
223 
224  nsCOMPtr<sbILibrary> mMainLibrary;
225  nsCOMPtr<sbILibrary> mDeviceLibrary;
226 
227  nsCOMPtr<nsIMutableArray> mLibraryChanges;
228 
229 public:
230  // The changeset we created
231  nsRefPtr<sbLibraryChangeset> mChangeset;
232 
233 };
234 
237 
238 // Enumerator used to determine which items to add to a destination playlist
239 // when adding or clobbering a list. This builds an array (in mListItems) that
240 // matches the semantics of sbILibraryChange.listItems.
241 class ListAddingEnumerationListener:
243 {
244 public:
246  NS_DECL_SBIMEDIALISTENUMERATIONLISTENER
247 
248  // Create an enumeration listener. aMainListener is the import or export
249  // listener used to determine whether the source item, destination item,
250  // or neither is added to aListItems. The caller should then call
251  // sourceList.enumerateAllItems() with this listener.
252  ListAddingEnumerationListener(SyncEnumListenerBase *aMainListener,
253  nsIMutableArray *aListItems):
254  mMainListener(aMainListener),
255  mListItems(aListItems)
256  {
257  };
258 
259  nsCOMPtr<SyncEnumListenerBase> mMainListener;
260  nsCOMPtr<nsIMutableArray> mListItems;
261 };
262 
263 NS_IMPL_THREADSAFE_ISUPPORTS1(ListAddingEnumerationListener,
265 
266 NS_IMETHODIMP
267 ListAddingEnumerationListener::OnEnumerationBegin(sbIMediaList *aMediaList,
268  PRUint16 *_retval)
269 {
271 
272  return NS_OK;
273 }
274 
275 NS_IMETHODIMP
276 ListAddingEnumerationListener::OnEnumeratedItem(sbIMediaList *aMediaList,
277  sbIMediaItem *aMediaItem,
278  PRUint16 *_retval)
279 {
280  NS_ENSURE_ARG_POINTER(aMediaList);
281  NS_ENSURE_ARG_POINTER(aMediaItem);
282  NS_ENSURE_ARG_POINTER(_retval);
283 
284  nsresult rv;
285 
287  nsCOMPtr<sbIMediaItem> destItem;
288  rv = mMainListener->SelectChangeForItem(aMediaItem,
289  &changeType,
290  getter_AddRefs(destItem));
291  NS_ENSURE_SUCCESS(rv, rv);
292 
293  switch(changeType) {
296  // For both of these, use the source item
297  rv = mListItems->AppendElement(aMediaItem, PR_FALSE);
298  NS_ENSURE_SUCCESS(rv, rv);
299  break;
301  // For this one, use the (existing) destination item
302  rv = mListItems->AppendElement(destItem, PR_FALSE);
303  NS_ENSURE_SUCCESS(rv, rv);
304  break;
305  default:
306  // And this means it was an item we chose not to transfer at all.
307  break;
308  }
309 
311 
312  return NS_OK;
313 }
314 
315 NS_IMETHODIMP
316 ListAddingEnumerationListener::OnEnumerationEnd(sbIMediaList *aMediaList,
317  nsresult aStatusCode)
318 {
319  return NS_OK;
320 }
321 
322 nsresult
324  sbILibrary *aMainLibrary,
325  sbILibrary *aDeviceLibrary)
326 {
327  nsresult rv;
328 
329  mDropAction = aDropAction;
330 
331  mMainLibrary = aMainLibrary;
332  mDeviceLibrary = aDeviceLibrary;
333 
334  NS_NEWXPCOM(mChangeset, sbLibraryChangeset);
335  NS_ENSURE_TRUE(mChangeset, NS_ERROR_OUT_OF_MEMORY);
336 
337  mLibraryChanges = do_CreateInstance(
338  "@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
339  NS_ENSURE_SUCCESS(rv, rv);
340 
341  mSeenMediaItems.Init();
342 
343  return NS_OK;
344 }
345 
346 nsresult
348 {
349  nsresult rv;
350 
351  rv = mChangeset->SetChanges(mLibraryChanges);
352  NS_ENSURE_SUCCESS(rv, rv);
353 
354  return NS_OK;
355 }
356 
357 NS_IMETHODIMP
358 SyncEnumListenerBase::OnEnumerationBegin(sbIMediaList *aMediaList,
359  PRUint16 *_retval)
360 {
362 
363  return NS_OK;
364 }
365 
366 NS_IMETHODIMP
367 SyncEnumListenerBase::OnEnumeratedItem(sbIMediaList *aMediaList,
368  sbIMediaItem *aMediaItem,
369  PRUint16 *_retval)
370 {
371  NS_ENSURE_ARG_POINTER(aMediaList);
372  NS_ENSURE_ARG_POINTER(aMediaItem);
373  NS_ENSURE_ARG_POINTER(_retval);
374 
375  nsresult rv;
376 
377  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
378  bool isList = NS_SUCCEEDED(rv);
379 
380  // Skip things that aren't what we're trying to handle currently.
381  if ((mHandleMode == HANDLE_LISTS && !isList) ||
382  (mHandleMode == HANDLE_ITEMS && isList))
383  {
385  return NS_OK;
386  }
387 
388  // First: is this something we've already added? Ignore if so.
389  nsString itemId;
390  rv = aMediaItem->GetGuid(itemId);
391  NS_ENSURE_SUCCESS(rv, rv);
392 
393  if (mSeenMediaItems.GetEntry(itemId)) {
395  return NS_OK;
396  }
397  nsStringHashKey *key = mSeenMediaItems.PutEntry(itemId);
398  NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
399 
400  // Also ignore hidden items.
401  nsString hidden;
402  rv = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
403  hidden);
404  NS_ENSURE_SUCCESS(rv, rv);
405 
406  if (!hidden.EqualsLiteral("1")) {
407  // Finally, we've decided we're actually going to process an item - so go
408  // ahead. This will add the appropriate change (if any) to the changeset.
409  rv = ProcessItem(aMediaList, aMediaItem);
410  NS_ENSURE_SUCCESS(rv, rv);
411  }
412 
414 
415  return NS_OK;
416 }
417 
418 NS_IMETHODIMP
419 SyncEnumListenerBase::OnEnumerationEnd(sbIMediaList *aMediaList,
420  nsresult aStatusCode)
421 {
422  return NS_OK;
423 }
424 
425 bool
427 {
428  nsresult rv;
429 
430  PRUint16 listType;
431  rv = aList->GetListContentType(&listType);
432  NS_ENSURE_SUCCESS(rv, false);
433 
434  return listType ==
436 }
437 
438 bool
440 {
441  nsresult rv;
442 
443  PRUint16 listType;
444  rv = aList->GetListContentType(&listType);
445  NS_ENSURE_SUCCESS(rv, false);
446 
447  if (listType ==
449  {
450  // Special case. A mixed content playlist is always considered to be of the
451  // correct type. Mixed content playlists are crazy-shit.
452  return true;
453  }
454  else if ((listType == sbIMediaList::CONTENTTYPE_AUDIO &&
456  (listType == sbIMediaList::CONTENTTYPE_VIDEO &&
458  {
459  return true;
460  }
461 
462  return false;
463 }
464 
465 bool
467 {
468  nsresult rv;
469 
470  nsString contentType;
471  rv = aItem->GetContentType(contentType);
472  NS_ENSURE_SUCCESS(rv, false);
473 
474  if ((contentType.EqualsLiteral("audio") &&
476  (contentType.EqualsLiteral("video") &&
478  return true;
479  return false;
480 }
481 
482 nsresult
484  sbIMediaItem *aSourceItem,
485  sbIMediaItem *aDestinationItem,
486  nsIArray **aPropertyChanges)
487 {
488  nsresult rv;
489 
490  nsCOMPtr<sbIPropertyArray> sourceProperties;
491  nsCOMPtr<sbIPropertyArray> destinationProperties;
492 
493  rv = aSourceItem->GetProperties(nsnull,
494  getter_AddRefs(sourceProperties));
495  NS_ENSURE_SUCCESS(rv, rv);
496  rv = aDestinationItem->GetProperties(nsnull,
497  getter_AddRefs(destinationProperties));
498  NS_ENSURE_SUCCESS(rv, rv);
499 
500  nsCOMPtr<nsIMutableArray> propertyChanges =
501  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
502  NS_ENSURE_SUCCESS(rv, rv);
503 
504  PRUint32 sourceLength;
505  rv = sourceProperties->GetLength(&sourceLength);
506  NS_ENSURE_SUCCESS(rv, rv);
507 
508  PRUint32 destinationLength;
509  rv = destinationProperties->GetLength(&destinationLength);
510  NS_ENSURE_SUCCESS(rv, rv);
511 
512  nsCOMPtr<sbIProperty> property;
513  nsTHashtable<nsStringHashKey> sourcePropertyNamesFoundInDestination;
514 
515  PRBool success = sourcePropertyNamesFoundInDestination.Init();
516  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
517 
518  // These properties are excluded from checking since they
519  // are automatically maintained and do not reflect actual
520  // metadata differences in the items.
521  nsTHashtable<nsStringHashKey> propertyExclusionList;
522  success = propertyExclusionList.Init();
523  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
524 
525  nsStringHashKey* successHashkey =
526  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_CREATED));
527  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
528 
529  successHashkey =
530  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_UPDATED));
531  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
532 
533  successHashkey =
534  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_GUID));
535  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
536 
537  successHashkey =
538  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID));
539  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
540 
541  successHashkey =
542  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_ORIGINLIBRARYGUID));
543  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
544 
545  successHashkey =
546  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_ORIGINURL));
547  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
548 
549  // Content URL for source and destination will never bee the same
550  successHashkey =
551  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL));
552  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
553 
554  // Sadly we can't use content length because on a device the length may be
555  // different
556  successHashkey =
557  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_CONTENTLENGTH));
558  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
559 
560  successHashkey =
561  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_ORIGIN_IS_IN_MAIN_LIBRARY));
562  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
563 
564  // Playlist URL will never be the same for ML and DL
565  successHashkey =
566  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_PLAYLISTURL));
567  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
568 
569  // The availability property is just so we don't display items on the
570  // the device that haven't been copied so we can ignore this
571  successHashkey =
572  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_AVAILABILITY));
573  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
574 
575  // Column spec property may be different or absent between ML and DL and should
576  // be ignored
577  successHashkey =
578  propertyExclusionList.PutEntry(NS_LITERAL_STRING(SB_PROPERTY_COLUMNSPEC));
579  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
580 
581  nsString propertyId;
582  nsString propertyValue;
583  nsString propertyDestinationValue;
584 
585  // First, we process the sourceProperties.
586  for(PRUint32 current = 0; current < sourceLength; ++current)
587  {
588  rv = sourceProperties->GetPropertyAt(current, getter_AddRefs(property));
589  NS_ENSURE_SUCCESS(rv, rv);
590 
591  rv = property->GetId(propertyId);
592  NS_ENSURE_SUCCESS(rv, rv);
593 
594  rv = property->GetValue(propertyValue);
595  NS_ENSURE_SUCCESS(rv, rv);
596 
597  if(propertyExclusionList.GetEntry(propertyId)) {
598  continue;
599  }
600 
601  rv = destinationProperties->GetPropertyValue(propertyId,
602  propertyDestinationValue);
603  // Property has been added.
604  if(rv == NS_ERROR_NOT_AVAILABLE) {
605  // content type defaults to audio if not present
606  if (propertyId.Equals(NS_LITERAL_STRING(SB_PROPERTY_CONTENTTYPE)) &&
607  propertyValue.Equals(NS_LITERAL_STRING("audio"))) {
608  continue;
609  }
610  nsRefPtr<sbPropertyChange> propertyChange;
611  NS_NEWXPCOM(propertyChange, sbPropertyChange);
612  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
613 
614  rv = propertyChange->InitWithValues(sbIChangeOperation::ADDED,
615  propertyId,
616  EmptyString(),
617  propertyValue);
618  NS_ENSURE_SUCCESS(rv, rv);
619 
620  nsCOMPtr<nsISupports> element =
621  do_QueryInterface(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange), &rv);
622  NS_ENSURE_SUCCESS(rv, rv);
623 
624  rv = propertyChanges->AppendElement(element,
625  PR_FALSE);
626  NS_ENSURE_SUCCESS(rv, rv);
627  }
628  else {
629 
630  NS_ENSURE_SUCCESS(rv, rv);
631 
632  // Didn't fail, it should be present in both source and destination.
633  successHashkey = sourcePropertyNamesFoundInDestination.PutEntry(propertyId);
634  NS_ENSURE_TRUE(successHashkey, NS_ERROR_OUT_OF_MEMORY);
635 
636  // Check if the duration is the same in seconds, that's good enough
637  if (propertyId.EqualsLiteral(SB_PROPERTY_DURATION)) {
638  PRUint64 const sourceDuration = nsString_ToUint64(propertyValue, &rv);
639  if (NS_SUCCEEDED(rv)) {
640  PRUint64 const destDuration =
641  nsString_ToUint64(propertyDestinationValue, &rv);
642  // If the duration was parsed and the difference less than a second
643  // then treat it as unchanged
644  if (NS_SUCCEEDED(rv)) {
645  // MSVC has no llabs(), so do it this way.
646  PRInt64 durationDiff = sourceDuration - destDuration;
647  if ((durationDiff < 0 && -durationDiff < PR_USEC_PER_SEC) ||
648  durationDiff < PR_USEC_PER_SEC)
649  continue;
650  }
651  }
652  }
653  // Property values are the same, nothing changed,
654  // continue onto the next property.
655  else if(propertyValue.Equals(propertyDestinationValue)) {
656  continue;
657  }
658 
659  nsRefPtr<sbPropertyChange> propertyChange;
660  NS_NEWXPCOM(propertyChange, sbPropertyChange);
661  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
662 
663  rv = propertyChange->InitWithValues(sbIChangeOperation::MODIFIED,
664  propertyId,
665  propertyDestinationValue,
666  propertyValue);
667  NS_ENSURE_SUCCESS(rv, rv);
668 
669  nsCOMPtr<nsISupports> element =
670  do_QueryInterface(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange), &rv);
671  NS_ENSURE_SUCCESS(rv, rv);
672  rv = propertyChanges->AppendElement(element,
673  PR_FALSE);
674  NS_ENSURE_SUCCESS(rv, rv);
675  }
676  }
677 
678  // Second, we process the destinationProperties.
679  // This will enable us to determine which properties were removed
680  // from the source.
681  for(PRUint32 current = 0; current < destinationLength; ++current) {
682  rv = destinationProperties->GetPropertyAt(current,
683  getter_AddRefs(property));
684  NS_ENSURE_SUCCESS(rv, rv);
685 
686  rv = property->GetId(propertyId);
687  NS_ENSURE_SUCCESS(rv, rv);
688 
689  rv = property->GetValue(propertyDestinationValue);
690  NS_ENSURE_SUCCESS(rv, rv);
691 
692  if(propertyExclusionList.GetEntry(propertyId)) {
693  continue;
694  }
695 
696  if(!sourcePropertyNamesFoundInDestination.GetEntry(propertyId)) {
697 
698  // We couldn't find the property in the source properties, this means
699  // the property must've been removed.
700  nsRefPtr<sbPropertyChange> propertyChange;
701  NS_NEWXPCOM(propertyChange, sbPropertyChange);
702  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
703 
704  rv = propertyChange->InitWithValues(sbIChangeOperation::DELETED,
705  propertyId,
706  propertyDestinationValue,
707  EmptyString());
708  NS_ENSURE_SUCCESS(rv, rv);
709 
710  rv = propertyChanges->AppendElement(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange),
711  PR_FALSE);
712  NS_ENSURE_SUCCESS(rv, rv);
713  }
714  }
715 
716  return CallQueryInterface(propertyChanges.get(), aPropertyChanges);
717 }
718 
719 nsresult
721  sbIMediaItem *aSourceItem,
722  nsIArray **aPropertyChanges)
723 {
724  nsCOMPtr<sbIPropertyArray> properties;
725  nsresult rv = aSourceItem->GetProperties(nsnull, getter_AddRefs(properties));
726  NS_ENSURE_SUCCESS(rv, rv);
727 
728  nsCOMPtr<nsIMutableArray> propertyChanges =
729  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
730  NS_ENSURE_SUCCESS(rv, rv);
731 
732  PRUint32 propertyCount = 0;
733  rv = properties->GetLength(&propertyCount);
734  NS_ENSURE_SUCCESS(rv, rv);
735 
736  nsString strPropertyID;
737  nsString strPropertyValue;
738  nsCOMPtr<sbIProperty> property;
739 
740  for(PRUint32 current = 0; current < propertyCount; ++current) {
741 
742  rv = properties->GetPropertyAt(current, getter_AddRefs(property));
743  NS_ENSURE_SUCCESS(rv, rv);
744 
745  rv = property->GetId(strPropertyID);
746  NS_ENSURE_SUCCESS(rv, rv);
747 
748  rv = property->GetValue(strPropertyValue);
749  NS_ENSURE_SUCCESS(rv, rv);
750 
751  nsRefPtr<sbPropertyChange> propertyChange;
752  NS_NEWXPCOM(propertyChange, sbPropertyChange);
753  NS_ENSURE_TRUE(propertyChange, NS_ERROR_OUT_OF_MEMORY);
754 
755  rv = propertyChange->InitWithValues(sbIChangeOperation::ADDED,
756  strPropertyID,
757  EmptyString(),
758  strPropertyValue);
759  NS_ENSURE_SUCCESS(rv, rv);
760 
761  nsCOMPtr<nsISupports> element =
762  do_QueryInterface(NS_ISUPPORTS_CAST(sbIPropertyChange *, propertyChange), &rv);
763  NS_ENSURE_SUCCESS(rv, rv);
764 
765  rv = propertyChanges->AppendElement(element,
766  PR_FALSE);
767  NS_ENSURE_SUCCESS(rv, rv);
768  }
769 
770  return CallQueryInterface(propertyChanges.get(), aPropertyChanges);
771 }
772 
773 nsresult
775  sbIMediaList *aSrcList,
776  sbIMediaList *aDstList)
777 {
778  nsresult rv;
779 
780  nsCOMPtr<nsIMutableArray> listItems =
781  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
782 
783  // Now we want to enumerate aSrcList, and for each item, determine a
784  // corresponding item (or null). If we determine any item, add it to
785  // listItems.
786  nsRefPtr<ListAddingEnumerationListener> listener =
787  new ListAddingEnumerationListener(this, listItems);
788 
789  aSrcList->EnumerateAllItems(listener,
791  NS_ENSURE_SUCCESS(rv, rv);
792 
793  nsCOMPtr<sbIMediaItem> srcItem = do_QueryInterface(aSrcList, &rv);
794  NS_ENSURE_SUCCESS(rv, rv);
795 
796  nsCOMPtr<sbIMediaItem> dstItem;
797  if (aDstList) {
798  dstItem = do_QueryInterface(aDstList, &rv);
799  NS_ENSURE_SUCCESS(rv, rv);
800  }
801 
802  rv = AddChange(aChangeType,
803  srcItem,
804  dstItem,
805  listItems);
806  NS_ENSURE_SUCCESS(rv, rv);
807 
808  return NS_OK;
809 }
810 
811 nsresult
812 SyncEnumListenerBase::AddChange(PRUint32 aChangeType,
813  sbIMediaItem *aSrcItem,
814  sbIMediaItem *aDstItem,
815  nsIArray *aListItems)
816 {
817  nsresult rv;
818 
819  nsRefPtr<sbLibraryChange> libraryChange;
820  NS_NEWXPCOM(libraryChange, sbLibraryChange);
821  NS_ENSURE_TRUE(libraryChange, NS_ERROR_OUT_OF_MEMORY);
822 
823  nsCOMPtr<nsIArray> propertyChanges;
824 
825  if (aChangeType == sbIChangeOperation::ADDED) {
826  rv = CreatePropertyChangesForItemAdded(aSrcItem,
827  getter_AddRefs(propertyChanges));
828  NS_ENSURE_SUCCESS(rv, rv);
829  }
830  else if (aChangeType == sbIChangeOperation::MODIFIED) {
832  aDstItem,
833  getter_AddRefs(propertyChanges));
834  NS_ENSURE_SUCCESS(rv, rv);
835  }
836  else {
837  PR_NOT_REACHED("Wrong change type");
838  }
839 
840  rv = libraryChange->InitWithValues(aChangeType,
841  0,
842  aSrcItem,
843  aDstItem,
844  propertyChanges,
845  aListItems);
846  NS_ENSURE_SUCCESS(rv, rv);
847 
848  nsCOMPtr<nsISupports> element =
849  do_QueryInterface(
850  NS_ISUPPORTS_CAST(sbILibraryChange *, libraryChange), &rv);
851  NS_ENSURE_SUCCESS(rv, rv);
852 
853  rv = mLibraryChanges->AppendElement(element, PR_FALSE);
854  NS_ENSURE_SUCCESS(rv, rv);
855 
856  return NS_OK;
857 }
858 
859 nsresult
861  nsString aPropertyName,
862  PRInt64 *_result)
863 {
864  nsAutoString str;
865  nsresult rv = aMediaItem->GetProperty(aPropertyName, str);
866  NS_ENSURE_SUCCESS(rv, rv);
867 
868  *_result = nsString_ToInt64(str, &rv);
869  NS_ENSURE_SUCCESS(rv, rv);
870 
871  return NS_OK;
872 }
873 
875 {
876 public:
877 
879 
880  virtual nsresult ProcessItem(sbIMediaList *aMediaList,
881  sbIMediaItem *aMediaItem);
882 
883  virtual nsresult SelectChangeForItem(sbIMediaItem *aMediaItem,
884  ChangeType *aChangeType,
885  sbIMediaItem **aDestMediaItem);
886  virtual nsresult SelectChangeForList(sbIMediaList *aMediaList,
887  ChangeType *aChangeType,
888  sbIMediaList **aDestMediaList);
889 
890 protected:
891  nsresult GetItemWithOriginGUID(sbILibrary *aDeviceLibrary,
892  nsString aItemID,
893  sbIMediaItem **aMediaItem);
894 
895  virtual nsresult GetMatchingPlaylist(
896  sbILibrary *aLibrary,
897  sbIMediaList *aList,
898  sbIMediaList **aMatchingList);
899 
900 private:
901  virtual ~SyncExportEnumListener() { }
902 
903 public:
904  nsTArray<nsCOMPtr<sbIMediaList> > mMixedContentPlaylists;
905 };
906 
907 /* Look for an item in the device library with an origin item GUID matching
908  aItemID */
909 nsresult
911  nsString aItemID,
912  sbIMediaItem **aMediaItem)
913 {
914  nsresult rv;
915 
916  nsCOMPtr<nsIArray> items;
917  rv = aDeviceLibrary->GetItemsByProperty(
918  NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID),
919  aItemID,
920  getter_AddRefs(items));
921  if (rv == NS_ERROR_NOT_AVAILABLE) {
922  *aMediaItem = NULL;
923  return NS_OK;
924  }
925 
926  // We shouldn't ever get multiple matches here. If we do, warn, and just
927  // return the first.
928  PRUint32 count;
929  rv = items->GetLength(&count);
930  NS_ENSURE_SUCCESS(rv, rv);
931 
932  NS_WARN_IF_FALSE(count < 2, "Multiple OriginGUID matches");
933  NS_ASSERTION(count == 1, "GetItemsByProperty returned bogus array");
934 
935  nsCOMPtr<sbIMediaItem> item = do_QueryElementAt(items, 0, &rv);
936  NS_ENSURE_SUCCESS(rv, rv);
937 
938  item.forget(aMediaItem);
939 
940  return NS_OK;
941 }
942 
943 nsresult
945  sbILibrary *aLibrary,
946  sbIMediaList *aList,
947  sbIMediaList **aMatchingList)
948 {
949  nsresult rv;
950 
951  nsString listId;
952  rv = aList->GetGuid(listId);
953  NS_ENSURE_SUCCESS(rv, rv);
954 
955  nsCOMPtr<sbIMediaItem> matchingItem;
956  rv = GetItemWithOriginGUID(aLibrary, listId, getter_AddRefs(matchingItem));
957  NS_ENSURE_SUCCESS(rv, rv);
958 
959  if (matchingItem) {
960  rv = CallQueryInterface(matchingItem.get(), aMatchingList);
961  NS_ENSURE_SUCCESS(rv, rv);
962  }
963 
964  return NS_OK;
965 }
966 
967 nsresult
969  sbIMediaItem *aMediaItem)
970 {
971  nsresult rv;
972 
973  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
974  if (NS_SUCCEEDED(rv)) {
975  if (ListIsMixed(itemAsList)) {
976  // We have to do some absurd things with mixed-content playlists; keep
977  // track of them.
978  nsCOMPtr<sbIMediaList>* element =
979  mMixedContentPlaylists.AppendElement(itemAsList);
980  NS_ENSURE_TRUE(element, NS_ERROR_OUT_OF_MEMORY);
981  }
982 
983  ChangeType changeType = CHANGE_NONE;
984  nsCOMPtr<sbIMediaList> destList;
985  rv = SelectChangeForList(itemAsList, &changeType, getter_AddRefs(destList));
986  NS_ENSURE_SUCCESS(rv, rv);
987 
988  switch (changeType) {
989  case CHANGE_ADD:
991  itemAsList,
992  NULL);
993  NS_ENSURE_SUCCESS(rv, rv);
994  break;
995  case CHANGE_CLOBBER:
997  itemAsList,
998  destList);
999  NS_ENSURE_SUCCESS(rv, rv);
1000  break;
1001  default:
1002  // Others we do nothing with
1003  break;
1004  }
1005  }
1006  else {
1007  // Normal media item.
1008  ChangeType changeType = CHANGE_NONE;
1009  nsCOMPtr<sbIMediaItem> destItem;
1010  rv = SelectChangeForItem(aMediaItem, &changeType, getter_AddRefs(destItem));
1011  NS_ENSURE_SUCCESS(rv, rv);
1012 
1013  switch (changeType) {
1014  case CHANGE_ADD:
1016  aMediaItem,
1017  NULL);
1018  NS_ENSURE_SUCCESS(rv, rv);
1019  break;
1020  case CHANGE_CLOBBER:
1022  aMediaItem,
1023  destItem);
1024  NS_ENSURE_SUCCESS(rv, rv);
1025  break;
1026  default:
1027  // Others we do nothing with
1028  break;
1029  }
1030  }
1031 
1032  return NS_OK;
1033 }
1034 
1035 nsresult
1037  ChangeType *aChangeType,
1038  sbIMediaList **aDestMediaList)
1039 {
1040  nsresult rv;
1041 
1042  if (!ListHasCorrectContentType(aMediaList)) {
1043  *aChangeType = CHANGE_NONE;
1044  return NS_OK;
1045  }
1046 
1047  // We have a list. We only need to sync the list itself. If we're syncing
1048  // all content we'll enumerate the items anyway. If we're syncing individual
1049  // playlists, we'll explicitly enumerate each of those.
1050  nsCOMPtr<sbIMediaList> matchingPlaylist;
1052  aMediaList,
1053  getter_AddRefs(matchingPlaylist));
1054  NS_ENSURE_SUCCESS(rv, rv);
1055 
1056  if (matchingPlaylist) {
1057  // Check sync time vs. last modified time.
1058  PRInt64 itemLastModifiedTime;
1059  PRInt64 lastSyncTime;
1060  rv = aMediaList->GetUpdated(&itemLastModifiedTime);
1061  NS_ENSURE_SUCCESS(rv, rv);
1062 
1064  NS_LITERAL_STRING(SB_PROPERTY_LAST_SYNC_TIME),
1065  &lastSyncTime);
1066 
1067  if (NS_SUCCEEDED(rv) && itemLastModifiedTime > lastSyncTime) {
1068  // Ok, we have a match - clobber the target item.
1069  *aChangeType = CHANGE_CLOBBER;
1070  }
1071  else {
1072  // Otherwise, the playlist hasn't been modified since the last sync, or
1073  // we couldn't find the last sync time; don't do anything.
1074  *aChangeType = CHANGE_RETAIN;
1075  }
1076 
1077  matchingPlaylist.forget(aDestMediaList);
1078  }
1079  else {
1080  // No match found; add a new item.
1081  *aChangeType = CHANGE_ADD;
1082  }
1083  return NS_OK;
1084 }
1085 
1086 
1087 nsresult
1089  ChangeType *aChangeType,
1090  sbIMediaItem **aDestMediaItem)
1091 {
1092  nsresult rv;
1093 
1094  if (!HasCorrectContentType(aMediaItem)) {
1095  *aChangeType = CHANGE_NONE;
1096  return NS_OK;
1097  }
1098 
1099  // Now we get to the core of the sync logic!
1100  // Does the device library have an item with origin GUID equal to this
1101  // item's guid?
1102  nsString itemId;
1103  rv = aMediaItem->GetGuid(itemId);
1104  NS_ENSURE_SUCCESS(rv, rv);
1105 
1106  nsCOMPtr<sbIMediaItem> destMediaItem;
1108  itemId,
1109  getter_AddRefs(destMediaItem));
1110  PRBool hasOriginMatch = NS_SUCCEEDED(rv) && destMediaItem;
1111 
1112  if (mDropAction == DROP_TRACKS) {
1113  // Drag-and drop case is simple: if we have a matching origin item,
1114  // overwrite. Otherwise, add a new one.
1115  if (hasOriginMatch) {
1116  *aChangeType = CHANGE_CLOBBER;
1117  destMediaItem.forget(aDestMediaItem);
1118  return NS_OK;
1119  }
1120  else {
1121  *aChangeType = CHANGE_ADD;
1122  return NS_OK;
1123  }
1124  }
1125  else {
1126  // Sync case
1127  if (hasOriginMatch) {
1128  PRInt64 itemLastModifiedTime;
1129  PRInt64 lastSyncTime;
1130  rv = aMediaItem->GetUpdated(&itemLastModifiedTime);
1131  NS_ENSURE_SUCCESS(rv, rv);
1132 
1134  NS_LITERAL_STRING(SB_PROPERTY_LAST_SYNC_TIME),
1135  &lastSyncTime);
1136 
1137  if (NS_SUCCEEDED(rv) && itemLastModifiedTime > lastSyncTime) {
1138  // Ok. We want to overwrite the destination media item.
1139  *aChangeType = CHANGE_CLOBBER;
1140  destMediaItem.forget(aDestMediaItem);
1141 
1142  return NS_OK;
1143  }
1144  else {
1145  // No changes needed; we just point at the existing matched item.
1146  *aChangeType = CHANGE_RETAIN;
1147  destMediaItem.forget(aDestMediaItem);
1148 
1149  return NS_OK;
1150  }
1151  }
1152  else {
1153  // We don't have a definite (OriginGUID based) match, how about an
1154  // identity (hash) based match?
1155  nsCOMPtr<nsIArray> matchedItems;
1156  rv = mDeviceLibrary->GetItemsWithSameIdentity(
1157  aMediaItem,
1158  getter_AddRefs(matchedItems));
1159  NS_ENSURE_SUCCESS(rv, rv);
1160 
1161  PRUint32 matchedItemsLength;
1162  rv = matchedItems->GetLength(&matchedItemsLength);
1163  NS_ENSURE_SUCCESS(rv, rv);
1164 
1165  if (matchedItemsLength == 0) {
1166  // Ok, no match - appears to be an all-new file. Copy it as a new item
1167  // to the destination library.
1168  *aChangeType = CHANGE_ADD;
1169  return NS_OK;
1170  }
1171  else {
1172  // Otherwise: looks like it's probably a dupe, but we're not sure, so...
1173  // do nothing! Just point at the first thing we matched.
1174  *aChangeType = CHANGE_RETAIN;
1175 
1176  nsCOMPtr<sbIMediaItem> matchItem = do_QueryElementAt(
1177  matchedItems, 0, &rv);
1178  NS_ENSURE_SUCCESS(rv, rv);
1179 
1180  matchItem.forget(aDestMediaItem);
1181  return NS_OK;
1182  }
1183  }
1184  }
1185 }
1186 
1187 // Library enumeration listener to collect items to import from a device
1189 {
1190 public:
1191 
1193 
1194  virtual nsresult ProcessItem(sbIMediaList *aMediaList,
1195  sbIMediaItem *aMediaItem);
1196  virtual nsresult SelectChangeForItem(sbIMediaItem *aMediaItem,
1197  ChangeType *aChangeType,
1198  sbIMediaItem **aDestMediaItem);
1199  virtual nsresult SelectChangeForList(sbIMediaList *aMediaList,
1200  ChangeType *aChangeType,
1201  sbIMediaList **aDestMediaList);
1202 
1203 protected:
1205 
1206  nsresult IsFromMainLibrary(sbIMediaItem *aMediaItem,
1207  PRBool *aFromMainLibrary);
1208  nsresult GetItemInMainLibrary(sbIMediaItem *aMediaItem,
1209  sbIMediaItem **aMainLibraryItem);
1210  nsresult GetSimplePlaylistWithSameName(sbILibrary *aLibrary,
1211  sbIMediaList *aList,
1212  sbIMediaList **aMatchingList);
1213 
1214  virtual nsresult GetMatchingPlaylist(
1215  sbILibrary *aLibrary,
1216  sbIMediaList *aList,
1217  sbIMediaList **aMatchingList);
1218 };
1219 
1220 nsresult
1222  sbILibrary *aLibrary,
1223  sbIMediaList *aList,
1224  sbIMediaList **aMatchingList)
1225 
1226 {
1227  nsresult rv;
1228 
1229  // Find a list with type 'simple' and a name matching aList's name from
1230  // library 'aLibrary'. There might be multiple matches; in that case just
1231  // return the first.
1232  nsString listName;
1233  rv = aList->GetName(listName);
1234  NS_ENSURE_SUCCESS(rv, rv);
1235 
1236  // The list name is internally stored as the MEDIALISTNAME property.
1237  nsCOMPtr<nsIArray> items;
1238  rv = aLibrary->GetItemsByProperty(
1239  NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME),
1240  listName,
1241  getter_AddRefs(items));
1242  if (rv == NS_ERROR_NOT_AVAILABLE) {
1243  return NS_OK;
1244  }
1245  NS_ENSURE_SUCCESS(rv, rv);
1246 
1247  PRUint32 itemsLength;
1248  rv = items->GetLength(&itemsLength);
1249  NS_ENSURE_SUCCESS(rv, rv);
1250 
1251  for (PRUint32 i = 0; i < itemsLength; i++) {
1252  nsCOMPtr<sbIMediaList> list = do_QueryElementAt(items, 0, &rv);
1253  if (NS_FAILED(rv))
1254  continue;
1255 
1256  nsString playlistType;
1257  rv = list->GetType(playlistType);
1258  NS_ENSURE_SUCCESS(rv, rv);
1259 
1260  if (playlistType.EqualsLiteral("simple")) {
1261  // Found one; return it. We don't check to see if there might be multiple
1262  // matches.
1263  list.forget(aMatchingList);
1264  return NS_OK;
1265  }
1266  }
1267 
1268  // Not found
1269  *aMatchingList = NULL;
1270  return NS_OK;
1271 }
1272 
1273 nsresult
1275  PRBool *aFromMainLibrary)
1276 {
1277  // Determine if we know this is from the main library. Must have an origin
1278  // item GUID (which need not point to a currently-existing item!), and have an
1279  // origin library GUID matching the main library.
1280  nsresult rv;
1281 
1282  nsString originItemGUID;
1283  rv = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID),
1284  originItemGUID);
1285  NS_ENSURE_SUCCESS(rv, rv);
1286 
1287  if (originItemGUID.IsVoid()) {
1288  *aFromMainLibrary = PR_FALSE;
1289  return NS_OK;
1290  }
1291 
1292  nsString originLibraryGUID;
1293  rv = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINLIBRARYGUID),
1294  originLibraryGUID);
1295  NS_ENSURE_SUCCESS(rv, rv);
1296 
1297  nsString mainLibraryGUID;
1298  rv = mMainLibrary->GetGuid(mainLibraryGUID);
1299  NS_ENSURE_SUCCESS(rv, rv);
1300 
1301  *aFromMainLibrary = originLibraryGUID.Equals(mainLibraryGUID);
1302  return NS_OK;
1303 }
1304 
1305 nsresult
1307  sbIMediaItem **aMainLibraryItem)
1308 {
1309  // Is the origin item actually IN the main library? This is assumed to only
1310  // be called if we already know that the origin points at the main library.
1311  // Returns the object if found.
1312  nsresult rv;
1313 
1314  nsString originItemGUID;
1315  rv = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID),
1316  originItemGUID);
1317  NS_ENSURE_SUCCESS(rv, rv);
1318 
1319  nsCOMPtr<sbIMediaItem> item;
1320  rv = mMainLibrary->GetMediaItem(originItemGUID, getter_AddRefs(item));
1321 
1322  if (NS_SUCCEEDED(rv) && item) {
1323  item.forget(aMainLibraryItem);
1324  }
1325 
1326  return NS_OK;
1327 }
1328 
1329 nsresult
1331  sbIMediaList *aList,
1332  sbIMediaList **aMatchingList)
1333 {
1334  nsresult rv;
1335 
1336  // Ensure the origin library matches aLibrary
1337  nsString originLibraryGUID;
1338  rv = aList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINLIBRARYGUID),
1339  originLibraryGUID);
1340  NS_ENSURE_SUCCESS(rv, rv);
1341 
1342  nsString libraryGUID;
1343  rv = aLibrary->GetGuid(libraryGUID);
1344  NS_ENSURE_SUCCESS(rv, rv);
1345 
1346  if (!libraryGUID.Equals(originLibraryGUID)) {
1347  *aMatchingList = nsnull;
1348  return NS_OK;
1349  }
1350 
1351  // Get the origin item ID, which we'll look up in aLibrary
1352  nsString originItemGUID;
1353  rv = aList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINITEMGUID),
1354  originItemGUID);
1355  NS_ENSURE_SUCCESS(rv, rv);
1356 
1357 
1358  nsCOMPtr<sbIMediaItem> matchingItem;
1359  rv = aLibrary->GetMediaItem(originItemGUID, getter_AddRefs(matchingItem));
1360  // Possible we might not find the original item return null then
1361  if (rv == NS_ERROR_NOT_AVAILABLE) {
1362  *aMatchingList = nsnull;
1363  return NS_OK;
1364  }
1365  NS_ENSURE_SUCCESS(rv, rv);
1366 
1367  return CallQueryInterface(matchingItem.get(), aMatchingList);
1368 }
1369 
1370 nsresult
1372  ChangeType *aChangeType,
1373  sbIMediaList **aDestMediaList)
1374 {
1375  nsresult rv;
1376 
1377  if (!ListHasCorrectContentType(aMediaList)) {
1378  *aChangeType = CHANGE_NONE;
1379  return NS_OK;
1380  }
1381 
1382  nsCOMPtr<sbIMediaList> matchingPlaylist;
1384  aMediaList,
1385  getter_AddRefs(matchingPlaylist));
1386  NS_ENSURE_SUCCESS(rv, rv);
1387 
1388  if (matchingPlaylist) {
1389  nsString playlistType;
1390  rv = matchingPlaylist->GetType(playlistType);
1391  NS_ENSURE_SUCCESS(rv, rv);
1392 
1393  // We only import changes for simple playlists.
1394  if (playlistType.EqualsLiteral("simple")) {
1395  if (IsDrop()) {
1396  *aChangeType = CHANGE_CLOBBER;
1397  matchingPlaylist.forget(aDestMediaList);
1398 
1399  return NS_OK;
1400  }
1401  else {
1402  // Check timestamps...
1403  PRInt64 itemLastModifiedTime;
1404  PRInt64 lastSyncTime;
1405  rv = matchingPlaylist->GetUpdated(&itemLastModifiedTime);
1406  NS_ENSURE_SUCCESS(rv, rv);
1407 
1409  NS_LITERAL_STRING(SB_PROPERTY_LAST_SYNC_TIME),
1410  &lastSyncTime);
1411  NS_ENSURE_SUCCESS(rv, rv);
1412 
1413  if (itemLastModifiedTime < lastSyncTime) {
1414  // Main library item hasn't been modified since the last sync;
1415  // overwrite the version in the main library.
1416  // Note that this happens even if the device-side playlist is
1417  // unmodified (we don't know if it has been changed), so every sync
1418  // will result in these playlists being overwritten (and their
1419  // last-modified time updated)
1420  *aChangeType = CHANGE_CLOBBER;
1421  matchingPlaylist.forget(aDestMediaList);
1422 
1423  return NS_OK;
1424  }
1425  else {
1426  *aChangeType = CHANGE_RETAIN;
1427  matchingPlaylist.forget(aDestMediaList);
1428 
1429  return NS_OK;
1430  }
1431  }
1432  }
1433  else {
1434  // Matching list is a smart playlist; we only do anything with these
1435  // for d&d.
1436  if (IsDrop()) {
1437  nsCOMPtr<sbIMediaList> matchingSimplePlaylist;
1439  mMainLibrary,
1440  aMediaList,
1441  getter_AddRefs(matchingSimplePlaylist));
1442  NS_ENSURE_SUCCESS(rv, rv);
1443 
1444  if (matchingSimplePlaylist) {
1445  *aChangeType = CHANGE_CLOBBER;
1446  matchingSimplePlaylist.forget(aDestMediaList);
1447 
1448  return NS_OK;
1449  }
1450  else {
1451  *aChangeType = CHANGE_ADD;
1452  return NS_OK;
1453  }
1454  }
1455  else {
1456  *aChangeType = CHANGE_RETAIN;
1457  matchingPlaylist.forget(aDestMediaList);
1458  return NS_OK;
1459  }
1460  }
1461  }
1462  else {
1463  // New playlist; add it to the main library
1464  *aChangeType = CHANGE_ADD;
1465  return NS_OK;
1466  }
1467 }
1468 
1469 nsresult
1471  ChangeType *aChangeType,
1472  sbIMediaItem **aDestMediaItem)
1473 {
1474  nsresult rv;
1475 
1476  // Is it of the correct type?
1477  if (!HasCorrectContentType(aMediaItem)) {
1478  *aChangeType = CHANGE_NONE;
1479  return NS_OK;
1480  }
1481 
1482  // If it came from the main library (according to origin item and library
1483  // guids), we do not wish to re-import it (even if the linked item is no
1484  // longer in the main library!).
1485  PRBool isFromMainLibrary;
1486  rv = IsFromMainLibrary(aMediaItem, &isFromMainLibrary);
1487  NS_ENSURE_SUCCESS(rv, rv);
1488 
1489  if (IsDrop()) {
1490  // Drag-and-drop case.
1491  if (isFromMainLibrary) {
1492  // Ok, it's from the main library. Is it still IN the main library?
1493  nsCOMPtr<sbIMediaItem> mainLibItem;
1494  rv = GetItemInMainLibrary(aMediaItem, getter_AddRefs(mainLibItem));
1495  NS_ENSURE_SUCCESS(rv, rv);
1496 
1497  if (!mainLibItem) {
1498  // It's no longer in the main library, but the user dragged it there,
1499  // so take that to mean "re-add this"
1500  *aChangeType = CHANGE_ADD;
1501  return NS_OK;
1502  }
1503  else {
1504  *aChangeType = CHANGE_RETAIN;
1505  mainLibItem.forget(aDestMediaItem);
1506  return NS_OK;
1507  }
1508  }
1509  else {
1510  // Not from the main library; add it.
1511  *aChangeType = CHANGE_ADD;
1512  return NS_OK;
1513  }
1514  }
1515  else {
1516  // Sync case.
1517  if (!isFromMainLibrary) {
1518  // Didn't come from Songbird (at least not from this profile on this
1519  // machine). Is there something that _looks_ like the same item? If
1520  // there is, we don't want to import it.
1521  nsCOMPtr<nsIArray> matchedItems;
1522  rv = mMainLibrary->GetItemsWithSameIdentity(
1523  aMediaItem,
1524  getter_AddRefs(matchedItems));
1525  NS_ENSURE_SUCCESS(rv, rv);
1526 
1527  PRUint32 matchedItemsLength;
1528  rv = matchedItems->GetLength(&matchedItemsLength);
1529  NS_ENSURE_SUCCESS(rv, rv);
1530 
1531  if (matchedItemsLength == 0) {
1532  // It looks like an all-new item not present in the main library. Time
1533  // to actually import it!
1534  *aChangeType = CHANGE_ADD;
1535  return NS_OK;
1536  }
1537  else {
1538  // Point at the object we matched (might be several, that's ok...)
1539  *aChangeType = CHANGE_RETAIN;
1540 
1541  nsCOMPtr<sbIMediaItem> matchItem = do_QueryElementAt(
1542  matchedItems, 0, &rv);
1543  NS_ENSURE_SUCCESS(rv, rv);
1544 
1545  matchItem.forget(aDestMediaItem);
1546  return NS_OK;
1547  }
1548  }
1549  else {
1550  // It came from the main library. Do nothing (even if it's not IN the main
1551  // library any more). However, point at the main library item, if present.
1552  nsCOMPtr<sbIMediaItem> mainLibItem;
1553  rv = GetItemInMainLibrary(aMediaItem, getter_AddRefs(mainLibItem));
1554  NS_ENSURE_SUCCESS(rv, rv);
1555 
1556  if (mainLibItem) {
1557  *aChangeType = CHANGE_RETAIN;
1558  mainLibItem.forget(aDestMediaItem);
1559  return NS_OK;
1560  }
1561  else {
1562  *aChangeType = CHANGE_NONE;
1563  return NS_OK;
1564  }
1565  }
1566  }
1567 }
1568 
1569 nsresult
1571  sbIMediaItem *aMediaItem)
1572 {
1573  nsresult rv;
1574 
1575  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
1576  if (NS_SUCCEEDED(rv)) {
1577  ChangeType changeType = CHANGE_NONE;
1578  nsCOMPtr<sbIMediaList> destList;
1579  rv = SelectChangeForList(itemAsList, &changeType, getter_AddRefs(destList));
1580  NS_ENSURE_SUCCESS(rv, rv);
1581 
1582  switch (changeType) {
1583  case CHANGE_ADD:
1585  itemAsList,
1586  NULL);
1587  NS_ENSURE_SUCCESS(rv, rv);
1588  break;
1589  case CHANGE_CLOBBER:
1591  itemAsList,
1592  destList);
1593  NS_ENSURE_SUCCESS(rv, rv);
1594  break;
1595  default:
1596  // Others we do nothing with
1597  break;
1598  }
1599  }
1600  else {
1601  // Normal media item.
1602  ChangeType changeType = CHANGE_NONE;
1603  nsCOMPtr<sbIMediaItem> destItem;
1604  rv = SelectChangeForItem(aMediaItem, &changeType, getter_AddRefs(destItem));
1605  NS_ENSURE_SUCCESS(rv, rv);
1606 
1607  switch (changeType) {
1608  case CHANGE_ADD:
1610  aMediaItem,
1611  NULL);
1612  NS_ENSURE_SUCCESS(rv, rv);
1613  break;
1614  case CHANGE_CLOBBER:
1616  aMediaItem,
1617  destItem);
1618  NS_ENSURE_SUCCESS(rv, rv);
1619  break;
1620  default:
1621  // Others we do nothing with
1622  break;
1623  }
1624  }
1625 
1626  return NS_OK;
1627 }
1628 
1630 
1632 {
1633  SB_PRLOG_SETUP(sbDeviceLibrarySyncDiff);
1634 }
1635 
1636 sbDeviceLibrarySyncDiff::~sbDeviceLibrarySyncDiff()
1637 {
1638 }
1639 
1640 NS_IMETHODIMP
1641 sbDeviceLibrarySyncDiff::GenerateSyncLists(
1642  PRUint32 aMediaTypesToExportAll,
1643  PRUint32 aMediaTypesToImportAll,
1644  sbILibrary *aSourceLibrary,
1645  sbILibrary *aDestLibrary,
1646  nsIArray *aSourceLists,
1647  sbILibraryChangeset **aExportChangeset NS_OUTPARAM,
1648  sbILibraryChangeset **aImportChangeset NS_OUTPARAM)
1649 {
1650  NS_ENSURE_ARG_POINTER(aSourceLibrary);
1651  NS_ENSURE_ARG_POINTER(aDestLibrary);
1652  NS_ENSURE_ARG_POINTER(aExportChangeset);
1653  NS_ENSURE_ARG_POINTER(aImportChangeset);
1654 
1655  nsresult rv;
1656 
1657  // We first determine which (if any) items we need to export, depending on
1658  // settings.
1659 
1660  nsRefPtr<SyncExportEnumListener> exportListener =
1661  new SyncExportEnumListener();
1662  NS_ENSURE_TRUE(exportListener, NS_ERROR_OUT_OF_MEMORY);
1663  rv = exportListener->Init(SyncEnumListenerBase::NOT_DROP,
1664  aSourceLibrary,
1665  aDestLibrary);
1666  NS_ENSURE_SUCCESS(rv, rv);
1667 
1668  const PRUint32 mixedMediaTypes = sbIDeviceLibrarySyncDiff::SYNC_TYPE_AUDIO |
1670 
1671  if (aMediaTypesToExportAll) {
1672  // Enumerate all items to find the ones we need to sync (based on media
1673  // type, and what's already on the device). This includes playlists.
1674  // This will find all the playlists (including mixed-content ones!), as well
1675  // as media items, that match the desired media type. However, it won't find
1676  // items that are in a mixed-content playlist, but not of the right type.
1677  exportListener->SetMediaTypes(aMediaTypesToExportAll);
1678 
1679  // Handle all non-list items.
1680  exportListener->SetHandleMode(SyncEnumListenerBase::HANDLE_ITEMS);
1681  rv = aSourceLibrary->EnumerateAllItems(
1682  exportListener,
1684  NS_ENSURE_SUCCESS(rv, rv);
1685 
1686  // Handle all lists - this might need to refer to items above, so we have
1687  // to do it separately.
1688  exportListener->SetHandleMode(SyncEnumListenerBase::HANDLE_LISTS);
1689  rv = aSourceLibrary->EnumerateAllItems(
1690  exportListener,
1692  NS_ENSURE_SUCCESS(rv, rv);
1693 
1694  // If we have any mixed-content playlists, but are not syncing all media
1695  // types, now do a 2nd pass over those, but with the media types set to all.
1696  if (aMediaTypesToExportAll != mixedMediaTypes) {
1697  exportListener->SetHandleMode(SyncEnumListenerBase::HANDLE_ITEMS);
1698  exportListener->SetMediaTypes(mixedMediaTypes);
1699  PRInt32 const mixedListCount =
1700  exportListener->mMixedContentPlaylists.Length();
1701  for (PRInt32 i = 0; i < mixedListCount; i++) {
1702  rv = exportListener->mMixedContentPlaylists[i]->EnumerateAllItems(
1703  exportListener,
1705  NS_ENSURE_SUCCESS(rv, rv);
1706  }
1707  }
1708  }
1709 
1710  if (aSourceLists) {
1711  // We're not syncing everything, just some specific playlists.
1712  // Items found in multiple playlists (or multiple times in a single
1713  // playlist) are dealt with internally.
1714 
1715  // We'll only be asked to export playlists of the correct type. We want
1716  // to export ALL media in these playlists though - this means that we'll
1717  // sync video files from a mixed-content playlist.
1718  exportListener->SetMediaTypes(mixedMediaTypes);
1719 
1720  PRUint32 sourceListLength;
1721  rv = aSourceLists->GetLength(&sourceListLength);
1722  NS_ENSURE_SUCCESS(rv, rv);
1723 
1724  nsCOMPtr<sbIMediaList> list;
1725  for (PRUint32 i = 0; i < sourceListLength; i++) {
1726  list = do_QueryElementAt(aSourceLists, i, &rv);
1727  NS_ENSURE_SUCCESS(rv, rv);
1728 
1729  // We only want to enumerate the items in the list if we're going to
1730  // export the list itself - so we need to determine that first.
1732  nsCOMPtr<sbIMediaList> destList;
1733  rv = exportListener->SelectChangeForList(list,
1734  &changeType,
1735  getter_AddRefs(destList));
1736  NS_ENSURE_SUCCESS(rv, rv);
1737 
1738  // If we're actually importing the playlist, enumerate everything in it.
1739  if (changeType == SyncEnumListenerBase::CHANGE_ADD ||
1740  changeType == SyncEnumListenerBase::CHANGE_CLOBBER) {
1741  list->EnumerateAllItems(exportListener,
1743  NS_ENSURE_SUCCESS(rv, rv);
1744 
1745  // And add the list itself.
1746  if (changeType == SyncEnumListenerBase::CHANGE_ADD) {
1747  rv = exportListener->AddListChange(sbIChangeOperation::ADDED,
1748  list,
1749  NULL);
1750  NS_ENSURE_SUCCESS(rv, rv);
1751  }
1752  else {
1753  rv = exportListener->AddListChange(sbIChangeOperation::MODIFIED,
1754  list,
1755  destList);
1756  NS_ENSURE_SUCCESS(rv, rv);
1757  }
1758  }
1759  }
1760  }
1761 
1762  rv = exportListener->Finish();
1763  NS_ENSURE_SUCCESS(rv, rv);
1764 
1765  // All done with export. Now import; this is a little simpler as we don't
1766  // have as many configuration options available.
1767  nsRefPtr<SyncImportEnumListener> importListener =
1768  new SyncImportEnumListener();
1769  NS_ENSURE_TRUE(importListener, NS_ERROR_OUT_OF_MEMORY);
1770  rv = importListener->Init(SyncEnumListenerBase::NOT_DROP,
1771  aSourceLibrary,
1772  aDestLibrary);
1773  NS_ENSURE_SUCCESS(rv, rv);
1774 
1775  if (aMediaTypesToImportAll) {
1776  // We always import everything (not just select playlists) if this is
1777  // enabled. As for export, this needs to handle the items before the lists,
1778  // so that exporting a list can refer to the correct items contained within
1779  // the list.
1780  importListener->SetMediaTypes(aMediaTypesToImportAll);
1781  importListener->SetHandleMode(SyncEnumListenerBase::HANDLE_ITEMS);
1782  rv = aDestLibrary->EnumerateAllItems(
1783  importListener,
1785  NS_ENSURE_SUCCESS(rv, rv);
1786 
1787  importListener->SetHandleMode(SyncEnumListenerBase::HANDLE_LISTS);
1788  rv = aDestLibrary->EnumerateAllItems(
1789  importListener,
1791  NS_ENSURE_SUCCESS(rv, rv);
1792  }
1793 
1794  rv = importListener->Finish();
1795  NS_ENSURE_SUCCESS(rv, rv);
1796 
1797  NS_IF_ADDREF(*aExportChangeset = exportListener->mChangeset);
1798  NS_IF_ADDREF(*aImportChangeset = importListener->mChangeset);
1799 
1800  return NS_OK;
1801 }
1802 
1803 NS_IMETHODIMP
1804 sbDeviceLibrarySyncDiff::GenerateDropLists(
1805  sbILibrary *aSourceLibrary,
1806  sbILibrary *aDestLibrary,
1807  sbIMediaList *aSourceList,
1808  nsIArray *aSourceItems,
1809  nsIArray **aDestItems NS_OUTPARAM,
1810  sbILibraryChangeset **aChangeset NS_OUTPARAM)
1811 {
1812  NS_ENSURE_ARG_POINTER (aDestLibrary);
1813  NS_ENSURE_ARG_POINTER (aChangeset);
1814 
1815  if (!aSourceList)
1816  NS_ENSURE_ARG_POINTER(aSourceItems);
1817 
1818  nsresult rv;
1819 
1820  // Figure out if we're going to a device or from one. We might think that
1821  // checking sbILibrary.device for non-null would work - but what we actually
1822  // get here is the internal library itself, not the device library that wraps
1823  // that. Only the device library implements the 'device' attribute.
1824  bool toDevice = false;
1825  nsCOMPtr<sbIDeviceManager2> deviceManager = do_GetService(
1826  "@songbirdnest.com/Songbird/DeviceManager;2", &rv);
1827  NS_ENSURE_SUCCESS(rv, rv);
1828 
1829  nsCOMPtr<sbIDevice> device;
1830  rv = deviceManager->GetDeviceForItem(aDestLibrary, getter_AddRefs(device));
1831  if (NS_SUCCEEDED(rv) && device) {
1832  toDevice = true;
1833  }
1834 
1835  // Drag and drop always works for both audio and video.
1836  const PRUint32 allMediaTypes = sbIDeviceLibrarySyncDiff::SYNC_TYPE_AUDIO |
1838 
1839  nsCOMPtr<nsIMutableArray> destItems = do_CreateInstance(
1840  "@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1841  NS_ENSURE_SUCCESS(rv, rv);
1842 
1843  nsRefPtr<SyncEnumListenerBase> listener;
1844 
1845  // Determine if we're droping tracks or a list
1846  const SyncEnumListenerBase::DropAction dropAction =
1847  aSourceList ? SyncEnumListenerBase::DROP_LIST :
1849 
1850  // Figure out whether this is to a device (even if it's from a device as well)
1851  // or from a device to a non-device. The third possibility (no devices
1852  // involved at all) is not checked for; the callers must ensure that either
1853  // source or destination is a device.
1854  if (toDevice) {
1855  listener = new SyncExportEnumListener();
1856  NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
1857  rv = listener->Init(dropAction,
1858  aSourceLibrary,
1859  aDestLibrary);
1860  NS_ENSURE_SUCCESS(rv, rv);
1861  }
1862  else {
1863  listener = new SyncImportEnumListener();
1864  NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
1865  rv = listener->Init(dropAction,
1866  aDestLibrary,
1867  aSourceLibrary);
1868  NS_ENSURE_SUCCESS(rv, rv);
1869  }
1870 
1871  listener->SetMediaTypes(allMediaTypes);
1872 
1873  if (aSourceList) {
1874  // Enumerate the list into our hashmap. We can do this before deciding
1875  // what to do to the playlist itself because D&D of a playlist ALWAYS
1876  // results in transfer of the list (the only difference is whether it's
1877  // adding a new list or clobbering an existing one)
1878  aSourceList->EnumerateAllItems(listener,
1880  NS_ENSURE_SUCCESS(rv, rv);
1881 
1882  // And add the list itself.
1883  rv = listener->ProcessItem(aSourceLibrary, aSourceList);
1884  NS_ENSURE_SUCCESS(rv, rv);
1885  }
1886  else {
1887  // Transferring an array of items, not a media list.
1888  // For this, we just individually check each item. Note that this handles
1889  // only non-list items.
1890  PRUint32 itemsLen;
1891  rv = aSourceItems->GetLength(&itemsLen);
1892  NS_ENSURE_SUCCESS(rv, rv);
1893 
1894  for (PRUint32 i = 0; i < itemsLen; i++) {
1895  nsCOMPtr<sbIMediaItem> item = do_QueryElementAt(aSourceItems, i, &rv);
1896  NS_ENSURE_SUCCESS(rv, rv);
1897 
1898  rv = listener->ProcessItem(aSourceLibrary, item);
1899  NS_ENSURE_SUCCESS(rv, rv);
1900 
1902  nsCOMPtr<sbIMediaItem> destItem;
1903  rv = listener->SelectChangeForItem(item,
1904  &changeType,
1905  getter_AddRefs(destItem));
1906  NS_ENSURE_SUCCESS(rv, rv);
1907 
1908  switch(changeType) {
1911  // For both of these, use the source item
1912  rv = destItems->AppendElement(item, PR_FALSE);
1913  NS_ENSURE_SUCCESS(rv, rv);
1914  break;
1916  // For this one, use the (existing) destination item
1917  rv = destItems->AppendElement(destItem, PR_FALSE);
1918  NS_ENSURE_SUCCESS(rv, rv);
1919  break;
1920  default:
1921  // And this means it was an item we chose not to transfer at all.
1922  break;
1923  }
1924  }
1925  }
1926 
1927  rv = listener->Finish();
1928  NS_ENSURE_SUCCESS(rv, rv);
1929 
1930  NS_ADDREF(*aChangeset = listener->mChangeset);
1931  NS_ADDREF(*aDestItems = destItems);
1932 
1933  return NS_OK;
1934 }
1935 
const unsigned long DELETED
nsCOMPtr< nsIMutableArray > mLibraryChanges
#define SB_PRLOG_SETUP(x)
Definition: sbDebugUtils.h:115
return NS_OK
nsCOMPtr< sbILibrary > mMainLibrary
#define SB_PROPERTY_MEDIALISTNAME
nsresult GetItemWithOriginGUID(sbILibrary *aDeviceLibrary, nsString aItemID, sbIMediaItem **aMediaItem)
#define SB_PROPERTY_ORIGINLIBRARYGUID
virtual nsresult SelectChangeForItem(sbIMediaItem *aMediaItem, ChangeType *aChangeType, sbIMediaItem **aDestMediaItem)
#define SB_PROPERTY_ORIGINURL
nsresult CreatePropertyChangesForItemAdded(sbIMediaItem *aSourceItem, nsIArray **aPropertyChanges)
Interface used to enumerate the items in a media list.
NS_IMPL_ISUPPORTS1(sbDeviceCapabilitiesUtils, sbIDeviceCapabilitiesUtils) sbDeviceCapabilitiesUtils
nsCOMPtr< sbILibrary > mDeviceLibrary
void SetMediaTypes(PRUint32 aMediaTypes)
PRUint64 nsString_ToUint64(const nsAString &str, nsresult *rv)
bool ListIsMixed(sbIMediaList *aList)
virtual nsresult SelectChangeForItem(sbIMediaItem *aMediaItem, ChangeType *aChangeType, sbIMediaItem **aDestMediaItem)
#define SB_PROPERTY_HIDDEN
sbLibraryChangeset Definition.
nsresult AddChange(PRUint32 aChangeType, sbIMediaItem *aSrcItem, sbIMediaItem *aDstItem, nsIArray *aListItems=NULL)
nsresult GetTimeProperty(sbIMediaItem *aMediaItem, nsString aPropertyName, PRInt64 *_result)
A brief description of the contents of this interface.
const unsigned short CONTENTTYPE_AUDIO
virtual nsresult Init(DropAction aDropAction, sbILibrary *aMainLibrary, sbILibrary *aDeviceLibrary)
#define SB_PROPERTY_LAST_SYNC_TIME
nsTHashtable< nsStringHashKey > mSeenMediaItems
#define SB_PROPERTY_CONTENTLENGTH
nsresult AddListChange(PRUint32 aChangeType, sbIMediaList *aSrcList, sbIMediaList *aDstList)
virtual nsresult SelectChangeForItem(sbIMediaItem *aMediaItem, ChangeType *aChangeType, sbIMediaItem **aDestMediaItem)=0
virtual nsresult ProcessItem(sbIMediaList *aMediaList, sbIMediaItem *aMediaItem)
virtual nsresult SelectChangeForList(sbIMediaList *aMediaList, ChangeType *aChangeType, sbIMediaList **aDestMediaList)=0
var count
Definition: test_bug7406.js:32
#define SB_PROPERTY_CREATED
#define SB_PROPERTY_COLUMNSPEC
nsresult IsFromMainLibrary(sbIMediaItem *aMediaItem, PRBool *aFromMainLibrary)
void SetHandleMode(HandleMode aMode)
#define SB_PROPERTY_UPDATED
#define SB_PROPERTY_CONTENTTYPE
const unsigned short CONTENTTYPE_VIDEO
const unsigned short ENUMERATIONTYPE_SNAPSHOT
This flag means that the list being enumerated is a copy that may become out of date.
nsCOMPtr< SyncEnumListenerBase > mMainListener
nsresult GetSimplePlaylistWithSameName(sbILibrary *aLibrary, sbIMediaList *aList, sbIMediaList **aMatchingList)
#define SB_PROPERTY_DURATION
#define SB_PROPERTY_GUID
virtual nsresult GetMatchingPlaylist(sbILibrary *aLibrary, sbIMediaList *aList, sbIMediaList **aMatchingList)
#define SB_PROPERTY_ORIGIN_IS_IN_MAIN_LIBRARY
const unsigned long MODIFIED
bool ListHasCorrectContentType(sbIMediaList *aList)
Media library abstraction.
Definition: sbILibrary.idl:82
bool HasCorrectContentType(sbIMediaItem *aItem)
virtual nsresult ProcessItem(sbIMediaList *aMediaList, sbIMediaItem *aMediaItem)
virtual nsresult GetMatchingPlaylist(sbILibrary *aLibrary, sbIMediaList *aList, sbIMediaList **aMatchingList)=0
PRInt64 nsString_ToInt64(const nsAString &str, nsresult *rv)
virtual nsresult SelectChangeForList(sbIMediaList *aMediaList, ChangeType *aChangeType, sbIMediaList **aDestMediaList)
virtual nsresult GetMatchingPlaylist(sbILibrary *aLibrary, sbIMediaList *aList, sbIMediaList **aMatchingList)
nsRefPtr< sbLibraryChangeset > mChangeset
virtual nsresult ProcessItem(sbIMediaList *aMediaList, sbIMediaItem *aMediaItem)=0
nsCOMPtr< nsIMutableArray > mListItems
nsTArray< nsCOMPtr< sbIMediaList > > mMixedContentPlaylists
Interface that defines a single item of media in the system.
nsresult GetItemInMainLibrary(sbIMediaItem *aMediaItem, sbIMediaItem **aMainLibraryItem)
#define SB_PROPERTY_PLAYLISTURL
var hidden
#define SB_PROPERTY_AVAILABILITY
virtual nsresult SelectChangeForList(sbIMediaList *aMediaList, ChangeType *aChangeType, sbIMediaList **aDestMediaList)
nsresult CreatePropertyChangesForItemModified(sbIMediaItem *aSourceItem, sbIMediaItem *aDestinationItem, nsIArray **aPropertyChanges)
#define SB_PROPERTY_CONTENTURL
#define SB_PROPERTY_ORIGINITEMGUID
_getSelectedPageStyle s i
const unsigned long ADDED
NS_IMPL_THREADSAFE_ISUPPORTS1(SyncEnumListenerBase, sbIMediaListEnumerationListener) class ListAddingEnumerationListener