2 * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (C) 2007, 2008, 2009 Google, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "NPV8Object.h"
31 #include "PlatformSupport.h"
32 #include "DOMWindow.h"
34 #include "OwnArrayPtr.h"
35 #include "PlatformString.h"
36 #include "ScriptSourceCode.h"
37 #include "UserGestureIndicator.h"
38 #include "V8GCController.h"
39 #include "V8Helpers.h"
40 #include "V8NPUtils.h"
42 #include "WrapperTypeInfo.h"
43 #include "npruntime_impl.h"
44 #include "npruntime_priv.h"
47 #include <wtf/StringExtras.h>
49 using namespace WebCore;
53 WrapperTypeInfo* npObjectTypeInfo()
55 static WrapperTypeInfo typeInfo = { 0, 0, 0, 0 };
59 typedef HashMap<int, V8NPObject*> V8NPObjectMap;
61 static V8NPObjectMap* staticV8NPObjectMap()
63 DEFINE_STATIC_LOCAL(V8NPObjectMap, v8npObjectMap, ());
64 return &v8npObjectMap;
67 // FIXME: Comments on why use malloc and free.
68 static NPObject* allocV8NPObject(NPP, NPClass*)
70 return static_cast<NPObject*>(malloc(sizeof(V8NPObject)));
73 static void freeV8NPObject(NPObject* npObject)
75 V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
76 if (int v8ObjectHash = v8NpObject->v8Object->GetIdentityHash()) {
77 ASSERT(staticV8NPObjectMap()->contains(v8ObjectHash));
78 staticV8NPObjectMap()->remove(v8ObjectHash);
80 ASSERT(!v8::Context::InContext());
81 staticV8NPObjectMap()->clear();
85 V8GCController::unregisterGlobalHandle(v8NpObject, v8NpObject->v8Object);
87 v8NpObject->v8Object.Dispose();
91 static PassOwnArrayPtr<v8::Handle<v8::Value> > createValueListFromVariantArgs(const NPVariant* arguments, uint32_t argumentCount, NPObject* owner)
93 OwnArrayPtr<v8::Handle<v8::Value> > argv = adoptArrayPtr(new v8::Handle<v8::Value>[argumentCount]);
94 for (uint32_t index = 0; index < argumentCount; index++) {
95 const NPVariant* arg = &arguments[index];
96 argv[index] = convertNPVariantToV8Object(arg, owner);
98 return argv.release();
101 // Create an identifier (null terminated utf8 char*) from the NPIdentifier.
102 static v8::Local<v8::String> npIdentifierToV8Identifier(NPIdentifier name)
104 PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(name);
105 if (identifier->isString)
106 return v8::String::New(static_cast<const char*>(identifier->value.string));
109 snprintf(buffer, sizeof(buffer), "%d", identifier->value.number);
110 return v8::String::New(buffer);
113 NPObject* v8ObjectToNPObject(v8::Handle<v8::Object> object)
115 return reinterpret_cast<NPObject*>(object->GetPointerFromInternalField(v8DOMWrapperObjectIndex));
118 static NPClass V8NPObjectClass = { NP_CLASS_STRUCT_VERSION,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
123 // NPAPI's npruntime functions.
124 NPClass* npScriptObjectClass = &V8NPObjectClass;
126 NPObject* npCreateV8ScriptObject(NPP npp, v8::Handle<v8::Object> object, DOMWindow* root)
128 // Check to see if this object is already wrapped.
129 if (object->InternalFieldCount() == npObjectInternalFieldCount) {
130 WrapperTypeInfo* typeInfo = static_cast<WrapperTypeInfo*>(object->GetPointerFromInternalField(v8DOMWrapperTypeIndex));
131 if (typeInfo == npObjectTypeInfo()) {
133 NPObject* returnValue = v8ObjectToNPObject(object);
134 _NPN_RetainObject(returnValue);
139 int v8ObjectHash = object->GetIdentityHash();
140 ASSERT(v8ObjectHash);
141 if (staticV8NPObjectMap()->contains(v8ObjectHash)) {
142 V8NPObject* v8npObject = staticV8NPObjectMap()->get(v8ObjectHash);
143 ASSERT(v8npObject->v8Object == object);
144 _NPN_RetainObject(&v8npObject->object);
145 return reinterpret_cast<NPObject*>(v8npObject);
148 V8NPObject* v8npObject = reinterpret_cast<V8NPObject*>(_NPN_CreateObject(npp, &V8NPObjectClass));
149 v8npObject->v8Object = v8::Persistent<v8::Object>::New(object);
151 V8GCController::registerGlobalHandle(NPOBJECT, v8npObject, v8npObject->v8Object);
153 v8npObject->rootObject = root;
155 staticV8NPObjectMap()->set(v8ObjectHash, v8npObject);
157 return reinterpret_cast<NPObject*>(v8npObject);
160 } // namespace WebCore
162 bool _NPN_Invoke(NPP npp, NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
167 if (npObject->_class != npScriptObjectClass) {
168 if (npObject->_class->invoke)
169 return npObject->_class->invoke(npObject, methodName, arguments, argumentCount, result);
171 VOID_TO_NPVARIANT(*result);
175 V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
177 PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(methodName);
178 if (!identifier->isString)
181 if (!strcmp(identifier->value.string, "eval")) {
182 if (argumentCount != 1)
184 if (arguments[0].type != NPVariantType_String)
186 return _NPN_Evaluate(npp, npObject, const_cast<NPString*>(&arguments[0].value.stringValue), result);
189 v8::HandleScope handleScope;
190 // FIXME: should use the plugin's owner frame as the security context.
191 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
192 if (context.IsEmpty())
195 v8::Context::Scope scope(context);
196 ExceptionCatcher exceptionCatcher;
198 v8::Handle<v8::Value> functionObject = v8NpObject->v8Object->Get(v8::String::New(identifier->value.string));
199 if (functionObject.IsEmpty() || functionObject->IsNull()) {
200 NULL_TO_NPVARIANT(*result);
203 if (functionObject->IsUndefined()) {
204 VOID_TO_NPVARIANT(*result);
208 V8Proxy* proxy = toV8Proxy(npObject);
211 // Call the function object.
212 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(functionObject);
213 OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject);
214 v8::Local<v8::Value> resultObject = proxy->callFunction(function, v8NpObject->v8Object, argumentCount, argv.get());
216 // If we had an error, return false. The spec is a little unclear here, but says "Returns true if the method was
217 // successfully invoked". If we get an error return value, was that successfully invoked?
218 if (resultObject.IsEmpty())
221 convertV8ObjectToNPVariant(resultObject, npObject, result);
225 // FIXME: Fix it same as _NPN_Invoke (HandleScope and such).
226 bool _NPN_InvokeDefault(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
231 if (npObject->_class != npScriptObjectClass) {
232 if (npObject->_class->invokeDefault)
233 return npObject->_class->invokeDefault(npObject, arguments, argumentCount, result);
235 VOID_TO_NPVARIANT(*result);
239 V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
241 VOID_TO_NPVARIANT(*result);
243 v8::HandleScope handleScope;
244 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
245 if (context.IsEmpty())
248 v8::Context::Scope scope(context);
249 ExceptionCatcher exceptionCatcher;
251 // Lookup the function object and call it.
252 v8::Handle<v8::Object> functionObject(v8NpObject->v8Object);
253 if (!functionObject->IsFunction())
256 v8::Local<v8::Value> resultObject;
257 v8::Handle<v8::Function> function(v8::Function::Cast(*functionObject));
258 if (!function->IsNull()) {
259 V8Proxy* proxy = toV8Proxy(npObject);
262 OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject);
263 resultObject = proxy->callFunction(function, functionObject, argumentCount, argv.get());
265 // If we had an error, return false. The spec is a little unclear here, but says "Returns true if the method was
266 // successfully invoked". If we get an error return value, was that successfully invoked?
267 if (resultObject.IsEmpty())
270 convertV8ObjectToNPVariant(resultObject, npObject, result);
274 bool _NPN_Evaluate(NPP npp, NPObject* npObject, NPString* npScript, NPVariant* result)
276 bool popupsAllowed = PlatformSupport::popupsAllowed(npp);
277 return _NPN_EvaluateHelper(npp, popupsAllowed, npObject, npScript, result);
280 bool _NPN_EvaluateHelper(NPP npp, bool popupsAllowed, NPObject* npObject, NPString* npScript, NPVariant* result)
282 VOID_TO_NPVARIANT(*result);
286 if (npObject->_class != npScriptObjectClass)
289 v8::HandleScope handleScope;
290 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
291 if (context.IsEmpty())
294 V8Proxy* proxy = toV8Proxy(npObject);
297 v8::Context::Scope scope(context);
298 ExceptionCatcher exceptionCatcher;
300 // FIXME: Is this branch still needed after switching to using UserGestureIndicator?
303 filename = "npscript";
305 String script = String::fromUTF8(npScript->UTF8Characters, npScript->UTF8Length);
307 UserGestureIndicator gestureIndicator(popupsAllowed ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture);
308 v8::Local<v8::Value> v8result = proxy->evaluate(ScriptSourceCode(script, KURL(ParsedURLString, filename)), 0);
310 if (v8result.IsEmpty())
313 convertV8ObjectToNPVariant(v8result, npObject, result);
317 bool _NPN_GetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, NPVariant* result)
322 if (npObject->_class == npScriptObjectClass) {
323 V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
325 v8::HandleScope handleScope;
326 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
327 if (context.IsEmpty())
330 v8::Context::Scope scope(context);
331 ExceptionCatcher exceptionCatcher;
333 v8::Handle<v8::Object> obj(object->v8Object);
334 v8::Local<v8::Value> v8result = obj->Get(npIdentifierToV8Identifier(propertyName));
336 if (v8result.IsEmpty())
339 convertV8ObjectToNPVariant(v8result, npObject, result);
343 if (npObject->_class->hasProperty && npObject->_class->getProperty) {
344 if (npObject->_class->hasProperty(npObject, propertyName))
345 return npObject->_class->getProperty(npObject, propertyName, result);
348 VOID_TO_NPVARIANT(*result);
352 bool _NPN_SetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, const NPVariant* value)
357 if (npObject->_class == npScriptObjectClass) {
358 V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
360 v8::HandleScope handleScope;
361 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
362 if (context.IsEmpty())
365 v8::Context::Scope scope(context);
366 ExceptionCatcher exceptionCatcher;
368 v8::Handle<v8::Object> obj(object->v8Object);
369 obj->Set(npIdentifierToV8Identifier(propertyName),
370 convertNPVariantToV8Object(value, object->rootObject->frame()->script()->windowScriptNPObject()));
374 if (npObject->_class->setProperty)
375 return npObject->_class->setProperty(npObject, propertyName, value);
380 bool _NPN_RemoveProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
384 if (npObject->_class != npScriptObjectClass)
387 V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
389 v8::HandleScope handleScope;
390 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
391 if (context.IsEmpty())
393 v8::Context::Scope scope(context);
394 ExceptionCatcher exceptionCatcher;
396 v8::Handle<v8::Object> obj(object->v8Object);
397 // FIXME: Verify that setting to undefined is right.
398 obj->Set(npIdentifierToV8Identifier(propertyName), v8::Undefined());
402 bool _NPN_HasProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
407 if (npObject->_class == npScriptObjectClass) {
408 V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
410 v8::HandleScope handleScope;
411 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
412 if (context.IsEmpty())
414 v8::Context::Scope scope(context);
415 ExceptionCatcher exceptionCatcher;
417 v8::Handle<v8::Object> obj(object->v8Object);
418 return obj->Has(npIdentifierToV8Identifier(propertyName));
421 if (npObject->_class->hasProperty)
422 return npObject->_class->hasProperty(npObject, propertyName);
426 bool _NPN_HasMethod(NPP npp, NPObject* npObject, NPIdentifier methodName)
431 if (npObject->_class == npScriptObjectClass) {
432 V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
434 v8::HandleScope handleScope;
435 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
436 if (context.IsEmpty())
438 v8::Context::Scope scope(context);
439 ExceptionCatcher exceptionCatcher;
441 v8::Handle<v8::Object> obj(object->v8Object);
442 v8::Handle<v8::Value> prop = obj->Get(npIdentifierToV8Identifier(methodName));
443 return prop->IsFunction();
446 if (npObject->_class->hasMethod)
447 return npObject->_class->hasMethod(npObject, methodName);
451 void _NPN_SetException(NPObject* npObject, const NPUTF8 *message)
453 if (!npObject || npObject->_class != npScriptObjectClass) {
454 // We won't be able to find a proper scope for this exception, so just throw it.
455 // This is consistent with JSC, which throws a global exception all the time.
456 V8Proxy::throwError(V8Proxy::GeneralError, message);
459 v8::HandleScope handleScope;
460 v8::Handle<v8::Context> context = toV8Context(0, npObject);
461 if (context.IsEmpty())
464 v8::Context::Scope scope(context);
465 ExceptionCatcher exceptionCatcher;
467 V8Proxy::throwError(V8Proxy::GeneralError, message);
470 bool _NPN_Enumerate(NPP npp, NPObject* npObject, NPIdentifier** identifier, uint32_t* count)
475 if (npObject->_class == npScriptObjectClass) {
476 V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
478 v8::HandleScope handleScope;
479 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
480 if (context.IsEmpty())
482 v8::Context::Scope scope(context);
483 ExceptionCatcher exceptionCatcher;
485 v8::Handle<v8::Object> obj(object->v8Object);
487 // FIXME: http://b/issue?id=1210340: Use a v8::Object::Keys() method when it exists, instead of evaluating javascript.
489 // FIXME: Figure out how to cache this helper function. Run a helper function that collects the properties
490 // on the object into an array.
491 const char enumeratorCode[] =
494 " for (var prop in obj) {"
495 " props[props.length] = prop;"
499 v8::Handle<v8::String> source = v8::String::New(enumeratorCode);
500 v8::Handle<v8::Script> script = v8::Script::Compile(source, 0);
501 v8::Handle<v8::Value> enumeratorObj = script->Run();
502 v8::Handle<v8::Function> enumerator = v8::Handle<v8::Function>::Cast(enumeratorObj);
503 v8::Handle<v8::Value> argv[] = { obj };
504 v8::Local<v8::Value> propsObj = enumerator->Call(v8::Handle<v8::Object>::Cast(enumeratorObj), ARRAYSIZE_UNSAFE(argv), argv);
505 if (propsObj.IsEmpty())
508 // Convert the results into an array of NPIdentifiers.
509 v8::Handle<v8::Array> props = v8::Handle<v8::Array>::Cast(propsObj);
510 *count = props->Length();
511 *identifier = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier*) * *count));
512 for (uint32_t i = 0; i < *count; ++i) {
513 v8::Local<v8::Value> name = props->Get(v8::Integer::New(i));
514 (*identifier)[i] = getStringIdentifier(v8::Local<v8::String>::Cast(name));
519 if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate)
520 return npObject->_class->enumerate(npObject, identifier, count);
525 bool _NPN_Construct(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
530 if (npObject->_class == npScriptObjectClass) {
531 V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
533 v8::HandleScope handleScope;
534 v8::Handle<v8::Context> context = toV8Context(npp, npObject);
535 if (context.IsEmpty())
537 v8::Context::Scope scope(context);
538 ExceptionCatcher exceptionCatcher;
540 // Lookup the constructor function.
541 v8::Handle<v8::Object> ctorObj(object->v8Object);
542 if (!ctorObj->IsFunction())
545 // Call the constructor.
546 v8::Local<v8::Value> resultObject;
547 v8::Handle<v8::Function> ctor(v8::Function::Cast(*ctorObj));
548 if (!ctor->IsNull()) {
549 V8Proxy* proxy = toV8Proxy(npObject);
552 OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject);
553 resultObject = proxy->newInstance(ctor, argumentCount, argv.get());
556 if (resultObject.IsEmpty())
559 convertV8ObjectToNPVariant(resultObject, npObject, result);
563 if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npObject->_class) && npObject->_class->construct)
564 return npObject->_class->construct(npObject, arguments, argumentCount, result);