diff --git a/Atom.xcodeproj/project.pbxproj b/Atom.xcodeproj/project.pbxproj index afc739ad0..c799a3bef 100644 --- a/Atom.xcodeproj/project.pbxproj +++ b/Atom.xcodeproj/project.pbxproj @@ -83,7 +83,7 @@ 0487CDA014FEE1DA0045E5E3 /* liblibcef_wrapper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0487CC8114FEDF990045E5E3 /* liblibcef_wrapper.a */; }; 0487D15F14FEE7880045E5E3 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 0487D15E14FEE7880045E5E3 /* Resources */; }; 0487D16014FEE78E0045E5E3 /* Resources in Copy Chrome Resources */ = {isa = PBXBuildFile; fileRef = 0487D15E14FEE7880045E5E3 /* Resources */; }; - 048A2FC7154870DC0051715C /* PathWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 048A2FC6154870DC0051715C /* PathWatcher.m */; }; + 048A2FC7154870DC0051715C /* PathWatcher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 048A2FC6154870DC0051715C /* PathWatcher.mm */; }; 04E1DDDD152A0941001A9D07 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04E1DDDC152A0941001A9D07 /* Sparkle.framework */; }; 04E1DDDF152A09C3001A9D07 /* Sparkle.framework in Copy Sparkle Framework */ = {isa = PBXBuildFile; fileRef = 04E1DDDC152A0941001A9D07 /* Sparkle.framework */; }; /* End PBXBuildFile section */ @@ -394,7 +394,7 @@ 0487CD6C14FEE0EC0045E5E3 /* libcef.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcef.dylib; path = frameworks/libcef.dylib; sourceTree = ""; }; 0487D15E14FEE7880045E5E3 /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = ""; }; 048A2FC5154870DC0051715C /* PathWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathWatcher.h; sourceTree = ""; }; - 048A2FC6154870DC0051715C /* PathWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PathWatcher.m; sourceTree = ""; }; + 048A2FC6154870DC0051715C /* PathWatcher.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PathWatcher.mm; sourceTree = ""; }; 04E1DDDC152A0941001A9D07 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = frameworks/Sparkle.framework; sourceTree = ""; }; 04F21A2615644AC10083F6D4 /* ResourceConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ResourceConfig.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ @@ -810,7 +810,7 @@ 0487C93F14FED6090045E5E3 /* AtomController.mm */, 042180E614FF080D00DF25EA /* BrowserDelegate.h */, 048A2FC5154870DC0051715C /* PathWatcher.h */, - 048A2FC6154870DC0051715C /* PathWatcher.m */, + 048A2FC6154870DC0051715C /* PathWatcher.mm */, 0487C94014FED6090045E5E3 /* client_handler.h */, 0487C94114FED6090045E5E3 /* client_handler.mm */, 0487C94314FED6090045E5E3 /* native_handler.h */, @@ -963,7 +963,7 @@ 0487CD9614FEE1360045E5E3 /* client_handler.mm in Sources */, 0487CD9714FEE1380045E5E3 /* main.mm in Sources */, 0487CD9814FEE13B0045E5E3 /* native_handler.mm in Sources */, - 048A2FC7154870DC0051715C /* PathWatcher.m in Sources */, + 048A2FC7154870DC0051715C /* PathWatcher.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Atom/src/AtomController.mm b/Atom/src/AtomController.mm index bb323a7b9..e118054bc 100644 --- a/Atom/src/AtomController.mm +++ b/Atom/src/AtomController.mm @@ -88,8 +88,6 @@ #pragma mark BrowserDelegate - (void)loadStart { - [PathWatcher unwatchAll]; - CefRefPtr context = _clientHandler->GetBrowser()->GetMainFrame()->GetV8Context(); CefRefPtr global = context->GetGlobal(); diff --git a/Atom/src/PathWatcher.h b/Atom/src/PathWatcher.h index e9b64bbcb..a26d33916 100644 --- a/Atom/src/PathWatcher.h +++ b/Atom/src/PathWatcher.h @@ -1,15 +1,24 @@ +#import "include/cef_base.h" +#import "include/cef_v8.h" #import typedef void (^WatchCallback)(NSArray *); @interface PathWatcher : NSObject { int _kq; + CefRefPtr _context; NSMutableDictionary *_fileDescriptorsByPath; NSMutableDictionary *_callbacksByFileDescriptor; + + bool _keepWatching; } -+ (NSString *)watchPath:(NSString *)path callback:(WatchCallback)callback; -+ (void)unwatchPath:(NSString *)path callbackId:(NSString *)callbackId error:(NSError **)error; -+ (void)unwatchAll; ++ (PathWatcher *)pathWatcherForContext:(CefRefPtr)context; ++ (void)removePathWatcherForContext:(CefRefPtr)context; + +- (id)initWithContext:(CefRefPtr)context; +- (NSString *)watchPath:(NSString *)path callback:(WatchCallback)callback; +- (void)unwatchPath:(NSString *)path callbackId:(NSString *)callbackId error:(NSError **)error; + @end diff --git a/Atom/src/PathWatcher.m b/Atom/src/PathWatcher.mm similarity index 70% rename from Atom/src/PathWatcher.m rename to Atom/src/PathWatcher.mm index 4320df688..d74b68149 100644 --- a/Atom/src/PathWatcher.m +++ b/Atom/src/PathWatcher.mm @@ -4,33 +4,49 @@ #import #import - +static NSMutableArray *gPathWatchers; @interface PathWatcher () -- (NSString *)watchPath:(NSString *)path callback:(WatchCallback)callback; +- (bool)usesContext:(CefRefPtr)context; - (void)watchFileDescriptor:(int)fd; -- (void)unwatchPath:(NSString *)path callbackId:(NSString *)callbackId error:(NSError **)error; -- (void)unwatchAll; +- (void)stopWatching; @end @implementation PathWatcher -+ (id)instance { - static PathWatcher *pathWatcher; - if (!pathWatcher) pathWatcher = [[PathWatcher alloc] init]; ++ (PathWatcher *)pathWatcherForContext:(CefRefPtr)context { + if (!gPathWatchers) gPathWatchers = [[NSMutableArray alloc] init]; + + PathWatcher *pathWatcher = nil; + for (PathWatcher *p in gPathWatchers) { + if ([p usesContext:context]) { + pathWatcher = p; + break; + } + } + + if (!pathWatcher) { + pathWatcher = [[[PathWatcher alloc] initWithContext:context] autorelease]; + [gPathWatchers addObject:pathWatcher]; + } + return pathWatcher; } -+ (NSString *)watchPath:(NSString *)path callback:(WatchCallback)callback { - return [[self instance] watchPath:path callback:callback]; -} ++ (void)removePathWatcherForContext:(CefRefPtr)context { + PathWatcher *pathWatcher = nil; + for (PathWatcher *p in gPathWatchers) { + if ([p usesContext:context]) { + pathWatcher = p; + break; + } + } + + if (pathWatcher) { + [pathWatcher stopWatching]; + [gPathWatchers removeObject:pathWatcher]; + } -+ (void)unwatchPath:(NSString *)path callbackId:(NSString *)callbackId error:(NSError **)error { - return [[self instance] unwatchPath:path callbackId:callbackId error:error]; -} - -+ (void)unwatchAll { - return [[self instance] unwatchAll]; } - (void)dealloc { @@ -39,16 +55,18 @@ close([fdNumber intValue]); } [_callbacksByFileDescriptor release]; - + _context = nil; [super dealloc]; } -- (id)init { +- (id)initWithContext:(CefRefPtr)context { self = [super init]; + _keepWatching = YES; _callbacksByFileDescriptor = [[NSMutableDictionary alloc] init]; _fileDescriptorsByPath = [[NSMutableDictionary alloc] init]; _kq = kqueue(); + _context = context; if (_kq == -1) { [NSException raise:@"PathWatcher" format:@"Could not create kqueue"]; @@ -58,6 +76,15 @@ return self; } +- (bool)usesContext:(CefRefPtr)context { + return _context->IsSame(context); +} + +- (void)stopWatching { + [self unwatchAll]; + _keepWatching = false; +} + - (NSString *)watchPath:(NSString *)path callback:(WatchCallback)callback { path = [path stringByStandardizingPath]; NSString *callbackId; @@ -131,7 +158,7 @@ struct kevent event; int filter = EVFILT_VNODE; int flags = EV_ADD | EV_ENABLE | EV_CLEAR; - int filterFlags = NOTE_WRITE; + int filterFlags = NOTE_WRITE | NOTE_DELETE | NOTE_ATTRIB | NOTE_EXTEND | NOTE_RENAME | NOTE_REVOKE; EV_SET(&event, fd, filter, flags, filterFlags, 0, 0); kevent(_kq, &event, 1, NULL, 0, &timeout); } @@ -141,7 +168,7 @@ struct kevent event; struct timespec timeout = { 5, 0 }; // 5 seconds timeout. - while (true) { + while (_keepWatching) { int numberOfEvents = kevent(_kq, NULL, 0, &event, 1, &timeout); if (numberOfEvents < 0) { @@ -152,8 +179,8 @@ } NSMutableArray *eventFlags = [NSMutableArray array]; - - if (event.fflags & NOTE_WRITE) { + + if (event.fflags & NOTE_WRITE || [self isAtomicWrite:event]) { [eventFlags addObject:@"modified"]; } @@ -169,9 +196,21 @@ } } } - - [self release]; } } +- (bool)isAtomicWrite:(struct kevent)event { + if (!event.fflags & NOTE_DELETE) return NO; + + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *path = nil; + for (path in [_fileDescriptorsByPath allKeys]) { + if ([[_fileDescriptorsByPath objectForKey:path] unsignedLongValue] == event.ident) { + return [fm fileExistsAtPath:path]; + } + } + + return NO; +} + @end diff --git a/Atom/src/client_handler.h b/Atom/src/client_handler.h index a223aeac9..a95f666a6 100755 --- a/Atom/src/client_handler.h +++ b/Atom/src/client_handler.h @@ -107,7 +107,9 @@ public: virtual void OnContextCreated(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) OVERRIDE; - + virtual void OnContextReleased(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) OVERRIDE; // CefDragHandler methods. virtual bool OnDragStart(CefRefPtr browser, CefRefPtr dragData, diff --git a/Atom/src/client_handler.mm b/Atom/src/client_handler.mm index d2b60b675..2b501aba5 100755 --- a/Atom/src/client_handler.mm +++ b/Atom/src/client_handler.mm @@ -1,6 +1,7 @@ #import "include/cef_base.h" #import "client_handler.h" #import "AtomController.h" +#import "PathWatcher.h" #import #import @@ -174,6 +175,13 @@ void ClientHandler::OnContextCreated(CefRefPtr browser, REQUIRE_UI_THREAD(); } +void ClientHandler::OnContextReleased(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) { + REQUIRE_UI_THREAD(); + [PathWatcher removePathWatcherForContext:context]; +} + bool ClientHandler::OnDragStart(CefRefPtr browser, CefRefPtr dragData, DragOperationsMask mask) diff --git a/Atom/src/native_handler.mm b/Atom/src/native_handler.mm index 2dec7f095..5dad0e98c 100644 --- a/Atom/src/native_handler.mm +++ b/Atom/src/native_handler.mm @@ -319,8 +319,9 @@ bool NativeHandler::Execute(const CefString& name, context->Exit(); }; - - NSString *watchId = [PathWatcher watchPath:path callback:[[callback copy] autorelease]]; + + PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()]; + NSString *watchId = [pathWatcher watchPath:path callback:[[callback copy] autorelease]]; retval = CefV8Value::CreateString([watchId UTF8String]); return true; @@ -329,7 +330,8 @@ bool NativeHandler::Execute(const CefString& name, NSString *path = stringFromCefV8Value(arguments[0]); NSString *callbackId = stringFromCefV8Value(arguments[1]); NSError *error = nil; - [PathWatcher unwatchPath:path callbackId:callbackId error:&error]; + PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()]; + [pathWatcher unwatchPath:path callbackId:callbackId error:&error]; if (error) { exception = [[error localizedDescription] UTF8String];