diff --git a/configure.in b/configure.in
--- a/configure.in
+++ b/configure.in
@@ -8476,16 +8476,21 @@ if test "$MOZ_MEMORY"; then
    ac_configure_args="$ac_configure_args --enable-jemalloc"
    if test -n "$MOZ_MEMORY_LDFLAGS"; then
      export MOZ_MEMORY_LDFLAGS
    fi
 fi
 AC_OUTPUT_SUBDIRS(js/src)
 ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 
+# Run the libffi 'configure' script.
+ac_configure_args="--disable-shared --enable-static --with-pic"
+AC_OUTPUT_SUBDIRS(toolkit/components/js-ctypes/src/libffi)
+ac_configure_args="$_SUBDIR_CONFIG_ARGS"
+
 fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR
 
 dnl Prevent the regeneration of autoconf.mk forcing rebuilds of the world
 dnl Needs to be at the end to respect possible changes from NSPR configure
 if cmp -s config/autoconf.mk config/autoconf.mk.orig; then
   echo "config/autoconf.mk is unchanged"
   mv -f config/autoconf.mk.orig config/autoconf.mk 2> /dev/null
 else
diff --git a/toolkit/components/Makefile.in b/toolkit/components/Makefile.in
--- a/toolkit/components/Makefile.in
+++ b/toolkit/components/Makefile.in
@@ -57,16 +57,17 @@ DIRS += \
 	filepicker \
 	console \
 	cookie \
 	exthelper \
 	viewconfig \
 	typeaheadfind \
 	parentalcontrols \
 	passwordmgr \
+	js-ctypes \
 	$(NULL)
 
 ifneq (,$(filter cocoa, $(MOZ_WIDGET_TOOLKIT)))
 TOOL_DIRS += alerts
 else
 DIRS += alerts
 endif
 
diff --git a/toolkit/components/build/Makefile.in b/toolkit/components/build/Makefile.in
--- a/toolkit/components/build/Makefile.in
+++ b/toolkit/components/build/Makefile.in
@@ -66,16 +66,17 @@ CPPSRCS = nsToolkitCompsModule.cpp
 include $(topsrcdir)/config/config.mk
 
 LOCAL_INCLUDES = \
 	-I$(srcdir)/../downloads/src \
 	-I$(srcdir)/../startup/src \
 	-I$(srcdir)/../typeaheadfind/src \
 	-I$(srcdir)/../url-classifier/src \
 	-I$(srcdir)/../feeds/src \
+	-I$(srcdir)/../js-ctypes/src \
 	$(NULL)
 
 ifdef ALERTS_SERVICE
 LOCAL_INCLUDES += \
 	-I$(srcdir)/../alerts/src \
 	$(NULL)
 endif
 
@@ -111,16 +112,18 @@ endif
 
 ifdef MOZ_FEEDS
 SHARED_LIBRARY_LIBS += ../feeds/src/$(LIB_PREFIX)feed_s.$(LIB_SUFFIX)
 endif
 
 SHARED_LIBRARY_LIBS += ../typeaheadfind/src/$(LIB_PREFIX)fastfind_s.$(LIB_SUFFIX)
 EXTRA_DSO_LIBS = gkgfx
 
+SHARED_LIBRARY_LIBS += ../js-ctypes/src/$(LIB_PREFIX)js-ctypes_s.$(LIB_SUFFIX)
+
 EXTRA_DSO_LDOPTS += \
 	$(LIBS_DIR) \
 	$(EXTRA_DSO_LIBS) \
 	$(MOZ_UNICHARUTIL_LIBS) \
 	$(MOZ_COMPONENT_LIBS) \
 	$(MOZ_JS_LIBS) \
 	$(NULL)
 
diff --git a/toolkit/components/build/nsToolkitCompsCID.h b/toolkit/components/build/nsToolkitCompsCID.h
--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -111,16 +111,19 @@
   "@mozilla.org/browser/livemark-service;2"
 
 #define NS_MORKHISTORYIMPORTER_CONTRACTID \
   "@mozilla.org/browser/history-importer;1"
 
 #define NS_FAVICONSERVICE_CONTRACTID \
   "@mozilla.org/browser/favicon-service;1"
 
+#define NS_NATIVETYPES_CONTRACTID \
+  "@developer.mozilla.org/js-ctypes;1"
+
 /////////////////////////////////////////////////////////////////////////////
 
 // {A0CCAAF8-09DA-44D8-B250-9AC3E93C8117}
 #define NS_ALERTSSERVICE_CID \
 { 0xa0ccaaf8, 0x9da, 0x44d8, { 0xb2, 0x50, 0x9a, 0xc3, 0xe9, 0x3c, 0x81, 0x17 } }
 
 // {84E11F80-CA55-11DD-AD8B-0800200C9A66}
 #define NS_SYSTEMALERTSSERVICE_CID \
@@ -198,8 +201,11 @@
 #define NS_NAVBOOKMARKSSERVICE_CID \
 { 0x9de95a0c, 0x39a4, 0x4d64, {0x9a, 0x53, 0x17, 0x94, 0x0d, 0xd7, 0xca, 0xbb}}
 
 #define NS_MORKHISTORYIMPORTER_CID \
 { 0x428e6d12, 0x9c6d, 0x436f, {0xb7, 0xa3, 0x6c, 0xa5, 0xf4, 0x80, 0x92, 0x12}}
 
 #define NS_FAVICONSERVICE_CID \
 { 0x984e3259, 0x9266, 0x49cf, { 0xb6, 0x05, 0x60, 0xb0, 0x22, 0xa0, 0x07, 0x56 } }
+
+#define NS_NATIVETYPES_CID \
+{ 0xc797702, 0x1c60, 0x4051, { 0x9d, 0xd7, 0x4d, 0x74, 0x5, 0x60, 0x56, 0x42 } }
diff --git a/toolkit/components/build/nsToolkitCompsModule.cpp b/toolkit/components/build/nsToolkitCompsModule.cpp
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -63,16 +63,18 @@
 #include "nsUrlClassifierHashCompleter.h"
 #include "nsDocShellCID.h"
 #endif
 
 #ifdef MOZ_FEEDS
 #include "nsScriptableUnescapeHTML.h"
 #endif
 
+#include "nsNativeTypes.h"
+
 /////////////////////////////////////////////////////////////////////////////
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAppStartup, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUserInfo)
 
 #if defined(XP_WIN) && !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsParentalControlsServiceWin)
 #endif
@@ -113,16 +115,18 @@ nsUrlClassifierDBServiceConstructor(nsIS
     return rv;
 }
 #endif
 
 #ifdef MOZ_FEEDS
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableUnescapeHTML)
 #endif
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeTypes)
+
 /////////////////////////////////////////////////////////////////////////////
 
 static const nsModuleComponentInfo components[] =
 {
   { "App Startup Service",
     NS_TOOLKIT_APPSTARTUP_CID,
     NS_APPSTARTUP_CONTRACTID,
     nsAppStartupConstructor },
@@ -181,11 +185,15 @@ static const nsModuleComponentInfo compo
     nsUrlClassifierHashCompleterConstructor },
 #endif
 #ifdef MOZ_FEEDS
   { "Unescape HTML",
     NS_SCRIPTABLEUNESCAPEHTML_CID,
     NS_SCRIPTABLEUNESCAPEHTML_CONTRACTID,
     nsScriptableUnescapeHTMLConstructor },
 #endif
+  { "js-ctypes Component",
+    NS_NATIVETYPES_CID,
+    NS_NATIVETYPES_CONTRACTID,
+    nsNativeTypesConstructor },
 };
 
 NS_IMPL_NSGETMODULE(nsToolkitCompsModule, components)
diff --git a/toolkit/components/js-ctypes/Makefile.in b/toolkit/components/js-ctypes/Makefile.in
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/Makefile.in
@@ -0,0 +1,46 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the js-ctypes.
+#
+# Contributor(s):
+#  Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = public src
+
+ifdef ENABLE_TESTS
+DIRS += tests
+endif
+
+include $(topsrcdir)/config/rules.mk
diff --git a/toolkit/components/js-ctypes/public/Makefile.in b/toolkit/components/js-ctypes/public/Makefile.in
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/public/Makefile.in
@@ -0,0 +1,47 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the js-ctypes.
+#
+# Contributor(s):
+#  Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = js-ctypes
+XPIDL_MODULE = js-ctypes
+
+XPIDLSRCS	= \
+		nsINativeTypes.idl \
+		$(NULL)
+
+include $(topsrcdir)/config/rules.mk
diff --git a/toolkit/components/js-ctypes/public/nsINativeTypes.idl b/toolkit/components/js-ctypes/public/nsINativeTypes.idl
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/public/nsINativeTypes.idl
@@ -0,0 +1,93 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is js-ctypes.
+ *
+ * Contributor(s):
+ *  Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+/**
+ * Magical interface that implements nsIXPCScriptable
+ * so it can act as a callable object in JS
+ */
+[scriptable, uuid(a54a5c6c-9529-418c-9d9f-b8111c6e247d)]
+interface nsINativeMethod : nsISupports
+{
+};
+
+/**
+ * Interface that wraps a dynamic library and can create
+ * callable 'method' objects from exportable functions in the library
+ */
+[scriptable, uuid(352a72c1-5b99-451e-a330-c45079d8f087)]
+interface nsINativeTypes : nsISupports
+{
+    const PRUint16 DEFAULT    = 0;
+    const PRUint16 STDCALL    = 1;
+    const PRUint16 SYSV       = 2;
+    const PRUint16 UNIX64     = 3;
+
+    const PRUint16 UNKNOWN    = 0;
+    const PRUint16 VOID       = 1;
+    const PRUint16 BOOL       = 2;
+    const PRUint16 INT8       = 3;
+    const PRUint16 INT16      = 4;
+    const PRUint16 INT32      = 5;
+    const PRUint16 INT64      = 6;
+    const PRUint16 FLOAT      = 7;
+    const PRUint16 DOUBLE     = 8;
+    const PRUint16 STRING     = 9;
+    const PRUint16 WSTRING    = 10;
+    const PRUint16 STRUCT     = 11;
+
+    /**
+     * Returns the name of the dynamically loaded library
+     */
+    readonly attribute AString name;
+
+    /**
+     * Attempts to dynamically load the specified library
+     */
+    boolean open(in AString aName);
+
+    /**
+     * Unloads the currently loaded library. This will cause any active
+     * nsINativeMethod objects to fail.
+     */
+    boolean close();
+
+    /**
+     * Used to specify an exported method from the currently loaded library. Even
+     * though the method appears to take no parameters, it does in fact require
+     * several params. It uses some XPCOM/JSAPI magic to extract the parameters.
+     *
+     * declare(in AString aName, in int aReturnType, in int aArgType1, ...)
+     */
+    nsINativeMethod declare();
+};
diff --git a/toolkit/components/js-ctypes/src/Makefile.in b/toolkit/components/js-ctypes/src/Makefile.in
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/src/Makefile.in
@@ -0,0 +1,100 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the js-ctypes.
+#
+# Contributor(s):
+#  Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = js-ctypes
+LIBRARY_NAME = js-ctypes_s
+GRE_MODULE = 1
+FORCE_STATIC_LIB = 1
+LIBXUL_LIBRARY = 1
+#USE_STATIC_LIBS = 1
+
+CPPSRCS = \
+    nsNativeMethod.cpp \
+    nsNativeTypes.cpp \
+    $(NULL)
+
+LOCAL_INCLUDES += -Ilibffi -I$(srcdir)/libffi/include
+
+FFI_SRC_DIR = libffi/src
+
+CSRCS   = \
+    $(FFI_SRC_DIR)/prep_cif.c \
+    $(FFI_SRC_DIR)/types.c \
+    $(NULL)
+
+ifeq ($(OS_TEST),x86)
+FFI_ARCH_DIR = $(FFI_SRC_DIR)/x86
+CSRCS  += $(FFI_ARCH_DIR)/ffi.c
+ifeq ($(OS_ARCH),Linux)
+ASFILES = $(FFI_ARCH_DIR)/sysv.S
+endif
+ifeq ($(OS_ARCH),FreeBSD)
+ASFILES = $(FFI_ARCH_DIR)/freebsd.S
+endif
+ifeq (,$(filter WINNT WINCE,$(OS_ARCH)))
+ASFILES = $(FFI_ARCH_DIR)/win32.S
+endif
+endif
+
+ifeq ($(OS_TEST),x86_64)
+FFI_ARCH_DIR = $(FFI_SRC_DIR)/x86
+CSRCS  += $(FFI_ARCH_DIR)/ffi.c $(FFI_ARCH_DIR)/ffi64.c
+ifeq ($(OS_ARCH),Linux)
+ASFILES = $(FFI_ARCH_DIR)/sysv.S $(FFI_ARCH_DIR)/unix64.S
+endif
+ifeq ($(OS_ARCH),Darwin)
+ASFILES = $(FFI_ARCH_DIR)/darwin.S $(FFI_ARCH_DIR)/darwin64.S
+endif
+endif
+
+ifeq ($(OS_TEST),arm)
+FFI_ARCH_DIR = $(FFI_SRC_DIR)/arm
+ifeq (,$(filter WINNT WINCE,$(OS_ARCH)))
+CSRCS  += $(FFI_ARCH_DIR)/ffi.c
+ASFILES = $(FFI_ARCH_DIR)/sysv.S
+endif
+endif
+
+ASFLAGS   += $(LOCAL_INCLUDES)
+ASM_SUFFIX = S
+
+# TODO: rm -rf libffi/ on |make clean|
+
+include $(topsrcdir)/config/rules.mk
+
diff --git a/toolkit/components/js-ctypes/src/nsNativeMethod.cpp b/toolkit/components/js-ctypes/src/nsNativeMethod.cpp
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/src/nsNativeMethod.cpp
@@ -0,0 +1,682 @@
+#include "nsNativeMethod.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIXPConnect.h"
+#include "jsprvtd.h"
+#include "jsnum.h"
+
+NS_IMPL_ISUPPORTS2(nsNativeMethod, nsINativeMethod, nsIXPCScriptable)
+
+nsNativeMethod::nsNativeMethod()
+{
+  /* member initializers and constructor code */
+}
+
+nsNativeMethod::~nsNativeMethod()
+{
+  /* destructor code */
+}
+
+nsresult nsNativeMethod::Init(JSContext* aContext, const nsAString& aName, PRFuncPtr aFunc, PRUint16 aCallType, nsArgumentType aResultType, const nsTArray<nsArgumentType>& aArgTypes)
+{
+    nsresult rv;
+    JSAutoRequest ar(aContext);
+
+    mName = aName;
+    mFunc = aFunc;
+
+    // determine the ABI
+    switch (aCallType) {
+#ifdef WIN32
+        case nsINativeTypes::STDCALL:
+            mCallType = FFI_STDCALL;
+            break;
+#else
+        case nsINativeTypes::STDCALL:
+            // STDCALL on non-WIN32 doesn't exist.
+            return NS_ERROR_INVALID_ABI;
+#endif
+        case nsINativeTypes::SYSV:
+            mCallType = FFI_SYSV;
+            break;
+        case nsINativeTypes::UNIX64:
+            mCallType = FFI_UNIX64;
+            break;
+        case nsINativeTypes::DEFAULT:
+            mCallType = FFI_DEFAULT_ABI;
+            break;
+        default:
+            return NS_ERROR_INVALID_ARG;
+    }
+
+    // prepare the argument types
+    for (PRUint32 i = 0; i < aArgTypes.Length(); ++i) {
+        rv = PrepareType(aContext, aArgTypes[i], *mArgTypes.AppendElement());
+        if (NS_FAILED(rv)) return rv;
+
+        // ffi_prep_cif requires an array of ffi_types, so prepare a separate array
+        mFFITypes.AppendElement(&mArgTypes[i].mType);
+    }
+
+    // prepare the result type
+    rv = PrepareType(aContext, aResultType, mResultType);
+    if (NS_FAILED(rv)) return rv;
+
+    ffi_status status = ffi_prep_cif(&mCIF, mCallType, mFFITypes.Length(), &(mResultType.mType), mFFITypes.Elements());
+    // TODO: propagate invalid abi error code
+    if (status != FFI_OK) return NS_ERROR_FAILURE;
+
+    return NS_OK;
+}
+
+nsresult
+nsNativeMethod::PrepareType(JSContext* aContext, nsArgumentType aType, nsNativeType& aResult)
+{
+    aResult.mNativeType = aType.mType;
+    switch (aType.mType) {
+        case nsINativeTypes::VOID:
+            aResult.mType = ffi_type_void;
+            break;
+        case nsINativeTypes::BOOL:
+        case nsINativeTypes::INT8:
+            aResult.mType = ffi_type_sint8;
+            break;
+        case nsINativeTypes::INT16:
+            aResult.mType = ffi_type_sint16;
+            break;
+        case nsINativeTypes::INT32:
+            aResult.mType = ffi_type_sint32;
+            break;
+        case nsINativeTypes::INT64:
+            aResult.mType = ffi_type_sint64;
+            break;
+        case nsINativeTypes::FLOAT:
+            aResult.mType = ffi_type_float;
+            break;
+        case nsINativeTypes::DOUBLE:
+            aResult.mType = ffi_type_double;
+            break;
+        case nsINativeTypes::STRING:
+        case nsINativeTypes::WSTRING:
+            aResult.mType = ffi_type_pointer;
+            break;
+        case nsINativeTypes::STRUCT: {
+            // first, we need the prototype of the object
+            jsval proto;
+            if (JS_GetProperty(aContext, aType.mObject, "prototype", &proto) &&
+                JSVAL_IS_OBJECT(proto)) {
+                JSObject* obj = JSVAL_TO_OBJECT(proto);
+
+                // now get the _fields_ property...
+                jsval structFields;
+                if (!JS_GetProperty(aContext, obj, "_fields_", &structFields)) {
+                    JS_ReportError(aContext, "Couldn't find _fields_ property");
+                    return NS_ERROR_FAILURE;
+                }
+
+                JSObject* _fields_;
+                JS_ValueToObject(aContext, structFields, &_fields_);
+                jsuint len;
+                JS_GetArrayLength(aContext, _fields_, &len);
+
+                // fill in the ffi_type fields ourselves. ffi_prep_cif will
+                // determine and fill in the size and alignment for us.
+                aResult.mType.type = FFI_TYPE_STRUCT;
+                aResult.mType.size = 0;
+                aResult.mType.alignment = 0;
+                aResult.mType.elements = new ffi_type*[len + 1];
+                aResult.mType.elements[len] = NULL;
+
+                aResult.mElements = new nsNativeType[len];
+
+                nsArgumentType fieldType;
+                for (PRUint32 i = 0; i < len; ++i) {
+                    jsval item;
+                    JS_GetElement(aContext, _fields_, i, &item);
+                    JSObject* field;
+                    JS_ValueToObject(aContext, item, &field);
+                    if (JSObjExtractNameType(aContext, field, aResult.mElements[i].mName, fieldType) == PR_FALSE) {
+                        JS_ReportError(aContext, "Failed to extract data from struct._fields_");
+                        return NS_ERROR_FAILURE;
+                    }
+
+                    // recursively get the type of each field
+                    nsresult rv = PrepareType(aContext, fieldType, aResult.mElements[i]);
+                    if (NS_FAILED(rv)) return rv;
+
+                    aResult.mType.elements[i] = &aResult.mElements[i].mType;
+                }
+            }
+            break;
+        }
+        default: {
+            return NS_ERROR_NOT_IMPLEMENTED;
+        }
+    }
+
+    return NS_OK;
+}
+
+nsresult
+nsNativeMethod::PrepareValue(JSContext* aContext, const nsNativeType& aType, jsval aValue, nsNativeValue& aResult)
+{
+    nsresult rv;
+    int32 int32Buffer;
+    double dblBuffer;
+
+    switch (aType.mNativeType) {
+        case nsINativeTypes::BOOL:
+            JSBool b;
+            if (!JS_ValueToBoolean(aContext, aValue, &b))
+                return NS_ERROR_FAILURE;
+
+            aResult.mValue.mInt8 = (PRInt8)b;
+            aResult.mData = (void*)&(aResult.mValue.mInt8);
+            break;
+        case nsINativeTypes::INT8:
+            if (!JS_ValueToECMAInt32(aContext, aValue, &int32Buffer))
+                return NS_ERROR_FAILURE;
+
+            aResult.mValue.mInt8 = (PRUint8)int32Buffer;
+            aResult.mData = (void*)&(aResult.mValue.mInt8);
+            break;
+        case nsINativeTypes::INT16:
+            if (!JS_ValueToECMAInt32(aContext, aValue, &int32Buffer))
+                return NS_ERROR_FAILURE;
+
+            aResult.mValue.mInt16 = (PRInt16)int32Buffer;
+            aResult.mData = (void*)&(aResult.mValue.mInt16);
+            break;
+        case nsINativeTypes::INT32:
+            if (!JS_ValueToECMAInt32(aContext, aValue, &(aResult.mValue.mInt32)))
+                return NS_ERROR_FAILURE;
+
+            aResult.mData = (void*)&(aResult.mValue.mInt32);
+            break;
+        case nsINativeTypes::INT64:
+            if(JSVAL_IS_INT(aValue))
+            {
+                if(!JS_ValueToECMAInt32(aContext, aValue, &int32Buffer))
+                    return NS_ERROR_FAILURE;
+                LL_I2L(aResult.mValue.mInt64, int32Buffer);
+            }
+            else
+            {
+                if(!JS_ValueToNumber(aContext, aValue, &dblBuffer))
+                    return NS_ERROR_FAILURE;
+                LL_D2L(aResult.mValue.mInt64, dblBuffer);
+            }
+
+            aResult.mData = (void*)&(aResult.mValue.mInt64);
+            break;
+        case nsINativeTypes::FLOAT:
+            if (!JS_ValueToNumber(aContext, aValue, &dblBuffer))
+                return NS_ERROR_FAILURE;
+            aResult.mValue.mFloat = (float)dblBuffer;
+
+            aResult.mData = (void*)&(aResult.mValue.mFloat);
+            break;
+        case nsINativeTypes::DOUBLE:
+            if (!JS_ValueToNumber(aContext, aValue, &(aResult.mValue.mDouble)))
+                return NS_ERROR_FAILURE;
+
+            aResult.mData = (void*)&(aResult.mValue.mDouble);
+            break;
+        case nsINativeTypes::STRING: {
+            char* bytes = nsnull;
+            JSString* str;
+            if (!(str = JS_ValueToString(aContext, aValue)) || !(bytes = JS_GetStringBytes(str)))
+                return NS_ERROR_FAILURE;
+
+            aResult.mValue.mPointer = (void*)bytes;
+            aResult.mData = (void*)&(aResult.mValue.mPointer);
+            break;
+        }
+        case nsINativeTypes::WSTRING: {
+            jschar* chars = nsnull;
+            JSString* str;
+            if (!(str = JS_ValueToString(aContext, aValue)) || !(chars = JS_GetStringChars(str)))
+                return NS_ERROR_FAILURE;
+
+            aResult.mValue.mPointer = (void*)chars;
+            aResult.mData = (void*)&(aResult.mValue.mPointer);
+            break;
+        }
+        case nsINativeTypes::STRUCT: {
+            // we've already pulled out the field names for the struct,
+            // so just enumerate over them and get the corresponding property values
+            // from the object.
+            JSObject* obj;
+            JS_ValueToObject(aContext, aValue, &obj);
+            if (obj) {
+                PRUint32 i;
+                for (i = 0; aType.mType.elements[i]; ++i);
+                aResult.mElements = new nsNativeValue[i];
+
+                for (i = 0; aType.mType.elements[i]; ++i) {
+                    const nsCString& fieldName = aType.mElements[i].mName;
+
+                    jsval fieldVal;
+                    if (!JS_GetProperty(aContext, obj, fieldName.get(), &fieldVal)) {
+                        JS_ReportError(aContext, "Couldn't find value of field");
+                        return NS_ERROR_FAILURE;
+                    }
+
+                    rv = PrepareValue(aContext, aType.mElements[i], fieldVal, aResult.mElements[i]);
+                    if (NS_FAILED(rv)) return rv;
+                }
+            }
+            break;
+        }
+        default: {
+            return NS_ERROR_NOT_IMPLEMENTED;
+        }
+    }
+
+    return NS_OK;
+}
+
+static void
+PrepareReturnValue(const nsNativeType& aType, nsNativeValue& aResult)
+{
+    switch (aType.mNativeType) {
+        case nsINativeTypes::BOOL:
+            aResult.mData = &(aResult.mValue.mInt8);
+            break;
+        case nsINativeTypes::INT8:
+            aResult.mData = &(aResult.mValue.mInt8);
+            break;
+        case nsINativeTypes::INT16:
+            aResult.mData = &(aResult.mValue.mInt16);
+            break;
+        case nsINativeTypes::INT32:
+            aResult.mData = &(aResult.mValue.mInt32);
+            break;
+        case nsINativeTypes::INT64:
+            aResult.mData = &(aResult.mValue.mInt64);
+            break;
+        case nsINativeTypes::FLOAT:
+            aResult.mData = &(aResult.mValue.mFloat);
+            break;
+        case nsINativeTypes::DOUBLE:
+            aResult.mData = &(aResult.mValue.mDouble);
+            break;
+        case nsINativeTypes::STRING:
+        case nsINativeTypes::WSTRING:
+            aResult.mData = &(aResult.mValue.mPointer);
+            break;
+        case nsINativeTypes::STRUCT:
+            aResult.mData = new char[aType.mType.size];
+            break;
+        default:
+            break;
+    }
+}
+
+nsresult
+nsNativeMethod::ConvertReturnValue(JSContext* aContext, const nsNativeType& aResultType, const nsNativeValue& aResultValue, jsval* aValue)
+{
+    switch (aResultType.mNativeType) {
+        case nsINativeTypes::VOID:
+            *aValue = JSVAL_NULL;
+            break;
+        case nsINativeTypes::BOOL:
+            *aValue = BOOLEAN_TO_JSVAL(aResultValue.mValue.mInt8 ? JS_TRUE : JS_FALSE);
+            break;
+        case nsINativeTypes::INT8:
+            *aValue = INT_TO_JSVAL(aResultValue.mValue.mInt8);
+            break;
+        case nsINativeTypes::INT16:
+            *aValue = INT_TO_JSVAL(aResultValue.mValue.mInt16);
+            break;
+        case nsINativeTypes::INT32:
+            if (INT_FITS_IN_JSVAL(aResultValue.mValue.mInt32))
+              *aValue = INT_TO_JSVAL(aResultValue.mValue.mInt32);
+            else
+              JS_NewDoubleValue(aContext, (double)(aResultValue.mValue.mInt32), aValue);
+            break;
+        case nsINativeTypes::INT64:
+            JS_NewDoubleValue(aContext, (double)(aResultValue.mValue.mInt64), aValue);
+            break;
+        case nsINativeTypes::FLOAT:
+            JS_NewDoubleValue(aContext, (double)(aResultValue.mValue.mFloat), aValue);
+            break;
+        case nsINativeTypes::DOUBLE:
+            JS_NewDoubleValue(aContext, aResultValue.mValue.mDouble, aValue);
+            break;
+        case nsINativeTypes::STRING: {
+            JSString *jsstring = JS_NewStringCopyZ(aContext, *(char**)&aResultValue.mValue.mPointer);
+            *aValue = STRING_TO_JSVAL(jsstring);
+            break;
+        }
+        case nsINativeTypes::WSTRING: {
+            JSString *jsstring = JS_NewUCStringCopyZ(aContext, reinterpret_cast<const jschar*>(*(PRUnichar**)&aResultValue.mValue.mPointer));
+            *aValue = STRING_TO_JSVAL(jsstring);
+            break;
+        }
+        // implementation of C struct -> JS object conversion can wait for a better impl
+        // of structs
+        case nsINativeTypes::STRUCT: {
+            delete[] static_cast<char*>(aResultValue.mData);
+            return NS_ERROR_NOT_IMPLEMENTED;
+        }
+        default: {
+            return NS_ERROR_NOT_IMPLEMENTED;
+        }
+    }
+
+    return NS_OK;
+}
+
+#define ALIGN_POINTER(ptr, align) \
+    do { \
+        char* masked = reinterpret_cast<char*>(reinterpret_cast<ptrdiff_t>(ptr) & ~(align - 1)); \
+        if (ptr != masked) \
+            ptr = masked + align; \
+    } while (0);
+
+static void
+PrepareStruct(const nsNativeType& aStructData, const nsNativeValue& aStructValues, void* aResult) {
+    char* fieldPtr = static_cast<char*>(aResult);
+
+    for (PRUint32 i = 0; aStructData.mType.elements[i]; ++i) {
+        const nsNativeType& elementData = aStructData.mElements[i];
+
+        ALIGN_POINTER(fieldPtr, elementData.mType.alignment);
+
+        if (elementData.mType.type == FFI_TYPE_STRUCT) {
+            PrepareStruct(elementData, aStructValues.mElements[i], fieldPtr);
+        } else {
+            memcpy(fieldPtr, aStructValues.mElements[i].mData, elementData.mType.size);
+        }
+
+        fieldPtr += elementData.mType.size;
+    }
+}
+
+nsresult
+nsNativeMethod::Execute(JSContext* aContext, PRUint32 aArgc, jsval* aArgv, jsval* aValue)
+{
+    nsresult rv;
+    JSAutoRequest ar(aContext);
+
+    // prepare the values for each argument
+    nsAutoTArray<nsNativeValue, 16> nativeValues;
+    for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
+        rv = PrepareValue(aContext, mArgTypes[i], aArgv[i], *nativeValues.AppendElement());
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    // create an array of pointers to each value, for passing to ffi_call
+    nsAutoTArray<void*, 16> values;
+    for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
+        values.AppendElement(nativeValues[i].mData);
+
+        // if we have a struct...
+        if (mArgTypes[i].mType.type == FFI_TYPE_STRUCT) {
+            // allocate data for the whole struct (including nested structs):
+            // ffi_prep_cif conveniently gives us the total size, including padding.
+            values[i] = new char[mArgTypes[i].mType.size];
+
+            // prepare struct fields in place.
+            // TODO: move this directly into PrepareValue()
+            PrepareStruct(mArgTypes[i], nativeValues[i], values[i]);
+        }
+    }
+
+    // initialize a pointer to an appropriate location, for storing the result
+    nsNativeValue resultValue;
+    PrepareReturnValue(mResultType, resultValue);
+
+    ffi_call(&mCIF, mFunc, resultValue.mData, values.Elements());
+
+    // free memory allocated for structs
+    for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
+        if (mArgTypes[i].mType.type == FFI_TYPE_STRUCT) {
+            delete[] static_cast<char*>(values[i]);
+        }
+    }
+
+    // prepare a JS object from the result
+    rv = ConvertReturnValue(aContext, mResultType, resultValue, aValue);
+    if (NS_FAILED(rv)) return rv;
+
+    return NS_OK;
+}
+
+/*** nsIXPCScriptable interface ***/
+
+/* readonly attribute string className; */
+NS_IMETHODIMP nsNativeMethod::GetClassName(char * *aClassName)
+{
+    NS_ENSURE_ARG_POINTER(aClassName);
+    *aClassName = (char *) nsMemory::Clone("nsNativeMethod", 12);
+    if (!*aClassName)
+        return NS_ERROR_OUT_OF_MEMORY;
+    return NS_OK;
+}
+
+/* readonly attribute PRUint32 scriptableFlags; */
+NS_IMETHODIMP nsNativeMethod::GetScriptableFlags(PRUint32 *aScriptableFlags)
+{
+    *aScriptableFlags =
+        nsIXPCScriptable::WANT_CALL |
+        nsIXPCScriptable::WANT_NEWRESOLVE |
+        nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE;
+    return NS_OK;
+}
+
+/* PRBool call (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in PRUint32 argc, in JSValPtr argv, in JSValPtr vp); */
+NS_IMETHODIMP nsNativeMethod::Call(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                          JSObject * obj, PRUint32 argc, jsval * argv, jsval * vp, PRBool *_retval)
+{
+    if (!mFunc) {
+        JS_ReportError(cx, "Function is null");
+        *_retval = PR_TRUE;
+        return NS_ERROR_FAILURE;
+    }
+
+    if (argc != mArgTypes.Length()) {
+        JS_ReportError(cx, "Number of arguments does not match declaration");
+        *_retval = PR_FALSE;
+        return NS_ERROR_FAILURE;
+    }
+
+    nsresult rv = NS_OK;
+    rv = Execute(cx, argc, argv, vp);
+
+    *_retval = PR_TRUE;
+    return rv;
+}
+
+/* PRBool getProperty (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in JSValPtr vp); */
+NS_IMETHODIMP nsNativeMethod::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                 JSObject * obj, jsval id, jsval * vp, PRBool *_retval)
+{
+    *_retval = PR_FALSE;
+    return NS_OK;
+}
+
+
+/* PRBool setProperty (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in JSValPtr vp); */
+NS_IMETHODIMP nsNativeMethod::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                         JSObject * obj, jsval id, jsval * vp, PRBool *_retval)
+{
+    *_retval = PR_FALSE;
+    return NS_OK;
+}
+
+/* void preCreate (in nsISupports nativeObj, in JSContextPtr cx, in JSObjectPtr globalObj, out JSObjectPtr parentObj); */
+NS_IMETHODIMP nsNativeMethod::PreCreate(nsISupports *nativeObj, JSContext * cx,
+                       JSObject * globalObj, JSObject * *parentObj)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void create (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */
+NS_IMETHODIMP nsNativeMethod::Create(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void postCreate (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */
+NS_IMETHODIMP nsNativeMethod::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool addProperty (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in JSValPtr vp); */
+NS_IMETHODIMP nsNativeMethod::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                 JSObject * obj, jsval id, jsval * vp, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool delProperty (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in JSValPtr vp); */
+NS_IMETHODIMP nsNativeMethod::DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                 JSObject * obj, jsval id, jsval * vp, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool enumerate (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */
+NS_IMETHODIMP nsNativeMethod::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                               JSObject * obj, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool newEnumerate (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in PRUint32 enum_op, in JSValPtr statep, out JSID idp); */
+NS_IMETHODIMP nsNativeMethod::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                  JSObject * obj, PRUint32 enum_op, jsval * statep, jsid *idp, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in PRUint32 flags, out JSObjectPtr objp); */
+NS_IMETHODIMP nsNativeMethod::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                JSObject * obj, jsval id, PRUint32 flags, JSObject * *objp, PRBool *_retval)
+{
+    *_retval = PR_TRUE;
+    return NS_OK;
+}
+
+/* PRBool convert (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in PRUint32 type, in JSValPtr vp); */
+NS_IMETHODIMP
+nsNativeMethod::Convert(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                JSObject * obj, PRUint32 type, jsval * vp, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void finalize (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */
+NS_IMETHODIMP nsNativeMethod::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                              JSObject * obj)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool checkAccess (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in PRUint32 mode, in JSValPtr vp); */
+NS_IMETHODIMP nsNativeMethod::CheckAccess(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                 JSObject * obj, jsval id, PRUint32 mode, jsval * vp, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool construct (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in PRUint32 argc, in JSValPtr argv, in JSValPtr vp); */
+NS_IMETHODIMP nsNativeMethod::Construct(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                               JSObject * obj, PRUint32 argc, jsval * argv, jsval * vp, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal val, out PRBool bp); */
+NS_IMETHODIMP nsNativeMethod::HasInstance(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                                 JSObject * obj, jsval val, PRBool *bp, PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void trace (in nsIXPConnectWrappedNative wrapper, in JSTracerPtr trc, in JSObjectPtr obj); */
+NS_IMETHODIMP nsNativeMethod::Trace(nsIXPConnectWrappedNative *wrapper,
+                                  JSTracer *trc, JSObject *obj)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRBool equality(in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal val); */
+NS_IMETHODIMP nsNativeMethod::Equality(nsIXPConnectWrappedNative *wrapper,
+                                    JSContext *cx, JSObject *obj, jsval val,
+                                    PRBool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* JSObjectPtr outerObject(in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */
+NS_IMETHODIMP nsNativeMethod::OuterObject(nsIXPConnectWrappedNative *wrapper,
+                                        JSContext *cx, JSObject *obj,
+                                        JSObject **_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* JSObjectPtr innerObject(in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */
+NS_IMETHODIMP nsNativeMethod::InnerObject(nsIXPConnectWrappedNative *wrapper,
+                                        JSContext *cx, JSObject *obj,
+                                        JSObject **_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void postCreatePrototype(in JSContextPtr cx, in JSObjectPtr proto); */
+NS_IMETHODIMP nsNativeMethod::PostCreatePrototype(JSContext *cx, JSObject *proto)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/*
+  Extract the name and type of a struct member.
+
+  Struct.prototype = {
+    _fields_ = [{"x" : INT32}]
+  }
+
+  will yield name = "x", type = INT32.
+*/
+PRBool JSObjExtractNameType(JSContext* aContext, JSObject* aObj, nsCString& aName, nsArgumentType& aType)
+{
+    JSObject* iter = JS_NewPropertyIterator(aContext, aObj);
+
+    jsid propId;
+    jsval propVal;
+    if (!JS_NextProperty(aContext, iter, &propId) ||
+        propId == JSVAL_VOID ||
+        !JS_IdToValue(aContext, propId, &propVal)) {
+        JS_ReportError(aContext, "Unable to find property in struct._fields_");
+        return PR_FALSE;
+    }
+
+    JSString* propName = JS_ValueToString(aContext, propVal);
+    char* str = JS_GetStringBytes(propName);
+    aName = str;
+
+    if (!JS_GetProperty(aContext, aObj, str, &propVal)) {
+        JS_ReportError(aContext, "Unable to find property value in struct._fields_");
+        return PR_FALSE;
+    }
+
+    if (JSVAL_IS_OBJECT(propVal)) {
+        // have a struct object. store the object, so we can traverse its properties.
+        aType.mType = nsINativeTypes::STRUCT;
+        aType.mObject = JSVAL_TO_OBJECT(propVal);
+        return PR_TRUE;
+    }
+
+    if (!JS_ValueToUint16(aContext, propVal, &aType.mType)) {
+        JS_ReportError(aContext, "Unable to find value of property in struct._fields_");
+        return PR_FALSE;
+    }
+
+    return PR_TRUE;
+}
+
diff --git a/toolkit/components/js-ctypes/src/nsNativeMethod.h b/toolkit/components/js-ctypes/src/nsNativeMethod.h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/src/nsNativeMethod.h
@@ -0,0 +1,120 @@
+#ifndef NSNATIVEMETHOD_H
+#define NSNATIVEMETHOD_H
+
+#include "nsINativeTypes.h"
+#include "nsIXPCScriptable.h"
+#include "nsMemory.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsCOMArray.h"
+#include "nsIVariant.h"
+#include "prlink.h"
+#include "jsapi.h"
+#include "ffi.h"
+
+struct nsArgumentType {
+    PRUint16 mType;
+    JSObject* mObject;
+};
+
+PRBool JSObjExtractNameType(JSContext* aContext, JSObject* aObj, nsCString& aName, nsArgumentType& aType);
+
+class nsNativeType {
+public:
+    nsNativeType()
+        : mType(ffi_type_void)
+        , mNativeType(nsINativeTypes::VOID)
+        , mElements(nsnull)
+    {
+    }
+
+    ~nsNativeType() {
+        if (mElements) {
+            // free struct field info
+            delete[] mElements;
+            delete[] mType.elements;
+        }
+    }
+
+    ffi_type mType;
+    PRUint16 mNativeType;
+    nsNativeType* mElements;
+    nsCString mName;
+
+private:
+    // copy construction forbidden, since we have manually allocated members.
+    nsNativeType(const nsNativeType& aOther);
+};
+
+class nsNativeValue {
+public:
+    nsNativeValue()
+        : mElements(nsnull)
+    {
+    }
+
+    ~nsNativeValue() {
+        if (mElements) {
+            // free struct field info
+            delete[] mElements;
+        }
+    }
+
+    void* mData;
+    union {
+        PRInt8  mInt8;
+        PRInt16 mInt16;
+        PRInt32 mInt32;
+        PRInt64 mInt64;
+        float   mFloat;
+        double  mDouble;
+        void*   mPointer;
+    } mValue;
+
+    nsNativeValue* mElements;
+
+private:
+    // copy construction forbidden, since we have manually allocated members.
+    nsNativeValue(const nsNativeValue& aOther);
+};
+
+class nsNativeMethod : public nsINativeMethod,
+                       public nsIXPCScriptable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSINATIVEMETHOD
+  NS_DECL_NSIXPCSCRIPTABLE
+
+  nsNativeMethod();
+
+  nsresult Init(JSContext* aContext, const nsAString& aName, PRFuncPtr aFunc, PRUint16 aCallType, nsArgumentType aResultType, const nsTArray<nsArgumentType>& aArgTypes);
+
+private:
+  ~nsNativeMethod();
+
+  nsresult PrepareType(JSContext* aContext, nsArgumentType aType, nsNativeType& aResult);
+  nsresult PrepareValue(JSContext* aContext, const nsNativeType& aType, jsval aValue, nsNativeValue& aResult);
+  nsresult ConvertReturnValue(JSContext* aContext, const nsNativeType& aResultType, const nsNativeValue& aResultValue, jsval* aValue);
+  nsresult Execute(JSContext* aContext, PRUint32 aArgc, jsval* aArgv, jsval* aValue);
+
+protected:
+  /* additional members */
+  nsString mName;
+  PRFuncPtr mFunc;
+
+  ffi_abi mCallType;
+  nsNativeType mResultType;
+  nsAutoTArray<nsNativeType, 16> mArgTypes;
+  nsAutoTArray<ffi_type*, 16> mFFITypes;
+
+  ffi_cif mCIF;
+};
+
+#define NS_ERROR_INVALID_ABI \
+    NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NATIVE, 1)
+
+#define NS_ERROR_INVALID_TYPE \
+    NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NATIVE, 2)
+
+#endif
diff --git a/toolkit/components/js-ctypes/src/nsNativeTypes.cpp b/toolkit/components/js-ctypes/src/nsNativeTypes.cpp
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/src/nsNativeTypes.cpp
@@ -0,0 +1,183 @@
+#include "nsNativeTypes.h"
+#include "nsNativeMethod.h"
+#include "nsServiceManagerUtils.h"
+#include "prlink.h"
+#include "jsapi.h"
+#include "jsprvtd.h"
+#include "jsnum.h"
+#include "nsAutoPtr.h"
+
+/**
+ * JS Utilities
+ */
+PRBool JSValToUint16(PRUint16* aProp, JSContext* aContext, jsval aValue)
+{
+    uint16 temp;
+    if (JS_ValueToUint16(aContext, aValue, &temp)) {
+        *aProp = (PRUint16)temp;
+    }
+    else {
+        JS_ReportError(aContext, "Parameter must be an integer");
+        return JS_FALSE;
+    }
+
+    return JS_TRUE;
+}
+
+PRBool JSValToString(nsAString& aProp, JSContext* aContext, jsval aValue)
+{
+   if (JSVAL_IS_STRING(aValue)) {
+        JSString *jsstr = JS_ValueToString(aContext, aValue);
+        nsDependentString str((PRUnichar *)JS_GetStringChars(jsstr), JS_GetStringLength(jsstr));
+        aProp.Assign(str);
+    }
+    else {
+        JS_ReportError(aContext, "Parameter must be a string");
+        return JS_FALSE;
+    }
+
+    return JS_TRUE;
+}
+
+
+NS_IMPL_ISUPPORTS1(nsNativeTypes, nsINativeTypes)
+
+nsNativeTypes::nsNativeTypes()
+{
+  /* member initializers and constructor code */
+}
+
+nsNativeTypes::~nsNativeTypes()
+{
+  /* destructor code */
+}
+
+/* readonly attribute AString name; */
+NS_IMETHODIMP nsNativeTypes::GetName(nsAString & aName)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* boolean open (in AString name); */
+NS_IMETHODIMP nsNativeTypes::Open(const nsAString & aName, PRBool *_retval)
+{
+    PRLibSpec libSpec;
+#ifdef WIN32
+    nsString flatName = PromiseFlatString(aName);
+    libSpec.value.pathname_u = flatName.get();
+    libSpec.type = PR_LibSpec_PathnameU;
+#else
+    nsCString flatName = PromiseFlatCString(NS_ConvertUTF16toUTF8(aName));
+    libSpec.value.pathname = flatName.get();
+    libSpec.type = PR_LibSpec_Pathname;
+#endif
+    mLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
+    *_retval = (mLibrary != 0 ? PR_TRUE : PR_FALSE);
+
+    return (mLibrary != 0 ? NS_OK : NS_ERROR_FAILURE);
+}
+
+/* boolean close (); */
+NS_IMETHODIMP nsNativeTypes::Close(PRBool *_retval)
+{
+    *_retval = PR_FALSE;
+
+    nsresult rv = NS_ERROR_FAILURE;
+
+    if (mLibrary) {
+        PR_UnloadLibrary(mLibrary);
+        *_retval = PR_TRUE;
+        rv = NS_OK;
+    }
+
+    return rv;
+}
+
+/* nsINativeMethod declare (); */
+NS_IMETHODIMP nsNativeTypes::Declare(nsINativeMethod **_retval)
+{
+    *_retval = 0;
+    nsresult rv;
+
+    nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+
+    nsAXPCNativeCallContext* ncc;
+    rv = xpc->GetCurrentNativeCallContext(&ncc);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!ncc)
+        return NS_ERROR_FAILURE;
+
+    JSContext *ctx = nsnull;
+    rv = ncc->GetJSContext(&ctx);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    JSAutoRequest ar(ctx);
+
+    PRUint32 argc;
+    jsval *argv = nsnull;
+
+    ncc->GetArgc(&argc);
+    ncc->GetArgvPtr(&argv);
+
+    // we always need at least a method name, a call type and a return type
+    if (argc < 3)
+        return NS_ERROR_INVALID_ARG;
+
+    rv = NS_OK;
+
+#define GET_UINT_ARG(dest,whicharg) \
+    do { if (!JSValToUint16(dest, ctx, whicharg)) { rv = NS_ERROR_INVALID_ARG; goto FAIL; } } while (0)
+
+#define GET_STRING_ARG(dest,whicharg) \
+    do { if (!JSValToString(dest, ctx, whicharg)) { rv = NS_ERROR_INVALID_ARG; goto FAIL; } } while (0)
+
+    nsString name;
+    PRUint16 callType;
+    nsArgumentType resultType;
+    nsAutoTArray<nsArgumentType, 16> argTypes;
+
+    for (PRUint32 i=0; i<argc; i++) {
+        if (i == 0) {
+            GET_STRING_ARG(name, argv[i]);
+        }
+        else if (i == 1) {
+            GET_UINT_ARG(&callType, argv[i]);
+        }
+        else if (i == 2) {
+            if (JSVAL_IS_OBJECT(argv[i])) {
+                resultType.mType = nsINativeTypes::STRUCT;
+                resultType.mObject = JSVAL_TO_OBJECT(argv[i]);
+            }
+            else {
+                GET_UINT_ARG(&resultType.mType, argv[i]);
+            }
+        }
+        else {
+            nsArgumentType* argType = argTypes.AppendElement();
+            if (JSVAL_IS_OBJECT(argv[i])) {
+                argType->mType = nsINativeTypes::STRUCT;
+                argType->mObject = JSVAL_TO_OBJECT(argv[i]);
+            }
+            else {
+                GET_UINT_ARG(&argType->mType, argv[i]);
+            }
+        }
+    }
+
+#undef GET_UINT_ARG
+#undef GET_STRING_ARG
+
+    if (mLibrary) {
+        PRFuncPtr pFunc = PR_FindFunctionSymbol(mLibrary, NS_LossyConvertUTF16toASCII(name).get());
+        if (pFunc) {
+            nsRefPtr<nsNativeMethod> pCall = new nsNativeMethod;
+            rv = pCall->Init(ctx, name, pFunc, callType, resultType, argTypes);
+            if (NS_FAILED(rv)) return rv;
+            NS_ADDREF(*_retval = pCall);
+        }
+    }
+
+FAIL:
+    return rv;
+}
diff --git a/toolkit/components/js-ctypes/src/nsNativeTypes.h b/toolkit/components/js-ctypes/src/nsNativeTypes.h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/src/nsNativeTypes.h
@@ -0,0 +1,24 @@
+#ifndef NSNATIVETYPES_H
+#define NSNATIVETYPES_H
+
+#include "nsINativeTypes.h"
+
+struct PRLibrary;
+
+class nsNativeTypes : public nsINativeTypes
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSINATIVETYPES
+
+  nsNativeTypes();
+
+private:
+  ~nsNativeTypes();
+
+protected:
+  /* additional members */
+  PRLibrary* mLibrary;
+};
+
+#endif
diff --git a/toolkit/components/js-ctypes/tests/Makefile.in b/toolkit/components/js-ctypes/tests/Makefile.in
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/tests/Makefile.in
@@ -0,0 +1,48 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is js-ctypes.
+#
+# Contributor(s):
+#   Fredrik Larsson <nossralf@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = js-ctypes-test
+LIBRARY_NAME = js-ctypes-test
+EXPORT_LIBRARY = 1
+
+CPPSRCS = js-ctypes-test.cpp
+
+XPCSHELL_TESTS = unit
+
+include $(topsrcdir)/config/rules.mk
diff --git a/toolkit/components/js-ctypes/tests/js-ctypes-test.cpp b/toolkit/components/js-ctypes/tests/js-ctypes-test.cpp
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/tests/js-ctypes-test.cpp
@@ -0,0 +1,167 @@
+#include "js-ctypes-test.h"
+#include <string.h>
+#include <wchar.h>
+#include <math.h>
+
+void
+test_v()
+{
+  // do nothing
+  return;
+}
+
+short
+test_s()
+{
+  return 12345;
+}
+
+short
+test_s_s(short number)
+{
+  return number;
+}
+
+short
+test_s_ss(short number1, short number2)
+{
+  return number1 + number2;
+}
+
+short*
+test_s_echo(short *number)
+{
+  return number;
+}
+
+int
+test_i()
+{
+  return 123456789;
+}
+
+int
+test_i_i(int number)
+{
+  return number;
+}
+
+int
+test_i_ii(int number1, int number2)
+{
+  return number1 + number2;
+}
+
+int*
+test_i_echo(int *number)
+{
+  return number;
+}
+
+float
+test_f()
+{
+  return 123456.5f;
+}
+
+float
+test_f_f(float number)
+{
+  return number;
+}
+
+float
+test_f_ff(float number1, float number2)
+{
+  return (number1 + number2);
+}
+
+float*
+test_f_echo(float *number)
+{
+  return number;
+}
+
+double
+test_d()
+{
+  return 123456789.5;
+}
+
+double
+test_d_d(double number)
+{
+  return number;
+}
+
+double
+test_d_dd(double number1, double number2)
+{
+  return (number1 + number2);
+}
+
+double*
+test_d_echo(double *number)
+{
+  return number;
+}
+
+int
+test_strlen(const char* string)
+{
+  return strlen(string);
+}
+
+int
+test_wcslen(const wchar_t* string)
+{
+  return wcslen(string);
+}
+
+char gDummyString[] = "success";
+
+char *
+test_strret()
+{
+  return gDummyString;
+}
+
+char *
+test_str_echo(const char* string)
+{
+  return (char*)string;
+}
+
+int
+test_i_if_floor(int number1, float number2)
+{
+  return floor(float(number1) + number2);
+}
+
+int
+test_pt_in_rect(RECT rc, POINT pt)
+{
+  if (pt.x < rc.left || pt.x > rc.right)
+    return 0;
+  if (pt.y < rc.bottom || pt.y > rc.top)
+    return 0;
+  return 1;
+}
+
+void
+test_init_pt(POINT* pt, int x, int y)
+{
+  pt->x = x;
+  pt->y = y;
+}
+
+int test_nested_struct(NESTED n) {
+  return n.n1 + n.n2 + n.inner.i1 + n.inner.i2 + n.inner.i3 + n.n3 + n.n4;
+}
+
+POINT test_struct_return(RECT r) {
+  POINT p;
+  p.x = r.left; p.y = r.top;
+  return p;
+}
+
diff --git a/toolkit/components/js-ctypes/tests/js-ctypes-test.h b/toolkit/components/js-ctypes/tests/js-ctypes-test.h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/tests/js-ctypes-test.h
@@ -0,0 +1,65 @@
+#include "nscore.h"
+
+NS_EXTERN_C
+{
+  NS_EXPORT void test_v();
+
+  NS_EXPORT short test_s();
+  NS_EXPORT short test_s_s(short);
+  NS_EXPORT short test_s_ss(short, short);
+  NS_EXPORT short* test_s_echo(short*);
+
+  NS_EXPORT int test_i();
+  NS_EXPORT int test_i_i(int);
+  NS_EXPORT int test_i_ii(int, int);
+  NS_EXPORT int* test_i_echo(int*);
+
+  NS_EXPORT float test_f();
+  NS_EXPORT float test_f_f(float);
+  NS_EXPORT float test_f_ff(float, float);
+  NS_EXPORT float* test_f_echo(float*);
+
+  NS_EXPORT double test_d();
+  NS_EXPORT double test_d_d(double);
+  NS_EXPORT double test_d_dd(double, double);
+  NS_EXPORT double* test_d_echo(double*);
+
+  NS_EXPORT int test_strlen(const char*);
+  NS_EXPORT int test_wcslen(const wchar_t*);
+  NS_EXPORT char* test_strret();
+  NS_EXPORT char* test_str_echo(const char*);
+
+  NS_EXPORT int test_i_if_floor(int, float);
+
+  struct POINT {
+    int x;
+    int y;
+  };
+
+  struct RECT {
+    int top;
+    int left;
+    int bottom;
+    int right;
+  };
+
+  struct INNER {
+    unsigned char i1;
+    long long int i2;
+    char i3;
+  };
+
+  struct NESTED {
+    int n1;
+    short n2;
+    INNER inner;
+    long long int n3;
+    int n4;
+  };
+
+  NS_EXPORT int test_pt_in_rect(RECT, POINT);
+  NS_EXPORT void test_init_pt(POINT* pt, int x, int y);
+
+  NS_EXPORT int test_nested_struct(NESTED);
+  NS_EXPORT POINT test_struct_return(RECT);
+}
diff --git a/toolkit/components/js-ctypes/tests/unit/test_js-ctypes.js b/toolkit/components/js-ctypes/tests/unit/test_js-ctypes.js
new file mode 100644
--- /dev/null
+++ b/toolkit/components/js-ctypes/tests/unit/test_js-ctypes.js
@@ -0,0 +1,235 @@
+/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is js-ctypes.
+ *
+ * The Initial Developer of the Original Code is
+ * Fredrik Larsson <nossralf@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
+ *  Dan Witte <dwitte@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const Types = Ci.nsINativeTypes;
+
+function POINT(x, y) {
+  this.x = x; this.y = y;
+}
+
+POINT.prototype = {
+  _fields_ : [{"x" : Types.INT32}, {"y" : Types.INT32}]
+}
+
+function RECT(top, left, bottom, right) {
+  this.top = top; this.left = left; this.bottom = bottom; this.right = right;
+}
+
+RECT.prototype = {
+  _fields_ : [{"top" : Types.INT32}, {"left" : Types.INT32}, {"bottom" : Types.INT32}, {"right" : Types.INT32}]
+}
+
+function INNER(i1, i2, i3) {
+  this.i1 = i1; this.i2 = i2; this.i3 = i3;
+}
+
+INNER.prototype = {
+  _fields_ : [{"i1" : Types.INT8}, {"i2" : Types.INT64}, {"i3" : Types.INT8}]
+}
+
+function NESTED(n1, n2, inner, n3, n4) {
+  this.n1 = n1; this.n2 = n2; this.inner = inner; this.n3 = n3; this.n4 = n4;
+}
+
+NESTED.prototype = {
+  _fields_ : [{"n1" : Types.INT32}, {"n2" : Types.INT16}, {"inner" : INNER}, {"n3" : Types.INT64}, {"n4" : Types.INT32}]
+}
+
+var OS = { windows: 0, linux: 1, darwin: 2 };
+var gOS;
+
+function run_test()
+{
+  var dist_bin = Cc["@mozilla.org/file/directory_service;1"]
+                     .getService(Ci.nsIProperties)
+                     .get("CurProcD", Ci.nsIFile);
+  var testlib = dist_bin.path;
+
+  // determine OS, in a suitably hacky way
+  if ("@mozilla.org/windows-registry-key;1" in Components.classes) {
+    gOS = OS.windows;
+    testlib += "\\js-ctypes-test.dll";
+  } else if ("nsILocalFileMac" in Components.interfaces) {
+    gOS = OS.darwin;
+    testlib += "/libjs-ctypes-test.dylib";
+  } else if ("@mozilla.org/gnome-gconf-service;1" in Components.classes) {
+    gOS = OS.linux;
+    testlib += "/libjs-ctypes-test.so";
+  } else {
+    throw new Error("unknown OS!");
+  }
+
+  var library = Cc["@developer.mozilla.org/js-ctypes;1"]
+                .createInstance(Ci.nsINativeTypes);
+  var loaded = library.open(testlib);
+  do_check_true(loaded);
+
+  run_void_tests(library);
+  run_short_tests(library);
+  run_int_tests(library);
+  run_float_tests(library);
+  run_double_tests(library);
+  run_string_tests(library);
+  run_mixed_tests(library);
+  run_struct_tests(library);
+}
+
+function run_void_tests(library) {
+  var test_v = library.declare("test_v", Types.DEFAULT, Types.VOID);
+  do_check_eq(test_v(), null);
+}
+
+function run_short_tests(library) {
+  var test_s = library.declare("test_s", Types.DEFAULT, Types.INT16);
+  do_check_eq(test_s(), 12345);
+
+  var test_s_s = library.declare("test_s_s", Types.DEFAULT, Types.INT16, Types.INT16);
+  do_check_eq(test_s_s(5), 5);
+
+  var test_s_ss = library.declare("test_s_ss", Types.DEFAULT, Types.INT16, Types.INT16, Types.INT16);
+  do_check_eq(test_s_ss(5, 5), 10);
+
+  //var test_s_echo = library.declare("test_s_echo", Types.DEFAULT, Types.INT16_P, Types.INT16_P);
+  //var p = 50;
+  //do_check_eq(test_s_echo(p), 50);
+}
+
+function run_int_tests(library) {
+  var test_i = library.declare("test_i", Types.DEFAULT, Types.INT32);
+  do_check_eq(test_i(), 123456789);
+
+  var test_i_i = library.declare("test_i_i", Types.DEFAULT, Types.INT32, Types.INT32);
+  do_check_eq(test_i_i(5), 5);
+
+  var test_i_ii = library.declare("test_i_ii", Types.DEFAULT, Types.INT32, Types.INT32, Types.INT32);
+  do_check_eq(test_i_ii(5, 5), 10);
+
+  //var test_i_echo = library.declare("test_i_echo", Types.DEFAULT, Types.INT32_P, Types.INT32_P);
+  //var p = 550;
+  //do_check_eq(test_i_echo(p), 550);
+}
+
+function run_float_tests(library) {
+  var test_f = library.declare("test_f", Types.DEFAULT, Types.FLOAT);
+  do_check_eq(test_f(), 123456.5);
+
+  var test_f_f = library.declare("test_f_f", Types.DEFAULT, Types.FLOAT, Types.FLOAT);
+  do_check_eq(test_f_f(5), 5);
+  do_check_eq(test_f_f(5.25), 5.25);
+
+  var test_f_ff = library.declare("test_f_ff", Types.DEFAULT, Types.FLOAT, Types.FLOAT, Types.FLOAT);
+  do_check_eq(test_f_ff(5, 5), 10);
+  do_check_eq(test_f_ff(5.5, 5.5), 11);
+
+  //var test_f_echo = library.declare("test_f_echo", Types.DEFAULT, Types.FLOAT_P, Types.FLOAT_P);
+  //var p = 550.5;
+  //do_check_eq(test_f_echo(p), 550.5);
+}
+
+function run_double_tests(library) {
+  var test_d = library.declare("test_d", Types.DEFAULT, Types.DOUBLE);
+  do_check_eq(test_d(), 123456789.5);
+
+  var test_d_d = library.declare("test_d_d", Types.DEFAULT, Types.DOUBLE, Types.DOUBLE);
+  do_check_eq(test_d_d(5), 5);
+  do_check_eq(test_d_d(5.25), 5.25);
+
+  var test_d_dd = library.declare("test_d_dd", Types.DEFAULT, Types.DOUBLE, Types.DOUBLE, Types.DOUBLE);
+  do_check_eq(test_d_dd(5, 5), 10);
+  do_check_eq(test_d_dd(5.5, 5.5), 11);
+
+  //var test_d_echo = library.declare("test_d_echo", Types.SYSV, Types.DOUBLE_P, Types.DOUBLE_P);
+  //var p = 123450.25;
+  //do_check_eq(test_d_echo(p), 123450.25);
+}
+
+function run_string_tests(library) {
+  var test_ansi_len = library.declare("test_strlen", Types.DEFAULT, Types.INT32, Types.STRING);
+  do_check_eq(test_ansi_len("hello world"), 11);
+
+  var test_wide_len = library.declare("test_wcslen", Types.DEFAULT, Types.INT32, Types.WSTRING);
+  if (gOS == OS.windows)
+    do_check_eq(test_wide_len("hello world"), 11);
+
+  var test_ansi_ret = library.declare("test_strret", Types.DEFAULT, Types.STRING);
+  do_check_eq(test_ansi_ret(), "success");
+
+  var test_ansi_echo = library.declare("test_str_echo", Types.DEFAULT, Types.STRING, Types.STRING);
+  do_check_eq(test_ansi_echo("anybody in there?"), "anybody in there?");
+}
+
+function run_mixed_tests(library) {
+  var test_i_if_floor = library.declare("test_i_if_floor", Types.DEFAULT, Types.INT32, Types.INT32, Types.FLOAT);
+  do_check_eq(test_i_if_floor(5, 5.5), 10);
+}
+
+function run_struct_tests(library) {
+  var test_pt_in_rect = library.declare("test_pt_in_rect", Types.DEFAULT, Types.INT32, RECT, POINT);
+  var rect = new RECT(10, 5, 5, 10);
+  var pt1 = new POINT(6, 6);
+  do_check_eq(test_pt_in_rect(rect, pt1), 1);
+  var pt2 = new POINT(2, 2);
+  do_check_eq(test_pt_in_rect(rect, pt2), 0);
+
+  var test_nested_struct = library.declare("test_nested_struct", Types.DEFAULT, Types.INT32, NESTED);
+  var inner = new INNER(161, 523412, 43);
+  var nested = new NESTED(13155, 1241, inner, 24512115, 1234111);
+  // add up all the numbers and make sure the C function agrees
+  do_check_eq(test_nested_struct(nested), 26284238);
+
+  // test returning a struct by value - not supported yet
+  /*
+  var test_struct_return = library.declare("test_struct_return", Types.DEFAULT, POINT, RECT);
+  var ret = test_struct_return(rect);
+  do_check_eq(ret.x, rect.left);
+  do_check_eq(ret.y, rect.top);
+  */
+
+  // test passing a struct by reference - not supported yet
+  /*
+  var test_init_pt = library.declare("test_init_pt", Types.SYSV, Types.VOID, POINT*, Types.INT32, Types.INT32);
+  var foo = new POINT(0, 0);
+  test_init_pt(foo, 10, 10)
+  do_check_eq(foo.x, 10);
+  */
+}
+
diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -702,16 +702,20 @@ MAKEFILES_xulapp="
   toolkit/components/exthelper/Makefile
   toolkit/components/feeds/Makefile
   toolkit/components/feeds/public/Makefile
   toolkit/components/feeds/src/Makefile
   toolkit/components/filepicker/Makefile
   toolkit/components/filepicker/public/Makefile
   toolkit/components/filepicker/src/Makefile
   toolkit/components/help/Makefile
+  toolkit/components/js-ctypes/Makefile
+  toolkit/components/js-ctypes/public/Makefile
+  toolkit/components/js-ctypes/src/Makefile
+  toolkit/components/js-ctypes/tests/Makefile
   toolkit/components/microformats/Makefile
   toolkit/components/microformats/src/Makefile
   toolkit/components/parentalcontrols/Makefile
   toolkit/components/parentalcontrols/public/Makefile
   toolkit/components/parentalcontrols/src/Makefile
   toolkit/components/passwordmgr/Makefile
   toolkit/components/passwordmgr/public/Makefile
   toolkit/components/passwordmgr/src/Makefile
diff --git a/xpcom/base/nsError.h b/xpcom/base/nsError.h
--- a/xpcom/base/nsError.h
+++ b/xpcom/base/nsError.h
@@ -92,16 +92,17 @@
 #define NS_ERROR_MODULE_CONTENT    25
 #define NS_ERROR_MODULE_PYXPCOM    26
 #define NS_ERROR_MODULE_XSLT       27
 #define NS_ERROR_MODULE_IPC        28
 #define NS_ERROR_MODULE_SVG        29
 #define NS_ERROR_MODULE_STORAGE    30
 #define NS_ERROR_MODULE_SCHEMA     31
 #define NS_ERROR_MODULE_DOM_FILE   32
+#define NS_ERROR_MODULE_NATIVE     33
 
 /* NS_ERROR_MODULE_GENERAL should be used by modules that do not
  * care if return code values overlap. Callers of methods that
  * return such codes should be aware that they are not
  * globally unique. Implementors should be careful about blindly
  * returning codes from other modules that might also use
  * the generic base.
  */
