Home | History | Annotate | Download | only in Hosted
      1 /*
      2  * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #if USE(PLUGIN_HOST_PROCESS)
     27 
     28 #import "ProxyInstance.h"
     29 
     30 #import "NetscapePluginHostProxy.h"
     31 #import <WebCore/IdentifierRep.h>
     32 #import <WebCore/JSDOMWindow.h>
     33 #import <WebCore/npruntime_impl.h>
     34 #import <runtime/PropertyNameArray.h>
     35 
     36 extern "C" {
     37 #import "WebKitPluginHost.h"
     38 }
     39 
     40 using namespace JSC;
     41 using namespace JSC::Bindings;
     42 using namespace std;
     43 using namespace WebCore;
     44 
     45 namespace WebKit {
     46 
     47 class ProxyClass : public JSC::Bindings::Class {
     48 private:
     49     virtual MethodList methodsNamed(const Identifier&, Instance*) const;
     50     virtual Field* fieldNamed(const Identifier&, Instance*) const;
     51 };
     52 
     53 MethodList ProxyClass::methodsNamed(const Identifier& identifier, Instance* instance) const
     54 {
     55     return static_cast<ProxyInstance*>(instance)->methodsNamed(identifier);
     56 }
     57 
     58 Field* ProxyClass::fieldNamed(const Identifier& identifier, Instance* instance) const
     59 {
     60     return static_cast<ProxyInstance*>(instance)->fieldNamed(identifier);
     61 }
     62 
     63 static ProxyClass* proxyClass()
     64 {
     65     DEFINE_STATIC_LOCAL(ProxyClass, proxyClass, ());
     66     return &proxyClass;
     67 }
     68 
     69 class ProxyField : public JSC::Bindings::Field {
     70 public:
     71     ProxyField(uint64_t serverIdentifier)
     72         : m_serverIdentifier(serverIdentifier)
     73     {
     74     }
     75 
     76     uint64_t serverIdentifier() const { return m_serverIdentifier; }
     77 
     78 private:
     79     virtual JSValue valueFromInstance(ExecState*, const Instance*) const;
     80     virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const;
     81 
     82     uint64_t m_serverIdentifier;
     83 };
     84 
     85 JSValue ProxyField::valueFromInstance(ExecState* exec, const Instance* instance) const
     86 {
     87     return static_cast<const ProxyInstance*>(instance)->fieldValue(exec, this);
     88 }
     89 
     90 void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const
     91 {
     92     static_cast<const ProxyInstance*>(instance)->setFieldValue(exec, this, value);
     93 }
     94 
     95 class ProxyMethod : public JSC::Bindings::Method {
     96 public:
     97     ProxyMethod(uint64_t serverIdentifier)
     98         : m_serverIdentifier(serverIdentifier)
     99     {
    100     }
    101 
    102     uint64_t serverIdentifier() const { return m_serverIdentifier; }
    103 
    104 private:
    105     virtual int numParameters() const { return 0; }
    106 
    107     uint64_t m_serverIdentifier;
    108 };
    109 
    110 ProxyInstance::ProxyInstance(PassRefPtr<RootObject> rootObject, NetscapePluginInstanceProxy* instanceProxy, uint32_t objectID)
    111     : Instance(rootObject)
    112     , m_instanceProxy(instanceProxy)
    113     , m_objectID(objectID)
    114 {
    115     m_instanceProxy->addInstance(this);
    116 }
    117 
    118 ProxyInstance::~ProxyInstance()
    119 {
    120     deleteAllValues(m_fields);
    121     deleteAllValues(m_methods);
    122 
    123     if (!m_instanceProxy)
    124         return;
    125 
    126     m_instanceProxy->removeInstance(this);
    127 
    128     invalidate();
    129 }
    130 
    131 JSC::Bindings::Class *ProxyInstance::getClass() const
    132 {
    133     return proxyClass();
    134 }
    135 
    136 JSValue ProxyInstance::invoke(JSC::ExecState* exec, InvokeType type, uint64_t identifier, const JSC::ArgList& args)
    137 {
    138     if (!m_instanceProxy)
    139         return jsUndefined();
    140 
    141     RetainPtr<NSData*> arguments(m_instanceProxy->marshalValues(exec, args));
    142 
    143     uint32_t requestID = m_instanceProxy->nextRequestID();
    144 
    145     if (_WKPHNPObjectInvoke(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID,
    146                             type, identifier, (char*)[arguments.get() bytes], [arguments.get() length]) != KERN_SUCCESS)
    147         return jsUndefined();
    148 
    149     auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
    150     NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
    151     if (!reply.get() || !reply->m_returnValue)
    152         return jsUndefined();
    153 
    154     return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
    155 }
    156 
    157 JSValue ProxyInstance::invokeMethod(ExecState* exec, const MethodList& methodList, const ArgList& args)
    158 {
    159     ASSERT(methodList.size() == 1);
    160 
    161     ProxyMethod* method = static_cast<ProxyMethod*>(methodList[0]);
    162 
    163     return invoke(exec, Invoke, method->serverIdentifier(), args);
    164 }
    165 
    166 bool ProxyInstance::supportsInvokeDefaultMethod() const
    167 {
    168     if (!m_instanceProxy)
    169         return false;
    170 
    171     uint32_t requestID = m_instanceProxy->nextRequestID();
    172 
    173     if (_WKPHNPObjectHasInvokeDefaultMethod(m_instanceProxy->hostProxy()->port(),
    174                                             m_instanceProxy->pluginID(), requestID,
    175                                             m_objectID) != KERN_SUCCESS)
    176         return false;
    177 
    178     auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
    179     if (reply.get() && reply->m_result)
    180         return true;
    181 
    182     return false;
    183 }
    184 
    185 JSValue ProxyInstance::invokeDefaultMethod(ExecState* exec, const ArgList& args)
    186 {
    187     return invoke(exec, InvokeDefault, 0, args);
    188 }
    189 
    190 bool ProxyInstance::supportsConstruct() const
    191 {
    192     if (!m_instanceProxy)
    193         return false;
    194 
    195     uint32_t requestID = m_instanceProxy->nextRequestID();
    196 
    197     if (_WKPHNPObjectHasConstructMethod(m_instanceProxy->hostProxy()->port(),
    198                                         m_instanceProxy->pluginID(), requestID,
    199                                         m_objectID) != KERN_SUCCESS)
    200         return false;
    201 
    202     auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
    203     if (reply.get() && reply->m_result)
    204         return true;
    205 
    206     return false;
    207 }
    208 
    209 JSValue ProxyInstance::invokeConstruct(ExecState* exec, const ArgList& args)
    210 {
    211     return invoke(exec, Construct, 0, args);
    212 }
    213 
    214 JSValue ProxyInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
    215 {
    216     if (hint == PreferString)
    217         return stringValue(exec);
    218     if (hint == PreferNumber)
    219         return numberValue(exec);
    220     return valueOf(exec);
    221 }
    222 
    223 JSValue ProxyInstance::stringValue(ExecState* exec) const
    224 {
    225     // FIXME: Implement something sensible.
    226     return jsEmptyString(exec);
    227 }
    228 
    229 JSValue ProxyInstance::numberValue(ExecState* exec) const
    230 {
    231     // FIXME: Implement something sensible.
    232     return jsNumber(exec, 0);
    233 }
    234 
    235 JSValue ProxyInstance::booleanValue() const
    236 {
    237     // FIXME: Implement something sensible.
    238     return jsBoolean(false);
    239 }
    240 
    241 JSValue ProxyInstance::valueOf(ExecState* exec) const
    242 {
    243     return stringValue(exec);
    244 }
    245 
    246 void ProxyInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
    247 {
    248     if (!m_instanceProxy)
    249         return;
    250 
    251     uint32_t requestID = m_instanceProxy->nextRequestID();
    252 
    253     if (_WKPHNPObjectEnumerate(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID) != KERN_SUCCESS)
    254         return;
    255 
    256     auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
    257     NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
    258     if (!reply.get() || !reply->m_returnValue)
    259         return;
    260 
    261     RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:(NSData *)reply->m_result.get()
    262                                                                  mutabilityOption:NSPropertyListImmutable
    263                                                                            format:0
    264                                                                  errorDescription:0];
    265 
    266     for (NSNumber *number in array.get()) {
    267         IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>([number longLongValue]);
    268         if (!IdentifierRep::isValid(identifier))
    269             continue;
    270 
    271         if (identifier->isString()) {
    272             const char* str = identifier->string();
    273             nameArray.add(Identifier(JSDOMWindow::commonJSGlobalData(), String::fromUTF8WithLatin1Fallback(str, strlen(str))));
    274         } else
    275             nameArray.add(Identifier::from(exec, identifier->number()));
    276     }
    277 }
    278 
    279 MethodList ProxyInstance::methodsNamed(const Identifier& identifier)
    280 {
    281     if (!m_instanceProxy)
    282         return MethodList();
    283 
    284     // If we already have an entry in the map, use it.
    285     MethodMap::iterator existingMapEntry = m_methods.find(identifier.ustring().rep());
    286     if (existingMapEntry != m_methods.end()) {
    287         MethodList methodList;
    288         if (existingMapEntry->second)
    289             methodList.append(existingMapEntry->second);
    290         return methodList;
    291     }
    292 
    293     uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii()));
    294     uint32_t requestID = m_instanceProxy->nextRequestID();
    295 
    296     if (_WKPHNPObjectHasMethod(m_instanceProxy->hostProxy()->port(),
    297                                m_instanceProxy->pluginID(), requestID,
    298                                m_objectID, methodName) != KERN_SUCCESS)
    299         return MethodList();
    300 
    301     auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
    302     if (!reply.get())
    303         return MethodList();
    304 
    305     if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
    306         return MethodList();
    307 
    308     // Add a new entry to the map unless an entry was added while we were in waitForReply.
    309     pair<MethodMap::iterator, bool> mapAddResult = m_methods.add(identifier.ustring().rep(), 0);
    310     if (mapAddResult.second && reply->m_result)
    311         mapAddResult.first->second = new ProxyMethod(methodName);
    312 
    313     MethodList methodList;
    314     if (mapAddResult.first->second)
    315         methodList.append(mapAddResult.first->second);
    316     return methodList;
    317 }
    318 
    319 Field* ProxyInstance::fieldNamed(const Identifier& identifier)
    320 {
    321     if (!m_instanceProxy)
    322         return 0;
    323 
    324     // If we already have an entry in the map, use it.
    325     FieldMap::iterator existingMapEntry = m_fields.find(identifier.ustring().rep());
    326     if (existingMapEntry != m_fields.end())
    327         return existingMapEntry->second;
    328 
    329     uint64_t propertyName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii()));
    330     uint32_t requestID = m_instanceProxy->nextRequestID();
    331 
    332     if (_WKPHNPObjectHasProperty(m_instanceProxy->hostProxy()->port(),
    333                                  m_instanceProxy->pluginID(), requestID,
    334                                  m_objectID, propertyName) != KERN_SUCCESS)
    335         return 0;
    336 
    337     auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
    338     if (!reply.get())
    339         return 0;
    340 
    341     if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
    342         return 0;
    343 
    344     // Add a new entry to the map unless an entry was added while we were in waitForReply.
    345     pair<FieldMap::iterator, bool> mapAddResult = m_fields.add(identifier.ustring().rep(), 0);
    346     if (mapAddResult.second && reply->m_result)
    347         mapAddResult.first->second = new ProxyField(propertyName);
    348     return mapAddResult.first->second;
    349 }
    350 
    351 JSC::JSValue ProxyInstance::fieldValue(ExecState* exec, const Field* field) const
    352 {
    353     if (!m_instanceProxy)
    354         return jsUndefined();
    355 
    356     uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
    357     uint32_t requestID = m_instanceProxy->nextRequestID();
    358 
    359     if (_WKPHNPObjectGetProperty(m_instanceProxy->hostProxy()->port(),
    360                                  m_instanceProxy->pluginID(), requestID,
    361                                  m_objectID, serverIdentifier) != KERN_SUCCESS)
    362         return jsUndefined();
    363 
    364     auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
    365     NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
    366     if (!reply.get() || !reply->m_returnValue)
    367         return jsUndefined();
    368 
    369     return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
    370 }
    371 
    372 void ProxyInstance::setFieldValue(ExecState* exec, const Field* field, JSValue value) const
    373 {
    374     if (!m_instanceProxy)
    375         return;
    376 
    377     uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
    378     uint32_t requestID = m_instanceProxy->nextRequestID();
    379 
    380     data_t valueData;
    381     mach_msg_type_number_t valueLength;
    382 
    383     m_instanceProxy->marshalValue(exec, value, valueData, valueLength);
    384     kern_return_t kr = _WKPHNPObjectSetProperty(m_instanceProxy->hostProxy()->port(),
    385                                                 m_instanceProxy->pluginID(), requestID,
    386                                                 m_objectID, serverIdentifier, valueData, valueLength);
    387     mig_deallocate(reinterpret_cast<vm_address_t>(valueData), valueLength);
    388     if (kr != KERN_SUCCESS)
    389         return;
    390 
    391     auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
    392     NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
    393 }
    394 
    395 void ProxyInstance::invalidate()
    396 {
    397     ASSERT(m_instanceProxy);
    398 
    399     if (NetscapePluginHostProxy* hostProxy = m_instanceProxy->hostProxy())
    400         _WKPHNPObjectRelease(hostProxy->port(),
    401                              m_instanceProxy->pluginID(), m_objectID);
    402     m_instanceProxy = 0;
    403 }
    404 
    405 } // namespace WebKit
    406 
    407 #endif // USE(PLUGIN_HOST_PROCESS)
    408 
    409