d7a81fbd417cd58c3e2bd388212a51855356c389
[WebKit-https.git] / Source / WebKit / mac / Plugins / Hosted / ProxyInstance.mm
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
40 extern "C" {
41 #import "WebKitPluginHost.h"
42 }
43
44 using namespace JSC;
45 using namespace JSC::Bindings;
46 using namespace WebCore;
47
48 namespace WebKit {
49
50 class ProxyClass : public JSC::Bindings::Class {
51 private:
52     virtual Method* methodNamed(PropertyName, Instance*) const;
53     virtual Field* fieldNamed(PropertyName, Instance*) const;
54 };
55
56 Method* ProxyClass::methodNamed(PropertyName propertyName, Instance* instance) const
57 {
58     return static_cast<ProxyInstance*>(instance)->methodNamed(propertyName);
59 }
60
61 Field* ProxyClass::fieldNamed(PropertyName propertyName, Instance* instance) const
62 {
63     return static_cast<ProxyInstance*>(instance)->fieldNamed(propertyName);
64 }
65
66 static ProxyClass* proxyClass()
67 {
68     DEFINE_STATIC_LOCAL(ProxyClass, proxyClass, ());
69     return &proxyClass;
70 }
71     
72 class ProxyField : public JSC::Bindings::Field {
73 public:
74     ProxyField(uint64_t serverIdentifier)
75         : m_serverIdentifier(serverIdentifier)
76     {
77     }
78     
79     uint64_t serverIdentifier() const { return m_serverIdentifier; }
80
81 private:
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
88 JSValue ProxyField::valueFromInstance(ExecState* exec, const Instance* instance) const
89 {
90     return static_cast<const ProxyInstance*>(instance)->fieldValue(exec, this);
91 }
92     
93 void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const
94 {
95     static_cast<const ProxyInstance*>(instance)->setFieldValue(exec, this, value);
96 }
97
98 class ProxyMethod : public JSC::Bindings::Method {
99 public:
100     ProxyMethod(uint64_t serverIdentifier)
101         : m_serverIdentifier(serverIdentifier)
102     {
103     }
104
105     uint64_t serverIdentifier() const { return m_serverIdentifier; }
106
107 private:
108     virtual int numParameters() const { return 0; }
109
110     uint64_t m_serverIdentifier;
111 };
112
113 ProxyInstance::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
121 ProxyInstance::~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     
134 RuntimeObject* ProxyInstance::newRuntimeObject(ExecState* exec)
135 {
136     return ProxyRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
137 }
138
139 JSC::Bindings::Class* ProxyInstance::getClass() const
140 {
141     return proxyClass();
142 }
143
144 JSValue 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
179 class ProxyRuntimeMethod : public RuntimeMethod {
180 public:
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     DECLARE_INFO;
199
200 private:
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(info()));
210     }
211 };
212
213 const ClassInfo ProxyRuntimeMethod::s_info = { "ProxyRuntimeMethod", &RuntimeMethod::s_info, 0, 0, CREATE_METHOD_TABLE(ProxyRuntimeMethod) };
214
215 JSValue 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
221 JSValue ProxyInstance::invokeMethod(ExecState* exec, JSC::RuntimeMethod* runtimeMethod)
222 {
223     if (!asObject(runtimeMethod)->inherits(ProxyRuntimeMethod::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
232 bool 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
251 JSValue ProxyInstance::invokeDefaultMethod(ExecState* exec)
252 {
253     return invoke(exec, InvokeDefault, 0, ArgList(exec));
254 }
255
256 bool 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     
275 JSValue ProxyInstance::invokeConstruct(ExecState* exec, const ArgList& args)
276 {
277     return invoke(exec, Construct, 0, args);
278 }
279
280 JSValue 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
289 JSValue ProxyInstance::stringValue(ExecState* exec) const
290 {
291     // FIXME: Implement something sensible.
292     return jsEmptyString(exec);
293 }
294
295 JSValue ProxyInstance::numberValue(ExecState*) const
296 {
297     // FIXME: Implement something sensible.
298     return jsNumber(0);
299 }
300
301 JSValue ProxyInstance::booleanValue() const
302 {
303     // FIXME: Implement something sensible.
304     return jsBoolean(false);
305 }
306
307 JSValue ProxyInstance::valueOf(ExecState* exec) const
308 {
309     return stringValue(exec);
310 }
311
312 void 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
345 Method* 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     auto 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     auto 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
384 Field* 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     auto 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     auto 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
420 JSC::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     
441 void 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
467 void 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