Camino only - Bug 393562: kqueue download folders, not individual downloads. r=kreeger sr=pink
git-svn-id: svn://10.0.0.236/trunk@236307 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -48,14 +48,14 @@
|
||||
// This method will need to return the full path of the file/folder
|
||||
// that is being watched.
|
||||
//
|
||||
-(const char*)representedFilePath;
|
||||
-(NSString*)representedFilePath;
|
||||
|
||||
//
|
||||
// This method gets called when the watcher recieves news that the
|
||||
// watched path has changed.
|
||||
// This method gets called when the watcher recieves news that a file has
|
||||
// been removed at the watched path.
|
||||
// Note: This method will be called on a background thread.
|
||||
//
|
||||
-(void)fileDidChange;
|
||||
-(void)fileRemoved;
|
||||
|
||||
@end
|
||||
|
||||
@@ -66,9 +66,8 @@
|
||||
//
|
||||
@interface FileChangeWatcher : NSObject
|
||||
{
|
||||
@private
|
||||
NSMutableArray* mWatchedFileDelegates; // strong ref
|
||||
NSMutableArray* mWatchedFileDescriptors; // strong ref
|
||||
@private
|
||||
NSMutableDictionary* mWatchedDirectories; // strong ref
|
||||
|
||||
int mQueueFileDesc;
|
||||
BOOL mShouldRunThread;
|
||||
|
||||
@@ -42,13 +42,20 @@
|
||||
#import "unistd.h"
|
||||
#import "fcntl.h"
|
||||
|
||||
const int kSecondPollingInterval = 60;
|
||||
static const int kSecondPollingInterval = 60;
|
||||
|
||||
static const unsigned int kMaxWatchedDirectories = 25;
|
||||
|
||||
// Watched directory dictionary keys
|
||||
static NSString* const kDelegatesKey = @"delegates";
|
||||
static NSString* const kFileDescriptorKey = @"fdes";
|
||||
|
||||
@interface FileChangeWatcher(Private)
|
||||
|
||||
-(void)startPolling;
|
||||
-(void)stopPolling;
|
||||
-(void)pollWatchedDirectories;
|
||||
-(void)directoryChanged:(NSString*)directory;
|
||||
|
||||
@end
|
||||
|
||||
@@ -57,8 +64,7 @@ const int kSecondPollingInterval = 60;
|
||||
-(id)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
mWatchedFileDelegates = [[NSMutableArray alloc] init];
|
||||
mWatchedFileDescriptors = [[NSMutableArray alloc] init];
|
||||
mWatchedDirectories = [[NSMutableDictionary alloc] init];
|
||||
mQueueFileDesc = kqueue();
|
||||
}
|
||||
|
||||
@@ -68,45 +74,66 @@ const int kSecondPollingInterval = 60;
|
||||
-(void)dealloc
|
||||
{
|
||||
close(mQueueFileDesc);
|
||||
[mWatchedFileDelegates release];
|
||||
[mWatchedFileDescriptors release];
|
||||
[mWatchedDirectories release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
-(void)addWatchedFileDelegate:(id<WatchedFileDelegate>)aWatchedFileDelegate
|
||||
{
|
||||
if ([mWatchedFileDelegates containsObject:aWatchedFileDelegate])
|
||||
return;
|
||||
|
||||
int fileDesc = open([aWatchedFileDelegate representedFilePath], O_EVTONLY, 0);
|
||||
if (fileDesc >= 0) {
|
||||
struct timespec nullts = { 0, 0 };
|
||||
struct kevent ev;
|
||||
u_int fflags = NOTE_RENAME | NOTE_WRITE | NOTE_DELETE;
|
||||
|
||||
EV_SET(&ev, fileDesc, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, fflags, 0, (void*)aWatchedFileDelegate);
|
||||
|
||||
[mWatchedFileDelegates addObject:aWatchedFileDelegate];
|
||||
[mWatchedFileDescriptors addObject:[NSNumber numberWithInt:fileDesc]];
|
||||
kevent(mQueueFileDesc, &ev, 1, NULL, 0, &nullts);
|
||||
if (!mShouldRunThread && [mWatchedFileDescriptors count] > 0)
|
||||
[self startPolling];
|
||||
{
|
||||
NSString* parentDirectory =
|
||||
[[aWatchedFileDelegate representedFilePath] stringByDeletingLastPathComponent];
|
||||
NSMutableDictionary* directoryInfo = [mWatchedDirectories objectForKey:parentDirectory];
|
||||
if (directoryInfo) {
|
||||
NSMutableArray* directoryDelegates = [directoryInfo objectForKey:kDelegatesKey];
|
||||
if (![directoryDelegates containsObject:aWatchedFileDelegate])
|
||||
[directoryDelegates addObject:aWatchedFileDelegate];
|
||||
}
|
||||
else {
|
||||
// We cap the number of kqueues so we don't end up sucking down all the
|
||||
// available file descriptors.
|
||||
if ([mWatchedDirectories count] >= kMaxWatchedDirectories)
|
||||
return;
|
||||
|
||||
int fileDesc = open([parentDirectory fileSystemRepresentation], O_EVTONLY, 0);
|
||||
if (fileDesc >= 0) {
|
||||
struct timespec nullts = { 0, 0 };
|
||||
struct kevent ev;
|
||||
u_int fflags = NOTE_RENAME | NOTE_WRITE | NOTE_DELETE;
|
||||
|
||||
EV_SET(&ev, fileDesc, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, fflags,
|
||||
0, (void*)parentDirectory);
|
||||
|
||||
kevent(mQueueFileDesc, &ev, 1, NULL, 0, &nullts);
|
||||
if (!mShouldRunThread)
|
||||
[self startPolling];
|
||||
|
||||
directoryInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSMutableArray arrayWithObject:aWatchedFileDelegate], kDelegatesKey,
|
||||
[NSNumber numberWithInt:fileDesc], kFileDescriptorKey,
|
||||
nil];
|
||||
[mWatchedDirectories setObject:directoryInfo forKey:parentDirectory];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)removeWatchedFileDelegate:(id<WatchedFileDelegate>)aWatchedFileDelegate
|
||||
{
|
||||
int index = [mWatchedFileDelegates indexOfObject:aWatchedFileDelegate];
|
||||
if (index == NSNotFound)
|
||||
NSString* parentDirectory =
|
||||
[[aWatchedFileDelegate representedFilePath] stringByDeletingLastPathComponent];
|
||||
NSMutableDictionary* directoryInfo = [mWatchedDirectories objectForKey:parentDirectory];
|
||||
NSMutableArray* directoryDelegates = [directoryInfo objectForKey:kDelegatesKey];
|
||||
if (![directoryDelegates containsObject:aWatchedFileDelegate])
|
||||
return;
|
||||
|
||||
int fileDesc = [[mWatchedFileDescriptors objectAtIndex:index] intValue];
|
||||
[mWatchedFileDelegates removeObjectAtIndex:index];
|
||||
[mWatchedFileDescriptors removeObjectAtIndex:index];
|
||||
close(fileDesc);
|
||||
|
||||
if (mShouldRunThread && [mWatchedFileDelegates count] == 0)
|
||||
[self stopPolling];
|
||||
|
||||
int fileDesc = [[directoryInfo objectForKey:kFileDescriptorKey] intValue];
|
||||
[directoryDelegates removeObject:aWatchedFileDelegate];
|
||||
|
||||
if ([directoryDelegates count] == 0) {
|
||||
close(fileDesc);
|
||||
[mWatchedDirectories removeObjectForKey:parentDirectory];
|
||||
if (mShouldRunThread && [mWatchedDirectories count] == 0)
|
||||
[self stopPolling];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)startPolling
|
||||
@@ -149,7 +176,7 @@ const int kSecondPollingInterval = 60;
|
||||
struct kevent event;
|
||||
int n = kevent(mQueueFileDesc, NULL, 0, &event, 1, &timeInterval);
|
||||
if (n > 0 && event.filter == EVFILT_VNODE && event.fflags) {
|
||||
[(id<WatchedFileDelegate>)event.udata fileDidChange];
|
||||
[self directoryChanged:(NSString*)event.udata];
|
||||
}
|
||||
}
|
||||
@catch (id exception) {
|
||||
@@ -160,4 +187,20 @@ const int kSecondPollingInterval = 60;
|
||||
}
|
||||
}
|
||||
|
||||
-(void)directoryChanged:(NSString*)directory
|
||||
{
|
||||
NSSet* existingFiles =
|
||||
[NSSet setWithArray:[[NSFileManager defaultManager] directoryContentsAtPath:directory]];
|
||||
|
||||
NSMutableDictionary* directoryInfo = [mWatchedDirectories objectForKey:directory];
|
||||
NSMutableArray* directoryDelegates = [directoryInfo objectForKey:kDelegatesKey];
|
||||
NSEnumerator* delegateEnumerator = [directoryDelegates objectEnumerator];
|
||||
id fileDelegate;
|
||||
while ((fileDelegate = [delegateEnumerator nextObject])) {
|
||||
NSString* filename = [[fileDelegate representedFilePath] lastPathComponent];
|
||||
if (![existingFiles member:filename])
|
||||
[fileDelegate fileRemoved];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -110,8 +110,8 @@
|
||||
-(int)numDownloadsInProgress;
|
||||
-(void)clearAllDownloads;
|
||||
-(void)didStartDownload:(ProgressViewController*)progressDisplay;
|
||||
-(void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status;
|
||||
-(void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay suppressRedraw:(BOOL)suppressRedraw;
|
||||
-(void)didEndDownload:(ProgressViewController*)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status;
|
||||
-(void)removeDownload:(ProgressViewController*)progressDisplay suppressRedraw:(BOOL)suppressRedraw;
|
||||
-(NSApplicationTerminateReply)allowTerminate;
|
||||
-(void)applicationWillTerminate;
|
||||
-(void)saveProgressViewControllers;
|
||||
|
||||
@@ -240,8 +240,13 @@ static id gSharedProgressController = nil;
|
||||
|
||||
// now remove stuff, don't need to check if active, the toolbar/menu validates
|
||||
for (unsigned int i = 0; i < selectedCount; i++) {
|
||||
[[selected objectAtIndex:i] deleteFile:sender];
|
||||
[self removeDownload:[selected objectAtIndex:i] suppressRedraw:YES];
|
||||
ProgressViewController* progressController = [selected objectAtIndex:i];
|
||||
// if the file was moved without the controller noticing, the move to trash
|
||||
// will fail, but cause it to notice that the file is missing. Leave it in
|
||||
// the list (now showing missing) so the user doesn't think it was
|
||||
// successfully trashed.
|
||||
if ([progressController deleteFile])
|
||||
[self removeDownload:progressController suppressRedraw:YES];
|
||||
}
|
||||
|
||||
[self rebuildViews];
|
||||
@@ -558,7 +563,7 @@ static id gSharedProgressController = nil;
|
||||
[self scrollIntoView:progressDisplay];
|
||||
}
|
||||
|
||||
-(void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status
|
||||
-(void)didEndDownload:(ProgressViewController*)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status
|
||||
{
|
||||
[self rebuildViews]; // to swap in the completed view
|
||||
[[[self window] toolbar] validateVisibleItems]; // force update which doesn't always happen
|
||||
@@ -649,7 +654,7 @@ static id gSharedProgressController = nil;
|
||||
{
|
||||
}
|
||||
|
||||
-(void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay suppressRedraw:(BOOL)suppressRedraw
|
||||
-(void)removeDownload:(ProgressViewController*)progressDisplay suppressRedraw:(BOOL)suppressRedraw
|
||||
{
|
||||
[progressDisplay displayWillBeRemoved];
|
||||
// This is sometimes called by code that thinks it can continue
|
||||
|
||||
@@ -115,10 +115,11 @@ const int kRemoveUponSuccessfulDownloadPrefValue = 2;
|
||||
-(IBAction)remove:(id)sender;
|
||||
-(IBAction)reveal:(id)sender;
|
||||
-(IBAction)open:(id)sender;
|
||||
-(IBAction)deleteFile:(id)sender;
|
||||
-(IBAction)pause:(id)sender;
|
||||
-(IBAction)resume:(id)sender;
|
||||
|
||||
-(BOOL)deleteFile;
|
||||
|
||||
-(BOOL)isActive;
|
||||
-(BOOL)isCanceled;
|
||||
-(BOOL)isSelected;
|
||||
@@ -127,7 +128,6 @@ const int kRemoveUponSuccessfulDownloadPrefValue = 2;
|
||||
|
||||
-(BOOL)hasSucceeded;
|
||||
|
||||
-(void)checkFileExists;
|
||||
-(void)setSelected:(BOOL)inSelected;
|
||||
|
||||
-(NSDictionary*)downloadInfoDictionary;
|
||||
|
||||
@@ -64,6 +64,7 @@ enum {
|
||||
-(void)viewDidLoad;
|
||||
-(void)setupFileSystemNotification;
|
||||
-(void)unsubscribeFileSystemNotification;
|
||||
-(void)checkFileExists;
|
||||
-(void)refreshDownloadInfo;
|
||||
-(void)launchFileIfAppropriate;
|
||||
-(void)setProgressViewFromDictionary:(NSDictionary*)aDict;
|
||||
@@ -293,15 +294,21 @@ enum {
|
||||
|
||||
-(IBAction)reveal:(id)sender
|
||||
{
|
||||
if (![[NSWorkspace sharedWorkspace] selectFile:mDestPath
|
||||
inFileViewerRootedAtPath:[mDestPath stringByDeletingLastPathComponent]]) {
|
||||
[self checkFileExists];
|
||||
if (!mFileExists ||
|
||||
![[NSWorkspace sharedWorkspace] selectFile:mDestPath
|
||||
inFileViewerRootedAtPath:[mDestPath stringByDeletingLastPathComponent]])
|
||||
{
|
||||
NSBeep();
|
||||
}
|
||||
}
|
||||
|
||||
-(IBAction)open:(id)sender
|
||||
{
|
||||
if (![[NSWorkspace sharedWorkspace] openFile:mDestPath]) {
|
||||
[self checkFileExists];
|
||||
if (!mFileExists ||
|
||||
![[NSWorkspace sharedWorkspace] openFile:mDestPath])
|
||||
{
|
||||
NSBeep();
|
||||
}
|
||||
return;
|
||||
@@ -339,9 +346,14 @@ enum {
|
||||
[self refreshDownloadInfo];
|
||||
}
|
||||
|
||||
// Delete the file we downloaded and remove this item from the list
|
||||
-(IBAction)deleteFile:(id)sender
|
||||
// Move the downloaded file to the trash.
|
||||
-(BOOL)deleteFile
|
||||
{
|
||||
[self checkFileExists];
|
||||
if (!mFileExists) {
|
||||
NSBeep();
|
||||
return NO;
|
||||
}
|
||||
NSString* fileName = [mDestPath lastPathComponent];
|
||||
NSString* path = [mDestPath stringByDeletingLastPathComponent];
|
||||
|
||||
@@ -350,6 +362,7 @@ enum {
|
||||
destination:@""
|
||||
files:[NSArray arrayWithObject:fileName]
|
||||
tag:nil];
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void)downloadDidEnd
|
||||
@@ -590,12 +603,12 @@ enum {
|
||||
return dict;
|
||||
}
|
||||
|
||||
-(const char*)representedFilePath
|
||||
-(NSString*)representedFilePath
|
||||
{
|
||||
return [mDestPath fileSystemRepresentation];
|
||||
return mDestPath;
|
||||
}
|
||||
|
||||
-(void)fileDidChange
|
||||
-(void)fileRemoved
|
||||
{
|
||||
// This method gets called on a background thread, so switch the |checkFileExists| call to the main thread.
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
Reference in New Issue
Block a user