Commits

ryanackley committed 43e5547

Added file watching for os x

Comments (0)

Files changed (7)

+//
+//  FileWatcher.h
+//  FireBreath
+//
+//  Created by Ryan Ackley on 5/17/12.
+//  Copyright (c) 2012 Benryan Software Inc. All rights reserved.
+//
+
+#ifndef FireBreath_FileWatcher_h
+#define FireBreath_FileWatcher_h
+
+#include <string>
+#include "APITypes.h"
+
+class FileWatcher
+{
+public:
+    static FileWatcher* get();
+    virtual void WatchFile(std::string key, std::string path, FB::JSObjectPtr callback)=0;
+    virtual void StopWatching(std::string key)=0;
+
+protected:
+    FileWatcher(){}
+    virtual ~FileWatcher(){}
+};
+
+#endif

Mac/FileWatcherMac.h

+//
+//  FileWatcherMac.h
+//  FireBreath
+//
+//  Created by Ryan Ackley on 5/17/12.
+//  Copyright (c) 2012 Benryan Software Inc. All rights reserved.
+//
+
+#ifndef FileWatcherMac_h__
+#define FileWatcherMac_h__
+
+#include <map>
+#include <CoreServices/CoreServices.h>
+#include "../FileWatcher.h"
+#import "FileWatcherTask.h"
+
+class FileWatcherMac : public FileWatcher
+{
+public:
+    void WatchFile(std::string key, std::string path, FB::JSObjectPtr callback);
+    void StopWatching(std::string key);
+    
+protected:
+    FileWatcherMac(){};
+    ~FileWatcherMac(){};
+    friend class FileWatcher;
+private:
+    std::map<std::string,FileWatcherTask*> watchers;
+};
+#endif

Mac/FileWatcherMac.mm

+//
+//  FileWatcherMac.mm
+//  FireBreath
+//
+//  Created by Ryan Ackley on 5/17/12.
+//  Copyright (c) 2012 Benryan Software Inc. All rights reserved.
+//
+#include "FileWatcherMac.h"
+
+FileWatcher* FileWatcher::get()
+{
+    static FileWatcherMac inst;
+    return &inst;
+}
+
+void FileWatcherMac::WatchFile(std::string key, std::string path, FB::JSObjectPtr callback)
+{
+    NSAutoreleasePool * pool = [NSAutoreleasePool new];
+    FileWatcherTask* task = [[FileWatcherTask alloc] initWithCallback:callback];
+    NSString* strPath = [NSString stringWithUTF8String:path.c_str()];
+    [task performSelectorInBackground:@selector(listenForFileEvents:) withObject:strPath];
+    watchers[key] = task;
+    [pool release];
+}
+
+void FileWatcherMac::StopWatching(std::string key)
+{
+    NSAutoreleasePool * pool = [NSAutoreleasePool new];
+    FileWatcherTask* task = watchers[key];
+    if (task)
+    {
+        watchers.erase(key);
+        [task stopWatching];
+        [task release];
+    }
+    [pool release]; 
+}

Mac/FileWatcherTask.h

+//
+//  FileWatcherTask.h
+//  FireBreath
+//
+//  Created by Ryan Ackley on 5/18/12.
+//  Copyright (c) 2012 Benryan Software Inc. All rights reserved.
+//
+
+#ifndef FileWatcherTask_h__
+#define FileWatcherTask_h__
+
+#import <Foundation/Foundation.h>
+#include "JSObject.h"
+
+@interface FileWatcherTask : NSObject
+{
+    NSMutableDictionary* pathModificationDates;
+    NSDate* appStartedTimestamp;
+    NSFileManager* fm;
+    FSEventStreamRef stream;
+    FB::JSObjectPtr callback;
+    CFRunLoopRef runLoop;
+}
+-(id)initWithCallback:(FB::JSObjectPtr)obj;
+-(void)listenForFileEvents:(NSString*)path;
+-(void)postCallback:(NSString*)path;
+-(void)stopWatching;
+@end
+
+#endif

Mac/FileWatcherTask.mm

+ //
+//  FileWatcherTask.m
+//  FireBreath
+//
+//  Created by Ryan Ackley on 5/18/12.
+//  Copyright (c) 2012 Benryan Software Inc. All rights reserved.
+//
+
+#import "FileWatcherTask.h"
+#include "variant_list.h"
+
+void fsevents_callback(ConstFSEventStreamRef streamRef,
+                       void *userData,
+                       size_t numEvents,
+                       void *eventPaths,
+                       const FSEventStreamEventFlags eventFlags[],
+                       const FSEventStreamEventId eventIds[])
+{
+    FileWatcherTask *fw = (FileWatcherTask *)userData;
+	size_t i;
+	for(i=0; i<numEvents; i++){
+        [fw postCallback:[(NSArray *)eventPaths objectAtIndex:i]];
+	}
+    
+}
+
+@implementation FileWatcherTask
+
+-(id)initWithCallback:(FB::JSObjectPtr)obj
+{
+    self = [super init];
+    callback = obj;
+    fm = [NSFileManager defaultManager];
+    return self;
+    
+}
+
+-(void)listenForFileEvents:(NSString*)path
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSArray *pathsToWatch = [NSArray arrayWithObject:path];
+    appStartedTimestamp = [NSDate date];
+    
+    void *appPointer = (void *)self;
+    FSEventStreamContext context = {0, appPointer, NULL, NULL, NULL};
+    NSTimeInterval latency = 1.0;
+    stream = FSEventStreamCreate(NULL,
+                                 &fsevents_callback,
+                                 &context,
+                                 (CFArrayRef) pathsToWatch,
+                                 kFSEventStreamEventIdSinceNow, 
+                                 (CFAbsoluteTime) latency,
+                                 kFSEventStreamCreateFlagUseCFTypes
+                                 );
+    runLoop = CFRunLoopGetCurrent();
+    FSEventStreamScheduleWithRunLoop(stream, runLoop, kCFRunLoopDefaultMode);
+	FSEventStreamStart(stream);
+    CFRunLoopRun();
+    [pool release];
+}
+
+-(void)postCallback:(NSString*)path
+{
+    
+    NSArray *contents = [fm directoryContentsAtPath:path];
+    NSString* fullPath = nil;
+    
+    for(NSString* node in contents) 
+    {
+        fullPath = [NSString stringWithFormat:@"%@%@",path,node];
+        
+        NSDictionary *fileAttributes = [fm attributesOfItemAtPath:fullPath error:NULL];
+        NSDate *fileModDate =
+        [fileAttributes objectForKey:NSFileModificationDate];
+        if([fileModDate compare:[self lastModificationDateForPath:path]] == NSOrderedDescending) 
+        {
+            std::string stdPath = [fullPath UTF8String];
+            callback->Invoke("",FB::variant_list_of(stdPath));
+        }
+    }
+    [self updateLastModificationDateForPath:path];
+}
+
+- (void)updateLastModificationDateForPath: (NSString *)path
+{
+	[pathModificationDates setObject:[NSDate date] forKey:path];
+}
+
+- (void)stopWatching
+{
+    CFRunLoopStop(runLoop);
+}
+
+- (NSDate *)lastModificationDateForPath: (NSString *)path
+{
+	if(nil != [pathModificationDates valueForKey:path]) {
+		return [pathModificationDates valueForKey:path];
+	}
+	else{
+		return appStartedTimestamp;
+	}
+}
+
+@end

NPAPIFileIOforChromeAPI.cpp

 #include "NPObjectAPI.h"
 #include "NpapiBrowserHost.h"
 #include "DialogManager.h"
+#include "FileWatcher.h"
 
 using namespace boost::filesystem;
 using namespace FB;
 {
     callback->Invoke("",FB::variant_list_of(path));
 }
+void NPAPIFileIOforChromeAPI::watchDirectory(std::string key, std::string path, JSObjectPtr callback)
+{
+    FileWatcher* watcher = FileWatcher::get();
+    watcher->WatchFile(key, path, callback);
+}
+void NPAPIFileIOforChromeAPI::stopWatching(std::string key)
+{
+    FileWatcher* watcher = FileWatcher::get();
+    watcher->StopWatching(key);
+}
 std::string NPAPIFileIOforChromeAPI::getChromeDataDir(std::string version)
 {
 #if defined __MACH__ && defined __APPLE__

NPAPIFileIOforChromeAPI.h

         registerMethod("testEvent", make_method(this, &NPAPIFileIOforChromeAPI::testEvent));
         //registerMethod("saveToFileUrl",      make_method(this, &NPAPIFileIOforChromeAPI::saveToFileUrl));
         registerMethod("launchFileSelect",     make_method(this, &NPAPIFileIOforChromeAPI::launchFileSelect));
+        registerMethod("watchDirectory",     make_method(this, &NPAPIFileIOforChromeAPI::watchDirectory));
+        registerMethod("stopWatching",     make_method(this, &NPAPIFileIOforChromeAPI::stopWatching));
         registerMethod("createDirectory",      make_method(this, &NPAPIFileIOforChromeAPI::createDirectory));
         registerMethod("saveBlobToFile",      make_method(this, &NPAPIFileIOforChromeAPI::saveBlobToFile));
         registerMethod("getDirEntries",      make_method(this, &NPAPIFileIOforChromeAPI::getDirEntries));
     bool fileExists(std::string path);
     bool removeRecursively(std::string path);
     void launchFileSelect(FB::JSObjectPtr callback); 
+    void watchDirectory(std::string key, std::string path, FB::JSObjectPtr callback);
+    void stopWatching(std::string key);
     std::string getChromeDataDir(std::string version);
 
 private:
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.