| Index: chrome_frame/np_event_listener.cc
|
| ===================================================================
|
| --- chrome_frame/np_event_listener.cc (revision 0)
|
| +++ chrome_frame/np_event_listener.cc (revision 0)
|
| @@ -0,0 +1,367 @@
|
| +// Copyright (c) 2009 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 "chrome_frame/np_event_listener.h"
|
| +
|
| +#include "base/string_util.h"
|
| +
|
| +#include "third_party/xulrunner-sdk/win/include/string/nsEmbedString.h"
|
| +#include "third_party/xulrunner-sdk/win/include/dom/nsIDOMElement.h"
|
| +#include "third_party/xulrunner-sdk/win/include/dom/nsIDOMEventTarget.h"
|
| +#include "third_party/xulrunner-sdk/win/include/dom/nsIDOMEvent.h"
|
| +
|
| +#include "chrome_frame/scoped_ns_ptr_win.h"
|
| +#include "chrome_frame/ns_associate_iid_win.h"
|
| +
|
| +ASSOCIATE_IID(NS_IDOMELEMENT_IID_STR, nsIDOMElement);
|
| +ASSOCIATE_IID(NS_IDOMNODE_IID_STR, nsIDOMNode);
|
| +ASSOCIATE_IID(NS_IDOMEVENTTARGET_IID_STR, nsIDOMEventTarget);
|
| +ASSOCIATE_IID(NS_IDOMEVENTLISTENER_IID_STR, nsIDOMEventListener);
|
| +
|
| +DomEventListener::DomEventListener(NpEventDelegate* delegate)
|
| + : NpEventListenerBase<DomEventListener>(delegate) {
|
| +}
|
| +
|
| +DomEventListener::~DomEventListener() {
|
| +}
|
| +
|
| +// We implement QueryInterface etc ourselves in order to avoid
|
| +// extra dependencies brought on by the NS_IMPL_* macros.
|
| +NS_IMETHODIMP DomEventListener::QueryInterface(REFNSIID iid, void** ptr) {
|
| + DCHECK(thread_id_ == ::GetCurrentThreadId());
|
| + nsresult res = NS_NOINTERFACE;
|
| +
|
| + if (memcmp(&iid, &__uuidof(nsIDOMEventListener), sizeof(nsIID)) == 0 ||
|
| + memcmp(&iid, &__uuidof(nsISupports), sizeof(nsIID)) == 0) {
|
| + *ptr = static_cast<nsIDOMEventListener*>(this);
|
| + AddRef();
|
| + res = NS_OK;
|
| + }
|
| +
|
| + return res;
|
| +}
|
| +
|
| +NS_IMETHODIMP DomEventListener::HandleEvent(nsIDOMEvent *event) {
|
| + DCHECK(thread_id_ == ::GetCurrentThreadId());
|
| + DCHECK(event);
|
| +
|
| + nsEmbedString tag;
|
| + event->GetType(tag);
|
| + delegate_->OnEvent(WideToUTF8(tag.get()).c_str());
|
| +
|
| + return NS_OK;
|
| +}
|
| +
|
| +bool DomEventListener::Subscribe(NPP instance,
|
| + const char* event_names[],
|
| + int event_name_count) {
|
| + DCHECK(event_names);
|
| + DCHECK(event_name_count > 0);
|
| +
|
| + ScopedNsPtr<nsIDOMElement> element;
|
| + bool ret = GetObjectElement(instance, element.Receive());
|
| + if (ret) {
|
| + ScopedNsPtr<nsIDOMEventTarget> target;
|
| + target.QueryFrom(element);
|
| + if (target) {
|
| + for (int i = 0; i < event_name_count && ret; ++i) {
|
| + nsEmbedString name(ASCIIToWide(event_names[i]).c_str());
|
| + // See NPObjectEventListener::Subscribe (below) for a note on why
|
| + // we set the useCapture parameter to PR_FALSE.
|
| + nsresult res = target->AddEventListener(name, this, PR_FALSE);
|
| + DCHECK(res == NS_OK) << "AddEventListener: " << event_names[i];
|
| + ret = NS_SUCCEEDED(res);
|
| + }
|
| + } else {
|
| + DLOG(ERROR) << "failed to get nsIDOMEventTarget";
|
| + ret = false;
|
| + }
|
| + }
|
| +
|
| + return ret;
|
| +}
|
| +
|
| +bool DomEventListener::Unsubscribe(NPP instance,
|
| + const char* event_names[],
|
| + int event_name_count) {
|
| + DCHECK(event_names);
|
| + DCHECK(event_name_count > 0);
|
| +
|
| + ScopedNsPtr<nsIDOMElement> element;
|
| + bool ret = GetObjectElement(instance, element.Receive());
|
| + if (ret) {
|
| + ScopedNsPtr<nsIDOMEventTarget> target;
|
| + target.QueryFrom(element);
|
| + if (target) {
|
| + for (int i = 0; i < event_name_count && ret; ++i) {
|
| + nsEmbedString name(ASCIIToWide(event_names[i]).c_str());
|
| + nsresult res = target->RemoveEventListener(name, this, PR_FALSE);
|
| + DCHECK(res == NS_OK) << "RemoveEventListener: " << event_names[i];
|
| + ret = NS_SUCCEEDED(res) && ret;
|
| + }
|
| + } else {
|
| + DLOG(ERROR) << "failed to get nsIDOMEventTarget";
|
| + ret = false;
|
| + }
|
| + }
|
| +
|
| + return ret;
|
| +}
|
| +
|
| +bool DomEventListener::GetObjectElement(NPP instance, nsIDOMElement** element) {
|
| + DCHECK(element);
|
| +
|
| + ScopedNsPtr<nsIDOMElement> elem;
|
| + // Fetching the dom element works in Firefox, but is not implemented
|
| + // in webkit.
|
| + npapi::GetValue(instance, NPNVDOMElement, elem.Receive());
|
| + if (!elem.get()) {
|
| + DLOG(INFO) << "Failed to get NPNVDOMElement";
|
| + return false;
|
| + }
|
| +
|
| + nsEmbedString tag;
|
| + nsresult res = elem->GetTagName(tag);
|
| + if (NS_SUCCEEDED(res)) {
|
| + if (LowerCaseEqualsASCII(tag.get(), tag.get() + tag.Length(), "embed")) {
|
| + ScopedNsPtr<nsIDOMNode> parent;
|
| + elem->GetParentNode(parent.Receive());
|
| + if (parent) {
|
| + elem.Release();
|
| + res = parent.QueryInterface(elem.Receive());
|
| + DCHECK(NS_SUCCEEDED(res));
|
| + }
|
| + }
|
| + } else {
|
| + NOTREACHED() << " GetTagName";
|
| + }
|
| +
|
| + *element = elem.Detach();
|
| +
|
| + return *element != NULL;
|
| +}
|
| +
|
| +///////////////////////////////////
|
| +// NPObjectEventListener
|
| +
|
| +NPObjectEventListener::NPObjectEventListener(NpEventDelegate* delegate)
|
| + : NpEventListenerBase<NPObjectEventListener>(delegate) {
|
| +}
|
| +
|
| +NPObjectEventListener::~NPObjectEventListener() {
|
| + DLOG_IF(ERROR, npo_.get() == NULL);
|
| +}
|
| +
|
| +NPObject* NPObjectEventListener::GetObjectElement(NPP instance) {
|
| + NPObject* object = NULL;
|
| + // We can't trust the return value from getvalue.
|
| + // In Opera, the return value can be false even though the correct
|
| + // object is returned.
|
| + npapi::GetValue(instance, NPNVPluginElementNPObject, &object);
|
| +
|
| + if (object) {
|
| + NPIdentifier* ids = GetCachedStringIds();
|
| + NPVariant var;
|
| + if (npapi::GetProperty(instance, object, ids[TAG_NAME], &var)) {
|
| + DCHECK(NPVARIANT_IS_STRING(var));
|
| + const NPString& np_tag = NPVARIANT_TO_STRING(var);
|
| + std::string tag(np_tag.UTF8Characters, np_tag.UTF8Length);
|
| + npapi::ReleaseVariantValue(&var);
|
| +
|
| + if (lstrcmpiA(tag.c_str(), "embed") == 0) {
|
| + // We've got the <embed> element but we really want
|
| + // the <object> element.
|
| + if (npapi::GetProperty(instance, object, ids[PARENT_ELEMENT], &var)) {
|
| + DCHECK(NPVARIANT_IS_OBJECT(var));
|
| + npapi::ReleaseObject(object);
|
| + object = NPVARIANT_TO_OBJECT(var);
|
| + }
|
| + } else {
|
| + DLOG(INFO) << __FUNCTION__ << " got " << tag;
|
| + }
|
| + } else {
|
| + DLOG(INFO) << __FUNCTION__ << " failed to get the element's tag";
|
| + }
|
| + } else {
|
| + DLOG(WARNING) << __FUNCTION__ << " failed to get NPNVPluginElementNPObject";
|
| + }
|
| +
|
| + return object;
|
| +}
|
| +
|
| +// Implementation of NpEventListener
|
| +bool NPObjectEventListener::Subscribe(NPP instance,
|
| + const char* event_names[],
|
| + int event_name_count) {
|
| + DCHECK(event_names);
|
| + DCHECK(event_name_count > 0);
|
| + DCHECK(npo_.get() == NULL);
|
| +
|
| + ScopedNpObject<> plugin_element(GetObjectElement(instance));
|
| + if (!plugin_element.get())
|
| + return false;
|
| +
|
| + // This object seems to be getting leaked :-(
|
| + bool ret = false;
|
| + npo_.Attach(reinterpret_cast<Npo*>(
|
| + npapi::CreateObject(instance, PluginClass())));
|
| +
|
| + if (!npo_.get()) {
|
| + NOTREACHED() << "createobject";
|
| + } else {
|
| + npo_->Initialize(this);
|
| + ret = true;
|
| +
|
| + NPIdentifier* ids = GetCachedStringIds();
|
| +
|
| + NPVariant args[3];
|
| + OBJECT_TO_NPVARIANT(npo_, args[1]);
|
| + // We don't want to set 'capture' (last parameter) to true.
|
| + // If we do, then in Opera, we'll simply not get callbacks unless
|
| + // the target <object> or <embed> element we're syncing with has its
|
| + // on[event] property assigned to some function handler. weird.
|
| + // Ideally though we'd like to set capture to true since we'd like to
|
| + // only be triggered for this particular object (and not for bubbling
|
| + // events, but alas it's not meant to be.
|
| + BOOLEAN_TO_NPVARIANT(false, args[2]);
|
| + for (int i = 0; i < event_name_count; ++i) {
|
| + ScopedNpVariant result;
|
| + STRINGZ_TO_NPVARIANT(event_names[i], args[0]);
|
| + ret = npapi::Invoke(instance, plugin_element, ids[ADD_EVENT_LISTENER],
|
| + args, arraysize(args), &result) && ret;
|
| + if (!ret) {
|
| + DLOG(WARNING) << __FUNCTION__ << " invoke failed for "
|
| + << event_names[i];
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return ret;
|
| +}
|
| +
|
| +bool NPObjectEventListener::Unsubscribe(NPP instance,
|
| + const char* event_names[],
|
| + int event_name_count) {
|
| + DCHECK(event_names);
|
| + DCHECK(event_name_count > 0);
|
| + DCHECK(npo_.get() != NULL);
|
| +
|
| + ScopedNpObject<> plugin_element(GetObjectElement(instance));
|
| + if (!plugin_element.get())
|
| + return false;
|
| +
|
| + NPIdentifier* ids = GetCachedStringIds();
|
| +
|
| + NPVariant args[3];
|
| + OBJECT_TO_NPVARIANT(npo_, args[1]);
|
| + BOOLEAN_TO_NPVARIANT(false, args[2]);
|
| + for (int i = 0; i < event_name_count; ++i) {
|
| + // TODO(tommi): look into why chrome isn't releasing the reference
|
| + // count here. As it stands the reference count doesn't go down
|
| + // and as a result, the NPO gets leaked.
|
| + ScopedNpVariant result;
|
| + STRINGZ_TO_NPVARIANT(event_names[i], args[0]);
|
| + bool ret = npapi::Invoke(instance, plugin_element,
|
| + ids[REMOVE_EVENT_LISTENER], args, arraysize(args), &result);
|
| + DLOG_IF(ERROR, !ret) << __FUNCTION__ << " invoke: " << ret;
|
| + }
|
| +
|
| + npo_.Free();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void NPObjectEventListener::HandleEvent(Npo* npo, NPObject* event) {
|
| + DCHECK(npo);
|
| + DCHECK(event);
|
| +
|
| + NPIdentifier* ids = GetCachedStringIds();
|
| + ScopedNpVariant result;
|
| + bool ret = npapi::GetProperty(npo->npp(), event, ids[TYPE], &result);
|
| + DCHECK(ret) << "getproperty(type)";
|
| + if (ret) {
|
| + DCHECK(NPVARIANT_IS_STRING(result));
|
| + // Opera doesn't zero terminate its utf8 strings.
|
| + const NPString& type = NPVARIANT_TO_STRING(result);
|
| + std::string zero_terminated(type.UTF8Characters, type.UTF8Length);
|
| + DLOG(INFO) << "handleEvent: " << zero_terminated;
|
| + delegate_->OnEvent(zero_terminated.c_str());
|
| + }
|
| +}
|
| +
|
| +NPClass* NPObjectEventListener::PluginClass() {
|
| + static NPClass _np_class = {
|
| + NP_CLASS_STRUCT_VERSION,
|
| + reinterpret_cast<NPAllocateFunctionPtr>(AllocateObject),
|
| + reinterpret_cast<NPDeallocateFunctionPtr>(DeallocateObject),
|
| + NULL, // invalidate
|
| + reinterpret_cast<NPHasMethodFunctionPtr>(HasMethod),
|
| + reinterpret_cast<NPInvokeFunctionPtr>(Invoke),
|
| + NULL, // InvokeDefault,
|
| + NULL, // HasProperty,
|
| + NULL, // GetProperty,
|
| + NULL, // SetProperty,
|
| + NULL // construct
|
| + };
|
| +
|
| + return &_np_class;
|
| +}
|
| +
|
| +bool NPObjectEventListener::HasMethod(NPObjectEventListener::Npo* npo,
|
| + NPIdentifier name) {
|
| + NPIdentifier* ids = GetCachedStringIds();
|
| + if (name == ids[HANDLE_EVENT])
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool NPObjectEventListener::Invoke(NPObjectEventListener::Npo* npo,
|
| + NPIdentifier name, const NPVariant* args,
|
| + uint32_t arg_count, NPVariant* result) {
|
| + NPIdentifier* ids = GetCachedStringIds();
|
| + if (name != ids[HANDLE_EVENT])
|
| + return false;
|
| +
|
| + if (arg_count != 1 || !NPVARIANT_IS_OBJECT(args[0])) {
|
| + NOTREACHED();
|
| + } else {
|
| + NPObject* ev = NPVARIANT_TO_OBJECT(args[0]);
|
| + npo->listener()->HandleEvent(npo, ev);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +NPObject* NPObjectEventListener::AllocateObject(NPP instance,
|
| + NPClass* class_name) {
|
| + return new Npo(instance);
|
| +}
|
| +
|
| +void NPObjectEventListener::DeallocateObject(NPObjectEventListener::Npo* npo) {
|
| + delete npo;
|
| +}
|
| +
|
| +NPIdentifier* NPObjectEventListener::GetCachedStringIds() {
|
| + static NPIdentifier _identifiers[IDENTIFIER_COUNT] = {0};
|
| + if (!_identifiers[0]) {
|
| + const NPUTF8* identifier_names[] = {
|
| + "handleEvent",
|
| + "type",
|
| + "addEventListener",
|
| + "removeEventListener",
|
| + "tagName",
|
| + "parentElement",
|
| + };
|
| + COMPILE_ASSERT(arraysize(identifier_names) == arraysize(_identifiers),
|
| + mismatched_array_size);
|
| + npapi::GetStringIdentifiers(identifier_names, IDENTIFIER_COUNT,
|
| + _identifiers);
|
| + for (int i = 0; i < IDENTIFIER_COUNT; ++i) {
|
| + DCHECK(_identifiers[i] != 0);
|
| + }
|
| + }
|
| + return _identifiers;
|
| +}
|
|
|