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) && ENABLE(NETSCAPE_PLUGIN_API)
27
28#import "ProxyInstance.h"
29
30#import "NetscapePluginHostProxy.h"
31#import "ProxyRuntimeObject.h"
32#import <WebCore/IdentifierRep.h>
33#import <WebCore/JSDOMWindow.h>
34#import <WebCore/npruntime_impl.h>
35#import <WebCore/runtime_method.h>
36#import <runtime/Error.h>
37#import <runtime/FunctionPrototype.h>
38#import <runtime/PropertyNameArray.h>
39
40extern "C" {
41#import "WebKitPluginHost.h"
42}
43
44using namespace JSC;
45using namespace JSC::Bindings;
46using namespace WebCore;
47
48namespace WebKit {
49
50class ProxyClass : public JSC::Bindings::Class {
51private:
52    virtual Method* methodNamed(PropertyName, Instance*) const;
53    virtual Field* fieldNamed(PropertyName, Instance*) const;
54};
55
56Method* ProxyClass::methodNamed(PropertyName propertyName, Instance* instance) const
57{
58    return static_cast<ProxyInstance*>(instance)->methodNamed(propertyName);
59}
60
61Field* ProxyClass::fieldNamed(PropertyName propertyName, Instance* instance) const
62{
63    return static_cast<ProxyInstance*>(instance)->fieldNamed(propertyName);
64}
65
66static ProxyClass* proxyClass()
67{
68    DEFINE_STATIC_LOCAL(ProxyClass, proxyClass, ());
69    return &proxyClass;
70}
71
72class ProxyField : public JSC::Bindings::Field {
73public:
74    ProxyField(uint64_t serverIdentifier)
75        : m_serverIdentifier(serverIdentifier)
76    {
77    }
78
79    uint64_t serverIdentifier() const { return m_serverIdentifier; }
80
81private:
82    virtual JSValue valueFromInstance(ExecState*, const Instance*) const;
83    virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const;
84
85    uint64_t m_serverIdentifier;
86};
87
88JSValue ProxyField::valueFromInstance(ExecState* exec, const Instance* instance) const
89{
90    return static_cast<const ProxyInstance*>(instance)->fieldValue(exec, this);
91}
92
93void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const
94{
95    static_cast<const ProxyInstance*>(instance)->setFieldValue(exec, this, value);
96}
97
98class ProxyMethod : public JSC::Bindings::Method {
99public:
100    ProxyMethod(uint64_t serverIdentifier)
101        : m_serverIdentifier(serverIdentifier)
102    {
103    }
104
105    uint64_t serverIdentifier() const { return m_serverIdentifier; }
106
107private:
108    virtual int numParameters() const { return 0; }
109
110    uint64_t m_serverIdentifier;
111};
112
113ProxyInstance::ProxyInstance(PassRefPtr<RootObject> rootObject, NetscapePluginInstanceProxy* instanceProxy, uint32_t objectID)
114    : Instance(rootObject)
115    , m_instanceProxy(instanceProxy)
116    , m_objectID(objectID)
117{
118    m_instanceProxy->addInstance(this);
119}
120
121ProxyInstance::~ProxyInstance()
122{
123    deleteAllValues(m_fields);
124    deleteAllValues(m_methods);
125
126    if (!m_instanceProxy)
127        return;
128
129    m_instanceProxy->removeInstance(this);
130
131    invalidate();
132}
133
134RuntimeObject* ProxyInstance::newRuntimeObject(ExecState* exec)
135{
136    return ProxyRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
137}
138
139JSC::Bindings::Class* ProxyInstance::getClass() const
140{
141    return proxyClass();
142}
143
144JSValue ProxyInstance::invoke(JSC::ExecState* exec, InvokeType type, uint64_t identifier, const ArgList& args)
145{
146    if (!m_instanceProxy)
147        return jsUndefined();
148
149    RetainPtr<NSData*> arguments(m_instanceProxy->marshalValues(exec, args));
150
151    uint32_t requestID = m_instanceProxy->nextRequestID();
152
153    for (unsigned i = 0; i < args.size(); i++)
154        m_instanceProxy->retainLocalObject(args.at(i));
155
156    if (_WKPHNPObjectInvoke(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID,
157                            type, identifier, (char*)[arguments.get() bytes], [arguments.get() length]) != KERN_SUCCESS) {
158        if (m_instanceProxy) {
159            for (unsigned i = 0; i < args.size(); i++)
160                m_instanceProxy->releaseLocalObject(args.at(i));
161        }
162        return jsUndefined();
163    }
164
165    std::auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
166    NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
167
168    if (m_instanceProxy) {
169        for (unsigned i = 0; i < args.size(); i++)
170            m_instanceProxy->releaseLocalObject(args.at(i));
171    }
172
173    if (!reply.get() || !reply->m_returnValue)
174        return jsUndefined();
175
176    return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
177}
178
179class ProxyRuntimeMethod : public RuntimeMethod {
180public:
181    typedef RuntimeMethod Base;
182
183    static ProxyRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const String& name, Bindings::Method* method)
184    {
185        // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
186        // exec-vm() is also likely wrong.
187        Structure* domStructure = deprecatedGetDOMStructure<ProxyRuntimeMethod>(exec);
188        ProxyRuntimeMethod* runtimeMethod = new (allocateCell<ProxyRuntimeMethod>(*exec->heap())) ProxyRuntimeMethod(globalObject, domStructure, method);
189        runtimeMethod->finishCreation(exec->vm(), name);
190        return runtimeMethod;
191    }
192
193    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
194    {
195        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
196    }
197
198    static const ClassInfo s_info;
199
200private:
201    ProxyRuntimeMethod(JSGlobalObject* globalObject, Structure* structure, Bindings::Method* method)
202        : RuntimeMethod(globalObject, structure, method)
203    {
204    }
205
206    void finishCreation(VM& vm, const String& name)
207    {
208        Base::finishCreation(vm, name);
209        ASSERT(inherits(&s_info));
210    }
211};
212
213const ClassInfo ProxyRuntimeMethod::s_info = { "ProxyRuntimeMethod", &RuntimeMethod::s_info, 0, 0, CREATE_METHOD_TABLE(ProxyRuntimeMethod) };
214
215JSValue ProxyInstance::getMethod(JSC::ExecState* exec, PropertyName propertyName)
216{
217    Method* method = getClass()->methodNamed(propertyName, this);
218    return ProxyRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName.publicName(), method);
219}
220
221JSValue ProxyInstance::invokeMethod(ExecState* exec, JSC::RuntimeMethod* runtimeMethod)
222{
223    if (!asObject(runtimeMethod)->inherits(&ProxyRuntimeMethod::s_info))
224        return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object."));
225
226    ProxyMethod* method = static_cast<ProxyMethod*>(runtimeMethod->method());
227    ASSERT(method);
228
229    return invoke(exec, Invoke, method->serverIdentifier(), ArgList(exec));
230}
231
232bool ProxyInstance::supportsInvokeDefaultMethod() const
233{
234    if (!m_instanceProxy)
235        return false;
236
237    uint32_t requestID = m_instanceProxy->nextRequestID();
238
239    if (_WKPHNPObjectHasInvokeDefaultMethod(m_instanceProxy->hostProxy()->port(),
240                                            m_instanceProxy->pluginID(), requestID,
241                                            m_objectID) != KERN_SUCCESS)
242        return false;
243
244    std::auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
245    if (reply.get() && reply->m_result)
246        return true;
247
248    return false;
249}
250
251JSValue ProxyInstance::invokeDefaultMethod(ExecState* exec)
252{
253    return invoke(exec, InvokeDefault, 0, ArgList(exec));
254}
255
256bool ProxyInstance::supportsConstruct() const
257{
258    if (!m_instanceProxy)
259        return false;
260
261    uint32_t requestID = m_instanceProxy->nextRequestID();
262
263    if (_WKPHNPObjectHasConstructMethod(m_instanceProxy->hostProxy()->port(),
264                                        m_instanceProxy->pluginID(), requestID,
265                                        m_objectID) != KERN_SUCCESS)
266        return false;
267
268    std::auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
269    if (reply.get() && reply->m_result)
270        return true;
271
272    return false;
273}
274
275JSValue ProxyInstance::invokeConstruct(ExecState* exec, const ArgList& args)
276{
277    return invoke(exec, Construct, 0, args);
278}
279
280JSValue ProxyInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
281{
282    if (hint == PreferString)
283        return stringValue(exec);
284    if (hint == PreferNumber)
285        return numberValue(exec);
286    return valueOf(exec);
287}
288
289JSValue ProxyInstance::stringValue(ExecState* exec) const
290{
291    // FIXME: Implement something sensible.
292    return jsEmptyString(exec);
293}
294
295JSValue ProxyInstance::numberValue(ExecState*) const
296{
297    // FIXME: Implement something sensible.
298    return jsNumber(0);
299}
300
301JSValue ProxyInstance::booleanValue() const
302{
303    // FIXME: Implement something sensible.
304    return jsBoolean(false);
305}
306
307JSValue ProxyInstance::valueOf(ExecState* exec) const
308{
309    return stringValue(exec);
310}
311
312void ProxyInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
313{
314    if (!m_instanceProxy)
315        return;
316
317    uint32_t requestID = m_instanceProxy->nextRequestID();
318
319    if (_WKPHNPObjectEnumerate(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID) != KERN_SUCCESS)
320        return;
321
322    std::auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
323    NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
324    if (!reply.get() || !reply->m_returnValue)
325        return;
326
327    RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:(NSData *)reply->m_result.get()
328                                                                 mutabilityOption:NSPropertyListImmutable
329                                                                           format:0
330                                                                 errorDescription:0];
331
332    for (NSNumber *number in array.get()) {
333        IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>([number longLongValue]);
334        if (!IdentifierRep::isValid(identifier))
335            continue;
336
337        if (identifier->isString()) {
338            const char* str = identifier->string();
339            nameArray.add(Identifier(JSDOMWindow::commonVM(), String::fromUTF8WithLatin1Fallback(str, strlen(str))));
340        } else
341            nameArray.add(Identifier::from(exec, identifier->number()));
342    }
343}
344
345Method* ProxyInstance::methodNamed(PropertyName propertyName)
346{
347    String name(propertyName.publicName());
348    if (name.isNull())
349        return 0;
350
351    if (!m_instanceProxy)
352        return 0;
353
354    // If we already have an entry in the map, use it.
355    MethodMap::iterator existingMapEntry = m_methods.find(name.impl());
356    if (existingMapEntry != m_methods.end()) {
357        if (existingMapEntry->value)
358            return existingMapEntry->value;
359    }
360
361    uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(name.ascii().data()));
362    uint32_t requestID = m_instanceProxy->nextRequestID();
363
364    if (_WKPHNPObjectHasMethod(m_instanceProxy->hostProxy()->port(),
365                               m_instanceProxy->pluginID(), requestID,
366                               m_objectID, methodName) != KERN_SUCCESS)
367        return 0;
368
369    std::auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
370    if (!reply.get())
371        return 0;
372
373    if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
374        return 0;
375
376    // Add a new entry to the map unless an entry was added while we were in waitForReply.
377    MethodMap::AddResult mapAddResult = m_methods.add(name.impl(), 0);
378    if (mapAddResult.isNewEntry && reply->m_result)
379        mapAddResult.iterator->value = new ProxyMethod(methodName);
380
381    return mapAddResult.iterator->value;
382}
383
384Field* ProxyInstance::fieldNamed(PropertyName propertyName)
385{
386    String name(propertyName.publicName());
387    if (name.isNull())
388        return 0;
389
390    if (!m_instanceProxy)
391        return 0;
392
393    // If we already have an entry in the map, use it.
394    FieldMap::iterator existingMapEntry = m_fields.find(name.impl());
395    if (existingMapEntry != m_fields.end())
396        return existingMapEntry->value;
397
398    uint64_t identifier = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(name.ascii().data()));
399    uint32_t requestID = m_instanceProxy->nextRequestID();
400
401    if (_WKPHNPObjectHasProperty(m_instanceProxy->hostProxy()->port(),
402                                 m_instanceProxy->pluginID(), requestID,
403                                 m_objectID, identifier) != KERN_SUCCESS)
404        return 0;
405
406    std::auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
407    if (!reply.get())
408        return 0;
409
410    if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
411        return 0;
412
413    // Add a new entry to the map unless an entry was added while we were in waitForReply.
414    FieldMap::AddResult mapAddResult = m_fields.add(name.impl(), 0);
415    if (mapAddResult.isNewEntry && reply->m_result)
416        mapAddResult.iterator->value = new ProxyField(identifier);
417    return mapAddResult.iterator->value;
418}
419
420JSC::JSValue ProxyInstance::fieldValue(ExecState* exec, const Field* field) const
421{
422    if (!m_instanceProxy)
423        return jsUndefined();
424
425    uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
426    uint32_t requestID = m_instanceProxy->nextRequestID();
427
428    if (_WKPHNPObjectGetProperty(m_instanceProxy->hostProxy()->port(),
429                                 m_instanceProxy->pluginID(), requestID,
430                                 m_objectID, serverIdentifier) != KERN_SUCCESS)
431        return jsUndefined();
432
433    std::auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
434    NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
435    if (!reply.get() || !reply->m_returnValue)
436        return jsUndefined();
437
438    return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
439}
440
441void ProxyInstance::setFieldValue(ExecState* exec, const Field* field, JSValue value) const
442{
443    if (!m_instanceProxy)
444        return;
445
446    uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
447    uint32_t requestID = m_instanceProxy->nextRequestID();
448
449    data_t valueData;
450    mach_msg_type_number_t valueLength;
451
452    m_instanceProxy->marshalValue(exec, value, valueData, valueLength);
453    m_instanceProxy->retainLocalObject(value);
454    kern_return_t kr = _WKPHNPObjectSetProperty(m_instanceProxy->hostProxy()->port(),
455                                                m_instanceProxy->pluginID(), requestID,
456                                                m_objectID, serverIdentifier, valueData, valueLength);
457    mig_deallocate(reinterpret_cast<vm_address_t>(valueData), valueLength);
458    if (m_instanceProxy)
459        m_instanceProxy->releaseLocalObject(value);
460    if (kr != KERN_SUCCESS)
461        return;
462
463    std::auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
464    NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
465}
466
467void ProxyInstance::invalidate()
468{
469    ASSERT(m_instanceProxy);
470
471    if (NetscapePluginHostProxy* hostProxy = m_instanceProxy->hostProxy())
472        _WKPHNPObjectRelease(hostProxy->port(),
473                             m_instanceProxy->pluginID(), m_objectID);
474    m_instanceProxy = 0;
475}
476
477} // namespace WebKit
478
479#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
480
481