1/*
2 * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
3 * Copyright (C) 2007, 2008, 2009 Google, Inc.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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.
25 */
26
27#include "config.h"
28
29#include "bindings/v8/NPV8Object.h"
30
31#include "bindings/v8/ScriptController.h"
32#include "bindings/v8/ScriptSourceCode.h"
33#include "bindings/v8/V8Binding.h"
34#include "bindings/v8/V8GCController.h"
35#include "bindings/v8/V8NPUtils.h"
36#include "bindings/v8/V8ObjectConstructor.h"
37#include "bindings/v8/V8ScriptRunner.h"
38#include "bindings/v8/WrapperTypeInfo.h"
39#include "bindings/v8/npruntime_impl.h"
40#include "bindings/v8/npruntime_priv.h"
41#include "core/dom/UserGestureIndicator.h"
42#include "core/page/DOMWindow.h"
43#include "core/page/Frame.h"
44#include "wtf/OwnArrayPtr.h"
45
46#include <stdio.h>
47#include "wtf/StringExtras.h"
48#include "wtf/text/WTFString.h"
49
50using namespace WebCore;
51
52namespace WebCore {
53
54WrapperTypeInfo* npObjectTypeInfo()
55{
56    static WrapperTypeInfo typeInfo = { 0, 0, 0, 0, 0, 0, 0, WrapperTypeObjectPrototype };
57    return &typeInfo;
58}
59
60// FIXME: Comments on why use malloc and free.
61static NPObject* allocV8NPObject(NPP, NPClass*)
62{
63    return static_cast<NPObject*>(malloc(sizeof(V8NPObject)));
64}
65
66static void freeV8NPObject(NPObject* npObject)
67{
68    V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
69    disposeUnderlyingV8Object(npObject);
70    free(v8NpObject);
71}
72
73static NPClass V8NPObjectClass = {
74    NP_CLASS_STRUCT_VERSION,
75    allocV8NPObject,
76    freeV8NPObject,
77    0, 0, 0, 0, 0, 0, 0, 0, 0, 0
78};
79
80static v8::Local<v8::Context> toV8Context(NPP npp, NPObject* npObject)
81{
82    ASSERT(npObject->_class == &V8NPObjectClass);
83    V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
84    DOMWindow* window = object->rootObject;
85    if (!window || !window->isCurrentlyDisplayedInFrame())
86        return v8::Local<v8::Context>();
87    return ScriptController::mainWorldContext(object->rootObject->frame());
88}
89
90static PassOwnArrayPtr<v8::Handle<v8::Value> > createValueListFromVariantArgs(const NPVariant* arguments, uint32_t argumentCount, NPObject* owner, v8::Isolate* isolate)
91{
92    OwnArrayPtr<v8::Handle<v8::Value> > argv = adoptArrayPtr(new v8::Handle<v8::Value>[argumentCount]);
93    for (uint32_t index = 0; index < argumentCount; index++) {
94        const NPVariant* arg = &arguments[index];
95        argv[index] = convertNPVariantToV8Object(arg, owner, isolate);
96    }
97    return argv.release();
98}
99
100// Create an identifier (null terminated utf8 char*) from the NPIdentifier.
101static v8::Local<v8::String> npIdentifierToV8Identifier(NPIdentifier name)
102{
103    PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(name);
104    if (identifier->isString)
105        return v8::String::NewSymbol(static_cast<const char*>(identifier->value.string));
106
107    char buffer[32];
108    snprintf(buffer, sizeof(buffer), "%d", identifier->value.number);
109    return v8::String::NewSymbol(buffer);
110}
111
112NPObject* v8ObjectToNPObject(v8::Handle<v8::Object> object)
113{
114    return reinterpret_cast<NPObject*>(object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex));
115}
116
117NPObject* npCreateV8ScriptObject(NPP npp, v8::Handle<v8::Object> object, DOMWindow* root)
118{
119    // Check to see if this object is already wrapped.
120    if (object->InternalFieldCount() == npObjectInternalFieldCount) {
121        WrapperTypeInfo* typeInfo = static_cast<WrapperTypeInfo*>(object->GetAlignedPointerFromInternalField(v8DOMWrapperTypeIndex));
122        if (typeInfo == npObjectTypeInfo()) {
123            NPObject* returnValue = v8ObjectToNPObject(object);
124            _NPN_RetainObject(returnValue);
125            return returnValue;
126        }
127    }
128
129    V8NPObjectVector* objectVector = 0;
130    if (V8PerContextData* perContextData = V8PerContextData::from(object->CreationContext())) {
131        int v8ObjectHash = object->GetIdentityHash();
132        ASSERT(v8ObjectHash);
133        V8NPObjectMap* v8NPObjectMap = perContextData->v8NPObjectMap();
134        V8NPObjectMap::iterator iter = v8NPObjectMap->find(v8ObjectHash);
135        if (iter != v8NPObjectMap->end()) {
136            V8NPObjectVector& objects = iter->value;
137            for (size_t index = 0; index < objects.size(); ++index) {
138                V8NPObject* v8npObject = objects.at(index);
139                if (v8npObject->v8Object == object && v8npObject->rootObject == root) {
140                    _NPN_RetainObject(&v8npObject->object);
141                    return reinterpret_cast<NPObject*>(v8npObject);
142                }
143            }
144        } else {
145            iter = v8NPObjectMap->set(v8ObjectHash, V8NPObjectVector()).iterator;
146        }
147        objectVector = &iter->value;
148    }
149
150    V8NPObject* v8npObject = reinterpret_cast<V8NPObject*>(_NPN_CreateObject(npp, &V8NPObjectClass));
151    // This is uninitialized memory, we need to clear it so that
152    // Persistent::Reset won't try to Dispose anything bogus.
153    v8npObject->v8Object.Clear();
154    v8npObject->v8Object.Reset(v8::Isolate::GetCurrent(), object);
155    v8npObject->rootObject = root;
156
157    if (objectVector)
158        objectVector->append(v8npObject);
159
160    return reinterpret_cast<NPObject*>(v8npObject);
161}
162
163V8NPObject* npObjectToV8NPObject(NPObject* npObject)
164{
165    if (npObject->_class != &V8NPObjectClass)
166        return 0;
167    V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
168    if (v8NpObject->v8Object.IsEmpty())
169        return 0;
170    return v8NpObject;
171}
172
173void disposeUnderlyingV8Object(NPObject* npObject)
174{
175    ASSERT(npObject);
176    V8NPObject* v8NpObject = npObjectToV8NPObject(npObject);
177    if (!v8NpObject)
178        return;
179    v8::Isolate* isolate = v8::Isolate::GetCurrent();
180    v8::HandleScope scope(isolate);
181    v8::Handle<v8::Object> v8Object = v8::Local<v8::Object>::New(isolate, v8NpObject->v8Object);
182    ASSERT(!v8Object->CreationContext().IsEmpty());
183    if (V8PerContextData* perContextData = V8PerContextData::from(v8Object->CreationContext())) {
184        V8NPObjectMap* v8NPObjectMap = perContextData->v8NPObjectMap();
185        int v8ObjectHash = v8Object->GetIdentityHash();
186        ASSERT(v8ObjectHash);
187        V8NPObjectMap::iterator iter = v8NPObjectMap->find(v8ObjectHash);
188        if (iter != v8NPObjectMap->end()) {
189            V8NPObjectVector& objects = iter->value;
190            for (size_t index = 0; index < objects.size(); ++index) {
191                if (objects.at(index) == v8NpObject) {
192                    objects.remove(index);
193                    break;
194                }
195            }
196            if (objects.isEmpty())
197                v8NPObjectMap->remove(v8ObjectHash);
198        }
199    }
200    v8NpObject->v8Object.Dispose();
201    v8NpObject->v8Object.Clear();
202    v8NpObject->rootObject = 0;
203}
204
205} // namespace WebCore
206
207bool _NPN_Invoke(NPP npp, NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
208{
209    if (!npObject)
210        return false;
211
212    v8::Isolate* isolate = v8::Isolate::GetCurrent();
213
214    V8NPObject* v8NpObject = npObjectToV8NPObject(npObject);
215    if (!v8NpObject) {
216        if (npObject->_class->invoke)
217            return npObject->_class->invoke(npObject, methodName, arguments, argumentCount, result);
218
219        VOID_TO_NPVARIANT(*result);
220        return true;
221    }
222
223    PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(methodName);
224    if (!identifier->isString)
225        return false;
226
227    if (!strcmp(identifier->value.string, "eval")) {
228        if (argumentCount != 1)
229            return false;
230        if (arguments[0].type != NPVariantType_String)
231            return false;
232        return _NPN_Evaluate(npp, npObject, const_cast<NPString*>(&arguments[0].value.stringValue), result);
233    }
234
235    v8::HandleScope handleScope(isolate);
236    // FIXME: should use the plugin's owner frame as the security context.
237    v8::Handle<v8::Context> context = toV8Context(npp, npObject);
238    if (context.IsEmpty())
239        return false;
240
241    v8::Context::Scope scope(context);
242    ExceptionCatcher exceptionCatcher;
243
244    v8::Handle<v8::Object> v8Object = v8::Local<v8::Object>::New(isolate, v8NpObject->v8Object);
245    v8::Handle<v8::Value> functionObject = v8Object->Get(v8::String::NewSymbol(identifier->value.string));
246    if (functionObject.IsEmpty() || functionObject->IsNull()) {
247        NULL_TO_NPVARIANT(*result);
248        return false;
249    }
250    if (functionObject->IsUndefined()) {
251        VOID_TO_NPVARIANT(*result);
252        return false;
253    }
254
255    Frame* frame = v8NpObject->rootObject->frame();
256    ASSERT(frame);
257
258    // Call the function object.
259    v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(functionObject);
260    OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject, isolate);
261    v8::Local<v8::Value> resultObject = frame->script()->callFunction(function, v8Object, argumentCount, argv.get());
262
263    // If we had an error, return false.  The spec is a little unclear here, but says "Returns true if the method was
264    // successfully invoked".  If we get an error return value, was that successfully invoked?
265    if (resultObject.IsEmpty())
266        return false;
267
268    convertV8ObjectToNPVariant(resultObject, npObject, result);
269    return true;
270}
271
272// FIXME: Fix it same as _NPN_Invoke (HandleScope and such).
273bool _NPN_InvokeDefault(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
274{
275    if (!npObject)
276        return false;
277
278    v8::Isolate* isolate = v8::Isolate::GetCurrent();
279
280    V8NPObject* v8NpObject = npObjectToV8NPObject(npObject);
281    if (!v8NpObject) {
282        if (npObject->_class->invokeDefault)
283            return npObject->_class->invokeDefault(npObject, arguments, argumentCount, result);
284
285        VOID_TO_NPVARIANT(*result);
286        return true;
287    }
288
289    VOID_TO_NPVARIANT(*result);
290
291    v8::HandleScope handleScope(isolate);
292    v8::Handle<v8::Context> context = toV8Context(npp, npObject);
293    if (context.IsEmpty())
294        return false;
295
296    v8::Context::Scope scope(context);
297    ExceptionCatcher exceptionCatcher;
298
299    // Lookup the function object and call it.
300    v8::Local<v8::Object> functionObject = v8::Local<v8::Object>::New(isolate, v8NpObject->v8Object);
301    if (!functionObject->IsFunction())
302        return false;
303
304    v8::Local<v8::Value> resultObject;
305    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(functionObject);
306    if (!function->IsNull()) {
307        Frame* frame = v8NpObject->rootObject->frame();
308        ASSERT(frame);
309
310        OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject, isolate);
311        resultObject = frame->script()->callFunction(function, functionObject, argumentCount, argv.get());
312    }
313    // If we had an error, return false.  The spec is a little unclear here, but says "Returns true if the method was
314    // successfully invoked".  If we get an error return value, was that successfully invoked?
315    if (resultObject.IsEmpty())
316        return false;
317
318    convertV8ObjectToNPVariant(resultObject, npObject, result);
319    return true;
320}
321
322bool _NPN_Evaluate(NPP npp, NPObject* npObject, NPString* npScript, NPVariant* result)
323{
324    // FIXME: Give the embedder a way to control this.
325    bool popupsAllowed = false;
326    return _NPN_EvaluateHelper(npp, popupsAllowed, npObject, npScript, result);
327}
328
329bool _NPN_EvaluateHelper(NPP npp, bool popupsAllowed, NPObject* npObject, NPString* npScript, NPVariant* result)
330{
331    VOID_TO_NPVARIANT(*result);
332    if (!npObject)
333        return false;
334
335    V8NPObject* v8NpObject = npObjectToV8NPObject(npObject);
336    if (!v8NpObject)
337        return false;
338
339    v8::HandleScope handleScope;
340    v8::Handle<v8::Context> context = toV8Context(npp, npObject);
341    if (context.IsEmpty())
342        return false;
343
344    v8::Context::Scope scope(context);
345    ExceptionCatcher exceptionCatcher;
346
347    // FIXME: Is this branch still needed after switching to using UserGestureIndicator?
348    String filename;
349    if (!popupsAllowed)
350        filename = "npscript";
351
352    Frame* frame = v8NpObject->rootObject->frame();
353    ASSERT(frame);
354
355    String script = String::fromUTF8(npScript->UTF8Characters, npScript->UTF8Length);
356
357    UserGestureIndicator gestureIndicator(popupsAllowed ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture);
358    v8::Local<v8::Value> v8result = frame->script()->compileAndRunScript(ScriptSourceCode(script, KURL(ParsedURLString, filename)));
359
360    if (v8result.IsEmpty())
361        return false;
362
363    if (_NPN_IsAlive(npObject))
364        convertV8ObjectToNPVariant(v8result, npObject, result);
365    return true;
366}
367
368bool _NPN_GetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, NPVariant* result)
369{
370    if (!npObject)
371        return false;
372
373    if (V8NPObject* object = npObjectToV8NPObject(npObject)) {
374        v8::Isolate* isolate = v8::Isolate::GetCurrent();
375        v8::HandleScope handleScope(isolate);
376        v8::Handle<v8::Context> context = toV8Context(npp, npObject);
377        if (context.IsEmpty())
378            return false;
379
380        v8::Context::Scope scope(context);
381        ExceptionCatcher exceptionCatcher;
382
383        v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(isolate, object->v8Object);
384        v8::Local<v8::Value> v8result = obj->Get(npIdentifierToV8Identifier(propertyName));
385
386        if (v8result.IsEmpty())
387            return false;
388
389        convertV8ObjectToNPVariant(v8result, npObject, result);
390        return true;
391    }
392
393    if (npObject->_class->hasProperty && npObject->_class->getProperty) {
394        if (npObject->_class->hasProperty(npObject, propertyName))
395            return npObject->_class->getProperty(npObject, propertyName, result);
396    }
397
398    VOID_TO_NPVARIANT(*result);
399    return false;
400}
401
402bool _NPN_SetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, const NPVariant* value)
403{
404    if (!npObject)
405        return false;
406
407    if (V8NPObject* object = npObjectToV8NPObject(npObject)) {
408        v8::Isolate* isolate = v8::Isolate::GetCurrent();
409        v8::HandleScope handleScope(isolate);
410        v8::Handle<v8::Context> context = toV8Context(npp, npObject);
411        if (context.IsEmpty())
412            return false;
413
414        v8::Context::Scope scope(context);
415        ExceptionCatcher exceptionCatcher;
416
417        v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(isolate, object->v8Object);
418        obj->Set(npIdentifierToV8Identifier(propertyName), convertNPVariantToV8Object(value, object->rootObject->frame()->script()->windowScriptNPObject(), context->GetIsolate()));
419        return true;
420    }
421
422    if (npObject->_class->setProperty)
423        return npObject->_class->setProperty(npObject, propertyName, value);
424
425    return false;
426}
427
428bool _NPN_RemoveProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
429{
430    if (!npObject)
431        return false;
432
433    V8NPObject* object = npObjectToV8NPObject(npObject);
434    if (!object)
435        return false;
436
437    v8::Isolate* isolate = v8::Isolate::GetCurrent();
438    v8::HandleScope handleScope(isolate);
439    v8::Handle<v8::Context> context = toV8Context(npp, npObject);
440    if (context.IsEmpty())
441        return false;
442    v8::Context::Scope scope(context);
443    ExceptionCatcher exceptionCatcher;
444
445    v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(isolate, object->v8Object);
446    // FIXME: Verify that setting to undefined is right.
447    obj->Set(npIdentifierToV8Identifier(propertyName), v8::Undefined());
448    return true;
449}
450
451bool _NPN_HasProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
452{
453    if (!npObject)
454        return false;
455
456    if (V8NPObject* object = npObjectToV8NPObject(npObject)) {
457        v8::Isolate* isolate = v8::Isolate::GetCurrent();
458        v8::HandleScope handleScope(isolate);
459        v8::Handle<v8::Context> context = toV8Context(npp, npObject);
460        if (context.IsEmpty())
461            return false;
462        v8::Context::Scope scope(context);
463        ExceptionCatcher exceptionCatcher;
464
465        v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(isolate, object->v8Object);
466        return obj->Has(npIdentifierToV8Identifier(propertyName));
467    }
468
469    if (npObject->_class->hasProperty)
470        return npObject->_class->hasProperty(npObject, propertyName);
471    return false;
472}
473
474bool _NPN_HasMethod(NPP npp, NPObject* npObject, NPIdentifier methodName)
475{
476    if (!npObject)
477        return false;
478
479    if (V8NPObject* object = npObjectToV8NPObject(npObject)) {
480        v8::Isolate* isolate = v8::Isolate::GetCurrent();
481        v8::HandleScope handleScope(isolate);
482        v8::Handle<v8::Context> context = toV8Context(npp, npObject);
483        if (context.IsEmpty())
484            return false;
485        v8::Context::Scope scope(context);
486        ExceptionCatcher exceptionCatcher;
487
488        v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(isolate, object->v8Object);
489        v8::Handle<v8::Value> prop = obj->Get(npIdentifierToV8Identifier(methodName));
490        return prop->IsFunction();
491    }
492
493    if (npObject->_class->hasMethod)
494        return npObject->_class->hasMethod(npObject, methodName);
495    return false;
496}
497
498void _NPN_SetException(NPObject* npObject, const NPUTF8 *message)
499{
500    if (!npObject || !npObjectToV8NPObject(npObject)) {
501        // We won't be able to find a proper scope for this exception, so just throw it.
502        // This is consistent with JSC, which throws a global exception all the time.
503        throwError(v8GeneralError, message, v8::Isolate::GetCurrent());
504        return;
505    }
506
507    v8::HandleScope handleScope;
508    v8::Handle<v8::Context> context = toV8Context(0, npObject);
509    if (context.IsEmpty())
510        return;
511
512    v8::Context::Scope scope(context);
513    ExceptionCatcher exceptionCatcher;
514
515    throwError(v8GeneralError, message, context->GetIsolate());
516}
517
518bool _NPN_Enumerate(NPP npp, NPObject* npObject, NPIdentifier** identifier, uint32_t* count)
519{
520    if (!npObject)
521        return false;
522
523    if (V8NPObject* object = npObjectToV8NPObject(npObject)) {
524        v8::Isolate* isolate = v8::Isolate::GetCurrent();
525        v8::HandleScope handleScope(isolate);
526        v8::Local<v8::Context> context = toV8Context(npp, npObject);
527        if (context.IsEmpty())
528            return false;
529        v8::Context::Scope scope(context);
530        ExceptionCatcher exceptionCatcher;
531
532        v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(isolate, object->v8Object);
533
534        // FIXME: http://b/issue?id=1210340: Use a v8::Object::Keys() method when it exists, instead of evaluating javascript.
535
536        // FIXME: Figure out how to cache this helper function.  Run a helper function that collects the properties
537        // on the object into an array.
538        const char enumeratorCode[] =
539            "(function (obj) {"
540            "  var props = [];"
541            "  for (var prop in obj) {"
542            "    props[props.length] = prop;"
543            "  }"
544            "  return props;"
545            "});";
546        v8::Handle<v8::String> source = v8::String::New(enumeratorCode);
547        v8::Handle<v8::Value> result = V8ScriptRunner::compileAndRunInternalScript(source, context->GetIsolate());
548        ASSERT(!result.IsEmpty());
549        ASSERT(result->IsFunction());
550        v8::Handle<v8::Function> enumerator = v8::Handle<v8::Function>::Cast(result);
551        v8::Handle<v8::Value> argv[] = { obj };
552        v8::Local<v8::Value> propsObj = V8ScriptRunner::callInternalFunction(enumerator, v8::Handle<v8::Object>::Cast(result), WTF_ARRAY_LENGTH(argv), argv, context->GetIsolate());
553        if (propsObj.IsEmpty())
554            return false;
555
556        // Convert the results into an array of NPIdentifiers.
557        v8::Handle<v8::Array> props = v8::Handle<v8::Array>::Cast(propsObj);
558        *count = props->Length();
559        *identifier = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier*) * *count));
560        for (uint32_t i = 0; i < *count; ++i) {
561            v8::Local<v8::Value> name = props->Get(v8::Integer::New(i, context->GetIsolate()));
562            (*identifier)[i] = getStringIdentifier(v8::Local<v8::String>::Cast(name));
563        }
564        return true;
565    }
566
567    if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate)
568       return npObject->_class->enumerate(npObject, identifier, count);
569
570    return false;
571}
572
573bool _NPN_Construct(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
574{
575    if (!npObject)
576        return false;
577
578    v8::Isolate* isolate = v8::Isolate::GetCurrent();
579
580    if (V8NPObject* object = npObjectToV8NPObject(npObject)) {
581        v8::HandleScope handleScope(isolate);
582        v8::Handle<v8::Context> context = toV8Context(npp, npObject);
583        if (context.IsEmpty())
584            return false;
585        v8::Context::Scope scope(context);
586        ExceptionCatcher exceptionCatcher;
587
588        // Lookup the constructor function.
589        v8::Handle<v8::Object> ctorObj = v8::Local<v8::Object>::New(isolate, object->v8Object);
590        if (!ctorObj->IsFunction())
591            return false;
592
593        // Call the constructor.
594        v8::Local<v8::Value> resultObject;
595        v8::Handle<v8::Function> ctor = v8::Handle<v8::Function>::Cast(ctorObj);
596        if (!ctor->IsNull()) {
597            Frame* frame = object->rootObject->frame();
598            ASSERT(frame);
599            OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject, isolate);
600            resultObject = V8ObjectConstructor::newInstanceInDocument(ctor, argumentCount, argv.get(), frame ? frame->document() : 0);
601        }
602
603        if (resultObject.IsEmpty())
604            return false;
605
606        convertV8ObjectToNPVariant(resultObject, npObject, result);
607        return true;
608    }
609
610    if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npObject->_class) && npObject->_class->construct)
611        return npObject->_class->construct(npObject, arguments, argumentCount, result);
612
613    return false;
614}
615