sbLocalDatabasePropertyCache.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 <sbIDatabaseQuery.h>
28 #include <sbILibraryManager.h>
29 #include <sbIPropertyManager.h>
30 #include <sbPropertiesCID.h>
31 
32 #include <DatabaseQuery.h>
33 #include <nsAutoLock.h>
34 #include <nsCOMArray.h>
35 #include <nsComponentManagerUtils.h>
36 #include <nsIObserverService.h>
37 #include <nsIProxyObjectManager.h>
38 #include <nsServiceManagerUtils.h>
39 #include <nsIThreadManager.h>
40 #include <nsThreadUtils.h>
41 #include <nsUnicharUtils.h>
42 #include <nsXPCOM.h>
43 #include <nsXPCOMCIDInternal.h>
44 #include <prlog.h>
45 #include <nsThreadUtils.h>
46 #include <nsIClassInfoImpl.h>
47 #include <nsIProgrammingLanguage.h>
48 #include <nsMemory.h>
49 #include <nsIStringBundle.h>
50 #include <nsIPrefBranch.h>
51 #include <nsIPrefService.h>
52 
55 #include "sbLocalDatabaseLibrary.h"
58 #include <sbIJobProgressService.h>
60 #include <sbStringBundle.h>
61 #include <sbStringUtils.h>
63 #include <sbIPropertyArray.h>
64 #include <sbIPropertyInfo.h>
65 #include "sbLocalDatabaseSQL.h"
66 #include <sbIDatabaseQuery.h>
67 #include <sbThreadPoolService.h>
68 #include <sbDebugUtils.h>
69 
70 /*
71  * To log this module, set the following environment variable:
72  * NSPR_LOG_MODULES=sbLocalDatabasePropertyCache:5
73  */
74 
78 #define SB_LOCALDATABASE_CACHE_FLUSH_DELAY (1000)
79 
80 #define CACHE_HASHTABLE_SIZE 500
81 
82 
87 #define SORTINVALIDATE_TIMER_PERIOD 50
88 
89 #define NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID "xpcom-shutdown-threads"
90 #define NS_FINAL_UI_STARTUP_OBSERVER_ID "final-ui-startup"
91 
92 
96 
98 : mWritePendingCount(0),
99  mDependentGUIDArrayMonitor(nsnull),
100  mMonitor(nsnull),
101  mCache(sbLocalDatabasePropertyCache::CACHE_SIZE),
102  mLibrary(nsnull),
103  mSortInvalidateJob(nsnull)
104 {
105  MOZ_COUNT_CTOR(sbLocalDatabasePropertyCache);
106 
108 }
109 
111 {
112  if (mDependentGUIDArrayMonitor) {
113  nsAutoMonitor::DestroyMonitor(mDependentGUIDArrayMonitor);
114  }
115 
116  if (mMonitor) {
117  nsAutoMonitor::DestroyMonitor(mMonitor);
118  }
119 
120  MOZ_COUNT_DTOR(sbLocalDatabasePropertyCache);
121 }
122 
123 NS_IMETHODIMP
124 sbLocalDatabasePropertyCache::GetWritePending(PRBool *aWritePending)
125 {
126  NS_ASSERTION(mLibrary, "You didn't initialize!");
127  NS_ENSURE_ARG_POINTER(aWritePending);
128  *aWritePending = (mWritePendingCount > 0);
129 
130  return NS_OK;
131 }
132 
133 nsresult
135  const nsAString& aLibraryResourceGUID)
136 {
137  NS_ASSERTION(!mLibrary, "Already initialized!");
138  NS_ENSURE_ARG_POINTER(aLibrary);
139 
140  mLibraryResourceGUID = aLibraryResourceGUID;
141 
142  nsresult rv = aLibrary->GetDatabaseGuid(mDatabaseGUID);
143  NS_ENSURE_SUCCESS(rv, rv);
144 
145  rv = aLibrary->GetDatabaseLocation(getter_AddRefs(mDatabaseLocation));
146  NS_ENSURE_SUCCESS(rv, rv);
147 
148  mPropertyManager = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
149  NS_ENSURE_SUCCESS(rv, rv);
150 
151  mDependentGUIDArrayMonitor =
152  nsAutoMonitor::NewMonitor("sbLocalDatabasePropertyCache::mDependentGUIDArrayMonitor");
153  NS_ENSURE_TRUE(mDependentGUIDArrayMonitor, NS_ERROR_OUT_OF_MEMORY);
154 
155  mMonitor = nsAutoMonitor::NewMonitor("sbLocalDatabasePropertyCache::mMonitor");
156  NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
157 
158  rv = LoadProperties();
159  NS_ENSURE_SUCCESS(rv, rv);
160 
161  PRBool success = mDirty.Init(CACHE_HASHTABLE_SIZE);
162  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
163 
164  mThreadPoolService = do_GetService(SB_THREADPOOLSERVICE_CONTRACTID, &rv);
165  NS_ENSURE_SUCCESS(rv, rv);
166 
167  rv = mThreadPoolService->SetIdleThreadTimeout(15000);
168  NS_ENSURE_SUCCESS(rv, rv);
169 
170  mFlushTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
171  NS_ENSURE_SUCCESS(rv, rv);
172 
173  rv = mFlushTimer->Init(this,
175  nsITimer::TYPE_REPEATING_SLACK);
176  NS_ENSURE_SUCCESS(rv, rv);
177 
178  mInvalidateTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
179  NS_ENSURE_SUCCESS(rv, rv);
180 
181  mLibrary = aLibrary;
182 
183  nsCOMPtr<nsIObserverService> observerService =
184  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
185  NS_ENSURE_SUCCESS(rv, rv);
186 
187  // Shut down the prop cache when the app goes away
188  rv = observerService->AddObserver(this,
190  PR_FALSE);
191  NS_ENSURE_SUCCESS(rv, rv);
192 
193  rv = observerService->AddObserver(this,
195  PR_FALSE);
196  NS_ENSURE_SUCCESS(rv, rv);
197 
198  // Shut down any library related tasks BEFORE the library goes away
199  rv = observerService->AddObserver(this,
201  PR_FALSE);
202  NS_ENSURE_SUCCESS(rv, rv);
203 
204  // Check for background tasks to be run when the first window comes up
205  rv = observerService->AddObserver(this,
207  PR_FALSE);
208  NS_ENSURE_SUCCESS(rv, rv);
209 
210  // Initialise some prepared statements.
211  nsCOMPtr<sbIDatabaseQuery> query;
212  rv = MakeQuery(getter_AddRefs(query));
213  NS_ENSURE_SUCCESS(rv, rv);
214 
215  rv = query->PrepareQuery(mSQLStrings.MediaItemSelect(),
216  getter_AddRefs(mItemSelectPreparedStatement));
217  NS_ENSURE_SUCCESS(rv, rv);
218 
219  rv = query->PrepareQuery(mSQLStrings.SecondaryPropertySelect(),
220  getter_AddRefs(mSecondaryPropertySelectPreparedStatement));
221  NS_ENSURE_SUCCESS(rv, rv);
222 
223  rv = query->PrepareQuery(mSQLStrings.MediaItemsFtsAllDelete(),
224  getter_AddRefs(mMediaItemsFtsAllDeletePreparedStatement));
225  NS_ENSURE_SUCCESS(rv, rv);
226 
227  rv = query->PrepareQuery(mSQLStrings.MediaItemsFtsAllInsert(),
228  getter_AddRefs(mMediaItemsFtsAllInsertPreparedStatement));
229  NS_ENSURE_SUCCESS(rv, rv);
230 
231  rv = query->PrepareQuery(sbLocalDatabaseSQL::PropertiesDelete(),
232  getter_AddRefs(mPropertiesDeletePreparedStatement));
233  NS_ENSURE_SUCCESS(rv, rv);
234 
235  rv = query->PrepareQuery(sbLocalDatabaseSQL::PropertiesInsert(),
236  getter_AddRefs(mPropertiesInsertPreparedStatement));
237  NS_ENSURE_SUCCESS(rv, rv);
238 
239  // Put together the update queries for each property.
240  // By preparing these queries in advance we avoid having to recompile them all the time.
241  success = mMediaItemsUpdatePreparedStatements.Init(sStaticPropertyCount);
242  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
243  for (PRUint32 i = 0; i < sStaticPropertyCount; i++) {
244  nsString updateSQL = NS_LITERAL_STRING("UPDATE media_items SET ");
245  updateSQL.AppendLiteral(sStaticProperties[i].mColumn);
246  updateSQL.Append(NS_LITERAL_STRING(" = ? WHERE media_item_id = ?"));
247 
248  nsCOMPtr<sbIDatabasePreparedStatement> updateMediaItemPreparedStatement;
249  rv = query->PrepareQuery(updateSQL, getter_AddRefs(updateMediaItemPreparedStatement));
250  NS_ENSURE_SUCCESS(rv,rv);
251 
252  success = mMediaItemsUpdatePreparedStatements.Put(sStaticProperties[i].mDBID,
253  updateMediaItemPreparedStatement);
254  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
255  }
256 
257  // Create a second, identical set of queries for the library media item.
258  // The library media item is stored in its own table.
259  success = mLibraryMediaItemUpdatePreparedStatements.Init(sStaticPropertyCount);
260  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
261  for (PRUint32 i = 0; i < sStaticPropertyCount; i++) {
262  nsString updateSQL = NS_LITERAL_STRING("UPDATE library_media_item SET ");
263  updateSQL.AppendLiteral(sStaticProperties[i].mColumn);
264  updateSQL.Append(NS_LITERAL_STRING(" = ?"));
265 
266  nsCOMPtr<sbIDatabasePreparedStatement> updateMediaItemPreparedStatement;
267  rv = query->PrepareQuery(updateSQL, getter_AddRefs(updateMediaItemPreparedStatement));
268  NS_ENSURE_SUCCESS(rv,rv);
269 
270  success = mLibraryMediaItemUpdatePreparedStatements.Put(sStaticProperties[i].mDBID,
271  updateMediaItemPreparedStatement);
272  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
273  }
274 
275  return NS_OK;
276 }
277 
278 nsresult
279 sbLocalDatabasePropertyCache::Shutdown()
280 {
281  TRACE("sbLocalDatabasePropertyCache[0x%.8x] - Shutdown()", this);
282 
283  nsresult rv = NS_OK;
284 
285  if(mFlushTimer) {
286  rv = mFlushTimer->Cancel();
287  NS_ENSURE_SUCCESS(rv, rv);
288  }
289 
290  // If we were in the middle of rebuilding all the sort info
291  // then must abort.
292  if (mSortInvalidateJob) {
293  mSortInvalidateJob->Shutdown();
294  mSortInvalidateJob = nsnull;
295  }
296 
297  if(mWritePendingCount) {
298  rv = Write();
299  }
300 
301  mItemSelectPreparedStatement = nsnull;
302  mSecondaryPropertySelectPreparedStatement = nsnull;
303  mMediaItemsFtsAllDeletePreparedStatement = nsnull;
304  mMediaItemsFtsAllInsertPreparedStatement = nsnull;
305  mPropertiesDeletePreparedStatement = nsnull;
306  mPropertiesInsertPreparedStatement = nsnull;
307 
308  mMediaItemsUpdatePreparedStatements.Clear();
309  mLibraryMediaItemUpdatePreparedStatements.Clear();
310 
311  return rv;
312 }
313 
314 template <class T>
315 nsresult
316 sbLocalDatabasePropertyCache::RetrievePrimaryProperties(sbIDatabaseQuery* query,
317  T const & aGuids,
318  IDToBagMap & aIDToBagMap,
319  nsCOMArray<sbLocalDatabaseResourcePropertyBag> & aBags,
320  nsTArray<PRUint32> & aMissesIDs)
321 {
322  nsresult rv;
323 
324  PRUint32 const length = aGuids.Length();
325  PRUint32 itemsRequested = length;
326  for (PRUint32 i = 0; i < length; ++i) {
327  // Skip any blank guid's as that's probably the library item
328  nsString const & aGuid = aGuids[i];
329  if (aGuid.IsEmpty()) {
330  --itemsRequested;
331  continue;
332  }
333  if (i % mSQLStrings.MediaItemBindCount == 0) {
334  rv = query->AddPreparedStatement(mItemSelectPreparedStatement);
335  NS_ENSURE_SUCCESS(rv, rv);
336  }
337  rv = query->BindStringParameter(i % mSQLStrings.MediaItemBindCount, aGuids[i]);
338  NS_ENSURE_SUCCESS(rv, rv);
339  }
340  // We might have been asked for only the library guid if so there's
341  // nothing to do
342  if (itemsRequested == 0)
343  return NS_OK;
344  PRInt32 dbOk;
345  rv = query->Execute(&dbOk);
346  NS_ENSURE_SUCCESS(rv, rv);
347  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
348 
349  nsCOMPtr<sbIDatabaseResult> result;
350  rv = query->GetResultObject(getter_AddRefs(result));
351  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
352 
353  PRUint32 rowCount;
354  rv = result->GetRowCount(&rowCount);
355  NS_ENSURE_SUCCESS(rv, rv);
356 
357  // Make sure we got the number of guid's we expected
358  NS_ENSURE_TRUE(rowCount == itemsRequested, NS_ERROR_UNEXPECTED);
359 
360  // Need to fill out the bag array as we won't be straight appending
361  // but filling in as we find the guids
362  aMissesIDs.SetLength(rowCount);
363 
364  // Variables declared outside of loops to be a little more efficient
365  nsString mediaItemIdStr;
366  nsString guid;
367  nsString value;
368  for (PRUint32 row = 0; row < rowCount; row++) {
369 
370  rv = result->GetRowCell(row, 0, mediaItemIdStr);
371  NS_ENSURE_SUCCESS(rv, rv);
372 
373  PRUint32 mediaItemId = mediaItemIdStr.ToInteger(&rv);
374  NS_ENSURE_SUCCESS(rv, rv);
375 
376  rv = result->GetRowCell(row, 1, guid);
377  NS_ENSURE_SUCCESS(rv, rv);
378 
379  nsRefPtr<sbLocalDatabaseResourcePropertyBag> bag =
380  new sbLocalDatabaseResourcePropertyBag(this, mediaItemId, guid);
381  NS_ENSURE_TRUE(bag, NS_ERROR_OUT_OF_MEMORY);
382 
383  nsresult rv = bag->Init();
384  NS_ENSURE_SUCCESS(rv, rv);
385 
386  for (PRUint32 i = 0; i < sStaticPropertyCount; i++) {
387  rv = result->GetRowCell(row, i + 1, value);
388  NS_ENSURE_SUCCESS(rv, rv);
389 
390  if (!value.IsVoid()) {
391 
392  rv = bag->PutValue(sStaticProperties[i].mDBID, value);
393  NS_ENSURE_SUCCESS(rv, rv);
394  }
395  }
396 
397  // Lookup where we should put this bag and it's ID
398  PRInt32 const index = aGuids.IndexOf(guid);
399  NS_ENSURE_TRUE(index >= 0, NS_ERROR_UNEXPECTED);
400 
401  aMissesIDs[index] = mediaItemId;
402 
403  PRBool const success = aIDToBagMap.Put(mediaItemId, bag);
404  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
405 
406  aBags.ReplaceObjectAt(bag, index);
407  }
408 
409  query->ResetQuery();
410  return NS_OK;
411 }
412 
413 nsresult
414 sbLocalDatabasePropertyCache::RetrieveSecondaryProperties(sbIDatabaseQuery* query,
415  nsTArray<PRUint32> itemIDs,
416  IDToBagMap const & bags)
417 {
418  nsresult rv;
419 
420  PRUint32 const length = itemIDs.Length();
421  // If we weren't passed any id's there's nothing to do and just return
422  if (length == 0)
423  return NS_OK;
424  for (PRUint32 i = 0; i < length; ++i) {
425  if ( i % mSQLStrings.SecondaryPropertyBindCount == 0 ) {
426  rv = query->AddPreparedStatement(mSecondaryPropertySelectPreparedStatement);
427  NS_ENSURE_SUCCESS(rv, rv);
428  }
429  rv = query->BindInt32Parameter(i % mSQLStrings.SecondaryPropertyBindCount, itemIDs[i]);
430  NS_ENSURE_SUCCESS(rv, rv);
431  }
432 
433  PRInt32 dbOk;
434  rv = query->Execute(&dbOk);
435  NS_ENSURE_SUCCESS(rv, rv);
436  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
437 
438  nsCOMPtr<sbIDatabaseResult> result;
439  rv = query->GetResultObject(getter_AddRefs(result));
440  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
441 
442  PRUint32 rowCount;
443  rv = result->GetRowCount(&rowCount);
444  NS_ENSURE_SUCCESS(rv, rv);
445 
446  nsString objSearchable;
447  nsString objSortable;
448  nsString obj;
449  nsString propertyIDStr;
450  nsRefPtr<sbLocalDatabaseResourcePropertyBag> bag;
451  for (PRUint32 row = 0; row < rowCount; row++) {
452 
453  nsString mediaItemIdStr;
454  rv = result->GetRowCell(row, 0, mediaItemIdStr);
455  NS_ENSURE_SUCCESS(rv, rv);
456 
457  PRUint32 mediaItemId = mediaItemIdStr.ToInteger(&rv);
458  NS_ENSURE_SUCCESS(rv, rv);
459 
460  bags.Get(mediaItemId, getter_AddRefs(bag));
461  NS_ENSURE_TRUE(bag, NS_ERROR_FAILURE);
462 
463  // Add each property / object pair to the current bag
464  rv = result->GetRowCell(row, 1, propertyIDStr);
465  NS_ENSURE_SUCCESS(rv, rv);
466 
467  PRUint32 propertyID = propertyIDStr.ToInteger(&rv);
468  NS_ENSURE_SUCCESS(rv, rv);
469 
470  rv = result->GetRowCell(row, 2, obj);
471  NS_ENSURE_SUCCESS(rv, rv);
472 
473  rv = bag->PutValue(propertyID, obj);
474 
475  NS_ENSURE_SUCCESS(rv, rv);
476  }
477  query->ResetQuery();
478 
479  return NS_OK;
480 }
481 
482 nsresult
483 sbLocalDatabasePropertyCache::RetrieveLibraryProperties(sbLocalDatabaseResourcePropertyBag * aBag)
484 {
485  nsCOMPtr<sbIDatabaseQuery> query;
486  nsresult rv = MakeQuery(getter_AddRefs(query));
487  NS_ENSURE_SUCCESS(rv, rv);
488 
489  // no sense preparing this. it hardly ever happens.
491  NS_ENSURE_SUCCESS(rv, rv);
492 
493  PRInt32 dbOk;
494  rv = query->Execute(&dbOk);
495  NS_ENSURE_SUCCESS(rv, rv);
496  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
497 
498  nsCOMPtr<sbIDatabaseResult> result;
499  rv = query->GetResultObject(getter_AddRefs(result));
500  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
501 
502  PRUint32 rowCount;
503  rv = result->GetRowCount(&rowCount);
504  NS_ENSURE_SUCCESS(rv, rv);
505 
506  for (PRUint32 row = 0; row < rowCount; row++) {
507 
508  nsString propertyIDStr;
509  rv = result->GetRowCell(row, 0, propertyIDStr);
510  NS_ENSURE_SUCCESS(rv, rv);
511 
512  PRUint32 propertyID = propertyIDStr.ToInteger(&rv);
513  NS_ENSURE_SUCCESS(rv, rv);
514 
515  nsString obj;
516  rv = result->GetRowCell(row, 1, obj);
517  NS_ENSURE_SUCCESS(rv, rv);
518 
519  rv = aBag->PutValue(propertyID, obj);
520  NS_ENSURE_SUCCESS(rv, rv);
521  }
522 
523  rv = MakeQuery(getter_AddRefs(query));
524  NS_ENSURE_SUCCESS(rv, rv);
525 
526  // no sense preparing this either.
527  rv = query->AddQuery(mSQLStrings.LibraryMediaItemSelect());
528  NS_ENSURE_SUCCESS(rv, rv);
529 
530  rv = query->Execute(&dbOk);
531  NS_ENSURE_SUCCESS(rv, rv);
532  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
533 
534  rv = query->GetResultObject(getter_AddRefs(result));
535  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
536 
537  rv = result->GetRowCount(&rowCount);
538  NS_ENSURE_SUCCESS(rv, rv);
539 
540  NS_ASSERTION(rowCount == 1, "Failed to get data from library_media_item");
541 
542  for (PRUint32 i = 0; i < sStaticPropertyCount; i++) {
543  nsString value;
544  rv = result->GetRowCell(0, i, value);
545  NS_ENSURE_SUCCESS(rv, rv);
546 
547  if (!value.IsVoid()) {
548 
549  rv = aBag->PutValue(sStaticProperties[i].mDBID, value);
550 
551  NS_ENSURE_SUCCESS(rv, rv);
552  }
553  }
554  return NS_OK;
555 }
556 
562 template <class T>
563 inline
564 PRUint32 GetGUIDCount(T const & aGUIDs,
565  PRInt32 aLibraryItemPosition)
566 {
567  return aLibraryItemPosition == -1 ? aGUIDs.Length() : (aGUIDs.Length() - 1);
568 }
569 
570 template <class T>
571 nsresult sbLocalDatabasePropertyCache::RetrieveProperties(
572  T & aGUIDs,
573  nsCOMArray<sbLocalDatabaseResourcePropertyBag> & aBags)
574 {
575  nsresult rv;
576  PRInt32 const libraryItemPosition = aGUIDs.IndexOf(mLibraryResourceGUID);
577  // blank out the library guid so we don't process it as a media item
578  if (libraryItemPosition != -1) {
579  aGUIDs[libraryItemPosition].Truncate();
580  }
581  // Start with the top level properties. This pulls the data from the
582  // media_items table for each requested guid. This also serves to
583  // translate the guid to a media_item_id so we can use the id for
584  // the next lookup on the resource_properties table
585 
586  if (aGUIDs.Length() > 0) {
587  nsCOMPtr<sbIDatabaseQuery> query;
588  nsresult rv = MakeQuery(getter_AddRefs(query));
589  NS_ENSURE_SUCCESS(rv, rv);
590 
591  // As we read in the data from media_item_ids, keep a map of the id to
592  // property bag so we don't need guids in the second pass
593 
594  nsTArray<PRUint32> itemIDs(aGUIDs.Length());
595  IDToBagMap idToBagMap;
596  PRBool const success = idToBagMap.Init(aGUIDs.Length());
597  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
598 
599  rv = RetrievePrimaryProperties(query, aGUIDs, idToBagMap, aBags, itemIDs);
600  NS_ENSURE_SUCCESS(rv, rv);
601 
602  NS_ASSERTION(idToBagMap.Count() == GetGUIDCount(aGUIDs, libraryItemPosition) &&
603  GetGUIDCount(aGUIDs, libraryItemPosition) == itemIDs.Length() &&
604  GetGUIDCount(aGUIDs, libraryItemPosition) == PRUint32(aBags.Count()),
605  "RetrieveProperties returned an inconsistent result set");
606 
607  // Now do the data from resource_properties
608  rv = RetrieveSecondaryProperties(query, itemIDs, idToBagMap);
609  }
610  // Cache the library's property data from library_media_item and
611  // resource_properties if we were passed the library guid
612  if (libraryItemPosition != -1) {
613  nsRefPtr<sbLocalDatabaseResourcePropertyBag> bag =
614  new sbLocalDatabaseResourcePropertyBag(this, 0, mLibraryResourceGUID);
615  NS_ENSURE_TRUE(bag, NS_ERROR_OUT_OF_MEMORY);
616 
617  rv = bag->Init();
618  NS_ENSURE_SUCCESS(rv, rv);
619 
620  rv = RetrieveLibraryProperties(bag);
621  NS_ENSURE_SUCCESS(rv, rv);
622 
623  // Restore the library GUID
624  aGUIDs[libraryItemPosition] = mLibraryResourceGUID;
625  aBags.ReplaceObjectAt(bag, libraryItemPosition);
626  }
627 
628  return NS_OK;
629 }
630 
632 {
633 public:
634  PRUnicharAdaptor(PRUnichar const * * aCharArray,
635  PRUint32 aLength) : mCharArray(aCharArray), mLength(aLength)
636  {
637  NS_ASSERTION(aCharArray, "PRUnicharAdaptor must be not passed a null pointer");
638  }
639  nsString operator[](PRUint32 aIndex) const {
640  NS_ASSERTION(aIndex < mLength,
641  "PRunicharAdaptor::operator [] index exceeds array length");
642  return nsDependentString(mCharArray[aIndex]);
643  }
644  PRUint32 Length() const {
645  return mLength;
646  }
647  PRBool RemoveElement(nsAString const & aItemToRemove)
648  {
649  for (PRUint32 index = 0; index < mLength; ++index) {
650  if (aItemToRemove.Equals(mCharArray[index])) {
651  // Shift everything down, overwriting and removing the desired element
652  // These are pointers from nsAString::BeginReading so no need to free
653  memcpy(mCharArray + index,
654  mCharArray + index + 1,
655  (mLength - index - 1) * sizeof(PRUnichar *));
656  --mLength;
657  return PR_TRUE;
658  }
659  }
660  return PR_FALSE;
661  }
662  PRInt32 IndexOf(nsAString const & aItemToFind) const
663  {
664  for (PRUint32 index = 0; index < mLength; ++index) {
665  if (aItemToFind.Equals(mCharArray[index])) {
666  return index;
667  }
668  }
669  return -1;
670  }
671 private:
672  PRUnichar const * * mCharArray;
673  PRUint32 mLength;
674 };
675 
676 NS_IMETHODIMP
677 sbLocalDatabasePropertyCache::CacheProperties(const PRUnichar **aGUIDArray,
678  PRUint32 aGUIDArrayCount)
679 {
680  TRACE("sbLocalDatabasePropertyCache[0x%.8x] - CacheProperties(%d)", this,
681  aGUIDArrayCount);
682 
683  NS_ASSERTION(mLibrary, "You didn't initialize!");
684  nsresult rv;
685 
686  // If we're asked to cache more than we can store, then just ignore since
687  // it would just do a lot of reads that would be throw away.
688  if (aGUIDArrayCount > CACHE_SIZE) {
689  NS_WARNING("Requested to cache more items than the cache can hold, ignoring request");
690  return NS_OK;
691  }
692  // First, collect all the guids that are not cached
693  nsTArray<nsString> misses;
694  PRBool cacheLibraryMediaItem = PR_FALSE;
695 
696  // Unfortunately need to lock for the duration of this call
697  // till after we build the misses array, we don't want another
698  // thread coming in behind us and thinking there's misses when they're
699  // going to be loaded.
700  {
701  nsAutoMonitor mon(mMonitor);
702 
703  for (PRUint32 i = 0; i < aGUIDArrayCount; i++) {
704 
705  nsDependentString guid(aGUIDArray[i]);
706 
707  if (mCache.Get(guid) == nsnull) {
708 
709  if (guid.Equals(mLibraryResourceGUID)) {
710  cacheLibraryMediaItem = PR_TRUE;
711  }
712  else {
713  nsString* newElement = misses.AppendElement(guid);
714  NS_ENSURE_TRUE(newElement, NS_ERROR_OUT_OF_MEMORY);
715  }
716  }
717  }
718 
719  PRUint32 const numMisses = misses.Length();
720 
721  // If there were no misses and we don't have to cache the library media
722  // item, just return since there is nothing to do
723  if (numMisses > 0) {
724  nsCOMArray<sbLocalDatabaseResourcePropertyBag> bags(numMisses);
725  rv = RetrieveProperties(misses, bags);
726  NS_ENSURE_SUCCESS(rv, rv);
727  NS_ENSURE_TRUE(bags.Count() == PRInt32(numMisses), NS_ERROR_UNEXPECTED);
728  for (PRUint32 index = 0; index < numMisses; ++index) {
729  sbLocalDatabaseResourcePropertyBag * const bag = bags[index];
730  // Skip nulls, these weren't found in the database
731  if (bag == nsnull) {
732  TRACE("sbLocalDatabasePropertyCache[0x%.8x] - CacheProperties() - "
733  "media item with id %s not found in database",
734  this, NS_ConvertUTF16toUTF8(misses[index]).get());
735  continue;
736  }
737 #ifdef DEBUG
738  nsString temp;
739  bag->GetGuid(temp);
740  NS_ASSERTION(misses[index].Equals(temp), "inserting an bag that doesn't match the guid");
741 #endif
742  mCache.Put(misses[index], bag);
743  }
744  }
745 
746  TRACE("sbLocalDatabasePropertyCache[0x%.8x] - CacheProperties() - Misses %d", this,
747  numMisses);
748  }
749 
750  return NS_OK;
751 }
752 
753 NS_IMETHODIMP
754 sbLocalDatabasePropertyCache::GetProperties(const PRUnichar **aGUIDArray,
755  PRUint32 aGUIDArrayCount,
756  PRUint32 *aPropertyArrayCount,
757  sbILocalDatabaseResourcePropertyBag ***aPropertyArray)
758 {
759  NS_ASSERTION(mLibrary, "You didn't initialize!");
760 
761  NS_ENSURE_ARG_POINTER(aPropertyArrayCount);
762  NS_ENSURE_ARG_POINTER(aPropertyArray);
763 
764  if (!aGUIDArrayCount) {
765  NS_WARNING("Asked for 0 properties in call to GetProperties!");
766  *aPropertyArrayCount = 0;
767  *aPropertyArray = nsnull;
768  return NS_OK;
769  }
770 
771  // A lot of letters just to allocate the correct number of pointers...
772  nsAutoArrayPtr<sbILocalDatabaseResourcePropertyBag*> propertyBagArray(
773  static_cast<sbILocalDatabaseResourcePropertyBag**> (
774  NS_Alloc(sizeof(sbILocalDatabaseResourcePropertyBag*) * aGUIDArrayCount)));
775  NS_ENSURE_TRUE(propertyBagArray, NS_ERROR_OUT_OF_MEMORY);
776  memset(propertyBagArray.get(), 0, sizeof(sbILocalDatabaseResourcePropertyBag*) * aGUIDArrayCount);
777 
778  // must initialize because we look for errors coming out of the for loop
779  nsresult rv = NS_OK;
780  nsTArray<PRUint32> missesIndex(CACHE_SIZE);
781  nsTArray<nsString> misses(CACHE_SIZE);
782  PRUint32 i;
783  PRBool cacheUpdated = PR_FALSE;
784 
785  nsAutoMonitor mon(mMonitor);
786 
787  for (i = 0; i < aGUIDArrayCount; i++) {
788 
789  nsDependentString const guid(aGUIDArray[i]);
790  sbLocalDatabaseResourcePropertyBag * bag = nsnull;
791 
792  // If the bag has a pending write waiting we need to get it into the
793  // database so that what is returned is consistent.
794  if (mDirty.Get(guid, nsnull)) {
795  // Write will acquire the lock as necessary. Write will
796  // also potentially have to call back into the property
797  // cache on the main thread to invalidate the GUID arrays.
798  mon.Exit();
799 
800  rv = Write();
801  NS_ENSURE_SUCCESS(rv, rv);
802 
803  // Relock.
804  mon.Enter();
805  }
806 
807  bag = mCache.Get(guid);
808  if (bag) {
809  // Bag is not addref from mCache, and propertyBagArray is straight
810  // pointers
811  NS_ADDREF(propertyBagArray[i] = bag);
812  }
813  else {
814  // Save the miss guid and index so we can retrieve it later
815  PRUint32 * const newIndex = missesIndex.AppendElement(i);
816  NS_ENSURE_TRUE(newIndex, NS_ERROR_OUT_OF_MEMORY);
817 
818  nsString * const newGuid =
819  misses.AppendElement(guid);
820  NS_ENSURE_TRUE(newGuid, NS_ERROR_OUT_OF_MEMORY);
821  }
822 
823  // Batch up our reads. We want to read multiple bags at a time
824  // and we'll use the BATCH_READ_SIZE as an arbitrary point to read.
825  // And we want to be sure to read on the last item
826  PRBool const timeToRead = misses.Length() == BATCH_READ_SIZE ||
827  i == aGUIDArrayCount - 1;
828  if (timeToRead && misses.Length() > 0) {
829  // Clear the temporary bag container and retrieve the misses
830  nsCOMArray<sbLocalDatabaseResourcePropertyBag> bags(CACHE_SIZE);
831  rv = RetrieveProperties(misses, bags);
832  if (NS_FAILED(rv))
833  break;
834  if (bags.Count() > 0) {
835  PRUint32 const missesLength = missesIndex.Length();
836  NS_ENSURE_TRUE(PRInt32(missesLength) == bags.Count(),
837  NS_ERROR_UNEXPECTED);
838  // Now store the bags we retrieved into the out parameter
839  for (PRUint32 index = 0; index < missesLength; ++index) {
840  PRUint32 const missIndex = missesIndex[index];
841  sbLocalDatabaseResourcePropertyBag * const bag = bags[index];
842  // If we're missing an item error out
843  if (bag == nsnull) {
844  rv = NS_ERROR_NOT_AVAILABLE;
845  break;
846  }
847  // If this is the first set of misses and within the cache size
848  // update the cache
849  if (!cacheUpdated && missIndex < CACHE_SIZE) {
850 #ifdef DEBUG
851  nsString temp;
852  bag->GetGuid(temp);
853  NS_ASSERTION(temp.Equals(aGUIDArray[missIndex]), "inserting an bag that doesn't match the guid");
854 #endif
855  mCache.Put(nsDependentString(aGUIDArray[missIndex]), bag);
856  }
857  NS_ADDREF(propertyBagArray[missIndex] = bag);
858  }
859  cacheUpdated = PR_TRUE;
860  }
861  else {
862  NS_WARNING("RetrieveProperties didn't retrieve anything");
863  }
864  missesIndex.Clear();
865  misses.Clear();
866  }
867  }
868 
869  // Check for error in loop and clean up
870  if (NS_FAILED(rv)) {
871  for (PRUint32 index = 0; index < i; ++index) {
872  NS_IF_RELEASE(propertyBagArray[index]);
873  }
874  return rv;
875  }
876 
877  *aPropertyArrayCount = aGUIDArrayCount;
878  *aPropertyArray = propertyBagArray.forget();
879  return NS_OK;
880 }
881 
882 NS_IMETHODIMP
883 sbLocalDatabasePropertyCache::SetProperties(const PRUnichar **aGUIDArray,
884  PRUint32 aGUIDArrayCount,
885  sbILocalDatabaseResourcePropertyBag **aPropertyArray,
886  PRUint32 aPropertyArrayCount,
887  PRBool aWriteThroughNow)
888 {
889  NS_ASSERTION(mLibrary, "You didn't initialize!");
890  NS_ENSURE_ARG_POINTER(aGUIDArray);
891  NS_ENSURE_ARG_POINTER(aPropertyArray);
892  NS_ENSURE_TRUE(aGUIDArrayCount == aPropertyArrayCount, NS_ERROR_INVALID_ARG);
893 
894  nsresult rv = NS_OK;
895 
896  sbAutoBatchHelper batchHelper(*mLibrary);
897 
898  // Scoped locking. Must always avoid calling Write with monitor acquired.
899  {
900  nsAutoMonitor mon(mMonitor);
901  for(PRUint32 i = 0; i < aGUIDArrayCount; i++) {
902  nsDependentString const guid(aGUIDArray[i]);
903  nsRefPtr<sbLocalDatabaseResourcePropertyBag> bag;
904 
905  bag = mCache.Get(guid);
906  // If it's not cached we need to create a new bag
907  if (!bag) {
908  PRUint32 mediaItemId;
909  rv = aPropertyArray[i]->GetMediaItemId(&mediaItemId);
910  NS_ENSURE_SUCCESS(rv, rv);
911 
912  bag = new sbLocalDatabaseResourcePropertyBag(this, mediaItemId, guid);
913  }
914  nsCOMPtr<nsIStringEnumerator> ids;
915  rv = aPropertyArray[i]->GetIds(getter_AddRefs(ids));
916  NS_ENSURE_SUCCESS(rv, rv);
917 
918  PRBool hasMore = PR_FALSE;
919  nsString id, value;
920 
921  while(NS_SUCCEEDED(ids->HasMore(&hasMore)) && hasMore) {
922  rv = ids->GetNext(id);
923  NS_ENSURE_SUCCESS(rv, rv);
924 
925  rv = aPropertyArray[i]->GetProperty(id, value);
926  NS_ENSURE_SUCCESS(rv, rv);
927 
928  bag->SetProperty(id, value);
929  }
930  NS_ENSURE_TRUE(bag, NS_ERROR_UNEXPECTED);
931  mDirty.Put(guid, bag);
932  }
933  }
934 
935  if(aWriteThroughNow) {
936  rv = Write();
937  NS_ENSURE_SUCCESS(rv, rv);
938  }
939 
940  return rv;
941 }
942 
947 {
948 public:
951  sbIDatabaseQuery * aQuery,
952  PRUint32 aMediaItemID,
953  PRBool aIsLibrary) :
954  mCache(aCache),
955  mBag(aBag),
956  mQuery(aQuery),
957  mMediaItemID(aMediaItemID),
958  mIsLibrary(aIsLibrary) {}
959  nsresult Process(PRUint32 aDirtyPropertyKey);
960 private:
961  // None-owning reference
963  // non-owning reference
965  // Non-owning reference
966  sbIDatabaseQuery * mQuery;
967  PRUint32 mMediaItemID;
968  PRBool mIsLibrary;
969  nsTArray<nsString> mTopLevelSets;
970 };
971 
972 nsresult DirtyPropertyEnumerator::Process(PRUint32 aDirtyPropertyKey)
973 {
974  nsString propertyID;
975 
976  //If this isn't true, something is very wrong, so bail out.
977  PRBool success = mCache->GetPropertyID(aDirtyPropertyKey, propertyID);
978  NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
979 
980  // Never change the guid
981  if (propertyID.EqualsLiteral(SB_PROPERTY_GUID)) {
982  NS_WARNING("Attempt to change the SB_PROPERTY_GUID property");
983  return NS_OK;
984  }
985 
986  nsString value;
987  nsresult rv = mBag->GetPropertyByID(aDirtyPropertyKey, value);
988  NS_ENSURE_SUCCESS(rv, rv);
989 
990  //Top level properties need to be treated differently, so check for them.
991  if(SB_IsTopLevelProperty(aDirtyPropertyKey)) {
992  // First, account for Top Level Properties.
993  nsCOMPtr<sbIDatabasePreparedStatement> topLevelPropertyUpdate;
994  if (!mIsLibrary) {
995  success = mCache->mMediaItemsUpdatePreparedStatements.Get(aDirtyPropertyKey,
996  getter_AddRefs(topLevelPropertyUpdate));
997  }
998  else {
999  success = mCache->mLibraryMediaItemUpdatePreparedStatements.Get(aDirtyPropertyKey,
1000  getter_AddRefs(topLevelPropertyUpdate));
1001  }
1002  NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
1003 
1004  rv = mQuery->AddPreparedStatement(topLevelPropertyUpdate);
1005  NS_ENSURE_SUCCESS(rv,rv);
1006 
1007  nsString columnName;
1008  rv = SB_GetTopLevelPropertyColumn(aDirtyPropertyKey, columnName);
1009  NS_ENSURE_SUCCESS(rv,rv);
1010 
1011  PRUint32 columnType = (PRUint32)-1;
1012  rv = SB_GetTopLevelPropertyColumnType(aDirtyPropertyKey, columnType);
1013  NS_ENSURE_SUCCESS(rv, rv);
1014 
1015  if (columnType == SB_COLUMN_TYPE_TEXT) {
1016  rv = mQuery->BindStringParameter(0, value);
1017  NS_ENSURE_SUCCESS(rv,rv);
1018  }
1019  else if (columnType == SB_COLUMN_TYPE_INTEGER) {
1020  PRUint64 intVal = nsString_ToUint64(value, &rv);
1021  NS_ENSURE_SUCCESS(rv,rv);
1022  rv = mQuery->BindInt64Parameter(0, intVal);
1023  NS_ENSURE_SUCCESS(rv,rv);
1024  }
1025  else {
1026  NS_WARNING("Failed to determine the column type of a top level property.");
1027  return NS_ERROR_CANNOT_CONVERT_DATA;
1028  }
1029  if (!mIsLibrary) {
1030  rv = mQuery->BindInt32Parameter(1, mMediaItemID);
1031  NS_ENSURE_SUCCESS(rv,rv);
1032  }
1033  }
1034  else { //Regular properties all go in the same spot.
1035  if (value.IsVoid()) {
1036  rv = mQuery->AddPreparedStatement(mCache->mPropertiesDeletePreparedStatement);
1037  NS_ENSURE_SUCCESS(rv, rv);
1038 
1039  rv = mQuery->BindInt32Parameter(0, mMediaItemID);
1040  NS_ENSURE_SUCCESS(rv, rv);
1041 
1042  rv = mQuery->BindInt32Parameter(1, aDirtyPropertyKey);
1043  NS_ENSURE_SUCCESS(rv, rv);
1044  }
1045  else {
1046  nsString searchable;
1047  rv = mBag->GetSearchablePropertyByID(aDirtyPropertyKey, searchable);
1048  NS_ENSURE_SUCCESS(rv, rv);
1049 
1050  nsString sortable;
1051  rv = mBag->GetSortablePropertyByID(aDirtyPropertyKey, sortable);
1052  NS_ENSURE_SUCCESS(rv, rv);
1053 
1054  nsString secondarySortable;
1055  rv = mCache->CreateSecondarySortValue(mBag,
1056  aDirtyPropertyKey, secondarySortable);
1057  NS_ENSURE_SUCCESS(rv, rv);
1058 
1059  rv = mQuery->AddPreparedStatement(mCache->mPropertiesInsertPreparedStatement);
1060  NS_ENSURE_SUCCESS(rv, rv);
1061 
1062  rv = mQuery->BindInt32Parameter(0, mMediaItemID);
1063  NS_ENSURE_SUCCESS(rv, rv);
1064 
1065  rv = mQuery->BindInt32Parameter(1, aDirtyPropertyKey);
1066  NS_ENSURE_SUCCESS(rv, rv);
1067 
1068  rv = mQuery->BindStringParameter(2, value);
1069  NS_ENSURE_SUCCESS(rv, rv);
1070 
1071  rv = mQuery->BindStringParameter(3, searchable);
1072  NS_ENSURE_SUCCESS(rv, rv);
1073 
1074  rv = mQuery->BindStringParameter(4, sortable);
1075  NS_ENSURE_SUCCESS(rv, rv);
1076 
1077  rv = mQuery->BindStringParameter(5, secondarySortable);
1078  NS_ENSURE_SUCCESS(rv, rv);
1079  }
1080  }
1081  return NS_OK;
1082 }
1083 
1084 PR_STATIC_CALLBACK(PLDHashOperator)
1085 EnumDirtyProps(nsUint32HashKey *aKey, void *aClosure)
1086 {
1087  DirtyPropertyEnumerator * const dirtyPropsEnum = static_cast<DirtyPropertyEnumerator *>(aClosure);
1088  dirtyPropsEnum->Process(aKey->GetKey());
1089  return PL_DHASH_NEXT;
1090 }
1091 
1097 {
1098  nsTArray<nsString> mGUIDs;
1099  nsTArray<PRUint32> mIDs;
1100 };
1101 
1102 PR_STATIC_CALLBACK(PLDHashOperator)
1103 EnumDirtyItems(nsAString const &aKey, sbLocalDatabaseResourcePropertyBag * aBag, void *aClosure)
1104 {
1105  DirtyItems *dirtyItem = static_cast<DirtyItems *>(aClosure);
1106  NS_ENSURE_TRUE(dirtyItem->mGUIDs.AppendElement(aKey),
1107  PL_DHASH_STOP);
1108  nsresult rv = aBag->GetMediaItemId(dirtyItem->mIDs.AppendElement());
1109  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
1110 
1111  return PL_DHASH_NEXT;
1112 }
1113 
1114 PR_STATIC_CALLBACK(PLDHashOperator)
1116 {
1117  aBag->ClearDirty();
1118  return PL_DHASH_NEXT;
1119 }
1120 
1121 // note: this might be called either from the main thread (for a forced write)
1122 // or a background thread (from the flush thread)
1123 nsresult
1124 sbLocalDatabasePropertyCache::Write()
1125 {
1126  NS_ASSERTION(mLibrary, "You didn't initialize!");
1127  nsresult rv = NS_OK;
1128 
1129  nsCOMPtr<sbIDatabaseQuery> query;
1130  PRUint32 dirtyItemCount;
1131  { // find the new dirty properties
1132  DirtyItems dirtyItems;
1133 
1134  //Lock it.
1135  nsAutoMonitor mon(mMonitor);
1136 
1137  if (!mDirty.Count()) {
1138  return NS_OK;
1139  }
1140 
1141  //Reset the dirty flag of the property bags help by mDirty
1142  dirtyItemCount = mDirty.EnumerateRead(EnumDirtyItems, (void *) &dirtyItems);
1143  mWritePendingCount = 0;
1144 
1145  if (!dirtyItemCount)
1146  return NS_OK;
1147 
1148  rv = MakeQuery(getter_AddRefs(query));
1149  NS_ENSURE_SUCCESS(rv, rv);
1150 
1151  rv = query->AddQuery(NS_LITERAL_STRING("begin"));
1152  NS_ENSURE_SUCCESS(rv, rv);
1153 
1154  // Run through the list of dirty items and build the fts delete/insert
1155  // queries
1156  for (PRUint32 i = 0; i < dirtyItemCount; ++i) {
1157  rv = query->AddPreparedStatement(mMediaItemsFtsAllDeletePreparedStatement);
1158  NS_ENSURE_SUCCESS(rv, rv);
1159  rv = query->BindInt32Parameter(0, dirtyItems.mIDs[i]);
1160  NS_ENSURE_SUCCESS(rv, rv);
1161  }
1162 
1163  //For each GUID, there's a property bag that needs to be processed as well.
1164  for(PRUint32 i = 0; i < dirtyItemCount; ++i) {
1165  nsRefPtr<sbLocalDatabaseResourcePropertyBag> bag;
1166  nsString const guid(dirtyItems.mGUIDs[i]);
1167  if (mDirty.Get(guid, getter_AddRefs(bag))) {
1168 
1169  PRUint32 const mediaItemId = dirtyItems.mIDs[i];
1170 
1171  PRBool const isLibrary = guid.Equals(mLibraryResourceGUID);
1172 
1173  DirtyPropertyEnumerator dirtyPropertyEnumerator(this,
1174  bag,
1175  query,
1176  mediaItemId,
1177  isLibrary);
1178  PRUint32 dirtyPropsCount;
1179  rv = bag->EnumerateDirty(EnumDirtyProps, (void *) &dirtyPropertyEnumerator, &dirtyPropsCount);
1180  NS_ENSURE_SUCCESS(rv, rv);
1181 
1182  // Build a new FTS data table entry by concatenating all the user-viewable properties.
1183  // NOTE: This includes both top-level and not-top-level properties!
1184  // TODO: Look at top level properties to see if you want them searchable!
1185  nsString newFTSData;
1186  nsCOMPtr<nsIStringEnumerator> bagProperties;
1187  rv = bag->GetIds(getter_AddRefs(bagProperties));
1188  NS_ENSURE_SUCCESS(rv, rv);
1189  PRBool hasMore;
1190  while (NS_SUCCEEDED(bagProperties->HasMore(&hasMore)) && hasMore) {
1191  nsAutoString propertyId;
1192  rv = bagProperties->GetNext(propertyId);
1193  NS_ENSURE_SUCCESS(rv, rv);
1194 
1195  PRBool hasProperty, isUserViewable;
1196  rv = mPropertyManager->HasProperty(propertyId, &hasProperty);
1197  NS_ENSURE_SUCCESS(rv, rv);
1198  if (!hasProperty) {
1199  continue;
1200  }
1201 
1202  nsCOMPtr<sbIPropertyInfo> propertyInfo;
1203  rv = mPropertyManager->GetPropertyInfo(propertyId,
1204  getter_AddRefs(propertyInfo));
1205  NS_ENSURE_SUCCESS(rv,rv);
1206  rv = propertyInfo->GetUserViewable(&isUserViewable);
1207  NS_ENSURE_SUCCESS(rv,rv);
1208 
1209  if (isUserViewable) {
1210  PRUint32 propertyDBID;
1211  rv = GetPropertyDBID(propertyId, &propertyDBID);
1212  NS_ENSURE_SUCCESS(rv, rv);
1213  nsString propertySearchable;
1214  rv = bag->GetSearchablePropertyByID(propertyDBID, propertySearchable);
1215  NS_ENSURE_SUCCESS(rv, rv);
1216  newFTSData.Append(propertySearchable);
1217  newFTSData.AppendLiteral(" ");
1218  }
1219  }
1220 
1221  if (!newFTSData.IsEmpty()) {
1222  rv = query->AddPreparedStatement(mMediaItemsFtsAllInsertPreparedStatement);
1223  NS_ENSURE_SUCCESS(rv, rv);
1224  rv = query->BindInt32Parameter(0, dirtyItems.mIDs[i]);
1225  NS_ENSURE_SUCCESS(rv, rv);
1226  rv = query->BindStringParameter(1, newFTSData);
1227  NS_ENSURE_SUCCESS(rv, rv);
1228  }
1229  }
1230  }
1231 
1232  rv = query->AddQuery(NS_LITERAL_STRING("commit"));
1233  NS_ENSURE_SUCCESS(rv, rv);
1234 
1235  mDirty.EnumerateRead(EnumDirtyItemsSetDirty, nsnull);
1236 
1237  //Clear out dirty guid hashtable.
1238  mDirty.Clear();
1239  }
1240 
1241  PRInt32 dbOk;
1242  rv = query->Execute(&dbOk);
1243  NS_ENSURE_SUCCESS(rv, rv);
1244  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1245 
1246  if(!NS_IsMainThread()) {
1247  nsCOMPtr<nsIThread> mainThread;
1248  rv = NS_GetMainThread(getter_AddRefs(mainThread));
1249  NS_ENSURE_SUCCESS(rv, rv);
1250 
1251  nsCOMPtr<nsIRunnable> runnable =
1252  NS_NEW_RUNNABLE_METHOD(sbLocalDatabasePropertyCache,
1253  this,
1254  InvalidateGUIDArrays);
1255  NS_ENSURE_TRUE(runnable, NS_ERROR_FAILURE);
1256 
1257  // We must dispatch async since the guid array may have acquired the
1258  // mCacheMonitor. Synchronous dispatch can cause deadlocks. This is only
1259  // a band-aid and not a proper fix. See bug 23777 for more information.
1260  // TODO: XXX Fix this so either we don't need to dispatch or don't hold
1261  // the lock while dispatching.
1262  rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
1263  NS_ENSURE_SUCCESS(rv, rv);
1264  }
1265  else {
1266  rv = InvalidateGUIDArrays();
1267  NS_ENSURE_SUCCESS(rv, rv);
1268  }
1269 
1270  // Since we just invalidated the GUID arrays, we can cancel the timer.
1271  mInvalidateTimer->Cancel();
1272 
1273  return NS_OK;
1274 }
1275 
1276 NS_IMETHODIMP
1277 sbLocalDatabasePropertyCache::GetPropertyDBID(const nsAString& aPropertyID,
1278  PRUint32* _retval)
1279 {
1280  NS_ENSURE_ARG_POINTER(_retval);
1281 
1282  *_retval = GetPropertyDBIDInternal(aPropertyID);
1283  return NS_OK;
1284 }
1285 
1286 NS_IMETHODIMP
1287 sbLocalDatabasePropertyCache::Observe(nsISupports* aSubject,
1288  const char* aTopic,
1289  const PRUnichar* aData)
1290 {
1291  nsresult rv = NS_OK;
1292 
1293  nsCOMPtr<nsIObserverService> observerService =
1294  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
1295  NS_ASSERTION(NS_SUCCEEDED(rv), "Prop cache failed to get observer service");
1296  if (NS_SUCCEEDED(rv)) {
1297  observerService->RemoveObserver(this, aTopic);
1298  }
1299 
1300  if (strcmp(aTopic, SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC) == 0 ||
1301  strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID) == 0) {
1302  Shutdown();
1303 
1304  } else if (strcmp(aTopic, NS_FINAL_UI_STARTUP_OBSERVER_ID) == 0) {
1305 
1306  // If InvalidateSortData was previously called for this library but did not
1307  // complete, then we need to start it up again. Don't worry about UI this time,
1308  // just let it run in the background.
1309  PRBool hasInvalidData = PR_FALSE;
1310  GetSetInvalidSortDataPref(PR_FALSE, hasInvalidData);
1311  if (NS_SUCCEEDED(rv) && hasInvalidData) {
1312  nsCOMPtr<sbIJobProgress> job;
1313  InvalidateSortData(getter_AddRefs(job));
1314 
1315  // Launch modal UI. This prevents all libraries from trying to
1316  // update at once, in the case were multiple libraries have
1317  // been flagged as invalid
1318  nsCOMPtr<sbIJobProgressService> progressService =
1319  do_GetService("@songbirdnest.com/Songbird/JobProgressService;1", &rv);
1320  NS_ENSURE_SUCCESS(rv, rv);
1321  rv = progressService->ShowProgressDialog(job, nsnull, 0);
1322  NS_ENSURE_SUCCESS(rv, rv);
1323  }
1324  } else if (strcmp(aTopic, SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC) == 0) {
1325 
1326  // If we were in the middle of rebuilding all the sort info
1327  // then must abort. Do this BEFORE library shutdown, as
1328  // when library shutdown actually happens the invalidate job
1329  // library enumeration will fail.
1330  if (mSortInvalidateJob) {
1331  mSortInvalidateJob->Shutdown();
1332  mSortInvalidateJob = nsnull;
1333  }
1334  } else if (strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0) {
1335  if(SameCOMIdentity(aSubject, mFlushTimer)) {
1336  rv = DispatchFlush();
1337  NS_ENSURE_SUCCESS(rv, rv);
1338  }
1339  else if(SameCOMIdentity(aSubject, mInvalidateTimer)) {
1340  rv = InvalidateGUIDArrays();
1341  NS_ENSURE_SUCCESS(rv, rv);
1342  }
1343  }
1344 
1345  return NS_OK;
1346 }
1347 
1348 NS_IMETHODIMP
1349 sbLocalDatabasePropertyCache::InvalidateSortData(sbIJobProgress** _retval)
1350 {
1351  NS_ENSURE_ARG_POINTER(_retval);
1352  NS_ENSURE_TRUE(!mSortInvalidateJob, NS_ERROR_ALREADY_INITIALIZED);
1353  nsresult rv = NS_OK;
1354 
1355  // Flag this library as containing invalid sort data. This way
1356  // if we crash or are shutdown before the invalidate job completes
1357  // we can try again on next launch
1358  PRBool hasInvalidData = PR_TRUE;
1359  GetSetInvalidSortDataPref(PR_TRUE, hasInvalidData);
1360 
1361  // Start a job to read, process, and set all the properties in the library
1362  NS_NEWXPCOM(mSortInvalidateJob, sbLocalDatabaseSortInvalidateJob);
1363  NS_ENSURE_TRUE(mSortInvalidateJob, NS_ERROR_OUT_OF_MEMORY);
1364 
1365  // Go!
1366  mSortInvalidateJob->Init(this, mLibrary);
1367  NS_ENSURE_SUCCESS(rv, rv);
1368 
1369  NS_ADDREF(*_retval = mSortInvalidateJob);
1370  return rv;
1371 }
1372 
1373 nsresult
1375 
1376  // Clear the needs-rebuilding flag
1377  PRBool hasInvalidData = PR_FALSE;
1378  GetSetInvalidSortDataPref(PR_TRUE, hasInvalidData);
1379 
1380  mSortInvalidateJob = nsnull;
1381  return NS_OK;
1382 }
1383 
1384 nsresult
1385 sbLocalDatabasePropertyCache::GetSetInvalidSortDataPref(
1386  PRBool aWrite, PRBool& aValue)
1387 {
1388  // Figure out the pref key
1389  nsString guid;
1390  nsresult rv = mLibrary->GetDatabaseGuid(guid);
1391  NS_ENSURE_SUCCESS(rv, rv);
1392  nsCString pref = NS_LITERAL_CSTRING("songbird.propertycache.");
1393  pref.Append(NS_LossyConvertUTF16toASCII(guid));
1394  pref.AppendLiteral(".invalidSortData");
1395 
1396  nsCOMPtr<nsIPrefBranch> prefBranch =
1397  do_GetService("@mozilla.org/preferences-service;1", &rv);
1398  NS_ENSURE_SUCCESS(rv, rv);
1399  nsCOMPtr<nsIPrefService> prefRoot = do_QueryInterface(prefBranch, &rv);
1400  NS_ENSURE_SUCCESS(rv, rv);
1401 
1402  // If writing, set value and flush to disk just in case
1403  if (aWrite) {
1404  rv = prefBranch->SetBoolPref(pref.get(), aValue);
1405  NS_ENSURE_SUCCESS(rv, rv);
1406  rv = prefRoot->SavePrefFile(nsnull);
1407  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to save prefs.");
1408  // If reading, assume not set == false
1409  } else {
1410  rv = prefBranch->GetBoolPref(
1411  pref.get(), &aValue);
1412  if (NS_FAILED(rv)) {
1413  aValue = PR_FALSE;
1414  }
1415  }
1416  return rv;
1417 }
1418 
1419 void
1420 sbLocalDatabasePropertyCache::AddDependentGUIDArray(
1421  sbLocalDatabaseGUIDArray *aGUIDArray)
1422 {
1423  NS_ENSURE_TRUE(aGUIDArray, /*void*/);
1424 
1425  nsAutoMonitor mon(mDependentGUIDArrayMonitor);
1426  nsCOMPtr<nsISupports> supports =
1427  do_QueryInterface(static_cast<sbILocalDatabaseGUIDArray*>(aGUIDArray));
1428  nsCOMPtr<nsIWeakReference> weakRef =
1429  do_QueryInterface(static_cast<sbSupportsWeakReference*>(aGUIDArray));
1430  mDependentGUIDArrays[supports.get()] = weakRef;
1431 
1432  return;
1433 }
1434 
1435 void
1436 sbLocalDatabasePropertyCache::RemoveDependentGUIDArray(
1437  sbLocalDatabaseGUIDArray *aGUIDArray)
1438 {
1439  NS_ENSURE_TRUE(aGUIDArray, /*void*/);
1440  nsAutoMonitor mon(mDependentGUIDArrayMonitor);
1441 
1442  nsCOMPtr<nsISupports> supports =
1443  do_QueryInterface(static_cast<sbILocalDatabaseGUIDArray*>(aGUIDArray));
1444  DependentGUIDArrays_t::iterator it = mDependentGUIDArrays.find(supports);
1445  if(it != mDependentGUIDArrays.end()) {
1446  mDependentGUIDArrays.erase(it);
1447  }
1448 
1449  return;
1450 }
1451 
1452 nsresult
1453 sbLocalDatabasePropertyCache::DispatchFlush()
1454 {
1455  nsCOMPtr<nsIRunnable> runnable =
1456  NS_NEW_RUNNABLE_METHOD(sbLocalDatabasePropertyCache, this, RunFlushThread);
1457  NS_ENSURE_TRUE(runnable, NS_ERROR_FAILURE);
1458 
1459  nsresult rv = mThreadPoolService->Dispatch(runnable, NS_DISPATCH_NORMAL);
1460  NS_ENSURE_SUCCESS(rv, rv);
1461 
1462  LOG("property cache flush operation dispatched");
1463 
1464  return NS_OK;
1465 }
1466 
1467 void
1468 sbLocalDatabasePropertyCache::RunFlushThread()
1469 {
1470  nsresult SB_UNUSED_IN_RELEASE(rv) = Write();
1471  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to flush property cache; will retry");
1472 }
1473 
1474 nsresult
1475 sbLocalDatabasePropertyCache::MakeQuery(sbIDatabaseQuery** _retval)
1476 {
1477  NS_ENSURE_ARG_POINTER(_retval);
1478 
1479  nsresult rv;
1480 
1481  nsCOMPtr<sbIDatabaseQuery> query =
1482  do_CreateInstance(SONGBIRD_DATABASEQUERY_CONTRACTID, &rv);
1483  NS_ENSURE_SUCCESS(rv, rv);
1484 
1485  rv = query->SetDatabaseGUID(mDatabaseGUID);
1486  NS_ENSURE_SUCCESS(rv, rv);
1487 
1488  if (mDatabaseLocation) {
1489  rv = query->SetDatabaseLocation(mDatabaseLocation);
1490  NS_ENSURE_SUCCESS(rv, rv);
1491  }
1492 
1493  rv = query->SetAsyncQuery(PR_FALSE);
1494  NS_ENSURE_SUCCESS(rv, rv);
1495 
1496  NS_ADDREF(*_retval = query);
1497  return NS_OK;
1498 }
1499 
1500 nsresult
1501 sbLocalDatabasePropertyCache::LoadProperties()
1502 {
1503  nsresult rv;
1504  PRInt32 dbOk;
1505 
1506  if (!mPropertyIDToDBID.IsInitialized()) {
1507  PRBool success = mPropertyIDToDBID.Init(100);
1508  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1509  }
1510  else {
1511  mPropertyIDToDBID.Clear();
1512  }
1513 
1514  if (!mPropertyDBIDToID.IsInitialized()) {
1515  PRBool success = mPropertyDBIDToID.Init(100);
1516  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1517  }
1518  else {
1519  mPropertyDBIDToID.Clear();
1520  }
1521 
1522  nsCOMPtr<sbIDatabaseQuery> query;
1523  rv = MakeQuery(getter_AddRefs(query));
1524  NS_ENSURE_SUCCESS(rv, rv);
1525 
1526  rv = query->AddQuery(sbLocalDatabaseSQL::PropertiesSelect());
1527  NS_ENSURE_SUCCESS(rv, rv);
1528 
1529  rv = query->Execute(&dbOk);
1530  NS_ENSURE_SUCCESS(rv, rv);
1531  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1532 
1533  nsCOMPtr<sbIDatabaseResult> result;
1534  rv = query->GetResultObject(getter_AddRefs(result));
1535  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1536 
1537  PRUint32 rowCount;
1538  rv = result->GetRowCount(&rowCount);
1539  NS_ENSURE_SUCCESS(rv, rv);
1540 
1541  for (PRUint32 i = 0; i < rowCount; i++) {
1542  nsAutoString propertyDBIDStr;
1543  rv = result->GetRowCell(i, 0, propertyDBIDStr);
1544  NS_ENSURE_SUCCESS(rv, rv);
1545 
1546  PRUint32 propertyDBID = propertyDBIDStr.ToInteger(&rv);
1547  NS_ENSURE_SUCCESS(rv, rv);
1548 
1549  nsString propertyID;
1550  rv = result->GetRowCell(i, 1, propertyID);
1551  NS_ENSURE_SUCCESS(rv, rv);
1552 
1553  PRBool success = mPropertyDBIDToID.Put(propertyDBID, propertyID);
1554  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1555 
1556  TRACE("Added %d => %s to property name cache", propertyDBID,
1557  NS_ConvertUTF16toUTF8(propertyID).get());
1558 
1559  success = mPropertyIDToDBID.Put(propertyID, propertyDBID);
1560  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1561 
1562  }
1563 
1564  /*
1565  * Add top level properties
1566  */
1567  for (PRUint32 i = 0; i < sStaticPropertyCount; i++) {
1568 
1569  nsString propertyID(NS_ConvertASCIItoUTF16(sStaticProperties[i].mPropertyID));
1570 
1571  PRBool success = mPropertyDBIDToID.Put(sStaticProperties[i].mDBID, propertyID);
1572  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1573 
1574  success = mPropertyIDToDBID.Put(propertyID, sStaticProperties[i].mDBID);
1575  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1576 
1577  }
1578 
1579  return NS_OK;
1580 }
1581 
1582 nsresult
1583 sbLocalDatabasePropertyCache::AddDirty(const nsAString &aGuid,
1585 {
1586  nsresult rv;
1587  NS_ENSURE_ARG_POINTER(aBag);
1588  nsAutoString guid(aGuid);
1589 
1590  nsAutoMonitor mon(mMonitor);
1591 
1592  // If another bag for the same guid is already in the dirty list, then we
1593  // risk losing information if we don't write out immediately.
1594  if (mDirty.Get(guid, nsnull)) {
1595  NS_WARNING("Property cache forcing Write() due to duplicate "
1596  "guids in the dirty bag list. This should be a rare event.");
1597 
1598  // Never call Write with monitor acquired. Write may have to proxy
1599  // invalidation of the GUID arrays to the main thread which requires
1600  // acquiring the monitor.
1601  mon.Exit();
1602 
1603  rv = Write();
1604  NS_ENSURE_SUCCESS(rv, rv);
1605 
1606  // Relock.
1607  mon.Enter();
1608  }
1609 
1610  mDirty.Put(guid, aBag);
1611  ++mWritePendingCount;
1612 
1613  // Add dirty property ids for invalidation of guid arrays.
1614  std::set<PRUint32> dirtyPropIds;
1615  rv = aBag->GetDirtyForInvalidation(dirtyPropIds);
1616  NS_ENSURE_SUCCESS(rv, rv);
1617 
1618  mDirtyForInvalidation.insert(dirtyPropIds.begin(), dirtyPropIds.end());
1619 
1620  // Invalidate uses a timer which enables the invalidation to occur
1621  // after 'AddDirty' stops being called. This will avoid constant
1622  // rebuilds of the GUID array when data is changing in bulk.
1623  rv = mInvalidateTimer->Cancel();
1624  NS_ENSURE_SUCCESS(rv, rv);
1625 
1626  rv = mInvalidateTimer->Init(this, 1000, nsITimer::TYPE_ONE_SHOT);
1627  NS_ENSURE_SUCCESS(rv, rv);
1628 
1629  return NS_OK;
1630 }
1631 
1632 nsresult
1633 sbLocalDatabasePropertyCache::InvalidateGUIDArrays()
1634 {
1635  // Invalidate dependent guid arrays.
1636  nsCOMArray<sbILocalDatabaseGUIDArray> arrays;
1637 
1638  {
1639  nsAutoMonitor mon(mDependentGUIDArrayMonitor);
1640  DependentGUIDArrays_t::iterator cit = mDependentGUIDArrays.begin();
1641  DependentGUIDArrays_t::iterator end = mDependentGUIDArrays.end();
1642  while (cit != end) {
1643  nsCOMPtr<sbILocalDatabaseGUIDArray> guidArray =
1644  do_QueryReferent(cit->second);
1645  // If we have a valid object then add it, else we'll remove it from our
1646  // list
1647  if (guidArray) {
1648  NS_ENSURE_TRUE(arrays.AppendObject(guidArray.get()), NS_ERROR_OUT_OF_MEMORY);
1649  ++cit;
1650  }
1651  else {
1652  DependentGUIDArrays_t::iterator deleteIter = cit;
1653  ++cit;
1654  mDependentGUIDArrays.erase(deleteIter);
1655  }
1656  }
1657  }
1658 
1659  std::vector<PRUint32> dirtyPropIDs;
1660 
1661  // Copy the data into a temporary set to avoid invalidating the guid arrays
1662  // with the property cache lock held.
1663  {
1664  nsAutoMonitor mon(mMonitor);
1665  std::insert_iterator<std::vector<PRUint32> > insertIter(dirtyPropIDs,
1666  dirtyPropIDs.end());
1667  std::copy(mDirtyForInvalidation.begin(),
1668  mDirtyForInvalidation.end(),
1669  insertIter);
1670  mDirtyForInvalidation.clear();
1671  }
1672 
1673  PRInt32 const count = arrays.Count();
1674  for (PRInt32 index = 0; index < count; ++index) {
1675  nsresult SB_UNUSED_IN_RELEASE(rv) =
1676  arrays[index]->MayInvalidate(&dirtyPropIDs[0],
1677  dirtyPropIDs.size());
1678  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1679  "Failed to invalidate GUID array, GUIDs may be stale.");
1680  }
1681  return NS_OK;
1682 }
1683 
1684 PRUint32
1685 sbLocalDatabasePropertyCache::GetPropertyDBIDInternal(const nsAString& aPropertyID)
1686 {
1687  PRUint32 retval;
1688  if (!mPropertyIDToDBID.Get(aPropertyID, &retval)) {
1689  nsresult rv = InsertPropertyIDInLibrary(aPropertyID, &retval);
1690 
1691  if(NS_FAILED(rv)) {
1692  retval = 0;
1693  }
1694 
1695  }
1696  return retval;
1697 }
1698 
1699 PRBool
1701  nsAString& aPropertyID)
1702 {
1703  nsString propertyID;
1704  if (mPropertyDBIDToID.Get(aPropertyDBID, &propertyID)) {
1705  aPropertyID = propertyID;
1706  return PR_TRUE;
1707  }
1708  return PR_FALSE;
1709 }
1710 
1711 nsresult
1712 sbLocalDatabasePropertyCache::InsertPropertyIDInLibrary(const nsAString& aPropertyID,
1713  PRUint32 *aPropertyDBID)
1714 {
1715  NS_ENSURE_ARG_POINTER(aPropertyDBID);
1716  nsAutoString sql;
1717 
1718  nsCOMPtr<sbIDatabaseQuery> query;
1719  nsresult rv = MakeQuery(getter_AddRefs(query));
1720  NS_ENSURE_SUCCESS(rv, rv);
1721 
1722  rv = query->AddQuery(sbLocalDatabaseSQL::PropertiesTableInsert());
1723  NS_ENSURE_SUCCESS(rv, rv);
1724 
1725  rv = query->BindStringParameter(0, aPropertyID);
1726  NS_ENSURE_SUCCESS(rv, rv);
1727 
1728  sql.AssignLiteral("select last_insert_rowid()");
1729  rv = query->AddQuery(sql);
1730  NS_ENSURE_SUCCESS(rv, rv);
1731 
1732  PRInt32 dbOk;
1733  rv = query->Execute(&dbOk);
1734  NS_ENSURE_SUCCESS(rv, rv);
1735  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1736 
1737  nsCOMPtr<sbIDatabaseResult> result;
1738  rv = query->GetResultObject(getter_AddRefs(result));
1739  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1740 
1741  nsAutoString propertyDBIDStr;
1742  rv = result->GetRowCell(0, 0, propertyDBIDStr);
1743  NS_ENSURE_SUCCESS(rv, rv);
1744 
1745  PRUint32 propertyDBID = propertyDBIDStr.ToInteger(&rv);
1746  NS_ENSURE_SUCCESS(rv, rv);
1747 
1748  *aPropertyDBID = propertyDBID;
1749 
1750  mPropertyDBIDToID.Put(propertyDBID, nsAutoString(aPropertyID));
1751  mPropertyIDToDBID.Put(nsAutoString(aPropertyID), propertyDBID);
1752 
1753  return NS_OK;
1754 }
1755 
1756 nsresult
1759  PRUint32 aPropertyDBID,
1760  nsAString& _retval)
1761 {
1762  NS_ENSURE_ARG_POINTER(aBag);
1763  nsresult rv = NS_OK;
1764  _retval.Truncate();
1765 
1766  // Get the secondary sort properties for the given property
1767  nsString id;
1768  rv = GetPropertyID(aPropertyDBID, id);
1769  NS_ENSURE_SUCCESS(rv, rv);
1770 
1771  nsCOMPtr<sbIPropertyInfo> propertyInfo;
1772  rv = mPropertyManager->GetPropertyInfo(id,
1773  getter_AddRefs(propertyInfo));
1774  NS_ENSURE_SUCCESS(rv, rv);
1775 
1776  nsCOMPtr<sbIPropertyArray> secondaryProps;
1777  rv = propertyInfo->GetSecondarySort(getter_AddRefs(secondaryProps));
1778  NS_ENSURE_SUCCESS(rv, rv);
1779 
1780  // If we found secondary properties, get the sort value for
1781  // each and concat them together
1782  if (secondaryProps) {
1783  PRUint32 secondaryPropCount;
1784  rv = secondaryProps->GetLength(&secondaryPropCount);
1785  NS_ENSURE_SUCCESS(rv, rv);
1786 
1787  nsTArray<nsString> strings(secondaryPropCount);
1788 
1789  for (PRUint32 i = 0; i < secondaryPropCount; i++) {
1790  nsCOMPtr<sbIProperty> property;
1791  rv = secondaryProps->GetPropertyAt(i, getter_AddRefs(property));
1792  NS_ENSURE_SUCCESS(rv, rv);
1793 
1794  nsString propertyID;
1795  rv = property->GetId(propertyID);
1796  NS_ENSURE_SUCCESS(rv, rv);
1797 
1798  nsString sortable;
1799  rv = aBag->GetSortablePropertyByID(GetPropertyDBIDInternal(propertyID),
1800  sortable);
1801  NS_ENSURE_SUCCESS(rv, rv);
1802 
1803  nsString* appended = strings.AppendElement(sortable);
1804  NS_ENSURE_TRUE(appended, NS_ERROR_OUT_OF_MEMORY);
1805  }
1806 
1807  // Use a small separator so that Foo|Bar comes before Foobar|Baz
1808  // \u001f is INFO SEPARATOR 1
1809  nsString result;
1810  sbAppendStringArray(result, NS_LITERAL_STRING("\x1f"), strings);
1811  _retval = result;
1812  }
1813 
1814  return NS_OK;
1815 }
1816 
1817 
1818 
1820  nsIClassInfo,
1822  nsIRunnable,
1824  nsIObserver);
1825 
1827  nsIClassInfo,
1829  nsIRunnable,
1831  nsIObserver)
1832 
1835 
1837  mShouldShutdown(PR_FALSE),
1838  mThread(nsnull),
1839  mLibrary(nsnull),
1840  mPropCache(nsnull),
1841  mNotificationTimer(nsnull),
1842  mStatus(sbIJobProgress::STATUS_RUNNING),
1843  mCompletedItemCount(0),
1844  mTotalItemCount(0)
1845 {
1846  MOZ_COUNT_CTOR(sbLocalDatabaseSortInvalidateJob);
1847 }
1848 
1850 {
1851  MOZ_COUNT_DTOR(sbLocalDatabaseSortInvalidateJob);
1852  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Destroyed",
1853  this);
1854  Shutdown();
1855 }
1856 
1858  sbLocalDatabasePropertyCache* aPropCache,
1859  sbLocalDatabaseLibrary* aLibrary)
1860 {
1861  NS_ENSURE_ARG_POINTER(aPropCache);
1862  NS_ENSURE_ARG_POINTER(aLibrary);
1863  NS_ENSURE_TRUE(!mThread, NS_ERROR_ALREADY_INITIALIZED);
1864  NS_ASSERTION(NS_IsMainThread(),
1865  "sbLocalDatabaseSortInvalidateJob::Init called off the main thread!");
1866  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Initialized",
1867  this);
1868  nsresult rv;
1869 
1870  mPropCache = aPropCache;
1871  mLibrary = aLibrary;
1872 
1873  // Set up job progress params
1874  mLibrary->GetLength(&mTotalItemCount);
1875  mCompletedItemCount = 0;
1876 
1877  // Localize some strings
1879  mTitleText = strings.Get("propertycache.invalidatesortjob.title",
1880  "Updating Library");
1881  mStatusText = strings.Get("propertycache.invalidatesortjob.status",
1882  "Rebuilding library sorting data");
1883  mFailedText = strings.Get("propertycache.invalidatesortjob.failed",
1884  "Failed!");
1885 
1886  // Start a timer to send job progress notifications
1887  if (!mNotificationTimer) {
1888  mNotificationTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
1889  NS_ENSURE_SUCCESS(rv, rv);
1890  }
1891  rv = mNotificationTimer->Init(this,
1893  nsITimer::TYPE_REPEATING_SLACK);
1894  NS_ENSURE_SUCCESS(rv, rv);
1895 
1896  // Start up processing thread
1897  rv = NS_NewThread(getter_AddRefs(mThread), this);
1898  NS_ENSURE_SUCCESS(rv, rv);
1899 
1900  return NS_OK;
1901 }
1902 
1904 {
1905  NS_ASSERTION(NS_IsMainThread(),
1906  "sbLocalDatabaseSortInvalidateJob::Shutdown called off the main thread!");
1907  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Shutdown requested",
1908  this);
1909  nsresult rv;
1910 
1911  mShouldShutdown = PR_TRUE;
1912 
1913  // We're done. No more notifications needed.
1914  mListeners.Clear();
1915 
1916  if (mNotificationTimer) {
1917  rv = mNotificationTimer->Cancel();
1918  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to cancel a notification timer");
1919  mNotificationTimer = nsnull;
1920  }
1921 
1922  // Wait for the thread to shutdown
1923  if (mThread) {
1924  mThread->Shutdown();
1925  mThread = nsnull;
1926  }
1927 
1928  return NS_OK;
1929 }
1930 
1934 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::Run()
1935 {
1936  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Thread Starting",
1937  this);
1938  nsresult rv;
1939 
1940  nsCOMPtr<sbIMediaListBatchCallback> batchCallback =
1941  new sbMediaListBatchCallback(&sbLocalDatabaseSortInvalidateJob::RunLibraryBatch);
1942  NS_ENSURE_TRUE(batchCallback, NS_ERROR_OUT_OF_MEMORY);
1943 
1944  rv = mLibrary->RunInBatchMode(
1945  batchCallback, static_cast<sbIJobProgress*>(this));
1946  if (NS_FAILED(rv)) {
1948  }
1949 
1950  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Thread Finished", this);
1951  return NS_OK;
1952 }
1953 
1954 /* static */
1955 nsresult
1956 sbLocalDatabaseSortInvalidateJob::RunLibraryBatch(nsISupports* aUserData)
1957 {
1958  TRACE("sbLocalDatabaseSortInvalidateJob::RunLibraryBatch[0x%.8x]");
1959  NS_ENSURE_ARG_POINTER(aUserData);
1961  static_cast<sbLocalDatabaseSortInvalidateJob*>(
1962  static_cast<sbIJobProgress*>(aUserData));
1963  NS_ENSURE_TRUE(thisJob->mPropCache, NS_ERROR_UNEXPECTED);
1964 
1965  // Begin processing every item in the library
1966  nsresult rv = thisJob->mLibrary->EnumerateAllItems(thisJob,
1968  if (NS_FAILED(rv)) {
1969  thisJob->mStatus = sbIJobProgress::STATUS_FAILED;
1970  }
1971 
1972  return NS_OK;
1973 }
1974 
1975 // ======================================================
1976 // == sbIMediaListEnumerationListener Implementation ==
1977 // ======================================================
1978 
1979 /* unsigned short onEnumerationBegin (in sbIMediaList aMediaList); */
1980 NS_IMETHODIMP
1981 sbLocalDatabaseSortInvalidateJob::OnEnumerationBegin(sbIMediaList *aMediaList,
1982  PRUint16 *_retval)
1983 {
1984  NS_ENSURE_ARG_POINTER(aMediaList);
1985  NS_ENSURE_ARG_POINTER(_retval);
1987  return NS_OK;
1988 }
1989 
1990 /* unsigned short onEnumeratedItem (in sbIMediaList aMediaList, in sbIMediaItem aMediaItem); */
1991 NS_IMETHODIMP
1992 sbLocalDatabaseSortInvalidateJob::OnEnumeratedItem(sbIMediaList *aMediaList,
1993  sbIMediaItem *aMediaItem,
1994  PRUint16 *_retval)
1995 {
1996  NS_ENSURE_ARG_POINTER(aMediaList);
1997  NS_ENSURE_ARG_POINTER(aMediaItem);
1998  NS_ENSURE_ARG_POINTER(_retval);
1999  nsresult rv = NS_OK;
2000 
2001  // On error, keep enumerating
2003 
2004 
2005  // Loop through all the properties for the current item,
2006  // and set back all non top-level properties.
2007  // This will force the sortable and secondary sortable
2008  // info to be recomputed.
2009 
2010  nsCOMPtr<sbIPropertyArray> properties;
2011  rv = aMediaItem->GetProperties(nsnull, getter_AddRefs(properties));
2012  NS_ENSURE_SUCCESS(rv, NS_OK);
2013 
2014  nsCOMPtr<sbIMutablePropertyArray> newPropertyArray =
2015  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
2016  NS_ENSURE_SUCCESS(rv, rv);
2017  rv = newPropertyArray->SetStrict(PR_FALSE);
2018  NS_ENSURE_SUCCESS(rv, NS_OK);
2019 
2020  PRUint32 propCount;
2021  rv = properties->GetLength(&propCount);
2022  NS_ENSURE_SUCCESS(rv, NS_OK);
2023 
2024  for (PRUint32 i = 0; i < propCount; i++) {
2025  nsCOMPtr<sbIProperty> property;
2026  rv = properties->GetPropertyAt(i, getter_AddRefs(property));
2027  NS_ENSURE_SUCCESS(rv, NS_OK);
2028 
2029  nsString propertyID;
2030  rv = property->GetId(propertyID);
2031  NS_ENSURE_SUCCESS(rv, NS_OK);
2032 
2033  if (!SB_IsTopLevelProperty(propertyID)) {
2034  nsString propertyValue;
2035  rv = property->GetValue(propertyValue);
2036  NS_ENSURE_SUCCESS(rv, NS_OK);
2037 
2038  rv = newPropertyArray->AppendProperty(propertyID, propertyValue);
2039  NS_ENSURE_SUCCESS(rv, NS_OK);
2040  }
2041  }
2042 
2043  rv = aMediaItem->SetProperties(newPropertyArray);
2044  NS_ENSURE_SUCCESS(rv, NS_OK);
2045 
2046  mCompletedItemCount++;
2047 
2048  if (mShouldShutdown) {
2051  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Thread saw shutdown request",
2052  this);
2053  }
2054 
2055  return NS_OK;
2056 }
2057 
2058 /* void onEnumerationEnd (in sbIMediaList aMediaList, in nsresult aStatusCode); */
2059 NS_IMETHODIMP
2060 sbLocalDatabaseSortInvalidateJob::OnEnumerationEnd(sbIMediaList *aMediaList,
2061  nsresult aStatusCode)
2062 {
2063  NS_ENSURE_ARG_POINTER(aMediaList);
2064  if (mCompletedItemCount == mTotalItemCount) {
2066  } else {
2068  }
2069 
2070  mShouldShutdown = PR_TRUE;
2071 
2072  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Finished enumerating",
2073  this);
2074 
2075  return NS_OK;
2076 }
2077 
2078 // =====================================
2079 // == sbIJobProgress Implementation ==
2080 // =====================================
2081 
2082 /* readonly attribute unsigned short status; */
2083 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetStatus(PRUint16* aStatus)
2084 {
2085  NS_ENSURE_ARG_POINTER( aStatus );
2086  *aStatus = mStatus;
2087  return NS_OK;
2088 }
2089 
2090 /* readonly attribute boolean blocked; */
2091 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetBlocked(PRBool* aBlocked)
2092 {
2093  NS_ENSURE_ARG_POINTER( aBlocked );
2094  *aBlocked = PR_FALSE;
2095  return NS_OK;
2096 }
2097 
2098 /* readonly attribute unsigned AString statusText; */
2099 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetStatusText(nsAString& aText)
2100 {
2101  NS_ASSERTION(NS_IsMainThread(), \
2102  "sbLocalDatabaseSortInvalidateJob::GetStatusText is main thread only!");
2103  nsresult rv = NS_OK;
2104 
2105  if (mStatus == sbIJobProgress::STATUS_FAILED) {
2106  aText = mFailedText;
2107  } else {
2108  aText = mStatusText;
2109  }
2110 
2111  return rv;
2112 }
2113 
2114 /* readonly attribute AString titleText; */
2115 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetTitleText(nsAString& aText)
2116 {
2117  aText = mTitleText;
2118  return NS_OK;
2119 }
2120 
2121 /* readonly attribute unsigned long progress; */
2122 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetProgress(PRUint32* aProgress)
2123 {
2124  NS_ENSURE_ARG_POINTER( aProgress );
2125  NS_ASSERTION(NS_IsMainThread(), \
2126  "sbLocalDatabaseSortInvalidateJob::GetProgress is main thread only!");
2127 
2128  *aProgress = mCompletedItemCount;
2129  return NS_OK;
2130 }
2131 
2132 /* readonly attribute unsigned long total; */
2133 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetTotal(PRUint32* aTotal)
2134 {
2135  NS_ENSURE_ARG_POINTER( aTotal );
2136  NS_ASSERTION(NS_IsMainThread(), \
2137  "sbLocalDatabaseSortInvalidateJob::GetTotal is main thread only!");
2138 
2139  *aTotal = mTotalItemCount;
2140  return NS_OK;
2141 }
2142 
2143 /* readonly attribute unsigned long errorCount; */
2144 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetErrorCount(PRUint32* aCount)
2145 {
2146  NS_ENSURE_ARG_POINTER( aCount );
2147  NS_ASSERTION(NS_IsMainThread(), \
2148  "sbLocalDatabaseSortInvalidateJob::GetErrorCount is main thread only!");
2149  // We don't provide additional info
2150  *aCount = 0;
2151  return NS_OK;
2152 }
2153 
2154 /* nsIStringEnumerator getErrorMessages(); */
2155 NS_IMETHODIMP sbLocalDatabaseSortInvalidateJob::GetErrorMessages(nsIStringEnumerator** aMessages)
2156 {
2157  NS_ENSURE_ARG_POINTER(aMessages);
2158  NS_ASSERTION(NS_IsMainThread(), \
2159  "sbLocalDatabaseSortInvalidateJob::GetErrorMessages is main thread only!");
2160 
2161  // No error messages, so just give back an empty enumerator
2162  *aMessages = nsnull;
2163  nsTArray<nsString> empty;
2164  nsCOMPtr<nsIStringEnumerator> enumerator =
2165  new sbTArrayStringEnumerator(&empty);
2166  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
2167 
2168  enumerator.forget(aMessages);
2169 
2170  return NS_OK;
2171 }
2172 
2173 /* void addJobProgressListener( in sbIJobProgressListener aListener ); */
2174 NS_IMETHODIMP
2175 sbLocalDatabaseSortInvalidateJob::AddJobProgressListener(sbIJobProgressListener *aListener)
2176 {
2177  NS_ENSURE_ARG_POINTER(aListener);
2178  NS_ASSERTION(NS_IsMainThread(), \
2179  "sbLocalDatabaseSortInvalidateJob::AddJobProgressListener is main thread only!");
2180  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Listener added",
2181  this);
2182 
2183  PRInt32 index = mListeners.IndexOf(aListener);
2184  if (index >= 0) {
2185  // the listener already exists, do not re-add
2186  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
2187  }
2188  PRBool succeeded = mListeners.AppendObject(aListener);
2189  return succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
2190 }
2191 
2192 /* void removeJobProgressListener( in sbIJobProgressListener aListener ); */
2193 NS_IMETHODIMP
2194 sbLocalDatabaseSortInvalidateJob::RemoveJobProgressListener(sbIJobProgressListener* aListener)
2195 {
2196  NS_ENSURE_ARG_POINTER(aListener);
2197  NS_ASSERTION(NS_IsMainThread(), \
2198  "sbLocalDatabaseSortInvalidateJob::RemoveJobProgressListener is main thread only!");
2199  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Listener removed",
2200  this);
2201 
2202  PRInt32 indexToRemove = mListeners.IndexOf(aListener);
2203  if (indexToRemove < 0) {
2204  // Err, no such listener
2205  return NS_ERROR_UNEXPECTED;
2206  }
2207 
2208  // remove the listener
2209  PRBool succeeded = mListeners.RemoveObjectAt(indexToRemove);
2210  NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
2211 
2212  return NS_OK;
2213 }
2214 
2215 // ==================================
2216 // == nsIObserver Implementation ==
2217 // ==================================
2218 
2219 // Called by mNotification Timer.
2220 NS_IMETHODIMP
2221 sbLocalDatabaseSortInvalidateJob::Observe(nsISupports *aSubject,
2222  const char *aTopic,
2223  const PRUnichar *aData)
2224 {
2225  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Notification Timer Callback", this);
2226  nsresult rv;
2227 
2228  // Then announce status to the world
2229  for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i) {
2230  mListeners[i]->OnJobProgress(this);
2231  }
2232 
2233  if (mStatus != sbIJobProgress::STATUS_RUNNING) {
2234  TRACE("sbLocalDatabaseSortInvalidateJob[0x%.8x] - Finishing job...", this);
2235  Shutdown();
2236 
2237  // Write everything to disk so that the new sort data takes effect
2238  rv = mLibrary->Flush();
2239  NS_ASSERTION(NS_SUCCEEDED(rv),
2240  "sbLocalDatabaseSortInvalidateJob failed to flush library!");
2241 
2242  // Vacuum, analyze, etc.
2243  rv = mLibrary->Optimize(PR_FALSE);
2244  NS_ASSERTION(NS_SUCCEEDED(rv),
2245  "sbLocalDatabaseSortInvalidateJob failed to optimize library!");
2246 
2247 
2248  // Let the property cache know that we're done.
2249  // --------------------------------- NOTE ---------------------------------
2250  // This call may release our instance, deleting us.
2251  // It is not safe to use any member variables after this point
2252  mPropCache->InvalidateSortDataComplete();
2253  }
2254 
2255  return NS_OK;
2256 }
2257 
static const int MediaItemBindCount
NS_IMPL_CI_INTERFACE_GETTER5(sbLocalDatabaseSortInvalidateJob, nsIClassInfo, sbIJobProgress, nsIRunnable, sbIMediaListEnumerationListener, nsIObserver) sbLocalDatabaseSortInvalidateJob
#define SB_PRLOG_SETUP(x)
Definition: sbDebugUtils.h:115
return NS_OK
EnumDirtyItemsSetDirty(nsAString const &aKey, sbLocalDatabaseResourcePropertyBag *aBag, void *aClosure)
EnumDirtyItems(nsAString const &aKey, sbLocalDatabaseResourcePropertyBag *aBag, void *aClosure)
static nsString PropertiesDelete()
#define SONGBIRD_DATABASEQUERY_CONTRACTID
Definition: DatabaseQuery.h:63
PRUint32 GetGUIDCount(T const &aGUIDs, PRInt32 aLibraryItemPosition)
static nsCOMPtr< nsIObserverService > observerService
Definition: UnityProxy.cpp:6
S sbAppendStringArray(S &aTarget, Sep const &aSeparator, T const &aStringArray, E aExtractor)
static nsString MediaItemsFtsAllDelete()
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
#define SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC
#define LOG(args)
PRUnicharAdaptor(PRUnichar const **aCharArray, PRUint32 aLength)
#define CACHE_HASHTABLE_SIZE
Interface used to enumerate the items in a media list.
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
#define NS_FINAL_UI_STARTUP_OBSERVER_ID
PR_STATIC_CALLBACK(PRBool) FindElementCallback(void *aElement
NS_IMPL_THREADSAFE_ISUPPORTS5(sbLocalDatabaseSortInvalidateJob, nsIClassInfo, sbIJobProgress, nsIRunnable, sbIMediaListEnumerationListener, nsIObserver)
function succeeded(ch, cx, status, data)
PRUint64 nsString_ToUint64(const nsAString &str, nsresult *rv)
static nsString MediaItemsFtsAllInsert()
var pref
Definition: openLocation.js:44
Generic interface for exposing long running jobs to the UI.
static nsString PropertiesTableInsert()
nsString Get(const nsAString &aKey, const nsAString &aDefault=SBVoidString())
NS_IMETHOD RunInBatchMode(sbIMediaListBatchCallback *aCallback, nsISupports *aUserData)
static const int SecondaryPropertyBindCount
static const PRUint32 sStaticPropertyCount
static nsresult SB_GetTopLevelPropertyColumnType(const nsAString &aProperty, PRUint32 &aColumnType)
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
static nsresult SB_GetTopLevelPropertyColumn(const nsAString &aProperty, nsAString &aColumnName)
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
DirtyPropertyEnumerator(sbLocalDatabasePropertyCache *aCache, sbLocalDatabaseResourcePropertyBag *aBag, sbIDatabaseQuery *aQuery, PRUint32 aMediaItemID, PRBool aIsLibrary)
static nsString PropertiesInsert()
const unsigned short STATUS_SUCCEEDED
Constant indicating that the job has completed.
static PRBool SB_IsTopLevelProperty(PRUint32 aPropertyDBID)
A brief description of the contents of this interface.
Simple class to make sure we notify listeners that a batch operation has completed every time they ar...
static nsString LibraryMediaItemsPropertiesSelect()
#define SB_PROPERTYMANAGER_CONTRACTID
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
var strings
Definition: Info.js:46
#define SB_THREADPOOLSERVICE_CONTRACTID
PRBool GetPropertyID(PRUint32 aPropertyDBID, nsAString &aPropertyID)
nsresult Init(sbLocalDatabaseLibrary *aLibrary, const nsAString &aLibraryResourceGUID)
var count
Definition: test_bug7406.js:32
NS_IMPL_THREADSAFE_ISUPPORTS2(sbLocalDatabasePropertyCache, sbILocalDatabasePropertyCache, nsIObserver) sbLocalDatabasePropertyCache
const PRUint32 SB_COLUMN_TYPE_TEXT
PRInt32 IndexOf(nsAString const &aItemToFind) const
Songbird Thread Pool Service.
function TRACE(s)
static nsString PropertiesSelect()
#define NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID
void * aClosure
Definition: sbArray.cpp:52
nsTArray< nsString > mGUIDs
EnumDirtyProps(nsUint32HashKey *aKey, void *aClosure)
#define SB_PROPERTY_GUID
const PRUint32 SB_COLUMN_TYPE_INTEGER
NS_IMETHOD GetLength(PRUint32 *aLength)
nsresult Process(PRUint32 aDirtyPropertyKey)
nsString operator[](PRUint32 aIndex) const
nsTArray< PRUint32 > mIDs
Songbird String Bundle Definitions.
#define SORTINVALIDATE_TIMER_PERIOD
Number of milliseconds between sbIJobProgress notifications for sbLocalDatabaseSortInvalidateJob.
nsresult Init(sbLocalDatabasePropertyCache *aPropCache, sbLocalDatabaseLibrary *aLibrary)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
inst dpDiv empty().append(this._generateHTML(inst)).find('iframe.ui-datepicker-cover').css(
static nsString SecondaryPropertySelect()
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
PRBool RemoveElement(nsAString const &aItemToRemove)
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
#define SB_LOCALDATABASE_CACHE_FLUSH_DELAY
Number of milliseconds after the last write to force a cache write.
NS_IMETHOD EnumerateAllItems(sbIMediaListEnumerationListener *aEnumerationListener, PRUint16 aEnumerationType)
An object responsible for executing SQL queries on the database.
Implemented to receive notifications from sbIJobProgress interfaces.
static sbStaticProperty sStaticProperties[]
Interface that defines a single item of media in the system.
restoreHistoryPrecursor aCount
nsresult SetProperties(nsIArray *aProperties)
const unsigned short STATUS_FAILED
Constant indicating that the job has completed with errors.
nsresult CreateSecondarySortValue(sbILocalDatabaseResourcePropertyBag *aBag, PRUint32 aPropertyDBID, nsAString &_retval)
_getSelectedPageStyle s i
nsresult PutValue(PRUint32 aPropertyID, const nsAString &aValue)
#define SB_UNUSED_IN_RELEASE(decl)
Definition: sbDebugUtils.h:55
const SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC
const unsigned short ENUMERATIONTYPE_LOCKING
This flag means that the list is protected from changes by other threads during the enumeration...
_updateTextAndScrollDataForFrame aData
nsresult GetDirtyForInvalidation(std::set< PRUint32 > &aDirty)
Songbird Database Object Definition.