Writing an NPAPI plugin for Mac OS X

Recently I have been doing some work to make life easier for NPAPI plugin developers. As part of that effort I started to revive our NPAPI SDK and rewrite some of our sample code. Today I’m going to explain how to write an NPAPI plugin for Mac OS X, based on the basic sample plugin in our SDK. It is actually pretty easy now. You can have one built and running in the browser in minutes.

The sample code I’m going to be referencing today can be found here:

http://mxr.mozilla.org/mozilla-central/source/modules/plugin/sdk/samples/basic/mac/

We decided to commit that sample under a BSD-style license which means you can use it for anything, even if you don’t plan to release source code. This is unfortunately common for NPAPI plugins.

If you check out the Mozilla source code in Mac OS X, you can simply open “BasicPlugin.xcodeproj” and hit build and you’re done. A “build” directory will be created next to “BasicPlugin.xcodeproj”, and if you built in release mode that will contain a “Release” directory with the plugin inside it. Copy that plugin into one of the following two locations and it’ll work:

/Library/Internet\ Plugins/
~/Library/Internet\ Plugins/

Just modify the basic sample plugin to do whatever you want.

Now I’ll explain some things that might not be obvious if you were trying to get such a plugin building on your own.

The plugin communicates its MIME and extension information using the “Info.plist” file which is packaged in the plugin bundle. Just read the file to see how that is done. The plugin also communicates its bundle type in that file, under the key “CFBundlePackageType” – the type is “BRPL”. If the type is not an NPAPI plugin type, the bundle won’t load as an NPAPI plugin. You can just always use “BRPL”.

I made a GCC preprocessor definition in the project file, “XP_MACOSX=1″. This is used by the NPAPI headers, if you don’t define it they won’t be interpreted correctly on Mac OS X. This is easy to miss in the sample project file build settings.

Symbol visibility is a common problem area for people trying to get NPAPI plugins working. Some symbols must be visible as standard C symbols so the browser can find them, which (always?) means simply prefixed with an underscore (Foo() = _Foo). There are three symbols that need to be visible as standard C symbols on Mac OS X – “NP_Initialize”, “NP_GetEntryPoints”, and “NP_Shutdown”. Our sample plugin is written entirely in standard C and uses a standard Xcode build configuration so by default all of its symbols are C-style and visible. If you wanted to implement your plugin in C++ (.cpp) or Obj-C++ (.mm), you would have to do this:

#pragma GCC visibility push(default)
extern "C"
{
NPError NP_Initialize(NPNetscapeFuncs *browserFuncs);
NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);
void NP_Shutdown(void);
}
#pragma GCC visibility pop

You can check to see if your symbols are visible and in standard C naming format using the “nm” utility on Mac OS X, targeted at the plugin binary. The results should look like this:

[josh@denmark MacOS] nm BasicPlugin
...
00000810 T _NP_GetEntryPoints
000007fa T _NP_Initialize
000008a0 T _NP_Shutdown
...

This basic knowledge should get new NPAPI plugin developers pretty far on Mac OS X. Soon I’ll write up the same thing for getting an NPAPI plugin working on Linux/UNIX systems.

About these ads

About Josh Aas

Josh Aas is a Gecko platform software engineer with Mozilla Corporation.
This entry was posted in Mozilla, Programming. Bookmark the permalink.

27 Responses to Writing an NPAPI plugin for Mac OS X

  1. I’ve been thinking that it would be really nice to have a “test plugin” that exercised every aspect of the API, for when one is messing with the browser side of the interface. For instance, there’s a bug somewhere to the effect that if you make drastic-enough style changes from script to a page with a plugin on it, the plugin gets reloaded; it would be nice to fix that, but testing currently would be a matter of “well, it appears to work with Flash and Java and a media player…” A test plugin could let me say “it works with code that pushes the API to its limits but no farther, so any plugin it breaks is buggy.”

  2. Josh Aas says:

    We plan to create a testing system for NPAPI.

  3. I’ve migrated this post into MDC here:

    https://developer.mozilla.org/En/Writing_a_plugin_for_Mac_OS_X

    Feel free to poke at it if you see anything I botched up during the copy and paste and edit session. :)

  4. Petoi says:

    I built it and compiled the plugin which results in a BasicPlugin.bundle file which I copy to ~/Library/Internet Plug-ins. I then load the test.html page in the parent folder of the project using Firefox and it does not find the plugin. It asks if I want to download the plugin. Any ideas?

  5. Francis says:

    I have the same problem as Petoi. Using Xcode 3.1, latest mozilla source from mozilla-central (as of 17 Feb 2009), and Firefox 3.0.6. The plugin builds fine and creates the BasicPlugin.bundle. Putting this in the plug-in paths as described doesn’t seem to do anything. about:plugins doesn’t show it as being installed. Any help would be really appreciated.

  6. fugelen says:

    Anyone know a similar tutorial for Linux?

  7. elan says:

    I have the same problem as Petoi and Francis above… Interesting observation is that Safari actually finds the basic plugin, but it won’t show in Firefox

  8. Having problems building the BasicPlugin sample for 10.4 using XCode 2.3. I’ve changed the SDKROOT to /Developer/SDKs/MacOSX10.4u.sdk, and renamed npfunctions.h to npupp.h in BasicPlugin.h (as per the latest gecko sdk where it hasn’t been renamed to npfunctions.h yet), but whenever I build using either Xcode.app or xcodebuild, it doesn’t actually build the plugin. In the build/Release/BasicPlugin.bundle/Contents dir I only see Info.plist and Resources, but no MacOS and no plugin. There are no errors. For some reason when it builds it is only copying over the resources – it never even tries to build the plugin. Does the sample project not work with 10.4? I have a Safari plugin that builds fine using xcode.

  9. Petoi, Francis and Elan:

    Safari and the Minefield FireFox builds use the Info.plist file for the plugin information. But the official FireFox and Opera use the Resource Fork (BasicPlugin/Contents/Resources/BasicPlugin.rsrc) of the plugin for info. In Xcode create a file called BasicPlugin.r with the following contents (i hope formatting will be allright):

    #include
    resource ‘STR#’ (126, “Description”) { { “Shows the user agent using CoreGraphics”, “Basic Plugin” } };
    resource ‘STR#’ (127, “MIME type description”) { { “The basic plugin mime type” } };
    resource ‘STR#’ (128, “MIME type”) { { “application/basic-plugin”, “” } };

    And add a ‘Build ResourceManager Resources’ build phase to the project. That should get you started.

  10. That include line should be:

    #include <Carbon/Carbon.r>

  11. Francis says:

    Thanks Selwyn, works a treat now. For others, be careful copying and pasting though. The single and double quotes came out wrong for me when I pasted in xcode. (Maybe this will paste better, I dunno)

    #include
    resource ‘STR#’ (126, “Description”) { { “Shows the user agent using CoreGraphics”, “Basic Plugin” } };
    resource ‘STR#’ (127, “MIME type description”) { { “The basic plugin mime type” } };
    resource ‘STR#’ (128, “MIME type”) { { “application/basic-plugin”, “” } };

    Francis

  12. Francis says:

    No, looks like its done the same again.

  13. JJGL says:

    Hello,

    I have built your project with XCode and placed the bundle with the .rsrc file in the Internet Plug-Ins path. The plugin works fine on Safari and on Mozilla Firefox 3.x. However on my Mozilla Firefox 2.0.0.13 it isn’t loaded although it appears in about:plugins. I have checked that NP_Initialize function is not called. Any idea about compatibility with Firefox 2.x?

    Thanks in advance,

    Juanjo

  14. Arian says:

    Hey josh, truly amazing work. I really found everything useful.

    Question though, I’m a little confused as to how I use this plugin. I’ve made a simple plugin that runs in the browser but it does nothing, just draws the frame and all.

    Now, if I have an API with native draw calls and such that is utilized by a javascript code I write, how can my plugin get access to this javascript through the browser. I assume it’s passed in as some html text or something? Basically I want to make a native plugin that’ll run my code across many platforms. I’m just wondering how my plugin would implement these certain functions (such as interpreting certain draw calls) in conjunction with the browser.

    I know it’s kind of specific but I’m pretty confused and there is VERY little documentation on this type of thing…

  15. So, has anyone actually managed to get this demo to compile on 10.4?

  16. I’ve just upgraded our dev machine to 10.6 and the sample plugin builds fine, so it looks like you need OSX 10.6.

    In reply to Arian’s question about javascript calls – here is what you need to do to call functions within the plugin:

    - remove BasicPlugin.c from the project
    - add the .cpp files from the npruntime sample to the project:

    http://mxr.mozilla.org/mozilla-central/source/modules/plugin/sdk/samples/npruntime/

    - comment out the code inside handleEvent in plugin.cpp
    - comment out the #include in plugin.cpp
    - add these lines to np_entry.cpp:

    #ifdef XP_MACOSX
    #pragma GCC visibility push(default)
    extern “C”
    {
    NPError NP_Initialize(NPNetscapeFuncs *browserFuncs);
    NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);
    NPError NP_Shutdown(void);
    NPError NP_GetValue(void* future, NPPVariable variable, void *value);
    }
    #pragma GCC visibility pop
    #endif

    - copy the NP_GetEntryPoints function from BasicPlugin.c

  17. Jerry Krinock says:

    Well, here we are two years later. I built this little plug-in, without the .rsrc file, installed it in ~/Library/Internet\ Plug-Ins, and it seems to work (gives me the User-Agent string) after relaunching the following browsers:
    Firefox 4.0, Chrome 7.0, Safari 5.0, iCab 4.8, OmniWeb 5.10
    The following browsers say that the plug-in is not loaded:
    Camino 2.0, Opera 10.6, Flock 1.2, Flock 2.6
    This browser loads the page and then apparently crashes while loading the plug-in content:
    Firefox 3.6

    I don’t see any pattern there. Can someone please explain why it does/not work in different browsers? I built a debug version (Intel x86 binary only), but I know that all of these browsers are “Universal Binary”, meaning that they should accept an x86 plug-in.

  18. Jerry Krinock says:

    Update: When I added the .r file and built the .rsrc, now instead of crashing, Firefox 3.6 shows an empty space instead of the box containing the User-Agent string. (And then when I removed the .rsrc, it still shows the empty space and doesn’t crash, so I don’t know what happened to the crashes I saw yesterday.) Note that this is a different response than I get from Camino 2.0, Opera 10.6, and Flock which show a box with some text indicating that the plug-in is not available.

    When I enter “about:plugins” into the Firefox 3.6 address bar, my Basic Plug-In Sample is listed.

    Eeeek. I just realized that most people are still using Firefox 3.6. Why might this plug-in not work in Firefox 3.6?

  19. Jerry,

    It should work fine in all browsers. Make sure you’re using a recent xulrunner sdk.

    Also, you must have the correct versions of np_entry.cpp, npn_gate.cpp and npp_gate.cpp. I remember I had lots of crashing problems at first when using the code given above. My versions of these 3 files are very different from the ones in the mozilla-central repository, but I can’t remember if I got them from somewhere else or cobbled them together myself from into on the web. Whatever, our plugin works pretty much perfectly on all browsers and has been in commercial use by some very demanding customers for the past few years. We use this across all 3 platforms (linux, OSX and Windows) and the only problem we have is in 64-bit safari: when the user closes the page containing the plugin, it seems to sometimes hang the browser.

    Here is the working code if you want to take a look at it:

    http://dev.groupboard.com/mozplugin_common.zip

    Dave

  20. Jerry Krinock says:

    Thank you, Dave. When I unzipped your download I found four files:
    np_entry.cpp
    npn_gate.cpp
    npp_gate.cpp
    plugin.cpp
    and when I compare these files to those of the same name which I found in my 723 MB of mozilla/src/, indeed I find that yours are different, and many of the differences seem to regard compiler switches for Mac OS X. But I’m not sure how adding this code to Josh’ project would make it work any better; it seems to me that more code just means more symbol definitions, and if I was missing symbol definitions it wouldn’t compile, and of course more code makes more possible bugs.

    But since I may well need your code by the time my project does everything it needs to do, I’m going to keep it in my back pocket. Besides compiler switches for Mac OS X, your code appears to add some stuff for scriptability.

    I tried including your code to see what would happen. After several passes it seemed that I was off on the road to to what I call “#include hell”, which is that you add a file to satisfy one #include and each or these require 2 more files, which each depend on 2 more files, etc., which would grow exponentially until I end up having to include the whole damned 723 MB of mozilla/src, and by that time I’m going to have thousands of errors and warnings and might never get it working. I hope that my project does not require doing that.

    When building a plug-in, does one normally need to include the whole damned 723 MB of mozilla/src in their project?

  21. Actually I don’t include a single line of Mozilla source – it’s just too much of a pain in the ass. All I have are those 3 modified cpp files from the example plugin and my plugin.cpp file. I just include the gecko xulrunner sdk (http://releases.mozilla.org/pub/mozilla.org/xulrunner/releases/1.9.2.12/sdk/xulrunner-1.9.2.12.en-US.mac-i386.sdk.tar.bz2). Note that you probably don’t want to take my plugin.cpp (it won’t compile without some other stuff). However the other 3 files should compile as long as you set up the include path for the xulrunner/include directory.

    Dave

  22. Jerry Krinock says:

    Dave, thank you for telling me that I don’t need Mozilla’s megabytes of C++. Whew!

    So, after a few tests I’ve concluded that, while Josh’s “Basic Plug-In” is kind of an NPAPI “Hello World”, the code you posted, when combined with xulrunner, is kind of a “Hello World : Chapter 2 : Scripting”. For now, I’ve gone back to working on my plug-in, and I just got it to receive a NSDistributedNotification – woohoo. Since this is my first plug-in, I’m going to try to get it working in Chrome, then if it doesn’t work in Firefox 3.6, I’ll try your code. By that time I should have a much better idea what I’m doing here on this planet.

    Thanks again.

    (This discussion is still open here if someone can explain why Josh’s code works in all those browsers noted in my first post but not in Firefox 3.6.)

  23. I think I might have figured out the problem – make sure you use the npruntime code and NOT the code from common. I did some diffs and my np_entry.cpp code seems to be closer (but not identical) to the version in npruntime than to the version in common.

    I think the common directory uses the old (XPCOM?) method of calling, which doesn’t work properly in newer browsers. In order to support all current browsers just make sure you use the npruntime code.

  24. Hello Josh,

    BasicPlugin is my mentor.
    But it doesn’t work anymore in firefox 4 beta 6

    I’ve download firefox source from mozilla-central by Mercurial a couple of days ago. I’ve download binary firefox 4 beta I’ve only compiled “modules/plugin/sdk/samples/basic/mac” where BasicPlugin exists
    It works in safari and chrome.

    For more precisely, cocoa event dose not occur except NPCocoaEventFocusChanged.

    Can you teach me?

    Regards,
    neochoon

  25. Jerry Krinock says:

    Thank you, David. While trying to figure out what code to use I found the Firebreath project. It’s an open-source C++ library for accessing NPAPI. I had ten lines of code which was starting to work but was crashing inexplicably. I replaced this ten lines with one line invoking a Firebreath method, and the problem was solved. I recommend it to anyone having trouble getting started with NPAPI. It’s not magic, but it reduces the NPAPI project from being what I’d say is extremely difficult and time-consuming down to being only moderately difficult and time-consuming.

    • Jerry Krinock says:

      Hey, it worked!! I’ve been trying to post the above reply for about a month, but it always failed silently. Apparently it was because I added an HTML tag to the Firebreath project, because after I eliminated that HTML tag, the post worked. Anyhow, the domain is firebreath.org.

  26. Nico says:

    HI guys, I need to make a safari plugin!
    I will appreciate if any send me the example or re-upload in Firefox’s filesystem (Josh Aas or Eric Shepherd).
    I could use very good any read support or code and understand how it works.
    Thanks you.
    Regards!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s