Home | History | Annotate | Download | only in v8
      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 "NPV8Object.h"
     30 
     31 #include "PlatformBridge.h"
     32 #include "DOMWindow.h"
     33 #include "Frame.h"
     34 #include "OwnArrayPtr.h"
     35 #include "PlatformString.h"
     36 #include "ScriptController.h"
     37 #include "UserGestureIndicator.h"
     38 #include "V8GCController.h"
     39 #include "V8Helpers.h"
     40 #include "V8NPUtils.h"
     41 #include "V8Proxy.h"
     42 #include "WrapperTypeInfo.h"
     43 #include "npruntime_impl.h"
     44 #include "npruntime_priv.h"
     45 
     46 #if PLATFORM(CHROMIUM)
     47 #include <bindings/npruntime.h>
     48 #else
     49 #include "npruntime.h"
     50 #endif
     51 
     52 #include <stdio.h>
     53 #include <wtf/StringExtras.h>
     54 
     55 using namespace WebCore;
     56 
     57 namespace WebCore {
     58 
     59 WrapperTypeInfo* npObjectTypeInfo()
     60 {
     61   static WrapperTypeInfo typeInfo = { 0, 0, 0, 0 };
     62     return &typeInfo;
     63 }
     64 
     65 // FIXME: Comments on why use malloc and free.
     66 static NPObject* allocV8NPObject(NPP, NPClass*)
     67 {
     68     return static_cast<NPObject*>(malloc(sizeof(V8NPObject)));
     69 }
     70 
     71 static void freeV8NPObject(NPObject* npObject)
     72 {
     73     V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
     74 #ifndef NDEBUG
     75     V8GCController::unregisterGlobalHandle(v8NpObject, v8NpObject->v8Object);
     76 #endif
     77     v8NpObject->v8Object.Dispose();
     78     free(v8NpObject);
     79 }
     80 
     81 static PassOwnArrayPtr<v8::Handle<v8::Value> > createValueListFromVariantArgs(const NPVariant* arguments, uint32_t argumentCount, NPObject* owner)
     82 {
     83     OwnArrayPtr<v8::Handle<v8::Value> > argv = adoptArrayPtr(new v8::Handle<v8::Value>[argumentCount]);
     84     for (uint32_t index = 0; index < argumentCount; index++) {
     85         const NPVariant* arg = &arguments[index];
     86         argv[index] = convertNPVariantToV8Object(arg, owner);
     87     }
     88     return argv.release();
     89 }
     90 
     91 // Create an identifier (null terminated utf8 char*) from the NPIdentifier.
     92 static v8::Local<v8::String> npIdentifierToV8Identifier(NPIdentifier name)
     93 {
     94     PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(name);
     95     if (identifier->isString)
     96         return v8::String::New(static_cast<const char*>(identifier->value.string));
     97 
     98     char buffer[32];
     99     snprintf(buffer, sizeof(buffer), "%d", identifier->value.number);
    100     return v8::String::New(buffer);
    101 }
    102 
    103 NPObject* v8ObjectToNPObject(v8::Handle<v8::Object> object)
    104 {
    105     return reinterpret_cast<NPObject*>(object->GetPointerFromInternalField(v8DOMWrapperObjectIndex));
    106 }
    107 
    108 static NPClass V8NPObjectClass = { NP_CLASS_STRUCT_VERSION,
    109                                    allocV8NPObject,
    110                                    freeV8NPObject,
    111                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    112 
    113 // NPAPI's npruntime functions.
    114 NPClass* npScriptObjectClass = &V8NPObjectClass;
    115 
    116 NPObject* npCreateV8ScriptObject(NPP npp, v8::Handle<v8::Object> object, DOMWindow* root)
    117 {
    118     // Check to see if this object is already wrapped.
    119     if (object->InternalFieldCount() == npObjectInternalFieldCount) {
    120         WrapperTypeInfo* typeInfo = static_cast<WrapperTypeInfo*>(object->GetPointerFromInternalField(v8DOMWrapperTypeIndex));
    121         if (typeInfo == npObjectTypeInfo()) {
    122 
    123             NPObject* returnValue = v8ObjectToNPObject(object);
    124             _NPN_RetainObject(returnValue);
    125             return returnValue;
    126         }
    127     }
    128 
    129     V8NPObject* v8npObject = reinterpret_cast<V8NPObject*>(_NPN_CreateObject(npp, &V8NPObjectClass));
    130     v8npObject->v8Object = v8::Persistent<v8::Object>::New(object);
    131 #ifndef NDEBUG
    132     V8GCController::registerGlobalHandle(NPOBJECT, v8npObject, v8npObject->v8Object);
    133 #endif
    134     v8npObject->rootObject = root;
    135     return reinterpret_cast<NPObject*>(v8npObject);
    136 }
    137 
    138 } // namespace WebCore
    139 
    140 bool _NPN_Invoke(NPP npp, NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
    141 {
    142     if (!npObject)
    143         return false;
    144 
    145     if (npObject->_class != npScriptObjectClass) {
    146         if (npObject->_class->invoke)
    147             return npObject->_class->invoke(npObject, methodName, arguments, argumentCount, result);
    148 
    149         VOID_TO_NPVARIANT(*result);
    150         return true;
    151     }
    152 
    153     V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
    154 
    155     PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(methodName);
    156     if (!identifier->isString)
    157         return false;
    158 
    159     if (!strcmp(identifier->value.string, "eval")) {
    160         if (argumentCount != 1)
    161             return false;
    162         if (arguments[0].type != NPVariantType_String)
    163             return false;
    164         return _NPN_Evaluate(npp, npObject, const_cast<NPString*>(&arguments[0].value.stringValue), result);
    165     }
    166 
    167     v8::HandleScope handleScope;
    168     // FIXME: should use the plugin's owner frame as the security context.
    169     v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    170     if (context.IsEmpty())
    171         return false;
    172 
    173     v8::Context::Scope scope(context);
    174     ExceptionCatcher exceptionCatcher;
    175 
    176     v8::Handle<v8::Value> functionObject = v8NpObject->v8Object->Get(v8::String::New(identifier->value.string));
    177     if (functionObject.IsEmpty() || functionObject->IsNull()) {
    178         NULL_TO_NPVARIANT(*result);
    179         return false;
    180     }
    181     if (functionObject->IsUndefined()) {
    182         VOID_TO_NPVARIANT(*result);
    183         return false;
    184     }
    185 
    186     V8Proxy* proxy = toV8Proxy(npObject);
    187     ASSERT(proxy);
    188 
    189     // Call the function object.
    190     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(functionObject);
    191     OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject);
    192     v8::Local<v8::Value> resultObject = proxy->callFunction(function, v8NpObject->v8Object, argumentCount, argv.get());
    193 
    194     // If we had an error, return false.  The spec is a little unclear here, but says "Returns true if the method was
    195     // successfully invoked".  If we get an error return value, was that successfully invoked?
    196     if (resultObject.IsEmpty())
    197         return false;
    198 
    199     convertV8ObjectToNPVariant(resultObject, npObject, result);
    200     return true;
    201 }
    202 
    203 // FIXME: Fix it same as _NPN_Invoke (HandleScope and such).
    204 bool _NPN_InvokeDefault(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
    205 {
    206     if (!npObject)
    207         return false;
    208 
    209     if (npObject->_class != npScriptObjectClass) {
    210         if (npObject->_class->invokeDefault)
    211             return npObject->_class->invokeDefault(npObject, arguments, argumentCount, result);
    212 
    213         VOID_TO_NPVARIANT(*result);
    214         return true;
    215     }
    216 
    217     V8NPObject* v8NpObject = reinterpret_cast<V8NPObject*>(npObject);
    218 
    219     VOID_TO_NPVARIANT(*result);
    220 
    221     v8::HandleScope handleScope;
    222     v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    223     if (context.IsEmpty())
    224         return false;
    225 
    226     v8::Context::Scope scope(context);
    227     ExceptionCatcher exceptionCatcher;
    228 
    229     // Lookup the function object and call it.
    230     v8::Handle<v8::Object> functionObject(v8NpObject->v8Object);
    231     if (!functionObject->IsFunction())
    232         return false;
    233 
    234     v8::Local<v8::Value> resultObject;
    235     v8::Handle<v8::Function> function(v8::Function::Cast(*functionObject));
    236     if (!function->IsNull()) {
    237         V8Proxy* proxy = toV8Proxy(npObject);
    238         ASSERT(proxy);
    239 
    240         OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject);
    241         resultObject = proxy->callFunction(function, functionObject, argumentCount, argv.get());
    242     }
    243     // If we had an error, return false.  The spec is a little unclear here, but says "Returns true if the method was
    244     // successfully invoked".  If we get an error return value, was that successfully invoked?
    245     if (resultObject.IsEmpty())
    246         return false;
    247 
    248     convertV8ObjectToNPVariant(resultObject, npObject, result);
    249     return true;
    250 }
    251 
    252 bool _NPN_Evaluate(NPP npp, NPObject* npObject, NPString* npScript, NPVariant* result)
    253 {
    254     bool popupsAllowed = PlatformBridge::popupsAllowed(npp);
    255     return _NPN_EvaluateHelper(npp, popupsAllowed, npObject, npScript, result);
    256 }
    257 
    258 bool _NPN_EvaluateHelper(NPP npp, bool popupsAllowed, NPObject* npObject, NPString* npScript, NPVariant* result)
    259 {
    260     VOID_TO_NPVARIANT(*result);
    261     if (!npObject)
    262         return false;
    263 
    264     if (npObject->_class != npScriptObjectClass)
    265         return false;
    266 
    267     v8::HandleScope handleScope;
    268     v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    269     if (context.IsEmpty())
    270         return false;
    271 
    272     V8Proxy* proxy = toV8Proxy(npObject);
    273     ASSERT(proxy);
    274 
    275     v8::Context::Scope scope(context);
    276     ExceptionCatcher exceptionCatcher;
    277 
    278     String filename;
    279     if (!popupsAllowed)
    280         filename = "npscript";
    281 
    282     // Set popupsAllowed flag to the current execution frame, so WebKit can get
    283     // right gesture status for popups initiated from plugins.
    284     Frame* frame = proxy->frame();
    285     ASSERT(frame);
    286     bool oldAllowPopups = frame->script()->allowPopupsFromPlugin();
    287     frame->script()->setAllowPopupsFromPlugin(popupsAllowed);
    288 
    289     String script = String::fromUTF8(npScript->UTF8Characters, npScript->UTF8Length);
    290     v8::Local<v8::Value> v8result = proxy->evaluate(ScriptSourceCode(script, KURL(ParsedURLString, filename)), 0);
    291     // Restore the old flag.
    292     frame->script()->setAllowPopupsFromPlugin(oldAllowPopups);
    293 
    294     if (v8result.IsEmpty())
    295         return false;
    296 
    297     convertV8ObjectToNPVariant(v8result, npObject, result);
    298     return true;
    299 }
    300 
    301 bool _NPN_GetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, NPVariant* result)
    302 {
    303     if (!npObject)
    304         return false;
    305 
    306     if (npObject->_class == npScriptObjectClass) {
    307         V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
    308 
    309         v8::HandleScope handleScope;
    310         v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    311         if (context.IsEmpty())
    312             return false;
    313 
    314         v8::Context::Scope scope(context);
    315         ExceptionCatcher exceptionCatcher;
    316 
    317         v8::Handle<v8::Object> obj(object->v8Object);
    318         v8::Local<v8::Value> v8result = obj->Get(npIdentifierToV8Identifier(propertyName));
    319 
    320         if (v8result.IsEmpty())
    321             return false;
    322 
    323         convertV8ObjectToNPVariant(v8result, npObject, result);
    324         return true;
    325     }
    326 
    327     if (npObject->_class->hasProperty && npObject->_class->getProperty) {
    328         if (npObject->_class->hasProperty(npObject, propertyName))
    329             return npObject->_class->getProperty(npObject, propertyName, result);
    330     }
    331 
    332     VOID_TO_NPVARIANT(*result);
    333     return false;
    334 }
    335 
    336 bool _NPN_SetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, const NPVariant* value)
    337 {
    338     if (!npObject)
    339         return false;
    340 
    341     if (npObject->_class == npScriptObjectClass) {
    342         V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
    343 
    344         v8::HandleScope handleScope;
    345         v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    346         if (context.IsEmpty())
    347             return false;
    348 
    349         v8::Context::Scope scope(context);
    350         ExceptionCatcher exceptionCatcher;
    351 
    352         v8::Handle<v8::Object> obj(object->v8Object);
    353         obj->Set(npIdentifierToV8Identifier(propertyName),
    354                  convertNPVariantToV8Object(value, object->rootObject->frame()->script()->windowScriptNPObject()));
    355         return true;
    356     }
    357 
    358     if (npObject->_class->setProperty)
    359         return npObject->_class->setProperty(npObject, propertyName, value);
    360 
    361     return false;
    362 }
    363 
    364 bool _NPN_RemoveProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
    365 {
    366     if (!npObject)
    367         return false;
    368     if (npObject->_class != npScriptObjectClass)
    369         return false;
    370 
    371     V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
    372 
    373     v8::HandleScope handleScope;
    374     v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    375     if (context.IsEmpty())
    376         return false;
    377     v8::Context::Scope scope(context);
    378     ExceptionCatcher exceptionCatcher;
    379 
    380     v8::Handle<v8::Object> obj(object->v8Object);
    381     // FIXME: Verify that setting to undefined is right.
    382     obj->Set(npIdentifierToV8Identifier(propertyName), v8::Undefined());
    383     return true;
    384 }
    385 
    386 bool _NPN_HasProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
    387 {
    388     if (!npObject)
    389         return false;
    390 
    391     if (npObject->_class == npScriptObjectClass) {
    392         V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
    393 
    394         v8::HandleScope handleScope;
    395         v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    396         if (context.IsEmpty())
    397             return false;
    398         v8::Context::Scope scope(context);
    399         ExceptionCatcher exceptionCatcher;
    400 
    401         v8::Handle<v8::Object> obj(object->v8Object);
    402         return obj->Has(npIdentifierToV8Identifier(propertyName));
    403     }
    404 
    405     if (npObject->_class->hasProperty)
    406         return npObject->_class->hasProperty(npObject, propertyName);
    407     return false;
    408 }
    409 
    410 bool _NPN_HasMethod(NPP npp, NPObject* npObject, NPIdentifier methodName)
    411 {
    412     if (!npObject)
    413         return false;
    414 
    415     if (npObject->_class == npScriptObjectClass) {
    416         V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
    417 
    418         v8::HandleScope handleScope;
    419         v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    420         if (context.IsEmpty())
    421             return false;
    422         v8::Context::Scope scope(context);
    423         ExceptionCatcher exceptionCatcher;
    424 
    425         v8::Handle<v8::Object> obj(object->v8Object);
    426         v8::Handle<v8::Value> prop = obj->Get(npIdentifierToV8Identifier(methodName));
    427         return prop->IsFunction();
    428     }
    429 
    430     if (npObject->_class->hasMethod)
    431         return npObject->_class->hasMethod(npObject, methodName);
    432     return false;
    433 }
    434 
    435 void _NPN_SetException(NPObject* npObject, const NPUTF8 *message)
    436 {
    437     if (!npObject || npObject->_class != npScriptObjectClass) {
    438         // We won't be able to find a proper scope for this exception, so just throw it.
    439         // This is consistent with JSC, which throws a global exception all the time.
    440 #if PLATFORM(ANDROID)
    441         // However, if there isn't a v8 context, throw the error away as there really isn't anything useful to do with it.
    442         if (v8::Context::InContext())
    443             V8Proxy::throwError(V8Proxy::GeneralError, message);
    444 #endif
    445         return;
    446     }
    447     v8::HandleScope handleScope;
    448     v8::Handle<v8::Context> context = toV8Context(0, npObject);
    449     if (context.IsEmpty())
    450         return;
    451 
    452     v8::Context::Scope scope(context);
    453     ExceptionCatcher exceptionCatcher;
    454 
    455     V8Proxy::throwError(V8Proxy::GeneralError, message);
    456 }
    457 
    458 bool _NPN_Enumerate(NPP npp, NPObject* npObject, NPIdentifier** identifier, uint32_t* count)
    459 {
    460     if (!npObject)
    461         return false;
    462 
    463     if (npObject->_class == npScriptObjectClass) {
    464         V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
    465 
    466         v8::HandleScope handleScope;
    467         v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    468         if (context.IsEmpty())
    469             return false;
    470         v8::Context::Scope scope(context);
    471         ExceptionCatcher exceptionCatcher;
    472 
    473         v8::Handle<v8::Object> obj(object->v8Object);
    474 
    475         // FIXME: http://b/issue?id=1210340: Use a v8::Object::Keys() method when it exists, instead of evaluating javascript.
    476 
    477         // FIXME: Figure out how to cache this helper function.  Run a helper function that collects the properties
    478         // on the object into an array.
    479         const char enumeratorCode[] =
    480             "(function (obj) {"
    481             "  var props = [];"
    482             "  for (var prop in obj) {"
    483             "    props[props.length] = prop;"
    484             "  }"
    485             "  return props;"
    486             "});";
    487         v8::Handle<v8::String> source = v8::String::New(enumeratorCode);
    488         v8::Handle<v8::Script> script = v8::Script::Compile(source, 0);
    489         v8::Handle<v8::Value> enumeratorObj = script->Run();
    490         v8::Handle<v8::Function> enumerator = v8::Handle<v8::Function>::Cast(enumeratorObj);
    491         v8::Handle<v8::Value> argv[] = { obj };
    492         v8::Local<v8::Value> propsObj = enumerator->Call(v8::Handle<v8::Object>::Cast(enumeratorObj), ARRAYSIZE_UNSAFE(argv), argv);
    493         if (propsObj.IsEmpty())
    494             return false;
    495 
    496         // Convert the results into an array of NPIdentifiers.
    497         v8::Handle<v8::Array> props = v8::Handle<v8::Array>::Cast(propsObj);
    498         *count = props->Length();
    499         *identifier = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier*) * *count));
    500         for (uint32_t i = 0; i < *count; ++i) {
    501             v8::Local<v8::Value> name = props->Get(v8::Integer::New(i));
    502             (*identifier)[i] = getStringIdentifier(v8::Local<v8::String>::Cast(name));
    503         }
    504         return true;
    505     }
    506 
    507     if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate)
    508        return npObject->_class->enumerate(npObject, identifier, count);
    509 
    510     return false;
    511 }
    512 
    513 bool _NPN_Construct(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
    514 {
    515     if (!npObject)
    516         return false;
    517 
    518     if (npObject->_class == npScriptObjectClass) {
    519         V8NPObject* object = reinterpret_cast<V8NPObject*>(npObject);
    520 
    521         v8::HandleScope handleScope;
    522         v8::Handle<v8::Context> context = toV8Context(npp, npObject);
    523         if (context.IsEmpty())
    524             return false;
    525         v8::Context::Scope scope(context);
    526         ExceptionCatcher exceptionCatcher;
    527 
    528         // Lookup the constructor function.
    529         v8::Handle<v8::Object> ctorObj(object->v8Object);
    530         if (!ctorObj->IsFunction())
    531             return false;
    532 
    533         // Call the constructor.
    534         v8::Local<v8::Value> resultObject;
    535         v8::Handle<v8::Function> ctor(v8::Function::Cast(*ctorObj));
    536         if (!ctor->IsNull()) {
    537             V8Proxy* proxy = toV8Proxy(npObject);
    538             ASSERT(proxy);
    539 
    540             OwnArrayPtr<v8::Handle<v8::Value> > argv = createValueListFromVariantArgs(arguments, argumentCount, npObject);
    541             resultObject = proxy->newInstance(ctor, argumentCount, argv.get());
    542         }
    543 
    544         if (resultObject.IsEmpty())
    545             return false;
    546 
    547         convertV8ObjectToNPVariant(resultObject, npObject, result);
    548         return true;
    549     }
    550 
    551     if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npObject->_class) && npObject->_class->construct)
    552         return npObject->_class->construct(npObject, arguments, argumentCount, result);
    553 
    554     return false;
    555 }
    556