System for testing plugins, take 4. b=386676 r=smichaud sr=roc
authorJosh Aas <joshmoz@gmail.com>
Mon, 12 Jan 2009 22:06:21 -0500
changeset 23574 7b1292782cd39c59fbe1e14986eda53a16ca7f9e
parent 23573 b5120305d873923f8273675b7b742cc37f48a0d3
child 23575 526ceb7dd261f289380c4ff0d8065fff9bc21093
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmichaud, roc
bugs386676
milestone1.9.2a1pre
System for testing plugins, take 4. b=386676 r=smichaud sr=roc
modules/plugin/test/Makefile.in
modules/plugin/test/mochitest/Makefile.in
modules/plugin/test/mochitest/test_npruntime.xul
modules/plugin/test/testplugin/Makefile.in
modules/plugin/test/testplugin/nptest.cpp
modules/plugin/test/testplugin/nptest.def
modules/plugin/test/testplugin/nptest.h
modules/plugin/test/testplugin/nptest_gtk2.cpp
modules/plugin/test/testplugin/nptest_macosx.mm
modules/plugin/test/testplugin/nptest_platform.h
modules/plugin/test/testplugin/nptest_utils.cpp
modules/plugin/test/testplugin/nptest_utils.h
modules/plugin/test/testplugin/nptest_windows.cpp
--- a/modules/plugin/test/Makefile.in
+++ b/modules/plugin/test/Makefile.in
@@ -15,16 +15,17 @@
 # The Original Code is mozilla.org code.
 #
 # The Initial Developer of the Original Code is mozilla.org
 # Portions created by the Initial Developer are Copyright (C) 2008
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Dave Townsend <dtownsend@oxymoronical.com>
+#   Josh Aas <josh@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
@@ -45,9 +46,16 @@ include $(DEPTH)/config/autoconf.mk
 MODULE = test_plugin
 
 DIRS = testplugin
 
 XPCSHELL_TESTS = \
   unit \
   $(NULL)
 
+# plugin mochitests only work on UNIX variants, including Mac OS X, at this time
+ifneq (,$(filter gtk2 cocoa windows,$(MOZ_WIDGET_TOOLKIT)))
+ifdef ENABLE_TESTS
+DIRS  += mochitest
+endif
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/mochitest/Makefile.in
@@ -0,0 +1,50 @@
+#
+# ***** 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 mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of 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@
+relativesrcdir  = modules/plugin/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES =	test_npruntime.xul
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/mochitest/test_npruntime.xul
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<window title="Basic Plugin Tests"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <title>Basic Plugin Tests</title>
+  <script type="application/javascript" 
+   src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+  var pluginElement = document.getElementById("plugin1");
+
+  ok(pluginElement.identifierToStringTest('foo') == "foo", "identifierToStringTest failed");
+
+  SimpleTest.finish();
+}
+]]>
+</script>
+</window>
--- a/modules/plugin/test/testplugin/Makefile.in
+++ b/modules/plugin/test/testplugin/Makefile.in
@@ -49,23 +49,36 @@ MODULE_NAME  = TestPlugin
 REQUIRES = \
   plugin \
   $(NULL)
 
 # Need to custom install plugins
 NO_DIST_INSTALL	= 1
 NO_INSTALL = 1
 
-ifeq ($(OS_ARCH),WINNT)
+CPPSRCS   =  \
+  nptest.cpp \
+  nptest_utils.cpp \
+  $(NULL)
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
+CMMSRCS = nptest_macosx.mm
+endif
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
+CPPSRCS += nptest_gtk2.cpp
+endif
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
+CPPSRCS  += nptest_windows.cpp
 RCFILE    = nptest.rc
 RESFILE   = nptest.res
+DEFFILE   = $(win_srcdir)/nptest.def
 endif
 
-CPPSRCS   = nptest.cpp
-
 include $(topsrcdir)/config/rules.mk
 
 install-plugin: $(SHARED_LIBRARY)
 ifdef SHARED_LIBRARY
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 	$(INSTALL) $(srcdir)/Info.plist $(DIST)/bin/plugins/Test.plugin/Contents
 	$(INSTALL) $(SHARED_LIBRARY) $(DIST)/bin/plugins/Test.plugin/Contents/MacOS
 else
--- a/modules/plugin/test/testplugin/nptest.cpp
+++ b/modules/plugin/test/testplugin/nptest.cpp
@@ -1,83 +1,524 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is mozilla.org
- * Portions created by the Initial Developer are Copyright (C) 2008
- * the Initial Developer. All Rights Reserved.
- *
+ * 
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
  * Contributor(s):
  *   Dave Townsend <dtownsend@oxymoronical.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.
- *
+ *   Josh Aas <josh@mozilla.com>
+ * 
  * ***** END LICENSE BLOCK ***** */
 
-#include "npapi.h"
-#include "npfunctions.h"
+#include "nptest.h"
+#include "nptest_utils.h"
+#include "nptest_platform.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define PLUGIN_NAME        "Test Plug-in"
+#define PLUGIN_DESCRIPTION "Plug-in for testing purposes."
+#define PLUGIN_VERSION     "1.0.0.0"
+
+//
+// static data
+//
+
+static NPNetscapeFuncs* sBrowserFuncs = NULL;
+static NPClass sNPClass;
+
+//
+// identifiers
+//
+
+#define IDENTIFIER_TO_STRING_TEST_METHOD 0
+#define NUM_METHOD_IDENTIFIERS           1
+
+static NPIdentifier sPluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
+static const NPUTF8 *sPluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
+  "identifierToStringTest",
+};
+
+static bool sIdentifiersInitialized = false;
+
+static void initializeIdentifiers()
+{
+  if (!sIdentifiersInitialized) {
+    NPN_GetStringIdentifiers(sPluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, sPluginMethodIdentifiers);
+    sIdentifiersInitialized = true;    
+  }
+}
+
+static void clearIdentifiers()
+{
+  memset(sPluginMethodIdentifierNames, 0, NUM_METHOD_IDENTIFIERS * sizeof(NPIdentifier));
+  sIdentifiersInitialized = false;
+}
+
+//
+// function signatures
+//
+
+bool identifierToStringTest(const NPVariant* args, uint32_t argCount, NPVariant* result);
+
+NPObject* scriptableAllocate(NPP npp, NPClass* aClass);
+void scriptableDeallocate(NPObject* npobj);
+void scriptableInvalidate(NPObject* npobj);
+bool scriptableHasMethod(NPObject* npobj, NPIdentifier name);
+bool scriptableInvoke(NPObject* npobj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result);
+bool scriptableInvokeDefault(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+bool scriptableHasProperty(NPObject* npobj, NPIdentifier name);
+bool scriptableGetProperty(NPObject* npobj, NPIdentifier name, NPVariant* result);
+bool scriptableSetProperty(NPObject* npobj, NPIdentifier name, const NPVariant* value);
+bool scriptableRemoveProperty(NPObject* npobj, NPIdentifier name);
+bool scriptableEnumerate(NPObject* npobj, NPIdentifier** identifier, uint32_t* count);
+bool scriptableConstruct(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+
+//
+// npapi plugin functions
+//
+
+#ifdef XP_UNIX
+NP_EXPORT(char*)
+NP_GetPluginVersion()
+{
+  return PLUGIN_VERSION;
+}
+#endif
+
+#if defined(XP_UNIX)
+NP_EXPORT(char*) NP_GetMIMEDescription()
+#elif defined(XP_WIN)
+char* NP_GetMIMEDescription()
+#endif
+{
+  return "application/x-test:tst:Test mimetype";
+}
+
+#ifdef XP_UNIX
+NP_EXPORT(NPError)
+NP_GetValue(void* future, NPPVariable aVariable, void* aValue) {
+  switch (aVariable) {
+    case NPPVpluginNameString:
+      *((char**)aValue) = PLUGIN_NAME;
+      break;
+    case NPPVpluginDescriptionString:
+      *((char**)aValue) = PLUGIN_DESCRIPTION;
+      break;
+    default:
+      return NPERR_INVALID_PARAM;
+      break;
+  }
+  return NPERR_NO_ERROR;
+}
+#endif
+
+static void fillPluginFunctionTable(NPPluginFuncs* pFuncs)
+{
+  pFuncs->version = 11;
+  pFuncs->size = sizeof(*pFuncs);
+  pFuncs->newp = NPP_New;
+  pFuncs->destroy = NPP_Destroy;
+  pFuncs->setwindow = NPP_SetWindow;
+  pFuncs->newstream = NPP_NewStream;
+  pFuncs->destroystream = NPP_DestroyStream;
+  pFuncs->asfile = NPP_StreamAsFile;
+  pFuncs->writeready = NPP_WriteReady;
+  pFuncs->write = NPP_Write;
+  pFuncs->print = NPP_Print;
+  pFuncs->event = NPP_HandleEvent;
+  pFuncs->urlnotify = NPP_URLNotify;
+  pFuncs->getvalue = NPP_GetValue;
+  pFuncs->setvalue = NPP_SetValue;
+}
+
+#if defined(XP_MACOSX)
+NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs)
+#elif defined(XP_WIN)
+NPError OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs)
+#elif defined(XP_UNIX)
+NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs)
+#endif
+{
+  sBrowserFuncs = bFuncs;
+
+  initializeIdentifiers();
+
+  memset(&sNPClass, 0, sizeof(NPClass));
+  sNPClass.structVersion =  NP_CLASS_STRUCT_VERSION;
+  sNPClass.allocate =       (NPAllocateFunctionPtr)scriptableAllocate;
+  sNPClass.deallocate =     (NPDeallocateFunctionPtr)scriptableDeallocate;
+  sNPClass.invalidate =     (NPInvalidateFunctionPtr)scriptableInvalidate;
+  sNPClass.hasMethod =      (NPHasMethodFunctionPtr)scriptableHasMethod;
+  sNPClass.invoke =         (NPInvokeFunctionPtr)scriptableInvoke;
+  sNPClass.invokeDefault =  (NPInvokeDefaultFunctionPtr)scriptableInvokeDefault;
+  sNPClass.hasProperty =    (NPHasPropertyFunctionPtr)scriptableHasProperty;
+  sNPClass.getProperty =    (NPGetPropertyFunctionPtr)scriptableGetProperty;
+  sNPClass.setProperty =    (NPSetPropertyFunctionPtr)scriptableSetProperty;
+  sNPClass.removeProperty = (NPRemovePropertyFunctionPtr)scriptableRemoveProperty;
+  sNPClass.enumerate =      (NPEnumerationFunctionPtr)scriptableEnumerate;
+  sNPClass.construct =      (NPConstructFunctionPtr)scriptableConstruct;
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+  fillPluginFunctionTable(pFuncs);
+#endif
+
+  return NPERR_NO_ERROR;
+}
+
+#if defined(XP_MACOSX)
+NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs)
+#elif defined(XP_WIN)
+NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs)
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+{
+  fillPluginFunctionTable(pFuncs);
+  return NPERR_NO_ERROR;
+}
+#endif
 
 #if defined(XP_UNIX)
+NP_EXPORT(NPError) NP_Shutdown()
+#elif defined(XP_WIN)
+NPError OSCALL NP_Shutdown()
+#endif
+{
+  clearIdentifiers();
 
-#define PLUGIN_NAME         "Test Plug-in"
-#define PLUGIN_DESCRIPTION  "Plug-in for testing purposes."
-#define PLUGIN_VERSION      "1.0.0.0"
+  return NPERR_NO_ERROR;
+}
+
+NPError
+NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved)
+{
+  NPN_SetValue(instance, NPPVpluginWindowBool, NULL);
+
+  // set up our our instance data
+  InstanceData* instanceData = (InstanceData*)malloc(sizeof(InstanceData));
+  if (!instanceData)
+    return NPERR_OUT_OF_MEMORY_ERROR;
+  memset(instanceData, 0, sizeof(InstanceData));
+  instanceData->npp = instance;
+  instance->pdata = instanceData;
+
+  TestNPObject* scriptableObject = (TestNPObject*)NPN_CreateObject(instance, &sNPClass);
+  if (!scriptableObject) {
+    printf("NPN_CreateObject failed to create an object, can't create a plugin instance\n");
+    return NPERR_GENERIC_ERROR;
+  }
+  NPN_RetainObject(scriptableObject);
+  scriptableObject->npp = instance;
+  instanceData->scriptableObject = scriptableObject;
+
+  // do platform-specific initialization
+  NPError err = pluginInstanceInit(instanceData);
+  if (err != NPERR_NO_ERROR)
+    return err;
+
+  return NPERR_NO_ERROR;
+}
 
-NP_EXPORT(char*)
-NP_GetPluginVersion(void) {
-    return PLUGIN_VERSION;
+NPError
+NPP_Destroy(NPP instance, NPSavedData** save)
+{
+  InstanceData* instanceData = (InstanceData*)(instance->pdata);
+  NPN_ReleaseObject(instanceData->scriptableObject);
+  free(instanceData);
+  return NPERR_NO_ERROR;
+}
+
+NPError
+NPP_SetWindow(NPP instance, NPWindow* window)
+{
+  InstanceData* instanceData = (InstanceData*)(instance->pdata);
+  instanceData->window = *window;
+  return NPERR_NO_ERROR;
+}
+
+NPError
+NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
+{
+  *stype = NP_ASFILEONLY;
+  return NPERR_NO_ERROR;
+}
+
+NPError
+NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
+{
+  return NPERR_NO_ERROR;
+}
+
+int32_t
+NPP_WriteReady(NPP instance, NPStream* stream)
+{
+  return 0;
 }
 
-NP_EXPORT(char*)
-NP_GetMIMEDescription(void) {
-    return "application/x-test:tst:Test mimetype";
+int32_t
+NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer)
+{
+  return 0;
+}
+
+void
+NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+}
+
+void
+NPP_Print(NPP instance, NPPrint* platformPrint)
+{
+}
+
+int16_t
+NPP_HandleEvent(NPP instance, void* event)
+{
+  InstanceData* instanceData = (InstanceData*)(instance->pdata);
+  return pluginHandleEvent(instanceData, event);
+}
+
+void
+NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
 }
 
-NP_EXPORT(NPError)
-NP_Initialize(NPNetscapeFuncs*, NPPluginFuncs*) {
+NPError
+NPP_GetValue(NPP instance, NPPVariable variable, void* value)
+{
+  if (variable == NPPVpluginScriptableNPObject) {
+    NPObject* object = ((InstanceData*)instance->pdata)->scriptableObject;
+    NPN_RetainObject(object);
+    *((NPObject**)value) = object;
     return NPERR_NO_ERROR;
+  }
+
+  return NPERR_GENERIC_ERROR;
+}
+
+NPError
+NPP_SetValue(NPP instance, NPNVariable variable, void* value)
+{
+  return NPERR_GENERIC_ERROR;
+}
+
+//
+// npapi browser functions
+//
+
+bool
+NPN_SetProperty(NPP instance, NPObject* obj, NPIdentifier propertyName, const NPVariant* value)
+{
+  return sBrowserFuncs->setproperty(instance, obj, propertyName, value);
+}
+
+NPIdentifier
+NPN_GetIntIdentifier(int32_t intid)
+{
+  return sBrowserFuncs->getintidentifier(intid);
+}
+
+NPIdentifier
+NPN_GetStringIdentifier(const NPUTF8* name)
+{
+  return sBrowserFuncs->getstringidentifier(name);
+}
+
+void
+NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers)
+{
+  return sBrowserFuncs->getstringidentifiers(names, nameCount, identifiers);
+}
+
+NPUTF8*
+NPN_UTF8FromIdentifier(NPIdentifier identifier)
+{
+  return sBrowserFuncs->utf8fromidentifier(identifier);
 }
 
-NP_EXPORT(NPError)
-NP_Shutdown(void) {
-    return NPERR_NO_ERROR;
+int32_t
+NPN_IntFromIdentifier(NPIdentifier identifier)
+{
+  return sBrowserFuncs->intfromidentifier(identifier);
+}
+
+NPError
+NPN_GetValue(NPP instance, NPNVariable variable, void* value)
+{
+  return sBrowserFuncs->getvalue(instance, variable, value);
+}
+
+NPError
+NPN_SetValue(NPP instance, NPPVariable variable, void* value)
+{
+  return sBrowserFuncs->setvalue(instance, variable, value);
+}
+
+bool
+NPN_HasProperty(NPP instance, NPObject* obj, NPIdentifier propertyName)
+{
+  return sBrowserFuncs->hasproperty(instance, obj, propertyName);
+}
+
+NPObject*
+NPN_CreateObject(NPP instance, NPClass* aClass)
+{
+  return sBrowserFuncs->createobject(instance, aClass);
+}
+
+const char*
+NPN_UserAgent(NPP instance)
+{
+  return sBrowserFuncs->uagent(instance);
+}
+
+NPObject*
+NPN_RetainObject(NPObject* obj)
+{
+  return sBrowserFuncs->retainobject(obj);
+}
+
+void
+NPN_ReleaseObject(NPObject* obj)
+{
+  return sBrowserFuncs->releaseobject(obj);
+}
+
+void*
+NPN_MemAlloc(uint32_t size)
+{
+  return sBrowserFuncs->memalloc(size);
+}
+
+void
+NPN_MemFree(void* ptr)
+{
+  return sBrowserFuncs->memfree(ptr);
+}
+
+//
+// npruntime object functions
+//
+
+NPObject*
+scriptableAllocate(NPP npp, NPClass* aClass)
+{
+  TestNPObject* object = (TestNPObject*)NPN_MemAlloc(sizeof(TestNPObject));
+  if (!object)
+    return NULL;
+  memset(object, 0, sizeof(TestNPObject));
+  return object;
+}
+
+void
+scriptableDeallocate(NPObject* npobj)
+{
+  NPN_MemFree(npobj);
 }
 
-NP_EXPORT(NPError) 
-NP_GetValue(void *future, NPPVariable aVariable, void *aValue) {
-   switch (aVariable) {
-     case NPPVpluginNameString:
-       *((char **)aValue) = PLUGIN_NAME;
-       break;
-     case NPPVpluginDescriptionString:
-       *((char **)aValue) = PLUGIN_DESCRIPTION;
-       break;
-     default:
-       return NPERR_INVALID_PARAM;
-       break;
-   }
-   return NPERR_NO_ERROR;
+void
+scriptableInvalidate(NPObject* npobj)
+{
+}
+
+bool
+scriptableHasMethod(NPObject* npobj, NPIdentifier name)
+{
+  for (int i = 0; i < NUM_METHOD_IDENTIFIERS; i++) {
+    if (name == sPluginMethodIdentifiers[i])
+      return true;
+  }
+  return false;
+}
+
+bool
+scriptableInvoke(NPObject* npobj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  if (name == sPluginMethodIdentifiers[IDENTIFIER_TO_STRING_TEST_METHOD])
+    return identifierToStringTest(args, argCount, result);
+  return false;
+}
+
+bool
+scriptableInvokeDefault(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  return false;
+}
+
+bool
+scriptableHasProperty(NPObject* npobj, NPIdentifier name)
+{
+  return false;
+}
+
+bool
+scriptableGetProperty(NPObject* npobj, NPIdentifier name, NPVariant* result)
+{
+  return false;
 }
 
-#endif
+bool
+scriptableSetProperty(NPObject* npobj, NPIdentifier name, const NPVariant* value)
+{
+  return false;
+}
+
+bool
+scriptableRemoveProperty(NPObject* npobj, NPIdentifier name)
+{
+  return false;
+}
+
+bool
+scriptableEnumerate(NPObject* npobj, NPIdentifier** identifier, uint32_t* count)
+{
+  return false;
+}
+
+bool
+scriptableConstruct(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  return false;
+}
+
+//
+// test functions
+//
+
+bool
+identifierToStringTest(const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  if (argCount != 1)
+    return false;
+  NPIdentifier identifier = variantToIdentifier(args[0]);
+  if (!identifier)
+    return false;
+  NPUTF8* utf8String = NPN_UTF8FromIdentifier(identifier);
+  if (!utf8String)
+    return false;
+  STRINGZ_TO_NPVARIANT(utf8String, *result);
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY   NPTEST
+
+EXPORTS
+  NP_GetEntryPoints     @1
+  NP_Initialize         @2
+  NP_Shutdown           @3
+  NP_GetMIMEDescription @4
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest.h
@@ -0,0 +1,53 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * 
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nptest_h_
+#define nptest_h_
+
+#include "mozilla-config.h"
+
+#include "npapi.h"
+#include "npfunctions.h"
+#include "npruntime.h"
+
+typedef struct TestNPObject : NPObject {
+  NPP npp;
+} TestNPObject;
+
+typedef struct InstanceData {
+  NPP npp;
+  NPWindow window;
+  TestNPObject* scriptableObject;
+} InstanceData;
+
+#endif // nptest_h_
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest_gtk2.cpp
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * 
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_platform.h"
+
+NPError
+pluginInstanceInit(InstanceData* instanceData)
+{
+  return NPERR_NO_ERROR;
+}
+
+int16_t
+pluginHandleEvent(InstanceData* instanceData, void* event)
+{
+  return 0;
+}
+
+void
+pluginDraw(InstanceData* instanceData)
+{
+}
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest_macosx.mm
@@ -0,0 +1,167 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * 
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_platform.h"
+#include <CoreServices/CoreServices.h>
+
+NPError
+pluginInstanceInit(InstanceData* instanceData)
+{
+  NPP npp = instanceData->npp;
+  // select the right drawing model if necessary
+  NPBool supportsCoreGraphics = false;
+  if (NPN_GetValue(npp, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) == NPERR_NO_ERROR && supportsCoreGraphics) {
+    NPN_SetValue(npp, NPPVpluginDrawingModel, (void*)NPDrawingModelCoreGraphics);
+  } else {
+    printf("CoreGraphics drawing model not supported, can't create a plugin instance.\n");
+    return NPERR_INCOMPATIBLE_VERSION_ERROR;
+  }
+  return NPERR_NO_ERROR;
+}
+
+int16_t
+pluginHandleEvent(InstanceData* instanceData, void* event)
+{
+  EventRecord* carbonEvent = (EventRecord*)event;
+  if (carbonEvent && (carbonEvent->what == updateEvt)) {
+    pluginDraw(instanceData);
+    return 1;
+  }
+  return 0;
+}
+
+void
+pluginDraw(InstanceData* instanceData)
+{
+  if (!instanceData)
+    return;
+
+  NPP npp = instanceData->npp;
+  if (!npp)
+    return;
+
+  const char* uaString = NPN_UserAgent(npp);
+  if (!uaString)
+    return;
+
+  NPWindow window = instanceData->window;
+
+  CGContextRef cgContext = ((NP_CGContext*)(window.window))->context;
+
+  CFStringRef uaCFString = CFStringCreateWithCString(kCFAllocatorDefault, uaString, kCFStringEncodingASCII);
+
+  float windowWidth = window.width;
+  float windowHeight = window.height;
+
+  // save the cgcontext gstate
+  CGContextSaveGState(cgContext);
+
+  // we get a flipped context
+  CGContextTranslateCTM(cgContext, 0.0, windowHeight);
+  CGContextScaleCTM(cgContext, 1.0, -1.0);
+
+  // draw a gray background for the plugin
+  CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
+  CGContextSetGrayFillColor(cgContext, 0.5, 1.0);
+  CGContextDrawPath(cgContext, kCGPathFill);
+
+  // draw a black frame around the plugin
+  CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
+  CGContextSetGrayStrokeColor(cgContext, 0.0, 1.0);
+  CGContextSetLineWidth(cgContext, 6.0);
+  CGContextStrokePath(cgContext);
+
+  // draw the UA string using ATSUI
+  CGContextSetGrayFillColor(cgContext, 0.0, 1.0);
+  ATSUStyle atsuStyle;
+  ATSUCreateStyle(&atsuStyle);
+  CFIndex stringLength = CFStringGetLength(uaCFString);
+  UniChar* unicharBuffer = (UniChar*)malloc((stringLength + 1) * sizeof(UniChar));
+  CFStringGetCharacters(uaCFString, CFRangeMake(0, stringLength), unicharBuffer);
+  UniCharCount runLengths = kATSUToTextEnd;
+  ATSUTextLayout atsuLayout;
+  ATSUCreateTextLayoutWithTextPtr(unicharBuffer,
+                                  kATSUFromTextBeginning,
+                                  kATSUToTextEnd,
+                                  stringLength,
+                                  1,
+                                  &runLengths,
+                                  &atsuStyle,
+                                  &atsuLayout);
+  ATSUAttributeTag contextTag = kATSUCGContextTag;
+  ByteCount byteSize = sizeof(CGContextRef);
+  ATSUAttributeValuePtr contextATSUPtr = &cgContext;
+  ATSUSetLayoutControls(atsuLayout, 1, &contextTag, &byteSize, &contextATSUPtr);
+  ATSUTextMeasurement lineAscent, lineDescent;
+  ATSUGetLineControl(atsuLayout,
+                     kATSUFromTextBeginning,
+                     kATSULineAscentTag,
+                     sizeof(ATSUTextMeasurement),
+                     &lineAscent,
+                     &byteSize);
+  ATSUGetLineControl(atsuLayout,
+                     kATSUFromTextBeginning,
+                     kATSULineDescentTag,
+                     sizeof(ATSUTextMeasurement),
+                     &lineDescent,
+                     &byteSize);
+  float lineHeight = FixedToFloat(lineAscent) + FixedToFloat(lineDescent);
+  ItemCount softBreakCount;
+  ATSUBatchBreakLines(atsuLayout,
+                      kATSUFromTextBeginning,
+                      stringLength,
+                      FloatToFixed(windowWidth - 10.0),
+                      &softBreakCount);
+  ATSUGetSoftLineBreaks(atsuLayout,
+                        kATSUFromTextBeginning,
+                        kATSUToTextEnd,
+                        0, NULL, &softBreakCount);
+  UniCharArrayOffset* softBreaks = (UniCharArrayOffset*)malloc(softBreakCount * sizeof(UniCharArrayOffset));
+  ATSUGetSoftLineBreaks(atsuLayout,
+                        kATSUFromTextBeginning,
+                        kATSUToTextEnd,
+                        softBreakCount, softBreaks, &softBreakCount);
+  UniCharArrayOffset currentDrawOffset = kATSUFromTextBeginning;
+  unsigned int i = 0;
+  while (i < softBreakCount) {
+    ATSUDrawText(atsuLayout, currentDrawOffset, softBreaks[i], FloatToFixed(5.0), FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0))));
+    currentDrawOffset = softBreaks[i];
+    i++;
+  }
+  ATSUDrawText(atsuLayout, currentDrawOffset, kATSUToTextEnd, FloatToFixed(5.0), FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0))));
+  free(unicharBuffer);
+  free(softBreaks);
+
+  // restore the cgcontext gstate
+  CGContextRestoreGState(cgContext);
+}
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest_platform.h
@@ -0,0 +1,44 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * 
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nptest_platform_h_
+#define nptest_platform_h_
+
+#include "nptest.h"
+
+NPError pluginInstanceInit(InstanceData* instanceData);
+int16_t pluginHandleEvent(InstanceData* instanceData, void* event);
+// draws a gray box with a black border containing the browser UA string
+void    pluginDraw(InstanceData* instanceData);
+
+#endif // nptest_platform_h_
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest_utils.cpp
@@ -0,0 +1,84 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * 
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_utils.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+NPUTF8*
+createCStringFromNPVariant(const NPVariant* variant)
+{
+  size_t length = NPVARIANT_TO_STRING(*variant).UTF8Length;
+  NPUTF8* result = (NPUTF8*)malloc(length + 1);
+  memcpy(result, NPVARIANT_TO_STRING(*variant).UTF8Characters, length);
+  result[length] = '\0';
+  return result;
+}
+
+NPIdentifier
+variantToIdentifier(NPVariant variant)
+{
+  if (NPVARIANT_IS_STRING(variant))
+    return stringVariantToIdentifier(variant);
+  else if (NPVARIANT_IS_INT32(variant))
+    return int32VariantToIdentifier(variant);
+  else if (NPVARIANT_IS_DOUBLE(variant))
+    return doubleVariantToIdentifier(variant);
+  return 0;
+}
+
+NPIdentifier
+stringVariantToIdentifier(NPVariant variant)
+{
+  assert(NPVARIANT_IS_STRING(variant));
+  NPUTF8* utf8String = createCStringFromNPVariant(&variant);
+  NPIdentifier identifier = NPN_GetStringIdentifier(utf8String);
+  free(utf8String);
+  return identifier;
+}
+
+NPIdentifier
+int32VariantToIdentifier(NPVariant variant)
+{
+  assert(NPVARIANT_IS_INT32(variant));
+  int32 integer = NPVARIANT_TO_INT32(variant);
+  return NPN_GetIntIdentifier(integer);
+}
+
+NPIdentifier
+doubleVariantToIdentifier(NPVariant variant)
+{
+  assert(NPVARIANT_IS_DOUBLE(variant));
+  double value = NPVARIANT_TO_DOUBLE(variant);
+  // sadly there is no "getdoubleidentifier"
+  int32 integer = static_cast<int32>(value);
+  return NPN_GetIntIdentifier(integer);
+}
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest_utils.h
@@ -0,0 +1,43 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * 
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nptest_utils_h_
+#define nptest_utils_h_
+
+#include "nptest.h"
+
+NPUTF8* createCStringFromNPVariant(const NPVariant* variant);
+
+NPIdentifier variantToIdentifier(NPVariant variant);
+NPIdentifier stringVariantToIdentifier(NPVariant variant);
+NPIdentifier int32VariantToIdentifier(NPVariant variant);
+NPIdentifier doubleVariantToIdentifier(NPVariant variant);
+
+#endif // nptest_utils_h_
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/testplugin/nptest_windows.cpp
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * 
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_platform.h"
+
+NPError
+pluginInstanceInit(InstanceData* instanceData)
+{
+  return NPERR_NO_ERROR;
+}
+
+int16_t
+pluginHandleEvent(InstanceData* instanceData, void* event)
+{
+  return 0;
+}
+
+void
+pluginDraw(InstanceData* instanceData)
+{
+}