// Copyright (c) 2006-2008 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 "webkit/activex_shim/dispatch_object.h" #include #include #include #include "base/logging.h" #include "webkit/activex_shim/activex_util.h" #include "webkit/activex_shim/npp_impl.h" using std::string; using std::wstring; namespace activex_shim { // Used when the browser asks for scriptable object. static NPClass npclass = { 1, // NP_CLASS_STRUCT_VERSION, NPAllocate, NPDeallocate, NPInvalidate, NPHasMethod, NPInvoke, NPInvokeDefault, NPHasProperty, NPGetProperty, NPSetProperty, NPRemoveProperty }; DispatchObject::DispatchObject(DispatchObject* root) : npobject_(NULL), root_(root), deleting_spawned_children_(false) { } DispatchObject::~DispatchObject() { if (npobject_) { // We are gone, but the NPObject may still be there. So remove // the reference to myself to avoid future trouble. npobject_->dispatch_object = NULL; } } NPObject* DispatchObject::GetScriptableNPObject() { if (npobject_ == NULL) { npobject_ = static_cast(NPAllocate(&npclass)); } else { // If it is requesting the object again, we should just return the // object with increased reference count. g_browser->retainobject(npobject_); } return npobject_; } NPObject* DispatchObject::NPAllocate(NPClass* cls) { DispatchNPObject* obj = new DispatchNPObject(); obj->_class = cls; obj->referenceCount = 1; obj->dispatch_object = this; return obj; } void DispatchObject::NPInvalidate() { } void DispatchObject::OnDeallocateObject(DispatchNPObject* obj) { DCHECK_EQ(obj, npobject_); if (obj == npobject_) { // Just null our reference so that we won't accidentally access it // during destruction. npobject_ = NULL; if (NPObjectOwnsMe()) { delete this; } } } bool DispatchObject::NPHasMethod(NPIdentifier name) { wstring wname; if (!NPIdentifierToWString(name, &wname)) return false; return DispIsMethodOrProperty(GetDispatch(), wname.c_str(), true); } bool DispatchObject::NPHasProperty(NPIdentifier name) { wstring wname; if (!NPIdentifierToWString(name, &wname)) return false; return DispIsMethodOrProperty(GetDispatch(), wname.c_str(), false); // Here is another way. But the problem is it can not distiguish between // method and property. // DISPID dispid; // if (GetDispID(npobj->dispatch_object->GetDispatch(), wname.c_str(), &dispid)) // return true; } bool DispatchObject::NPInvoke(NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) { wstring wname; if (!NPIdentifierToWString(name, &wname)) return false; std::vector vars; ScopedVariant vtres; bool res = false; do { if (argCount > 0) { vars.resize(argCount); unsigned int i; for (i = 0; i < argCount; i++) { // Note that we need to reverse the order of arguments for // IDispatch::Invoke. if (!NPVariantToVariant(&args[argCount - i - 1], &vars[i])) break; } if (i < argCount) break; } if (!DispInvoke(GetDispatch(), wname.c_str(), argCount > 0 ? &vars[0] : NULL, argCount, &vtres)) break; if (!VariantToNPVariant(this, &vtres, result)) break; res = true; } while (false); return res; } bool DispatchObject::NPInvokeDefault(const NPVariant* args, uint32_t argCount, NPVariant* result) { return false; } bool DispatchObject::NPGetProperty(NPIdentifier name, NPVariant* variant) { wstring wname; if (!NPIdentifierToWString(name, &wname)) return false; ScopedVariant result; if (!DispInvoke(GetDispatch(), wname.c_str(), NULL, 0, &result)) return false; if (!VariantToNPVariant(this, &result, variant)) return false; return true; } bool DispatchObject::NPSetProperty(NPIdentifier name, const NPVariant* variant) { wstring wname; if (!NPIdentifierToWString(name, &wname)) return false; ScopedVariant rvalue; if (!NPVariantToVariant(variant, &rvalue)) return false; if (!DispSetProperty(GetDispatch(), wname.c_str(), rvalue)) return false; return true; } bool DispatchObject::NPRemoveProperty(NPIdentifier propertyName) { return false; } void DispatchObject::AddSpawned(DispatchObject* obj) { // I myself must be the root. DCHECK(root_ == NULL); spawned_children_.push_back(obj); } void DispatchObject::RemoveSpawned(DispatchObject* obj) { // This is to avoid problem when the root object is calling ReleaseSpawned to // delete all spawned children. if (deleting_spawned_children_) return; DCHECK(root_ == NULL); SpawnedChildrenList::iterator it = std::find(spawned_children_.begin(), spawned_children_.end(), obj); if (it == spawned_children_.end()) { DCHECK(false); return; } spawned_children_.erase(it); } void DispatchObject::ReleaseSpawned() { DCHECK(root_ == NULL); deleting_spawned_children_ = true; for (SpawnedChildrenList::iterator it = spawned_children_.begin(); it != spawned_children_.end(); ++it) delete *it; deleting_spawned_children_ = false; spawned_children_.clear(); } SpawnedDispatchObject::SpawnedDispatchObject(IDispatch* dispatch, DispatchObject* root) : DispatchObject(root), dispatch_(dispatch) { if (dispatch) dispatch->AddRef(); DCHECK(root != NULL); root->AddSpawned(this); } SpawnedDispatchObject::~SpawnedDispatchObject() { if (dispatch_) dispatch_->Release(); DCHECK(root_ != NULL); root_->RemoveSpawned(this); } /////////////////////////////////////////////////////////////////////////////// // Scripting object functions implementation. NPObject* NPAllocate(NPP npp, NPClass* theClass) { DispatchObject* dispatch_object = static_cast(npp->pdata); return dispatch_object->NPAllocate(theClass); } void NPDeallocate(NPObject* obj) { DispatchNPObject* npobj = static_cast(obj); // The dispatch_object could be well gone before the NPObject is released. if (npobj->dispatch_object != NULL) { npobj->dispatch_object->OnDeallocateObject(npobj); } delete npobj; } void NPInvalidate(NPObject* obj) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return; npobj->dispatch_object->NPInvalidate(); } bool NPHasMethod(NPObject* obj, NPIdentifier name) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return false; return npobj->dispatch_object->NPHasMethod(name); } bool NPInvoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return false; return npobj->dispatch_object->NPInvoke(name, args, argCount, result); } bool NPInvokeDefault(NPObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return false; return npobj->dispatch_object->NPInvokeDefault(args, argCount, result); } bool NPHasProperty(NPObject* obj, NPIdentifier name) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return false; return npobj->dispatch_object->NPHasProperty(name); } bool NPGetProperty(NPObject* obj, NPIdentifier name, NPVariant* variant) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return false; return npobj->dispatch_object->NPGetProperty(name, variant); } bool NPSetProperty(NPObject* obj, NPIdentifier name, const NPVariant* variant) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return false; return npobj->dispatch_object->NPSetProperty(name, variant); } bool NPRemoveProperty(NPObject* obj, NPIdentifier name) { DispatchNPObject* npobj = static_cast(obj); if (npobj->dispatch_object == NULL) return false; return npobj->dispatch_object->NPRemoveProperty(name); } } // namespace activex_shim