Handling CGEventTaps in an NPAPI plugin - cocoa

I'm trying to create an NPAPI plugin to listen to the Media Keys on a macbook and pass that to javascript to control things like pandora or soundcloud. I'm using Spotify's SPMediaKeyTap library, which just wraps CGEventTap running on a separate thread.
My problem is that I use npn_invoke to call back to javascript. This works normally, but when it's triggered from the CGEventTap callback, it crashes the plugin. I realize this needs to be run from the plugin thread, and I've tried to pass it back to the main thread both by using [NSObject performSelectorOnMainThread] and [NSObject performSelector:onThread] with the thread i've stored away in the main plugin threads create method. Both of these solutions still crash on any npn call. Is there anything else that goes on when handling a CGEventTap event that causes state to be invalid for NPN browser interaction calls?

Don't try to second guess the threading model by saving the thread like you are; just use performSelectorOnMainThread to call NPN methods. I do this all the time and it works fine, so I'm guessing that something with your method of cross-thread marshalling isn't working the way it needs to.

Related

How to Terminate/Reset an bundle XPC helper?

Does anyone know how to terminate or reset an XPC helper?
According to the Apple documentation, launchd takes care of the XPC helper if it crashes. But there is no indication of what to do, if you need to interrupt or cancel a running helper.
For instance, if I have an XPC client that is rendering a 32-bit QuickTime movie, how do I get my 64-bit "parent" application to signal the XPC helper to cancel the job and clean up?
Also, What is the proper way for an XPC helper app to handle a parent that has "Quit"?
Currently, to terminate on the parent app's side, I am using (NSXPCConnection):
(void)suspend
(void)invalidate
These seem to close off the connection. But I am not seeing any evidence that the helper app is paying attention.
Thanks in advance!
Your question doesn’t seem to ask for the proper handling of any crashes of the helper. Instead, you seem to simply need a way to properly tell the helper to terminate itself. If this is correct then please read on for a solution. At least if you still need one three years after asking…
You didn’t specify a language to use so please be aware my examples are written in Swift.
As part of your `NSXPCConnection* you had to define a protocol to be used for information sharing between app and helper. In this protocol you could add a simple function like this:
terminateHelper(withReply reply: (String) -> Void) {
reply("Terminating Helper.")
exit(0)
}
This function reports a message string back to the main app using the supplied closure and then terminates itself using the exit system call.
From your main app, you would call this function like this:
if let helper = helperConnection.remoteObjectProxyWithErrorHandler({ (error) in
let e = error as NSError
print("Helper communication failed. Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
}) as? HelperProtocol {
helper.terminateHelper(withReply: { (replyString) in
print(replyString)
})
}
Please not that launchd will immediately restart the terminated helper app despite the fact that it hasn’t crashed but was gracefully terminated instead. This will, however, guarantee that the helper returns to an initialized state with all previous helper processing being stopped.
If you suspend or invalidate the way you put in your question then you only cancel the XPC connection. But neither suspending nor invalidating the connection will send a message of any kind to the helper. The helper itself will simply see that the connection is suspended or invalidated without knowing anything about the reason.
Hope this gives you at least an idea of how to proceed with your problem.

How to show a Carbon dialog as part of a plugin for an application with an existing event loop?

I'm writing a plugin for an application and need to use Carbon to show a dialog. I have everything set up including the event handler, but I cannot possibly call RunApplicationEventLoop() because this would stall the host application.
How can I fix this? Will I need to create a separate thread and call RunApplicationEventLoop() from there?
-Joe
What makes you think you need to call RunApplicationEventLoop? The host app is presumably running an event loop, probably either using RunApplicationEventLoop or NSApplicationMain. By the way, would your dialog be modal? Modal is easier.

MACOSX - When calling -finishSyncing on an ISyncSessionDriver the app makes repeated calls to the delegate

I'm using Sync Services in my application. I'm using the normal way of getting the contacts from the address book (using sync services).
I want to prematurely end a sync session if the user decides to do that, therefore, when the user presses the "cancel" button, I make a call to [driver finishSyncing]
Attached to the ISyncSessionDriver is a delegate which deals with delegate methods typical of a sync session. One of those methods is - (BOOL)sessionDriver:(ISyncSessionDriver *)sender willFinishSessionAndReturnError:(NSError **)outError
The problem is that when calling finishSyncing, the sessionDriver:willFinishSessionAndReturnError: gets repeatedly called, not just once, but hunderds of times. Eventually it will throw an error.
So, how could I fix this, or what better debugging can I do to figure out what the problem is?
Thanks
Use instead - (void)cancelSyncing.
Make sure to release the receiver soon afterward because you cannot continue using a canceled session.

How can a Mac app determine the method used to launch it?

I have a Mac OS X application that is also a protocol handler (just as, for example, Safari is a protocol handler for the HTTP and HTTPS protocols). So when a user clicks a link of the form myscheme://some-kind-of-info in any application at all, my application launches to handle the link.
Now I need to be able to determine if the application was launched by such a link click, or if it was launched by any other method. In other words, it was launched by any method besides a link click. (In those cases, I want the app to stay open, but if it was launched by a link it should quit and ignore the link. This way it only operates when already running.)
Is there some way within the app at startup to introspect and find out that it was launched by a standard method rather than by an AppleScript GetURL event? I'd like to find out through a documented method, rather than - for example - just have my app only open these links after it's been running for a half a second.
You can register a handler for each of the possible Apple Events you'll get on launch, and make note of which one you receive first.
If the application is launched without documents, you'll get kAEOpenApplication.
If it's launched with documents, you'll get kAEOpenDocuments (or
kAEPrintDocuments).
If it's launched with a URL, then (obviously) you'll get kAEGetURL.
There's also kAEOpenContents, but I wasn't able to trigger it easily in my test app; it's probably worth supporting no matter what.
How Cocoa Applications Handle Apple Events documents all of this stuff.
There is one error in there, though; it says that AppleScript's "launch" will send kAEOpenApplication. It won't, it'll send ascr/noop (kASAppleScriptSuite/kASLaunchEvent, defined in ASRegistry.h). I couldn't get the usual Cocoa event handler mechanism to trap this event, so you may need to do some more digging there.
One way you can check if the event is sent at launch is to register the event handlers in your application delegate's applicationWillFinishLaunching: method; they should deliver by the time applicationDidFinishLaunching: is invoked. With that method, you could potentially only check for kAEGetURL.

How can I update the status messages in Firefox from any thread?

I encountered a problem with updating the status message on Firefox from a Plugin code.
As the documentation says calling NPN_Status works only when called from the main thread. My requirement is to update the status from any thread within the Firefox process.
Any help would be appreciated!
You can't update it from any thread because that would violate some of the threadsafety rules. You will have to proxy your update back to the main thread.
Like sdwilsh said, you are to call the NPN_*-functions only from the main thread. NPN_PluginThreadAsyncCall was only introduced in Gecko 1.9 and isn't supported in all current browsers.
Workarounds depend on the platform:
on Windows subclass the window your plugin receives, post/send messages to it and invoke the call from the handling window process
on Mac with Cocoa you can use e.g. performSelectorOnMainThread
on Mac with Carbon you can use invoke the calls on the null event
... etc.

Resources