< Previous PageNext Page > Hide TOC

Creating Plug-ins with Cocoa and WebKit

You can write browser plug-ins with the native WebKit plug-in API. Written in Objective-C, WebKit-based plug-ins are supported only by WebKit-based applications and cannot be ported to other platforms. The API is extremely simple, so many fewer lines of code are required to deploy a WebKit plug-in versus a Netscape one and you can use Xcode and Interface Builder to design and implement a plug-in’s functionality.

Contents:

Introduction To WebKit Plug-ins
Becoming A Plug-in
Using Plug-in Scripting
Implementing a Plug-in


Introduction To WebKit Plug-ins

WebKit plug-ins are based on core Cocoa API. The plug-in itself is simply an instance of an NSView, a common class in many other Objective-C applications. It provides a cornucopia of features, including the management of events such as mouse and keyboard inputs. Your plug-in inherits these “for free.” URL loading is also inherited, via NSURLConnection. You can access WebKit classes through the plug-in’s WebFrame and the browser scripting environment through the WebKit WebScriptMethods protocol.

Becoming A Plug-in

For the plug-in to act like a standard web browser plug-in, it needs to conform to the WebPlugIn informal protocol. This protocol has just one required constructor method, plugInViewWithArguments:, which your NSView subclass should implement.

Optional methods you can implement include:

These methods are implemented by the container of the plug-in; that is, they affect the web view that surrounds the plug-in:

Using Plug-in Scripting

The WebKit API allows your plug-ins to easily access a scripting environment (such as JavaScript) from the plug-in, and vice versa. Your plug-in can call JavaScript methods and read JavaScript properties, while your containing page can call methods from your plug-in from its JavaScript environment.

When the browser encounters your plug-in, it will use JavaScript to request the object representing your plug-in using objectForWebScript. The object that you return from that method represents the interface to your plug-in. This can be, but is not required to be, the same object as your plug-in. In that case, your implementation of objectForWebScript would simply look like:

- (id)objectForWebScript
{
    return self;
}

The object you return needs to have control over which of its methods should be visible to the scripting environment. In all likelihood, you don’t want all of your methods exposed to the environment, which they will be, by default. To counteract this, implement these methods:

Similarly, you want to give the scripting environment access to all of your properties. The syntax is very similar for restricting those:

Implementing a Plug-in

In this example, you create a QuickTime movie plug-in. This is a powerful example, because it requires very few lines of code and yet provides a useful extension to a web browser or WebKit application.

First, you need to create the view class. In this case, you use Cocoa’s built-in NSMovieView and subclass it to create your PlugInMovieView (see Listing 1).

Listing 1  PlugInMovieView header (PlugInMovieView.h)

#import <AppKit/AppKit.h>
 
@interface PlugInMovieView : NSMovieView
{
    NSDictionary *_arguments;
    BOOL _loadedMovie;
    BOOL muted;
}
 
- (void)setArguments:(NSDictionary *)arguments;
 
@end

Now you can write the implementation. You first need to conform to the WebPlugIn protocol, by implementing plugInViewWithArguments: (see Listing 2). Create an instance of your movie view, assign it the arguments passed into your method, and return it. Notice that an accessor method is being used to set the arguments—this is good Cocoa coding style.

Listing 2  Returning your plug-in’s view

+ (NSView *)plugInViewWithArguments:(NSDictionary *)arguments
{
    PlugInMovieView *movieView = [[[self alloc] initWithFrame:NSZeroRect] autorelease];
    [movieView setArguments:arguments];
    return movieView;
}

Now that you’ve returned the view, you need to make a decision. Do you have any operations to perform on initialization? In the case of NSMovieView, you can set a movie’s controller to be visible (or not) and also specify whether or not you’d like the user to be able to adjust its size. In this case, you should show the controller but prevent the user from resizing the movie in the frame—the most common layout for embedded movies (see Listing 3).

Listing 3  Initializing the movie plug-in

- (void)webPlugInInitialize
{
    [self showController:YES adjustingSize:NO];
}

From the enclosing container, nestled in an embed tag, you’ll receive a URL pointing to a movie. This will arrive in one of the keys specified by the arguments dictionary that you set in Listing 2. Use that URL to load and play the movie (see Listing 4).

Listing 4  Loading and playing a movie from a URL

- (void)webPlugInStart
{
    if (!_loadedMovie) {
        _loadedMovie = YES;
        NSString *URLString = [[_arguments objectForKey:WebPlugInAttributesKey] objectForKey:@"src"];
        if ([URLString length] != 0) {
            NSURL *baseURL = [_arguments objectForKey:WebPlugInBaseURLKey];
            NSURL *URL = [NSURL URLWithString:URLString relativeToURL:baseURL];
            NSMovie *movie = [[NSMovie alloc] initWithURL:URL byReference:NO];
            [self setMovie:movie];
            [movie release];
        }
    }
 
    [self start:self];
}

Eventually, all good things must come to an end, and so shall your plug-in. This will be announced by a call to webPlugInStop. You should take the opportunity to stop the movie from playing (see Listing 5).

Listing 5  Stopping the movie

- (void)webPlugInStop
{
    [self stop:self];
}

You’ve just implemented a fully functional WebKit movie-playing plug-in. You could build this code, install the plug-in, and have your own working QuickTime player embedded in Safari or a WebKit-based application. However, you might want to add a little more flair and use a form—with HTML buttons—to play and pause the movie. It just takes a few more lines of code (see Listing 6).

Listing 6  Opening the plug-in to JavaScript

+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector
{
    if (selector == @selector(play) || selector == @selector(pause)) {
        return NO;
    }
    return YES;
}
 
+ (BOOL)isKeyExcludedFromWebScript:(const char *)property
{
    if (strcmp(property,"muted") == 0) {
        return NO;
    }
    return YES;
}
 
- (id)objectForWebScript
{
    return self;
}
 
- (void)play
{
    [self start:self];
}
 
- (void)pause
{
    [self stop:self];
}

You only had to add two extra methods, play and pause, so that the buttons in the interface could be tied to public methods. Then you exposed those methods to the JavaScript scripting environment.

If you want to explore further, this example is available at:

    /Developer/Examples/WebKit/WebKitMoviePlugIn


< Previous PageNext Page > Hide TOC


© 2005, 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-10-15)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.