blob: 1e5ae6608aadc41f2fe404f8e23b805f31eaa910 [file] [log] [blame]
/*
* Copyright (C) 2008, 2009, 2010 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.
*/
#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
#import "ProxyInstance.h"
#import "NetscapePluginHostProxy.h"
#import "ProxyRuntimeObject.h"
#import <WebCore/IdentifierRep.h>
#import <WebCore/JSDOMWindow.h>
#import <WebCore/npruntime_impl.h>
#import <WebCore/runtime_method.h>
#import <runtime/Error.h>
#import <runtime/FunctionPrototype.h>
#import <runtime/PropertyNameArray.h>
extern "C" {
#import "WebKitPluginHost.h"
}
using namespace JSC;
using namespace JSC::Bindings;
using namespace std;
using namespace WebCore;
namespace WebKit {
class ProxyClass : public JSC::Bindings::Class {
private:
virtual Method* methodNamed(PropertyName, Instance*) const;
virtual Field* fieldNamed(PropertyName, Instance*) const;
};
Method* ProxyClass::methodNamed(PropertyName propertyName, Instance* instance) const
{
return static_cast<ProxyInstance*>(instance)->methodNamed(propertyName);
}
Field* ProxyClass::fieldNamed(PropertyName propertyName, Instance* instance) const
{
return static_cast<ProxyInstance*>(instance)->fieldNamed(propertyName);
}
static ProxyClass* proxyClass()
{
DEFINE_STATIC_LOCAL(ProxyClass, proxyClass, ());
return &proxyClass;
}
class ProxyField : public JSC::Bindings::Field {
public:
ProxyField(uint64_t serverIdentifier)
: m_serverIdentifier(serverIdentifier)
{
}
uint64_t serverIdentifier() const { return m_serverIdentifier; }
private:
virtual JSValue valueFromInstance(ExecState*, const Instance*) const;
virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const;
uint64_t m_serverIdentifier;
};
JSValue ProxyField::valueFromInstance(ExecState* exec, const Instance* instance) const
{
return static_cast<const ProxyInstance*>(instance)->fieldValue(exec, this);
}
void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const
{
static_cast<const ProxyInstance*>(instance)->setFieldValue(exec, this, value);
}
class ProxyMethod : public JSC::Bindings::Method {
public:
ProxyMethod(uint64_t serverIdentifier)
: m_serverIdentifier(serverIdentifier)
{
}
uint64_t serverIdentifier() const { return m_serverIdentifier; }
private:
virtual int numParameters() const { return 0; }
uint64_t m_serverIdentifier;
};
ProxyInstance::ProxyInstance(PassRefPtr<RootObject> rootObject, NetscapePluginInstanceProxy* instanceProxy, uint32_t objectID)
: Instance(rootObject)
, m_instanceProxy(instanceProxy)
, m_objectID(objectID)
{
m_instanceProxy->addInstance(this);
}
ProxyInstance::~ProxyInstance()
{
deleteAllValues(m_fields);
deleteAllValues(m_methods);
if (!m_instanceProxy)
return;
m_instanceProxy->removeInstance(this);
invalidate();
}
RuntimeObject* ProxyInstance::newRuntimeObject(ExecState* exec)
{
return ProxyRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
}
JSC::Bindings::Class* ProxyInstance::getClass() const
{
return proxyClass();
}
JSValue ProxyInstance::invoke(JSC::ExecState* exec, InvokeType type, uint64_t identifier, const ArgList& args)
{
if (!m_instanceProxy)
return jsUndefined();
RetainPtr<NSData*> arguments(m_instanceProxy->marshalValues(exec, args));
uint32_t requestID = m_instanceProxy->nextRequestID();
for (unsigned i = 0; i < args.size(); i++)
m_instanceProxy->retainLocalObject(args.at(i));
if (_WKPHNPObjectInvoke(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID,
type, identifier, (char*)[arguments.get() bytes], [arguments.get() length]) != KERN_SUCCESS) {
if (m_instanceProxy) {
for (unsigned i = 0; i < args.size(); i++)
m_instanceProxy->releaseLocalObject(args.at(i));
}
return jsUndefined();
}
auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
if (m_instanceProxy) {
for (unsigned i = 0; i < args.size(); i++)
m_instanceProxy->releaseLocalObject(args.at(i));
}
if (!reply.get() || !reply->m_returnValue)
return jsUndefined();
return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
}
class ProxyRuntimeMethod : public RuntimeMethod {
public:
typedef RuntimeMethod Base;
static ProxyRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const String& name, Bindings::Method* method)
{
// FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
// exec-globalData() is also likely wrong.
Structure* domStructure = deprecatedGetDOMStructure<ProxyRuntimeMethod>(exec);
ProxyRuntimeMethod* runtimeMethod = new (allocateCell<ProxyRuntimeMethod>(*exec->heap())) ProxyRuntimeMethod(globalObject, domStructure, method);
runtimeMethod->finishCreation(exec->globalData(), name);
return runtimeMethod;
}
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
}
static const ClassInfo s_info;
private:
ProxyRuntimeMethod(JSGlobalObject* globalObject, Structure* structure, Bindings::Method* method)
: RuntimeMethod(globalObject, structure, method)
{
}
void finishCreation(JSGlobalData& globalData, const String& name)
{
Base::finishCreation(globalData, name);
ASSERT(inherits(&s_info));
}
};
const ClassInfo ProxyRuntimeMethod::s_info = { "ProxyRuntimeMethod", &RuntimeMethod::s_info, 0, 0, CREATE_METHOD_TABLE(ProxyRuntimeMethod) };
JSValue ProxyInstance::getMethod(JSC::ExecState* exec, PropertyName propertyName)
{
Method* method = getClass()->methodNamed(propertyName, this);
return ProxyRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName.publicName(), method);
}
JSValue ProxyInstance::invokeMethod(ExecState* exec, JSC::RuntimeMethod* runtimeMethod)
{
if (!asObject(runtimeMethod)->inherits(&ProxyRuntimeMethod::s_info))
return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object."));
ProxyMethod* method = static_cast<ProxyMethod*>(runtimeMethod->method());
ASSERT(method);
return invoke(exec, Invoke, method->serverIdentifier(), ArgList(exec));
}
bool ProxyInstance::supportsInvokeDefaultMethod() const
{
if (!m_instanceProxy)
return false;
uint32_t requestID = m_instanceProxy->nextRequestID();
if (_WKPHNPObjectHasInvokeDefaultMethod(m_instanceProxy->hostProxy()->port(),
m_instanceProxy->pluginID(), requestID,
m_objectID) != KERN_SUCCESS)
return false;
auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
if (reply.get() && reply->m_result)
return true;
return false;
}
JSValue ProxyInstance::invokeDefaultMethod(ExecState* exec)
{
return invoke(exec, InvokeDefault, 0, ArgList(exec));
}
bool ProxyInstance::supportsConstruct() const
{
if (!m_instanceProxy)
return false;
uint32_t requestID = m_instanceProxy->nextRequestID();
if (_WKPHNPObjectHasConstructMethod(m_instanceProxy->hostProxy()->port(),
m_instanceProxy->pluginID(), requestID,
m_objectID) != KERN_SUCCESS)
return false;
auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
if (reply.get() && reply->m_result)
return true;
return false;
}
JSValue ProxyInstance::invokeConstruct(ExecState* exec, const ArgList& args)
{
return invoke(exec, Construct, 0, args);
}
JSValue ProxyInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
{
if (hint == PreferString)
return stringValue(exec);
if (hint == PreferNumber)
return numberValue(exec);
return valueOf(exec);
}
JSValue ProxyInstance::stringValue(ExecState* exec) const
{
// FIXME: Implement something sensible.
return jsEmptyString(exec);
}
JSValue ProxyInstance::numberValue(ExecState*) const
{
// FIXME: Implement something sensible.
return jsNumber(0);
}
JSValue ProxyInstance::booleanValue() const
{
// FIXME: Implement something sensible.
return jsBoolean(false);
}
JSValue ProxyInstance::valueOf(ExecState* exec) const
{
return stringValue(exec);
}
void ProxyInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
{
if (!m_instanceProxy)
return;
uint32_t requestID = m_instanceProxy->nextRequestID();
if (_WKPHNPObjectEnumerate(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID) != KERN_SUCCESS)
return;
auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
if (!reply.get() || !reply->m_returnValue)
return;
RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:(NSData *)reply->m_result.get()
mutabilityOption:NSPropertyListImmutable
format:0
errorDescription:0];
for (NSNumber *number in array.get()) {
IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>([number longLongValue]);
if (!IdentifierRep::isValid(identifier))
continue;
if (identifier->isString()) {
const char* str = identifier->string();
nameArray.add(Identifier(JSDOMWindow::commonJSGlobalData(), String::fromUTF8WithLatin1Fallback(str, strlen(str))));
} else
nameArray.add(Identifier::from(exec, identifier->number()));
}
}
Method* ProxyInstance::methodNamed(PropertyName propertyName)
{
String name(propertyName.publicName());
if (name.isNull())
return 0;
if (!m_instanceProxy)
return 0;
// If we already have an entry in the map, use it.
MethodMap::iterator existingMapEntry = m_methods.find(name.impl());
if (existingMapEntry != m_methods.end()) {
if (existingMapEntry->value)
return existingMapEntry->value;
}
uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(name.ascii().data()));
uint32_t requestID = m_instanceProxy->nextRequestID();
if (_WKPHNPObjectHasMethod(m_instanceProxy->hostProxy()->port(),
m_instanceProxy->pluginID(), requestID,
m_objectID, methodName) != KERN_SUCCESS)
return 0;
auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
if (!reply.get())
return 0;
if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
return 0;
// Add a new entry to the map unless an entry was added while we were in waitForReply.
MethodMap::AddResult mapAddResult = m_methods.add(name.impl(), 0);
if (mapAddResult.isNewEntry && reply->m_result)
mapAddResult.iterator->value = new ProxyMethod(methodName);
return mapAddResult.iterator->value;
}
Field* ProxyInstance::fieldNamed(PropertyName propertyName)
{
String name(propertyName.publicName());
if (name.isNull())
return 0;
if (!m_instanceProxy)
return 0;
// If we already have an entry in the map, use it.
FieldMap::iterator existingMapEntry = m_fields.find(name.impl());
if (existingMapEntry != m_fields.end())
return existingMapEntry->value;
uint64_t identifier = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(name.ascii().data()));
uint32_t requestID = m_instanceProxy->nextRequestID();
if (_WKPHNPObjectHasProperty(m_instanceProxy->hostProxy()->port(),
m_instanceProxy->pluginID(), requestID,
m_objectID, identifier) != KERN_SUCCESS)
return 0;
auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
if (!reply.get())
return 0;
if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
return 0;
// Add a new entry to the map unless an entry was added while we were in waitForReply.
FieldMap::AddResult mapAddResult = m_fields.add(name.impl(), 0);
if (mapAddResult.isNewEntry && reply->m_result)
mapAddResult.iterator->value = new ProxyField(identifier);
return mapAddResult.iterator->value;
}
JSC::JSValue ProxyInstance::fieldValue(ExecState* exec, const Field* field) const
{
if (!m_instanceProxy)
return jsUndefined();
uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
uint32_t requestID = m_instanceProxy->nextRequestID();
if (_WKPHNPObjectGetProperty(m_instanceProxy->hostProxy()->port(),
m_instanceProxy->pluginID(), requestID,
m_objectID, serverIdentifier) != KERN_SUCCESS)
return jsUndefined();
auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
if (!reply.get() || !reply->m_returnValue)
return jsUndefined();
return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
}
void ProxyInstance::setFieldValue(ExecState* exec, const Field* field, JSValue value) const
{
if (!m_instanceProxy)
return;
uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
uint32_t requestID = m_instanceProxy->nextRequestID();
data_t valueData;
mach_msg_type_number_t valueLength;
m_instanceProxy->marshalValue(exec, value, valueData, valueLength);
m_instanceProxy->retainLocalObject(value);
kern_return_t kr = _WKPHNPObjectSetProperty(m_instanceProxy->hostProxy()->port(),
m_instanceProxy->pluginID(), requestID,
m_objectID, serverIdentifier, valueData, valueLength);
mig_deallocate(reinterpret_cast<vm_address_t>(valueData), valueLength);
if (m_instanceProxy)
m_instanceProxy->releaseLocalObject(value);
if (kr != KERN_SUCCESS)
return;
auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
}
void ProxyInstance::invalidate()
{
ASSERT(m_instanceProxy);
if (NetscapePluginHostProxy* hostProxy = m_instanceProxy->hostProxy())
_WKPHNPObjectRelease(hostProxy->port(),
m_instanceProxy->pluginID(), m_objectID);
m_instanceProxy = 0;
}
} // namespace WebKit
#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)