sbWatchFolder.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2009 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 #include "sbWatchFolder.h"
28 #include "sbWatchFolderDefines.h"
29 #include <sbIWFMoveRenameHelper9000.h>
30 #include <sbIWFRemoveHelper9001.h>
31 #include <sbPropertiesCID.h>
32 #include <sbStandardProperties.h>
33 #include <sbIApplicationController.h>
34 #include <sbIDirectoryImportService.h>
35 #include <sbIFileMetadataService.h>
36 #include <sbIJobProgress.h>
37 #include <sbIJobProgressService.h>
38 #include <sbIPropertyArray.h>
39 #include <sbStringBundle.h>
40 #include <sbIPrompter.h>
41 #include <nsComponentManagerUtils.h>
42 #include <nsServiceManagerUtils.h>
43 #include <nsICategoryManager.h>
44 #include <nsILocalFile.h>
45 #include <nsIObserverService.h>
46 #include <nsIURI.h>
47 #include <nsTArray.h>
48 #include <sbIMediacoreTypeSniffer.h>
49 #include <nsThreadUtils.h>
50 #include <nsXPCOMCIDInternal.h>
51 #include <nsIXULRuntime.h>
52 #include <prlog.h>
53 
54 
55 #ifdef PR_LOGGING
56 static PRLogModuleInfo* gLog = nsnull;
57 #endif
58 
65 
67 {
68  mCanInteract = PR_TRUE;
69  mHasWatcherStarted = PR_FALSE;
70  mShouldReinitWatcher = PR_FALSE;
71  mShouldSynchronize = PR_FALSE;
72  mEventPumpTimerIsSet = PR_FALSE;
73  mChangeDelayTimerIsSet = PR_FALSE;
74  mShouldProcessEvents = PR_FALSE;
75  mCurrentProcessType = eNone;
76 
77 #ifdef PR_LOGGING
78  if (!gLog) {
79  gLog = PR_NewLogModule("sbWatchFolder");
80  }
81 #endif
82 }
83 
85 {
86 }
87 
88 nsresult
90 {
91  nsresult rv;
92 
93  // The watch folder services are not supported if the file system watcher is
94  // not. The file system watcher component may not be available at all on this
95  // OS or may not be supported on this OS version. In either case, just return
96  // without logging any errors.
97  nsCOMPtr<sbIFileSystemWatcher> fileSystemWatcher =
98  do_CreateInstance("@songbirdnest.com/filesystem/watcher;1", &rv);
99  if (NS_FAILED(rv))
100  return NS_OK;
101 
102  PRBool isWatcherSupported = PR_FALSE;
103  rv = fileSystemWatcher->GetIsSupported(&isWatcherSupported);
104  NS_ENSURE_SUCCESS(rv, rv);
105 
106  // if watching is supported check for safe-mode
107  if (isWatcherSupported) {
108  nsCOMPtr<nsIXULRuntime> appInfo =
109  do_GetService(XULRUNTIME_SERVICE_CONTRACTID, &rv);
110  // If we can't get or QI the runtime assume we're not in safe-mode
111  if (NS_SUCCEEDED(rv)) {
112  PRBool isInSafeMode = PR_FALSE;
113  rv = appInfo->GetInSafeMode(&isInSafeMode);
114  // If we're not in safe mode or we can't determine if we are, enable it
115  isWatcherSupported = NS_FAILED(rv) || !isInSafeMode;
116  }
117  }
118 
119  if (!isWatcherSupported) {
120  mServiceState = eNotSupported;
121  return NS_OK;
122  }
123 
124  mLibraryUtils =
125  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
126  NS_ENSURE_SUCCESS(rv, rv);
127 
128  // Assume the service is disabled. This will be verified on the delayed
129  // initialization in |InitInternal()|.
130  mServiceState = eDisabled;
131 
132  return NS_OK;
133 }
134 
135 nsresult
137 {
138  nsresult rv;
139 
140  // Set the service as disabled, if this method exits cleanly, the service
141  // will be considered 'started'.
142  mServiceState = eDisabled;
143 
144  // If the service is set to be turned off, do not continue.
145  if (!mMediaList) {
146  return NS_OK;
147  }
148 
149  // Don't start the service if the watch path isn't defined.
150  if (mWatchPath.Equals(EmptyString())) {
151  return NS_ERROR_UNEXPECTED;
152  }
153 
154  // The service is now considered started.
155  mServiceState = eStarted;
156 
157  // Now start watching the folder.
158  rv = StartWatchingFolder();
159  NS_ENSURE_SUCCESS(rv, rv);
160 
161  TRACE(("%s: started watching [%s]",
162  __FUNCTION__,
163  NS_ConvertUTF16toUTF8(mWatchPath).get()));
164 
165  return NS_OK;
166 }
167 
168 nsresult
170 {
171  // Don't start if the service is not in the |eStarted| state or if the
172  // watch path is empty.
173  if (mWatchPath.IsEmpty() || mServiceState != eStarted) {
174  return NS_OK;
175  }
176 
177  nsresult rv;
178  mFileSystemWatcher =
179  do_CreateInstance("@songbirdnest.com/filesystem/watcher;1", &rv);
180  NS_ENSURE_SUCCESS(rv, rv);
181 
182  if (mFileSystemWatcherGUID.Equals(EmptyCString())) {
183  // Init a new file-system watcher. The session GUID for the new watcher
184  // will be saved in StopWatching().
185  TRACE(("%s: initiating new FS watcher for [%s]",
186  __FUNCTION__,
187  NS_ConvertUTF16toUTF8(mWatchPath).get()));
188  rv = mFileSystemWatcher->Init(this, mWatchPath, PR_TRUE);
189  NS_ENSURE_SUCCESS(rv, rv);
190  }
191  else {
192  TRACE(("%s: initiating saved session %s",
193  __FUNCTION__, mFileSystemWatcherGUID.get()));
194  rv = mFileSystemWatcher->InitWithSession(mFileSystemWatcherGUID, this);
195  NS_ENSURE_SUCCESS(rv, rv);
196  }
197 
198  if (mShouldSynchronize) {
199  NS_ENSURE_STATE(mMediaList);
200  mMediaList->Clear();
201  }
202 
203  rv = mFileSystemWatcher->StartWatching();
204  NS_ENSURE_SUCCESS(rv, rv);
205 
206  // The service is now watching
207  mServiceState = eWatching;
208 
209  if (mShouldSynchronize) {
210  mShouldSynchronize = PR_FALSE;
211  Rescan();
212  }
213  return NS_OK;
214 }
215 
216 nsresult
218 {
219  if (mServiceState != eWatching) {
220  return NS_OK;
221  }
222 
223  NS_ENSURE_STATE(mFileSystemWatcher);
224 
225  // Clear all event paths.
226  mAddedPaths.clear();
227  mRemovedPaths.clear();
228  mChangedPaths.clear();
229  mDelayedChangedPaths.clear();
230 
231  nsresult rv;
232  if (mFileSystemWatcherGUID.Equals(EmptyCString())) {
233  // This is the first time the file system watcher has run. Save the session
234  // guid so changes can be determined when the watcher starts next.
235  rv = mFileSystemWatcher->GetSessionGuid(mFileSystemWatcherGUID);
236  NS_ENSURE_SUCCESS(rv, rv);
237  }
238 
239  // Stop and kill the file-system watcher.
240  rv = mFileSystemWatcher->StopWatching(PR_TRUE);
241  NS_ENSURE_SUCCESS(rv, rv);
242 
243  // The service is no longer watching - mark as |eStarted|.
244  mServiceState = eStarted;
245  return NS_OK;
246 }
247 
248 nsresult
250 {
251  nsresult rv;
252  if (!mStartupDelayTimer) {
253  LOG(("%s: creating new startup delay timer", __FUNCTION__));
254  mStartupDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
255  NS_ENSURE_SUCCESS(rv, rv);
256  }
257 
258  LOG(("%s: arming up startup delay timer [%08x]",
259  __FUNCTION__, mStartupDelayTimer.get()));
260  return mStartupDelayTimer->InitWithCallback(this,
262  nsITimer::TYPE_ONE_SHOT);
263 }
264 
265 nsresult
267 {
268  if (mHasWatcherStarted) {
269  if (mEventPumpTimerIsSet) {
270  // The event pump timer is already set, but more events have been
271  // received. Set this flags so that the timer will re-arm itself
272  // when it is fired.
273  mShouldProcessEvents = PR_FALSE;
274  }
275  else {
276  LOG(("%s: arming event pump timer [%08x]",
277  __FUNCTION__, mEventPumpTimer.get()));
278 
279  nsresult rv =
280  mEventPumpTimer->InitWithCallback(this,
282  nsITimer::TYPE_ONE_SHOT);
283  NS_ENSURE_SUCCESS(rv, rv);
284 
285  mEventPumpTimerIsSet = PR_TRUE;
286  mShouldProcessEvents = PR_TRUE;
287  }
288  }
289 
290  return NS_OK;
291 }
292 
293 nsresult
295 {
296  TRACE(("%s: Processing the observed event paths", __FUNCTION__));
297  TRACE(("%s: mRemovedPaths.size() == %i", __FUNCTION__, mRemovedPaths.size()));
298  TRACE(("%s: mAddedPaths.size() == %i", __FUNCTION__, mAddedPaths.size()));
299  TRACE(("%s: mChangedPaths.size() == %i", __FUNCTION__, mChangedPaths.size()));
300 
301  // For now, just process remove, added, and changed paths.
302  nsresult rv;
303 
304  // If possible, try to guess moves and renames and avoid
305  // just removing and re-adding
306  if (mRemovedPaths.size() > 0 && mAddedPaths.size() > 0) {
307  LOG(("sbWatchFolder: possible move/rename detected"));
308  rv = HandleEventPathList(mRemovedPaths, eMoveOrRename);
309  NS_ENSURE_SUCCESS(rv, rv);
310  }
311  else {
312  rv = HandleEventPathList(mRemovedPaths, eRemoval);
313  NS_ENSURE_SUCCESS(rv, rv);
314 
315  rv = ProcessAddedPaths();
316  NS_ENSURE_SUCCESS(rv, rv);
317  }
318 
319  rv = HandleEventPathList(mChangedPaths, eChanged);
320  NS_ENSURE_SUCCESS(rv, rv);
321 
322  return NS_OK;
323 }
324 
325 nsresult
327  EProcessType aProcessType)
328 {
329  LOG(("%s: Processing event path list [aEventPathSet.size == %i]",
330  __FUNCTION__, aEventPathSet.size()));
331 
332  if (aEventPathSet.empty()) {
333  return NS_OK;
334  }
335 
336  mCurrentProcessType = aProcessType;
337 
338  nsresult rv = EnumerateItemsByPaths(aEventPathSet);
339  NS_ENSURE_SUCCESS(rv, rv);
340 
341  aEventPathSet.clear();
342  return NS_OK;
343 }
344 
345 nsresult
347 {
348  LOG(("%s: Processing the added file paths... [mAddedPaths.size == %i]",
349  __FUNCTION__, mAddedPaths.size()));
350 
351  if (mAddedPaths.empty()) {
352  return NS_OK;
353  }
354 
355  nsresult rv;
356  nsCOMPtr<nsIArray> uriArray;
357  rv = GetURIArrayForStringPaths(mAddedPaths, getter_AddRefs(uriArray));
358  NS_ENSURE_SUCCESS(rv, rv);
359 
360  mAddedPaths.clear();
361 
362  PRUint32 uriArrayLength = 0;
363  rv = uriArray->GetLength(&uriArrayLength);
364  NS_ENSURE_SUCCESS(rv, rv);
365 
366  if (uriArrayLength > 0) {
367  nsCOMPtr<sbIDirectoryImportService> importService;
368  rv = GetImporter(getter_AddRefs(importService));
369  NS_ENSURE_SUCCESS(rv, rv);
370 
371  //
372  // XXX todo This can cause problems if this fires when the user is dragging
373  // and dropping into a playlist. This will need to be fixed.
374  //
375  nsCOMPtr<sbIDirectoryImportJob> job;
376  rv = importService->ImportWithCustomSnifferAndMetadataScanner(
377  uriArray,
378  mTypeSniffer,
379  mMetadataScanner,
380  mMediaList,
381  -1,
382  getter_AddRefs(job));
383  NS_ENSURE_SUCCESS(rv, rv);
384 
385  nsCOMPtr<sbIJobProgressService> progressService =
386  do_GetService("@songbirdnest.com/Songbird/JobProgressService;1", &rv);
387  if (NS_SUCCEEDED(rv) && progressService) {
388  nsCOMPtr<sbIJobProgress> jobProgress = do_QueryInterface(job, &rv);
389  NS_ENSURE_SUCCESS(rv, rv);
390 
391  rv = progressService->ShowProgressDialog(jobProgress, nsnull, 1);
392  NS_ENSURE_SUCCESS(rv, rv);
393  }
394  }
395 
396  return NS_OK;
397 }
398 
399 nsresult
401  nsIArray **aURIs)
402 {
403  NS_ENSURE_ARG_POINTER(aURIs);
404  nsresult rv;
405 
406  nsCOMPtr<nsIMutableArray> uriArray =
407  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
408  NS_ENSURE_SUCCESS(rv, rv);
409 
410  nsCOMPtr<sbIMediacoreTypeSniffer> typeSniffer;
411  rv = GetTypeSniffer(getter_AddRefs(typeSniffer));
412  NS_ENSURE_SUCCESS(rv,rv);
413 
414  sbStringSetIter begin = aPathsSet.begin();
415  sbStringSetIter end = aPathsSet.end();
417  for (next = begin; next != end; ++next) {
418  nsCOMPtr<nsIURI> fileURI;
419  rv = GetFilePathURI(*next, getter_AddRefs(fileURI));
420  if (NS_FAILED(rv)) {
421  NS_WARNING("Could not get a URI for a file!");
422  continue;
423  }
424 
425  // Don't add every type of file, have the mediacore sniffer validate this
426  // is a URI that we can handle.
427  PRBool isValid = PR_FALSE;
428  rv = typeSniffer->IsValidMediaURL(fileURI, &isValid);
429  if (NS_SUCCEEDED(rv) && isValid) {
430  rv = uriArray->AppendElement(fileURI, PR_FALSE);
431  if (NS_FAILED(rv)) {
432  NS_WARNING("Could not append the URI to the mutable array!");
433  }
434  }
435  }
436 
437  nsCOMPtr<nsIArray> array = do_QueryInterface(uriArray, &rv);
438  NS_ENSURE_SUCCESS(rv, rv);
439  array.forget(aURIs);
440  return rv;
441 }
442 
443 nsresult
444 sbWatchFolder::GetFilePathURI(const nsAString & aFilePath,
445  nsIURI **aURIRetVal)
446 {
447  NS_ENSURE_ARG_POINTER(aURIRetVal);
448 
449  nsresult rv;
450 
451  nsCOMPtr<nsILocalFile> pathFile =
452  do_CreateInstance("@mozilla.org/file/local;1", &rv);
453  NS_ENSURE_SUCCESS(rv, rv);
454 
455  rv = pathFile->InitWithPath(aFilePath);
456  NS_ENSURE_SUCCESS(rv, rv);
457 
458  return mLibraryUtils->GetFileContentURI(pathFile, aURIRetVal);
459 }
460 
461 nsresult
463 {
464  nsresult rv;
465  nsCOMPtr<sbIMutablePropertyArray> properties =
466  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
467  NS_ENSURE_SUCCESS(rv, rv);
468 
469  nsString propName(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL));
470 
471  sbStringSetIter begin = aPathSet.begin();
472  sbStringSetIter end = aPathSet.end();
474  for (next = begin; next != end; ++next) {
475  // Convert the current path to a URI, then get the spec.
476  nsCOMPtr<nsIURI> fileURI;
477  rv = GetFilePathURI(*next, getter_AddRefs(fileURI));
478  if (NS_FAILED(rv)) {
479  NS_WARNING("Could not get the file path URI!");
480  continue;
481  }
482 
483  nsCString pathSpec;
484  rv = fileURI->GetSpec(pathSpec);
485  if (NS_FAILED(rv)) {
486  NS_WARNING("Could not get the URI spec!");
487  continue;
488  }
489 
490  rv = properties->AppendProperty(propName, NS_ConvertUTF8toUTF16(pathSpec));
491  if (NS_FAILED(rv)) {
492  NS_WARNING("Could not append a property!");
493  }
494  }
495 
496  PRUint16 enumType = sbIMediaList::ENUMERATIONTYPE_SNAPSHOT;
497  rv = mMediaList->EnumerateItemsByProperties(properties, this, enumType);
498  NS_ENSURE_SUCCESS(rv, rv);
499 
500  return NS_OK;
501 }
502 
503 nsresult
505 {
506  NS_ENSURE_ARG_POINTER(aSongbirdWindow);
507 
508  nsresult rv;
509  nsCOMPtr<sbIApplicationController> appController =
510  do_GetService("@songbirdnest.com/Songbird/ApplicationController;1", &rv);
511  NS_ENSURE_SUCCESS(rv, rv);
512 
513  return appController->GetActiveMainWindow(aSongbirdWindow);
514 }
515 
516 nsresult
518 {
519  NS_ASSERTION(NS_IsMainThread(),
520  "HandleSessionLoadError() not called on main thread!");
521  NS_ENSURE_STATE(mFileSystemWatcher);
522 
523  // If the unit tests are running, don't show the dialog (Don't bother
524  // checking result of call too).
525  nsresult rv;
526  if (!mCanInteract) {
527  return NS_OK;
528  }
529 
530  // If this method gets called, than the watcher could not load the stored
531  // session. The tree will need to be re-initialized, this time without
532  // loading a session.
533  if (!mFileSystemWatcherGUID.IsEmpty()) {
534  // Attempt the remove the session data. Don't bother returning the result
535  // if it fails, just warn about it.
536  rv = mFileSystemWatcher->DeleteSession(mFileSystemWatcherGUID);
537  if (NS_FAILED(rv)) {
538  NS_WARNING("Could not delete the bad session data file!");
539  }
540 
541  mFileSystemWatcherGUID.Truncate();
542  }
543 
544  rv = mFileSystemWatcher->Init(this, mWatchPath, PR_TRUE);
545  NS_ENSURE_SUCCESS(rv, rv);
546 
547  rv = mFileSystemWatcher->StartWatching();
548  NS_ENSURE_SUCCESS(rv, rv);
549 
551  nsString dialogTitle =
552  bundle.Get("watch_folder.session_load_error.rescan_title");
553 
554  nsTArray<nsString> params;
555  params.AppendElement(mWatchPath);
556  nsString dialogText =
557  bundle.Format("watch_folder.session_load_error.rescan_text", params);
558 
559  nsCOMPtr<nsIDOMWindow> songbirdWindow;
560  rv = GetSongbirdWindow(getter_AddRefs(songbirdWindow));
561  NS_ENSURE_SUCCESS(rv, rv);
562 
563  nsCOMPtr<sbIPrompter> prompter =
564  do_CreateInstance("@songbirdnest.com/Songbird/Prompter;1", &rv);
565  NS_ENSURE_SUCCESS(rv, rv);
566 
567  rv = prompter->SetWaitForWindow(PR_TRUE);
568  NS_ENSURE_SUCCESS(rv, rv);
569 
570  PRBool shouldRescan = PR_FALSE;
571  prompter->Confirm(songbirdWindow,
572  dialogTitle.BeginReading(),
573  dialogText.BeginReading(),
574  &shouldRescan);
575 
576  // Only start the scan if the user picked the YES button (option 0).
577  if (shouldRescan) {
578  // The user elected to rescan their watched directory.
579  rv = Rescan();
580  NS_ENSURE_SUCCESS(rv, rv);
581  }
582 
583  return NS_OK;
584 }
585 
586 nsresult
588 {
589  nsresult rv;
590 
591  // Setup the directory scan service.
592  nsCOMPtr<sbIDirectoryImportService> dirImportService;
593  rv = GetImporter(getter_AddRefs(dirImportService));
594  NS_ENSURE_SUCCESS(rv, rv);
595 
596  // The directory import service wants the paths as an array.
597  nsCOMPtr<nsILocalFile> watchPathFile =
598  do_CreateInstance("@mozilla.org/file/local;1", &rv);
599  NS_ENSURE_SUCCESS(rv, rv);
600 
601  rv = watchPathFile->InitWithPath(mWatchPath);
602  NS_ENSURE_SUCCESS(rv, rv);
603 
604  nsCOMPtr<nsIMutableArray> dirArray =
605  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
606 
607  rv = dirArray->AppendElement(watchPathFile, PR_FALSE);
608  NS_ENSURE_SUCCESS(rv, rv);
609 
610  nsCOMPtr<sbIDirectoryImportJob> importJob;
611  rv = dirImportService->ImportWithCustomSnifferAndMetadataScanner(
612  dirArray,
613  mTypeSniffer,
614  mMetadataScanner,
615  mMediaList,
616  -1,
617  getter_AddRefs(importJob));
618  NS_ENSURE_SUCCESS(rv, rv);
619 
620  if (!importJob) {
621  return NS_OK;
622  }
623 
624  if (!mCanInteract) {
625  return NS_OK;
626  }
627 
628  nsCOMPtr<sbIJobProgressService> progressService =
629  do_GetService("@songbirdnest.com/Songbird/JobProgressService;1", &rv);
630  NS_ENSURE_SUCCESS(rv, rv);
631 
632  nsCOMPtr<sbIJobProgress> job = do_QueryInterface(importJob, &rv);
633  NS_ENSURE_SUCCESS(rv, rv);
634 
635  nsCOMPtr<sbIApplicationController> appController =
636  do_GetService("@songbirdnest.com/Songbird/ApplicationController;1", &rv);
637  NS_ENSURE_SUCCESS(rv, rv);
638 
639  nsCOMPtr<nsIDOMWindow> activeWindow;
640  rv = appController->GetActiveWindow(getter_AddRefs(activeWindow));
641  NS_ENSURE_SUCCESS(rv, rv);
642 
643  rv = progressService->ShowProgressDialog(job, activeWindow, 1);
644  NS_ENSURE_SUCCESS(rv, rv);
645 
646  return NS_OK;
647 }
648 
649 nsresult
651 {
652  // If the unit tests are running, don't show the dialog (Don't bother
653  // checking result of call too).
654  nsresult rv;
655  if (!mCanInteract) {
656  return NS_OK;
657  }
658 
660  nsString dialogTitle = bundle.Get("watch_folder.root_path_missing.title");
661 
662  nsTArray<nsString> params;
663  params.AppendElement(mWatchPath);
664  nsString dialogText =
665  bundle.Format("watch_folder.root_path_missing.text", params);
666 
667  nsCOMPtr<nsIDOMWindow> songbirdWindow;
668  rv = GetSongbirdWindow(getter_AddRefs(songbirdWindow));
669  NS_ENSURE_SUCCESS(rv, rv);
670 
671  nsCOMPtr<sbIPrompter> prompter =
672  do_CreateInstance("@songbirdnest.com/Songbird/Prompter;1", &rv);
673  NS_ENSURE_SUCCESS(rv, rv);
674 
675  rv = prompter->SetWaitForWindow(PR_TRUE);
676  NS_ENSURE_SUCCESS(rv, rv);
677 
678  rv = prompter->Alert(songbirdWindow,
679  dialogTitle.BeginReading(),
680  dialogText.BeginReading());
681  NS_ENSURE_SUCCESS(rv, rv);
682 
683  return NS_OK;
684 }
685 
686 nsresult
687 sbWatchFolder::DecrementIgnoredPathCount(const nsAString & aFilePath,
688  PRBool *aIsIgnoredPath)
689 {
690  NS_ENSURE_ARG_POINTER(aIsIgnoredPath);
691 
692  sbStringMap::iterator foundIter = mIgnorePaths.find(nsString(aFilePath));
693  if (foundIter == mIgnorePaths.end()) {
694  *aIsIgnoredPath = PR_FALSE;
695  } else {
696  *aIsIgnoredPath = PR_TRUE;
697  if (foundIter->second.count > 0) {
698  --foundIter->second.count;
699  if ((foundIter->second.count < 1) && (foundIter->second.depth < 1)) {
700  // the count has reached zero, stop ignoring
701  mIgnorePaths.erase(foundIter);
702  }
703  }
704  }
705  return NS_OK;
706 }
707 
708 NS_IMETHODIMP
709 sbWatchFolder::Start(const nsACString & aSessionGuid)
710 {
711  mFileSystemWatcherGUID = aSessionGuid;
712 
713  nsresult rv = SetStartupDelayTimer();
714  NS_ENSURE_SUCCESS(rv, rv);
715 
716  return NS_OK;
717 }
718 
719 NS_IMETHODIMP
720 sbWatchFolder::Stop(nsACString & _retval NS_OUTPARAM)
721 {
722  nsresult rv;
723  if (mServiceState == eWatching) {
724  rv = StopWatchingFolder();
725  NS_ENSURE_SUCCESS(rv, rv);
726  }
727 
728  if (mStartupDelayTimer) {
729  rv = mStartupDelayTimer->Cancel();
730  NS_ENSURE_SUCCESS(rv, rv);
731 
732  LOG(("%s: app is shutting down, aborting startup delay timer [%08x]",
733  __FUNCTION__, mStartupDelayTimer.get()));
734  }
735 
736  _retval = mFileSystemWatcherGUID;
737 
738  // Prevent reference loops and leaks.
739  mFileSystemWatcher = nsnull;
740 
741  return NS_OK;
742 }
743 
744 NS_IMETHODIMP
745 sbWatchFolder::SetFolder(const nsAString & aNewWatchPath, PRBool aSynchronizeMediaList)
746 {
747  LOG(("%s: %s",
748  __FUNCTION__,
749  NS_ConvertUTF16toUTF8(aNewWatchPath).get()));
750 
751  if (mWatchPath.Equals(aNewWatchPath, CaseInsensitiveCompare)) {
752  return NS_OK;
753  }
754 
755  nsresult rv;
756 
757  mWatchPath = aNewWatchPath;
758  mShouldSynchronize = aSynchronizeMediaList;
759 
760  if (mServiceState == eWatching) {
761  TRACE(("%s: already watching, stopping...", __FUNCTION__));
762  NS_ENSURE_STATE(mFileSystemWatcher);
763 
764  // The service is currently running with a file system watcher
765  // that is currently active. The watcher needs to be stopped (
766  // without saving a session) and re-started once it has
767  // successfully shutdown.
768 
769  if (!mFileSystemWatcherGUID.IsEmpty()) {
770  // Clear any previously stored data from the session that might
771  // be stored in the users profile.
772  rv = mFileSystemWatcher->DeleteSession(mFileSystemWatcherGUID);
773  if (NS_FAILED(rv)) {
774  // Just warn if deleting the previous session fails.
775  NS_WARNING("Could not delete old session data!");
776  }
777 
778  mFileSystemWatcherGUID.Truncate();
779  }
780 
781  // Set a flag to re-setup a file system watcher once the current
782  // one has shutdown.
783  mShouldReinitWatcher = PR_TRUE;
784 
785  // The service is no longer watching
786  mServiceState = eStarted;
787 
788  // Flush all event paths, reset flags, and stop the watcher.
789  mAddedPaths.clear();
790  mRemovedPaths.clear();
791  mChangedPaths.clear();
792  mDelayedChangedPaths.clear();
793 
794  rv = mFileSystemWatcher->StopWatching(PR_FALSE);
795  NS_ENSURE_SUCCESS(rv, rv);
796  }
797  else if (mServiceState == eDisabled && !mWatchPath.IsEmpty()) {
798  // The service has not started up internally, but the watch path
799  // has changed. If the service has been set to be enabled, start
800  // the delayed internal startup.
801  if (mMediaList) {
802  TRACE(("%s: not watching yet, arming...", __FUNCTION__));
803  // Now that the service state is disabled, the watch path has
804  // been set, and the service should enable - it is time to
805  // start the delayed internal init.
806  rv = SetStartupDelayTimer();
807  NS_ENSURE_SUCCESS(rv, rv);
808  }
809  }
810 
811  return NS_OK;
812 }
813 
814 nsresult
815 sbWatchFolder::Disable()
816 {
817  LOG(("%s", __FUNCTION__));
818 
819  nsresult rv;
820 
821  // Stop watching since the service is watching and the pref was toggled
822  // to not watch.
823  if (mServiceState == eWatching) {
824  rv = StopWatchingFolder();
825  NS_ENSURE_SUCCESS(rv, rv);
826  }
827 
828  return NS_OK;
829 }
830 
831 nsresult
832 sbWatchFolder::Enable()
833 {
834  LOG(("%s", __FUNCTION__));
835 
836  nsresult rv;
837 
838  // Start watching since the service is not watching and the pref was
839  // toggled to start watching.
840  if (mServiceState == eStarted) {
841  rv = StartWatchingFolder();
842  NS_ENSURE_SUCCESS(rv, rv);
843  }
844 
845  // The service has not yet attempted to start up and was just turned on.
846  // Start the timer if the service is in a disabled state, the watch path
847  // has been defined, and the service should enable.
848  else if (mServiceState == eDisabled &&
849  !mWatchPath.IsEmpty())
850  {
851  rv = SetStartupDelayTimer();
852  NS_ENSURE_SUCCESS(rv, rv);
853  }
854 
855  return NS_OK;
856 }
857 
858 //------------------------------------------------------------------------------
859 // sbIWatchFolder
860 
861 NS_IMETHODIMP sbWatchFolder::GetMediaList(sbIMediaList * *aMediaList)
862 {
863  NS_ENSURE_ARG_POINTER(aMediaList);
864  NS_IF_ADDREF(*aMediaList = mMediaList);
865  return NS_OK;
866 }
867 
868 NS_IMETHODIMP sbWatchFolder::SetMediaList(sbIMediaList * aMediaList)
869 {
870  nsresult rv;
871 
872  if (mMediaList && aMediaList) {
873  PRBool same = PR_FALSE;
874  rv = mMediaList->Equals(aMediaList, &same);
875  NS_ENSURE_SUCCESS(rv, rv);
876 
877  if (same) {
878  return NS_OK;
879  }
880  }
881 
882  if (mMediaList) {
883  Disable();
884  }
885 
886  mMediaList = aMediaList;
887 
888  if (mMediaList) {
889  Enable();
890  }
891 
892  return NS_OK;
893 }
894 
895 NS_IMETHODIMP sbWatchFolder::GetPath(nsAString & aPath)
896 {
897  aPath = mWatchPath;
898  return NS_OK;
899 }
900 
901 NS_IMETHODIMP sbWatchFolder::GetImporter(sbIDirectoryImportService * *aImporter)
902 {
903  NS_ENSURE_ARG_POINTER(aImporter);
904 
905  nsresult rv;
906  nsCOMPtr<sbIDirectoryImportService> importer = mCustomImporter;
907  if (!importer) {
908  importer = do_GetService(
909  "@songbirdnest.com/Songbird/DirectoryImportService;1", &rv);
910  NS_ENSURE_SUCCESS(rv, rv);
911  }
912  importer.forget(aImporter);
913 
914  return NS_OK;
915 }
916 
917 NS_IMETHODIMP sbWatchFolder::SetImporter(sbIDirectoryImportService * aImporter)
918 {
919  mCustomImporter = aImporter;
920  return NS_OK;
921 }
922 
923 NS_IMETHODIMP sbWatchFolder::GetTypeSniffer(sbIMediacoreTypeSniffer * *aTypeSniffer)
924 {
925  NS_ENSURE_ARG_POINTER(aTypeSniffer);
926  nsresult rv;
927  nsCOMPtr<sbIMediacoreTypeSniffer> sniffer = mTypeSniffer;
928  if (!sniffer) {
929  sniffer = do_GetService(
930  "@songbirdnest.com/Songbird/Mediacore/TypeSniffer;1", &rv);
931  NS_ENSURE_SUCCESS(rv, rv);
932  }
933  sniffer.forget(aTypeSniffer);
934 
935  return NS_OK;
936 }
937 
938 NS_IMETHODIMP sbWatchFolder::SetTypeSniffer(sbIMediacoreTypeSniffer * aTypeSniffer)
939 {
940  mTypeSniffer = aTypeSniffer;
941  return NS_OK;
942 }
943 
944 NS_IMETHODIMP sbWatchFolder::GetMetadataScanner(sbIFileMetadataService * *aMetadataScanner)
945 {
946  NS_ENSURE_ARG_POINTER(aMetadataScanner);
947 
948  nsresult rv;
949  nsCOMPtr<sbIFileMetadataService> scanner = mMetadataScanner;
950  if (!scanner) {
951  scanner = do_GetService(
952  "@songbirdnest.com/Songbird/FileMetadataService;1", &rv);
953  NS_ENSURE_SUCCESS(rv, rv);
954  }
955  scanner.forget(aMetadataScanner);
956 
957  return NS_OK;
958 }
959 
960 NS_IMETHODIMP sbWatchFolder::SetMetadataScanner(sbIFileMetadataService * aMetadataScanner)
961 {
962  mMetadataScanner = aMetadataScanner;
963  return NS_OK;
964 }
965 
966 NS_IMETHODIMP sbWatchFolder::GetCanInteract(PRBool *aCanInteract)
967 {
968  NS_ENSURE_ARG_POINTER(aCanInteract);
969  *aCanInteract = mCanInteract;
970  return NS_OK;
971 }
972 
973 NS_IMETHODIMP sbWatchFolder::SetCanInteract(PRBool aCanInteract)
974 {
975  mCanInteract = aCanInteract;
976  return NS_OK;
977 }
978 
979 NS_IMETHODIMP
980 sbWatchFolder::GetIsSupported(PRBool *aIsSupported)
981 {
982  NS_ENSURE_ARG_POINTER(aIsSupported);
983  *aIsSupported = mServiceState != eNotSupported;
984  return NS_OK;
985 }
986 
987 NS_IMETHODIMP
988 sbWatchFolder::GetIsRunning(PRBool *aIsRunning)
989 {
990  NS_ENSURE_ARG_POINTER(aIsRunning);
991  *aIsRunning = (mServiceState == eWatching);
992  return NS_OK;
993 }
994 
995 NS_IMETHODIMP
996 sbWatchFolder::AddIgnorePath(const nsAString & aFilePath)
997 {
998  LOG(("sbWatchFolder::AddIgnorePath %s",
999  NS_LossyConvertUTF16toASCII(aFilePath).get()));
1000 
1001  nsString filePath(aFilePath);
1002 
1003  sbStringMap::iterator it = mIgnorePaths.find(filePath);
1004  if (it == mIgnorePaths.end()) {
1005  // new ignore path
1006  mIgnorePaths[filePath] = ignorePathData_t(1, 0);
1007  } else {
1008  // existing ignore path, set the always-ignore flag
1009  ++(it->second.depth);
1010  }
1011  return NS_OK;
1012 }
1013 
1014 NS_IMETHODIMP
1015 sbWatchFolder::RemoveIgnorePath(const nsAString & aFilePath)
1016 {
1017  LOG(("sbWatchFolder::RemoveIgnorePath %s",
1018  NS_LossyConvertUTF16toASCII(aFilePath).get()));
1019 
1020  nsString filePath(aFilePath);
1021 
1022  sbStringMap::iterator it = mIgnorePaths.find(filePath);
1023  // note: there is no error if this is called too many times.
1024  if (it != mIgnorePaths.end()) {
1025  it->second.depth = PR_MAX(0, it->second.depth - 1);
1026  if (it->second.depth < 1 && it->second.count < 1) {
1027  // there is no counter, the ignore path can be removed
1028  mIgnorePaths.erase(it);
1029  }
1030  }
1031 
1032  return NS_OK;
1033 }
1034 
1035 NS_IMETHODIMP
1036 sbWatchFolder::AddIgnoreCount(const nsAString & aFilePath,
1037  PRInt32 aCount)
1038 {
1039  nsString filePath(aFilePath);
1040 
1041  sbStringMap::iterator it = mIgnorePaths.find(filePath);
1042  if (it == mIgnorePaths.end()) {
1043  // not found, make a new entry
1044  ignorePathData_t newData(0, 0);
1045  it = mIgnorePaths.insert(sbStringMap::value_type(filePath, newData)).first;
1046  }
1047 
1048  it->second.count += aCount;
1049  if (it->second.count < 1) {
1050  it->second.count = 0;
1051  if (it->second.depth < 1) {
1052  // the count has reached zero, and there is no always-ignore flag
1053  mIgnorePaths.erase(it);
1054  }
1055  }
1056 
1057  return NS_OK;
1058 }
1059 
1060 //------------------------------------------------------------------------------
1061 // sbIFileSystemListener
1062 
1063 NS_IMETHODIMP
1064 sbWatchFolder::OnWatcherStarted()
1065 {
1066  nsresult rv;
1067 
1068  // Now start up the timer
1069  if (!mEventPumpTimer) {
1070  mEventPumpTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
1071  NS_ENSURE_SUCCESS(rv, rv);
1072  }
1073 
1074  if (!mChangeDelayTimer) {
1075  mChangeDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
1076  NS_ENSURE_SUCCESS(rv, rv);
1077  }
1078 
1079  // Process any event received before the watcher has started. These will
1080  // be all the events from comparing the de-serialized session to the current
1081  // filesystem state.
1082  LOG(("%s: arming event pump timer [%08x]",
1083  __FUNCTION__, mEventPumpTimer.get()));
1084  rv = mEventPumpTimer->InitWithCallback(this,
1086  nsITimer::TYPE_ONE_SHOT);
1087  NS_ENSURE_SUCCESS(rv, rv);
1088 
1089  mEventPumpTimerIsSet = PR_TRUE;
1090  mShouldProcessEvents = PR_TRUE;
1091  mHasWatcherStarted = PR_TRUE;
1092 
1093  TRACE(("sbWatchFolder::OnWatcherStarted (path [%s])",
1094  NS_ConvertUTF16toUTF8(mWatchPath).get()));
1095  return NS_OK;
1096 }
1097 
1098 NS_IMETHODIMP
1099 sbWatchFolder::OnWatcherStopped()
1100 {
1101  if (mEventPumpTimer) {
1102  mEventPumpTimer->Cancel();
1103  }
1104  if (mChangeDelayTimer) {
1105  mChangeDelayTimer->Cancel();
1106  }
1107 
1108  mHasWatcherStarted = PR_FALSE;
1109 
1110  // If this flag is set, the watch path has been changed. Since the watcher
1111  // has now sucesfully stoped, set a callback timer to null out the current
1112  // file system watcher and create a new one.
1113  if (mShouldReinitWatcher) {
1114  nsresult rv;
1115  if (!mFlushFSWatcherTimer) {
1116  mFlushFSWatcherTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
1117  NS_ENSURE_SUCCESS(rv, rv);
1118  }
1119 
1120  rv = mFlushFSWatcherTimer->InitWithCallback(this,
1122  nsITimer::TYPE_ONE_SHOT);
1123  NS_ENSURE_SUCCESS(rv, rv);
1124  }
1125 
1126  TRACE(("sbWatchFolder::OnWatcherStopped (path [%s])",
1127  NS_ConvertUTF16toUTF8(mWatchPath).get()));
1128  return NS_OK;
1129 }
1130 
1131 NS_IMETHODIMP
1132 sbWatchFolder::OnWatcherError(PRUint32 aErrorType,
1133  const nsAString & aDescription)
1134 {
1135  nsresult rv;
1136  switch (aErrorType) {
1138  NS_WARNING("WARNING: Root path is missing!");
1139  rv = HandleRootPathMissing();
1140  NS_ENSURE_SUCCESS(rv, rv);
1141  break;
1142 
1144  NS_WARNING("WARNING: Invalid directory!");
1145  break;
1146 
1148  rv = HandleSessionLoadError();
1149  NS_ENSURE_SUCCESS(rv, rv);
1150  break;
1151  }
1152 
1153  return NS_OK;
1154 }
1155 
1156 NS_IMETHODIMP
1157 sbWatchFolder::OnFileSystemChanged(const nsAString & aFilePath)
1158 {
1159  LOG(("sbWatchFolder::OnFileSystemChanged %s",
1160  NS_LossyConvertUTF16toASCII(aFilePath).get()));
1161 
1162  PRBool isIgnoredPath = PR_FALSE;
1163  nsresult rv = DecrementIgnoredPathCount(aFilePath, &isIgnoredPath);
1164  NS_ENSURE_SUCCESS(rv, rv);
1165 
1166  // Don't bother with this change if it is currently being ignored.
1167  if (isIgnoredPath) {
1168  return NS_OK;
1169  }
1170 
1171  nsString filePath(aFilePath);
1172 
1173  // The watcher will report all changes from the previous session before the
1174  // watcher has started. Don't set the timer until then.
1175  if (mHasWatcherStarted) {
1176  // See if the changed path set already has this path inside of it.
1177  sbStringSetIter foundIter = mChangedPaths.find(filePath);
1178  if (foundIter != mChangedPaths.end()) {
1179  // If this path is currently in the changed path vector already,
1180  // delay processing this path until a later time.
1181  mDelayedChangedPaths.insert(filePath);
1182 
1183  // Start the delayed timer if it isn't running already.
1184  if (!mChangeDelayTimerIsSet) {
1185  rv = mChangeDelayTimer->InitWithCallback(this,
1187  nsITimer::TYPE_ONE_SHOT);
1188  NS_ENSURE_SUCCESS(rv, rv);
1189 
1190  mChangeDelayTimerIsSet = PR_TRUE;
1191  }
1192  }
1193  else {
1194  mChangedPaths.insert(filePath);
1195  rv = SetEventPumpTimer();
1196  NS_ENSURE_SUCCESS(rv, rv);
1197  }
1198  }
1199  else {
1200  mChangedPaths.insert(filePath);
1201  }
1202 
1203  return NS_OK;
1204 }
1205 
1206 NS_IMETHODIMP
1207 sbWatchFolder::OnFileSystemRemoved(const nsAString & aFilePath)
1208 {
1209  LOG(("sbWatchFolder::OnFileSystemRemoved %s",
1210  NS_LossyConvertUTF16toASCII(aFilePath).get()));
1211 
1212  PRBool isIgnoredPath = PR_FALSE;
1213  nsresult rv = DecrementIgnoredPathCount(aFilePath, &isIgnoredPath);
1214  NS_ENSURE_SUCCESS(rv, rv);
1215 
1216  if (!isIgnoredPath) {
1217  mRemovedPaths.insert(nsString(aFilePath));
1218 
1219  // The method will guard against |mHasWatcherStarted|
1220  rv = SetEventPumpTimer();
1221  NS_ENSURE_SUCCESS(rv, rv);
1222  }
1223 
1224  return NS_OK;
1225 }
1226 
1227 NS_IMETHODIMP
1228 sbWatchFolder::OnFileSystemAdded(const nsAString & aFilePath)
1229 {
1230  LOG(("sbWatchFolder::OnFileSystemAdded %s",
1231  NS_LossyConvertUTF16toASCII(aFilePath).get()));
1232 
1233  PRBool isIgnoredPath = PR_FALSE;
1234  nsresult rv = DecrementIgnoredPathCount(aFilePath, &isIgnoredPath);
1235  NS_ENSURE_SUCCESS(rv, rv);
1236 
1237  if (!isIgnoredPath) {
1238  mAddedPaths.insert(nsString(aFilePath));
1239 
1240  // The method will guard against |mHasWatcherStarted|
1241  rv = SetEventPumpTimer();
1242  NS_ENSURE_SUCCESS(rv, rv);
1243  }
1244 
1245  return NS_OK;
1246 }
1247 
1248 //------------------------------------------------------------------------------
1249 // sbIMediaListEnumerationListener
1250 
1251 NS_IMETHODIMP
1252 sbWatchFolder::OnEnumerationBegin(sbIMediaList *aMediaList,
1253  PRUint16 *aRetVal)
1254 {
1255  if (!mEnumeratedMediaItems) {
1256  nsresult rv;
1257  mEnumeratedMediaItems =
1258  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1259  NS_ENSURE_SUCCESS(rv, rv);
1260  }
1261 
1263  return NS_OK;
1264 }
1265 
1266 NS_IMETHODIMP
1267 sbWatchFolder::OnEnumeratedItem(sbIMediaList *aMediaList,
1268  sbIMediaItem *aMediaItem,
1269  PRUint16 *aRetVal)
1270 {
1271  mEnumeratedMediaItems->AppendElement(aMediaItem, PR_FALSE);
1273  return NS_OK;
1274 }
1275 
1276 NS_IMETHODIMP
1277 sbWatchFolder::OnEnumerationEnd(sbIMediaList *aMediaList,
1278  nsresult aStatusCode)
1279 {
1280  nsresult rv;
1281  PRUint32 length;
1282  rv = mEnumeratedMediaItems->GetLength(&length);
1283  NS_ENSURE_SUCCESS(rv, rv);
1284 
1285  LOG(("%s: Found %i media items during enumeration", __FUNCTION__, length));
1286 
1287  if (length > 0) {
1288  if (mCurrentProcessType == eRemoval) {
1289  // Remove the found items from the library, pop up the progress dialog.
1290  nsCOMPtr<sbIWFRemoveHelper9001> helper =
1291  do_GetService("@songbirdnest.com/Songbird/RemoveHelper;1", &rv);
1292  NS_ENSURE_SUCCESS(rv, rv);
1293 
1294  mRemovedPaths.clear();
1295 
1296  helper->Remove(mEnumeratedMediaItems);
1297  NS_ENSURE_SUCCESS(rv, rv);
1298  }
1299  else if (mCurrentProcessType == eChanged) {
1300  // Rescan the changed items.
1301  nsCOMPtr<sbIFileMetadataService> metadataService;
1302  rv = GetMetadataScanner(getter_AddRefs(metadataService));
1303  NS_ENSURE_SUCCESS(rv, rv);
1304 
1305  nsCOMPtr<sbIJobProgress> jobProgress;
1306  rv = metadataService->Read(mEnumeratedMediaItems,
1307  getter_AddRefs(jobProgress));
1308  NS_ENSURE_SUCCESS(rv, rv);
1309 
1310  }
1311  else if (mCurrentProcessType == eMoveOrRename) {
1312  // Try to detect move/rename
1313  nsCOMPtr<sbIWFMoveRenameHelper9000> helper =
1314  do_GetService("@songbirdnest.com/Songbird/MoveRenameHelper;1", &rv);
1315  NS_ENSURE_SUCCESS(rv, rv);
1316 
1317  nsCOMPtr<nsIArray> uriArray;
1318  rv = GetURIArrayForStringPaths(mAddedPaths, getter_AddRefs(uriArray));
1319  NS_ENSURE_SUCCESS(rv, rv);
1320  mAddedPaths.clear();
1321 
1322 #ifdef PR_LOGGING
1323  PRUint32 length;
1324  rv = uriArray->GetLength(&length);
1325  NS_ENSURE_SUCCESS(rv, rv);
1326 
1327  LOG(("%s: Sending %i added item URL's to the move-rename helper",
1328  __FUNCTION__, length));
1329 #endif
1330 
1331  rv = helper->Process(mEnumeratedMediaItems, uriArray, this);
1332  NS_ENSURE_SUCCESS(rv, rv);
1333  }
1334  }
1335  else if (mCurrentProcessType == eMoveOrRename) {
1336  // If no items where found during the move or rename enumeration lookup
1337  // (i.e. no removed items where found) - add the items that still exist.
1338  // This usually happens when the a directory changes very fast and none
1339  // of the removed items actually exist in the library.
1340  sbStringSet addedPathsCopy = mAddedPaths;
1341  sbStringSetIter begin = addedPathsCopy.begin();
1342  sbStringSetIter end = addedPathsCopy.end();
1344  for (next = begin; next != end; ++next) {
1345  nsCOMPtr<nsILocalFile> curFile =
1346  do_CreateInstance("@mozilla.org/file/local;1", &rv);
1347  NS_ENSURE_SUCCESS(rv, rv);
1348 
1349  rv = curFile->InitWithPath(*next);
1350  if (NS_FAILED(rv)) {
1351  NS_WARNING("ERROR: Could not init a nsILocalFile with a path!");
1352  continue;
1353  }
1354 
1355  PRBool doesExist = PR_FALSE;
1356  rv = curFile->Exists(&doesExist);
1357  if (NS_FAILED(rv) || !doesExist) {
1358  mAddedPaths.erase(*next);
1359  }
1360  }
1361 
1362  rv = ProcessAddedPaths();
1363  NS_ENSURE_SUCCESS(rv, rv);
1364  }
1365 
1366  rv = mEnumeratedMediaItems->Clear();
1367  NS_ENSURE_SUCCESS(rv, rv);
1368 
1369  mCurrentProcessType = eNone;
1370  return NS_OK;
1371 }
1372 
1373 //------------------------------------------------------------------------------
1374 // nsITimerCallback
1375 
1376 NS_IMETHODIMP
1377 sbWatchFolder::Notify(nsITimer *aTimer)
1378 {
1379  nsresult rv;
1380 
1381  // Handle startup delay (internally init)
1382  if (aTimer == mStartupDelayTimer) {
1383  LOG(("%s: startup delay timer [%08x] fired",
1384  __FUNCTION__, mStartupDelayTimer.get()));
1385  rv = InitInternal();
1386  NS_ENSURE_SUCCESS(rv, rv);
1387  }
1388 
1389  // Handle flushing the old file system watcher with a new one
1390  else if (aTimer == mFlushFSWatcherTimer) {
1391  mFileSystemWatcher = nsnull;
1392  mShouldReinitWatcher = PR_FALSE;
1393 
1394  rv = StartWatchingFolder();
1395  NS_ENSURE_SUCCESS(rv, rv);
1396  }
1397 
1398  // Standard processing of removed and non-queued changed paths.
1399  else if (aTimer == mEventPumpTimer) {
1400  if (mShouldProcessEvents) {
1401  // No events have been received since the event pump timer was armed. Go
1402  // ahead and process the event paths now.
1403  rv = ProcessEventPaths();
1404  NS_ENSURE_SUCCESS(rv, rv);
1405 
1406  mEventPumpTimerIsSet = PR_FALSE;
1407  }
1408  else {
1409  // Some event has happened since the last time the event pump timer was
1410  // armed. Go ahead and wait another |EVENT_PUMP_TIMER_DELAY| milliseconds.
1411  LOG(("%s: arming event pump timer [%08x]",
1412  __FUNCTION__, mEventPumpTimer.get()));
1413  rv = mEventPumpTimer->InitWithCallback(this,
1415  nsITimer::TYPE_ONE_SHOT);
1416  NS_ENSURE_SUCCESS(rv, rv);
1417 
1418  mShouldProcessEvents = PR_TRUE;
1419  }
1420  }
1421 
1422  // Process queued changed event paths.
1423  else if (aTimer == mChangeDelayTimer) {
1424  rv = HandleEventPathList(mDelayedChangedPaths, eChanged);
1425  NS_ENSURE_SUCCESS(rv, rv);
1426 
1427  mChangeDelayTimerIsSet = PR_FALSE;
1428  }
1429 
1430  return NS_OK;
1431 }
1432 
1433 //------------------------------------------------------------------------------
1434 // sbIJobProgressListener
1435 
1436 NS_IMETHODIMP
1437 sbWatchFolder::OnJobProgress(sbIJobProgress *aJobProgress)
1438 {
1439  NS_ENSURE_ARG_POINTER(aJobProgress);
1440 
1441  PRUint16 status;
1442  nsresult rv = aJobProgress->GetStatus(&status);
1443  NS_ENSURE_SUCCESS(rv, rv);
1444 
1445  // This method is the callback for a move-rename process. Simply set the
1446  // event timer until the job completes. This prevents executing
1447  // synchronous move-rename jobs that could cause data loss.
1448 
1449  if (status == sbIJobProgress::STATUS_RUNNING) {
1450  rv = SetEventPumpTimer();
1451  NS_ENSURE_SUCCESS(rv, rv);
1452  }
1453 
1454  return NS_OK;
1455 }
sbStringSet::iterator sbStringSetIter
nsresult StartWatchingFolder()
#define STARTUP_TIMER_DELAY
return NS_OK
_updateCookies aPath
nsresult Rescan()
#define LOG(args)
Interface used to enumerate the items in a media list.
nsresult HandleSessionLoadError()
virtual ~sbWatchFolder()
nsresult GetSongbirdWindow(nsIDOMWindow **aSongbirdWindow)
inArray array
Generic interface for exposing long running jobs to the UI.
nsString Get(const nsAString &aKey, const nsAString &aDefault=SBVoidString())
nsString Format(const nsAString &aKey, nsTArray< nsString > &aParams, const nsAString &aDefault=SBVoidString())
nsresult InitInternal()
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
A brief description of the contents of this interface.
NS_IMPL_ISUPPORTS5(sbWatchFolder, sbIWatchFolder, sbIFileSystemListener, sbIMediaListEnumerationListener, nsITimerCallback, sbIJobProgressListener) sbWatchFolder
const unsigned long INVALID_DIRECTORY
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
const char * propName
const nsIDOMWindow
var bundle
#define CHANGE_DELAY_TIMER_DELAY
NS_DECL_ISUPPORTS NS_DECL_SBIWATCHFOLDER NS_DECL_SBIFILESYSTEMLISTENER NS_DECL_SBIMEDIALISTENUMERATIONLISTENER NS_DECL_NSITIMERCALLBACK NS_DECL_SBIJOBPROGRESSLISTENER nsresult Init()
const unsigned short ENUMERATIONTYPE_SNAPSHOT
This flag means that the list being enumerated is a copy that may become out of date.
nsresult StopWatchingFolder()
function TRACE(s)
nsresult ProcessEventPaths()
Handles finding media files in directories and adding them to a library or list.
nsresult GetFilePathURI(const nsAString &aFilePath, nsIURI **aURIRetVal)
nsresult GetURIArrayForStringPaths(sbStringSet &aPathsSet, nsIArray **aURIs)
#define FLUSH_FS_WATCHER_DELAY
const unsigned long ROOT_PATH_MISSING
Songbird String Bundle Definitions.
sbStringSet::iterator sbStringSetIter
#define EVENT_PUMP_TIMER_DELAY
nsresult ProcessAddedPaths()
nsresult EnumerateItemsByPaths(sbStringSet &aPathSet)
nsresult DecrementIgnoredPathCount(const nsAString &aFilePath, PRBool *aIsIgnoredPath)
std::set< nsString > sbStringSet
nsresult SetEventPumpTimer()
nsresult HandleEventPathList(sbStringSet &aEventPathSet, EProcessType aProcessType)
Implemented to receive notifications from sbIJobProgress interfaces.
Interface that defines a single item of media in the system.
nsresult HandleRootPathMissing()
restoreHistoryPrecursor aCount
attribute sbIDirectoryImportService importer
#define SB_PROPERTY_CONTENTURL
attribute sbIMediacoreTypeSniffer typeSniffer
const unsigned long SESSION_LOAD_ERROR
nsITimerCallback
nsresult SetStartupDelayTimer()
function next()
std::set< nsString, IgnoringCase > sbStringSet