Application • 32:48
This session will cover best practices for Safari browser plug-in development and deployment, CFM versus Mach-O plug-ins, and techniques for everything from general plug-in development for Safari and Web Kit to advanced methods of integration with JavaScript.
Speaker: Chris Blumenberg
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
4.13, plug-in development for Safari and Web Kit. I'm Chris Blumenberg. I'm a Safari engineer. So first of all, what are you going to learn here? Well, first of all, you're going to learn what are plug-ins, in case you don't already know. Then I'm going to go into why you develop plug-ins. The typical reason for wanting to develop plug-ins is to extend the functionality of a web browser, but you'll soon see that there are other reasons as well.
I'll go into how to develop plug-ins. I'll go into the types of plug-ins that you can develop and then the issues involved with developing those plug-ins. And then I'll discuss using plug-ins in your web page. That will involve the right kind of HTML for embedding your plug-in on a web page as well as scripting your plug-in from JavaScript.
So what are plug-ins? Well, plug-ins are libraries. Typically, they're written by third parties. You may be familiar with Photoshop plug-ins. They add filter effects to images in Photoshop. Well, in web browsers, plug-ins support content types that the browser doesn't handle by default. For example, without the Apple QuickTime plug-in, you wouldn't be able to see QuickTime movies in Safari and other web browsers. Other popular examples are Mac for MediaFlash and Windows Media Player.
So why would you want to develop a plug-in? Well, first of all, you'd want to extend web browsers, as I mentioned before. You'd want to handle content types that the web browser doesn't handle by default. And the other reason for wanting to write a plug-in for a web browser is to basically run native code. From a web page, you can display HTML. You can run JavaScript. But in some cases, you may want to run some native code to get some sort of rich browsing experience.
And the other major reason for writing a plug-in is to extend your Web Kit application. If you have a Web Kit application, the only way that you can get a widget inside of a WebView is to, you know, write a plug-in. For example, Mail uses plug-ins for their file attachment widget, or they will be using plug-ins for their file attachment widget in Mail messages. And dashboard widgets can use plug-ins, too. So I think you have about 24 hours to get a dashboard widget working, and you can probably get a pretty cool one if you use a plug-in.
So this is the biggest part of my talk here. It's how to develop plug-ins. First of all, I'll be covering the two types of plug-ins or the two types of APIs that you can use to develop a plug-in. First of all, I'll be discussing the Neskate plug-in API. It's been around for a number of years. I'm sure that most of you are familiar with it. And then I'll discuss the Web Kit plug-in API, which is something completely new, and I'll go into depth about that shortly.
Then I'll discuss a bunch of things which relate to both kinds of plug-ins. That is, using bundles, which is how you represent and how you package up your plug-in on disk, and then registration, which is how you actually register your plug-in with the host application. Then I'll go into installation, which is basically where you install your plug-in.
So here in this whizzy graphic here, you can see that there are two types of plug-ins that work in Safari and Web Kit. There's the Netscape plug-in and there's the Web Kit plug-in. First, I'm going to focus on the Netscape plug-in. So before I go any further about this, I'm going to discuss history. So the Netscape plug-in API has been around since 1996. It was introduced in Netscape 2.0. Microsoft Internet Explorer shortly after adopted it in their version 3.0. is a developer at Microsoft.
He's been working on a lot of the development So here's what the Netscape plug-in API provides. It provides a drawing API so that your plug-in knows when and where to draw. It provides an events API so that your plug-in can receive user events. It provides a networking API so that your plug-in can send and receive network streams.
So before I go in depth about any sort of APIs, I want to discuss an issue about the actual file format of your plug-in. Well, on Mac OS X, a plug-in or library can be compiled in two different ways. It can be compiled in Mach-O or PEF, a.k.a. CFM file formats. Well, we really like it for you to use Mach-O. It's the native object file format on Mac OS X. And CFM is really something of the past back in the classic days.
Mach-O plug-ins load much faster than CFM plug-ins. In fact, when the QuickTime plug-in was ported over to Mach-O, we found that it loaded 100 times faster. New APIs on the system are only going to be provided, are only going to be available to Mach-O applications and libraries. They won't be available for CFM applications and libraries. And you can develop and debug a Mach-O plug-in inside of Xcode. That can't be done with CFM.
So here's the big news. We're extending the Netscape API. We're adding more stuff to it. First of all, we're adding a scripting API, which is the ability for your plug-in to be accessed from JavaScript, and from your plug-in, you can get to JavaScript and the rest of the document.
We're also adding an API to access HTTP headers. This is kind of a developer request here. This allows your plug-in to get at the HTTP headers, which is basically all the server information besides the content. Also, we're adding a state information API. This is an API that allows you to get the state of the plug-in inside of the web page, that is, whether the plug-in is in the frontmost tab or not, whether the window is the frontmost window or not, whether the window is minimized or not.
And we're adding much more. You can expect to hear a lot more from us. And what's important about this is that this is not just an Apple thing. We're working with companies such as Mozilla, Macromedia, and Opera to basically get this new API working in many web browsers and many plug-ins out there.
So here's my whizzy Netscape scripting graphic. So I'm going to discuss Netscape scripting. This is basically the only new Netscape API that I'm going to discuss today. So what is scripting? Well, it allows JavaScript to access your plug-ins, and it allows plug-ins to access JavaScript and the rest of the document.
So the Netscape API used to actually provide this functionality in a technology called Live Connect. Live Connect is a Java API. It's pretty slow because it involves starting up a Java VM and involving Java in this rather simple communication between a plug-in and a browser. In Web Kit, Live Connect is only used for Java applets. We don't support it for communication with plug-ins. Well, in the new API, it's a C API.
That is, it's just like the rest of the Netscape API. It's all based on C, and it's much faster than Live Connect. So I'm going to talk about-- I'm going to get more in depth about the scripting API here, but first I'm going to discuss the communication from JavaScript to the Netscape plug-in.
So when JavaScript encounters your plug-in in a script, it will call NPP get value to get the NP class and the NP object, which represent your plug-in. Now, the NP class you can think of as the interface between your plug-in and JavaScript, and NP object is the, you can think of that as an instance of your NP class.
I'll get more into that shortly. So here's an example. I hope you can see that. Here's an example of NPP get value. In this case, if the variable that the browser is asking the plug-in for is NPP v plug-in scriptable NP object, I then return an NP object, which represents my plug-in to JavaScript.
And here's NP class. So, NP class is basically a struct, consists of a struct version and a set of function pointers which define the interface between your plug-in and JavaScript. First of all, there's allocate. The allocate function is called to allocate an NP object. Deallocate is called to deallocate that NP object. Invalidate is called when JavaScript no longer is using your NP object. has method is called when JavaScript is calling your-- is calling a method on your NPE object to make sure it actually has it.
Invoke is called to invoke a specific method on your NP object. And has property is called to check if your NP object has a property. Get property is called to get a value for a specific property. Set property is called to set a value for a specific property.
And here's an example of NP object. In this case, my NP object is a movie object. It basically contains an NP class and a reference count. Those are the required fields of the struct there. And then everything that follows is basically anything else that I want to have in that NP object. So what you can see here is my movie object. This is basically how I represent my plug-in in JavaScript.
So here's an example of movie allocate, my allocate function. Basically, allocates a movie object and returns it to JavaScript. And here's an example of MP invoke, excuse me, movie invoke, which is my movie, which is my invoke function. So if JavaScript is calling play, I just call play movie, which is an internal function within my plug-in.
If JavaScript is calling pause, I just call pause movie, which is an internal function within my plug-in. And, you know, before I exit this function, I set a return value. In this case, well, in either case, I have no return value, so I set undefined. So I've discussed the communication from JavaScript to the plug-in. Now I'm going to go in the opposite direction.
And that's from the plug-in to JavaScript. So the way this works is that if your plug-in wants to get a JavaScript, the interpreter and the rest of the document, it basically calls NPN get value to get the window object using the variable name here, NPN vWindowNPObject. So once it gets back this window object, it's basically an NP object, so I can start making NP object calls on it. For example, I can call create object, which creates an object. I'll actually need to implement this in order to return an object back to JavaScript if I ever need to do that.
Retain object in case I want to keep a specific object around for a while. And of course, for every retain, you have to do a release, so there's release object. And here are my invocation methods. There's NPN call, which allows me to invoke a specific method on an NP object. And then there's NPN Evaluate, which allows me to take a string, a JavaScript string, and evaluate it and get back a result.
And here are property access methods, which allow me to get to actual properties of NP objects. So there's NPN getProperty, which returns a property of an NP object. SetProperty, which allows me to change a property of an NP object. NPN removeProperty, which allows me to remove a property of an NP object. So let's do a little demo.
Sorry, I need a drink. OK, so what I'm going to show here is my little Netscape plug-in. It's called Netscape Movie Plug-in. It's basically just like the QuickTime plug-in, just a little simpler. And what I'm going to show you here is I'm not going to go too in-depth with the code here. As you can see, it's pretty complex. It takes a good amount of code to basically get a simple plug-in up and running, a Netscape plug-in up and running in Web Kit.
So what I want to show you actually here in this file is the number of lines it takes. It takes about 626 lines to get a simple plug-in up and running. So remember that number because it's going to matter a little later in this presentation. So let's give this guy a try. Cross your fingers.
So as you can see here, I basically have my little plug-in working in here on this web page. This is not the QuickTime plug-in. I don't even have it installed. So on this page, I have the plug-in and two form elements, play and pause. Now, these form elements are actually calling JavaScript functions on my movie.
for seamless integration of iPod and automobile. So that's it. So you can actually download that plug-in from connect.apple.com and use it as a template if you want to. To the presentation machine, please. Okay, here we go. Okay. Okay, so I've discussed the development of a Netscape plug-in, so now let's focus on the development of a Web Kit plug-in.
Well, the Web Kit plug-in API is an Objective-C API. It's a Cocoa API. It's very simple. Both Cocoa and the API, the plug-in API itself, are very simple to work with. Any Web Kit plug-in that you work, that you develop, will work inside of a Web Kit application.
So here's what the Web Kit plug-in API provides. Well, first of all, every instance of your plug-in is an NSView. So there's no explicit API for dealing with events or drawing. That's all done the NSView way. There's no explicit API for URL loading because you can do that very easily yourself using NSURL connection, which is a foundation API.
Another thing that this API provides is access to Web Kit classes such as Web Frame so that you can have a closer interaction with Web Kit if you choose to have that. And also, it has a scripting API, which is basically Web Kit's new scripting API, which was discussed in depth this morning, but I'll go into that shortly.
So here's the first method that your plug-in should implement. It's called plug-in view with arguments. This is actually the only method your plug-in needs to implement. So as you can see, it's very simple to get one of these up and running. So plug-in view with arguments basically returns your plug-in view, the plug-in view which is supposed to be inside of your web view.
And here's some other plug-in methods. These are all optional. You can implement them if you want. The first one is Web Plug-in Initialize. This method is called soon after your plug-in is created. In this method, you basically start allocating resources and doing that sort of thing. Then there's Web Plug-in Start. In this method, you start doing work. You start animating.
And this is what plug-ins stop. In this method, you stop doing work. You see some sort of animation. Then there's Web Plug-in Destroy. In this method, you destroy or dealloc any sort of resources that you might have, or you can just wait until you get the dealloc method.
And here are some other methods that your plug-in can implement. First of all, this Web plug-in is selected. This is called when your plug-in's selection state has changed. So if you are selected, you probably want to draw some sort of selection state. And here's Object for Web Script. This is the method that returns the object which represents your plug-in in JavaScript, and I'll go into that shortly. So here are the container methods. These are the methods that from your plug-in you can call onto the container.
So first of all, there's Web plug-in container load request and frame. This basically tells the container to load a specific web page in a specific frame. Oops. Okay. Then there's what plug-in container shows status. If the container, such as Safari, has a status bar, it should show some sort of status message at the bottom of the page at this point. Then there's Web Plugin Container Selection Color. So if your plug-in is selected, it should use this selection color to draw some sort of selection state.
And lastly, there's Web Frame. So from your plug-in, you can get the Web Frame, which contains the plug-in. From the Web Frame, you can get to the Web Data Source and the WebView and all the other Web Kit classes. Okay, so now I'm going to go into Web Kit scripting. This was covered this morning, but I'm just going to brush over this right now. First of all, I'll go into scripting from JavaScript to your Web Kit plug-in.
When JavaScript encounters your plug-in object, it will call object for web script to get the object to see representation of your JavaScript object. Now that object that is returned can choose to implement these methods. First of all, there's WebScript name for selector, which allows you to choose a name given a selector that your object implements. And then there is isSelector is excluded from WebScript. Chris Blumenberg Now, in this method, you basically decide which methods you want your plug-in to implement.
Now, this method is pretty important as far as security is concerned because you only want to export methods that you think are secure, won't crash the page and what have you. So here's an example of an implementation of is selection excluded from web script. In this case, I'm allowing the play method on my plug-in to be exported. And at the bottom there is just a simple limitation of a play method.
Then here are some methods about property access. First of all, there's WebScript name for key, which allows me to choose a name given a property on my plug-in object. And then there is is key excluded from WebScript. And just like the previous method or like the method that dealt with JavaScript methods, in here I choose which properties I want to export to JavaScript.
So there is--here's an example. IsKey excluded from Web Script. In this example, I'm allowing muted-- the muted property of my object to be exported. And at the bottom there are some methods which allow the key value coding to access my muted property. Okay, so I've discussed scripting from JavaScript to your Web Kit plug-in. Now let's go in the other direction.
So from your plug-in, you can get at the document or the window object in two different ways. You can call Windows Script Object on WebView, which will return the Objective-C version of the JavaScript window object, or you can call DOM Document on WebFrame, which will basically be the DOM Document which contains your plug-in.
So here's the interface for Windows Script Object on WebView. As you can see here, it returns a WebScript object. So I'm going to just show you now a few methods that WebScript Object implements or provides. First of all, there's setValueForKey, which allows you to set a specific value for a specific key on a WebScript object. Then there's valueForKey, which allows you to get a value given a key.
There's call-webscript method with arguments which allows you to invoke a specific method on a WebScript object. There's evaluate-webscript which allows you to evaluate a JavaScript string. And then here's the interface for getting the DOM document off the web frame. As you can see here, it returns a DOM document object.
And here's just kind of a hodgepodge of methods that DOM document implements and provides. Actually, these methods are also DOM node methods because DOM document is a subclass of DOM node. So here's attributes. This allows me to get the attributes of the document or DOM node. Here's paranode, which gives me the paranode of a document or a DOM node.
childNodes gives me the children of a DOM node, firstChild gives me the first child of a DOM node. These are rather self-explanatory. lastChild gives me the last child of a DOM node, previous sibling, so on and so forth. So for more information about the DOM document, DOM nodes, I suggest you go to this talk on Friday at 10.30. I'm not sure of the number. Maybe you want to give that to me? 426. That will discuss the DOM document and all that, as well as editing, if you're interested in that. So let's do a little demo of a WebKit plugin.
So I'm not very original. My Web Kit plug-in does the same thing as my previous plug-in does. It's called Web Kit Movie Plug-in. But remember that the previous plug-in did everything in about 600 plus lines of code. This does it in... So, as you can see, it's extremely easy to write one of these puppies. So, I'm not going to go into the code here, but I'm just going to go into a little demo of it.
Okay, so you may be thinking now, okay, he just did this same demo two times in a row. That's pretty damn boring. Okay, so let me try to spice it up a little bit. Wouldn't it be cool if I could somehow control the play rate of that movie? You know, control how fast it plays from the webpage. So I'll go down to my plug-in here and enable my set play rate function. And the very important step, I exported it to JavaScript here in this method. Give it a little compiling.
It works. And then I'll go into my HTML file. And enable a slider which is a kind of new form control that is sort of Web Kit specific. And as you can see here, my slider basically just calls setMoviePlayerRate, which is a JavaScript function which calls my plug-in. And let's give this a try. Oops. Hey, it's beta. Let's give it another try.
Okay, so there you go. Okay, so that's my Web Kit plug-in demo. So I've discussed the two types of plug-ins that you can develop for Safari and Web Kit. Let's give them a little comparison. So first of all, the Netskit plug-in API is a C API. It's cross-platform. The same API will work on Windows, Linux, and the Mac.
It works in most web browsers out there. In fact, it probably works in all web browsers out there. And the Web Kit plug-in API is a Cocoa Objective C API. It only works in Web Kit applications. It's possible that it may work in other applications in the future. And as you've seen here, it's very easy to develop one of these things.
So now I'm going to get into some issues that relate to both types of plug-ins. Well, first of all, plug-ins on disk are bundles. Well, what is a bundle? A bundle is a folder which contains the library. That is the compiled code of your plug-in. It contains resources, which are the resources that your plug-in may want to use, such as images and those types of things. It contains localizations, so your plug-in can be localized in many different languages. A single plug-in bundle can be localized in many different languages. And lastly, it contains registration information so that your plug-in is properly registered with a host application. So let's get into registration information.
Well, your plug-in is responsible for registering the content types that it can handle. That registration information can be stored in two different ways. The first way and the way these days or the way that it's always been done is with Carbon resource files. The new way is with the Info.plist file. Basically, the Info.plist file is, you're probably all familiar with it, but it's just a very easy way to edit and maintain metadata.
You can use it for your application or a bundle. On Mach OS X, this is a modern way of actually doing it. Working with Carbon resource files tends to be a big pain. So if you care, currently, Info.plist files only work in Web Kit applications, but if you care about working with other browsers as well on the Mach, you can use both, and that'll work just fine.
So let me go into registration via the Info.plist file. First of all, you want to provide the description of your plug-in, which is the user visible description of your plug-in. Then you want to provide a key and value for the name of your plug-in, which is the user visible name of your plug-in. And lastly, you want to go into the MIME types that your plug-in can handle.
So within that MIME type, you want to specify the extensions that map that MIME type, as well as a user-visible description for that MIME type. So with all this information, you'll be able to register your plug-in nice and easily inside of a Web Kit application. Now that may look a little complex. It certainly does to me. But inside of Property List Editor, it's very easy to write one of these things.
So you've got your plug-in compiled. It's got all the registration information all set up. Where do you install this thing? Well, you can install it in three different places. You can install it in slash library slash internet plug-ins so that it works with all applications for all users. You can install it in tilde library slash internet plug-ins so that it works for all applications just for that specific user. Or you can install it in the plug-ins directory of your application so it will only work inside of that application.
So this is the last part of my talk here. This is actually how to use your plug-in. I'm going to first discuss the good kind of HTML for embedding your plug-in on a web page, and then I'll discuss how you script your plug-in from JavaScript. Well, most web browsers out there support the embed tag.
It's a sufficient way of embedding a plug-in on a web page. But if you have a Windows version of your plug-in, you'll need to use the object tag. Well, it's possible to use HTML that does both and thus works with all browsers. Here's the embed tag. I'm sure you've seen it all before.
And here's the object tag. It's required by Windows. And param tags, which are... children of the object tag, and then the embed tag, which is also a child of the object tag. And that's basically it. So with this little somewhat lengthy HTML snippet here, you can get your plug-in working on all browsers.
So let's get into scripting now from JavaScript. Basically, each instance of your plug-in on the page has a JavaScript object which represents it. You can get at that JavaScript object by name or by number. Once you actually have it, you can call the exposed methods on it as well as the exposed properties.
So here's a little sample. This is actually taken right from my demo here. Here I am getting a plug-in by name. Since I know that -- excuse me, by number. Since I know that it's the first plug-in on the page, I can just use index zero on the embeds array. And then here's an example of me using or calling a plug-in method, which is play.
And then here's an example of me getting a plug-in by name. You don't see it here, but my embed tag actually has a name attribute, which is movie plug-in. And here I am getting it by movie plug-in. And at the bottom there, I'm actually getting and setting a plug-in property.
So everything that I discussed today is going to be available on Safari, is available on Safari and Web Kit 1.3 beta on Panther. You can download it now at connect.apple.com. It's also available on your Tiger seed that you got today, except for the new Netscape scripting APIs. Those are only available on the Panther beta. Chris Blumenberg Now once both the Panther and Tiger versions of Safari go GM, everything that I mentioned today will work in both.
You can go to connect.apple.com to get sample code, links, and demos. You can actually get the demos which I showed you today, and you can use them as templates if you want. You can also go there for new documentation for Web Kit. And lastly, at the bottom there, you can go to that Netscape URL to get information about the Netscape plug-in API. That doesn't actually include the new APIs. To get at those and for information about those, you need to actually use my demo that I gave today.