2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
28 #import "ProxyInstance.h"
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>
41 #import "WebKitPluginHost.h"
45 using namespace JSC::Bindings;
47 using namespace WebCore;
51 class ProxyClass : public JSC::Bindings::Class {
53 virtual MethodList methodsNamed(const Identifier&, Instance*) const;
54 virtual Field* fieldNamed(const Identifier&, Instance*) const;
57 MethodList ProxyClass::methodsNamed(const Identifier& identifier, Instance* instance) const
59 return static_cast<ProxyInstance*>(instance)->methodsNamed(identifier);
62 Field* ProxyClass::fieldNamed(const Identifier& identifier, Instance* instance) const
64 return static_cast<ProxyInstance*>(instance)->fieldNamed(identifier);
67 static ProxyClass* proxyClass()
69 DEFINE_STATIC_LOCAL(ProxyClass, proxyClass, ());
73 class ProxyField : public JSC::Bindings::Field {
75 ProxyField(uint64_t serverIdentifier)
76 : m_serverIdentifier(serverIdentifier)
80 uint64_t serverIdentifier() const { return m_serverIdentifier; }
83 virtual JSValue valueFromInstance(ExecState*, const Instance*) const;
84 virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const;
86 uint64_t m_serverIdentifier;
89 JSValue ProxyField::valueFromInstance(ExecState* exec, const Instance* instance) const
91 return static_cast<const ProxyInstance*>(instance)->fieldValue(exec, this);
94 void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const
96 static_cast<const ProxyInstance*>(instance)->setFieldValue(exec, this, value);
99 class ProxyMethod : public JSC::Bindings::Method {
101 ProxyMethod(uint64_t serverIdentifier)
102 : m_serverIdentifier(serverIdentifier)
106 uint64_t serverIdentifier() const { return m_serverIdentifier; }
109 virtual int numParameters() const { return 0; }
111 uint64_t m_serverIdentifier;
114 ProxyInstance::ProxyInstance(PassRefPtr<RootObject> rootObject, NetscapePluginInstanceProxy* instanceProxy, uint32_t objectID)
115 : Instance(rootObject)
116 , m_instanceProxy(instanceProxy)
117 , m_objectID(objectID)
119 m_instanceProxy->addInstance(this);
122 ProxyInstance::~ProxyInstance()
124 deleteAllValues(m_fields);
125 deleteAllValues(m_methods);
127 if (!m_instanceProxy)
130 m_instanceProxy->removeInstance(this);
135 RuntimeObject* ProxyInstance::newRuntimeObject(ExecState* exec)
137 return ProxyRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
140 JSC::Bindings::Class* ProxyInstance::getClass() const
145 JSValue ProxyInstance::invoke(JSC::ExecState* exec, InvokeType type, uint64_t identifier, const ArgList& args)
147 if (!m_instanceProxy)
148 return jsUndefined();
150 RetainPtr<NSData*> arguments(m_instanceProxy->marshalValues(exec, args));
152 uint32_t requestID = m_instanceProxy->nextRequestID();
154 for (unsigned i = 0; i < args.size(); i++)
155 m_instanceProxy->retainLocalObject(args.at(i));
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));
163 return jsUndefined();
166 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
167 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
169 if (m_instanceProxy) {
170 for (unsigned i = 0; i < args.size(); i++)
171 m_instanceProxy->releaseLocalObject(args.at(i));
174 if (!reply.get() || !reply->m_returnValue)
175 return jsUndefined();
177 return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
180 class ProxyRuntimeMethod : public RuntimeMethod {
182 typedef RuntimeMethod Base;
184 static ProxyRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list)
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 return new (allocateCell<ProxyRuntimeMethod>(*exec->heap())) ProxyRuntimeMethod(exec, globalObject, domStructure, name, list);
192 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
194 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
197 static const ClassInfo s_info;
200 ProxyRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, const Identifier& name, Bindings::MethodList& list)
201 : RuntimeMethod(globalObject, structure, list)
203 finishCreation(exec->globalData(), name);
206 void finishCreation(JSGlobalData& globalData, const Identifier& name)
208 Base::finishCreation(globalData, name);
209 ASSERT(inherits(&s_info));
213 const ClassInfo ProxyRuntimeMethod::s_info = { "ProxyRuntimeMethod", &RuntimeMethod::s_info, 0, 0 };
215 JSValue ProxyInstance::getMethod(JSC::ExecState* exec, const JSC::Identifier& propertyName)
217 MethodList methodList = getClass()->methodsNamed(propertyName, this);
218 return ProxyRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName, methodList);
221 JSValue ProxyInstance::invokeMethod(ExecState* exec, JSC::RuntimeMethod* runtimeMethod)
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."));
226 const MethodList& methodList = *runtimeMethod->methods();
228 ASSERT(methodList.size() == 1);
230 ProxyMethod* method = static_cast<ProxyMethod*>(methodList[0]);
232 return invoke(exec, Invoke, method->serverIdentifier(), ArgList(exec));
235 bool ProxyInstance::supportsInvokeDefaultMethod() const
237 if (!m_instanceProxy)
240 uint32_t requestID = m_instanceProxy->nextRequestID();
242 if (_WKPHNPObjectHasInvokeDefaultMethod(m_instanceProxy->hostProxy()->port(),
243 m_instanceProxy->pluginID(), requestID,
244 m_objectID) != KERN_SUCCESS)
247 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
248 if (reply.get() && reply->m_result)
254 JSValue ProxyInstance::invokeDefaultMethod(ExecState* exec)
256 return invoke(exec, InvokeDefault, 0, ArgList(exec));
259 bool ProxyInstance::supportsConstruct() const
261 if (!m_instanceProxy)
264 uint32_t requestID = m_instanceProxy->nextRequestID();
266 if (_WKPHNPObjectHasConstructMethod(m_instanceProxy->hostProxy()->port(),
267 m_instanceProxy->pluginID(), requestID,
268 m_objectID) != KERN_SUCCESS)
271 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
272 if (reply.get() && reply->m_result)
278 JSValue ProxyInstance::invokeConstruct(ExecState* exec, const ArgList& args)
280 return invoke(exec, Construct, 0, args);
283 JSValue ProxyInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
285 if (hint == PreferString)
286 return stringValue(exec);
287 if (hint == PreferNumber)
288 return numberValue(exec);
289 return valueOf(exec);
292 JSValue ProxyInstance::stringValue(ExecState* exec) const
294 // FIXME: Implement something sensible.
295 return jsEmptyString(exec);
298 JSValue ProxyInstance::numberValue(ExecState*) const
300 // FIXME: Implement something sensible.
304 JSValue ProxyInstance::booleanValue() const
306 // FIXME: Implement something sensible.
307 return jsBoolean(false);
310 JSValue ProxyInstance::valueOf(ExecState* exec) const
312 return stringValue(exec);
315 void ProxyInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
317 if (!m_instanceProxy)
320 uint32_t requestID = m_instanceProxy->nextRequestID();
322 if (_WKPHNPObjectEnumerate(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID) != KERN_SUCCESS)
325 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
326 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
327 if (!reply.get() || !reply->m_returnValue)
330 RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:(NSData *)reply->m_result.get()
331 mutabilityOption:NSPropertyListImmutable
335 for (NSNumber *number in array.get()) {
336 IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>([number longLongValue]);
337 if (!IdentifierRep::isValid(identifier))
340 if (identifier->isString()) {
341 const char* str = identifier->string();
342 nameArray.add(Identifier(JSDOMWindow::commonJSGlobalData(), stringToUString(String::fromUTF8WithLatin1Fallback(str, strlen(str)))));
344 nameArray.add(Identifier::from(exec, identifier->number()));
348 MethodList ProxyInstance::methodsNamed(const Identifier& identifier)
350 if (!m_instanceProxy)
353 // If we already have an entry in the map, use it.
354 MethodMap::iterator existingMapEntry = m_methods.find(identifier.impl());
355 if (existingMapEntry != m_methods.end()) {
356 MethodList methodList;
357 if (existingMapEntry->second)
358 methodList.append(existingMapEntry->second);
362 uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii().data()));
363 uint32_t requestID = m_instanceProxy->nextRequestID();
365 if (_WKPHNPObjectHasMethod(m_instanceProxy->hostProxy()->port(),
366 m_instanceProxy->pluginID(), requestID,
367 m_objectID, methodName) != KERN_SUCCESS)
370 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
374 if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
377 // Add a new entry to the map unless an entry was added while we were in waitForReply.
378 pair<MethodMap::iterator, bool> mapAddResult = m_methods.add(identifier.impl(), 0);
379 if (mapAddResult.second && reply->m_result)
380 mapAddResult.first->second = new ProxyMethod(methodName);
382 MethodList methodList;
383 if (mapAddResult.first->second)
384 methodList.append(mapAddResult.first->second);
388 Field* ProxyInstance::fieldNamed(const Identifier& identifier)
390 if (!m_instanceProxy)
393 // If we already have an entry in the map, use it.
394 FieldMap::iterator existingMapEntry = m_fields.find(identifier.impl());
395 if (existingMapEntry != m_fields.end())
396 return existingMapEntry->second;
398 uint64_t propertyName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii().data()));
399 uint32_t requestID = m_instanceProxy->nextRequestID();
401 if (_WKPHNPObjectHasProperty(m_instanceProxy->hostProxy()->port(),
402 m_instanceProxy->pluginID(), requestID,
403 m_objectID, propertyName) != KERN_SUCCESS)
406 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
410 if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods())
413 // Add a new entry to the map unless an entry was added while we were in waitForReply.
414 pair<FieldMap::iterator, bool> mapAddResult = m_fields.add(identifier.impl(), 0);
415 if (mapAddResult.second && reply->m_result)
416 mapAddResult.first->second = new ProxyField(propertyName);
417 return mapAddResult.first->second;
420 JSC::JSValue ProxyInstance::fieldValue(ExecState* exec, const Field* field) const
422 if (!m_instanceProxy)
423 return jsUndefined();
425 uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
426 uint32_t requestID = m_instanceProxy->nextRequestID();
428 if (_WKPHNPObjectGetProperty(m_instanceProxy->hostProxy()->port(),
429 m_instanceProxy->pluginID(), requestID,
430 m_objectID, serverIdentifier) != KERN_SUCCESS)
431 return jsUndefined();
433 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
434 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
435 if (!reply.get() || !reply->m_returnValue)
436 return jsUndefined();
438 return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get()));
441 void ProxyInstance::setFieldValue(ExecState* exec, const Field* field, JSValue value) const
443 if (!m_instanceProxy)
446 uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier();
447 uint32_t requestID = m_instanceProxy->nextRequestID();
450 mach_msg_type_number_t valueLength;
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);
459 m_instanceProxy->releaseLocalObject(value);
460 if (kr != KERN_SUCCESS)
463 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
464 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec);
467 void ProxyInstance::invalidate()
469 ASSERT(m_instanceProxy);
471 if (NetscapePluginHostProxy* hostProxy = m_instanceProxy->hostProxy())
472 _WKPHNPObjectRelease(hostProxy->port(),
473 m_instanceProxy->pluginID(), m_objectID);
477 } // namespace WebKit
479 #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)