diff -r 000000000000 -r 4f2f89ce4247 WebKit/mac/Plugins/Hosted/ProxyInstance.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebKit/mac/Plugins/Hosted/ProxyInstance.mm Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,458 @@ +/* + * 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) + +#import "ProxyInstance.h" + +#import "NetscapePluginHostProxy.h" +#import "ProxyRuntimeObject.h" +#import +#import +#import +#import +#import +#import + +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 MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; +}; + +MethodList ProxyClass::methodsNamed(const Identifier& identifier, Instance* instance) const +{ + return static_cast(instance)->methodsNamed(identifier); +} + +Field* ProxyClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + return static_cast(instance)->fieldNamed(identifier); +} + +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(instance)->fieldValue(exec, this); +} + +void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const +{ + static_cast(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, 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 new (exec) ProxyRuntimeObject(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 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 reply = waitForReply(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: + ProxyRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list) + : RuntimeMethod(exec, globalObject, name, list) + { + } + + virtual const ClassInfo* classInfo() const { return &s_info; } + + static const ClassInfo s_info; +}; + +const ClassInfo ProxyRuntimeMethod::s_info = { "ProxyRuntimeMethod", &RuntimeMethod::s_info, 0, 0 }; + +JSValue ProxyInstance::getMethod(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + MethodList methodList = getClass()->methodsNamed(propertyName, this); + return new (exec) ProxyRuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +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.")); + + const MethodList& methodList = *runtimeMethod->methods(); + + ASSERT(methodList.size() == 1); + + ProxyMethod* method = static_cast(methodList[0]); + + 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 reply = waitForReply(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 reply = waitForReply(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* exec) const +{ + // FIXME: Implement something sensible. + return jsNumber(exec, 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 reply = waitForReply(requestID); + NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); + if (!reply.get() || !reply->m_returnValue) + return; + + RetainPtr array = [NSPropertyListSerialization propertyListFromData:(NSData *)reply->m_result.get() + mutabilityOption:NSPropertyListImmutable + format:0 + errorDescription:0]; + + for (NSNumber *number in array.get()) { + IdentifierRep* identifier = reinterpret_cast([number longLongValue]); + if (!IdentifierRep::isValid(identifier)) + continue; + + if (identifier->isString()) { + const char* str = identifier->string(); + nameArray.add(Identifier(JSDOMWindow::commonJSGlobalData(), stringToUString(String::fromUTF8WithLatin1Fallback(str, strlen(str))))); + } else + nameArray.add(Identifier::from(exec, identifier->number())); + } +} + +MethodList ProxyInstance::methodsNamed(const Identifier& identifier) +{ + if (!m_instanceProxy) + return MethodList(); + + // If we already have an entry in the map, use it. + MethodMap::iterator existingMapEntry = m_methods.find(identifier.ustring().rep()); + if (existingMapEntry != m_methods.end()) { + MethodList methodList; + if (existingMapEntry->second) + methodList.append(existingMapEntry->second); + return methodList; + } + + uint64_t methodName = reinterpret_cast(_NPN_GetStringIdentifier(identifier.ascii())); + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectHasMethod(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID, methodName) != KERN_SUCCESS) + return MethodList(); + + auto_ptr reply = waitForReply(requestID); + if (!reply.get()) + return MethodList(); + + if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods()) + return MethodList(); + + // Add a new entry to the map unless an entry was added while we were in waitForReply. + pair mapAddResult = m_methods.add(identifier.ustring().rep(), 0); + if (mapAddResult.second && reply->m_result) + mapAddResult.first->second = new ProxyMethod(methodName); + + MethodList methodList; + if (mapAddResult.first->second) + methodList.append(mapAddResult.first->second); + return methodList; +} + +Field* ProxyInstance::fieldNamed(const Identifier& identifier) +{ + if (!m_instanceProxy) + return 0; + + // If we already have an entry in the map, use it. + FieldMap::iterator existingMapEntry = m_fields.find(identifier.ustring().rep()); + if (existingMapEntry != m_fields.end()) + return existingMapEntry->second; + + uint64_t propertyName = reinterpret_cast(_NPN_GetStringIdentifier(identifier.ascii())); + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectHasProperty(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID, propertyName) != KERN_SUCCESS) + return 0; + + auto_ptr reply = waitForReply(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. + pair mapAddResult = m_fields.add(identifier.ustring().rep(), 0); + if (mapAddResult.second && reply->m_result) + mapAddResult.first->second = new ProxyField(propertyName); + return mapAddResult.first->second; +} + +JSC::JSValue ProxyInstance::fieldValue(ExecState* exec, const Field* field) const +{ + if (!m_instanceProxy) + return jsUndefined(); + + uint64_t serverIdentifier = static_cast(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 reply = waitForReply(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(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(valueData), valueLength); + if (m_instanceProxy) + m_instanceProxy->releaseLocalObject(value); + if (kr != KERN_SUCCESS) + return; + + auto_ptr reply = waitForReply(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) +