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