/* * 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 #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) // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object // exec-globalData() is also likely wrong. : RuntimeMethod(exec, globalObject, deprecatedGetDOMStructure(exec), name, list) { ASSERT(inherits(&s_info)); } static PassRefPtr createStructure(JSGlobalData& globalData, JSValue prototype) { return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &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*) 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 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.impl()); 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().data())); 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.impl(), 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.impl()); if (existingMapEntry != m_fields.end()) return existingMapEntry->second; uint64_t propertyName = reinterpret_cast(_NPN_GetStringIdentifier(identifier.ascii().data())); 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.impl(), 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) && ENABLE(NETSCAPE_PLUGIN_API)