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