Linus
Linus

Reputation: 622

Shell script as default browser

Let's say I have an executable file called my_link_opener, how do I set it as the default browser?

I have tried the following already, without success:

Wrapping it as an .app
I tried using Appify and then setting the generated .app as my default browser in Safari.
However, because OS X expects programs to only have one "instance" that opens many links in its lifetime, this doesn't work out.
Instead of passing the URL on the command-line, it sends a -psn_xxxxx argument.

Wrapping it with Automator
Turns out, if you add it as a workflow in Automator you can make it take opened files as arguments.
However, that only works for files and folders.
As far I see, there is no way to make URLs valid input for the Automator workflow.

So, I'm out of luck. Is there any other kind of wrapping magic I could do?

Upvotes: 5

Views: 1085

Answers (1)

Antonio E.
Antonio E.

Reputation: 4391

I couldn't find any already option (but still, some may exist) so I ended up writing one. Basically what it does is telling the system that the app IS a browser, eventually set itself as default browser (see #define SET_AS_DEFAULT_BROWSER) and finally manages the url when it receives the right event by sending the url to a script called my_link_opener located in the app's bundle (that is, inside the .app directory). The bash command called is:

nohup /path/to/my_link_opener "_an_url_" >& /dev/null &

#import "ZFTAppDelegate.h"

NSString *runCommand(NSString *commandToRun)
{
    NSTask *task;
    task = [[NSTask alloc] init];
    [task setLaunchPath: @"/bin/sh"];

    NSArray *arguments = @[@"-c" ,[NSString stringWithFormat:@"%@", commandToRun]];
    [task setArguments: arguments];

    NSPipe *pipe;
    pipe = [NSPipe pipe];
    [task setStandardOutput: pipe];

    NSFileHandle *file;
    file = [pipe fileHandleForReading];

    [task launch];

    NSData *data;
    data = [file readDataToEndOfFile];

    NSString *output;
    output = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
    return output;
}

#define SET_AS_DEFAULT_BROWSER

@implementation ZFTAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
    [em setEventHandler:self
            andSelector:@selector(getUrl:withReplyEvent:)
          forEventClass:kInternetEventClass
             andEventID:kAEGetURL];
    [em setEventHandler:self
            andSelector:@selector(getUrl:withReplyEvent:)
          forEventClass:'WWW!'
             andEventID:'OURL'];


#ifdef SET_AS_DEFAULT_BROWSER
    CFStringRef bundleID = (__bridge CFStringRef)[[NSBundle mainBundle] bundleIdentifier];
    LSSetDefaultHandlerForURLScheme(CFSTR("http"), bundleID);
    LSSetDefaultHandlerForURLScheme(CFSTR("https"), bundleID);
#endif


}

- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
    NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
    NSString* scriptPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"my_link_opener"];
    runCommand([NSString stringWithFormat:@"nohup %@ \"%@\" >& /dev/null &", scriptPath, url]);
}

@end

Remember that in the app's info.plist you should have this (needed to inform the OS about the capability of your app to handle http/s urls):

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>https</string>
        </array>
        <key>CFBundleURLName</key>
        <string>Secure http URL</string>
    </dict>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>http</string>
        </array>
        <key>CFBundleURLName</key>
        <string>http URL</string>
    </dict>
</array>

Upvotes: 3

Related Questions