sbFileMetadataService.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 
30 // INCLUDES ===================================================================
31 #include <nspr.h>
32 #include <nscore.h>
33 #include <nsComponentManagerUtils.h>
34 #include <nsServiceManagerUtils.h>
35 #include <nsIObserverService.h>
36 #include <nsArrayUtils.h>
37 #include <nsIPrefBranch.h>
38 #include <nsIPromptService.h>
39 #include <nsIWindowMediator.h>
40 #include <nsIDOMWindow.h>
41 #include <nsIDOMWindowInternal.h>
42 #include <nsThreadUtils.h>
43 #include <prlog.h>
44 
45 #include <sbILibraryManager.h>
46 #include <sbIMediacoreSequencer.h>
47 #include <sbIMediacoreStatus.h>
48 #include <sbIMediaItem.h>
50 #include <sbStandardProperties.h>
51 #include <sbIDataRemote.h>
52 
53 #include "sbFileMetadataService.h"
54 #include "sbMetadataJobItem.h"
57 #include "sbMetadataCrashTracker.h"
58 
59 
60 // DEFINES ====================================================================
61 #include "prlog.h"
62 #ifdef PR_LOGGING
63 extern PRLogModuleInfo* gMetadataLog;
64 #define TRACE(args) PR_LOG(gMetadataLog, PR_LOG_DEBUG, args)
65 #define LOG(args) PR_LOG(gMetadataLog, PR_LOG_WARN, args)
66 #ifdef __GNUC__
67 #define __FUNCTION__ __PRETTY_FUNCTION__
68 #endif
69 #else
70 #define TRACE(args) /* nothing */
71 #define LOG(args) /* nothing */
72 #endif
73 
74 // Controls how often we send sbIJobProgress notifications
75 #define TIMER_PERIOD 33
76 
77 // GLOBALS ====================================================================
78 
79 // CLASSES ====================================================================
84 
86  mMainThreadProcessor(nsnull),
87  mInitialized(PR_FALSE),
88  mRunning(PR_FALSE),
89  mNotificationTimer(nsnull),
90  mNextJobIndex(0),
91  mCrashTracker(nsnull)
92 {
93  MOZ_COUNT_CTOR(sbFileMetadataService);
94  TRACE(("%s[%.8x]", __FUNCTION__, this));
95 }
96 
98 {
99  MOZ_COUNT_DTOR(sbFileMetadataService);
100  TRACE(("%s[%.8x]", __FUNCTION__, this));
101 
102  if (mJobLock) {
103  nsAutoLock::DestroyLock(mJobLock);
104  }
105 }
106 
108 {
109  TRACE(("%s[%.8x]", __FUNCTION__, this));
110  nsresult rv;
111 
113  // WARNING: Init may be called off of the main thread. //
115 
116  mJobLock = nsAutoLock::NewLock(
117  "sbFileMetadataService job items lock");
118  NS_ENSURE_TRUE(mJobLock, NS_ERROR_OUT_OF_MEMORY);
119 
120  // Get the mediacore manager.
122  NS_ENSURE_SUCCESS(rv, rv);
123 
124  // Listen for library shutdown. The observer service must be used on the main
125  // thread.
126  nsCOMPtr<nsIObserverService> obsSvc;
127  if (NS_IsMainThread()) {
128  obsSvc = do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
129  }
130  else {
131  obsSvc = do_ProxiedGetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
132  }
133  NS_ENSURE_SUCCESS(rv, rv);
134  nsCOMPtr<nsIObserver> observer =
135  do_QueryInterface(NS_ISUPPORTS_CAST(nsIObserver*, this), &rv);
136  NS_ENSURE_SUCCESS(rv, rv);
137  rv = obsSvc->AddObserver(observer, SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC, PR_FALSE);
138  NS_ENSURE_SUCCESS(rv, rv);
139 
140  mInitialized = PR_TRUE;
141  return rv;
142 }
143 
144 
146 {
147  TRACE(("%s[%.8x]", __FUNCTION__, this));
148  NS_ASSERTION(NS_IsMainThread(),
149  "\n\n\nsbFileMetadataService::Shutdown IS MAIN THREAD ONLY!!!!111\n\n\n");
150  nsresult rv;
151 
152  if (mMainThreadProcessor) {
153  rv = mMainThreadProcessor->Stop();
154  NS_ASSERTION(NS_SUCCEEDED(rv),
155  "Failed to stop main thread metadata processor");
156  mMainThreadProcessor = nsnull;
157  }
158 
159  // Must not lock mJobLock before calling stop, as
160  // the background thread may be in the middle of something
161  // that will involve mJobLock
163  rv = mBackgroundThreadProcessor->Stop();
164  NS_ASSERTION(NS_SUCCEEDED(rv),
165  "Failed to stop background thread metadata processor");
167  }
168 
169  nsAutoLock lock(mJobLock);
170 
171  if (mNotificationTimer) {
172  rv = mNotificationTimer->Cancel();
173  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to cancel a notification timer");
174  mNotificationTimer = nsnull;
175  }
176 
177  mRunning = PR_FALSE;
178  mInitialized = PR_FALSE;
179 
180  // Cancel the jobs. No risk of additional jobs as mJobArray is locked.
181  for (PRInt32 i = mJobArray.Length() - 1; i >= 0; --i) {
182  rv = mJobArray[i]->Cancel();
183  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to cancel a metadata job");
184  mJobArray.RemoveElementAt(i);
185  }
186  NS_ASSERTION(0 == mJobArray.Length(),
187  "Metadata jobs remaining after stopping the manager");
188 
189  // Update the legacy flags that show up in the status bar
190  UpdateDataRemotes(mJobArray.Length());
191 
192  // Shutdown the crash tracker
193  if (mCrashTracker) {
194  rv = mCrashTracker->ResetLog();
195  NS_ASSERTION(NS_SUCCEEDED(rv),
196  "sbFileMetadataService::Shutdown failed to stop crash tracker");
197  mCrashTracker = nsnull;
198  }
199 
200  return NS_OK;
201 }
202 
203 NS_IMETHODIMP
204 sbFileMetadataService::Read(nsIArray* aMediaItemsArray,
205  sbIJobProgress** _retval)
206 {
207  TRACE(("%s[%.8x]", __FUNCTION__, this));
208  return ProxiedStartJob(aMediaItemsArray,
209  nsnull,
211  _retval);
212 }
213 
214 NS_IMETHODIMP
215 sbFileMetadataService::Write(nsIArray* aMediaItemsArray,
216  nsIStringEnumerator* aRequiredProperties,
217  sbIJobProgress** _retval)
218 {
219  TRACE(("%s[%.8x]", __FUNCTION__, this));
220  return ProxiedStartJob(aMediaItemsArray,
221  aRequiredProperties,
223  _retval);
224 }
225 
226 NS_IMETHODIMP
227 sbFileMetadataService::RestartProcessors(PRUint16 aProcessorsToRestart)
228 {
229  TRACE(("%s[%.8x]", __FUNCTION__, this));
230 
231  nsresult rv = ProxiedRestartProcessors(aProcessorsToRestart);
232  NS_ENSURE_SUCCESS(rv, rv);
233 
234  return NS_OK;
235 }
236 
237 nsresult
239 {
240  TRACE(("%s[%.8x]", __FUNCTION__, this));
241  nsresult rv = NS_OK;
242 
243  if (!NS_IsMainThread()) {
244  LOG(("%s[%.8x] proxying main thread RestartProcessors()", __FUNCTION__, this));
245  nsCOMPtr<nsIThread> target;
246  rv = NS_GetMainThread(getter_AddRefs(target));
247  NS_ENSURE_SUCCESS(rv, rv);
248 
249  nsCOMPtr<sbIFileMetadataService> proxy;
250  rv = do_GetProxyForObject(target,
251  NS_GET_IID(sbIFileMetadataService),
252  static_cast<sbIFileMetadataService*>(this),
253  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
254  getter_AddRefs(proxy));
255  NS_ENSURE_SUCCESS(rv, rv);
256  // Can't call ProxiedRestartProcessors via proxy, since it is not
257  // an interface method.
258  rv = proxy->RestartProcessors(aProcessorsToRestart);
259  NS_ENSURE_SUCCESS(rv, rv);
260  }
261  else {
262  NS_ENSURE_STATE(mMainThreadProcessor);
263  NS_ENSURE_STATE(mBackgroundThreadProcessor);
264 
265  if (aProcessorsToRestart & sbIFileMetadataService::MAIN_THREAD_PROCESSOR) {
266  rv = mMainThreadProcessor->Start();
267  NS_ENSURE_SUCCESS(rv, rv);
268  }
269 
270  if (aProcessorsToRestart & sbIFileMetadataService::BACKGROUND_THREAD_PROCESSOR) {
271  nsCOMPtr<nsIRunnable> event =
272  NS_NEW_RUNNABLE_METHOD(sbBackgroundThreadMetadataProcessor,
274  Start);
275  NS_DispatchToCurrentThread(event);
276  }
277  }
278 
279  return NS_OK;
280 }
281 
282 nsresult
283 sbFileMetadataService::ProxiedStartJob(nsIArray* aMediaItemsArray,
284  nsIStringEnumerator* aRequiredProperties,
285  sbMetadataJob::JobType aJobType,
286  sbIJobProgress** _retval)
287 {
288  TRACE(("%s[%.8x]", __FUNCTION__, this));
289  nsresult rv = NS_OK;
290 
291  // Make sure StartJob is called on the main thread
292  if (!NS_IsMainThread()) {
293  LOG(("%s[%.8x] proxying main thread StartJob()", __FUNCTION__, this));
294  nsCOMPtr<nsIThread> target;
295  rv = NS_GetMainThread(getter_AddRefs(target));
296  NS_ENSURE_SUCCESS(rv, rv);
297 
298  nsCOMPtr<sbIFileMetadataService> proxy;
299  rv = do_GetProxyForObject(target,
300  NS_GET_IID(sbIFileMetadataService),
301  static_cast<sbIFileMetadataService*>(this),
302  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
303  getter_AddRefs(proxy));
304  NS_ENSURE_SUCCESS(rv, rv);
305  // Can't call StartJob via proxy, since it is not
306  // an interface method.
307  if (aJobType == sbMetadataJob::TYPE_WRITE) {
308  rv = proxy->Write(aMediaItemsArray, aRequiredProperties, _retval);
309  } else {
310  rv = proxy->Read(aMediaItemsArray, _retval);
311  }
312  } else {
313  rv = StartJob(aMediaItemsArray, aRequiredProperties, aJobType, _retval);
314  }
315  return rv;
316 }
317 
318 
319 nsresult
320 sbFileMetadataService::StartJob(nsIArray* aMediaItemsArray,
321  nsIStringEnumerator* aRequiredProperties,
322  sbMetadataJob::JobType aJobType,
323  sbIJobProgress** _retval)
324 {
325  TRACE(("%s[%.8x]", __FUNCTION__, this));
326  NS_ENSURE_ARG_POINTER(aMediaItemsArray);
327  NS_ENSURE_ARG_POINTER(_retval);
328  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
329 
330  NS_ASSERTION(NS_IsMainThread(),
331  "\n\n\nsbFileMetadataService::StartJob IS MAIN THREAD ONLY!!!!!!!\n\n\n");
332 
333  nsresult rv = NS_OK;
334 
335  // Do not allow write jobs unless the user has specifically given
336  // permission.
337  if (aJobType == sbMetadataJob::TYPE_WRITE) {
338  rv = EnsureWritePermitted();
339  NS_ENSURE_SUCCESS(rv, rv);
340  }
341 
342  nsRefPtr<sbMetadataJob> job = new sbMetadataJob();
343  NS_ENSURE_TRUE(job, NS_ERROR_OUT_OF_MEMORY);
344 
345  rv = job->Init(aMediaItemsArray, aRequiredProperties, aJobType);
346  NS_ENSURE_SUCCESS(rv, rv);
347 
348  {
349  nsAutoLock lock(mJobLock);
350  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
351 
352  // If the last job in the job array is blocked, the new job will be too.
353  PRUint32 jobCount = mJobArray.Length();
354  if (jobCount > 0) {
355  PRBool blocked;
356  rv = mJobArray[jobCount-1]->GetBlocked(&blocked);
357  NS_ENSURE_SUCCESS(rv, rv);
358  if (blocked) {
359  rv = job->SetBlocked(PR_TRUE);
360  NS_ENSURE_SUCCESS(rv, rv);
361  }
362  }
363 
364  mJobArray.AppendElement(job);
365 
366  // Update the legacy flags that show up in the status bar
367  UpdateDataRemotes(mJobArray.Length());
368  }
369 
370  if (!mRunning) {
371  // Start a timer to send job progress notifications
372  if (!mNotificationTimer) {
373  mNotificationTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
374  NS_ENSURE_SUCCESS(rv, rv);
375  }
376  rv = mNotificationTimer->Init(this,
377  TIMER_PERIOD,
378  nsITimer::TYPE_REPEATING_SLACK);
379  NS_ENSURE_SUCCESS(rv, rv);
380 
381  if (!mCrashTracker) {
383  NS_ENSURE_TRUE(mCrashTracker, NS_ERROR_OUT_OF_MEMORY);
384  rv = mCrashTracker->Init();
385  if (NS_FAILED(rv)) {
386  NS_ERROR("sbFileMetadataService::StartJob failed to "
387  "start crash tracker");
388  rv = NS_OK;
389  }
390  }
391 
392  mRunning = PR_TRUE;
393  }
394 
395  // TODO Not necessary, but could only start processors when needed.
396 
397  // Start up the main thread (timer driven) metadata processor
398  if (!mMainThreadProcessor) {
400  }
401  NS_ENSURE_TRUE(mMainThreadProcessor, NS_ERROR_OUT_OF_MEMORY);
402  rv = mMainThreadProcessor->Start();
403  NS_ENSURE_SUCCESS(rv, rv);
404 
405  // Start background thread metadata processor
406  // (will continue if already started)
409  }
410  NS_ENSURE_TRUE(mBackgroundThreadProcessor, NS_ERROR_OUT_OF_MEMORY);
411  rv = mBackgroundThreadProcessor->Start();
412  NS_ENSURE_SUCCESS(rv, rv);
413 
414  rv = CallQueryInterface(job.get(), _retval);
415  NS_ENSURE_SUCCESS(rv, rv);
416 
417  return NS_OK;
418 }
419 
420 
421 nsresult sbFileMetadataService::GetQueuedJobItem(PRBool aMainThreadOnly,
422  sbMetadataJobItem** aJobItem)
423 {
424  TRACE(("%s[%.8x]", __FUNCTION__, this));
425  NS_ENSURE_ARG_POINTER(aJobItem);
426  nsresult rv = NS_OK;
427 
428  nsAutoLock lock(mJobLock);
429 
430  if (mJobArray.Length() > 0) {
431  nsRefPtr<sbMetadataJobItem> item;
432 
433  // Look through active jobs for a job item to be processed.
434  // Skip any files that may have cause an sbIMetadataHandler
435  // to crash in the past.
436  PRBool isURLBlacklisted;
437  do {
438  isURLBlacklisted = PR_FALSE;
439 
440  // TODO consider giving preference to the most recent job
441 
442  // Loop through all active jobs looking for one that can supply a JobItem.
443  // (Necessary because jobs may be out of items, but haven't yet
444  // been marked completed by the notification timer)
445  for (PRUint32 i=0; i < mJobArray.Length(); i++) {
446  if (mNextJobIndex >= mJobArray.Length()) {
447  mNextJobIndex = 0;
448  }
449  rv = mJobArray[mNextJobIndex++]->GetQueuedItem(aMainThreadOnly,
450  getter_AddRefs(item));
451  if (rv != NS_ERROR_NOT_AVAILABLE) {
452  break;
453  }
454  }
455 
456  // Use the crash tracker to avoid processing files
457  // that have previously crashed an sbIMetadataHandler
458  if (mCrashTracker && NS_SUCCEEDED(rv)) {
459  nsCString url;
460  rv = item->GetURL(url);
461  NS_ENSURE_SUCCESS(rv, rv);
462 
463  // Check to see if we've crashed on this file before
464  mCrashTracker->IsURLBlacklisted(url, &isURLBlacklisted);
465  if (isURLBlacklisted) {
466  // Blacklisted. Just skip this file and record it as failed.
467  LOG(("sbFileMetadataService ignored blacklisted file %s.",
468  url.BeginReading()));
469  PutProcessedJobItem(item);
470  } else {
471  // Record that this item is being started
472  TRACE(("sbFileMetadataService[9x%.8x] GetQueuedJobItem starting %s",
473  this, url.BeginReading()));
474  rv = mCrashTracker->LogURLBegin(url);
475  if (NS_FAILED(rv)) {
476  NS_ERROR("sbFileMetadataService::GetQueuedJobItem couldn't log URL");
477  }
478  }
479  }
480  } while (isURLBlacklisted);
481 
482  // If we still haven't found an item, that's fine
483  if (rv == NS_ERROR_NOT_AVAILABLE) {
484  TRACE(("sbFileMetadataService[0x%.8x] GetQueuedJobItem NOT_AVAILABLE",
485  this));
486  return rv;
487  }
488  NS_ENSURE_SUCCESS(rv, rv);
489 
490  item.forget(aJobItem);
491  return NS_OK;
492  }
493 
494  return NS_ERROR_NOT_AVAILABLE;
495 }
496 
497 
499 {
500  TRACE(("%s[%.8x]", __FUNCTION__, this));
501  NS_ENSURE_ARG_POINTER(aJobItem);
502  nsresult rv;
503 
504  // Forward results on to original job
505  nsRefPtr<sbMetadataJob> job;
506  rv = aJobItem->GetOwningJob(getter_AddRefs(job));
507  NS_ENSURE_SUCCESS(rv, rv);
508 
509  // Record that this item has been completed
510  if (mCrashTracker) {
511  nsCString url;
512  rv = aJobItem->GetURL(url);
513  NS_ENSURE_SUCCESS(rv, rv);
514  rv = mCrashTracker->LogURLEnd(url);
515  if (NS_FAILED(rv)) {
516  NS_ERROR("sbFileMetadataService::PutProcessedJobItem couldn't log URL");
517  }
518  }
519 
520  return job->PutProcessedItem(aJobItem);
521 }
522 
523 nsresult
525  PRBool* aJobItemIsBlocked)
526 {
527  NS_ENSURE_ARG_POINTER(aJobItem);
528  NS_ENSURE_ARG_POINTER(aJobItemIsBlocked);
529 
530  nsresult rv;
531 
532  // Get the job type.
533  sbMetadataJob::JobType jobType;
534  rv = aJobItem->GetJobType(&jobType);
535  NS_ENSURE_SUCCESS(rv, rv);
536 
537  // Only writing jobs can be blocked.
538  if (jobType != sbMetadataJob::TYPE_WRITE) {
539  *aJobItemIsBlocked = PR_FALSE;
540  return NS_OK;
541  }
542 
543  // Job is not blocked if the mediacore is not playing.
544  nsCOMPtr<sbIMediacoreStatus> status;
545  PRUint32 state = 0;
546  rv = mMediacoreManager->GetStatus(getter_AddRefs(status));
547  NS_ENSURE_SUCCESS(rv, rv);
548  rv = status->GetState(&state);
549  NS_ENSURE_SUCCESS(rv, rv);
550  if (state != sbIMediacoreStatus::STATUS_PLAYING) {
551  *aJobItemIsBlocked = PR_FALSE;
552  return NS_OK;
553  }
554 
555  // Get the currently playing media item.
556  nsCOMPtr<sbIMediacoreSequencer> sequencer;
557  nsCOMPtr<sbIMediaItem> sequencerCurrentItem;
558  rv = mMediacoreManager->GetSequencer(getter_AddRefs(sequencer));
559  NS_ENSURE_SUCCESS(rv, rv);
560  rv = sequencer->GetCurrentItem(getter_AddRefs(sequencerCurrentItem));
561  NS_ENSURE_SUCCESS(rv, rv);
562 
563  // Get the job media item.
564  nsCOMPtr<sbIMediaItem> jobItem;
565  rv = aJobItem->GetMediaItem(getter_AddRefs(jobItem));
566  NS_ENSURE_SUCCESS(rv, rv);
567 
568  // Job is not blocked if the job media item is not currently being played.
569  PRBool equals;
570  rv = jobItem->Equals(sequencerCurrentItem, &equals);
571  NS_ENSURE_SUCCESS(rv, rv);
572  if (!equals) {
573  *aJobItemIsBlocked = PR_FALSE;
574  return NS_OK;
575  }
576 
577  // Job is blocked.
578  *aJobItemIsBlocked = PR_TRUE;
579 
580  return NS_OK;
581 }
582 
583 
584 // nsIObserver
585 NS_IMETHODIMP
586 sbFileMetadataService::Observe(nsISupports *aSubject,
587  const char *aTopic,
588  const PRUnichar *aData)
589 {
590  TRACE(("%s[%.8x] (%s)", __FUNCTION__, this, aTopic));
591  nsresult rv;
592 
593  //
594  // Library Down
595  //
596  if (!strcmp(SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC, aTopic)) {
597 
598  rv = Shutdown();
599  NS_ENSURE_SUCCESS(rv, rv);
600 
601  // Remove all the observer callbacks
602  nsCOMPtr<nsIObserverService> obsSvc =
603  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
604  NS_ENSURE_SUCCESS(rv, rv);
605  nsCOMPtr<nsIObserver> observer =
606  do_QueryInterface(NS_ISUPPORTS_CAST(nsIObserver*, this), &rv);
607  NS_ENSURE_SUCCESS(rv, rv);
608  rv = obsSvc->RemoveObserver(observer, SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC);
609  NS_ENSURE_SUCCESS(rv, rv);
610 
611  //
612  // Notification Timer
613  //
614  } else if (!strcmp(NS_TIMER_CALLBACK_TOPIC, aTopic)) {
615  TRACE(("%s[%.8x] - Notification Timer Callback", __FUNCTION__, this));
616 
617  // Snapshot the job array so that we don't leave
618  // things locked while we call onJobProgress and
619  // potentially do a bunch of work
620  nsTArray<nsRefPtr<sbMetadataJob> > jobs;
621  {
622  nsAutoLock lock(mJobLock);
623  jobs.AppendElements(mJobArray);
624 
625  // Update blocked status of jobs. If any job is blocked, all jobs after
626  // it are also blocked.
627  PRBool blocked = PR_FALSE;
628  PRUint32 jobCount = jobs.Length();
629  for (PRUint32 i=0; i < jobCount; i++) {
630  // If no jobs are blocked yet, check current job. Otherwise, mark
631  // current job as blocked.
632  if (!blocked)
633  rv = jobs[i]->GetBlocked(&blocked);
634  else
635  rv = jobs[i]->SetBlocked(PR_TRUE);
636  }
637  }
638 
639  PRUint16 status;
640  for (PRUint32 i=0; i < jobs.Length(); i++) {
641  jobs[i]->OnJobProgress();
642  // Ignore errors
643  }
644 
645  // Now lock again and see if there are any active jobs left
646  {
647  PRBool allComplete = PR_TRUE;
648  nsAutoLock lock(mJobLock);
649  for (PRUint32 i=0; i < mJobArray.Length(); i++) {
650  mJobArray[i]->GetStatus(&status);
651  if (status == sbIJobProgress::STATUS_RUNNING) {
652  allComplete = PR_FALSE;
653  }
654  }
655  // If none of the jobs are active shut everything down
656  // This is stupid-ish. Could just remove items one by one
657  // as they complete.
658  if (allComplete) {
659  TRACE(("%s[%.8x] - Notification - All Complete", __FUNCTION__, this));
660  rv = mNotificationTimer->Cancel();
661  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to cancel a notification timer");
662  mRunning = PR_FALSE;
663 
664  // TODO Not necessary, but we could shut down the scanners here
665 
666  mJobArray.Clear();
667 
668  // Update the legacy flags that show up in the status bar
669  UpdateDataRemotes(mJobArray.Length());
670 
671  // The crash tracker can forget about everything we've just done
672  if (mCrashTracker) {
673  rv = mCrashTracker->ResetLog();
674  if (NS_FAILED(rv)) {
675  NS_ERROR("sbFileMetadataService::Shutdown failed to stop crash tracker");
676  }
677  }
678  }
679  }
680  }
681  return NS_OK;
682 }
683 
691 {
692  TRACE(("%s[%.8x]", __FUNCTION__, this));
693  nsresult rv;
694 
695  PRBool enableWriting = PR_FALSE;
696  nsCOMPtr<nsIPrefBranch> prefService =
697  do_GetService( "@mozilla.org/preferences-service;1", &rv );
698  NS_ENSURE_SUCCESS( rv, rv);
699  prefService->GetBoolPref( "songbird.metadata.enableWriting", &enableWriting );
700 
701  if (!enableWriting) {
702 
703  // Let the user know what the situation is.
704  // Allow them to enable writing if desired.
705 
706  PRBool promptOnWrite = PR_TRUE;
707  prefService->GetBoolPref( "songbird.metadata.promptOnWrite", &promptOnWrite );
708 
709  if (promptOnWrite) {
710  // Don't bother to prompt unless there is a player window open.
711  // This avoids popping a modal dialog mid unit test.
712  nsCOMPtr<nsIWindowMediator> windowMediator =
713  do_GetService("@mozilla.org/appshell/window-mediator;1", &rv);
714  NS_ENSURE_SUCCESS( rv, rv);
715  nsCOMPtr<nsIDOMWindowInternal> mainWindow;
716  windowMediator->GetMostRecentWindow(nsnull,
717  getter_AddRefs(mainWindow));
718  if (mainWindow) {
719  nsCOMPtr<nsIPromptService> promptService =
720  do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
721  NS_ENSURE_SUCCESS( rv, rv);
722 
723  PRBool promptResult = PR_FALSE;
724  PRBool checkState = PR_FALSE;
725 
726  // TODO Clean up, localize, or remove from the product
727  rv = promptService->ConfirmCheck(mainWindow,
728  NS_LITERAL_STRING("WARNING! TAG WRITING IS EXPERIMENTAL!").get(),
729  NS_MULTILINE_LITERAL_STRING(
730  NS_LL("Are you sure you want to write metadata changes")
731  NS_LL(" back to your media files?\n\nTag writing has not been tested yet,")
732  NS_LL(" and may damage your media files. If you'd like to help us test")
733  NS_LL(" this functionality, great, but we advise you to back up your media first.")
734  ).get(),
735  NS_LITERAL_STRING("Don't show this dialog again").get(),
736  &checkState,
737  &promptResult);
738  NS_ENSURE_SUCCESS( rv, rv);
739 
740  if (checkState) {
741  prefService->SetBoolPref( "songbird.metadata.promptOnWrite", PR_FALSE );
742  }
743 
744  if (promptResult) {
745  prefService->SetBoolPref( "songbird.metadata.enableWriting", PR_TRUE );
746  enableWriting = PR_TRUE;
747  }
748  }
749  }
750  }
751 
752  return (enableWriting) ? NS_OK : NS_ERROR_NOT_AVAILABLE;
753 }
754 
755 nsresult sbFileMetadataService::UpdateDataRemotes(PRInt64 aJobCount)
756 {
757  TRACE(("%s[%.8x]", __FUNCTION__, this));
758  NS_ASSERTION(NS_IsMainThread(),
759  "sbFileMetadataService::UpdateDataRemotes is main thread only!");
760  nsresult rv = NS_OK;
761 
762  // Get the legacy dataremote used for signaling metadata state
765  do_CreateInstance("@songbirdnest.com/Songbird/DataRemote;1", &rv);
766  NS_ENSURE_SUCCESS(rv, rv);
767  rv = mDataCurrentMetadataJobs->Init(NS_LITERAL_STRING("backscan.concurrent"),
768  EmptyString());
769  NS_ENSURE_SUCCESS(rv, rv);
770  }
771 
772  return mDataCurrentMetadataJobs->SetIntValue(aJobCount);;
773 }
774 
775 /* void sbPIFileMetadataService::AddBlacklistURL (in ACString aURL); */
776 NS_IMETHODIMP
777 sbFileMetadataService::AddBlacklistURL(const nsACString & aURL)
778 {
779  LOG(("%s[%.8x] Adding blacklist url \"%s\"",
780  __FUNCTION__,
781  this,
782  aURL.BeginReading()));
783  nsresult rv;
784  if (!mCrashTracker) {
785  // probably was running the unit test by itself
787  NS_ENSURE_TRUE(mCrashTracker, NS_ERROR_OUT_OF_MEMORY);
788  rv = mCrashTracker->Init();
789  NS_ENSURE_SUCCESS(rv, rv);
790  }
791  rv = mCrashTracker->AddBlacklistURL(aURL);
792  NS_ENSURE_SUCCESS(rv, rv);
793  return NS_OK;
794 }
795 
Runs sbIMetadataHandlers on a background thread.
nsresult ProxiedStartJob(nsIArray *aMediaItemsArray, nsIStringEnumerator *aRequiredProperties, sbMetadataJob::JobType aJobType, sbIJobProgress **_retval)
return NS_OK
nsresult PutProcessedJobItem(sbMetadataJobItem *aJobItem)
const unsigned long STATUS_PLAYING
nsRefPtr< sbBackgroundThreadMetadataProcessor > mBackgroundThreadProcessor
nsresult StartJob(nsIArray *aMediaItemsArray, nsIStringEnumerator *aRequiredProperties, sbMetadataJob::JobType aJobType, sbIJobProgress **_retval)
#define LOG(args)
NS_IMPL_THREADSAFE_ISUPPORTS3(sbFileMetadataService, sbIFileMetadataService, sbPIFileMetadataService, nsIObserver) sbFileMetadataService
Generic interface for exposing long running jobs to the UI.
nsTArray< nsRefPtr< sbMetadataJob > > mJobArray
var event
#define TIMER_PERIOD
nsresult GetJobType(sbMetadataJob::JobType *aJobType)
nsresult do_GetProxyForObject(nsIEventTarget *aTarget, REFNSIID aIID, nsISupports *aObj, PRInt32 aProxyType, void **aProxyObject)
nsresult GetURL(nsACString &aURL)
void AddBlacklistURL(in ACString aURL)
nsCOMPtr< sbIDataRemote > mDataCurrentMetadataJobs
Private file metadata service interface for unit tests.
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
nsRefPtr< sbMetadataCrashTracker > mCrashTracker
const sbCreateProxiedComponent do_ProxiedGetService(const nsCID &aCID, nsresult *error=0)
nsCOMPtr< sbIMediacoreManager > mMediacoreManager
nsresult GetMediaItem(sbIMediaItem **aMediaItem)
Logs file access and maintain a blacklist of crash-causing files.
nsresult GetQueuedJobItem(PRBool aMainThreadOnly, sbMetadataJobItem **aJobItem)
Manages reading and writing metadata to and from sbIMediaItem objects and media files.
const unsigned short MAIN_THREAD_PROCESSOR
Main Thread Processor Flag for restartProcessors.
nsresult ProxiedRestartProcessors(PRUint16 aProcessorsToRestart)
Proxied version of RestartProcessors present on the sbIFileMetadataService interface.
nsresult GetOwningJob(sbMetadataJob **aJob)
nsresult GetJobItemIsBlocked(sbMetadataJobItem *aJobItem, PRBool *aJobItemIsBlocked)
const unsigned short BACKGROUND_THREAD_PROCESSOR
Background Thread Processor Flag for restartProcessors.
function url(spec)
nsRefPtr< sbMainThreadMetadataProcessor > mMainThreadProcessor
#define TRACE(args)
let promptService
#define SB_MEDIACOREMANAGER_CONTRACTID
Runs sbIMetadataHandlers with a timer on the main thread.
nsCOMPtr< nsITimer > mNotificationTimer
A metadata job task.
nsresult UpdateDataRemotes(PRInt64 aJobCount)
_getSelectedPageStyle s i
let observer
Coordinates reading and writing of media file metadata.
const SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC
_updateTextAndScrollDataForFrame aData