root/content/child/npapi/npobject_proxy.cc

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

DEFINITIONS

This source file includes following definitions.
  1. GetProxy
  2. GetUnderlyingNPObject
  3. GetChannelListener
  4. page_url_
  5. Create
  6. Send
  7. NPAllocate
  8. NPDeallocate
  9. OnMessageReceived
  10. OnChannelError
  11. NPHasMethod
  12. NPInvoke
  13. NPInvokeDefault
  14. NPInvokePrivate
  15. NPHasProperty
  16. NPGetProperty
  17. NPSetProperty
  18. NPRemoveProperty
  19. NPPInvalidate
  20. NPNEnumerate
  21. NPNConstruct
  22. NPNEvaluate

// Copyright 2013 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.

#include "content/child/npapi/npobject_proxy.h"

#include "content/child/npapi/np_channel_base.h"
#include "content/child/npapi/npobject_util.h"
#include "content/child/plugin_messages.h"
#include "third_party/WebKit/public/web/WebBindings.h"

#if defined(ENABLE_PLUGINS)
#include "content/child/npapi/plugin_instance.h"
#endif

using blink::WebBindings;

namespace content {

struct NPObjectWrapper {
    NPObject object;
    NPObjectProxy* proxy;
};

NPClass NPObjectProxy::npclass_proxy_ = {
  NP_CLASS_STRUCT_VERSION,
  NPObjectProxy::NPAllocate,
  NPObjectProxy::NPDeallocate,
  NPObjectProxy::NPPInvalidate,
  NPObjectProxy::NPHasMethod,
  NPObjectProxy::NPInvoke,
  NPObjectProxy::NPInvokeDefault,
  NPObjectProxy::NPHasProperty,
  NPObjectProxy::NPGetProperty,
  NPObjectProxy::NPSetProperty,
  NPObjectProxy::NPRemoveProperty,
  NPObjectProxy::NPNEnumerate,
  NPObjectProxy::NPNConstruct
};

NPObjectProxy* NPObjectProxy::GetProxy(NPObject* object) {
  NPObjectProxy* proxy = NULL;

  // Wrapper exists only for NPObjects that we had created.
  if (&npclass_proxy_ == object->_class) {
    NPObjectWrapper* wrapper = reinterpret_cast<NPObjectWrapper*>(object);
    proxy = wrapper->proxy;
  }

  return proxy;
}

NPObject* NPObjectProxy::GetUnderlyingNPObject() {
  return NULL;
}

IPC::Listener* NPObjectProxy::GetChannelListener() {
  return static_cast<IPC::Listener*>(this);
}

NPObjectProxy::NPObjectProxy(
    NPChannelBase* channel,
    int route_id,
    int render_view_id,
    const GURL& page_url)
    : channel_(channel),
      route_id_(route_id),
      render_view_id_(render_view_id),
      page_url_(page_url) {
  channel_->AddRoute(route_id, this, this);
}

NPObjectProxy::~NPObjectProxy() {
  if (channel_.get()) {
    // This NPObjectProxy instance is now invalid and should not be reused for
    // requests initiated by plugins. We may receive requests for the
    // same NPObject in the context of the outgoing NPObjectMsg_Release call.
    // We should be creating new NPObjectProxy instances to wrap these
    // NPObjects.
    channel_->RemoveMappingForNPObjectProxy(route_id_);
    channel_->RemoveRoute(route_id_);
    Send(new NPObjectMsg_Release(route_id_));
  }
}

NPObject* NPObjectProxy::Create(NPChannelBase* channel,
                                int route_id,
                                int render_view_id,
                                const GURL& page_url,
                                NPP owner) {
  NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(
      WebBindings::createObject(owner, &npclass_proxy_));
  obj->proxy = new NPObjectProxy(channel, route_id, render_view_id, page_url);
  channel->AddMappingForNPObjectProxy(route_id, &obj->object);
  return reinterpret_cast<NPObject*>(obj);
}

bool NPObjectProxy::Send(IPC::Message* msg) {
  if (channel_.get())
    return channel_->Send(msg);

  delete msg;
  return false;
}

NPObject* NPObjectProxy::NPAllocate(NPP, NPClass*) {
  return reinterpret_cast<NPObject*>(new NPObjectWrapper);
}

void NPObjectProxy::NPDeallocate(NPObject* npObj) {
  NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(npObj);
  delete obj->proxy;
  delete obj;
}

bool NPObjectProxy::OnMessageReceived(const IPC::Message& msg) {
  NOTREACHED();
  return false;
}

void NPObjectProxy::OnChannelError() {
  // Release our ref count of the plugin channel object, as it addrefs the
  // process.
  channel_ = NULL;
}

bool NPObjectProxy::NPHasMethod(NPObject *obj,
                                NPIdentifier name) {
  if (obj == NULL)
    return false;

  bool result = false;
  NPObjectProxy* proxy = GetProxy(obj);

  if (!proxy) {
    return obj->_class->hasMethod(obj, name);
  }

  NPIdentifier_Param name_param;
  CreateNPIdentifierParam(name, &name_param);

  proxy->Send(new NPObjectMsg_HasMethod(proxy->route_id(), name_param,
                                        &result));
  return result;
}

bool NPObjectProxy::NPInvoke(NPObject *obj,
                             NPIdentifier name,
                             const NPVariant *args,
                             uint32_t arg_count,
                             NPVariant *result) {
  return NPInvokePrivate(0, obj, false, name, args, arg_count, result);
}

bool NPObjectProxy::NPInvokeDefault(NPObject *npobj,
                                    const NPVariant *args,
                                    uint32_t arg_count,
                                    NPVariant *result) {
  return NPInvokePrivate(0, npobj, true, 0, args, arg_count, result);
}

bool NPObjectProxy::NPInvokePrivate(NPP npp,
                                    NPObject *obj,
                                    bool is_default,
                                    NPIdentifier name,
                                    const NPVariant *args,
                                    uint32_t arg_count,
                                    NPVariant *np_result) {
  if (obj == NULL)
    return false;

  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    if (is_default) {
      return obj->_class->invokeDefault(obj, args, arg_count, np_result);
    } else {
      return obj->_class->invoke(obj, name, args, arg_count, np_result);
    }
  }

  bool result = false;
  int render_view_id = proxy->render_view_id_;
  NPIdentifier_Param name_param;
  if (is_default) {
    // The data won't actually get used, but set it so we don't send random
    // data.
    name_param.identifier = NULL;
  } else {
    CreateNPIdentifierParam(name, &name_param);
  }

  // Note: This instance can get destroyed in the context of
  // Send so addref the channel in this scope.
  scoped_refptr<NPChannelBase> channel_copy = proxy->channel_;
  std::vector<NPVariant_Param> args_param;
  for (unsigned int i = 0; i < arg_count; ++i) {
    NPVariant_Param param;
    CreateNPVariantParam(args[i],
                         channel_copy.get(),
                         &param,
                         false,
                         render_view_id,
                         proxy->page_url_);
    args_param.push_back(param);
  }

  NPVariant_Param param_result;
  NPObjectMsg_Invoke* msg = new NPObjectMsg_Invoke(
      proxy->route_id_, is_default, name_param, args_param, &param_result,
      &result);

  // If we're in the plugin process and this invoke leads to a dialog box, the
  // plugin will hang the window hierarchy unless we pump the window message
  // queue while waiting for a reply.  We need to do this to simulate what
  // happens when everything runs in-process (while calling MessageBox window
  // messages are pumped).
  if (IsPluginProcess() && proxy->channel()) {
    msg->set_pump_messages_event(
        proxy->channel()->GetModalDialogEvent(render_view_id));
  }

  GURL page_url = proxy->page_url_;
  proxy->Send(msg);

  // Send may delete proxy.
  proxy = NULL;

  if (!result)
    return false;

  CreateNPVariant(
      param_result, channel_copy.get(), np_result, render_view_id, page_url);
  return true;
}

bool NPObjectProxy::NPHasProperty(NPObject *obj,
                                  NPIdentifier name) {
  if (obj == NULL)
    return false;

  bool result = false;
  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    return obj->_class->hasProperty(obj, name);
  }

  NPIdentifier_Param name_param;
  CreateNPIdentifierParam(name, &name_param);

  NPVariant_Param param;
  proxy->Send(new NPObjectMsg_HasProperty(
      proxy->route_id(), name_param, &result));

  // Send may delete proxy.
  proxy = NULL;

  return result;
}

bool NPObjectProxy::NPGetProperty(NPObject *obj,
                                  NPIdentifier name,
                                  NPVariant *np_result) {
  // Please refer to http://code.google.com/p/chromium/issues/detail?id=2556,
  // which was a crash in the XStandard plugin during plugin shutdown. The
  // crash occured because the plugin requests the plugin script object,
  // which fails. The plugin does not check the result of the operation and
  // invokes NPN_GetProperty on a NULL object which lead to the crash. If
  // we observe similar crashes in other methods in the future, these null
  // checks may have to be replicated in the other methods in this class.
  if (obj == NULL)
    return false;

  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    return obj->_class->getProperty(obj, name, np_result);
  }

  bool result = false;
  int render_view_id = proxy->render_view_id_;
  NPIdentifier_Param name_param;
  CreateNPIdentifierParam(name, &name_param);

  NPVariant_Param param;
  scoped_refptr<NPChannelBase> channel(proxy->channel_);

  GURL page_url = proxy->page_url_;
  proxy->Send(new NPObjectMsg_GetProperty(
      proxy->route_id(), name_param, &param, &result));
  // Send may delete proxy.
  proxy = NULL;
  if (!result)
    return false;

  CreateNPVariant(
      param, channel.get(), np_result, render_view_id, page_url);

  return true;
}

bool NPObjectProxy::NPSetProperty(NPObject *obj,
                                  NPIdentifier name,
                                  const NPVariant *value) {
  if (obj == NULL)
    return false;

  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    return obj->_class->setProperty(obj, name, value);
  }

  bool result = false;
  int render_view_id = proxy->render_view_id_;
  NPIdentifier_Param name_param;
  CreateNPIdentifierParam(name, &name_param);

  NPVariant_Param value_param;
  CreateNPVariantParam(
      *value, proxy->channel(), &value_param, false, render_view_id,
      proxy->page_url_);

  proxy->Send(new NPObjectMsg_SetProperty(
      proxy->route_id(), name_param, value_param, &result));
  // Send may delete proxy.
  proxy = NULL;

  return result;
}

bool NPObjectProxy::NPRemoveProperty(NPObject *obj,
                                     NPIdentifier name) {
  if (obj == NULL)
    return false;

  bool result = false;
  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    return obj->_class->removeProperty(obj, name);
  }

  NPIdentifier_Param name_param;
  CreateNPIdentifierParam(name, &name_param);

  NPVariant_Param param;
  proxy->Send(new NPObjectMsg_RemoveProperty(
      proxy->route_id(), name_param, &result));
  // Send may delete proxy.
  proxy = NULL;

  return result;
}

void NPObjectProxy::NPPInvalidate(NPObject *obj) {
  if (obj == NULL)
    return;

  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    obj->_class->invalidate(obj);
    return;
  }

  proxy->Send(new NPObjectMsg_Invalidate(proxy->route_id()));
  // Send may delete proxy.
  proxy = NULL;
}

bool NPObjectProxy::NPNEnumerate(NPObject *obj,
                                 NPIdentifier **value,
                                 uint32_t *count) {
  if (obj == NULL)
    return false;

  bool result = false;
  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) {
      return obj->_class->enumerate(obj, value, count);
    } else {
      return false;
    }
  }

  std::vector<NPIdentifier_Param> value_param;
  proxy->Send(new NPObjectMsg_Enumeration(
      proxy->route_id(), &value_param, &result));
  // Send may delete proxy.
  proxy = NULL;

  if (!result)
    return false;

  *count = static_cast<unsigned int>(value_param.size());
  *value = static_cast<NPIdentifier *>(malloc(sizeof(NPIdentifier) * *count));
  for (unsigned int i = 0; i < *count; ++i)
    (*value)[i] = CreateNPIdentifier(value_param[i]);

  return true;
}

bool NPObjectProxy::NPNConstruct(NPObject *obj,
                                 const NPVariant *args,
                                 uint32_t arg_count,
                                 NPVariant *np_result) {
  if (obj == NULL)
    return false;

  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) {
      return obj->_class->construct(obj, args, arg_count, np_result);
    } else {
      return false;
    }
  }

  bool result = false;
  int render_view_id = proxy->render_view_id_;

  // Note: This instance can get destroyed in the context of
  // Send so addref the channel in this scope.
  scoped_refptr<NPChannelBase> channel_copy = proxy->channel_;
  std::vector<NPVariant_Param> args_param;
  for (unsigned int i = 0; i < arg_count; ++i) {
    NPVariant_Param param;
    CreateNPVariantParam(args[i],
                         channel_copy.get(),
                         &param,
                         false,
                         render_view_id,
                         proxy->page_url_);
    args_param.push_back(param);
  }

  NPVariant_Param param_result;
  NPObjectMsg_Construct* msg = new NPObjectMsg_Construct(
      proxy->route_id_, args_param, &param_result, &result);

  // See comment in NPObjectProxy::NPInvokePrivate.
  if (IsPluginProcess() && proxy->channel()) {
    msg->set_pump_messages_event(
        proxy->channel()->GetModalDialogEvent(proxy->render_view_id_));
  }

  GURL page_url = proxy->page_url_;
  proxy->Send(msg);

  // Send may delete proxy.
  proxy = NULL;

  if (!result)
    return false;

  CreateNPVariant(
      param_result, channel_copy.get(), np_result, render_view_id, page_url);
  return true;
}

bool NPObjectProxy::NPNEvaluate(NPP npp,
                                NPObject *obj,
                                NPString *script,
                                NPVariant *result_var) {
  NPObjectProxy* proxy = GetProxy(obj);
  if (!proxy) {
    return false;
  }

  bool result = false;
  int render_view_id = proxy->render_view_id_;
  bool popups_allowed = false;

#if defined(ENABLE_PLUGINS)
  if (npp) {
    PluginInstance* plugin_instance =
        reinterpret_cast<PluginInstance*>(npp->ndata);
    if (plugin_instance)
      popups_allowed = plugin_instance->popups_allowed();
  }
#endif

  NPVariant_Param result_param;
  std::string script_str = std::string(
      script->UTF8Characters, script->UTF8Length);

  NPObjectMsg_Evaluate* msg = new NPObjectMsg_Evaluate(proxy->route_id(),
                                                       script_str,
                                                       popups_allowed,
                                                       &result_param,
                                                       &result);

  // See comment in NPObjectProxy::NPInvokePrivate.
  if (IsPluginProcess() && proxy->channel()) {
    msg->set_pump_messages_event(
        proxy->channel()->GetModalDialogEvent(render_view_id));
  }
  scoped_refptr<NPChannelBase> channel(proxy->channel_);

  GURL page_url = proxy->page_url_;
  proxy->Send(msg);
  // Send may delete proxy.
  proxy = NULL;
  if (!result)
    return false;

  CreateNPVariant(
      result_param, channel.get(), result_var, render_view_id, page_url);
  return true;
}

}  // namespace content

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