Revision 110996 of Add to iPhoto

  • Revision slug: Mozilla/js-ctypes/Examples/Add_to_iPhoto
  • Revision title: Add to iPhoto
  • Revision id: 110996
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment checkpoint save; 617 words added

Revision Content

{{ gecko_minversion_header("1.9.3") }}

{{ draft() }}

This extension for Mac OS X serves as a demonstration of how to use js-ctypes to call Mac OS X Carbon, Core Foundation, and other system frameworks from an extension written entirely in JavaScript.

Note: This extension relies on changes added to Gecko as of the April 16, 2010 nightly of mozilla-central.

You can download an installable version of this extension on AMO.

Once installed, when you right-click on an image, you'll see among the options in the contextual menu an option to "Add Image to iPhoto". Choose it, and iPhoto will start up (if it's not already running) and import the image.

Declaring the APIs

The first thing we have to do is declare the Mac OS X APIs we'll be using. This extension uses a number of methods and data types, as well as constants, from three system frameworks.

Since a lot of this stuff is repetitive, we'll only look at selected parts of the code to get an idea how things work. You can download the extension and poke through the code inside it if you'd like to see all of it.

For the sake of organization, I chose to implement each system framework (and, mind you, I only declare the APIs I actually use, not all of them) as a JavaScript object containing all the types and methods that framework's API.

Core Foundation

The majority of the system routines we'll be using come from Core Foundation. Among these are routines for managing CFString, CFURL, and CFArray objects, among others. These are core system data formats that are used by other frameworks, and we'll be making use of them.

The Core Foundation API is implemented by the CoreFoundation object, which consists of two methods to initialize and shut down the library, a reference to the library, and all the types and methods declared to support Core Foundation.

Initializing Core Foundation

The init() method, which sets everything up, looks like this:

  init: function() {
    this.lib = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
    
    // declaring all the APIs goes here
  }

Shutting down Core Foundation

While the Core Foundation system framework itself doesn't need to be shut down, we do need to close the library we opened using the js-ctypes API; that's where the shutdown() method comes in:

  shutdown: function() {
    this.lib.close();
  }

Select API declarations

Let's take a look at a few of the key APIs we declare for Core Foundation, to see how it's done.

CFRange

CFRange is a structure that identifies a range; that is, it identifies an offset to an item in a list and a number of items. In C, the declaration looks like this:

typedef struct {
    CFIndex location;
    CFIndex length;
} CFRange;

To declare this for use with js-ctypes, we use the following code:

this.CFRange = new ctypes.StructType("CFRange",
                    [ {'location': ctypes.int32_t},
                      {'length': ctypes.int32_t}]);

This defines CoreFoundation.CFRange to represent this data type, comprised of two 32-bit integer fields called location and length.

Generic CFType routines

All Core Foundation data types are based upon a core CFType data type. Basic CFType routines handle memory management, dumping CFType objects to the console, comparing CFType values, and so forth. We'll be using a number of these methods, but for brevity's sake, since these are generally simple declarations, let's look at only the CFRelease() and CFRetain() declarations.

In C, these are declared thusly:

void CFRelease(CFTypeRef cf);
void CFRetain(CFTypeRef cf);

In JavaScript, this translates to:

    this.CFRelease = this.lib.declare("CFRelease",
                                ctypes.default_abi,
                                ctypes.void_t,
                                ctypes.voidptr_t);        // input: object to release
    
    this.CFRetain = this.lib.declare("CFRetain",
                                ctypes.default_abi,
                                ctypes.void_t,
                                ctypes.voidptr_t);        // input: object to retain

These methods are used to manage the reference counting for Core Foundation objects.

CFString

A CFString is an opaque data type that contains a string. The string can be stored in any of a number of encodings, so you use assorted functions that know how to cope with different encodings to set and get values of CFStrings, as well as to perform typical string operations.

The first declaration to be done here is to actually declare the CFStringRef data type; this is an opaque pointer to a CFString object.

this.CFStringRef = new ctypes.PointerType("CFStringRef");

Now that we've declared the core type, we can declare the methods we use that work with CFString objects. Let's take a look at one of these.

    this.CFStringCreateWithCharacters = this.lib.declare("CFStringCreateWithCharacters",
                                ctypes.default_abi,
                                this.CFStringRef,         // returns a new CFStringRef
                                ctypes.voidptr_t,         // allocator
                                ctypes.jschar.ptr,        // pointer to the Unicode string
                                ctypes.int32_t);          // length of the string

CFStringCreateWithCharacters() is used to create a new CFString object using a Unicode string as the source string, which is copied into the new CFString object. It returns a CFStringRef, which is a pointer to the new string, and accepts, as input, three parameters: an allocator, which is a pointer to a routine that will allocate the memory to contain the new object (we use the ctypes.voidptr_t type for this), a pointer to the Unicode string to copy into the new string object (ctypes.jschar.ptr), and the length of the Unicode string in characters.

Revision Source

<p>{{ gecko_minversion_header("1.9.3") }}</p>
<p>{{ draft() }}</p>
<p>This extension for Mac OS X serves as a demonstration of how to use js-ctypes to call Mac OS X Carbon, Core Foundation, and other system frameworks from an extension written entirely in JavaScript.</p>
<div class="note"><strong>Note:</strong> This extension relies on changes added to Gecko as of the April 16, 2010 nightly of mozilla-central.</div>
<p>You can <a class=" link-https" href="https://addons.mozilla.org/en-US/firefox/addon/146328/" title="https://addons.mozilla.org/en-US/firefox/addon/146328/">download an installable version</a> of this extension on AMO.</p>
<p>Once installed, when you right-click on an image, you'll see among the options in the contextual menu an option to "Add Image to iPhoto". Choose it, and iPhoto will start up (if it's not already running) and import the image.</p>
<h2>Declaring the APIs</h2>
<p>The first thing we have to do is declare the Mac OS X APIs we'll be using. This extension uses a number of methods and data types, as well as constants, from three system frameworks.</p>
<p>Since a lot of this stuff is repetitive, we'll only look at selected parts of the code to get an idea how things work. You can download the extension and poke through the code inside it if you'd like to see all of it.</p>
<p>For the sake of organization, I chose to implement each system framework (and, mind you, I only declare the APIs I actually use, not all of them) as a JavaScript object containing all the types and methods that framework's API.</p>
<h3>Core Foundation</h3>
<p>The majority of the system routines we'll be using come from Core Foundation. Among these are routines for managing <a class=" external" href="http://developer.apple.com/Mac/library/documentation/CoreFoundation/Reference/CFStringRef/Reference/reference.html" title="http://developer.apple.com/Mac/library/documentation/CoreFoundation/Reference/CFStringRef/Reference/reference.html"><code>CFString</code></a>, <a class=" external" href="http://developer.apple.com/mac/library/documentation/CoreFoundation/Reference/CFURLRef/Reference/reference.html" title="http://developer.apple.com/mac/library/documentation/CoreFoundation/Reference/CFURLRef/Reference/reference.html"><code>CFURL</code></a>, and <a class=" external" href="http://developer.apple.com/mac/library/DOCUMENTATION/CoreFoundation/Reference/CFArrayRef/Reference/reference.html" title="http://developer.apple.com/mac/library/DOCUMENTATION/CoreFoundation/Reference/CFArrayRef/Reference/reference.html"><code>CFArray</code></a> objects, among others. These are core system data formats that are used by other frameworks, and we'll be making use of them.</p>
<p>The Core Foundation API is implemented by the <code>CoreFoundation</code> object, which consists of two methods to initialize and shut down the library, a reference to the library, and all the types and methods declared to support Core Foundation.</p>
<h4>Initializing Core Foundation</h4>
<p>The <code>init()</code> method, which sets everything up, looks like this:</p>
<pre class="brush: js">  init: function() {
    this.lib = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
    
    // declaring all the APIs goes here
  }
</pre>
<h4>Shutting down Core Foundation</h4>
<p>While the Core Foundation system framework itself doesn't need to be shut down, we do need to close the library we opened using the js-ctypes API; that's where the <code>shutdown()</code> method comes in:</p>
<pre class="brush: js">  shutdown: function() {
    this.lib.close();
  }
</pre>
<h4>Select API declarations</h4>
<p>Let's take a look at a few of the key APIs we declare for Core Foundation, to see how it's done.</p>
<h5>CFRange</h5>
<p><code>CFRange</code> is a structure that identifies a <em>range</em>; that is, it identifies an offset to an item in a list and a number of items. In C, the declaration looks like this:</p>
<pre class="brush: cpp">typedef struct {
    CFIndex location;
    CFIndex length;
} CFRange;
</pre>
<p>To declare this for use with js-ctypes, we use the following code:</p>
<pre class="brush: js">this.CFRange = new ctypes.StructType("CFRange",
                    [ {'location': ctypes.int32_t},
                      {'length': ctypes.int32_t}]);
</pre>
<p>This defines <code>CoreFoundation.CFRange</code> to represent this data type, comprised of two 32-bit integer fields called <code>location</code> and <code>length</code>.</p>
<h5>Generic CFType routines</h5>
<p>All Core Foundation data types are based upon a core <code>CFType</code> data type. Basic <code>CFType</code> routines handle memory management, dumping <code>CFType</code> objects to the console, comparing <code>CFType</code> values, and so forth. We'll be using a number of these methods, but for brevity's sake, since these are generally simple declarations, let's look at only the <code>CFRelease()</code> and <code>CFRetain()</code> declarations.</p>
<p>In C, these are declared thusly:</p>
<pre class="brush: cpp">void CFRelease(CFTypeRef cf);
void CFRetain(CFTypeRef cf);
</pre>
<p>In JavaScript, this translates to:</p>
<pre class="brush: js">    this.CFRelease = this.lib.declare("CFRelease",
                                ctypes.default_abi,
                                ctypes.void_t,
                                ctypes.voidptr_t);        // input: object to release
    
    this.CFRetain = this.lib.declare("CFRetain",
                                ctypes.default_abi,
                                ctypes.void_t,
                                ctypes.voidptr_t);        // input: object to retain
</pre>
<p>These methods are used to manage the reference counting for Core Foundation objects.</p>
<h5>CFString</h5>
<p>A <code>CFString</code> is an opaque data type that contains a string. The string can be stored in any of a number of encodings, so you use assorted functions that know how to cope with different encodings to set and get values of <code>CFString</code>s, as well as to perform typical string operations.</p>
<p>The first declaration to be done here is to actually declare the <code>CFStringRef</code> data type; this is an opaque pointer to a <code>CFString</code> object.</p>
<pre class="brush: js">this.CFStringRef = new ctypes.PointerType("CFStringRef");
</pre>
<p>Now that we've declared the core type, we can declare the methods we use that work with <code>CFString</code> objects. Let's take a look at one of these.</p>
<pre>    this.CFStringCreateWithCharacters = this.lib.declare("CFStringCreateWithCharacters",
                                ctypes.default_abi,
                                this.CFStringRef,         // returns a new CFStringRef
                                ctypes.voidptr_t,         // allocator
                                ctypes.jschar.ptr,        // pointer to the Unicode string
                                ctypes.int32_t);          // length of the string
</pre>
<p><code>CFStringCreateWithCharacters()</code> is used to create a new <code>CFString</code> object using a Unicode string as the source string, which is copied into the new <code>CFString</code> object. It returns a <code>CFStringRef</code>, which is a pointer to the new string, and accepts, as input, three parameters: an allocator, which is a pointer to a routine that will allocate the memory to contain the new object (we use the <code>ctypes.voidptr_t</code> type for this), a pointer to the Unicode string to copy into the new string object (<code>ctypes.jschar.ptr</code>), and the length of the Unicode string in characters.</p>
Revert to this revision