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) 27 28 #import "ProxyInstance.h" 29 30 #import "NetscapePluginHostProxy.h" 31 #import <WebCore/IdentifierRep.h> 32 #import <WebCore/JSDOMWindow.h> 33 #import <WebCore/npruntime_impl.h> 34 #import <runtime/PropertyNameArray.h> 35 36 extern "C" { 37 #import "WebKitPluginHost.h" 38 } 39 40 using namespace JSC; 41 using namespace JSC::Bindings; 42 using namespace std; 43 using namespace WebCore; 44 45 namespace WebKit { 46 47 class ProxyClass : public JSC::Bindings::Class { 48 private: 49 virtual MethodList methodsNamed(const Identifier&, Instance*) const; 50 virtual Field* fieldNamed(const Identifier&, Instance*) const; 51 }; 52 53 MethodList ProxyClass::methodsNamed(const Identifier& identifier, Instance* instance) const 54 { 55 return static_cast<ProxyInstance*>(instance)->methodsNamed(identifier); 56 } 57 58 Field* ProxyClass::fieldNamed(const Identifier& identifier, Instance* instance) const 59 { 60 return static_cast<ProxyInstance*>(instance)->fieldNamed(identifier); 61 } 62 63 static ProxyClass* proxyClass() 64 { 65 DEFINE_STATIC_LOCAL(ProxyClass, proxyClass, ()); 66 return &proxyClass; 67 } 68 69 class ProxyField : public JSC::Bindings::Field { 70 public: 71 ProxyField(uint64_t serverIdentifier) 72 : m_serverIdentifier(serverIdentifier) 73 { 74 } 75 76 uint64_t serverIdentifier() const { return m_serverIdentifier; } 77 78 private: 79 virtual JSValue valueFromInstance(ExecState*, const Instance*) const; 80 virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const; 81 82 uint64_t m_serverIdentifier; 83 }; 84 85 JSValue ProxyField::valueFromInstance(ExecState* exec, const Instance* instance) const 86 { 87 return static_cast<const ProxyInstance*>(instance)->fieldValue(exec, this); 88 } 89 90 void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const 91 { 92 static_cast<const ProxyInstance*>(instance)->setFieldValue(exec, this, value); 93 } 94 95 class ProxyMethod : public JSC::Bindings::Method { 96 public: 97 ProxyMethod(uint64_t serverIdentifier) 98 : m_serverIdentifier(serverIdentifier) 99 { 100 } 101 102 uint64_t serverIdentifier() const { return m_serverIdentifier; } 103 104 private: 105 virtual int numParameters() const { return 0; } 106 107 uint64_t m_serverIdentifier; 108 }; 109 110 ProxyInstance::ProxyInstance(PassRefPtr<RootObject> rootObject, NetscapePluginInstanceProxy* instanceProxy, uint32_t objectID) 111 : Instance(rootObject) 112 , m_instanceProxy(instanceProxy) 113 , m_objectID(objectID) 114 { 115 m_instanceProxy->addInstance(this); 116 } 117 118 ProxyInstance::~ProxyInstance() 119 { 120 deleteAllValues(m_fields); 121 deleteAllValues(m_methods); 122 123 if (!m_instanceProxy) 124 return; 125 126 m_instanceProxy->removeInstance(this); 127 128 invalidate(); 129 } 130 131 JSC::Bindings::Class *ProxyInstance::getClass() const 132 { 133 return proxyClass(); 134 } 135 136 JSValue ProxyInstance::invoke(JSC::ExecState* exec, InvokeType type, uint64_t identifier, const JSC::ArgList& args) 137 { 138 if (!m_instanceProxy) 139 return jsUndefined(); 140 141 RetainPtr<NSData*> arguments(m_instanceProxy->marshalValues(exec, args)); 142 143 uint32_t requestID = m_instanceProxy->nextRequestID(); 144 145 if (_WKPHNPObjectInvoke(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID, 146 type, identifier, (char*)[arguments.get() bytes], [arguments.get() length]) != KERN_SUCCESS) 147 return jsUndefined(); 148 149 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); 150 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); 151 if (!reply.get() || !reply->m_returnValue) 152 return jsUndefined(); 153 154 return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get())); 155 } 156 157 JSValue ProxyInstance::invokeMethod(ExecState* exec, const MethodList& methodList, const ArgList& args) 158 { 159 ASSERT(methodList.size() == 1); 160 161 ProxyMethod* method = static_cast<ProxyMethod*>(methodList[0]); 162 163 return invoke(exec, Invoke, method->serverIdentifier(), args); 164 } 165 166 bool ProxyInstance::supportsInvokeDefaultMethod() const 167 { 168 if (!m_instanceProxy) 169 return false; 170 171 uint32_t requestID = m_instanceProxy->nextRequestID(); 172 173 if (_WKPHNPObjectHasInvokeDefaultMethod(m_instanceProxy->hostProxy()->port(), 174 m_instanceProxy->pluginID(), requestID, 175 m_objectID) != KERN_SUCCESS) 176 return false; 177 178 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 179 if (reply.get() && reply->m_result) 180 return true; 181 182 return false; 183 } 184 185 JSValue ProxyInstance::invokeDefaultMethod(ExecState* exec, const ArgList& args) 186 { 187 return invoke(exec, InvokeDefault, 0, args); 188 } 189 190 bool ProxyInstance::supportsConstruct() const 191 { 192 if (!m_instanceProxy) 193 return false; 194 195 uint32_t requestID = m_instanceProxy->nextRequestID(); 196 197 if (_WKPHNPObjectHasConstructMethod(m_instanceProxy->hostProxy()->port(), 198 m_instanceProxy->pluginID(), requestID, 199 m_objectID) != KERN_SUCCESS) 200 return false; 201 202 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 203 if (reply.get() && reply->m_result) 204 return true; 205 206 return false; 207 } 208 209 JSValue ProxyInstance::invokeConstruct(ExecState* exec, const ArgList& args) 210 { 211 return invoke(exec, Construct, 0, args); 212 } 213 214 JSValue ProxyInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const 215 { 216 if (hint == PreferString) 217 return stringValue(exec); 218 if (hint == PreferNumber) 219 return numberValue(exec); 220 return valueOf(exec); 221 } 222 223 JSValue ProxyInstance::stringValue(ExecState* exec) const 224 { 225 // FIXME: Implement something sensible. 226 return jsEmptyString(exec); 227 } 228 229 JSValue ProxyInstance::numberValue(ExecState* exec) const 230 { 231 // FIXME: Implement something sensible. 232 return jsNumber(exec, 0); 233 } 234 235 JSValue ProxyInstance::booleanValue() const 236 { 237 // FIXME: Implement something sensible. 238 return jsBoolean(false); 239 } 240 241 JSValue ProxyInstance::valueOf(ExecState* exec) const 242 { 243 return stringValue(exec); 244 } 245 246 void ProxyInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray) 247 { 248 if (!m_instanceProxy) 249 return; 250 251 uint32_t requestID = m_instanceProxy->nextRequestID(); 252 253 if (_WKPHNPObjectEnumerate(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID) != KERN_SUCCESS) 254 return; 255 256 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); 257 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); 258 if (!reply.get() || !reply->m_returnValue) 259 return; 260 261 RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:(NSData *)reply->m_result.get() 262 mutabilityOption:NSPropertyListImmutable 263 format:0 264 errorDescription:0]; 265 266 for (NSNumber *number in array.get()) { 267 IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>([number longLongValue]); 268 if (!IdentifierRep::isValid(identifier)) 269 continue; 270 271 if (identifier->isString()) { 272 const char* str = identifier->string(); 273 nameArray.add(Identifier(JSDOMWindow::commonJSGlobalData(), String::fromUTF8WithLatin1Fallback(str, strlen(str)))); 274 } else 275 nameArray.add(Identifier::from(exec, identifier->number())); 276 } 277 } 278 279 MethodList ProxyInstance::methodsNamed(const Identifier& identifier) 280 { 281 if (!m_instanceProxy) 282 return MethodList(); 283 284 // If we already have an entry in the map, use it. 285 MethodMap::iterator existingMapEntry = m_methods.find(identifier.ustring().rep()); 286 if (existingMapEntry != m_methods.end()) { 287 MethodList methodList; 288 if (existingMapEntry->second) 289 methodList.append(existingMapEntry->second); 290 return methodList; 291 } 292 293 uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii())); 294 uint32_t requestID = m_instanceProxy->nextRequestID(); 295 296 if (_WKPHNPObjectHasMethod(m_instanceProxy->hostProxy()->port(), 297 m_instanceProxy->pluginID(), requestID, 298 m_objectID, methodName) != KERN_SUCCESS) 299 return MethodList(); 300 301 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 302 if (!reply.get()) 303 return MethodList(); 304 305 if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods()) 306 return MethodList(); 307 308 // Add a new entry to the map unless an entry was added while we were in waitForReply. 309 pair<MethodMap::iterator, bool> mapAddResult = m_methods.add(identifier.ustring().rep(), 0); 310 if (mapAddResult.second && reply->m_result) 311 mapAddResult.first->second = new ProxyMethod(methodName); 312 313 MethodList methodList; 314 if (mapAddResult.first->second) 315 methodList.append(mapAddResult.first->second); 316 return methodList; 317 } 318 319 Field* ProxyInstance::fieldNamed(const Identifier& identifier) 320 { 321 if (!m_instanceProxy) 322 return 0; 323 324 // If we already have an entry in the map, use it. 325 FieldMap::iterator existingMapEntry = m_fields.find(identifier.ustring().rep()); 326 if (existingMapEntry != m_fields.end()) 327 return existingMapEntry->second; 328 329 uint64_t propertyName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii())); 330 uint32_t requestID = m_instanceProxy->nextRequestID(); 331 332 if (_WKPHNPObjectHasProperty(m_instanceProxy->hostProxy()->port(), 333 m_instanceProxy->pluginID(), requestID, 334 m_objectID, propertyName) != KERN_SUCCESS) 335 return 0; 336 337 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 338 if (!reply.get()) 339 return 0; 340 341 if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods()) 342 return 0; 343 344 // Add a new entry to the map unless an entry was added while we were in waitForReply. 345 pair<FieldMap::iterator, bool> mapAddResult = m_fields.add(identifier.ustring().rep(), 0); 346 if (mapAddResult.second && reply->m_result) 347 mapAddResult.first->second = new ProxyField(propertyName); 348 return mapAddResult.first->second; 349 } 350 351 JSC::JSValue ProxyInstance::fieldValue(ExecState* exec, const Field* field) const 352 { 353 if (!m_instanceProxy) 354 return jsUndefined(); 355 356 uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier(); 357 uint32_t requestID = m_instanceProxy->nextRequestID(); 358 359 if (_WKPHNPObjectGetProperty(m_instanceProxy->hostProxy()->port(), 360 m_instanceProxy->pluginID(), requestID, 361 m_objectID, serverIdentifier) != KERN_SUCCESS) 362 return jsUndefined(); 363 364 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); 365 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); 366 if (!reply.get() || !reply->m_returnValue) 367 return jsUndefined(); 368 369 return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get())); 370 } 371 372 void ProxyInstance::setFieldValue(ExecState* exec, const Field* field, JSValue value) const 373 { 374 if (!m_instanceProxy) 375 return; 376 377 uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier(); 378 uint32_t requestID = m_instanceProxy->nextRequestID(); 379 380 data_t valueData; 381 mach_msg_type_number_t valueLength; 382 383 m_instanceProxy->marshalValue(exec, value, valueData, valueLength); 384 kern_return_t kr = _WKPHNPObjectSetProperty(m_instanceProxy->hostProxy()->port(), 385 m_instanceProxy->pluginID(), requestID, 386 m_objectID, serverIdentifier, valueData, valueLength); 387 mig_deallocate(reinterpret_cast<vm_address_t>(valueData), valueLength); 388 if (kr != KERN_SUCCESS) 389 return; 390 391 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 392 NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); 393 } 394 395 void ProxyInstance::invalidate() 396 { 397 ASSERT(m_instanceProxy); 398 399 if (NetscapePluginHostProxy* hostProxy = m_instanceProxy->hostProxy()) 400 _WKPHNPObjectRelease(hostProxy->port(), 401 m_instanceProxy->pluginID(), m_objectID); 402 m_instanceProxy = 0; 403 } 404 405 } // namespace WebKit 406 407 #endif // USE(PLUGIN_HOST_PROCESS) 408 409