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