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