root/content/shell/tools/plugin/PluginObject.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pluginLogWithWindowObject
  2. pluginLogWithArguments
  3. pluginLog
  4. createPluginClass
  5. createCStringFromNPVariant
  6. initializeIdentifiers
  7. callDeletePlugin
  8. pluginHasProperty
  9. pluginHasMethod
  10. pluginGetProperty
  11. pluginSetProperty
  12. testDOMAccess
  13. stringVariantToIdentifier
  14. int32VariantToIdentifier
  15. doubleVariantToIdentifier
  16. variantToIdentifier
  17. testIdentifierToString
  18. testIdentifierToInt
  19. testPassTestObject
  20. testCallback
  21. testCallbackReturn
  22. getURL
  23. getURLNotify
  24. testInvokeDefault
  25. destroyStream
  26. destroyNullStream
  27. testEnumerate
  28. testGetIntIdentifier
  29. testGetProperty
  30. testHasProperty
  31. testHasMethod
  32. testEvaluate
  33. testGetPropertyReturnValue
  34. toCString
  35. testPostURLFile
  36. testConstruct
  37. testScriptObjectInvoke
  38. notifyTestCompletion
  39. testDocumentOpen
  40. testWindowOpen
  41. testSetStatus
  42. testResizeTo
  43. normalizeOverride
  44. invalidateRect
  45. objectsAreSame
  46. pluginInvoke
  47. pluginInvalidate
  48. pluginAllocate
  49. pluginDeallocate
  50. handleCallback
  51. notifyStream
  52. testNPRuntime

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/*
 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
 * Copyright (C) 2009 Holger Hans Peter Freyther
 * Copyright (C) 2010 Collabora Ltd.
 *
 * 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.
 */

#include "PluginObject.h"

#include "PluginTest.h"
#include "content/shell/tools/plugin/test_object.h"
#include "base/strings/string_util.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Helper function which takes in the plugin window object for logging to the
// console object.
static void pluginLogWithWindowObject(NPObject* windowObject,
                                      NPP instance,
                                      const char* message) {
    NPVariant consoleVariant;
    if (!browser->getproperty(instance,
                              windowObject,
                              browser->getstringidentifier("console"),
                              &consoleVariant)) {
      fprintf(stderr,
              "Failed to retrieve console object while logging: %s\n",
              message);
        return;
    }

    NPObject* consoleObject = NPVARIANT_TO_OBJECT(consoleVariant);

    NPVariant messageVariant;
    STRINGZ_TO_NPVARIANT(message, messageVariant);

    NPVariant result;
    if (!browser->invoke(instance,
                         consoleObject,
                         browser->getstringidentifier("log"),
                         &messageVariant,
                         1,
                         &result)) {
      fprintf(
          stderr, "Failed to invoke console.log while logging: %s\n", message);
        browser->releaseobject(consoleObject);
        return;
    }

    browser->releasevariantvalue(&result);
    browser->releaseobject(consoleObject);
}

void pluginLogWithArguments(NPP instance, const char* format, va_list args) {
  const size_t messageBufferSize = 2048;
  char message[messageBufferSize] = "PLUGIN: ";
  int messageLength = sizeof("PLUGIN: ") - 1;
  messageLength += vsnprintf(message + messageLength,
                             messageBufferSize - 1 - messageLength,
                             format,
                             args);
  message[messageLength] = '\0';

  NPObject* windowObject = 0;
  NPError error =
      browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
  if (error != NPERR_NO_ERROR) {
    fprintf(stderr,
            "Failed to retrieve window object while logging: %s\n",
            message);
    return;
  }

  pluginLogWithWindowObject(windowObject, instance, message);
  browser->releaseobject(windowObject);
}

// Helper function to log to the console object.
void pluginLog(NPP instance, const char* format, ...) {
  va_list args;
  va_start(args, format);
  pluginLogWithArguments(instance, format, args);
  va_end(args);
}

static void pluginInvalidate(NPObject*);
static bool pluginHasProperty(NPObject*, NPIdentifier name);
static bool pluginHasMethod(NPObject*, NPIdentifier name);
static bool pluginGetProperty(NPObject*, NPIdentifier name, NPVariant*);
static bool pluginSetProperty(NPObject*, NPIdentifier name, const NPVariant*);
static bool pluginInvoke(NPObject*,
                         NPIdentifier name,
                         const NPVariant* args,
                         uint32_t argCount,
                         NPVariant* result);
static NPObject* pluginAllocate(NPP npp, NPClass*);
static void pluginDeallocate(NPObject*);

NPNetscapeFuncs* browser;
NPPluginFuncs* pluginFunctions;

static NPClass pluginClass_ = {
    NP_CLASS_STRUCT_VERSION, pluginAllocate,    pluginDeallocate,
    pluginInvalidate,        pluginHasMethod,   pluginInvoke,
    0,  // NPClass::invokeDefault,
    pluginHasProperty,       pluginGetProperty, pluginSetProperty,
    0,  // NPClass::removeProperty
    0,  // NPClass::enumerate
    0,  // NPClass::construct
};

NPClass* createPluginClass(void) {
  NPClass* pluginClass = new NPClass;
  *pluginClass = pluginClass_;
  return pluginClass;
}

static bool identifiersInitialized = false;

enum {
  ID_PROPERTY_PROPERTY = 0,
  ID_PROPERTY_EVENT_LOGGING,
  ID_PROPERTY_HAS_STREAM,
  ID_PROPERTY_TEST_OBJECT,
  ID_PROPERTY_LOG_DESTROY,
  ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM,
  ID_PROPERTY_RETURN_NEGATIVE_ONE_FROM_WRITE,
  ID_PROPERTY_THROW_EXCEPTION_PROPERTY,
  ID_LAST_SET_WINDOW_ARGUMENTS,
  ID_PROPERTY_WINDOWED_PLUGIN,
  ID_PROPERTY_TEST_OBJECT_COUNT,
  ID_PROPERTY_DELETE_IN_GET_PROPERTY,
  ID_PROPERTY_DELETE_IN_HAS_PROPERTY_RETURN_TRUE,
  ID_PROPERTY_DELETE_IN_SET_PROPERTY,
  NUM_PROPERTY_IDENTIFIERS
};

static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
static const NPUTF8* pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
    "property",                            "eventLoggingEnabled",
    "hasStream",                           "testObject",
    "logDestroy",                          "returnErrorFromNewStream",
    "returnNegativeOneFromWrite",          "testThrowExceptionProperty",
    "lastSetWindowArguments",              "windowedPlugin",
    "testObjectCount",                     "deletePluginInGetProperty",
    "deletePluginInHasPropertyReturnTrue", "deletePluginInSetProperty"};

enum {
  ID_TEST_CALLBACK_METHOD = 0,
  ID_TEST_CALLBACK_METHOD_RETURN,
  ID_TEST_GETURL,
  ID_TEST_DOM_ACCESS,
  ID_TEST_GET_URL_NOTIFY,
  ID_TEST_INVOKE_DEFAULT,
  ID_DESTROY_STREAM,
  ID_TEST_ENUMERATE,
  ID_TEST_GETINTIDENTIFIER,
  ID_TEST_GET_PROPERTY,
  ID_TEST_HAS_PROPERTY,
  ID_TEST_HAS_METHOD,
  ID_TEST_EVALUATE,
  ID_TEST_GET_PROPERTY_RETURN_VALUE,
  ID_TEST_IDENTIFIER_TO_STRING,
  ID_TEST_IDENTIFIER_TO_INT,
  ID_TEST_PASS_TEST_OBJECT,
  ID_TEST_POSTURL_FILE,
  ID_TEST_CONSTRUCT,
  ID_TEST_THROW_EXCEPTION_METHOD,
  ID_TEST_FAIL_METHOD,
  ID_TEST_CLONE_OBJECT,
  ID_TEST_SCRIPT_OBJECT_INVOKE,
  ID_TEST_CREATE_TEST_OBJECT,
  ID_DESTROY_NULL_STREAM,
  ID_TEST_RELOAD_PLUGINS_NO_PAGES,
  ID_TEST_RELOAD_PLUGINS_AND_PAGES,
  ID_TEST_GET_BROWSER_PROPERTY,
  ID_TEST_SET_BROWSER_PROPERTY,
  ID_REMEMBER,
  ID_GET_REMEMBERED_OBJECT,
  ID_GET_AND_FORGET_REMEMBERED_OBJECT,
  ID_REF_COUNT,
  ID_SET_STATUS,
  ID_RESIZE_TO,
  ID_NORMALIZE,
  ID_INVALIDATE_RECT,
  ID_OBJECTS_ARE_SAME,
  ID_TEST_DELETE_WITHIN_INVOKE,
  NUM_METHOD_IDENTIFIERS
};

static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
static const NPUTF8* pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
    "testCallback",           "testCallbackReturn",
    "getURL",                 "testDOMAccess",
    "getURLNotify",           "testInvokeDefault",
    "destroyStream",          "testEnumerate",
    "testGetIntIdentifier",   "testGetProperty",
    "testHasProperty",        "testHasMethod",
    "testEvaluate",           "testGetPropertyReturnValue",
    "testIdentifierToString", "testIdentifierToInt",
    "testPassTestObject",     "testPostURLFile",
    "testConstruct",          "testThrowException",
    "testFail",               "testCloneObject",
    "testScriptObjectInvoke", "testCreateTestObject",
    "destroyNullStream",      "reloadPluginsNoPages",
    "reloadPluginsAndPages",  "testGetBrowserProperty",
    "testSetBrowserProperty", "remember",
    "getRememberedObject",    "getAndForgetRememberedObject",
    "refCount",               "setStatus",
    "resizeTo",               "normalize",
    "invalidateRect",         "objectsAreSame",
    "testDeleteWithinInvoke"};

static 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;
}

static void initializeIdentifiers(void) {
  browser->getstringidentifiers(pluginPropertyIdentifierNames,
                                NUM_PROPERTY_IDENTIFIERS,
                                pluginPropertyIdentifiers);
  browser->getstringidentifiers(pluginMethodIdentifierNames,
                                NUM_METHOD_IDENTIFIERS,
                                pluginMethodIdentifiers);
}

static bool callDeletePlugin(NPObject* obj,
                             NPIdentifier name,
                             NPIdentifier identifierToMatch) {
  if (name != identifierToMatch)
    return false;

  PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
  NPObject* windowScriptObject;
  browser->getvalue(plugin->npp, NPNVWindowNPObject, &windowScriptObject);

  NPIdentifier callbackIdentifier =
      browser->getstringidentifier("deletePlugin");
  NPVariant browserResult;
  if (browser->invoke(plugin->npp,
                      windowScriptObject,
                      callbackIdentifier,
                      0,
                      0,
                      &browserResult))
    browser->releasevariantvalue(&browserResult);
  return true;
}

static bool pluginHasProperty(NPObject* obj, NPIdentifier name) {
  if (callDeletePlugin(
          obj,
          name,
          browser->getstringidentifier("deletePluginInHasPropertyReturnFalse")))
    return false;

  callDeletePlugin(obj,
                   name,
                   pluginPropertyIdentifiers
                       [ID_PROPERTY_DELETE_IN_HAS_PROPERTY_RETURN_TRUE]);

  for (int i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
    if (name == pluginPropertyIdentifiers[i])
      return true;
  return false;
}

static bool pluginHasMethod(NPObject* obj, NPIdentifier name) {
  if (callDeletePlugin(
          obj, name, browser->getstringidentifier("deletePluginInHasMethod")))
    return true;

  for (int i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
    if (name == pluginMethodIdentifiers[i])
      return true;
  return false;
}

static bool pluginGetProperty(NPObject* obj,
                              NPIdentifier name,
                              NPVariant* result) {
  PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_PROPERTY]) {
    static const char* originalString = "property";
    char* buf =
        static_cast<char*>(browser->memalloc(strlen(originalString) + 1));
    strcpy(buf, originalString);
    STRINGZ_TO_NPVARIANT(buf, *result);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
    BOOLEAN_TO_NPVARIANT(plugin->eventLogging, *result);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
    BOOLEAN_TO_NPVARIANT(plugin->logDestroy, *result);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_HAS_STREAM]) {
    BOOLEAN_TO_NPVARIANT(plugin->stream, *result);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_TEST_OBJECT]) {
    NPObject* testObject = plugin->testObject;
    browser->retainobject(testObject);
    OBJECT_TO_NPVARIANT(testObject, *result);
    return true;
  }
  if (name ==
      pluginPropertyIdentifiers[ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM]) {
    BOOLEAN_TO_NPVARIANT(plugin->returnErrorFromNewStream, *result);
    return true;
  }
  if (name ==
      pluginPropertyIdentifiers[ID_PROPERTY_RETURN_NEGATIVE_ONE_FROM_WRITE]) {
    BOOLEAN_TO_NPVARIANT(plugin->returnNegativeOneFromWrite, *result);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) {
    browser->setexception(obj,
                          "plugin object testThrowExceptionProperty SUCCESS");
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_LAST_SET_WINDOW_ARGUMENTS]) {
    char* buf = static_cast<char*>(browser->memalloc(256));
    snprintf(
        buf,
        256,
        "x: %d, y: %d, width: %u, height: %u, clipRect: (%u, %u, %u, %u)",
        (int)plugin->lastWindow.x,
        (int)plugin->lastWindow.y,
        (unsigned)plugin->lastWindow.width,
        (unsigned)plugin->lastWindow.height,
        plugin->lastWindow.clipRect.left,
        plugin->lastWindow.clipRect.top,
        plugin->lastWindow.clipRect.right - plugin->lastWindow.clipRect.left,
        plugin->lastWindow.clipRect.bottom - plugin->lastWindow.clipRect.top);

    STRINGZ_TO_NPVARIANT(buf, *result);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_TEST_OBJECT_COUNT]) {
    INT32_TO_NPVARIANT(content::GetTestObjectCount(), *result);
    return true;
  }

  if (name == pluginPropertyIdentifiers[ID_PROPERTY_DELETE_IN_GET_PROPERTY]) {
    browser->retainobject(obj);
    callDeletePlugin(
        obj,
        name,
        pluginPropertyIdentifiers[ID_PROPERTY_DELETE_IN_GET_PROPERTY]);
    NPObject* testObject = plugin->testObject;
    browser->retainobject(testObject);
    OBJECT_TO_NPVARIANT(testObject, *result);
    browser->releaseobject(obj);
    return true;
  }

  return false;
}

static bool pluginSetProperty(NPObject* obj,
                              NPIdentifier name,
                              const NPVariant* variant) {
  PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
  if (callDeletePlugin(
          obj,
          name,
          pluginPropertyIdentifiers[ID_PROPERTY_DELETE_IN_SET_PROPERTY]))
    return true;

  if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
    plugin->eventLogging = NPVARIANT_TO_BOOLEAN(*variant);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
    plugin->logDestroy = NPVARIANT_TO_BOOLEAN(*variant);
    return true;
  }
  if (name ==
      pluginPropertyIdentifiers[ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM]) {
    plugin->returnErrorFromNewStream = NPVARIANT_TO_BOOLEAN(*variant);
    return true;
  }
  if (name ==
      pluginPropertyIdentifiers[ID_PROPERTY_RETURN_NEGATIVE_ONE_FROM_WRITE]) {
    plugin->returnNegativeOneFromWrite = NPVARIANT_TO_BOOLEAN(*variant);
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) {
    browser->setexception(obj,
                          "plugin object testThrowExceptionProperty SUCCESS");
    return true;
  }
  if (name == pluginPropertyIdentifiers[ID_PROPERTY_WINDOWED_PLUGIN]) {
    browser->setvalue(plugin->npp,
                      NPPVpluginWindowBool,
                      (void*)NPVARIANT_TO_BOOLEAN(*variant));
    return true;
  }

  return false;
}

static bool testDOMAccess(PluginObject* obj,
                          const NPVariant*,
                          uint32_t,
                          NPVariant* result) {
  // Get plug-in's DOM element
  NPObject* elementObject;
  if (browser->getvalue(obj->npp, NPNVPluginElementNPObject, &elementObject) ==
      NPERR_NO_ERROR) {
    // Get style
    NPVariant styleVariant;
    NPIdentifier styleIdentifier = browser->getstringidentifier("style");
    if (browser->getproperty(
            obj->npp, elementObject, styleIdentifier, &styleVariant) &&
        NPVARIANT_IS_OBJECT(styleVariant)) {
      // Set style.border
      NPIdentifier borderIdentifier = browser->getstringidentifier("border");
      NPVariant borderVariant;
      STRINGZ_TO_NPVARIANT("3px solid red", borderVariant);
      browser->setproperty(obj->npp,
                           NPVARIANT_TO_OBJECT(styleVariant),
                           borderIdentifier,
                           &borderVariant);
      browser->releasevariantvalue(&styleVariant);
    }

    browser->releaseobject(elementObject);
  }
  VOID_TO_NPVARIANT(*result);
  return true;
}

static NPIdentifier stringVariantToIdentifier(NPVariant variant) {
  assert(NPVARIANT_IS_STRING(variant));
  NPUTF8* utf8String = createCStringFromNPVariant(&variant);
  NPIdentifier identifier = browser->getstringidentifier(utf8String);
  free(utf8String);
  return identifier;
}

static NPIdentifier int32VariantToIdentifier(NPVariant variant) {
  assert(NPVARIANT_IS_INT32(variant));
  int32_t integer = NPVARIANT_TO_INT32(variant);
  return browser->getintidentifier(integer);
}

static NPIdentifier doubleVariantToIdentifier(NPVariant variant) {
  assert(NPVARIANT_IS_DOUBLE(variant));
  double value = NPVARIANT_TO_DOUBLE(variant);
  // Sadly there is no "getdoubleidentifier"
  int32_t integer = static_cast<int32_t>(value);
  return browser->getintidentifier(integer);
}

static NPIdentifier variantToIdentifier(NPVariant variant) {
  if (NPVARIANT_IS_STRING(variant))
    return stringVariantToIdentifier(variant);
  if (NPVARIANT_IS_INT32(variant))
    return int32VariantToIdentifier(variant);
  if (NPVARIANT_IS_DOUBLE(variant))
    return doubleVariantToIdentifier(variant);
  return 0;
}

static bool testIdentifierToString(PluginObject*,
                                   const NPVariant* args,
                                   uint32_t argCount,
                                   NPVariant* result) {
  if (argCount != 1)
    return true;
  NPIdentifier identifier = variantToIdentifier(args[0]);
  if (!identifier)
    return true;
  NPUTF8* utf8String = browser->utf8fromidentifier(identifier);
  if (!utf8String)
    return true;
  STRINGZ_TO_NPVARIANT(utf8String, *result);
  return true;
}

static bool testIdentifierToInt(PluginObject*,
                                const NPVariant* args,
                                uint32_t argCount,
                                NPVariant* result) {
  if (argCount != 1)
    return false;
  NPIdentifier identifier = variantToIdentifier(args[0]);
  if (!identifier)
    return false;
  int32_t integer = browser->intfromidentifier(identifier);
  INT32_TO_NPVARIANT(integer, *result);
  return true;
}

static bool testPassTestObject(PluginObject* obj,
                               const NPVariant* args,
                               uint32_t argCount,
                               NPVariant* result) {
  if (argCount != 2 || !NPVARIANT_IS_STRING(args[0]))
    return false;

  NPObject* windowScriptObject;
  browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);

  NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
  NPIdentifier callbackIdentifier =
      browser->getstringidentifier(callbackString);
  free(callbackString);

  NPVariant browserResult;
  browser->invoke(obj->npp,
                  windowScriptObject,
                  callbackIdentifier,
                  &args[1],
                  1,
                  &browserResult);
  browser->releasevariantvalue(&browserResult);

  VOID_TO_NPVARIANT(*result);
  return true;
}

static bool testCallback(PluginObject* obj,
                         const NPVariant* args,
                         uint32_t argCount,
                         NPVariant* result) {
  if (!argCount || !NPVARIANT_IS_STRING(args[0]))
    return false;

  NPObject* windowScriptObject;
  browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);

  NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
  NPIdentifier callbackIdentifier =
      browser->getstringidentifier(callbackString);
  free(callbackString);

  NPVariant browserResult;
  if (browser->invoke(obj->npp,
                      windowScriptObject,
                      callbackIdentifier,
                      0,
                      0,
                      &browserResult))
    browser->releasevariantvalue(&browserResult);

  browser->releaseobject(windowScriptObject);

  VOID_TO_NPVARIANT(*result);
  return true;
}

static bool testCallbackReturn(PluginObject* obj,
                               const NPVariant* args,
                               uint32_t argCount,
                               NPVariant* result) {
  if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
    return false;

  NPObject* windowScriptObject;
  browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);

  NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
  NPIdentifier callbackIdentifier =
      browser->getstringidentifier(callbackString);
  free(callbackString);

  NPVariant callbackArgs[1];
  OBJECT_TO_NPVARIANT(windowScriptObject, callbackArgs[0]);

  NPVariant browserResult;
  browser->invoke(obj->npp,
                  windowScriptObject,
                  callbackIdentifier,
                  callbackArgs,
                  1,
                  &browserResult);

  if (NPVARIANT_IS_OBJECT(browserResult))
    OBJECT_TO_NPVARIANT(NPVARIANT_TO_OBJECT(browserResult), *result);
  else {
    browser->releasevariantvalue(&browserResult);
    VOID_TO_NPVARIANT(*result);
  }

  return true;
}

static bool getURL(PluginObject* obj,
                   const NPVariant* args,
                   uint32_t argCount,
                   NPVariant* result) {
  if (argCount == 2 && NPVARIANT_IS_STRING(args[0]) &&
      NPVARIANT_IS_STRING(args[1])) {
    NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
    NPUTF8* targetString = createCStringFromNPVariant(&args[1]);
    NPError npErr = browser->geturl(obj->npp, urlString, targetString);
    free(urlString);
    free(targetString);

    INT32_TO_NPVARIANT(npErr, *result);
    return true;
  }
  if (argCount == 1 && NPVARIANT_IS_STRING(args[0])) {
    NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
    NPError npErr = browser->geturl(obj->npp, urlString, 0);
    free(urlString);

    INT32_TO_NPVARIANT(npErr, *result);
    return true;
  }
  return false;
}

static bool getURLNotify(PluginObject* obj,
                         const NPVariant* args,
                         uint32_t argCount,
                         NPVariant* result) {
  if (argCount != 3 || !NPVARIANT_IS_STRING(args[0]) ||
      (!NPVARIANT_IS_STRING(args[1]) && !NPVARIANT_IS_NULL(args[1])) ||
      !NPVARIANT_IS_STRING(args[2]))
    return false;

  NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
  NPUTF8* targetString =
      (NPVARIANT_IS_STRING(args[1]) ? createCStringFromNPVariant(&args[1]) : 0);
  NPUTF8* callbackString = createCStringFromNPVariant(&args[2]);

  NPIdentifier callbackIdentifier =
      browser->getstringidentifier(callbackString);
  browser->geturlnotify(obj->npp, urlString, targetString, callbackIdentifier);

  free(urlString);
  free(targetString);
  free(callbackString);

  VOID_TO_NPVARIANT(*result);
  return true;
}

static bool testInvokeDefault(PluginObject* obj,
                              const NPVariant* args,
                              uint32_t argCount,
                              NPVariant* result) {
  if (!NPVARIANT_IS_OBJECT(args[0]))
    return false;

  NPObject* callback = NPVARIANT_TO_OBJECT(args[0]);

  NPVariant invokeArgs[1];
  NPVariant browserResult;

  STRINGZ_TO_NPVARIANT("test", invokeArgs[0]);
  bool retval =
      browser->invokeDefault(obj->npp, callback, invokeArgs, 1, &browserResult);

  if (retval)
    browser->releasevariantvalue(&browserResult);

  BOOLEAN_TO_NPVARIANT(retval, *result);
  return true;
}

static bool destroyStream(PluginObject* obj,
                          const NPVariant* args,
                          uint32_t argCount,
                          NPVariant* result) {
  NPError npError =
      browser->destroystream(obj->npp, obj->stream, NPRES_USER_BREAK);
  INT32_TO_NPVARIANT(npError, *result);
  return true;
}

static bool destroyNullStream(PluginObject* obj,
                              const NPVariant* args,
                              uint32_t argCount,
                              NPVariant* result) {
  NPError npError = browser->destroystream(obj->npp, 0, NPRES_USER_BREAK);
  INT32_TO_NPVARIANT(npError, *result);
  return true;
}

static bool testEnumerate(PluginObject* obj,
                          const NPVariant* args,
                          uint32_t argCount,
                          NPVariant* result) {
  if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) ||
      !NPVARIANT_IS_OBJECT(args[1]))
    return false;

  uint32_t count;
  NPIdentifier* identifiers;
  if (browser->enumerate(
          obj->npp, NPVARIANT_TO_OBJECT(args[0]), &identifiers, &count)) {
    NPObject* outArray = NPVARIANT_TO_OBJECT(args[1]);
    NPIdentifier pushIdentifier = browser->getstringidentifier("push");

    for (uint32_t i = 0; i < count; i++) {
      NPUTF8* string = browser->utf8fromidentifier(identifiers[i]);

      if (!string)
        continue;

      NPVariant args[1];
      STRINGZ_TO_NPVARIANT(string, args[0]);
      NPVariant browserResult;
      if (browser->invoke(
              obj->npp, outArray, pushIdentifier, args, 1, &browserResult))
        browser->releasevariantvalue(&browserResult);
      browser->memfree(string);
    }

    browser->memfree(identifiers);
  }

  VOID_TO_NPVARIANT(*result);
  return true;
}

static bool testGetIntIdentifier(PluginObject*,
                                 const NPVariant* args,
                                 uint32_t argCount,
                                 NPVariant* result) {
  if (argCount != 1 || !NPVARIANT_IS_DOUBLE(args[0]))
    return false;

  NPIdentifier identifier =
      browser->getintidentifier((int)NPVARIANT_TO_DOUBLE(args[0]));
  INT32_TO_NPVARIANT((int32_t)(long long)identifier, *result);
  return true;
}

static bool testGetProperty(PluginObject* obj,
                            const NPVariant* args,
                            uint32_t argCount,
                            NPVariant* result) {
  if (!argCount)
    return false;

  NPObject* object;
  browser->getvalue(obj->npp, NPNVWindowNPObject, &object);

  for (uint32_t i = 0; i < argCount; i++) {
    assert(NPVARIANT_IS_STRING(args[i]));
    NPUTF8* propertyString = createCStringFromNPVariant(&args[i]);
    NPIdentifier propertyIdentifier =
        browser->getstringidentifier(propertyString);
    free(propertyString);

    NPVariant variant;
    bool retval =
        browser->getproperty(obj->npp, object, propertyIdentifier, &variant);
    browser->releaseobject(object);

    if (!retval)
      break;

    if (i + 1 < argCount) {
      assert(NPVARIANT_IS_OBJECT(variant));
      object = NPVARIANT_TO_OBJECT(variant);
    } else {
      *result = variant;
      return true;
    }
  }

  VOID_TO_NPVARIANT(*result);
  return false;
}

static bool testHasProperty(PluginObject* obj,
                            const NPVariant* args,
                            uint32_t argCount,
                            NPVariant* result) {
  if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) ||
      !NPVARIANT_IS_STRING(args[1]))
    return false;

  NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
  NPIdentifier propertyIdentifier =
      browser->getstringidentifier(propertyString);
  free(propertyString);

  bool retval = browser->hasproperty(
      obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier);

  BOOLEAN_TO_NPVARIANT(retval, *result);
  return true;
}

static bool testHasMethod(PluginObject* obj,
                          const NPVariant* args,
                          uint32_t argCount,
                          NPVariant* result) {
  if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) ||
      !NPVARIANT_IS_STRING(args[1]))
    return false;

  NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
  NPIdentifier propertyIdentifier =
      browser->getstringidentifier(propertyString);
  free(propertyString);

  bool retval = browser->hasmethod(
      obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier);

  BOOLEAN_TO_NPVARIANT(retval, *result);
  return true;
}

static bool testEvaluate(PluginObject* obj,
                         const NPVariant* args,
                         uint32_t argCount,
                         NPVariant* result) {
  if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
    return false;
  NPObject* windowScriptObject;
  browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);

  NPString s = NPVARIANT_TO_STRING(args[0]);

  bool retval = browser->evaluate(obj->npp, windowScriptObject, &s, result);
  browser->releaseobject(windowScriptObject);
  return retval;
}

static bool testGetPropertyReturnValue(PluginObject* obj,
                                       const NPVariant* args,
                                       uint32_t argCount,
                                       NPVariant* result) {
  if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) ||
      !NPVARIANT_IS_STRING(args[1]))
    return false;

  NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
  NPIdentifier propertyIdentifier =
      browser->getstringidentifier(propertyString);
  free(propertyString);

  NPVariant variant;
  bool retval = browser->getproperty(
      obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier, &variant);
  if (retval)
    browser->releasevariantvalue(&variant);

  BOOLEAN_TO_NPVARIANT(retval, *result);
  return true;
}

static char* toCString(const NPString& string) {
  char* result = static_cast<char*>(malloc(string.UTF8Length + 1));
  memcpy(result, string.UTF8Characters, string.UTF8Length);
  result[string.UTF8Length] = '\0';

  return result;
}

static bool testPostURLFile(PluginObject* obj,
                            const NPVariant* args,
                            uint32_t argCount,
                            NPVariant* result) {
  if (argCount != 4 || !NPVARIANT_IS_STRING(args[0]) ||
      !NPVARIANT_IS_STRING(args[1]) || !NPVARIANT_IS_STRING(args[2]) ||
      !NPVARIANT_IS_STRING(args[3]))
    return false;

  NPString urlString = NPVARIANT_TO_STRING(args[0]);
  char* url = toCString(urlString);

  NPString targetString = NPVARIANT_TO_STRING(args[1]);
  char* target = toCString(targetString);

  NPString pathString = NPVARIANT_TO_STRING(args[2]);
  char* path = toCString(pathString);

  NPString contentsString = NPVARIANT_TO_STRING(args[3]);

  FILE* tempFile = fopen(path, "w");
  if (!tempFile)
    return false;

  size_t written = fwrite(
      contentsString.UTF8Characters, contentsString.UTF8Length, 1, tempFile);
  fclose(tempFile);
  if (!written)
    return false;

  NPError error = browser->posturl(
      obj->npp, url, target, pathString.UTF8Length, path, true);

  free(path);
  free(target);
  free(url);

  BOOLEAN_TO_NPVARIANT(error == NPERR_NO_ERROR, *result);
  return true;
}

static bool testConstruct(PluginObject* obj,
                          const NPVariant* args,
                          uint32_t argCount,
                          NPVariant* result) {
  if (!argCount || !NPVARIANT_IS_OBJECT(args[0]))
    return false;

  return browser->construct(
      obj->npp, NPVARIANT_TO_OBJECT(args[0]), args + 1, argCount - 1, result);
}

// Invoke a script callback to get a script NPObject. Then call a method on the
// script NPObject passing it a freshly created NPObject.
static bool testScriptObjectInvoke(PluginObject* obj,
                                   const NPVariant* args,
                                   uint32_t argCount,
                                   NPVariant* result) {
  if (argCount != 2 || !NPVARIANT_IS_STRING(args[0]) ||
      !NPVARIANT_IS_STRING(args[1]))
    return false;
  NPObject* windowScriptObject;
  browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);

  // Arg1 is the name of the callback
  NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
  NPIdentifier callbackIdentifier =
      browser->getstringidentifier(callbackString);
  free(callbackString);

  // Invoke a callback that returns a script object
  NPVariant object_result;
  browser->invoke(obj->npp,
                  windowScriptObject,
                  callbackIdentifier,
                  &args[1],
                  1,
                  &object_result);

  // Script object returned
  NPObject* script_object = object_result.value.objectValue;

  // Arg2 is the name of the method to be called on the script object
  NPUTF8* object_mehod_string = createCStringFromNPVariant(&args[1]);
  NPIdentifier object_method =
      browser->getstringidentifier(object_mehod_string);
  free(object_mehod_string);

  // Create a fresh NPObject to be passed as an argument
  NPObject* object_arg = browser->createobject(obj->npp, obj->header._class);

  NPVariant invoke_args[1];
  OBJECT_TO_NPVARIANT(object_arg, invoke_args[0]);

  // Invoke the script method
  NPVariant object_method_result;
  browser->invoke(obj->npp,
                  script_object,
                  object_method,
                  invoke_args,
                  1,
                  &object_method_result);

  browser->releasevariantvalue(&object_result);
  VOID_TO_NPVARIANT(*result);
  if (NPVARIANT_IS_OBJECT(object_method_result)) {
    // Now return the callbacks return value back to our caller.
    // BUG 897451: This should be the same as the
    // windowScriptObject, but its not (in Chrome) - or at least, it
    // has a different refcount. This means Chrome will delete the
    // object before returning it and the calling JS gets a garbage
    // value.  Firefox handles it fine.
    OBJECT_TO_NPVARIANT(NPVARIANT_TO_OBJECT(object_method_result), *result);
  } else {
    browser->releasevariantvalue(&object_method_result);
    VOID_TO_NPVARIANT(*result);
  }

  browser->releaseobject(object_arg);

  return true;
}

// Helper function to notify the layout test controller that the test completed.
void notifyTestCompletion(NPP npp, NPObject* object) {
  NPVariant result;
  NPString script;
  script.UTF8Characters = "javascript:window.testRunner.notifyDone();";
  script.UTF8Length = strlen("javascript:window.testRunner.notifyDone();");
  browser->evaluate(npp, object, &script, &result);
  browser->releasevariantvalue(&result);
}

bool testDocumentOpen(NPP npp) {
  NPIdentifier documentId = browser->getstringidentifier("document");
  NPIdentifier openId = browser->getstringidentifier("open");

  NPObject* windowObject = 0;
  browser->getvalue(npp, NPNVWindowNPObject, &windowObject);
  if (!windowObject)
    return false;

  NPVariant docVariant;
  browser->getproperty(npp, windowObject, documentId, &docVariant);
  if (docVariant.type != NPVariantType_Object) {
    browser->releaseobject(windowObject);
    return false;
  }

  NPObject* documentObject = NPVARIANT_TO_OBJECT(docVariant);

  NPVariant openArgs[2];
  STRINGZ_TO_NPVARIANT("text/html", openArgs[0]);
  STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);

  NPVariant result;
  if (!browser->invoke(npp, documentObject, openId, openArgs, 2, &result)) {
    browser->releaseobject(windowObject);
    browser->releaseobject(documentObject);
    return false;
  }

  browser->releaseobject(documentObject);

  if (result.type != NPVariantType_Object) {
    browser->releaseobject(windowObject);
    browser->releasevariantvalue(&result);
    return false;
  }

  pluginLogWithWindowObject(windowObject, npp, "PLUGIN: DOCUMENT OPEN SUCCESS");
  notifyTestCompletion(npp, result.value.objectValue);
  browser->releaseobject(result.value.objectValue);
  browser->releaseobject(windowObject);
  return true;
}

bool testWindowOpen(NPP npp) {
  NPIdentifier openId = browser->getstringidentifier("open");

  NPObject* windowObject = 0;
  browser->getvalue(npp, NPNVWindowNPObject, &windowObject);
  if (!windowObject)
    return false;

  NPVariant openArgs[2];
  STRINGZ_TO_NPVARIANT("about:blank", openArgs[0]);
  STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);

  NPVariant result;
  if (!browser->invoke(npp, windowObject, openId, openArgs, 2, &result)) {
    browser->releaseobject(windowObject);
    return false;
  }

  if (result.type != NPVariantType_Object) {
    browser->releaseobject(windowObject);
    browser->releasevariantvalue(&result);
    return false;
  }

  pluginLogWithWindowObject(windowObject, npp, "PLUGIN: WINDOW OPEN SUCCESS");
  notifyTestCompletion(npp, result.value.objectValue);
  browser->releaseobject(result.value.objectValue);
  browser->releaseobject(windowObject);
  return true;
}

static bool testSetStatus(PluginObject* obj,
                          const NPVariant* args,
                          uint32_t argCount,
                          NPVariant* result) {
  char* message = 0;
  if (argCount && NPVARIANT_IS_STRING(args[0])) {
    NPString statusString = NPVARIANT_TO_STRING(args[0]);
    message = toCString(statusString);
  }

  browser->status(obj->npp, message);

  free(message);
  return true;
}

static bool testResizeTo(PluginObject* obj,
                         const NPVariant* args,
                         uint32_t argCount,
                         NPVariant* result) {
  VOID_TO_NPVARIANT(*result);

  NPObject* windowObject;
  if (NPERR_NO_ERROR !=
      browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject))
    return false;

  NPVariant callResult;
  if (browser->invoke(obj->npp,
                      windowObject,
                      browser->getstringidentifier("resizePlugin"),
                      args,
                      argCount,
                      &callResult))
    browser->releasevariantvalue(&callResult);

  // Force layout.
  if (browser->getproperty(obj->npp,
                           windowObject,
                           browser->getstringidentifier("pageYOffset"),
                           &callResult))
    browser->releasevariantvalue(&callResult);

  return true;
}

static bool normalizeOverride(PluginObject* obj,
                              const NPVariant* args,
                              uint32_t argCount,
                              NPVariant* result) {
  VOID_TO_NPVARIANT(*result);

  NPObject* windowObject;
  if (NPERR_NO_ERROR !=
      browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject))
    return false;

  NPVariant callResult;
  if (browser->invoke(obj->npp,
                      windowObject,
                      browser->getstringidentifier("pluginCallback"),
                      args,
                      argCount,
                      &callResult))
    browser->releasevariantvalue(&callResult);

  return true;
}

static bool invalidateRect(PluginObject* obj,
                           const NPVariant* args,
                           uint32_t argCount,
                           NPVariant* result) {
  if (argCount != 4)
    return false;

  NPRect rect;
  rect.left = static_cast<int>(NPVARIANT_TO_DOUBLE(args[0]));
  rect.top = static_cast<int>(NPVARIANT_TO_DOUBLE(args[1]));
  rect.right = static_cast<int>(NPVARIANT_TO_DOUBLE(args[2]));
  rect.bottom = static_cast<int>(NPVARIANT_TO_DOUBLE(args[3]));

  browser->invalidaterect(obj->npp, &rect);
  return true;
}

static bool objectsAreSame(PluginObject* obj,
                           const NPVariant* args,
                           uint32_t argCount,
                           NPVariant* result) {
  if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) ||
      !NPVARIANT_IS_OBJECT(args[1]))
    return false;

  BOOLEAN_TO_NPVARIANT(
      NPVARIANT_TO_OBJECT(args[0]) == NPVARIANT_TO_OBJECT(args[1]), *result);
  return true;
}

static bool pluginInvoke(NPObject* header,
                         NPIdentifier name,
                         const NPVariant* args,
                         uint32_t argCount,
                         NPVariant* result) {
  PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
  if (name == pluginMethodIdentifiers[ID_TEST_CALLBACK_METHOD])
    return testCallback(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_CALLBACK_METHOD_RETURN])
    return testCallbackReturn(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_GETURL])
    return getURL(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_DOM_ACCESS])
    return testDOMAccess(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_GET_URL_NOTIFY])
    return getURLNotify(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_INVOKE_DEFAULT])
    return testInvokeDefault(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_ENUMERATE])
    return testEnumerate(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_DESTROY_STREAM])
    return destroyStream(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_GETINTIDENTIFIER])
    return testGetIntIdentifier(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_EVALUATE])
    return testEvaluate(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY])
    return testGetProperty(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY_RETURN_VALUE])
    return testGetPropertyReturnValue(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_HAS_PROPERTY])
    return testHasProperty(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_HAS_METHOD])
    return testHasMethod(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_IDENTIFIER_TO_STRING])
    return testIdentifierToString(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_IDENTIFIER_TO_INT])
    return testIdentifierToInt(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_PASS_TEST_OBJECT])
    return testPassTestObject(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_POSTURL_FILE])
    return testPostURLFile(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_CONSTRUCT])
    return testConstruct(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_SCRIPT_OBJECT_INVOKE])
    return testScriptObjectInvoke(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_THROW_EXCEPTION_METHOD]) {
    browser->setexception(header, "plugin object testThrowException SUCCESS");
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_TEST_FAIL_METHOD]) {
    NPObject* windowScriptObject;
    browser->getvalue(plugin->npp, NPNVWindowNPObject, &windowScriptObject);
    browser->invoke(
        plugin->npp, windowScriptObject, name, args, argCount, result);
    return false;
  }
  if (name == pluginMethodIdentifiers[ID_TEST_CLONE_OBJECT]) {
    NPObject* new_object =
        browser->createobject(plugin->npp, plugin->header._class);
    assert(new_object->referenceCount == 1);
    OBJECT_TO_NPVARIANT(new_object, *result);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_TEST_CREATE_TEST_OBJECT]) {
    NPObject* testObject =
        browser->createobject(plugin->npp, content::GetTestClass());
    assert(testObject->referenceCount == 1);
    OBJECT_TO_NPVARIANT(testObject, *result);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_DESTROY_NULL_STREAM])
    return destroyNullStream(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_NO_PAGES]) {
    browser->reloadplugins(false);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_AND_PAGES]) {
    browser->reloadplugins(true);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_TEST_GET_BROWSER_PROPERTY]) {
    browser->getproperty(plugin->npp,
                         NPVARIANT_TO_OBJECT(args[0]),
                         stringVariantToIdentifier(args[1]),
                         result);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_TEST_SET_BROWSER_PROPERTY]) {
    browser->setproperty(plugin->npp,
                         NPVARIANT_TO_OBJECT(args[0]),
                         stringVariantToIdentifier(args[1]),
                         &args[2]);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_REMEMBER]) {
    if (plugin->rememberedObject)
      browser->releaseobject(plugin->rememberedObject);
    plugin->rememberedObject = NPVARIANT_TO_OBJECT(args[0]);
    browser->retainobject(plugin->rememberedObject);
    VOID_TO_NPVARIANT(*result);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_GET_REMEMBERED_OBJECT]) {
    assert(plugin->rememberedObject);
    browser->retainobject(plugin->rememberedObject);
    OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_GET_AND_FORGET_REMEMBERED_OBJECT]) {
    assert(plugin->rememberedObject);
    OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result);
    plugin->rememberedObject = 0;
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_REF_COUNT]) {
    uint32_t refCount = NPVARIANT_TO_OBJECT(args[0])->referenceCount;
    INT32_TO_NPVARIANT(refCount, *result);
    return true;
  }
  if (name == pluginMethodIdentifiers[ID_SET_STATUS])
    return testSetStatus(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_RESIZE_TO])
    return testResizeTo(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_NORMALIZE])
    return normalizeOverride(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_INVALIDATE_RECT])
    return invalidateRect(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_OBJECTS_ARE_SAME])
    return objectsAreSame(plugin, args, argCount, result);
  if (name == pluginMethodIdentifiers[ID_TEST_DELETE_WITHIN_INVOKE]) {
    NPObject* newObject =
        browser->createobject(plugin->npp, plugin->header._class);
    OBJECT_TO_NPVARIANT(newObject, *result);
    callDeletePlugin(
        header, name, pluginMethodIdentifiers[ID_TEST_DELETE_WITHIN_INVOKE]);
    return true;
  }
  return false;
}

static void pluginInvalidate(NPObject* header) {
  PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
  plugin->testObject = 0;
  plugin->rememberedObject = 0;
}

static NPObject* pluginAllocate(NPP npp, NPClass* theClass) {
  PluginObject* newInstance = (PluginObject*)malloc(sizeof(PluginObject));

  if (!identifiersInitialized) {
    identifiersInitialized = true;
    initializeIdentifiers();
  }

  newInstance->pluginTest = 0;
  newInstance->npp = npp;
  newInstance->testObject = browser->createobject(npp, content::GetTestClass());
  newInstance->rememberedObject = 0;
  newInstance->eventLogging = false;
  newInstance->onStreamLoad = 0;
  newInstance->onStreamDestroy = 0;
  newInstance->onDestroy = 0;
  newInstance->onURLNotify = 0;
  newInstance->onSetWindow = 0;
  newInstance->onPaintEvent = 0;
  newInstance->logDestroy = false;
  newInstance->logSetWindow = false;
  newInstance->returnErrorFromNewStream = false;
  newInstance->returnNegativeOneFromWrite = false;
  newInstance->stream = 0;

  newInstance->firstUrl = 0;
  newInstance->firstHeaders = 0;
  newInstance->lastUrl = 0;
  newInstance->lastHeaders = 0;

  newInstance->testGetURLOnDestroy = false;
  newInstance->testWindowOpen = false;
  newInstance->testKeyboardFocusForPlugins = false;

  newInstance->mouseDownForEvaluateScript = false;
  newInstance->evaluateScriptOnMouseDownOrKeyDown = 0;

  return (NPObject*)newInstance;
}

static void pluginDeallocate(NPObject* header) {
  PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
  delete plugin->pluginTest;
  if (plugin->testObject)
    browser->releaseobject(plugin->testObject);
  if (plugin->rememberedObject)
    browser->releaseobject(plugin->rememberedObject);

  free(plugin->firstUrl);
  free(plugin->firstHeaders);
  free(plugin->lastUrl);
  free(plugin->lastHeaders);
  free(plugin);
}

void handleCallback(PluginObject* object,
                    const char* url,
                    NPReason reason,
                    void* notifyData) {
  assert(object);

  NPVariant args[2];

  NPObject* windowScriptObject;
  browser->getvalue(object->npp, NPNVWindowNPObject, &windowScriptObject);

  NPIdentifier callbackIdentifier = notifyData;

  INT32_TO_NPVARIANT(reason, args[0]);

  char* strHdr = 0;
  if (object->firstUrl && object->firstHeaders && object->lastUrl &&
      object->lastHeaders) {
    // Format expected by JavaScript validator: four fields separated by \n\n:
    // First URL; first header block; last URL; last header block.
    // Note that header blocks already end with \n due to how NPStream::headers
    // works.
    int len = strlen(object->firstUrl) + 2 + strlen(object->firstHeaders) + 1 +
              strlen(object->lastUrl) + 2 + strlen(object->lastHeaders) + 1;
    strHdr = (char*)malloc(len + 1);
    snprintf(strHdr,
             len + 1,
             "%s\n\n%s\n%s\n\n%s\n",
             object->firstUrl,
             object->firstHeaders,
             object->lastUrl,
             object->lastHeaders);
    STRINGN_TO_NPVARIANT(strHdr, len, args[1]);
  } else
    NULL_TO_NPVARIANT(args[1]);

  NPVariant browserResult;
  if (browser->invoke(object->npp,
                      windowScriptObject,
                      callbackIdentifier,
                      args,
                      2,
                      &browserResult))
    browser->releasevariantvalue(&browserResult);

  free(strHdr);
}

void notifyStream(PluginObject* object, const char* url, const char* headers) {
  if (!object->firstUrl) {
    if (url)
      object->firstUrl = base::strdup(url);
    if (headers)
      object->firstHeaders = base::strdup(headers);
  } else {
    free(object->lastUrl);
    free(object->lastHeaders);
    object->lastUrl = (url ? base::strdup(url) : 0);
    object->lastHeaders = (headers ? base::strdup(headers) : 0);
  }
}

void testNPRuntime(NPP npp) {
  NPObject* windowScriptObject;
  browser->getvalue(npp, NPNVWindowNPObject, &windowScriptObject);

  // Invoke
  NPIdentifier testNPInvoke = browser->getstringidentifier("testNPInvoke");
  NPVariant args[7];

  VOID_TO_NPVARIANT(args[0]);
  NULL_TO_NPVARIANT(args[1]);
  BOOLEAN_TO_NPVARIANT(true, args[2]);
  INT32_TO_NPVARIANT(242, args[3]);
  DOUBLE_TO_NPVARIANT(242.242, args[4]);
  STRINGZ_TO_NPVARIANT("Hello, World", args[5]);
  OBJECT_TO_NPVARIANT(windowScriptObject, args[6]);

  NPVariant result;
  if (browser->invoke(npp, windowScriptObject, testNPInvoke, args, 7, &result))
    browser->releasevariantvalue(&result);

  browser->releaseobject(windowScriptObject);
}

/* [<][>][^][v][top][bottom][index][help] */