diff options
Diffstat (limited to 'Source/WebCore/bridge')
89 files changed, 16351 insertions, 0 deletions
diff --git a/Source/WebCore/bridge/Bridge.h b/Source/WebCore/bridge/Bridge.h new file mode 100644 index 0000000..50efc64 --- /dev/null +++ b/Source/WebCore/bridge/Bridge.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2003, 2008, 2009 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Bridge_h +#define Bridge_h + +#include "BridgeJSC.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + +namespace Bindings { + +class Method : public Noncopyable { +public: + virtual int numParameters() const = 0; + + virtual ~Method() { } +}; + +} // namespace Bindings + +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/IdentifierRep.cpp b/Source/WebCore/bridge/IdentifierRep.cpp new file mode 100644 index 0000000..fed47ca --- /dev/null +++ b/Source/WebCore/bridge/IdentifierRep.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IdentifierRep.h" +#include "JSDOMBinding.h" + +#include "PlatformString.h" +#include <runtime/UString.h> +#include <wtf/HashMap.h> +#include <wtf/StdLibExtras.h> + +using namespace JSC; + +namespace WebCore { + +typedef HashSet<IdentifierRep*> IdentifierSet; + +static IdentifierSet& identifierSet() +{ + DEFINE_STATIC_LOCAL(IdentifierSet, identifierSet, ()); + return identifierSet; +} + +typedef HashMap<int, IdentifierRep*> IntIdentifierMap; + +static IntIdentifierMap& intIdentifierMap() +{ + DEFINE_STATIC_LOCAL(IntIdentifierMap, intIdentifierMap, ()); + return intIdentifierMap; +} + +IdentifierRep* IdentifierRep::get(int intID) +{ + if (intID == 0 || intID == -1) { + static IdentifierRep* negativeOneAndZeroIdentifiers[2]; + + IdentifierRep* identifier = negativeOneAndZeroIdentifiers[intID + 1]; + if (!identifier) { + identifier = new IdentifierRep(intID); + + negativeOneAndZeroIdentifiers[intID + 1] = identifier; + } + + return identifier; + } + + pair<IntIdentifierMap::iterator, bool> result = intIdentifierMap().add(intID, 0); + if (result.second) { + ASSERT(!result.first->second); + result.first->second = new IdentifierRep(intID); + + identifierSet().add(result.first->second); + } + + return result.first->second; +} + +typedef HashMap<RefPtr<StringImpl>, IdentifierRep*> StringIdentifierMap; + +static StringIdentifierMap& stringIdentifierMap() +{ + DEFINE_STATIC_LOCAL(StringIdentifierMap, stringIdentifierMap, ()); + return stringIdentifierMap; +} + +IdentifierRep* IdentifierRep::get(const char* name) +{ + ASSERT(name); + if (!name) + return 0; + + UString string = stringToUString(String::fromUTF8WithLatin1Fallback(name, strlen(name))); + pair<StringIdentifierMap::iterator, bool> result = stringIdentifierMap().add(string.impl(), 0); + if (result.second) { + ASSERT(!result.first->second); + result.first->second = new IdentifierRep(name); + + identifierSet().add(result.first->second); + } + + return result.first->second; +} + +bool IdentifierRep::isValid(IdentifierRep* identifier) +{ + return identifierSet().contains(identifier); +} + +} // namespace WebCore diff --git a/Source/WebCore/bridge/IdentifierRep.h b/Source/WebCore/bridge/IdentifierRep.h new file mode 100644 index 0000000..99bae0b --- /dev/null +++ b/Source/WebCore/bridge/IdentifierRep.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef IdentifierRep_h +#define IdentifierRep_h + +#include <wtf/Assertions.h> +#include <wtf/FastAllocBase.h> +#include <wtf/StringExtras.h> +#include <string.h> + +namespace WebCore { + +class IdentifierRep : public FastAllocBase { +public: + static IdentifierRep* get(int); + static IdentifierRep* get(const char*); + + static bool isValid(IdentifierRep*); + + bool isString() const { return m_isString; } + + int number() const { return m_isString ? 0 : m_value.m_number; } + const char* string() const { return m_isString ? m_value.m_string : 0; } + +private: + IdentifierRep(int number) + : m_isString(false) + { + m_value.m_number = number; + } + + IdentifierRep(const char* name) + : m_isString(true) + { + m_value.m_string = fastStrDup(name); + } + + ~IdentifierRep() + { + // IdentifierReps should never be deleted. + ASSERT_NOT_REACHED(); + } + + union { + const char* m_string; + int m_number; + } m_value; + bool m_isString; +}; + +} // namespace WebCore + +#endif // IdentifierRep_h diff --git a/Source/WebCore/bridge/NP_jsobject.cpp b/Source/WebCore/bridge/NP_jsobject.cpp new file mode 100644 index 0000000..0780ad7 --- /dev/null +++ b/Source/WebCore/bridge/NP_jsobject.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "NP_jsobject.h" + +#include "PlatformString.h" +#include "PluginView.h" +#include "StringSourceProvider.h" +#include "c_utility.h" +#include "c_instance.h" +#include "IdentifierRep.h" +#include "JSDOMBinding.h" +#include "npruntime_impl.h" +#include "npruntime_priv.h" +#include "runtime_root.h" +#include <runtime/Error.h> +#include <runtime/JSGlobalObject.h> +#include <runtime/JSLock.h> +#include <runtime/PropertyNameArray.h> +#include <parser/SourceCode.h> +#include <runtime/Completion.h> +#include <runtime/Completion.h> + +using namespace JSC; +using namespace JSC::Bindings; +using namespace WebCore; + +class ObjectMap { +public: + NPObject* get(RootObject* rootObject, JSObject* jsObject) + { + return m_map.get(rootObject).get(jsObject); + } + + void add(RootObject* rootObject, JSObject* jsObject, NPObject* npObject) + { + HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject); + if (iter == m_map.end()) { + rootObject->addInvalidationCallback(&m_invalidationCallback); + iter = m_map.add(rootObject, JSToNPObjectMap()).first; + } + + ASSERT(iter->second.find(jsObject) == iter->second.end()); + iter->second.add(jsObject, npObject); + } + + void remove(RootObject* rootObject) + { + HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject); + ASSERT(iter != m_map.end()); + m_map.remove(iter); + } + + void remove(RootObject* rootObject, JSObject* jsObject) + { + HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject); + ASSERT(iter != m_map.end()); + ASSERT(iter->second.find(jsObject) != iter->second.end()); + + iter->second.remove(jsObject); + } + +private: + struct RootObjectInvalidationCallback : public RootObject::InvalidationCallback { + virtual void operator()(RootObject*); + }; + RootObjectInvalidationCallback m_invalidationCallback; + + // JSObjects are protected by RootObject. + typedef HashMap<JSObject*, NPObject*> JSToNPObjectMap; + HashMap<RootObject*, JSToNPObjectMap> m_map; +}; + + +static ObjectMap& objectMap() +{ + DEFINE_STATIC_LOCAL(ObjectMap, map, ()); + return map; +} + +void ObjectMap::RootObjectInvalidationCallback::operator()(RootObject* rootObject) +{ + objectMap().remove(rootObject); +} + +static void getListFromVariantArgs(ExecState* exec, const NPVariant* args, unsigned argCount, RootObject* rootObject, MarkedArgumentBuffer& aList) +{ + for (unsigned i = 0; i < argCount; ++i) + aList.append(convertNPVariantToValue(exec, &args[i], rootObject)); +} + +static NPObject* jsAllocate(NPP, NPClass*) +{ + return static_cast<NPObject*>(malloc(sizeof(JavaScriptObject))); +} + +static void jsDeallocate(NPObject* npObj) +{ + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(npObj); + + if (obj->rootObject && obj->rootObject->isValid()) { + objectMap().remove(obj->rootObject, obj->imp); + obj->rootObject->gcUnprotect(obj->imp); + } + + if (obj->rootObject) + obj->rootObject->deref(); + + free(obj); +} + +static NPClass javascriptClass = { 1, jsAllocate, jsDeallocate, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static NPClass noScriptClass = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +NPClass* NPScriptObjectClass = &javascriptClass; +static NPClass* NPNoScriptObjectClass = &noScriptClass; + +NPObject* _NPN_CreateScriptObject(NPP npp, JSObject* imp, PassRefPtr<RootObject> rootObject) +{ + if (NPObject* object = objectMap().get(rootObject.get(), imp)) + return _NPN_RetainObject(object); + + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(_NPN_CreateObject(npp, NPScriptObjectClass)); + + obj->rootObject = rootObject.releaseRef(); + + if (obj->rootObject) { + obj->rootObject->gcProtect(imp); + objectMap().add(obj->rootObject, imp, reinterpret_cast<NPObject*>(obj)); + } + + obj->imp = imp; + + return reinterpret_cast<NPObject*>(obj); +} + +NPObject* _NPN_CreateNoScriptObject(void) +{ + return _NPN_CreateObject(0, NPNoScriptObjectClass); +} + +bool _NPN_InvokeDefault(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + VOID_TO_NPVARIANT(*result); + + // Lookup the function object. + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + // Call the function object. + JSValue function = obj->imp; + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return false; + + MarkedArgumentBuffer argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + ProtectedPtr<JSGlobalObject> globalObject = rootObject->globalObject(); + globalObject->globalData().timeoutChecker.start(); + JSValue resultV = JSC::call(exec, function, callType, callData, function, argList); + globalObject->globalData().timeoutChecker.stop(); + + // Convert and return the result of the function call. + convertValueToNPVariant(exec, resultV, result); + exec->clearException(); + return true; + } + + if (o->_class->invokeDefault) + return o->_class->invokeDefault(o, args, argCount, result); + VOID_TO_NPVARIANT(*result); + return true; +} + +bool _NPN_Invoke(NPP npp, NPObject* o, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + IdentifierRep* i = static_cast<IdentifierRep*>(methodName); + if (!i->isString()) + return false; + + // Special case the "eval" method. + if (methodName == _NPN_GetStringIdentifier("eval")) { + if (argCount != 1) + return false; + if (args[0].type != NPVariantType_String) + return false; + return _NPN_Evaluate(npp, o, const_cast<NPString*>(&args[0].value.stringValue), result); + } + + // Look up the function object. + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue function = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string())); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return false; + + // Call the function object. + MarkedArgumentBuffer argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + ProtectedPtr<JSGlobalObject> globalObject = rootObject->globalObject(); + globalObject->globalData().timeoutChecker.start(); + JSValue resultV = JSC::call(exec, function, callType, callData, obj->imp, argList); + globalObject->globalData().timeoutChecker.stop(); + + // Convert and return the result of the function call. + convertValueToNPVariant(exec, resultV, result); + exec->clearException(); + return true; + } + + if (o->_class->invoke) + return o->_class->invoke(o, methodName, args, argCount, result); + + VOID_TO_NPVARIANT(*result); + return true; +} + +bool _NPN_Evaluate(NPP instance, NPObject* o, NPString* s, NPVariant* variant) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + // There is a crash in Flash when evaluating a script that destroys the + // PluginView, so we destroy it asynchronously. + PluginView::keepAlive(instance); + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + String scriptString = convertNPStringToUTF16(s); + ProtectedPtr<JSGlobalObject> globalObject = rootObject->globalObject(); + globalObject->globalData().timeoutChecker.start(); + Completion completion = JSC::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(scriptString), JSC::JSValue()); + globalObject->globalData().timeoutChecker.stop(); + ComplType type = completion.complType(); + + JSValue result; + if (type == Normal) { + result = completion.value(); + if (!result) + result = jsUndefined(); + } else + result = jsUndefined(); + + convertValueToNPVariant(exec, result, variant); + exec->clearException(); + return true; + } + + VOID_TO_NPVARIANT(*variant); + return false; +} + +bool _NPN_GetProperty(NPP, NPObject* o, NPIdentifier propertyName, NPVariant* variant) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + IdentifierRep* i = static_cast<IdentifierRep*>(propertyName); + + JSLock lock(SilenceAssertionsOnly); + JSValue result; + if (i->isString()) + result = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string())); + else + result = obj->imp->get(exec, i->number()); + + convertValueToNPVariant(exec, result, variant); + exec->clearException(); + return true; + } + + if (o->_class->hasProperty && o->_class->getProperty) { + if (o->_class->hasProperty(o, propertyName)) + return o->_class->getProperty(o, propertyName, variant); + return false; + } + + VOID_TO_NPVARIANT(*variant); + return false; +} + +bool _NPN_SetProperty(NPP, NPObject* o, NPIdentifier propertyName, const NPVariant* variant) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + IdentifierRep* i = static_cast<IdentifierRep*>(propertyName); + + if (i->isString()) { + PutPropertySlot slot; + obj->imp->put(exec, identifierFromNPIdentifier(exec, i->string()), convertNPVariantToValue(exec, variant, rootObject), slot); + } else + obj->imp->put(exec, i->number(), convertNPVariantToValue(exec, variant, rootObject)); + exec->clearException(); + return true; + } + + if (o->_class->setProperty) + return o->_class->setProperty(o, propertyName, variant); + + return false; +} + +bool _NPN_RemoveProperty(NPP, NPObject* o, NPIdentifier propertyName) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + IdentifierRep* i = static_cast<IdentifierRep*>(propertyName); + if (i->isString()) { + if (!obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()))) { + exec->clearException(); + return false; + } + } else { + if (!obj->imp->hasProperty(exec, i->number())) { + exec->clearException(); + return false; + } + } + + JSLock lock(SilenceAssertionsOnly); + if (i->isString()) + obj->imp->deleteProperty(exec, identifierFromNPIdentifier(exec, i->string())); + else + obj->imp->deleteProperty(exec, i->number()); + + exec->clearException(); + return true; + } + return false; +} + +bool _NPN_HasProperty(NPP, NPObject* o, NPIdentifier propertyName) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + IdentifierRep* i = static_cast<IdentifierRep*>(propertyName); + JSLock lock(SilenceAssertionsOnly); + if (i->isString()) { + bool result = obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string())); + exec->clearException(); + return result; + } + + bool result = obj->imp->hasProperty(exec, i->number()); + exec->clearException(); + return result; + } + + if (o->_class->hasProperty) + return o->_class->hasProperty(o, propertyName); + + return false; +} + +bool _NPN_HasMethod(NPP, NPObject* o, NPIdentifier methodName) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + IdentifierRep* i = static_cast<IdentifierRep*>(methodName); + if (!i->isString()) + return false; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue func = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string())); + exec->clearException(); + return !func.isUndefined(); + } + + if (o->_class->hasMethod) + return o->_class->hasMethod(o, methodName); + + return false; +} + +void _NPN_SetException(NPObject*, const NPUTF8* message) +{ + // Ignoring the NPObject param is consistent with the Mozilla implementation. + UString exception(message); + CInstance::setGlobalException(exception); +} + +bool _NPN_Enumerate(NPP, NPObject* o, NPIdentifier** identifier, uint32_t* count) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + PropertyNameArray propertyNames(exec); + + obj->imp->getPropertyNames(exec, propertyNames); + unsigned size = static_cast<unsigned>(propertyNames.size()); + // FIXME: This should really call NPN_MemAlloc but that's in WebKit + NPIdentifier* identifiers = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier) * size)); + + for (unsigned i = 0; i < size; ++i) + identifiers[i] = _NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data()); + + *identifier = identifiers; + *count = size; + + exec->clearException(); + return true; + } + + if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(o->_class) && o->_class->enumerate) + return o->_class->enumerate(o, identifier, count); + + return false; +} + +bool _NPN_Construct(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (o->_class == NPScriptObjectClass) { + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); + + VOID_TO_NPVARIANT(*result); + + // Lookup the constructor object. + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + // Call the constructor object. + JSValue constructor = obj->imp; + ConstructData constructData; + ConstructType constructType = getConstructData(constructor, constructData); + if (constructType == ConstructTypeNone) + return false; + + MarkedArgumentBuffer argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + ProtectedPtr<JSGlobalObject> globalObject = rootObject->globalObject(); + globalObject->globalData().timeoutChecker.start(); + JSValue resultV = JSC::construct(exec, constructor, constructType, constructData, argList); + globalObject->globalData().timeoutChecker.stop(); + + // Convert and return the result. + convertValueToNPVariant(exec, resultV, result); + exec->clearException(); + return true; + } + + if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(o->_class) && o->_class->construct) + return o->_class->construct(o, args, argCount, result); + + return false; +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebCore/bridge/NP_jsobject.h b/Source/WebCore/bridge/NP_jsobject.h new file mode 100644 index 0000000..6c49d1b --- /dev/null +++ b/Source/WebCore/bridge/NP_jsobject.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NP_JSOBJECT_H +#define NP_JSOBJECT_H + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "npruntime_internal.h" +#include <wtf/Forward.h> + +namespace JSC { + class JSObject; + namespace Bindings { + class RootObject; + } +} + +extern NPClass* NPScriptObjectClass; + +struct JavaScriptObject +{ + NPObject object; + JSC::JSObject* imp; + JSC::Bindings::RootObject* rootObject; +}; + +NPObject* _NPN_CreateScriptObject(NPP npp, JSC::JSObject*, PassRefPtr<JSC::Bindings::RootObject> rootObject); +NPObject* _NPN_CreateNoScriptObject(void); + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/Source/WebCore/bridge/c/CRuntimeObject.cpp b/Source/WebCore/bridge/c/CRuntimeObject.cpp new file mode 100644 index 0000000..4be4982 --- /dev/null +++ b/Source/WebCore/bridge/c/CRuntimeObject.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "CRuntimeObject.h" +#include "c_instance.h" + +namespace JSC { +namespace Bindings { + +const ClassInfo CRuntimeObject::s_info = { "CRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +CRuntimeObject::CRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<CInstance> instance) + : RuntimeObject(exec, globalObject, instance) +{ +} + +CRuntimeObject::~CRuntimeObject() +{ +} + +CInstance* CRuntimeObject::getInternalCInstance() const +{ + return static_cast<CInstance*>(getInternalInstance()); +} + + +} +} + +#endif diff --git a/Source/WebCore/bridge/c/CRuntimeObject.h b/Source/WebCore/bridge/c/CRuntimeObject.h new file mode 100644 index 0000000..bcd39d3 --- /dev/null +++ b/Source/WebCore/bridge/c/CRuntimeObject.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CRuntimeObject_h +#define CRuntimeObject_h + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "runtime_object.h" + +namespace JSC { +namespace Bindings { + +class CInstance; + +class CRuntimeObject : public RuntimeObject { +public: + CRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<CInstance>); + virtual ~CRuntimeObject(); + + CInstance* getInternalCInstance() const; + + static const ClassInfo s_info; + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } +}; + +} +} + +#endif +#endif diff --git a/Source/WebCore/bridge/c/c_class.cpp b/Source/WebCore/bridge/c/c_class.cpp new file mode 100644 index 0000000..f4ae5ca --- /dev/null +++ b/Source/WebCore/bridge/c/c_class.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "c_class.h" + +#include "c_instance.h" +#include "c_runtime.h" +#include "npruntime_impl.h" +#include <runtime/Identifier.h> +#include <runtime/JSLock.h> +#include <wtf/text/StringHash.h> + +namespace JSC { namespace Bindings { + +CClass::CClass(NPClass* aClass) +{ + _isa = aClass; +} + +CClass::~CClass() +{ + JSLock lock(SilenceAssertionsOnly); + + deleteAllValues(_methods); + _methods.clear(); + + deleteAllValues(_fields); + _fields.clear(); +} + +typedef HashMap<NPClass*, CClass*> ClassesByIsAMap; +static ClassesByIsAMap* classesByIsA = 0; + +CClass* CClass::classForIsA(NPClass* isa) +{ + if (!classesByIsA) + classesByIsA = new ClassesByIsAMap; + + CClass* aClass = classesByIsA->get(isa); + if (!aClass) { + aClass = new CClass(isa); + classesByIsA->set(isa, aClass); + } + + return aClass; +} + +MethodList CClass::methodsNamed(const Identifier& identifier, Instance* instance) const +{ + MethodList methodList; + + Method* method = _methods.get(identifier.ustring().impl()); + if (method) { + methodList.append(method); + return methodList; + } + + NPIdentifier ident = _NPN_GetStringIdentifier(identifier.ascii().data()); + const CInstance* inst = static_cast<const CInstance*>(instance); + NPObject* obj = inst->getObject(); + if (_isa->hasMethod && _isa->hasMethod(obj, ident)){ + Method* aMethod = new CMethod(ident); // deleted in the CClass destructor + { + JSLock lock(SilenceAssertionsOnly); + _methods.set(identifier.ustring().impl(), aMethod); + } + methodList.append(aMethod); + } + + return methodList; +} + +Field* CClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + Field* aField = _fields.get(identifier.ustring().impl()); + if (aField) + return aField; + + NPIdentifier ident = _NPN_GetStringIdentifier(identifier.ascii().data()); + const CInstance* inst = static_cast<const CInstance*>(instance); + NPObject* obj = inst->getObject(); + if (_isa->hasProperty && _isa->hasProperty(obj, ident)){ + aField = new CField(ident); // deleted in the CClass destructor + { + JSLock lock(SilenceAssertionsOnly); + _fields.set(identifier.ustring().impl(), aField); + } + } + return aField; +} + +} } // namespace JSC::Bindings + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebCore/bridge/c/c_class.h b/Source/WebCore/bridge/c/c_class.h new file mode 100644 index 0000000..52db2b9 --- /dev/null +++ b/Source/WebCore/bridge/c/c_class.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BINDINGS_C_CLASS_H_ +#define BINDINGS_C_CLASS_H_ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "Bridge.h" +#include "npruntime_internal.h" +#include <wtf/HashMap.h> + +namespace JSC { +namespace Bindings { + +class CClass : public Class { +protected: + CClass(NPClass*); // Use classForIsA to create a CClass. + +public: + static CClass* classForIsA(NPClass*); + virtual ~CClass(); + + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; + +private: + NPClass* _isa; + mutable MethodMap _methods; + mutable FieldMap _fields; +}; + +} // namespace Bindings +} // namespace JSC + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/Source/WebCore/bridge/c/c_instance.cpp b/Source/WebCore/bridge/c/c_instance.cpp new file mode 100644 index 0000000..f1dd4e5 --- /dev/null +++ b/Source/WebCore/bridge/c/c_instance.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "c_instance.h" + +#include "CRuntimeObject.h" +#include "IdentifierRep.h" +#include "c_class.h" +#include "c_runtime.h" +#include "c_utility.h" +#include "npruntime_impl.h" +#include "runtime_method.h" +#include "runtime_root.h" +#include <interpreter/CallFrame.h> +#include <runtime/ArgList.h> +#include <runtime/Error.h> +#include <runtime/JSLock.h> +#include <runtime/JSNumberCell.h> +#include <runtime/PropertyNameArray.h> +#include <wtf/Assertions.h> +#include <wtf/StdLibExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/Vector.h> + +using namespace WebCore; + +namespace JSC { +namespace Bindings { + +using JSC::UString; + +static JSC::UString& globalExceptionString() +{ + DEFINE_STATIC_LOCAL(JSC::UString, exceptionStr, ()); + return exceptionStr; +} + +void CInstance::setGlobalException(UString exception) +{ + globalExceptionString() = exception; +} + +void CInstance::moveGlobalExceptionToExecState(ExecState* exec) +{ + if (globalExceptionString().isNull()) + return; + + { + JSLock lock(SilenceAssertionsOnly); + throwError(exec, createError(exec, globalExceptionString())); + } + + globalExceptionString() = UString(); +} + +CInstance::CInstance(NPObject* o, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) +{ + _object = _NPN_RetainObject(o); + _class = 0; +} + +CInstance::~CInstance() +{ + _NPN_ReleaseObject(_object); +} + +RuntimeObject* CInstance::newRuntimeObject(ExecState* exec) +{ + return new (exec) CRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +Class *CInstance::getClass() const +{ + if (!_class) + _class = CClass::classForIsA(_object->_class); + return _class; +} + +bool CInstance::supportsInvokeDefaultMethod() const +{ + return _object->_class->invokeDefault; +} + +class CRuntimeMethod : public RuntimeMethod { +public: + CRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list) + : RuntimeMethod(exec, globalObject, name, list) + { + } + + virtual const ClassInfo* classInfo() const { return &s_info; } + + static const ClassInfo s_info; +}; + +const ClassInfo CRuntimeMethod::s_info = { "CRuntimeMethod", &RuntimeMethod::s_info, 0, 0 }; + +JSValue CInstance::getMethod(ExecState* exec, const Identifier& propertyName) +{ + MethodList methodList = getClass()->methodsNamed(propertyName, this); + return new (exec) CRuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue CInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod) +{ + if (!asObject(runtimeMethod)->inherits(&CRuntimeMethod::s_info)) + return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object.")); + + const MethodList& methodList = *runtimeMethod->methods(); + + // Overloading methods are not allowed by NPObjects. Should only be one + // name match for a particular method. + ASSERT(methodList.size() == 1); + + CMethod* method = static_cast<CMethod*>(methodList[0]); + + NPIdentifier ident = method->identifier(); + if (!_object->_class->hasMethod(_object, ident)) + return jsUndefined(); + + unsigned count = exec->argumentCount(); + Vector<NPVariant, 8> cArgs(count); + + unsigned i; + for (i = 0; i < count; i++) + convertValueToNPVariant(exec, exec->argument(i), &cArgs[i]); + + // Invoke the 'C' method. + bool retval = true; + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + ASSERT(globalExceptionString().isNull()); + retval = _object->_class->invoke(_object, ident, cArgs.data(), count, &resultVariant); + moveGlobalExceptionToExecState(exec); + } + + if (!retval) + throwError(exec, createError(exec, "Error calling method on NPObject.")); + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get()); + _NPN_ReleaseVariantValue(&resultVariant); + return resultValue; +} + + +JSValue CInstance::invokeDefaultMethod(ExecState* exec) +{ + if (!_object->_class->invokeDefault) + return jsUndefined(); + + unsigned count = exec->argumentCount(); + Vector<NPVariant, 8> cArgs(count); + + unsigned i; + for (i = 0; i < count; i++) + convertValueToNPVariant(exec, exec->argument(i), &cArgs[i]); + + // Invoke the 'C' method. + bool retval = true; + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + ASSERT(globalExceptionString().isNull()); + retval = _object->_class->invokeDefault(_object, cArgs.data(), count, &resultVariant); + moveGlobalExceptionToExecState(exec); + } + + if (!retval) + throwError(exec, createError(exec, "Error calling method on NPObject.")); + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get()); + _NPN_ReleaseVariantValue(&resultVariant); + return resultValue; +} + +bool CInstance::supportsConstruct() const +{ + return _object->_class->construct; +} + +JSValue CInstance::invokeConstruct(ExecState* exec, const ArgList& args) +{ + if (!_object->_class->construct) + return jsUndefined(); + + unsigned count = args.size(); + Vector<NPVariant, 8> cArgs(count); + + unsigned i; + for (i = 0; i < count; i++) + convertValueToNPVariant(exec, args.at(i), &cArgs[i]); + + // Invoke the 'C' method. + bool retval = true; + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + ASSERT(globalExceptionString().isNull()); + retval = _object->_class->construct(_object, cArgs.data(), count, &resultVariant); + moveGlobalExceptionToExecState(exec); + } + + if (!retval) + throwError(exec, createError(exec, "Error calling method on NPObject.")); + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get()); + _NPN_ReleaseVariantValue(&resultVariant); + return resultValue; +} + +JSValue CInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (hint == PreferString) + return stringValue(exec); + if (hint == PreferNumber) + return numberValue(exec); + return valueOf(exec); +} + +JSValue CInstance::stringValue(ExecState* exec) const +{ + char buf[1024]; + snprintf(buf, sizeof(buf), "NPObject %p, NPClass %p", _object, _object->_class); + return jsString(exec, buf); +} + +JSValue CInstance::numberValue(ExecState*) const +{ + // FIXME: Implement something sensible. + return jsNumber(0); +} + +JSValue CInstance::booleanValue() const +{ + // FIXME: Implement something sensible. + return jsBoolean(false); +} + +JSValue CInstance::valueOf(ExecState* exec) const +{ + return stringValue(exec); +} + +void CInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray) +{ + if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(_object->_class) || !_object->_class->enumerate) + return; + + uint32_t count; + NPIdentifier* identifiers; + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + ASSERT(globalExceptionString().isNull()); + bool ok = _object->_class->enumerate(_object, &identifiers, &count); + moveGlobalExceptionToExecState(exec); + if (!ok) + return; + } + + for (uint32_t i = 0; i < count; i++) { + IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]); + + if (identifier->isString()) + nameArray.add(identifierFromNPIdentifier(exec, identifier->string())); + else + nameArray.add(Identifier::from(exec, identifier->number())); + } + + // FIXME: This should really call NPN_MemFree but that's in WebKit + free(identifiers); +} + +} +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebCore/bridge/c/c_instance.h b/Source/WebCore/bridge/c/c_instance.h new file mode 100644 index 0000000..cc1a806 --- /dev/null +++ b/Source/WebCore/bridge/c/c_instance.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BINDINGS_C_INSTANCE_H_ +#define BINDINGS_C_INSTANCE_H_ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "Bridge.h" +#include "runtime_root.h" +#include <wtf/PassRefPtr.h> + +typedef struct NPObject NPObject; + +namespace JSC { + +class UString; + +namespace Bindings { + +class CClass; + +class CInstance : public Instance { +public: + static PassRefPtr<CInstance> create(NPObject* object, PassRefPtr<RootObject> rootObject) + { + return adoptRef(new CInstance(object, rootObject)); + } + + static void setGlobalException(JSC::UString exception); + static void moveGlobalExceptionToExecState(ExecState*); + + ~CInstance (); + + virtual Class *getClass() const; + + virtual JSValue valueOf(ExecState*) const; + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName); + virtual JSValue invokeMethod(ExecState*, RuntimeMethod* method); + virtual bool supportsInvokeDefaultMethod() const; + virtual JSValue invokeDefaultMethod(ExecState*); + + virtual bool supportsConstruct() const; + virtual JSValue invokeConstruct(ExecState*, const ArgList&); + + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + JSValue stringValue(ExecState*) const; + JSValue numberValue(ExecState*) const; + JSValue booleanValue() const; + + NPObject *getObject() const { return _object; } + +private: + CInstance(NPObject*, PassRefPtr<RootObject>); + + virtual RuntimeObject* newRuntimeObject(ExecState*); + + mutable CClass *_class; + NPObject *_object; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/Source/WebCore/bridge/c/c_runtime.cpp b/Source/WebCore/bridge/c/c_runtime.cpp new file mode 100644 index 0000000..e038cd4 --- /dev/null +++ b/Source/WebCore/bridge/c/c_runtime.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "c_runtime.h" + +#include "c_instance.h" +#include "c_utility.h" +#include "npruntime_impl.h" +#include <runtime/JSLock.h> + +namespace JSC { +namespace Bindings { + +JSValue CField::valueFromInstance(ExecState* exec, const Instance* inst) const +{ + const CInstance* instance = static_cast<const CInstance*>(inst); + NPObject* obj = instance->getObject(); + if (obj->_class->getProperty) { + NPVariant property; + VOID_TO_NPVARIANT(property); + + bool result; + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + result = obj->_class->getProperty(obj, _fieldIdentifier, &property); + CInstance::moveGlobalExceptionToExecState(exec); + } + if (result) { + JSValue result = convertNPVariantToValue(exec, &property, instance->rootObject()); + _NPN_ReleaseVariantValue(&property); + return result; + } + } + return jsUndefined(); +} + +void CField::setValueToInstance(ExecState *exec, const Instance *inst, JSValue aValue) const +{ + const CInstance* instance = static_cast<const CInstance*>(inst); + NPObject* obj = instance->getObject(); + if (obj->_class->setProperty) { + NPVariant variant; + convertValueToNPVariant(exec, aValue, &variant); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + obj->_class->setProperty(obj, _fieldIdentifier, &variant); + CInstance::moveGlobalExceptionToExecState(exec); + } + + _NPN_ReleaseVariantValue(&variant); + } +} + +} } + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebCore/bridge/c/c_runtime.h b/Source/WebCore/bridge/c/c_runtime.h new file mode 100644 index 0000000..5355934 --- /dev/null +++ b/Source/WebCore/bridge/c/c_runtime.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BINDINGS_C_RUNTIME_H_ +#define BINDINGS_C_RUNTIME_H_ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "Bridge.h" +#include "npruntime_internal.h" + +namespace JSC { +namespace Bindings { + +class CField : public Field { +public: + CField(NPIdentifier ident) : _fieldIdentifier(ident) { } + + virtual JSValue valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const; + + NPIdentifier identifier() const { return _fieldIdentifier; } + +private: + NPIdentifier _fieldIdentifier; +}; + + +class CMethod : public Method +{ +public: + CMethod(NPIdentifier ident) : _methodIdentifier(ident) { } + + NPIdentifier identifier() const { return _methodIdentifier; } + virtual int numParameters() const { return 0; } + +private: + NPIdentifier _methodIdentifier; +}; + +} // namespace Bindings +} // namespace JSC + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/Source/WebCore/bridge/c/c_utility.cpp b/Source/WebCore/bridge/c/c_utility.cpp new file mode 100644 index 0000000..ea970eb --- /dev/null +++ b/Source/WebCore/bridge/c/c_utility.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "c_utility.h" + +#include "CRuntimeObject.h" +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include "NP_jsobject.h" +#include "c_instance.h" +#include <runtime/JSGlobalObject.h> +#include <runtime/JSLock.h> +#include "PlatformString.h" +#include "npruntime_impl.h" +#include "npruntime_priv.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <wtf/Assertions.h> + +namespace JSC { namespace Bindings { + +static String convertUTF8ToUTF16WithLatin1Fallback(const NPUTF8* UTF8Chars, int UTF8Length) +{ + ASSERT(UTF8Chars || UTF8Length == 0); + + if (UTF8Length == -1) + UTF8Length = static_cast<int>(strlen(UTF8Chars)); + + String result = String::fromUTF8(UTF8Chars, UTF8Length); + + // If we got back a null string indicating an unsuccessful conversion, fall back to latin 1. + // Some plugins return invalid UTF-8 in NPVariantType_String, see <http://bugs.webkit.org/show_bug.cgi?id=5163> + // There is no "bad data" for latin1. It is unlikely that the plugin was really sending text in this encoding, + // but it should have used UTF-8, and now we are simply avoiding a crash. + if (result.isNull()) + result = String(UTF8Chars, UTF8Length); + + return result; +} + +// Variant value must be released with NPReleaseVariantValue() +void convertValueToNPVariant(ExecState* exec, JSValue value, NPVariant* result) +{ + JSLock lock(SilenceAssertionsOnly); + + VOID_TO_NPVARIANT(*result); + + if (value.isString()) { + UString ustring = value.toString(exec); + CString cstring = ustring.utf8(); + NPString string = { (const NPUTF8*)cstring.data(), static_cast<uint32_t>(cstring.length()) }; + NPN_InitializeVariantWithStringCopy(result, &string); + } else if (value.isNumber()) { + DOUBLE_TO_NPVARIANT(value.toNumber(exec), *result); + } else if (value.isBoolean()) { + BOOLEAN_TO_NPVARIANT(value.toBoolean(exec), *result); + } else if (value.isNull()) { + NULL_TO_NPVARIANT(*result); + } else if (value.isObject()) { + JSObject* object = asObject(value); + if (object->classInfo() == &CRuntimeObject::s_info) { + CRuntimeObject* runtimeObject = static_cast<CRuntimeObject*>(object); + CInstance* instance = runtimeObject->getInternalCInstance(); + if (instance) { + NPObject* obj = instance->getObject(); + _NPN_RetainObject(obj); + OBJECT_TO_NPVARIANT(obj, *result); + } + } else { +#ifdef ANDROID + RootObject* rootObject = findRootObject(exec->dynamicGlobalObject()); + if (!rootObject) + rootObject = findRootObject(exec->lexicalGlobalObject()); +#else + JSGlobalObject* globalObject = exec->dynamicGlobalObject(); + + RootObject* rootObject = findRootObject(globalObject); +#endif + if (rootObject) { + NPObject* npObject = _NPN_CreateScriptObject(0, object, rootObject); + OBJECT_TO_NPVARIANT(npObject, *result); + } + } + } +} + +JSValue convertNPVariantToValue(ExecState* exec, const NPVariant* variant, RootObject* rootObject) +{ + JSLock lock(SilenceAssertionsOnly); + + NPVariantType type = variant->type; + + if (type == NPVariantType_Bool) + return jsBoolean(NPVARIANT_TO_BOOLEAN(*variant)); + if (type == NPVariantType_Null) + return jsNull(); + if (type == NPVariantType_Void) + return jsUndefined(); + if (type == NPVariantType_Int32) + return jsNumber(NPVARIANT_TO_INT32(*variant)); + if (type == NPVariantType_Double) + return jsNumber(NPVARIANT_TO_DOUBLE(*variant)); + if (type == NPVariantType_String) + return WebCore::jsString(exec, convertNPStringToUTF16(&variant->value.stringValue)); + if (type == NPVariantType_Object) { + NPObject* obj = variant->value.objectValue; + + if (obj->_class == NPScriptObjectClass) + // Get JSObject from NP_JavaScriptObject. + return ((JavaScriptObject*)obj)->imp; + + // Wrap NPObject in a CInstance. + return CInstance::create(obj, rootObject)->createRuntimeObject(exec); + } + + return jsUndefined(); +} + +String convertNPStringToUTF16(const NPString* string) +{ + return String::fromUTF8WithLatin1Fallback(string->UTF8Characters, string->UTF8Length); +} + +Identifier identifierFromNPIdentifier(ExecState* exec, const NPUTF8* name) +{ + return Identifier(exec, WebCore::stringToUString(convertUTF8ToUTF16WithLatin1Fallback(name, -1))); +} + +} } + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebCore/bridge/c/c_utility.h b/Source/WebCore/bridge/c/c_utility.h new file mode 100644 index 0000000..43cb16c --- /dev/null +++ b/Source/WebCore/bridge/c/c_utility.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef C_UTILITY_H_ +#define C_UTILITY_H_ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "npruntime_internal.h" +#include <runtime/JSValue.h> +#include <wtf/Forward.h> + +namespace JSC { + +class ExecState; +class Identifier; + +namespace Bindings { + +class RootObject; + +typedef uint16_t NPUTF16; + +WTF::String convertNPStringToUTF16(const NPString *string); +void convertValueToNPVariant(ExecState*, JSValue, NPVariant* result); +JSValue convertNPVariantToValue(ExecState*, const NPVariant*, RootObject*); +Identifier identifierFromNPIdentifier(ExecState*, const NPUTF8* name); + +} } + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/Source/WebCore/bridge/jni/JNIBridge.cpp b/Source/WebCore/bridge/jni/JNIBridge.cpp new file mode 100644 index 0000000..c512ee7 --- /dev/null +++ b/Source/WebCore/bridge/jni/JNIBridge.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007, 2009 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JNIBridge.h" + +#if ENABLE(JAVA_BRIDGE) + +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +using namespace JSC; +using namespace JSC::Bindings; + +JavaParameter::JavaParameter(JNIEnv* env, jstring type) +{ + m_type = JavaString(env, type); + m_JNIType = JNITypeFromClassName(m_type.utf8()); +} + +JavaMethod::JavaMethod(JNIEnv* env, jobject aMethod) +{ + // Get return type name + jstring returnTypeName = 0; + if (jobject returnType = callJNIMethod<jobject>(aMethod, "getReturnType", "()Ljava/lang/Class;")) { + returnTypeName = static_cast<jstring>(callJNIMethod<jobject>(returnType, "getName", "()Ljava/lang/String;")); + if (!returnTypeName) + returnTypeName = env->NewStringUTF("<Unknown>"); + env->DeleteLocalRef(returnType); + } + m_returnType = JavaString(env, returnTypeName); + m_JNIReturnType = JNITypeFromClassName(m_returnType.utf8()); + env->DeleteLocalRef(returnTypeName); + + // Get method name + jstring methodName = static_cast<jstring>(callJNIMethod<jobject>(aMethod, "getName", "()Ljava/lang/String;")); + if (!returnTypeName) + returnTypeName = env->NewStringUTF("<Unknown>"); + m_name = JavaString(env, methodName); + env->DeleteLocalRef(methodName); + + // Get parameters + if (jarray jparameters = static_cast<jarray>(callJNIMethod<jobject>(aMethod, "getParameterTypes", "()[Ljava/lang/Class;"))) { + m_numParameters = env->GetArrayLength(jparameters); + m_parameters = new JavaParameter[m_numParameters]; + + for (int i = 0; i < m_numParameters; i++) { + jobject aParameter = env->GetObjectArrayElement(static_cast<jobjectArray>(jparameters), i); + jstring parameterName = static_cast<jstring>(callJNIMethod<jobject>(aParameter, "getName", "()Ljava/lang/String;")); + if (!parameterName) + parameterName = env->NewStringUTF("<Unknown>"); + m_parameters[i] = JavaParameter(env, parameterName); + env->DeleteLocalRef(aParameter); + env->DeleteLocalRef(parameterName); + } + env->DeleteLocalRef(jparameters); + } else { + m_numParameters = 0; + m_parameters = 0; + } + + // Created lazily. + m_signature = 0; + m_methodID = 0; + + jclass modifierClass = env->FindClass("java/lang/reflect/Modifier"); + int modifiers = callJNIMethod<jint>(aMethod, "getModifiers", "()I"); + m_isStatic = static_cast<bool>(callJNIStaticMethod<jboolean>(modifierClass, "isStatic", "(I)Z", modifiers)); + env->DeleteLocalRef(modifierClass); +} + +JavaMethod::~JavaMethod() +{ + if (m_signature) + fastFree(m_signature); + delete[] m_parameters; +}; + +// JNI method signatures use '/' between components of a class name, but +// we get '.' between components from the reflection API. +static void appendClassName(StringBuilder& builder, const char* className) +{ +#if USE(JSC) + ASSERT(JSLock::lockCount() > 0); +#endif + + char* c = fastStrDup(className); + + char* result = c; + while (*c) { + if (*c == '.') + *c = '/'; + c++; + } + + builder.append(result); + + fastFree(result); +} + +const char* JavaMethod::signature() const +{ + if (!m_signature) { +#if USE(JSC) + JSLock lock(SilenceAssertionsOnly); +#endif + + StringBuilder signatureBuilder; + signatureBuilder.append('('); + for (int i = 0; i < m_numParameters; i++) { + JavaParameter* aParameter = parameterAt(i); + JNIType type = aParameter->getJNIType(); + if (type == array_type) + appendClassName(signatureBuilder, aParameter->type()); + else { + signatureBuilder.append(signatureFromPrimitiveType(type)); + if (type == object_type) { + appendClassName(signatureBuilder, aParameter->type()); + signatureBuilder.append(';'); + } + } + } + signatureBuilder.append(')'); + + const char* returnType = m_returnType.utf8(); + if (m_JNIReturnType == array_type) + appendClassName(signatureBuilder, returnType); + else { + signatureBuilder.append(signatureFromPrimitiveType(m_JNIReturnType)); + if (m_JNIReturnType == object_type) { + appendClassName(signatureBuilder, returnType); + signatureBuilder.append(';'); + } + } + + String signatureString = signatureBuilder.toString(); + m_signature = fastStrDup(signatureString.utf8().data()); + } + + return m_signature; +} + +JNIType JavaMethod::JNIReturnType() const +{ + return m_JNIReturnType; +} + +jmethodID JavaMethod::methodID(jobject obj) const +{ + if (!m_methodID) + m_methodID = getMethodID(obj, m_name.utf8(), signature()); + return m_methodID; +} + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/JNIBridge.h b/Source/WebCore/bridge/jni/JNIBridge.h new file mode 100644 index 0000000..4f5e6b7 --- /dev/null +++ b/Source/WebCore/bridge/jni/JNIBridge.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JNIBridge_h +#define JNIBridge_h + +#if ENABLE(JAVA_BRIDGE) + +#include "Bridge.h" +#include "JNIUtility.h" + +#if USE(JSC) +#include "JavaStringJSC.h" +#elif USE(V8) +#include "JavaStringV8.h" +#endif + +namespace JSC { + +namespace Bindings { + +typedef const char* RuntimeType; + +class JavaString { +public: + JavaString() + { + m_impl.init(); + } + + JavaString(JNIEnv* e, jstring s) + { + m_impl.init(e, s); + } + + JavaString(jstring s) + { + m_impl.init(getJNIEnv(), s); + } + + const char* utf8() const { return m_impl.utf8(); } + const jchar* uchars() const { return m_impl.uchars(); } + int length() const { return m_impl.length(); } +#if USE(JSC) + operator UString() const { return m_impl.uString(); } +#endif + +private: + JavaStringImpl m_impl; +}; + +class JavaParameter { +public: + JavaParameter() : m_JNIType(invalid_type) { } + JavaParameter(JNIEnv*, jstring type); + virtual ~JavaParameter() { } + + RuntimeType type() const { return m_type.utf8(); } + JNIType getJNIType() const { return m_JNIType; } + +private: + JavaString m_type; + JNIType m_JNIType; +}; + +class JavaMethod : public Method { +public: + JavaMethod(JNIEnv*, jobject aMethod); + ~JavaMethod(); + + const JavaString& name() const { return m_name; } + RuntimeType returnType() const { return m_returnType.utf8(); } + JavaParameter* parameterAt(int i) const { return &m_parameters[i]; } + int numParameters() const { return m_numParameters; } + + const char* signature() const; + JNIType JNIReturnType() const; + + jmethodID methodID(jobject obj) const; + + bool isStatic() const { return m_isStatic; } + +private: + JavaParameter* m_parameters; + int m_numParameters; + JavaString m_name; + mutable char* m_signature; + JavaString m_returnType; + JNIType m_JNIReturnType; + mutable jmethodID m_methodID; + bool m_isStatic; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) + +#endif // JNIBridge_h diff --git a/Source/WebCore/bridge/jni/JNIUtility.cpp b/Source/WebCore/bridge/jni/JNIUtility.cpp new file mode 100644 index 0000000..4b4f393 --- /dev/null +++ b/Source/WebCore/bridge/jni/JNIUtility.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JNIUtility.h" + +#if ENABLE(JAVA_BRIDGE) + +#include <dlfcn.h> + +namespace JSC { + +namespace Bindings { + +static jint KJSGetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) +{ + static void* javaVMFramework = 0; + if (!javaVMFramework) + javaVMFramework = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY); + if (!javaVMFramework) + return JNI_ERR; + + typedef jint(*FunctionPointerType)(JavaVM**, jsize, jsize*); + static FunctionPointerType functionPointer = 0; + if (!functionPointer) + functionPointer = reinterpret_cast<FunctionPointerType>(dlsym(javaVMFramework, "JNI_GetCreatedJavaVMs")); + if (!functionPointer) + return JNI_ERR; + return functionPointer(vmBuf, bufLen, nVMs); +} + +static JavaVM* jvm = 0; + +// Provide the ability for an outside component to specify the JavaVM to use +// If the jvm value is set, the getJavaVM function below will just return. +// In getJNIEnv(), if AttachCurrentThread is called to a VM that is already +// attached, the result is a no-op. +void setJavaVM(JavaVM* javaVM) +{ + jvm = javaVM; +} + +JavaVM* getJavaVM() +{ + if (jvm) + return jvm; + + JavaVM* jvmArray[1]; + jsize bufLen = 1; + jsize nJVMs = 0; + jint jniError = 0; + + // Assumes JVM is already running ..., one per process + jniError = KJSGetCreatedJavaVMs(jvmArray, bufLen, &nJVMs); + if (jniError == JNI_OK && nJVMs > 0) + jvm = jvmArray[0]; + else + LOG_ERROR("JNI_GetCreatedJavaVMs failed, returned %ld", static_cast<long>(jniError)); + + return jvm; +} + +JNIEnv* getJNIEnv() +{ + union { + JNIEnv* env; + void* dummy; + } u; + jint jniError = 0; + + jniError = getJavaVM()->AttachCurrentThread(&u.dummy, 0); + if (jniError == JNI_OK) + return u.env; + LOG_ERROR("AttachCurrentThread failed, returned %ld", static_cast<long>(jniError)); + return 0; +} + +jmethodID getMethodID(jobject obj, const char* name, const char* sig) +{ + JNIEnv* env = getJNIEnv(); + jmethodID mid = 0; + + if (env) { + jclass cls = env->GetObjectClass(obj); + if (cls) { + mid = env->GetMethodID(cls, name, sig); + if (!mid) { + env->ExceptionClear(); + mid = env->GetStaticMethodID(cls, name, sig); + if (!mid) + env->ExceptionClear(); + } + } + env->DeleteLocalRef(cls); + } + return mid; +} + +const char* getCharactersFromJString(jstring aJString) +{ + return getCharactersFromJStringInEnv(getJNIEnv(), aJString); +} + +void releaseCharactersForJString(jstring aJString, const char* s) +{ + releaseCharactersForJStringInEnv(getJNIEnv(), aJString, s); +} + +const char* getCharactersFromJStringInEnv(JNIEnv* env, jstring aJString) +{ + jboolean isCopy; + const char* s = env->GetStringUTFChars(aJString, &isCopy); + if (!s) { + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf(stderr, "\n"); + } + return s; +} + +void releaseCharactersForJStringInEnv(JNIEnv* env, jstring aJString, const char* s) +{ + env->ReleaseStringUTFChars(aJString, s); +} + +const jchar* getUCharactersFromJStringInEnv(JNIEnv* env, jstring aJString) +{ + jboolean isCopy; + const jchar* s = env->GetStringChars(aJString, &isCopy); + if (!s) { + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf(stderr, "\n"); + } + return s; +} + +void releaseUCharactersForJStringInEnv(JNIEnv* env, jstring aJString, const jchar* s) +{ + env->ReleaseStringChars(aJString, s); +} + +JNIType JNITypeFromClassName(const char* name) +{ + JNIType type; + + if (!strcmp("byte", name)) + type = byte_type; + else if (!strcmp("short", name)) + type = short_type; + else if (!strcmp("int", name)) + type = int_type; + else if (!strcmp("long", name)) + type = long_type; + else if (!strcmp("float", name)) + type = float_type; + else if (!strcmp("double", name)) + type = double_type; + else if (!strcmp("char", name)) + type = char_type; + else if (!strcmp("boolean", name)) + type = boolean_type; + else if (!strcmp("void", name)) + type = void_type; + else if ('[' == name[0]) + type = array_type; + else + type = object_type; + + return type; +} + +const char* signatureFromPrimitiveType(JNIType type) +{ + switch (type) { + case void_type: + return "V"; + + case array_type: + return "["; + + case object_type: + return "L"; + + case boolean_type: + return "Z"; + + case byte_type: + return "B"; + + case char_type: + return "C"; + + case short_type: + return "S"; + + case int_type: + return "I"; + + case long_type: + return "J"; + + case float_type: + return "F"; + + case double_type: + return "D"; + + case invalid_type: + default: + break; + } + return ""; +} + +JNIType JNITypeFromPrimitiveType(char type) +{ + switch (type) { + case 'V': + return void_type; + + case 'L': + return object_type; + + case '[': + return array_type; + + case 'Z': + return boolean_type; + + case 'B': + return byte_type; + + case 'C': + return char_type; + + case 'S': + return short_type; + + case 'I': + return int_type; + + case 'J': + return long_type; + + case 'F': + return float_type; + + case 'D': + return double_type; + + default: + break; + } + return invalid_type; +} + +jvalue getJNIField(jobject obj, JNIType type, const char* name, const char* signature) +{ + JavaVM* jvm = getJavaVM(); + JNIEnv* env = getJNIEnv(); + jvalue result; + + memset(&result, 0, sizeof(jvalue)); + if (obj && jvm && env) { + jclass cls = env->GetObjectClass(obj); + if (cls) { + jfieldID field = env->GetFieldID(cls, name, signature); + if (field) { + switch (type) { + case array_type: + case object_type: + result.l = env->functions->GetObjectField(env, obj, field); + break; + case boolean_type: + result.z = env->functions->GetBooleanField(env, obj, field); + break; + case byte_type: + result.b = env->functions->GetByteField(env, obj, field); + break; + case char_type: + result.c = env->functions->GetCharField(env, obj, field); + break; + case short_type: + result.s = env->functions->GetShortField(env, obj, field); + break; + case int_type: + result.i = env->functions->GetIntField(env, obj, field); + break; + case long_type: + result.j = env->functions->GetLongField(env, obj, field); + break; + case float_type: + result.f = env->functions->GetFloatField(env, obj, field); + break; + case double_type: + result.d = env->functions->GetDoubleField(env, obj, field); + break; + default: + LOG_ERROR("Invalid field type (%d)", static_cast<int>(type)); + } + } else { + LOG_ERROR("Could not find field: %s", name); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf(stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } else + LOG_ERROR("Could not find class for object"); + } + + return result; +} + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/JNIUtility.h b/Source/WebCore/bridge/jni/JNIUtility.h new file mode 100644 index 0000000..5fb2138 --- /dev/null +++ b/Source/WebCore/bridge/jni/JNIUtility.h @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JNIUtility_h +#define JNIUtility_h + +#if ENABLE(JAVA_BRIDGE) + +#include <JavaVM/jni.h> + +// The order of these items can not be modified as they are tightly +// bound with the JVM on Mac OSX. If new types need to be added, they +// should be added to the end. It is used in jni_obc.mm when calling +// through to the JVM. Newly added items need to be made compatible +// in that file. +typedef enum { + invalid_type = 0, + void_type, + object_type, + boolean_type, + byte_type, + char_type, + short_type, + int_type, + long_type, + float_type, + double_type, + array_type +} JNIType; + +namespace JSC { + +namespace Bindings { + +class JavaParameter; + +const char* getCharactersFromJString(jstring); +void releaseCharactersForJString(jstring, const char*); + +const char* getCharactersFromJStringInEnv(JNIEnv*, jstring); +void releaseCharactersForJStringInEnv(JNIEnv*, jstring, const char*); +const jchar* getUCharactersFromJStringInEnv(JNIEnv*, jstring); +void releaseUCharactersForJStringInEnv(JNIEnv*, jstring, const jchar*); + +JNIType JNITypeFromClassName(const char* name); +JNIType JNITypeFromPrimitiveType(char type); +const char* signatureFromPrimitiveType(JNIType); + +jvalue getJNIField(jobject, JNIType, const char* name, const char* signature); + +jmethodID getMethodID(jobject, const char* name, const char* sig); +JNIEnv* getJNIEnv(); +JavaVM* getJavaVM(); +void setJavaVM(JavaVM*); + + +template <typename T> struct JNICaller; + +template<> struct JNICaller<void> { + static void callA(jobject obj, jmethodID mid, jvalue* args) + { + getJNIEnv()->CallVoidMethodA(obj, mid, args); + } + static void callV(jobject obj, jmethodID mid, va_list args) + { + getJNIEnv()->CallVoidMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jobject> { + static jobject callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallObjectMethodA(obj, mid, args); + } + static jobject callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallObjectMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jboolean> { + static jboolean callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallBooleanMethodA(obj, mid, args); + } + static jboolean callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallBooleanMethodV(obj, mid, args); + } + static jboolean callStaticV(jclass cls, jmethodID mid, va_list args) + { + return getJNIEnv()->CallStaticBooleanMethod(cls, mid, args); + } +}; + +template<> struct JNICaller<jbyte> { + static jbyte callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallByteMethodA(obj, mid, args); + } + static jbyte callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallByteMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jchar> { + static jchar callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallCharMethodA(obj, mid, args); + } + static jchar callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallCharMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jshort> { + static jshort callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallShortMethodA(obj, mid, args); + } + static jshort callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallShortMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jint> { + static jint callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallIntMethodA(obj, mid, args); + } + static jint callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallIntMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jlong> { + static jlong callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallLongMethodA(obj, mid, args); + } + static jlong callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallLongMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jfloat> { + static jfloat callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallFloatMethodA(obj, mid, args); + } + static jfloat callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallFloatMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jdouble> { + static jdouble callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallDoubleMethodA(obj, mid, args); + } + static jdouble callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallDoubleMethodV(obj, mid, args); + } +}; + +template<typename T> T callJNIMethodIDA(jobject obj, jmethodID mid, jvalue *args) +{ + return JNICaller<T>::callA(obj, mid, args); +} + +template<typename T> +static T callJNIMethodV(jobject obj, const char* name, const char* sig, va_list args) +{ + JavaVM* jvm = getJavaVM(); + JNIEnv* env = getJNIEnv(); + + if (obj && jvm && env) { + jclass cls = env->GetObjectClass(obj); + if (cls) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if (mid) { + // Avoids references to cls without popping the local frame. + env->DeleteLocalRef(cls); + return JNICaller<T>::callV(obj, mid, args); + } + LOG_ERROR("Could not find method: %s for %p", name, obj); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf(stderr, "\n"); + + env->DeleteLocalRef(cls); + } else + LOG_ERROR("Could not find class for %p", obj); + } + + return 0; +} + +template<typename T> +T callJNIMethod(jobject obj, const char* methodName, const char* methodSignature, ...) +{ + va_list args; + va_start(args, methodSignature); + + T result = callJNIMethodV<T>(obj, methodName, methodSignature, args); + + va_end(args); + + return result; +} + +template<typename T> +T callJNIStaticMethod(jclass cls, const char* methodName, const char* methodSignature, ...) +{ + JavaVM* jvm = getJavaVM(); + JNIEnv* env = getJNIEnv(); + va_list args; + + va_start(args, methodSignature); + + T result = 0; + + if (cls && jvm && env) { + jmethodID mid = env->GetStaticMethodID(cls, methodName, methodSignature); + if (mid) + result = JNICaller<T>::callStaticV(cls, mid, args); + else { + LOG_ERROR("Could not find method: %s for %p", methodName, cls); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf(stderr, "\n"); + } + } + + va_end(args); + + return result; +} + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) + +#endif // JNIUtility_h diff --git a/Source/WebCore/bridge/jni/jni_jsobject.h b/Source/WebCore/bridge/jni/jni_jsobject.h new file mode 100644 index 0000000..34a78ba --- /dev/null +++ b/Source/WebCore/bridge/jni/jni_jsobject.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H +#define JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H + +#if ENABLE(JAVA_BRIDGE) + +#include <JavaVM/jni.h> +#include <runtime/JSValue.h> +#include <wtf/RefPtr.h> + +#define jlong_to_ptr(a) ((void*)(uintptr_t)(a)) +#define jlong_to_impptr(a) (static_cast<JSC::JSObject*>(((void*)(uintptr_t)(a)))) +#define ptr_to_jlong(a) ((jlong)(uintptr_t)(a)) + +#if PLATFORM(MAC) + +namespace JSC { + +class ArgList; +class ExecState; +class JSObject; +class MarkedArgumentBuffer; + +namespace Bindings { + +class RootObject; + +enum JSObjectCallType { + CreateNative, + Call, + Eval, + GetMember, + SetMember, + RemoveMember, + GetSlot, + SetSlot, + ToString, + Finalize +}; + +struct JSObjectCallContext +{ + JSObjectCallType type; + jlong nativeHandle; + jstring string; + jobjectArray args; + jint index; + jobject value; + CFRunLoopRef originatingLoop; + jvalue result; +}; + +class JavaJSObject +{ +public: + JavaJSObject(jlong nativeHandle); + + static jlong createNative(jlong nativeHandle); + jobject call(jstring methodName, jobjectArray args) const; + jobject eval(jstring script) const; + jobject getMember(jstring memberName) const; + void setMember(jstring memberName, jobject value) const; + void removeMember(jstring memberName) const; + jobject getSlot(jint index) const; + void setSlot(jint index, jobject value) const; + jstring toString() const; + void finalize() const; + + static jvalue invoke(JSObjectCallContext*); + + jobject convertValueToJObject(JSValue) const; + JSValue convertJObjectToValue(ExecState*, jobject) const; + void getListFromJArray(ExecState*, jobjectArray, MarkedArgumentBuffer&) const; + + RootObject* rootObject() const; + + // Must be called from the thread that will be used to access JavaScript. + static void initializeJNIThreading(); +private: + RefPtr<RootObject> _rootObject; + JSObject* _imp; +}; + + +} // namespace Bindings + +} // namespace JSC + +extern "C" { + +// The Java VM calls these functions to handle calls to methods in Java's JSObject class. +jlong KJS_JSCreateNativeJSObject(JNIEnv*, jclass, jstring jurl, jlong nativeHandle, jboolean ctx); +void KJS_JSObject_JSFinalize(JNIEnv*, jclass, jlong nativeJSObject); +jobject KJS_JSObject_JSObjectCall(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring methodName, jobjectArray args, jboolean ctx); +jobject KJS_JSObject_JSObjectEval(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jscript, jboolean ctx); +jobject KJS_JSObject_JSObjectGetMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jboolean ctx); +void KJS_JSObject_JSObjectSetMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jobject value, jboolean ctx); +void KJS_JSObject_JSObjectRemoveMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jboolean ctx); +jobject KJS_JSObject_JSObjectGetSlot(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jint jindex, jboolean ctx); +void KJS_JSObject_JSObjectSetSlot(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jint jindex, jobject value, jboolean ctx); +jstring KJS_JSObject_JSObjectToString(JNIEnv*, jclass, jlong nativeJSObject); + +} + +#endif // PLATFORM(MAC) + +#endif // ENABLE(JAVA_BRIDGE) + +#endif // JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H diff --git a/Source/WebCore/bridge/jni/jni_jsobject.mm b/Source/WebCore/bridge/jni/jni_jsobject.mm new file mode 100644 index 0000000..f8ec1c4 --- /dev/null +++ b/Source/WebCore/bridge/jni/jni_jsobject.mm @@ -0,0 +1,705 @@ +/* + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "jni_jsobject.h" + +#if ENABLE(JAVA_BRIDGE) + +#include "Frame.h" +#include "JavaRuntimeObject.h" +#include "JNIBridge.h" +#include "JNIUtility.h" +#include "JNIUtilityPrivate.h" +#include "JSDOMBinding.h" +#include "Logging.h" +#include "ScriptController.h" +#include "StringSourceProvider.h" +#include "WebCoreFrameView.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <interpreter/CallFrame.h> +#include <runtime/Completion.h> +#include <runtime/JSGlobalObject.h> +#include <runtime/JSLock.h> + +using WebCore::Frame; + +using namespace JSC::Bindings; +using namespace JSC; +using namespace WebCore; + +#define UndefinedHandle 1 + +static CFRunLoopSourceRef _performJavaScriptSource; +static CFRunLoopRef _performJavaScriptRunLoop; + +// May only be set by dispatchToJavaScriptThread(). +static CFRunLoopSourceRef completionSource; + +static void completedJavaScriptAccess (void *i) +{ + ASSERT(CFRunLoopGetCurrent() != _performJavaScriptRunLoop); + + JSObjectCallContext *callContext = (JSObjectCallContext *)i; + CFRunLoopRef runLoop = (CFRunLoopRef)callContext->originatingLoop; + + ASSERT(CFRunLoopGetCurrent() == runLoop); + + CFRunLoopStop(runLoop); +} + +static pthread_once_t javaScriptAccessLockOnce = PTHREAD_ONCE_INIT; +static pthread_mutex_t javaScriptAccessLock; +static int javaScriptAccessLockCount = 0; + +static void initializeJavaScriptAccessLock() +{ + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&javaScriptAccessLock, &attr); +} + +static inline void lockJavaScriptAccess() +{ + // Perhaps add deadlock detection? + pthread_once(&javaScriptAccessLockOnce, initializeJavaScriptAccessLock); + pthread_mutex_lock(&javaScriptAccessLock); + javaScriptAccessLockCount++; +} + +static inline void unlockJavaScriptAccess() +{ + javaScriptAccessLockCount--; + pthread_mutex_unlock(&javaScriptAccessLock); +} + +static void dispatchToJavaScriptThread(JSObjectCallContext *context) +{ + // This lock guarantees that only one thread can invoke + // at a time, and also guarantees that completionSource; + // won't get clobbered. + lockJavaScriptAccess(); + + CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent(); + + ASSERT(currentRunLoop != _performJavaScriptRunLoop); + + // Setup a source to signal once the invocation of the JavaScript + // call completes. + // + // FIXME: This could be a potential performance issue. Creating and + // adding run loop sources is expensive. We could create one source + // per thread, as needed, instead. + context->originatingLoop = currentRunLoop; + CFRunLoopSourceContext sourceContext = {0, context, NULL, NULL, NULL, NULL, NULL, NULL, NULL, completedJavaScriptAccess}; + completionSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode); + + // Wakeup JavaScript access thread and make it do its work. + CFRunLoopSourceSignal(_performJavaScriptSource); + if (CFRunLoopIsWaiting(_performJavaScriptRunLoop)) + CFRunLoopWakeUp(_performJavaScriptRunLoop); + + // Wait until the JavaScript access thread is done. + CFRunLoopRun (); + + CFRunLoopRemoveSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode); + CFRelease (completionSource); + + unlockJavaScriptAccess(); +} + +static void performJavaScriptAccess(void*) +{ + ASSERT(CFRunLoopGetCurrent() == _performJavaScriptRunLoop); + + // Dispatch JavaScript calls here. + CFRunLoopSourceContext sourceContext; + CFRunLoopSourceGetContext (completionSource, &sourceContext); + JSObjectCallContext *callContext = (JSObjectCallContext *)sourceContext.info; + CFRunLoopRef originatingLoop = callContext->originatingLoop; + + JavaJSObject::invoke (callContext); + + // Signal the originating thread that we're done. + CFRunLoopSourceSignal (completionSource); + if (CFRunLoopIsWaiting(originatingLoop)) + CFRunLoopWakeUp(originatingLoop); +} + +// Must be called from the thread that will be used to access JavaScript. +void JavaJSObject::initializeJNIThreading() { + // Should only be called once. + ASSERT(!_performJavaScriptRunLoop); + + // Assume that we can retain this run loop forever. It'll most + // likely (always?) be the main loop. + _performJavaScriptRunLoop = (CFRunLoopRef)CFRetain(CFRunLoopGetCurrent()); + + // Setup a source the other threads can use to signal the _runLoop + // thread that a JavaScript call needs to be invoked. + CFRunLoopSourceContext sourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, performJavaScriptAccess}; + _performJavaScriptSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(_performJavaScriptRunLoop, _performJavaScriptSource, kCFRunLoopDefaultMode); +} + +static bool isJavaScriptThread() +{ + return (_performJavaScriptRunLoop == CFRunLoopGetCurrent()); +} + +jvalue JavaJSObject::invoke(JSObjectCallContext *context) +{ + jvalue result; + + bzero ((void *)&result, sizeof(jvalue)); + + if (!isJavaScriptThread()) { + // Send the call context to the thread that is allowed to + // call JavaScript. + dispatchToJavaScriptThread(context); + result = context->result; + } + else { + jlong nativeHandle = context->nativeHandle; + if (nativeHandle == UndefinedHandle || nativeHandle == 0) { + return result; + } + + if (context->type == CreateNative) { + result.j = JavaJSObject::createNative(nativeHandle); + } + else { + JSObject *imp = jlong_to_impptr(nativeHandle); + if (!findProtectingRootObject(imp)) { + LOG_ERROR("Attempt to access JavaScript from destroyed applet, type %d.", context->type); + return result; + } + + switch (context->type){ + case Call: { + result.l = JavaJSObject(nativeHandle).call(context->string, context->args); + break; + } + + case Eval: { + result.l = JavaJSObject(nativeHandle).eval(context->string); + break; + } + + case GetMember: { + result.l = JavaJSObject(nativeHandle).getMember(context->string); + break; + } + + case SetMember: { + JavaJSObject(nativeHandle).setMember(context->string, context->value); + break; + } + + case RemoveMember: { + JavaJSObject(nativeHandle).removeMember(context->string); + break; + } + + case GetSlot: { + result.l = JavaJSObject(nativeHandle).getSlot(context->index); + break; + } + + case SetSlot: { + JavaJSObject(nativeHandle).setSlot(context->index, context->value); + break; + } + + case ToString: { + result.l = (jobject) JavaJSObject(nativeHandle).toString(); + break; + } + + case Finalize: { + JavaJSObject(nativeHandle).finalize(); + break; + } + + default: { + LOG_ERROR("invalid JavaScript call"); + } + } + } + context->result = result; + } + + return result; +} + + +JavaJSObject::JavaJSObject(jlong nativeJSObject) +{ + _imp = jlong_to_impptr(nativeJSObject); + + ASSERT(_imp); + _rootObject = findProtectingRootObject(_imp); + ASSERT(_rootObject); +} + +RootObject* JavaJSObject::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +jobject JavaJSObject::call(jstring methodName, jobjectArray args) const +{ + LOG(LiveConnect, "JavaJSObject::call methodName = %s", JavaString(methodName).utf8()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + // Lookup the function object. + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + Identifier identifier(exec, JavaString(methodName)); + JSValue function = _imp->get(exec, identifier); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return 0; + + // Call the function object. + MarkedArgumentBuffer argList; + getListFromJArray(exec, args, argList); + rootObject->globalObject()->globalData().timeoutChecker.start(); + JSValue result = JSC::call(exec, function, callType, callData, _imp, argList); + rootObject->globalObject()->globalData().timeoutChecker.stop(); + + return convertValueToJObject(result); +} + +jobject JavaJSObject::eval(jstring script) const +{ + LOG(LiveConnect, "JavaJSObject::eval script = %s", JavaString(script).utf8()); + + JSValue result; + + JSLock lock(SilenceAssertionsOnly); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + rootObject->globalObject()->globalData().timeoutChecker.start(); + Completion completion = JSC::evaluate(rootObject->globalObject()->globalExec(), rootObject->globalObject()->globalScopeChain(), makeSource(JavaString(script)), JSC::JSValue()); + rootObject->globalObject()->globalData().timeoutChecker.stop(); + ComplType type = completion.complType(); + + if (type == Normal) { + result = completion.value(); + if (!result) + result = jsUndefined(); + } else + result = jsUndefined(); + + return convertValueToJObject (result); +} + +jobject JavaJSObject::getMember(jstring memberName) const +{ + LOG(LiveConnect, "JavaJSObject::getMember (%p) memberName = %s", _imp, JavaString(memberName).utf8()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(SilenceAssertionsOnly); + JSValue result = _imp->get(exec, Identifier(exec, JavaString(memberName))); + + return convertValueToJObject(result); +} + +void JavaJSObject::setMember(jstring memberName, jobject value) const +{ + LOG(LiveConnect, "JavaJSObject::setMember memberName = %s, value = %p", JavaString(memberName).utf8(), value); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(SilenceAssertionsOnly); + PutPropertySlot slot; + _imp->put(exec, Identifier(exec, JavaString(memberName)), convertJObjectToValue(exec, value), slot); +} + + +void JavaJSObject::removeMember(jstring memberName) const +{ + LOG(LiveConnect, "JavaJSObject::removeMember memberName = %s", JavaString(memberName).utf8()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + _imp->deleteProperty(exec, Identifier(exec, JavaString(memberName))); +} + + +jobject JavaJSObject::getSlot(jint index) const +{ + LOG(LiveConnect, "JavaJSObject::getSlot index = %ld", static_cast<long>(index)); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(SilenceAssertionsOnly); + JSValue result = _imp->get(exec, index); + + return convertValueToJObject(result); +} + + +void JavaJSObject::setSlot(jint index, jobject value) const +{ + LOG(LiveConnect, "JavaJSObject::setSlot index = %ld, value = %p", static_cast<long>(index), value); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(SilenceAssertionsOnly); + _imp->put(exec, (unsigned)index, convertJObjectToValue(exec, value)); +} + + +jstring JavaJSObject::toString() const +{ + LOG(LiveConnect, "JavaJSObject::toString"); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + JSLock lock(SilenceAssertionsOnly); + JSObject *thisObj = const_cast<JSObject*>(_imp); + ExecState* exec = rootObject->globalObject()->globalExec(); + + return static_cast<jstring>(convertValueToJValue(exec, rootObject, thisObj, object_type, "java.lang.String").l); +} + +void JavaJSObject::finalize() const +{ + if (RootObject* rootObject = this->rootObject()) + rootObject->gcUnprotect(_imp); +} + +static PassRefPtr<RootObject> createRootObject(void* nativeHandle) +{ + Frame* frame = 0; + for (NSView *view = (NSView *)nativeHandle; view; view = [view superview]) { + if ([view conformsToProtocol:@protocol(WebCoreFrameView)]) { + NSView<WebCoreFrameView> *webCoreFrameView = static_cast<NSView<WebCoreFrameView>*>(view); + frame = [webCoreFrameView _web_frame]; + break; + } + } + if (!frame) + return 0; + return frame->script()->createRootObject(nativeHandle); +} + +// We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or +// another JavaJSObject. +jlong JavaJSObject::createNative(jlong nativeHandle) +{ + LOG(LiveConnect, "JavaJSObject::createNative nativeHandle = %d", static_cast<int>(nativeHandle)); + + if (nativeHandle == UndefinedHandle) + return nativeHandle; + + if (findProtectingRootObject(jlong_to_impptr(nativeHandle))) + return nativeHandle; + + RefPtr<RootObject> rootObject = createRootObject(jlong_to_ptr(nativeHandle)); + + // If rootObject is !NULL We must have been called via netscape.javascript.JavaJSObject.getWindow(), + // otherwise we are being called after creating a JavaJSObject in + // JavaJSObject::convertValueToJObject(). + if (rootObject) { + JSObject* globalObject = rootObject->globalObject(); + // We call gcProtect here to get the object into the root object's "protect set" which + // is used to test if a native handle is valid as well as getting the root object given the handle. + rootObject->gcProtect(globalObject); + return ptr_to_jlong(globalObject); + } + + return nativeHandle; +} + +jobject JavaJSObject::convertValueToJObject(JSValue value) const +{ + JSLock lock(SilenceAssertionsOnly); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JNIEnv *env = getJNIEnv(); + jobject result = 0; + + // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition', + // figure 22-5. + // number -> java.lang.Double + // string -> java.lang.String + // boolean -> java.lang.Boolean + // Java instance -> Java instance + // Everything else -> JavaJSObject + + if (value.isNumber()) { + jclass JSObjectClass = env->FindClass ("java/lang/Double"); + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(D)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, (jdouble)value.toNumber(exec)); + } + } else if (value.isString()) { + UString stringValue = value.toString(exec); + JNIEnv *env = getJNIEnv(); + result = env->NewString ((const jchar *)stringValue.characters(), stringValue.length()); + } else if (value.isBoolean()) { + jclass JSObjectClass = env->FindClass ("java/lang/Boolean"); + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(Z)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, (jboolean)value.toBoolean(exec)); + } + } + else { + // Create a JavaJSObject. + jlong nativeHandle; + + if (value.isObject()) { + JSObject* object = asObject(value); + + // We either have a wrapper around a Java instance or a JavaScript + // object. If we have a wrapper around a Java instance, return that + // instance, otherwise create a new Java JavaJSObject with the JSObject* + // as its nativeHandle. + if (object->inherits(&JavaRuntimeObject::s_info)) { + JavaRuntimeObject* runtimeObject = static_cast<JavaRuntimeObject*>(object); + JavaInstance* runtimeInstance = runtimeObject->getInternalJavaInstance(); + if (!runtimeInstance) + return 0; + + return runtimeInstance->javaInstance(); + } else { + nativeHandle = ptr_to_jlong(object); + rootObject->gcProtect(object); + } + } else { + // All other types will result in an undefined object. + nativeHandle = UndefinedHandle; + } + + // Now create the Java JavaJSObject. Look for the JavaJSObject in its new (Tiger) + // location and in the original Java 1.4.2 location. + jclass JSObjectClass; + + JSObjectClass = env->FindClass ("sun/plugin/javascript/webkit/JSObject"); + if (!JSObjectClass) { + env->ExceptionDescribe(); + env->ExceptionClear(); + JSObjectClass = env->FindClass ("apple/applet/JSObject"); + } + + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(J)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, nativeHandle); + } + } + + return result; +} + +JSValue JavaJSObject::convertJObjectToValue(ExecState* exec, jobject theObject) const +{ + // Instances of netscape.javascript.JSObject get converted back to + // JavaScript objects. All other objects are wrapped. It's not + // possible to pass primitive types from the Java to JavaScript. + // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition', + // figure 22-4. + jobject classOfInstance = callJNIMethod<jobject>(theObject, "getClass", "()Ljava/lang/Class;"); + if (!classOfInstance) { + JSLock lock(SilenceAssertionsOnly); + return JavaInstance::create(theObject, _rootObject)->createRuntimeObject(exec); + } + + // Only the sun.plugin.javascript.webkit.JSObject has a member called nativeJSObject. This class is + // created above to wrap internal browser objects. The constructor of this class takes the native + // pointer and stores it in this object, so that it can be retrieved below. + jstring className = (jstring)callJNIMethod<jobject>(classOfInstance, "getName", "()Ljava/lang/String;"); + if (!className || (strcmp(JavaString(className).utf8(), "sun.plugin.javascript.webkit.JSObject") != 0)) { + JSLock lock(SilenceAssertionsOnly); + return JavaInstance::create(theObject, _rootObject)->createRuntimeObject(exec); + } + + // Pull the nativeJSObject value from the Java instance. This is a + // pointer to the JSObject. + JNIEnv *env = getJNIEnv(); + jfieldID fieldID = env->GetFieldID((jclass)classOfInstance, "nativeJSObject", "J"); + if (fieldID == NULL) + return jsUndefined(); + jlong nativeHandle = env->GetLongField(theObject, fieldID); + if (nativeHandle == UndefinedHandle) + return jsUndefined(); + JSObject *imp = static_cast<JSObject*>(jlong_to_impptr(nativeHandle)); + return imp; +} + +void JavaJSObject::getListFromJArray(ExecState* exec, jobjectArray jArray, MarkedArgumentBuffer& list) const +{ + JNIEnv *env = getJNIEnv(); + int numObjects = jArray ? env->GetArrayLength(jArray) : 0; + + for (int i = 0; i < numObjects; i++) { + jobject anObject = env->GetObjectArrayElement ((jobjectArray)jArray, i); + if (anObject) { + list.append(convertJObjectToValue(exec, anObject)); + env->DeleteLocalRef (anObject); + } + else { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } +} + +extern "C" { + +jlong KJS_JSCreateNativeJSObject (JNIEnv*, jclass, jstring, jlong nativeHandle, jboolean) +{ + JSObjectCallContext context; + context.type = CreateNative; + context.nativeHandle = nativeHandle; + return JavaJSObject::invoke (&context).j; +} + +void KJS_JSObject_JSFinalize (JNIEnv*, jclass, jlong nativeHandle) +{ + JSObjectCallContext context; + context.type = Finalize; + context.nativeHandle = nativeHandle; + JavaJSObject::invoke (&context); +} + +jobject KJS_JSObject_JSObjectCall (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring methodName, jobjectArray args, jboolean) +{ + JSObjectCallContext context; + context.type = Call; + context.nativeHandle = nativeHandle; + context.string = methodName; + context.args = args; + return JavaJSObject::invoke (&context).l; +} + +jobject KJS_JSObject_JSObjectEval (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jscript, jboolean) +{ + JSObjectCallContext context; + context.type = Eval; + context.nativeHandle = nativeHandle; + context.string = jscript; + return JavaJSObject::invoke (&context).l; +} + +jobject KJS_JSObject_JSObjectGetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean) +{ + JSObjectCallContext context; + context.type = GetMember; + context.nativeHandle = nativeHandle; + context.string = jname; + return JavaJSObject::invoke (&context).l; +} + +void KJS_JSObject_JSObjectSetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jobject value, jboolean) +{ + JSObjectCallContext context; + context.type = SetMember; + context.nativeHandle = nativeHandle; + context.string = jname; + context.value = value; + JavaJSObject::invoke (&context); +} + +void KJS_JSObject_JSObjectRemoveMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean) +{ + JSObjectCallContext context; + context.type = RemoveMember; + context.nativeHandle = nativeHandle; + context.string = jname; + JavaJSObject::invoke (&context); +} + +jobject KJS_JSObject_JSObjectGetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jboolean) +{ + JSObjectCallContext context; + context.type = GetSlot; + context.nativeHandle = nativeHandle; + context.index = jindex; + return JavaJSObject::invoke (&context).l; +} + +void KJS_JSObject_JSObjectSetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jobject value, jboolean) +{ + JSObjectCallContext context; + context.type = SetSlot; + context.nativeHandle = nativeHandle; + context.index = jindex; + context.value = value; + JavaJSObject::invoke (&context); +} + +jstring KJS_JSObject_JSObjectToString (JNIEnv*, jclass, jlong nativeHandle) +{ + JSObjectCallContext context; + context.type = ToString; + context.nativeHandle = nativeHandle; + return (jstring)JavaJSObject::invoke (&context).l; +} + +} + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/jni_objc.mm b/Source/WebCore/bridge/jni/jni_objc.mm new file mode 100644 index 0000000..8fa2c3f --- /dev/null +++ b/Source/WebCore/bridge/jni/jni_objc.mm @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(JAVA_BRIDGE) + +#import <Foundation/Foundation.h> +#import "JNIUtility.h" +#import "JNIUtilityPrivate.h" +#import "objc_utility.h" +#include <runtime/JSLock.h> + +using namespace JSC::Bindings; + +@interface NSObject (WebScriptingPrivate) +- (jvalue)webPlugInCallJava:(jobject)object method:(jmethodID)method returnType:(JNIType)returnType arguments:(jvalue*)args; +- (jvalue)webPlugInCallJava:(jobject)object + isStatic:(BOOL)isStatic + returnType:(JNIType)returnType + method:(jmethodID)method + arguments:(jvalue*)args + callingURL:(NSURL *)url + exceptionDescription:(NSString **)exceptionString; +@end + +bool JSC::Bindings::dispatchJNICall(ExecState* exec, const void* targetAppletView, jobject obj, bool isStatic, JNIType returnType, jmethodID methodID, jvalue* args, jvalue &result, const char*, JSValue& exceptionDescription) +{ + id view = (id)targetAppletView; + + // As array_type is not known by the Mac JVM, change it to a compatible type. + if (returnType == array_type) + returnType = object_type; + + if ([view respondsToSelector:@selector(webPlugInCallJava:isStatic:returnType:method:arguments:callingURL:exceptionDescription:)]) { + NSString *_exceptionDescription = 0; + + // Passing nil as the calling URL will cause the Java plugin to use the URL + // of the page that contains the applet. The execution restrictions + // implemented in WebCore will guarantee that only appropriate JavaScript + // can reference the applet. + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + result = [view webPlugInCallJava:obj isStatic:isStatic returnType:returnType method:methodID arguments:args callingURL:nil exceptionDescription:&_exceptionDescription]; + } + + if (_exceptionDescription != 0) { + exceptionDescription = convertNSStringToString(exec, _exceptionDescription); + } + return true; + } + else if ([view respondsToSelector:@selector(webPlugInCallJava:method:returnType:arguments:)]) { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + result = [view webPlugInCallJava:obj method:methodID returnType:returnType arguments:args]; + return true; + } + + bzero (&result, sizeof(jvalue)); + return false; +} + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/jsc/JNIBridgeJSC.cpp b/Source/WebCore/bridge/jni/jsc/JNIBridgeJSC.cpp new file mode 100644 index 0000000..3c16d05 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JNIBridgeJSC.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007, 2009 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JNIBridgeJSC.h" + +#if ENABLE(JAVA_BRIDGE) + +#include "JNIUtilityPrivate.h" +#include "Logging.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include <runtime/Error.h> + +using namespace JSC; +using namespace JSC::Bindings; +using namespace WebCore; + +JavaField::JavaField(JNIEnv* env, jobject aField) +{ + // Get field type name + jstring fieldTypeName = 0; + if (jobject fieldType = callJNIMethod<jobject>(aField, "getType", "()Ljava/lang/Class;")) + fieldTypeName = static_cast<jstring>(callJNIMethod<jobject>(fieldType, "getName", "()Ljava/lang/String;")); + if (!fieldTypeName) + fieldTypeName = env->NewStringUTF("<Unknown>"); + m_type = JavaString(env, fieldTypeName); + + m_JNIType = JNITypeFromClassName(m_type.utf8()); + + // Get field name + jstring fieldName = static_cast<jstring>(callJNIMethod<jobject>(aField, "getName", "()Ljava/lang/String;")); + if (!fieldName) + fieldName = env->NewStringUTF("<Unknown>"); + m_name = JavaString(env, fieldName); + + m_field = new JObjectWrapper(aField); +} + +JSValue JavaArray::convertJObjectToArray(ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject> rootObject) +{ + if (type[0] != '[') + return jsUndefined(); + + return new (exec) RuntimeArray(exec, new JavaArray(anObject, type, rootObject)); +} + +jvalue JavaField::dispatchValueFromInstance(ExecState* exec, const JavaInstance* instance, const char* name, const char* sig, JNIType returnType) const +{ + jobject jinstance = instance->javaInstance(); + jobject fieldJInstance = m_field->m_instance; + JNIEnv* env = getJNIEnv(); + jvalue result; + + memset(&result, 0, sizeof(jvalue)); + jclass cls = env->GetObjectClass(fieldJInstance); + if (cls) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if (mid) { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue exceptionDescription; + jvalue args[1]; + + args[0].l = jinstance; + dispatchJNICall(exec, rootObject->nativeHandle(), fieldJInstance, false, returnType, mid, args, result, 0, exceptionDescription); + if (exceptionDescription) + throwError(exec, createError(exec, exceptionDescription.toString(exec))); + } + } + } + return result; +} + +JSValue JavaField::valueFromInstance(ExecState* exec, const Instance* i) const +{ + const JavaInstance* instance = static_cast<const JavaInstance*>(i); + + JSValue jsresult = jsUndefined(); + + switch (m_JNIType) { + case array_type: + case object_type: + { + jvalue result = dispatchValueFromInstance(exec, instance, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", object_type); + jobject anObject = result.l; + + if (!anObject) + return jsNull(); + + const char* arrayType = type(); + if (arrayType[0] == '[') + jsresult = JavaArray::convertJObjectToArray(exec, anObject, arrayType, instance->rootObject()); + else if (anObject) + jsresult = JavaInstance::create(anObject, instance->rootObject())->createRuntimeObject(exec); + } + break; + + case boolean_type: + jsresult = jsBoolean(dispatchValueFromInstance(exec, instance, "getBoolean", "(Ljava/lang/Object;)Z", boolean_type).z); + break; + + case byte_type: + case char_type: + case short_type: + + case int_type: + { + jint value; + jvalue result = dispatchValueFromInstance(exec, instance, "getInt", "(Ljava/lang/Object;)I", int_type); + value = result.i; + jsresult = jsNumber(static_cast<int>(value)); + } + break; + + case long_type: + case float_type: + case double_type: + { + jdouble value; + jvalue result = dispatchValueFromInstance(exec, instance, "getDouble", "(Ljava/lang/Object;)D", double_type); + value = result.i; + jsresult = jsNumber(static_cast<double>(value)); + } + break; + default: + break; + } + + LOG(LiveConnect, "JavaField::valueFromInstance getting %s = %s", UString(name()).utf8().data(), jsresult.toString(exec).ascii().data()); + + return jsresult; +} + +void JavaField::dispatchSetValueToInstance(ExecState* exec, const JavaInstance* instance, jvalue javaValue, const char* name, const char* sig) const +{ + jobject jinstance = instance->javaInstance(); + jobject fieldJInstance = m_field->m_instance; + JNIEnv* env = getJNIEnv(); + + jclass cls = env->GetObjectClass(fieldJInstance); + if (cls) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if (mid) { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue exceptionDescription; + jvalue args[2]; + jvalue result; + + args[0].l = jinstance; + args[1] = javaValue; + dispatchJNICall(exec, rootObject->nativeHandle(), fieldJInstance, false, void_type, mid, args, result, 0, exceptionDescription); + if (exceptionDescription) + throwError(exec, createError(exec, exceptionDescription.toString(exec))); + } + } + } +} + +void JavaField::setValueToInstance(ExecState* exec, const Instance* i, JSValue aValue) const +{ + const JavaInstance* instance = static_cast<const JavaInstance*>(i); + jvalue javaValue = convertValueToJValue(exec, i->rootObject(), aValue, m_JNIType, type()); + + LOG(LiveConnect, "JavaField::setValueToInstance setting value %s to %s", UString(name()).utf8().data(), aValue.toString(exec).ascii().data()); + + switch (m_JNIType) { + case array_type: + case object_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V"); + } + break; + + case boolean_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setBoolean", "(Ljava/lang/Object;Z)V"); + } + break; + + case byte_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setByte", "(Ljava/lang/Object;B)V"); + } + break; + + case char_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setChar", "(Ljava/lang/Object;C)V"); + } + break; + + case short_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setShort", "(Ljava/lang/Object;S)V"); + } + break; + + case int_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setInt", "(Ljava/lang/Object;I)V"); + } + break; + + case long_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setLong", "(Ljava/lang/Object;J)V"); + } + break; + + case float_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setFloat", "(Ljava/lang/Object;F)V"); + } + break; + + case double_type: + { + dispatchSetValueToInstance(exec, instance, javaValue, "setDouble", "(Ljava/lang/Object;D)V"); + } + break; + default: + break; + } +} + +JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject) + : Array(rootObject) +{ + m_array = new JObjectWrapper(array); + // Java array are fixed length, so we can cache length. + JNIEnv* env = getJNIEnv(); + m_length = env->GetArrayLength(static_cast<jarray>(m_array->m_instance)); + m_type = strdup(type); +} + +JavaArray::~JavaArray() +{ + free(const_cast<char*>(m_type)); +} + +RootObject* JavaArray::rootObject() const +{ + return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0; +} + +void JavaArray::setValueAt(ExecState* exec, unsigned index, JSValue aValue) const +{ + JNIEnv* env = getJNIEnv(); + char* javaClassName = 0; + + JNIType arrayType = JNITypeFromPrimitiveType(m_type[1]); + if (m_type[1] == 'L') { + // The type of the array will be something like: + // "[Ljava.lang.string;". This is guaranteed, so no need + // for extra sanity checks. + javaClassName = strdup(&m_type[2]); + javaClassName[strchr(javaClassName, ';')-javaClassName] = 0; + } + jvalue aJValue = convertValueToJValue(exec, m_rootObject.get(), aValue, arrayType, javaClassName); + + switch (arrayType) { + case object_type: + { + env->SetObjectArrayElement(static_cast<jobjectArray>(javaArray()), index, aJValue.l); + break; + } + + case boolean_type: + { + env->SetBooleanArrayRegion(static_cast<jbooleanArray>(javaArray()), index, 1, &aJValue.z); + break; + } + + case byte_type: + { + env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray()), index, 1, &aJValue.b); + break; + } + + case char_type: + { + env->SetCharArrayRegion(static_cast<jcharArray>(javaArray()), index, 1, &aJValue.c); + break; + } + + case short_type: + { + env->SetShortArrayRegion(static_cast<jshortArray>(javaArray()), index, 1, &aJValue.s); + break; + } + + case int_type: + { + env->SetIntArrayRegion(static_cast<jintArray>(javaArray()), index, 1, &aJValue.i); + break; + } + + case long_type: + { + env->SetLongArrayRegion(static_cast<jlongArray>(javaArray()), index, 1, &aJValue.j); + } + + case float_type: + { + env->SetFloatArrayRegion(static_cast<jfloatArray>(javaArray()), index, 1, &aJValue.f); + break; + } + + case double_type: + { + env->SetDoubleArrayRegion(static_cast<jdoubleArray>(javaArray()), index, 1, &aJValue.d); + break; + } + default: + break; + } + + if (javaClassName) + free(const_cast<char*>(javaClassName)); +} + + +JSValue JavaArray::valueAt(ExecState* exec, unsigned index) const +{ + JNIEnv* env = getJNIEnv(); + JNIType arrayType = JNITypeFromPrimitiveType(m_type[1]); + switch (arrayType) { + case object_type: + { + jobjectArray objectArray = static_cast<jobjectArray>(javaArray()); + jobject anObject; + anObject = env->GetObjectArrayElement(objectArray, index); + + // No object? + if (!anObject) + return jsNull(); + + // Nested array? + if (m_type[1] == '[') + return JavaArray::convertJObjectToArray(exec, anObject, m_type + 1, rootObject()); + // or array of other object type? + return JavaInstance::create(anObject, rootObject())->createRuntimeObject(exec); + } + + case boolean_type: + { + jbooleanArray booleanArray = static_cast<jbooleanArray>(javaArray()); + jboolean aBoolean; + env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean); + return jsBoolean(aBoolean); + } + + case byte_type: + { + jbyteArray byteArray = static_cast<jbyteArray>(javaArray()); + jbyte aByte; + env->GetByteArrayRegion(byteArray, index, 1, &aByte); + return jsNumber(aByte); + } + + case char_type: + { + jcharArray charArray = static_cast<jcharArray>(javaArray()); + jchar aChar; + env->GetCharArrayRegion(charArray, index, 1, &aChar); + return jsNumber(aChar); + break; + } + + case short_type: + { + jshortArray shortArray = static_cast<jshortArray>(javaArray()); + jshort aShort; + env->GetShortArrayRegion(shortArray, index, 1, &aShort); + return jsNumber(aShort); + } + + case int_type: + { + jintArray intArray = static_cast<jintArray>(javaArray()); + jint anInt; + env->GetIntArrayRegion(intArray, index, 1, &anInt); + return jsNumber(anInt); + } + + case long_type: + { + jlongArray longArray = static_cast<jlongArray>(javaArray()); + jlong aLong; + env->GetLongArrayRegion(longArray, index, 1, &aLong); + return jsNumber(aLong); + } + + case float_type: + { + jfloatArray floatArray = static_cast<jfloatArray>(javaArray()); + jfloat aFloat; + env->GetFloatArrayRegion(floatArray, index, 1, &aFloat); + return jsNumber(aFloat); + } + + case double_type: + { + jdoubleArray doubleArray = static_cast<jdoubleArray>(javaArray()); + jdouble aDouble; + env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble); + return jsNumber(aDouble); + } + default: + break; + } + return jsUndefined(); +} + +unsigned int JavaArray::getLength() const +{ + return m_length; +} + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/jsc/JNIBridgeJSC.h b/Source/WebCore/bridge/jni/jsc/JNIBridgeJSC.h new file mode 100644 index 0000000..afb1bce --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JNIBridgeJSC.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JNIBridgeJSC_h +#define JNIBridgeJSC_h + +#if ENABLE(JAVA_BRIDGE) + +#include "Bridge.h" +#include "JNIBridge.h" +#include <JavaVM/jni.h> + +namespace JSC { + +namespace Bindings { + +class JavaField : public Field { +public: + JavaField(JNIEnv*, jobject aField); + + virtual JSValue valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const; + + const JavaString& name() const { return m_name; } + virtual RuntimeType type() const { return m_type.utf8(); } + + JNIType getJNIType() const { return m_JNIType; } + +private: + void dispatchSetValueToInstance(ExecState*, const JavaInstance*, jvalue, const char* name, const char* sig) const; + jvalue dispatchValueFromInstance(ExecState*, const JavaInstance*, const char* name, const char* sig, JNIType returnType) const; + + JavaString m_name; + JavaString m_type; + JNIType m_JNIType; + RefPtr<JObjectWrapper> m_field; +}; + +class JavaArray : public Array { +public: + JavaArray(jobject array, const char* type, PassRefPtr<RootObject>); + virtual ~JavaArray(); + + RootObject* rootObject() const; + + virtual void setValueAt(ExecState*, unsigned int index, JSValue) const; + virtual JSValue valueAt(ExecState*, unsigned int index) const; + virtual unsigned int getLength() const; + + jobject javaArray() const { return m_array->m_instance; } + + static JSValue convertJObjectToArray(ExecState*, jobject, const char* type, PassRefPtr<RootObject>); + +private: + RefPtr<JObjectWrapper> m_array; + unsigned int m_length; + const char* m_type; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) + +#endif // JNIBridge_h diff --git a/Source/WebCore/bridge/jni/jsc/JNIUtilityPrivate.cpp b/Source/WebCore/bridge/jni/jsc/JNIUtilityPrivate.cpp new file mode 100644 index 0000000..bf19642 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JNIUtilityPrivate.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2003, 2010 Apple, Inc. All rights reserved. + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JNIUtilityPrivate.h" + +#if ENABLE(JAVA_BRIDGE) + +#include "JavaRuntimeObject.h" +#include "JNIBridgeJSC.h" +#include "jni_jsobject.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include <runtime/JSArray.h> +#include <runtime/JSLock.h> + +namespace JSC { + +namespace Bindings { + +static jobject convertArrayInstanceToJavaArray(ExecState* exec, JSArray* jsArray, const char* javaClassName) +{ + JNIEnv* env = getJNIEnv(); + // As JS Arrays can contain a mixture of objects, assume we can convert to + // the requested Java Array type requested, unless the array type is some object array + // other than a string. + unsigned length = jsArray->length(); + jobjectArray jarray = 0; + + // Build the correct array type + switch (JNITypeFromPrimitiveType(javaClassName[1])) { + case object_type: + { + // Only support string object types + if (!strcmp("[Ljava.lang.String;", javaClassName)) { + jarray = (jobjectArray)env->NewObjectArray(length, + env->FindClass("java/lang/String"), + env->NewStringUTF("")); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + UString stringValue = item.toString(exec); + env->SetObjectArrayElement(jarray, i, + env->functions->NewString(env, (const jchar *)stringValue.characters(), stringValue.length())); + } + } + break; + } + + case boolean_type: + { + jarray = (jobjectArray)env->NewBooleanArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + jboolean value = (jboolean)item.toNumber(exec); + env->SetBooleanArrayRegion((jbooleanArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case byte_type: + { + jarray = (jobjectArray)env->NewByteArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + jbyte value = (jbyte)item.toNumber(exec); + env->SetByteArrayRegion((jbyteArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case char_type: + { + jarray = (jobjectArray)env->NewCharArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + UString stringValue = item.toString(exec); + jchar value = 0; + if (stringValue.length() > 0) + value = ((const jchar*)stringValue.characters())[0]; + env->SetCharArrayRegion((jcharArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case short_type: + { + jarray = (jobjectArray)env->NewShortArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + jshort value = (jshort)item.toNumber(exec); + env->SetShortArrayRegion((jshortArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case int_type: + { + jarray = (jobjectArray)env->NewIntArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + jint value = (jint)item.toNumber(exec); + env->SetIntArrayRegion((jintArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case long_type: + { + jarray = (jobjectArray)env->NewLongArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + jlong value = (jlong)item.toNumber(exec); + env->SetLongArrayRegion((jlongArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case float_type: + { + jarray = (jobjectArray)env->NewFloatArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + jfloat value = (jfloat)item.toNumber(exec); + env->SetFloatArrayRegion((jfloatArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case double_type: + { + jarray = (jobjectArray)env->NewDoubleArray(length); + for (unsigned i = 0; i < length; i++) { + JSValue item = jsArray->get(exec, i); + jdouble value = (jdouble)item.toNumber(exec); + env->SetDoubleArrayRegion((jdoubleArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case array_type: // don't handle embedded arrays + case void_type: // Don't expect arrays of void objects + case invalid_type: // Array of unknown objects + break; + } + + // if it was not one of the cases handled, then null is returned + return jarray; +} + +jvalue convertValueToJValue(ExecState* exec, RootObject* rootObject, JSValue value, JNIType jniType, const char* javaClassName) +{ + JSLock lock(SilenceAssertionsOnly); + + jvalue result; + memset(&result, 0, sizeof(jvalue)); + + switch (jniType) { + case array_type: + case object_type: + { + // FIXME: JavaJSObject::convertValueToJObject functionality is almost exactly the same, + // these functions should use common code. + + if (value.isObject()) { + JSObject* object = asObject(value); + if (object->inherits(&JavaRuntimeObject::s_info)) { + // Unwrap a Java instance. + JavaRuntimeObject* runtimeObject = static_cast<JavaRuntimeObject*>(object); + JavaInstance* instance = runtimeObject->getInternalJavaInstance(); + if (instance) + result.l = instance->javaInstance(); + } else if (object->classInfo() == &RuntimeArray::s_info) { + // Input is a JavaScript Array that was originally created from a Java Array + RuntimeArray* imp = static_cast<RuntimeArray*>(object); + JavaArray* array = static_cast<JavaArray*>(imp->getConcreteArray()); + result.l = array->javaArray(); + } else if (object->classInfo() == &JSArray::info) { + // Input is a Javascript Array. We need to create it to a Java Array. + result.l = convertArrayInstanceToJavaArray(exec, asArray(value), javaClassName); + } else if ((!result.l && (!strcmp(javaClassName, "java.lang.Object"))) + || (!strcmp(javaClassName, "netscape.javascript.JSObject"))) { + // Wrap objects in JSObject instances. + JNIEnv* env = getJNIEnv(); + jclass jsObjectClass = env->FindClass("sun/plugin/javascript/webkit/JSObject"); + jmethodID constructorID = env->GetMethodID(jsObjectClass, "<init>", "(J)V"); + if (constructorID) { + jlong nativeHandle = ptr_to_jlong(object); + rootObject->gcProtect(object); + result.l = env->NewObject(jsObjectClass, constructorID, nativeHandle); + } + } + } + + // Create an appropriate Java object if target type is java.lang.Object. + if (!result.l && !strcmp(javaClassName, "java.lang.Object")) { + if (value.isString()) { + UString stringValue = asString(value)->value(exec); + JNIEnv* env = getJNIEnv(); + jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.characters(), stringValue.length()); + result.l = javaString; + } else if (value.isNumber()) { + double doubleValue = value.uncheckedGetNumber(); + JNIEnv* env = getJNIEnv(); + jclass clazz = env->FindClass("java/lang/Double"); + jmethodID constructor = env->GetMethodID(clazz, "<init>", "(D)V"); + jobject javaDouble = env->functions->NewObject(env, clazz, constructor, doubleValue); + result.l = javaDouble; + } else if (value.isBoolean()) { + bool boolValue = value.getBoolean(); + JNIEnv* env = getJNIEnv(); + jclass clazz = env->FindClass("java/lang/Boolean"); + jmethodID constructor = env->GetMethodID(clazz, "<init>", "(Z)V"); + jobject javaBoolean = env->functions->NewObject(env, clazz, constructor, boolValue); + result.l = javaBoolean; + } else if (value.isUndefined()) { + UString stringValue = "undefined"; + JNIEnv* env = getJNIEnv(); + jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.characters(), stringValue.length()); + result.l = javaString; + } + } + + // Convert value to a string if the target type is a java.lang.String, and we're not + // converting from a null. + if (!result.l && !strcmp(javaClassName, "java.lang.String")) { + if (!value.isNull()) { + UString stringValue = value.toString(exec); + JNIEnv* env = getJNIEnv(); + jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.characters(), stringValue.length()); + result.l = javaString; + } + } + } + break; + + case boolean_type: + { + result.z = (jboolean)value.toNumber(exec); + } + break; + + case byte_type: + { + result.b = (jbyte)value.toNumber(exec); + } + break; + + case char_type: + { + result.c = (jchar)value.toNumber(exec); + } + break; + + case short_type: + { + result.s = (jshort)value.toNumber(exec); + } + break; + + case int_type: + { + result.i = (jint)value.toNumber(exec); + } + break; + + case long_type: + { + result.j = (jlong)value.toNumber(exec); + } + break; + + case float_type: + { + result.f = (jfloat)value.toNumber(exec); + } + break; + + case double_type: + { + result.d = (jdouble)value.toNumber(exec); + } + break; + + case invalid_type: + case void_type: + break; + } + return result; +} + +} // end of namespace Bindings + +} // end of namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/jsc/JNIUtilityPrivate.h b/Source/WebCore/bridge/jni/jsc/JNIUtilityPrivate.h new file mode 100644 index 0000000..1266acd --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JNIUtilityPrivate.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JNIUtilityPrivate_h +#define JNIUtilityPrivate_h + +#if ENABLE(JAVA_BRIDGE) + +#include "JNIUtility.h" +#include <runtime/JSValue.h> + +namespace JSC { + +class ExecState; +class JSObject; + +namespace Bindings { + +class RootObject; + +jvalue convertValueToJValue(ExecState*, RootObject*, JSValue, JNIType, const char* javaClassName); +bool dispatchJNICall(ExecState*, const void* targetAppletView, jobject obj, bool isStatic, JNIType returnType, jmethodID methodID, jvalue* args, jvalue& result, const char* callingURL, JSValue& exceptionDescription); + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) + +#endif // JNIUtilityPrivate_h diff --git a/Source/WebCore/bridge/jni/jsc/JavaClassJSC.cpp b/Source/WebCore/bridge/jni/jsc/JavaClassJSC.cpp new file mode 100644 index 0000000..43cdc96 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JavaClassJSC.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JavaClassJSC.h" + +#if ENABLE(JAVA_BRIDGE) + +#include "JNIUtility.h" +#include "JSDOMWindow.h" +#include <runtime/Identifier.h> +#include <runtime/JSLock.h> + +using namespace JSC::Bindings; + +JavaClass::JavaClass(jobject anInstance) +{ + jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;"); + + if (!aClass) { + LOG_ERROR("Unable to call getClass on instance %p", anInstance); + m_name = fastStrDup("<Unknown>"); + return; + } + + if (jstring className = (jstring)callJNIMethod<jobject>(aClass, "getName", "()Ljava/lang/String;")) { + const char* classNameC = getCharactersFromJString(className); + m_name = fastStrDup(classNameC); + releaseCharactersForJString(className, classNameC); + } else + m_name = fastStrDup("<Unknown>"); + + int i; + JNIEnv* env = getJNIEnv(); + + // Get the fields + if (jarray fields = (jarray)callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;")) { + int numFields = env->GetArrayLength(fields); + for (i = 0; i < numFields; i++) { + jobject aJField = env->GetObjectArrayElement((jobjectArray)fields, i); + JavaField* aField = new JavaField(env, aJField); // deleted in the JavaClass destructor + { + JSLock lock(SilenceAssertionsOnly); + m_fields.set(((UString)aField->name()).impl(), aField); + } + env->DeleteLocalRef(aJField); + } + env->DeleteLocalRef(fields); + } + + // Get the methods + if (jarray methods = (jarray)callJNIMethod<jobject>(aClass, "getMethods", "()[Ljava/lang/reflect/Method;")) { + int numMethods = env->GetArrayLength(methods); + for (i = 0; i < numMethods; i++) { + jobject aJMethod = env->GetObjectArrayElement((jobjectArray)methods, i); + JavaMethod* aMethod = new JavaMethod(env, aJMethod); // deleted in the JavaClass destructor + MethodList* methodList; + { + JSLock lock(SilenceAssertionsOnly); + + methodList = m_methods.get(((UString)aMethod->name()).impl()); + if (!methodList) { + methodList = new MethodList(); + m_methods.set(((UString)aMethod->name()).impl(), methodList); + } + } + methodList->append(aMethod); + env->DeleteLocalRef(aJMethod); + } + env->DeleteLocalRef(methods); + } + + env->DeleteLocalRef(aClass); +} + +JavaClass::~JavaClass() +{ + fastFree(const_cast<char*>(m_name)); + + JSLock lock(SilenceAssertionsOnly); + + deleteAllValues(m_fields); + m_fields.clear(); + + MethodListMap::const_iterator end = m_methods.end(); + for (MethodListMap::const_iterator it = m_methods.begin(); it != end; ++it) { + const MethodList* methodList = it->second; + deleteAllValues(*methodList); + delete methodList; + } + m_methods.clear(); +} + +MethodList JavaClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList* methodList = m_methods.get(identifier.ustring().impl()); + + if (methodList) + return *methodList; + return MethodList(); +} + +Field* JavaClass::fieldNamed(const Identifier& identifier, Instance*) const +{ + return m_fields.get(identifier.ustring().impl()); +} + +bool JavaClass::isNumberClass() const +{ + return (!strcmp(m_name, "java.lang.Byte") + || !strcmp(m_name, "java.lang.Short") + || !strcmp(m_name, "java.lang.Integer") + || !strcmp(m_name, "java.lang.Long") + || !strcmp(m_name, "java.lang.Float") + || !strcmp(m_name, "java.lang.Double")); +} + +bool JavaClass::isBooleanClass() const +{ + return !strcmp(m_name, "java.lang.Boolean"); +} + +bool JavaClass::isStringClass() const +{ + return !strcmp(m_name, "java.lang.String"); +} + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/jsc/JavaClassJSC.h b/Source/WebCore/bridge/jni/jsc/JavaClassJSC.h new file mode 100644 index 0000000..0527162 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JavaClassJSC.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaClassJSC_h +#define JavaClassJSC_h + +#if ENABLE(JAVA_BRIDGE) + +#include "JNIBridgeJSC.h" +#include <wtf/HashMap.h> + +namespace JSC { + +namespace Bindings { + +class JavaClass : public Class { +public: + JavaClass(jobject); + ~JavaClass(); + + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; + + bool isNumberClass() const; + bool isBooleanClass() const; + bool isStringClass() const; + +private: + const char* m_name; + FieldMap m_fields; + MethodListMap m_methods; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) + +#endif // JavaClassJSC_h diff --git a/Source/WebCore/bridge/jni/jsc/JavaInstanceJSC.cpp b/Source/WebCore/bridge/jni/jsc/JavaInstanceJSC.cpp new file mode 100644 index 0000000..6332545 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JavaInstanceJSC.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2003, 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JavaInstanceJSC.h" + +#if ENABLE(JAVA_BRIDGE) + +#include "JavaRuntimeObject.h" +#include "JNIBridgeJSC.h" +#include "JNIUtility.h" +#include "JNIUtilityPrivate.h" +#include "JavaClassJSC.h" +#include "Logging.h" +#include "jni_jsobject.h" +#include "runtime_method.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/ArgList.h> +#include <runtime/Error.h> +#include <runtime/JSLock.h> + +using namespace JSC::Bindings; +using namespace JSC; +using namespace WebCore; + +JavaInstance::JavaInstance(jobject instance, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) +{ + m_instance = new JObjectWrapper(instance); + m_class = 0; +} + +JavaInstance::~JavaInstance() +{ + delete m_class; +} + +RuntimeObject* JavaInstance::newRuntimeObject(ExecState* exec) +{ + return new (exec) JavaRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +#define NUM_LOCAL_REFS 64 + +void JavaInstance::virtualBegin() +{ + getJNIEnv()->PushLocalFrame(NUM_LOCAL_REFS); +} + +void JavaInstance::virtualEnd() +{ + getJNIEnv()->PopLocalFrame(0); +} + +Class* JavaInstance::getClass() const +{ + if (!m_class) + m_class = new JavaClass (m_instance->m_instance); + return m_class; +} + +JSValue JavaInstance::stringValue(ExecState* exec) const +{ + JSLock lock(SilenceAssertionsOnly); + + jstring stringValue = (jstring)callJNIMethod<jobject>(m_instance->m_instance, "toString", "()Ljava/lang/String;"); + + // Should throw a JS exception, rather than returning ""? - but better than a null dereference. + if (!stringValue) + return jsString(exec, UString()); + + JNIEnv* env = getJNIEnv(); + const jchar* c = getUCharactersFromJStringInEnv(env, stringValue); + UString u((const UChar*)c, (int)env->GetStringLength(stringValue)); + releaseUCharactersForJStringInEnv(env, stringValue, c); + return jsString(exec, u); +} + +JSValue JavaInstance::numberValue(ExecState*) const +{ + jdouble doubleValue = callJNIMethod<jdouble>(m_instance->m_instance, "doubleValue", "()D"); + return jsNumber(doubleValue); +} + +JSValue JavaInstance::booleanValue() const +{ + jboolean booleanValue = callJNIMethod<jboolean>(m_instance->m_instance, "booleanValue", "()Z"); + return jsBoolean(booleanValue); +} + +class JavaRuntimeMethod : public RuntimeMethod { +public: + JavaRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list) + : RuntimeMethod(exec, globalObject, name, list) + { + } + + virtual const ClassInfo* classInfo() const { return &s_info; } + + static const ClassInfo s_info; +}; + +const ClassInfo JavaRuntimeMethod::s_info = { "JavaRuntimeMethod", &RuntimeMethod::s_info, 0, 0 }; + +JSValue JavaInstance::getMethod(ExecState* exec, const Identifier& propertyName) +{ + MethodList methodList = getClass()->methodsNamed(propertyName, this); + return new (exec) JavaRuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue JavaInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod) +{ + if (!asObject(runtimeMethod)->inherits(&JavaRuntimeMethod::s_info)) + return throwError(exec, createTypeError(exec, "Attempt to invoke non-Java method on Java object.")); + + const MethodList& methodList = *runtimeMethod->methods(); + + int i; + int count = exec->argumentCount(); + JSValue resultValue; + Method* method = 0; + size_t numMethods = methodList.size(); + + // Try to find a good match for the overloaded method. The + // fundamental problem is that JavaScript doesn't have the + // notion of method overloading and Java does. We could + // get a bit more sophisticated and attempt to does some + // type checking as we as checking the number of parameters. + for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { + Method* aMethod = methodList[methodIndex]; + if (aMethod->numParameters() == count) { + method = aMethod; + break; + } + } + if (!method) { + LOG(LiveConnect, "JavaInstance::invokeMethod unable to find an appropiate method"); + return jsUndefined(); + } + + const JavaMethod* jMethod = static_cast<const JavaMethod*>(method); + LOG(LiveConnect, "JavaInstance::invokeMethod call %s %s on %p", UString(jMethod->name()).utf8().data(), jMethod->signature(), m_instance->m_instance); + + Vector<jvalue> jArgs(count); + + for (i = 0; i < count; i++) { + JavaParameter* aParameter = jMethod->parameterAt(i); + jArgs[i] = convertValueToJValue(exec, m_rootObject.get(), exec->argument(i), aParameter->getJNIType(), aParameter->type()); + LOG(LiveConnect, "JavaInstance::invokeMethod arg[%d] = %s", i, exec->argument(i).toString(exec).ascii().data()); + } + + jvalue result; + + // Try to use the JNI abstraction first, otherwise fall back to + // normal JNI. The JNI dispatch abstraction allows the Java plugin + // to dispatch the call on the appropriate internal VM thread. + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return jsUndefined(); + + bool handled = false; + if (rootObject->nativeHandle()) { + jobject obj = m_instance->m_instance; + JSValue exceptionDescription; + const char *callingURL = 0; // FIXME, need to propagate calling URL to Java + handled = dispatchJNICall(exec, rootObject->nativeHandle(), obj, jMethod->isStatic(), jMethod->JNIReturnType(), jMethod->methodID(obj), jArgs.data(), result, callingURL, exceptionDescription); + if (exceptionDescription) { + throwError(exec, createError(exec, exceptionDescription.toString(exec))); + return jsUndefined(); + } + } + +// This is a deprecated code path which should not be required on Android. +// Remove this guard once Bug 39476 is fixed. +#if PLATFORM(ANDROID) || defined(BUILDING_ON_TIGER) + if (!handled) { + jobject obj = m_instance->m_instance; + switch (jMethod->JNIReturnType()) { + case void_type: + callJNIMethodIDA<void>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case object_type: + result.l = callJNIMethodIDA<jobject>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case boolean_type: + result.z = callJNIMethodIDA<jboolean>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case byte_type: + result.b = callJNIMethodIDA<jbyte>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case char_type: + result.c = callJNIMethodIDA<jchar>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case short_type: + result.s = callJNIMethodIDA<jshort>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case int_type: + result.i = callJNIMethodIDA<jint>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case long_type: + result.j = callJNIMethodIDA<jlong>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case float_type: + result.f = callJNIMethodIDA<jfloat>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case double_type: + result.d = callJNIMethodIDA<jdouble>(obj, jMethod->methodID(obj), jArgs.data()); + break; + case array_type: + case invalid_type: + break; + } + } +#endif + + switch (jMethod->JNIReturnType()) { + case void_type: + { + resultValue = jsUndefined(); + } + break; + + case object_type: + { + if (result.l) { + // FIXME: array_type return type is handled below, can we actually get an array here? + const char* arrayType = jMethod->returnType(); + if (arrayType[0] == '[') + resultValue = JavaArray::convertJObjectToArray(exec, result.l, arrayType, rootObject); + else { + jobject classOfInstance = callJNIMethod<jobject>(result.l, "getClass", "()Ljava/lang/Class;"); + jstring className = static_cast<jstring>(callJNIMethod<jobject>(classOfInstance, "getName", "()Ljava/lang/String;")); + if (!strcmp(JavaString(className).utf8(), "sun.plugin.javascript.webkit.JSObject")) { + // Pull the nativeJSObject value from the Java instance. This is a pointer to the JSObject. + JNIEnv* env = getJNIEnv(); + jfieldID fieldID = env->GetFieldID(static_cast<jclass>(classOfInstance), "nativeJSObject", "J"); + jlong nativeHandle = env->GetLongField(result.l, fieldID); + // FIXME: Handling of undefined values differs between functions in JNIUtilityPrivate.cpp and those in those in jni_jsobject.mm, + // and so it does between different versions of LiveConnect spec. There should not be multiple code paths to do the same work. + if (nativeHandle == 1 /* UndefinedHandle */) + return jsUndefined(); + return static_cast<JSObject*>(jlong_to_ptr(nativeHandle)); + } else + return JavaInstance::create(result.l, rootObject)->createRuntimeObject(exec); + } + } else + return jsUndefined(); + } + break; + + case boolean_type: + { + resultValue = jsBoolean(result.z); + } + break; + + case byte_type: + { + resultValue = jsNumber(result.b); + } + break; + + case char_type: + { + resultValue = jsNumber(result.c); + } + break; + + case short_type: + { + resultValue = jsNumber(result.s); + } + break; + + case int_type: + { + resultValue = jsNumber(result.i); + } + break; + + case long_type: + { + resultValue = jsNumber(result.j); + } + break; + + case float_type: + { + resultValue = jsNumber(result.f); + } + break; + + case double_type: + { + resultValue = jsNumber(result.d); + } + break; + + case array_type: + { + const char* arrayType = jMethod->returnType(); + ASSERT(arrayType[0] == '['); + resultValue = JavaArray::convertJObjectToArray(exec, result.l, arrayType, rootObject); + } + break; + + case invalid_type: + { + resultValue = jsUndefined(); + } + break; + } + + return resultValue; +} + +JSValue JavaInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (hint == PreferString) + return stringValue(exec); + if (hint == PreferNumber) + return numberValue(exec); + JavaClass* aClass = static_cast<JavaClass*>(getClass()); + if (aClass->isStringClass()) + return stringValue(exec); + if (aClass->isNumberClass()) + return numberValue(exec); + if (aClass->isBooleanClass()) + return booleanValue(); + return valueOf(exec); +} + +JSValue JavaInstance::valueOf(ExecState* exec) const +{ + return stringValue(exec); +} + +JObjectWrapper::JObjectWrapper(jobject instance) + : m_refCount(0) +{ + ASSERT(instance); + + // Cache the JNIEnv used to get the global ref for this java instance. + // It'll be used to delete the reference. + m_env = getJNIEnv(); + + m_instance = m_env->NewGlobalRef(instance); + + LOG(LiveConnect, "JObjectWrapper ctor new global ref %p for %p", m_instance, instance); + + if (!m_instance) + LOG_ERROR("Could not get GlobalRef for %p", instance); +} + +JObjectWrapper::~JObjectWrapper() +{ + LOG(LiveConnect, "JObjectWrapper dtor deleting global ref %p", m_instance); + m_env->DeleteGlobalRef(m_instance); +} + +#endif // ENABLE(JAVA_BRIDGE) diff --git a/Source/WebCore/bridge/jni/jsc/JavaInstanceJSC.h b/Source/WebCore/bridge/jni/jsc/JavaInstanceJSC.h new file mode 100644 index 0000000..dbfcf75 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JavaInstanceJSC.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaInstanceJSC_h +#define JavaInstanceJSC_h + +#if ENABLE(JAVA_BRIDGE) + +#include "Bridge.h" +#include "runtime_root.h" + +#include <JavaVM/jni.h> + +namespace JSC { + +namespace Bindings { + +class JavaClass; + +class JObjectWrapper { +friend class RefPtr<JObjectWrapper>; +friend class JavaArray; +friend class JavaField; +friend class JavaInstance; +friend class JavaMethod; + +public: + jobject instance() const { return m_instance; } + void setInstance(jobject instance) { m_instance = instance; } + + void ref() { m_refCount++; } + void deref() + { + if (!(--m_refCount)) + delete this; + } + +protected: + JObjectWrapper(jobject instance); + ~JObjectWrapper(); + + jobject m_instance; + +private: + JNIEnv* m_env; + unsigned int m_refCount; +}; + +class JavaInstance : public Instance { +public: + static PassRefPtr<JavaInstance> create(jobject instance, PassRefPtr<RootObject> rootObject) + { + return adoptRef(new JavaInstance(instance, rootObject)); + } + + ~JavaInstance(); + + virtual Class* getClass() const; + + virtual JSValue valueOf(ExecState*) const; + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName); + virtual JSValue invokeMethod(ExecState* exec, RuntimeMethod* method); + + jobject javaInstance() const { return m_instance->m_instance; } + + JSValue stringValue(ExecState*) const; + JSValue numberValue(ExecState*) const; + JSValue booleanValue() const; + +protected: + JavaInstance(jobject instance, PassRefPtr<RootObject>); + + virtual RuntimeObject* newRuntimeObject(ExecState*); + + virtual void virtualBegin(); + virtual void virtualEnd(); + + RefPtr<JObjectWrapper> m_instance; + mutable JavaClass* m_class; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(JAVA_BRIDGE) + +#endif // JavaInstanceJSC_h diff --git a/Source/WebCore/bridge/jni/jsc/JavaRuntimeObject.cpp b/Source/WebCore/bridge/jni/jsc/JavaRuntimeObject.cpp new file mode 100644 index 0000000..6270f9f --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JavaRuntimeObject.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "JavaInstanceJSC.h" +#include "JavaRuntimeObject.h" + +namespace JSC { +namespace Bindings { + +const ClassInfo JavaRuntimeObject::s_info = { "JavaRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +JavaRuntimeObject::JavaRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<JavaInstance> instance) + : RuntimeObject(exec, globalObject, instance) +{ +} + +JavaRuntimeObject::~JavaRuntimeObject() +{ +} + +JavaInstance* JavaRuntimeObject::getInternalJavaInstance() const +{ + return static_cast<JavaInstance*>(getInternalInstance()); +} + + + +} +} diff --git a/Source/WebCore/bridge/jni/jsc/JavaRuntimeObject.h b/Source/WebCore/bridge/jni/jsc/JavaRuntimeObject.h new file mode 100644 index 0000000..0e400f4 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JavaRuntimeObject.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaRuntimeObject_h +#define JavaRuntimeObject_h + +#include "runtime_object.h" + +namespace JSC { +namespace Bindings { + +class JavaInstance; + +class JavaRuntimeObject : public RuntimeObject { +public: + JavaRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<JavaInstance>); + virtual ~JavaRuntimeObject(); + + JavaInstance* getInternalJavaInstance() const; + + static const ClassInfo s_info; + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } +}; + +} +} + +#endif diff --git a/Source/WebCore/bridge/jni/jsc/JavaStringJSC.h b/Source/WebCore/bridge/jni/jsc/JavaStringJSC.h new file mode 100644 index 0000000..cf575b2 --- /dev/null +++ b/Source/WebCore/bridge/jni/jsc/JavaStringJSC.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaStringJSC_h +#define JavaStringJSC_h + +#include "JNIUtility.h" +#include "JavaInstanceJSC.h" +#include <runtime/JSLock.h> + + +namespace JSC { + +namespace Bindings { + +class JavaStringImpl { +public: + ~JavaStringImpl() + { + JSLock lock(SilenceAssertionsOnly); + m_impl = 0; + } + + void init() + { + JSLock lock(SilenceAssertionsOnly); + m_impl = UString().impl(); + } + + void init(JNIEnv* e, jstring s) + { + int size = e->GetStringLength(s); + const jchar* uc = getUCharactersFromJStringInEnv(e, s); + { + JSLock lock(SilenceAssertionsOnly); + m_impl = UString(reinterpret_cast<const UChar*>(uc), size).impl(); + } + releaseUCharactersForJStringInEnv(e, s, uc); + } + + const char* utf8() const + { + if (!m_utf8String.data()) { + JSLock lock(SilenceAssertionsOnly); + m_utf8String = UString(m_impl).utf8(); + } + return m_utf8String.data(); + } + const jchar* uchars() const { return (const jchar*)m_impl->characters(); } + int length() const { return m_impl->length(); } + UString uString() const { return UString(m_impl); } + +private: + RefPtr<StringImpl> m_impl; + mutable CString m_utf8String; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // JavaStringJSC_h diff --git a/Source/WebCore/bridge/jni/v8/JNIBridgeV8.cpp b/Source/WebCore/bridge/jni/v8/JNIBridgeV8.cpp new file mode 100644 index 0000000..35775dc --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JNIBridgeV8.cpp @@ -0,0 +1,44 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JNIBridgeV8.h" + +using namespace JSC::Bindings; + +JavaField::JavaField(JNIEnv* env, jobject aField) +{ + // Get field type + jobject fieldType = callJNIMethod<jobject>(aField, "getType", "()Ljava/lang/Class;"); + jstring fieldTypeName = static_cast<jstring>(callJNIMethod<jobject>(fieldType, "getName", "()Ljava/lang/String;")); + m_type = JavaString(env, fieldTypeName); + m_JNIType = JNITypeFromClassName(m_type.utf8()); + + // Get field name + jstring fieldName = static_cast<jstring>(callJNIMethod<jobject>(aField, "getName", "()Ljava/lang/String;")); + m_name = JavaString(env, fieldName); + + m_field = new JObjectWrapper(aField); +} diff --git a/Source/WebCore/bridge/jni/v8/JNIBridgeV8.h b/Source/WebCore/bridge/jni/v8/JNIBridgeV8.h new file mode 100644 index 0000000..46cbd56 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JNIBridgeV8.h @@ -0,0 +1,56 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JNIBridgeV8_h +#define JNIBridgeV8_h + +#include "JNIBridge.h" // For JavaString +#include "JavaInstanceV8.h" // For JObjectWrapper + +namespace JSC { + +namespace Bindings { + +class JavaField { +public: + JavaField(JNIEnv*, jobject aField); + + const JavaString& name() const { return m_name; } + const char* type() const { return m_type.utf8(); } + + JNIType getJNIType() const { return m_JNIType; } + +private: + JavaString m_name; + JavaString m_type; + JNIType m_JNIType; + RefPtr<JObjectWrapper> m_field; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // JNIBridgeV8_h diff --git a/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp new file mode 100644 index 0000000..f104e65 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp @@ -0,0 +1,481 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JNIUtilityPrivate.h" + +#include "JavaInstanceV8.h" +#include "JavaNPObjectV8.h" +#include "npruntime_impl.h" + +namespace JSC { + +namespace Bindings { + +jvalue convertNPVariantToJValue(NPVariant value, JNIType jniType, const char* javaClassName) +{ + jvalue result; + NPVariantType type = value.type; + + switch (jniType) { + case array_type: + { + JNIEnv* env = getJNIEnv(); + jobject javaArray; + NPObject* object = NPVARIANT_IS_OBJECT(value) ? NPVARIANT_TO_OBJECT(value) : 0; + NPVariant npvLength; + bool success = _NPN_GetProperty(0, object, _NPN_GetStringIdentifier("length"), &npvLength); + if (!success) { + // No length property so we don't know how many elements to put into the array. + // Treat this as an error. +#ifdef EMULATE_JSC_BINDINGS + // JSC sends null for an array that is not an array of strings or basic types, + // do this also in the unknown length case. + memset(&result, 0, sizeof(jvalue)); +#else + // Sending NULL as JSC does seems dangerous. (Imagine the java method that asks + // for the length of the array it was passed). Here we send a 0 length array. + jclass objectClass = env->FindClass("java/lang/Object"); + javaArray = env->NewObjectArray(0, objectClass, 0); + env->DeleteLocalRef(objectClass); +#endif + break; + } + + jsize length = 0; + if (NPVARIANT_IS_INT32(npvLength)) + length = static_cast<jsize>(NPVARIANT_TO_INT32(npvLength)); + else if (NPVARIANT_IS_DOUBLE(npvLength)) + length = static_cast<jsize>(NPVARIANT_TO_DOUBLE(npvLength)); + + if (!strcmp(javaClassName, "[Ljava.lang.String;")) { + // Match JSC behavior by only allowing Object arrays if they are Strings. + jclass stringClass = env->FindClass("java/lang/String"); + javaArray = env->NewObjectArray(length, stringClass, 0); + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + if(NPVARIANT_IS_STRING(npvValue)) { + NPString str = NPVARIANT_TO_STRING(npvValue); + env->SetObjectArrayElement(static_cast<jobjectArray>(javaArray), i, env->NewStringUTF(str.UTF8Characters)); + } + } + + env->DeleteLocalRef(stringClass); + } else if (!strcmp(javaClassName, "[B")) { + // array of bytes + javaArray = env->NewByteArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + jbyte bVal = 0; + if (NPVARIANT_IS_INT32(npvValue)) { + bVal = static_cast<jbyte>(NPVARIANT_TO_INT32(npvValue)); + } else if (NPVARIANT_IS_DOUBLE(npvValue)) { + bVal = static_cast<jbyte>(NPVARIANT_TO_DOUBLE(npvValue)); + } + env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray), i, 1, &bVal); + } + } else if (!strcmp(javaClassName, "[C")) { + // array of chars + javaArray = env->NewCharArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + jchar cVal = 0; + if (NPVARIANT_IS_INT32(npvValue)) { + cVal = static_cast<jchar>(NPVARIANT_TO_INT32(npvValue)); + } else if (NPVARIANT_IS_STRING(npvValue)) { + NPString str = NPVARIANT_TO_STRING(npvValue); + cVal = str.UTF8Characters[0]; + } + env->SetCharArrayRegion(static_cast<jcharArray>(javaArray), i, 1, &cVal); + } + } else if (!strcmp(javaClassName, "[D")) { + // array of doubles + javaArray = env->NewDoubleArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + if (NPVARIANT_IS_DOUBLE(npvValue)) { + jdouble dVal = NPVARIANT_TO_DOUBLE(npvValue); + env->SetDoubleArrayRegion(static_cast<jdoubleArray>(javaArray), i, 1, &dVal); + } + } + } else if (!strcmp(javaClassName, "[F")) { + // array of floats + javaArray = env->NewFloatArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + if (NPVARIANT_IS_DOUBLE(npvValue)) { + jfloat fVal = static_cast<jfloat>(NPVARIANT_TO_DOUBLE(npvValue)); + env->SetFloatArrayRegion(static_cast<jfloatArray>(javaArray), i, 1, &fVal); + } + } + } else if (!strcmp(javaClassName, "[I")) { + // array of ints + javaArray = env->NewIntArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + jint iVal = 0; + if (NPVARIANT_IS_INT32(npvValue)) { + iVal = NPVARIANT_TO_INT32(npvValue); + } else if (NPVARIANT_IS_DOUBLE(npvValue)) { + iVal = static_cast<jint>(NPVARIANT_TO_DOUBLE(npvValue)); + } + env->SetIntArrayRegion(static_cast<jintArray>(javaArray), i, 1, &iVal); + } + } else if (!strcmp(javaClassName, "[J")) { + // array of longs + javaArray = env->NewLongArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + jlong jVal = 0; + if (NPVARIANT_IS_INT32(npvValue)) { + jVal = static_cast<jlong>(NPVARIANT_TO_INT32(npvValue)); + } else if (NPVARIANT_IS_DOUBLE(npvValue)) { + jVal = static_cast<jlong>(NPVARIANT_TO_DOUBLE(npvValue)); + } + env->SetLongArrayRegion(static_cast<jlongArray>(javaArray), i, 1, &jVal); + } + } else if (!strcmp(javaClassName, "[S")) { + // array of shorts + javaArray = env->NewShortArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + jshort sVal = 0; + if (NPVARIANT_IS_INT32(npvValue)) { + sVal = static_cast<jshort>(NPVARIANT_TO_INT32(npvValue)); + } else if (NPVARIANT_IS_DOUBLE(npvValue)) { + sVal = static_cast<jshort>(NPVARIANT_TO_DOUBLE(npvValue)); + } + env->SetShortArrayRegion(static_cast<jshortArray>(javaArray), i, 1, &sVal); + } + } else if (!strcmp(javaClassName, "[Z")) { + // array of booleans + javaArray = env->NewBooleanArray(length); + // Now iterate over each element and add to the array. + for (jsize i = 0; i < length; i++) { + NPVariant npvValue; + _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue); + if (NPVARIANT_IS_BOOLEAN(npvValue)) { + jboolean zVal = NPVARIANT_TO_BOOLEAN(npvValue); + env->SetBooleanArrayRegion(static_cast<jbooleanArray>(javaArray), i, 1, &zVal); + } + } + } else { +#ifdef EMULATE_JSC_BINDINGS + // JSC sends null for an array that is not an array of strings or basic types. + memset(&result, 0, sizeof(jvalue)); + break; +#else + // Sending NULL as JSC does seems dangerous. (Imagine the java method that asks + // for the length of the array it was passed). Here we send a 0 length array. + jclass objectClass = env->FindClass("java/lang/Object"); + javaArray = env->NewObjectArray(0, objectClass, 0); + env->DeleteLocalRef(objectClass); +#endif + } + + result.l = javaArray; + } + break; + + case object_type: + { + JNIEnv* env = getJNIEnv(); + result.l = static_cast<jobject>(0); + jobject javaString; + + // First see if we have a Java instance. + if (type == NPVariantType_Object) { + NPObject* objectImp = NPVARIANT_TO_OBJECT(value); + if (JavaInstance* instance = ExtractJavaInstance(objectImp)) + result.l = instance->javaInstance(); + } + + // Now convert value to a string if the target type is a java.lang.string, and we're not + // converting from a Null. + if (!result.l && !strcmp(javaClassName, "java.lang.String")) { +#ifdef CONVERT_NULL_TO_EMPTY_STRING + if (type == NPVariantType_Null) { + jchar buf[2]; + jobject javaString = env->functions->NewString(env, buf, 0); + result.l = javaString; + } else +#else + if (type == NPVariantType_String) +#endif + { + NPString src = NPVARIANT_TO_STRING(value); + javaString = env->NewStringUTF(src.UTF8Characters); + result.l = javaString; + } else if (type == NPVariantType_Int32) { + jint src = NPVARIANT_TO_INT32(value); + jclass integerClass = env->FindClass("java/lang/Integer"); + jmethodID toString = env->GetStaticMethodID(integerClass, "toString", "(I)Ljava/lang/String;"); + javaString = env->CallStaticObjectMethod(integerClass, toString, src); + result.l = javaString; + env->DeleteLocalRef(integerClass); + } else if (type == NPVariantType_Bool) { + jboolean src = NPVARIANT_TO_BOOLEAN(value); + jclass booleanClass = env->FindClass("java/lang/Boolean"); + jmethodID toString = env->GetStaticMethodID(booleanClass, "toString", "(Z)Ljava/lang/String;"); + javaString = env->CallStaticObjectMethod(booleanClass, toString, src); + result.l = javaString; + env->DeleteLocalRef(booleanClass); + } else if (type == NPVariantType_Double) { + jdouble src = NPVARIANT_TO_DOUBLE(value); + jclass doubleClass = env->FindClass("java/lang/Double"); + jmethodID toString = env->GetStaticMethodID(doubleClass, "toString", "(D)Ljava/lang/String;"); + javaString = env->CallStaticObjectMethod(doubleClass, toString, src); + result.l = javaString; + env->DeleteLocalRef(doubleClass); + } +#ifdef EMULATE_JSC_BINDINGS + // For the undefined value, JSC sends the String "undefined". Feels to me like we + // should send null in this case. + else if (!NPVARIANT_IS_NULL(value)) { + javaString = env->NewStringUTF("undefined"); + result.l = javaString; + } +#endif + } else if (!result.l) + memset(&result, 0, sizeof(jvalue)); // Handle it the same as a void case + } + break; + + case boolean_type: + { + if (type == NPVariantType_Bool) + result.z = NPVARIANT_TO_BOOLEAN(value); + else + memset(&result, 0, sizeof(jvalue)); // as void case + } + break; + + case byte_type: + { + if (type == NPVariantType_Int32) + result.b = static_cast<jbyte>(NPVARIANT_TO_INT32(value)); + else if (type == NPVariantType_Double) + result.b = static_cast<jbyte>(NPVARIANT_TO_DOUBLE(value)); + else + memset(&result, 0, sizeof(jvalue)); + } + break; + + case char_type: + { + if (type == NPVariantType_Int32) + result.c = static_cast<char>(NPVARIANT_TO_INT32(value)); +#ifndef EMULATE_JSC_BINDINGS + // There is no char type in JavaScript - just strings 1 character + // long. So just converting it to an int above doesn't work. Again, + // we emulate the behavior for now for maximum compatability. + else if (type == NPVariantType_String) { + NPString str = NPVARIANT_TO_STRING(value); + result.c = str.UTF8Characters[0]; + } +#endif + else + memset(&result, 0, sizeof(jvalue)); + } + break; + + case short_type: + { + if (type == NPVariantType_Int32) + result.s = static_cast<jshort>(NPVARIANT_TO_INT32(value)); + else if (type == NPVariantType_Double) + result.s = static_cast<jshort>(NPVARIANT_TO_DOUBLE(value)); + else + memset(&result, 0, sizeof(jvalue)); + } + break; + + case int_type: + { + if (type == NPVariantType_Int32) + result.i = static_cast<jint>(NPVARIANT_TO_INT32(value)); + else if (type == NPVariantType_Double) + result.i = static_cast<jint>(NPVARIANT_TO_DOUBLE(value)); + else + memset(&result, 0, sizeof(jvalue)); + } + break; + + case long_type: + { + if (type == NPVariantType_Int32) + result.j = static_cast<jlong>(NPVARIANT_TO_INT32(value)); + else if (type == NPVariantType_Double) + result.j = static_cast<jlong>(NPVARIANT_TO_DOUBLE(value)); + else + memset(&result, 0, sizeof(jvalue)); + } + break; + + case float_type: + { + if (type == NPVariantType_Int32) + result.f = static_cast<jfloat>(NPVARIANT_TO_INT32(value)); + else if (type == NPVariantType_Double) + result.f = static_cast<jfloat>(NPVARIANT_TO_DOUBLE(value)); + else + memset(&result, 0, sizeof(jvalue)); + } + break; + + case double_type: + { + if (type == NPVariantType_Int32) + result.d = static_cast<jdouble>(NPVARIANT_TO_INT32(value)); + else if (type == NPVariantType_Double) + result.d = static_cast<jdouble>(NPVARIANT_TO_DOUBLE(value)); + else + memset(&result, 0, sizeof(jvalue)); + } + break; + + case invalid_type: + default: + case void_type: + { + memset(&result, 0, sizeof(jvalue)); + } + break; + } + return result; +} + + +void convertJValueToNPVariant(jvalue value, JNIType jniType, const char* javaTypeName, NPVariant* result) +{ + switch (jniType) { + case void_type: + { + VOID_TO_NPVARIANT(*result); + } + break; + + case object_type: + { + if (value.l) { + if (!strcmp(javaTypeName, "java.lang.String")) { + const char* v = getCharactersFromJString(static_cast<jstring>(value.l)); + // s is freed in NPN_ReleaseVariantValue (see npruntime.cpp) + const char* s = strdup(v); + releaseCharactersForJString(static_cast<jstring>(value.l), v); + STRINGZ_TO_NPVARIANT(s, *result); + } else + OBJECT_TO_NPVARIANT(JavaInstanceToNPObject(new JavaInstance(value.l)), *result); + } else + VOID_TO_NPVARIANT(*result); + } + break; + + case boolean_type: + { + BOOLEAN_TO_NPVARIANT(value.z, *result); + } + break; + + case byte_type: + { + INT32_TO_NPVARIANT(value.b, *result); + } + break; + + case char_type: + { +#ifndef EMULATE_JSC_BINDINGS + // There is no char type in JavaScript - just strings 1 character + // long. So just converting it to an int above doesn't work. Again, + // we emulate the behavior for now for maximum compatability. + if (!strcmp(javaTypeName, "char")) { + const char c = value.c; + const char* v = strndup(&c, 1); + STRINGZ_TO_NPVARIANT(v, *result); + } else +#endif + INT32_TO_NPVARIANT(value.c, *result); + } + break; + + case short_type: + { + INT32_TO_NPVARIANT(value.s, *result); + } + break; + + case int_type: + { + INT32_TO_NPVARIANT(value.i, *result); + } + break; + + // TODO: Check if cast to double is needed. + case long_type: + { + DOUBLE_TO_NPVARIANT(value.j, *result); + } + break; + + case float_type: + { + DOUBLE_TO_NPVARIANT(value.f, *result); + } + break; + + case double_type: + { + DOUBLE_TO_NPVARIANT(value.d, *result); + } + break; + + case invalid_type: + default: + { + VOID_TO_NPVARIANT(*result); + } + break; + } +} + +} // end of namespace Bindings + +} // end of namespace JSC diff --git a/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.h b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.h new file mode 100644 index 0000000..da7a24c --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.h @@ -0,0 +1,49 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JNIUtilityPrivate_h +#define JNIUtilityPrivate_h + +#include "JNIUtility.h" +#include "npruntime.h" + +// FIXME: While fully implementing the bindings I noticed some differences between what +// I wrote and seemed intuitive and what JSC does. Need to verify if my intuition is wrong +// or there are bugs in the JSC bindings. For now, this macro makes the V8 bindings do the +// same as the JSC bindings. +#define EMULATE_JSC_BINDINGS 1 + +namespace JSC { + +namespace Bindings { + +jvalue convertNPVariantToJValue(NPVariant, JNIType, const char* javaClassName); +void convertJValueToNPVariant(jvalue, JNIType, const char* javaClassName, NPVariant*); + +} // namespace Bindings + +} // namespace JSC + +#endif // JNIUtilityPrivate_h diff --git a/Source/WebCore/bridge/jni/v8/JavaClassV8.cpp b/Source/WebCore/bridge/jni/v8/JavaClassV8.cpp new file mode 100644 index 0000000..1d381af --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JavaClassV8.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JavaClassV8.h" + +using namespace JSC::Bindings; + +JavaClass::JavaClass(jobject anInstance) +{ + jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;"); + + if (!aClass) { + fprintf(stderr, "%s: unable to call getClass on instance %p\n", __PRETTY_FUNCTION__, anInstance); + return; + } + + int i; + JNIEnv* env = getJNIEnv(); + + // Get the fields + jarray fields = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;")); + int numFields = env->GetArrayLength(fields); + for (i = 0; i < numFields; i++) { + jobject aJField = env->GetObjectArrayElement(static_cast<jobjectArray>(fields), i); + JavaField* aField = new JavaField(env, aJField); // deleted in the JavaClass destructor + { + m_fields.set(aField->name().utf8(), aField); + } + env->DeleteLocalRef(aJField); + } + + // Get the methods + jarray methods = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getMethods", "()[Ljava/lang/reflect/Method;")); + int numMethods = env->GetArrayLength(methods); + for (i = 0; i < numMethods; i++) { + jobject aJMethod = env->GetObjectArrayElement(static_cast<jobjectArray>(methods), i); + JavaMethod* aMethod = new JavaMethod(env, aJMethod); // deleted in the JavaClass destructor + MethodList* methodList; + { + methodList = m_methods.get(aMethod->name().utf8()); + if (!methodList) { + methodList = new MethodList(); + m_methods.set(aMethod->name().utf8(), methodList); + } + } + methodList->append(aMethod); + env->DeleteLocalRef(aJMethod); + } + env->DeleteLocalRef(fields); + env->DeleteLocalRef(methods); + env->DeleteLocalRef(aClass); +} + +JavaClass::~JavaClass() +{ + deleteAllValues(m_fields); + m_fields.clear(); + + MethodListMap::const_iterator end = m_methods.end(); + for (MethodListMap::const_iterator it = m_methods.begin(); it != end; ++it) { + const MethodList* methodList = it->second; + deleteAllValues(*methodList); + delete methodList; + } + m_methods.clear(); +} + +MethodList JavaClass::methodsNamed(const char* name) const +{ + MethodList* methodList = m_methods.get(name); + + if (methodList) + return *methodList; + return MethodList(); +} + +JavaField* JavaClass::fieldNamed(const char* name) const +{ + return m_fields.get(name); +} diff --git a/Source/WebCore/bridge/jni/v8/JavaClassV8.h b/Source/WebCore/bridge/jni/v8/JavaClassV8.h new file mode 100644 index 0000000..99137f1 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JavaClassV8.h @@ -0,0 +1,60 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaClassV8_h +#define JavaClassV8_h + +#include "JNIBridgeV8.h" +#include "PlatformString.h" +#include <wtf/HashMap.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +namespace JSC { + +namespace Bindings { + +typedef Vector<JavaMethod*> MethodList; +typedef HashMap<WTF::String, MethodList*> MethodListMap; +typedef HashMap<WTF::String, JavaField*> FieldMap; + +class JavaClass { +public: + JavaClass(jobject anInstance); + ~JavaClass(); + + MethodList methodsNamed(const char* name) const; + JavaField* fieldNamed(const char* name) const; + +private: + MethodListMap m_methods; + FieldMap m_fields; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // JavaClassV8_h diff --git a/Source/WebCore/bridge/jni/v8/JavaInstanceV8.cpp b/Source/WebCore/bridge/jni/v8/JavaInstanceV8.cpp new file mode 100644 index 0000000..27adca3 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JavaInstanceV8.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2003, 2008, 2010 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JavaInstanceV8.h" + +#include "JNIBridge.h" +#include "JNIUtilityPrivate.h" +#include "JavaClassV8.h" + +#include <assert.h> + +using namespace JSC::Bindings; + +JavaInstance::JavaInstance(jobject instance) +{ + m_instance = new JObjectWrapper(instance); + m_class = 0; +} + +JavaInstance::~JavaInstance() +{ + m_instance = 0; + delete m_class; +} + +#define NUM_LOCAL_REFS 64 + +void JavaInstance::virtualBegin() +{ + getJNIEnv()->PushLocalFrame(NUM_LOCAL_REFS); +} + +void JavaInstance::virtualEnd() +{ + getJNIEnv()->PopLocalFrame(0); +} + +JavaClass* JavaInstance::getClass() const +{ + if (!m_class) + m_class = new JavaClass(javaInstance()); + return m_class; +} + +bool JavaInstance::invokeMethod(const char* methodName, const NPVariant* args, int count, NPVariant* resultValue) +{ + VOID_TO_NPVARIANT(*resultValue); + + MethodList methodList = getClass()->methodsNamed(methodName); + + size_t numMethods = methodList.size(); + + // Try to find a good match for the overloaded method. The + // fundamental problem is that JavaScript doesn't have the + // notion of method overloading and Java does. We could + // get a bit more sophisticated and attempt to does some + // type checking as we as checking the number of parameters. + JavaMethod* aMethod; + JavaMethod* method = 0; + for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { + aMethod = methodList[methodIndex]; + if (aMethod->numParameters() == count) { + method = aMethod; + break; + } + } + if (!method) + return false; + + const JavaMethod* jMethod = static_cast<const JavaMethod*>(method); + + jvalue* jArgs = 0; + if (count > 0) + jArgs = static_cast<jvalue*>(malloc(count * sizeof(jvalue))); + + for (int i = 0; i < count; i++) { + JavaParameter* aParameter = jMethod->parameterAt(i); + jArgs[i] = convertNPVariantToJValue(args[i], aParameter->getJNIType(), aParameter->type()); + } + + jvalue result; + + // The following code can be conditionally removed once we have a Tiger update that + // contains the new Java plugin. It is needed for builds prior to Tiger. + { + jobject obj = javaInstance(); + switch (jMethod->JNIReturnType()) { + case void_type: + callJNIMethodIDA<void>(obj, jMethod->methodID(obj), jArgs); + break; + case object_type: + result.l = callJNIMethodIDA<jobject>(obj, jMethod->methodID(obj), jArgs); + break; + case boolean_type: + result.z = callJNIMethodIDA<jboolean>(obj, jMethod->methodID(obj), jArgs); + break; + case byte_type: + result.b = callJNIMethodIDA<jbyte>(obj, jMethod->methodID(obj), jArgs); + break; + case char_type: + result.c = callJNIMethodIDA<jchar>(obj, jMethod->methodID(obj), jArgs); + break; + case short_type: + result.s = callJNIMethodIDA<jshort>(obj, jMethod->methodID(obj), jArgs); + break; + case int_type: + result.i = callJNIMethodIDA<jint>(obj, jMethod->methodID(obj), jArgs); + break; + + case long_type: + result.j = callJNIMethodIDA<jlong>(obj, jMethod->methodID(obj), jArgs); + break; + case float_type: + result.f = callJNIMethodIDA<jfloat>(obj, jMethod->methodID(obj), jArgs); + break; + case double_type: + result.d = callJNIMethodIDA<jdouble>(obj, jMethod->methodID(obj), jArgs); + break; + case invalid_type: + default: + break; + } + } + + convertJValueToNPVariant(result, jMethod->JNIReturnType(), jMethod->returnType(), resultValue); + free(jArgs); + + return true; +} + +JObjectWrapper::JObjectWrapper(jobject instance) + : m_refCount(0) +{ + assert(instance); + + // Cache the JNIEnv used to get the global ref for this java instanace. + // It'll be used to delete the reference. + m_env = getJNIEnv(); + + m_instance = m_env->NewGlobalRef(instance); + + if (!m_instance) + fprintf(stderr, "%s: could not get GlobalRef for %p\n", __PRETTY_FUNCTION__, instance); +} + +JObjectWrapper::~JObjectWrapper() +{ + m_env->DeleteGlobalRef(m_instance); +} diff --git a/Source/WebCore/bridge/jni/v8/JavaInstanceV8.h b/Source/WebCore/bridge/jni/v8/JavaInstanceV8.h new file mode 100644 index 0000000..4f009a5 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JavaInstanceV8.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2003, 2008, 2010 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaInstanceV8_h +#define JavaInstanceV8_h + +#include "npruntime.h" + +#include <JavaVM/jni.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +using namespace WTF; + +namespace JSC { + +namespace Bindings { + +class JavaClass; + +class JObjectWrapper { +friend class RefPtr<JObjectWrapper>; +friend class JavaField; +friend class JavaInstance; + +public: + jobject instance() const { return m_instance; } + void setInstance(jobject instance) { m_instance = instance; } + + void ref() { m_refCount++; } + void deref() + { + if (!(--m_refCount)) + delete this; + } + +protected: + JObjectWrapper(jobject); + ~JObjectWrapper(); + + jobject m_instance; + +private: + JNIEnv* m_env; + unsigned int m_refCount; +}; + +class JavaInstance : public RefCounted<JavaInstance> { +public: + JavaInstance(jobject instance); + virtual ~JavaInstance(); + + JavaClass* getClass() const; + + bool invokeMethod(const char* name, const NPVariant* args, int argsCount, NPVariant* result); + + jobject javaInstance() const { return m_instance->m_instance; } + + // These functions are called before and after the main entry points into + // the native implementations. They can be used to establish and cleanup + // any needed state. + void begin() { virtualBegin(); } + void end() { virtualEnd(); } + +protected: + RefPtr<JObjectWrapper> m_instance; + mutable JavaClass* m_class; + + virtual void virtualBegin(); + virtual void virtualEnd(); +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // JavaInstanceV8_h diff --git a/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp b/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp new file mode 100644 index 0000000..3bb8e27 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "JavaNPObjectV8.h" + +#include "JNIUtilityPrivate.h" +#include "JavaClassV8.h" +#include "JavaInstanceV8.h" +#include "npruntime_impl.h" + +namespace JSC { + +namespace Bindings { + +static NPObject* AllocJavaNPObject(NPP, NPClass*) +{ + JavaNPObject* obj = static_cast<JavaNPObject*>(malloc(sizeof(JavaNPObject))); + if (!obj) + return 0; + memset(obj, 0, sizeof(JavaNPObject)); + return reinterpret_cast<NPObject*>(obj); +} + +static void FreeJavaNPObject(NPObject* npobj) +{ + JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(npobj); + obj->m_instance = 0; // free does not call the destructor + free(obj); +} + +static NPClass JavaNPObjectClass = { + NP_CLASS_STRUCT_VERSION, + AllocJavaNPObject, // allocate, + FreeJavaNPObject, // free, + 0, // invalidate + JavaNPObjectHasMethod, + JavaNPObjectInvoke, + 0, // invokeDefault, + JavaNPObjectHasProperty, + JavaNPObjectGetProperty, + 0, // setProperty + 0, // removeProperty + 0, // enumerate + 0 // construct +}; + +NPObject* JavaInstanceToNPObject(JavaInstance* instance) +{ + JavaNPObject* object = reinterpret_cast<JavaNPObject*>(_NPN_CreateObject(0, &JavaNPObjectClass)); + object->m_instance = instance; + return reinterpret_cast<NPObject*>(object); +} + +// Returns null if obj is not a wrapper of JavaInstance +JavaInstance* ExtractJavaInstance(NPObject* obj) +{ + if (obj->_class == &JavaNPObjectClass) + return reinterpret_cast<JavaNPObject*>(obj)->m_instance.get(); + return 0; +} + +bool JavaNPObjectHasMethod(NPObject* obj, NPIdentifier identifier) +{ + JavaInstance* instance = ExtractJavaInstance(obj); + if (!instance) + return false; + NPUTF8* name = _NPN_UTF8FromIdentifier(identifier); + if (!name) + return false; + + instance->begin(); + bool result = (instance->getClass()->methodsNamed(name).size() > 0); + instance->end(); + + // TODO: use NPN_MemFree + free(name); + + return result; +} + +bool JavaNPObjectInvoke(NPObject* obj, NPIdentifier identifier, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + JavaInstance* instance = ExtractJavaInstance(obj); + if (!instance) + return false; + NPUTF8* name = _NPN_UTF8FromIdentifier(identifier); + if (!name) + return false; + + instance->begin(); + bool r = instance->invokeMethod(name, args, argCount, result); + instance->end(); + + // TODO: use NPN_MemFree + free(name); + return r; +} + +bool JavaNPObjectHasProperty(NPObject* obj, NPIdentifier identifier) +{ + JavaInstance* instance = ExtractJavaInstance(obj); + if (!instance) + return false; + NPUTF8* name = _NPN_UTF8FromIdentifier(identifier); + if (!name) + return false; + instance->begin(); + bool result = instance->getClass()->fieldNamed(name); + instance->end(); + free(name); + return result; +} + +bool JavaNPObjectGetProperty(NPObject* obj, NPIdentifier identifier, NPVariant* result) +{ + VOID_TO_NPVARIANT(*result); + JavaInstance* instance = ExtractJavaInstance(obj); + if (!instance) + return false; + NPUTF8* name = _NPN_UTF8FromIdentifier(identifier); + if (!name) + return false; + + instance->begin(); + JavaField* field = instance->getClass()->fieldNamed(name); + instance->end(); + free(name); // TODO: use NPN_MemFree + + if (!field) + return false; + +#ifdef EMULATE_JSC_BINDINGS + // JSC does not seem to support returning object properties so we emulate that + // behaviour here. + jvalue value; +#else + // FIXME: Note here that field->type() refers to the Java class name and NOT the + // JNI signature i.e. "int" as opposed to "I". This means that the field lookup + // will fail. + jvalue value = getJNIField(instance->javaInstance(), + field->getJNIType(), + field->name().utf8(), + field->type()); +#endif + convertJValueToNPVariant(value, field->getJNIType(), field->type(), result); + + return true; +} + +} // namespace Bindings + +} // namespace JSC diff --git a/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.h b/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.h new file mode 100644 index 0000000..31b0ac7 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.h @@ -0,0 +1,56 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaNPObjectV8_h +#define JavaNPObjectV8_h + +#include "npruntime.h" +#include <wtf/RefPtr.h> + + +namespace JSC { + +namespace Bindings { + +class JavaInstance; + +struct JavaNPObject { + NPObject m_object; + RefPtr<JavaInstance> m_instance; +}; + +NPObject* JavaInstanceToNPObject(JavaInstance*); +JavaInstance* ExtractJavaInstance(NPObject*); + +bool JavaNPObjectHasMethod(NPObject*, NPIdentifier name); +bool JavaNPObjectInvoke(NPObject*, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result); +bool JavaNPObjectHasProperty(NPObject*, NPIdentifier name); +bool JavaNPObjectGetProperty(NPObject*, NPIdentifier name, NPVariant* result); + +} // namespace Bindings + +} // namespace JSC + +#endif // JavaNPObjectV8_h diff --git a/Source/WebCore/bridge/jni/v8/JavaStringV8.h b/Source/WebCore/bridge/jni/v8/JavaStringV8.h new file mode 100644 index 0000000..827d9f5 --- /dev/null +++ b/Source/WebCore/bridge/jni/v8/JavaStringV8.h @@ -0,0 +1,61 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaStringV8_h +#define JavaStringV8_h + +#include "JNIUtility.h" +#include <wtf/text/CString.h> + + +namespace JSC { + +namespace Bindings { + +class JavaStringImpl { +public: + void init() {} + + void init(JNIEnv* e, jstring s) + { + int size = e->GetStringLength(s); + const char* cs = getCharactersFromJStringInEnv(e, s); + m_utf8String = WTF::CString(cs, size); + releaseCharactersForJStringInEnv(e, s, cs); + } + + const char* utf8() const { return m_utf8String.data(); } + const jchar* uchars() const { return 0; } // Not implemented + int length() const { return m_utf8String.length(); } + +private: + WTF::CString m_utf8String; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // JavaStringV8_h diff --git a/Source/WebCore/bridge/jsc/BridgeJSC.cpp b/Source/WebCore/bridge/jsc/BridgeJSC.cpp new file mode 100644 index 0000000..49ffb2a --- /dev/null +++ b/Source/WebCore/bridge/jsc/BridgeJSC.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BridgeJSC.h" + +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/JSLock.h> + +#if PLATFORM(QT) +#include "qt_instance.h" +#endif + +namespace JSC { + +namespace Bindings { + +Array::Array(PassRefPtr<RootObject> rootObject) + : m_rootObject(rootObject) +{ + ASSERT(m_rootObject); +} + +Array::~Array() +{ +} + +Instance::Instance(PassRefPtr<RootObject> rootObject) + : m_rootObject(rootObject) +{ + ASSERT(m_rootObject); +} + +Instance::~Instance() +{ + ASSERT(!m_runtimeObject); + ASSERT(!m_runtimeObject.hasDeadObject()); +} + +static KJSDidExecuteFunctionPtr s_didExecuteFunction; + +void Instance::setDidExecuteFunction(KJSDidExecuteFunctionPtr func) +{ + s_didExecuteFunction = func; +} + +KJSDidExecuteFunctionPtr Instance::didExecuteFunction() +{ + return s_didExecuteFunction; +} + +void Instance::begin() +{ + virtualBegin(); +} + +void Instance::end() +{ + virtualEnd(); +} + +JSObject* Instance::createRuntimeObject(ExecState* exec) +{ + ASSERT(m_rootObject); + ASSERT(m_rootObject->isValid()); + if (RuntimeObject* existingObject = m_runtimeObject.get()) + return existingObject; + + JSLock lock(SilenceAssertionsOnly); + RuntimeObject* newObject = newRuntimeObject(exec); + m_runtimeObject = newObject; + m_rootObject->addRuntimeObject(newObject); + return newObject; +} + +RuntimeObject* Instance::newRuntimeObject(ExecState* exec) +{ + JSLock lock(SilenceAssertionsOnly); + return new (exec)RuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +void Instance::willDestroyRuntimeObject(RuntimeObject* object) +{ + ASSERT(m_rootObject); + ASSERT(m_rootObject->isValid()); + m_rootObject->removeRuntimeObject(object); + m_runtimeObject.clear(object); +} + +void Instance::willInvalidateRuntimeObject(RuntimeObject* object) +{ + ASSERT(object); + m_runtimeObject.clear(object); +} + +RootObject* Instance::rootObject() const +{ + return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0; +} + +} // namespace Bindings + +} // namespace JSC diff --git a/Source/WebCore/bridge/jsc/BridgeJSC.h b/Source/WebCore/bridge/jsc/BridgeJSC.h new file mode 100644 index 0000000..96974d9 --- /dev/null +++ b/Source/WebCore/bridge/jsc/BridgeJSC.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2003, 2008, 2009 Apple Inc. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BridgeJSC_h +#define BridgeJSC_h + +#if USE(JSC) + +#include <runtime/JSString.h> +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace JSC { + +class ArgList; +class Identifier; +class JSGlobalObject; +class PropertyNameArray; +class RuntimeMethod; + +namespace Bindings { + +class Instance; +class Method; +class RootObject; +class RuntimeObject; + +typedef Vector<Method*> MethodList; + +class Field { +public: + virtual JSValue valueFromInstance(ExecState*, const Instance*) const = 0; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const = 0; + + virtual ~Field() { } +}; + +class Class : public Noncopyable { +public: + virtual MethodList methodsNamed(const Identifier&, Instance*) const = 0; + virtual Field* fieldNamed(const Identifier&, Instance*) const = 0; + virtual JSValue fallbackObject(ExecState*, Instance*, const Identifier&) { return jsUndefined(); } + + virtual ~Class() { } +}; + +typedef void (*KJSDidExecuteFunctionPtr)(ExecState*, JSObject* rootObject); + +class Instance : public RefCounted<Instance> { +public: + Instance(PassRefPtr<RootObject>); + + static void setDidExecuteFunction(KJSDidExecuteFunctionPtr func); + static KJSDidExecuteFunctionPtr didExecuteFunction(); + + // These functions are called before and after the main entry points into + // the native implementations. They can be used to establish and cleanup + // any needed state. + void begin(); + void end(); + + virtual Class* getClass() const = 0; + JSObject* createRuntimeObject(ExecState*); + void willInvalidateRuntimeObject(RuntimeObject*); + void willDestroyRuntimeObject(RuntimeObject*); + + // Returns false if the value was not set successfully. + virtual bool setValueOfUndefinedField(ExecState*, const Identifier&, JSValue) { return false; } + + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName) = 0; + virtual JSValue invokeMethod(ExecState*, RuntimeMethod* method) = 0; + + virtual bool supportsInvokeDefaultMethod() const { return false; } + virtual JSValue invokeDefaultMethod(ExecState*) { return jsUndefined(); } + + virtual bool supportsConstruct() const { return false; } + virtual JSValue invokeConstruct(ExecState*, const ArgList&) { return JSValue(); } + + virtual void getPropertyNames(ExecState*, PropertyNameArray&) { } + + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const = 0; + + virtual JSValue valueOf(ExecState* exec) const = 0; + + RootObject* rootObject() const; + + virtual ~Instance(); + + virtual bool getOwnPropertySlot(JSObject*, ExecState*, const Identifier&, PropertySlot&) { return false; } + virtual bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&) { return false; } + virtual void put(JSObject*, ExecState*, const Identifier&, JSValue, PutPropertySlot&) { } + +protected: + virtual void virtualBegin() { } + virtual void virtualEnd() { } + virtual RuntimeObject* newRuntimeObject(ExecState*); + + RefPtr<RootObject> m_rootObject; + +private: + WeakGCPtr<RuntimeObject> m_runtimeObject; +}; + +class Array : public Noncopyable { +public: + Array(PassRefPtr<RootObject>); + virtual ~Array(); + + virtual void setValueAt(ExecState*, unsigned index, JSValue) const = 0; + virtual JSValue valueAt(ExecState*, unsigned index) const = 0; + virtual unsigned int getLength() const = 0; + +protected: + RefPtr<RootObject> m_rootObject; +}; + +const char* signatureForParameters(const ArgList&); + +typedef HashMap<RefPtr<StringImpl>, MethodList*> MethodListMap; +typedef HashMap<RefPtr<StringImpl>, Method*> MethodMap; +typedef HashMap<RefPtr<StringImpl>, Field*> FieldMap; + +} // namespace Bindings + +} // namespace JSC + +#endif // USE(JSC) + +#endif diff --git a/Source/WebCore/bridge/make_testbindings b/Source/WebCore/bridge/make_testbindings new file mode 100755 index 0000000..1f528fe --- /dev/null +++ b/Source/WebCore/bridge/make_testbindings @@ -0,0 +1,2 @@ +cc -g -o testbindingsM testbindings.mm -I../kjs -F$SYMROOTS -framework JavaScriptCore -framework Foundation -lstdc++ +cc -g -o testbindingsC testbindings.cpp -I../kjs -F$SYMROOTS -framework JavaScriptCore -framework Foundation -lstdc++ diff --git a/Source/WebCore/bridge/npapi.h b/Source/WebCore/bridge/npapi.h new file mode 100644 index 0000000..48e78c0 --- /dev/null +++ b/Source/WebCore/bridge/npapi.h @@ -0,0 +1,897 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef npapi_h_ +#define npapi_h_ + +#if defined(__OS2__) +#pragma pack(1) +#endif + +#include "nptypes.h" + +#if defined(__OS2__) || defined(OS2) +#ifndef XP_OS2 +#define XP_OS2 1 +#endif +#endif + +#ifdef INCLUDE_JAVA +#include "jri.h" /* Java Runtime Interface */ +#else +#define jref void * +#define JRIEnv void +#endif + +#if defined(_WIN32) && !defined(__SYMBIAN32__) +#include <windows.h> +#ifndef XP_WIN +#define XP_WIN 1 +#endif +#endif + +#if defined(__SYMBIAN32__) +#ifndef XP_SYMBIAN +#define XP_SYMBIAN 1 +#endif +#endif + +#if defined(__APPLE_CC__) && !defined(XP_UNIX) +#ifndef XP_MACOSX +#define XP_MACOSX 1 +#endif +#endif + +#if defined(XP_MACOSX) && defined(__LP64__) +#define NP_NO_QUICKDRAW +#define NP_NO_CARBON +#endif + +#if defined(XP_MACOSX) +#include <ApplicationServices/ApplicationServices.h> +#include <OpenGL/OpenGL.h> +#ifndef NP_NO_CARBON +#include <Carbon/Carbon.h> +#endif +#endif + +#if defined(XP_UNIX) +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <stdio.h> +#endif + +#if defined(XP_SYMBIAN) +#include <QEvent> +#include <QRegion> +#endif + +/*----------------------------------------------------------------------*/ +/* Plugin Version Constants */ +/*----------------------------------------------------------------------*/ + +#define NP_VERSION_MAJOR 0 +#define NP_VERSION_MINOR 24 + + +/* The OS/2 version of Netscape uses RC_DATA to define the + mime types, file extensions, etc that are required. + Use a vertical bar to separate types, end types with \0. + FileVersion and ProductVersion are 32bit ints, all other + entries are strings that MUST be terminated with a \0. + +AN EXAMPLE: + +RCDATA NP_INFO_ProductVersion { 1,0,0,1,} + +RCDATA NP_INFO_MIMEType { "video/x-video|", + "video/x-flick\0" } +RCDATA NP_INFO_FileExtents { "avi|", + "flc\0" } +RCDATA NP_INFO_FileOpenName{ "MMOS2 video player(*.avi)|", + "MMOS2 Flc/Fli player(*.flc)\0" } + +RCDATA NP_INFO_FileVersion { 1,0,0,1 } +RCDATA NP_INFO_CompanyName { "Netscape Communications\0" } +RCDATA NP_INFO_FileDescription { "NPAVI32 Extension DLL\0" +RCDATA NP_INFO_InternalName { "NPAVI32\0" ) +RCDATA NP_INFO_LegalCopyright { "Copyright Netscape Communications \251 1996\0" +RCDATA NP_INFO_OriginalFilename { "NVAPI32.DLL" } +RCDATA NP_INFO_ProductName { "NPAVI32 Dynamic Link Library\0" } +*/ +/* RC_DATA types for version info - required */ +#define NP_INFO_ProductVersion 1 +#define NP_INFO_MIMEType 2 +#define NP_INFO_FileOpenName 3 +#define NP_INFO_FileExtents 4 +/* RC_DATA types for version info - used if found */ +#define NP_INFO_FileDescription 5 +#define NP_INFO_ProductName 6 +/* RC_DATA types for version info - optional */ +#define NP_INFO_CompanyName 7 +#define NP_INFO_FileVersion 8 +#define NP_INFO_InternalName 9 +#define NP_INFO_LegalCopyright 10 +#define NP_INFO_OriginalFilename 11 + +#ifndef RC_INVOKED + +/*----------------------------------------------------------------------*/ +/* Definition of Basic Types */ +/*----------------------------------------------------------------------*/ + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef NULL +#define NULL (0L) +#endif + +typedef unsigned char NPBool; +typedef int16_t NPError; +typedef int16_t NPReason; +typedef char* NPMIMEType; + +/*----------------------------------------------------------------------*/ +/* Structures and definitions */ +/*----------------------------------------------------------------------*/ + +#if !defined(__LP64__) +#if defined(XP_MACOSX) +#pragma options align=mac68k +#endif +#endif /* __LP64__ */ + +/* + * NPP is a plug-in's opaque instance handle + */ +typedef struct _NPP +{ + void* pdata; /* plug-in private data */ + void* ndata; /* netscape private data */ +} NPP_t; + +typedef NPP_t* NPP; + +typedef struct _NPStream +{ + void* pdata; /* plug-in private data */ + void* ndata; /* netscape private data */ + const char* url; + uint32_t end; + uint32_t lastmodified; + void* notifyData; + const char* headers; /* Response headers from host. + * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS. + * Used for HTTP only; NULL for non-HTTP. + * Available from NPP_NewStream onwards. + * Plugin should copy this data before storing it. + * Includes HTTP status line and all headers, + * preferably verbatim as received from server, + * headers formatted as in HTTP ("Header: Value"), + * and newlines (\n, NOT \r\n) separating lines. + * Terminated by \n\0 (NOT \n\n\0). */ +} NPStream; + +typedef struct _NPByteRange +{ + int32_t offset; /* negative offset means from the end */ + uint32_t length; + struct _NPByteRange* next; +} NPByteRange; + +typedef struct _NPSavedData +{ + int32_t len; + void* buf; +} NPSavedData; + +typedef struct _NPRect +{ + uint16_t top; + uint16_t left; + uint16_t bottom; + uint16_t right; +} NPRect; + +typedef struct _NPSize +{ + int32_t width; + int32_t height; +} NPSize; + +/* Return values for NPP_HandleEvent */ +#define kNPEventNotHandled 0 +#define kNPEventHandled 1 +/* Exact meaning must be spec'd in event model. */ +#define kNPEventStartIME 2 + +#if defined(XP_UNIX) +/* + * Unix specific structures and definitions + */ + +/* + * Callback Structures. + * + * These are used to pass additional platform specific information. + */ +enum { + NP_SETWINDOW = 1, + NP_PRINT +}; + +typedef struct +{ + int32_t type; +} NPAnyCallbackStruct; + +typedef struct +{ + int32_t type; + Display* display; + Visual* visual; + Colormap colormap; + unsigned int depth; +} NPSetWindowCallbackStruct; + +typedef struct +{ + int32_t type; + FILE* fp; +} NPPrintCallbackStruct; + +#endif /* XP_UNIX */ + +#if defined(XP_MACOSX) +typedef enum { +#ifndef NP_NO_QUICKDRAW + NPDrawingModelQuickDraw = 0, +#endif + NPDrawingModelCoreGraphics = 1, + NPDrawingModelOpenGL = 2, + NPDrawingModelCoreAnimation = 3 +} NPDrawingModel; + +typedef enum { +#ifndef NP_NO_CARBON + NPEventModelCarbon = 0, +#endif + NPEventModelCocoa = 1 +} NPEventModel; +#endif + +/* + * The following masks are applied on certain platforms to NPNV and + * NPPV selectors that pass around pointers to COM interfaces. Newer + * compilers on some platforms may generate vtables that are not + * compatible with older compilers. To prevent older plugins from + * not understanding a new browser's ABI, these masks change the + * values of those selectors on those platforms. To remain backwards + * compatible with different versions of the browser, plugins can + * use these masks to dynamically determine and use the correct C++ + * ABI that the browser is expecting. This does not apply to Windows + * as Microsoft's COM ABI will likely not change. + */ + +#define NP_ABI_GCC3_MASK 0x10000000 +/* + * gcc 3.x generated vtables on UNIX and OSX are incompatible with + * previous compilers. + */ +#if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3)) +#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK +#else +#define _NP_ABI_MIXIN_FOR_GCC3 0 +#endif + +#if defined(XP_MACOSX) +#define NP_ABI_MACHO_MASK 0x01000000 +#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK +#else +#define _NP_ABI_MIXIN_FOR_MACHO 0 +#endif + +#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO) + +/* + * List of variable names for which NPP_GetValue shall be implemented + */ +typedef enum { + NPPVpluginNameString = 1, + NPPVpluginDescriptionString, + NPPVpluginWindowBool, + NPPVpluginTransparentBool, + NPPVjavaClass, /* Not implemented in WebKit */ + NPPVpluginWindowSize, /* Not implemented in WebKit */ + NPPVpluginTimerInterval, /* Not implemented in WebKit */ + NPPVpluginScriptableInstance = (10 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPPVpluginScriptableIID = 11, /* Not implemented in WebKit */ + NPPVjavascriptPushCallerBool = 12, /* Not implemented in WebKit */ + NPPVpluginKeepLibraryInMemory = 13, /* Not implemented in WebKit */ + NPPVpluginNeedsXEmbed = 14, /* Not implemented in WebKit */ + + /* Get the NPObject for scripting the plugin. Introduced in NPAPI minor version 14. + */ + NPPVpluginScriptableNPObject = 15, + + /* Get the plugin value (as \0-terminated UTF-8 string data) for + * form submission if the plugin is part of a form. Use + * NPN_MemAlloc() to allocate memory for the string data. Introduced + * in NPAPI minor version 15. + */ + NPPVformValue = 16, /* Not implemented in WebKit */ + + NPPVpluginUrlRequestsDisplayedBool = 17, /* Not implemented in WebKit */ + + /* Checks if the plugin is interested in receiving the http body of + * all http requests (including failed ones, http status != 200). + */ + NPPVpluginWantsAllNetworkStreams = 18, + + /* Browsers can retrieve a native ATK accessibility plug ID via this variable. */ + NPPVpluginNativeAccessibleAtkPlugId = 19, + + /* Checks to see if the plug-in would like the browser to load the "src" attribute. */ + NPPVpluginCancelSrcStream = 20 + +#if defined(XP_MACOSX) + /* Used for negotiating drawing models */ + , NPPVpluginDrawingModel = 1000 + /* Used for negotiating event models */ + , NPPVpluginEventModel = 1001 + /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */ + , NPPVpluginCoreAnimationLayer = 1003 +#endif + +#ifdef ANDROID + /* Used when the plugin returns 0 from NPN_WriteReady and wishes the browser + * to wait a certain amount of millis before calling NPN_WriteReady again. + */ + , NPPDataDeliveryDelayMs = 100 + + // TODO(reed): upstream + , NPPFakeValueToForce32Bits = 0x7FFFFFFF +#endif + +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + , NPPVpluginWindowlessLocalBool = 2002 +#endif +} NPPVariable; + +/* + * List of variable names for which NPN_GetValue should be implemented. + */ +typedef enum { + NPNVxDisplay = 1, + NPNVxtAppContext, + NPNVnetscapeWindow, + NPNVjavascriptEnabledBool, + NPNVasdEnabledBool, + NPNVisOfflineBool, + + NPNVserviceManager = (10 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVDOMElement = (11 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVDOMWindow = (12 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVToolkit = (13 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVSupportsXEmbedBool = 14, /* Not implemented in WebKit */ + + /* Get the NPObject wrapper for the browser window. */ + NPNVWindowNPObject = 15, + + /* Get the NPObject wrapper for the plugins DOM element. */ + NPNVPluginElementNPObject = 16, + + NPNVSupportsWindowless = 17, + + NPNVprivateModeBool = 18 + +#if defined(XP_MACOSX) + /* Used for negotiating drawing models */ + , NPNVpluginDrawingModel = 1000 +#ifndef NP_NO_QUICKDRAW + , NPNVsupportsQuickDrawBool = 2000 +#endif + , NPNVsupportsCoreGraphicsBool = 2001 + , NPNVsupportsOpenGLBool = 2002 + , NPNVsupportsCoreAnimationBool = 2003 +#ifndef NP_NO_CARBON + , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */ +#endif + , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */ +#endif /* XP_MACOSX */ + +#ifdef ANDROID + , NPNFakeValueToForce32Bits = 0x7FFFFFFF +#endif + +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + , NPNVSupportsWindowlessLocal = 2002 +#endif +} NPNVariable; + +typedef enum { + NPNURLVCookie = 501, + NPNURLVProxy +} NPNURLVariable; + +/* + * The type of Toolkit the widgets use + */ +typedef enum { + NPNVGtk12 = 1, + NPNVGtk2 +} NPNToolkitType; + +/* + * The type of a NPWindow - it specifies the type of the data structure + * returned in the window field. + */ +typedef enum { + NPWindowTypeWindow = 1, + NPWindowTypeDrawable +} NPWindowType; + +typedef struct _NPWindow +{ + void* window; /* Platform specific window handle */ + /* OS/2: x - Position of bottom left corner */ + /* OS/2: y - relative to visible netscape window */ + int32_t x; /* Position of top left corner relative */ + int32_t y; /* to a netscape page. */ + uint32_t width; /* Maximum window size */ + uint32_t height; + NPRect clipRect; /* Clipping rectangle in port coordinates */ +#if defined(XP_UNIX) || defined(XP_SYMBIAN) + void * ws_info; /* Platform-dependent additonal data */ +#endif /* XP_UNIX || XP_SYMBIAN */ + NPWindowType type; /* Is this a window or a drawable? */ +} NPWindow; + +typedef struct _NPImageExpose +{ + char* data; /* image pointer */ + int32_t stride; /* Stride of data image pointer */ + int32_t depth; /* Depth of image pointer */ + int32_t x; /* Expose x */ + int32_t y; /* Expose y */ + uint32_t width; /* Expose width */ + uint32_t height; /* Expose height */ + NPSize dataSize; /* Data buffer size */ + float translateX; /* translate X matrix value */ + float translateY; /* translate Y matrix value */ + float scaleX; /* scale X matrix value */ + float scaleY; /* scale Y matrix value */ +} NPImageExpose; + +typedef struct _NPFullPrint +{ + NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */ + NPBool printOne; /* TRUE if plugin should print one copy to default + printer */ + void* platformPrint; /* Platform-specific printing info */ +} NPFullPrint; + +typedef struct _NPEmbedPrint +{ + NPWindow window; + void* platformPrint; /* Platform-specific printing info */ +} NPEmbedPrint; + +typedef struct _NPPrint +{ + uint16_t mode; /* NP_FULL or NP_EMBED */ + union + { + NPFullPrint fullPrint; /* if mode is NP_FULL */ + NPEmbedPrint embedPrint; /* if mode is NP_EMBED */ + } print; +} NPPrint; + +#if defined(XP_MACOSX) +#ifndef NP_NO_CARBON +typedef EventRecord NPEvent; +#else +typedef void* NPEvent; +#endif +#elif defined(XP_SYMBIAN) +typedef QEvent NPEvent; +#elif defined(XP_WIN) +typedef struct _NPEvent +{ + uint16_t event; + uintptr_t wParam; + uintptr_t lParam; +} NPEvent; +#elif defined(XP_OS2) +typedef struct _NPEvent +{ + uint32_t event; + uint32_t wParam; + uint32_t lParam; +} NPEvent; +#elif defined(XP_UNIX) +typedef XEvent NPEvent; +#else +typedef void* NPEvent; +#endif + +#if defined(XP_MACOSX) +typedef void* NPRegion; +#ifndef NP_NO_QUICKDRAW +typedef RgnHandle NPQDRegion; +#endif +typedef CGPathRef NPCGRegion; +#elif defined(XP_WIN) +typedef HRGN NPRegion; +#elif defined(XP_UNIX) +typedef Region NPRegion; +#elif defined(XP_SYMBIAN) +typedef QRegion* NPRegion; +#else +typedef void *NPRegion; +#endif + +typedef struct _NPNSString NPNSString; +typedef struct _NPNSWindow NPNSWindow; +typedef struct _NPNSMenu NPNSMenu; + +#if defined(XP_MACOSX) +typedef NPNSMenu NPMenu; +#else +typedef void *NPMenu; +#endif + +typedef enum { + NPCoordinateSpacePlugin = 1, + NPCoordinateSpaceWindow, + NPCoordinateSpaceFlippedWindow, + NPCoordinateSpaceScreen, + NPCoordinateSpaceFlippedScreen +} NPCoordinateSpace; + +#if defined(XP_MACOSX) + +#ifndef NP_NO_QUICKDRAW +typedef struct NP_Port +{ + CGrafPtr port; + int32_t portx; /* position inside the topmost window */ + int32_t porty; +} NP_Port; +#endif /* NP_NO_QUICKDRAW */ + +/* + * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics + * as its drawing model. + */ + +typedef struct NP_CGContext +{ + CGContextRef context; +#ifdef NP_NO_CARBON + NPNSWindow *window; +#else + void *window; /* A WindowRef or NULL for the Cocoa event model. */ +#endif +} NP_CGContext; + +/* + * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its + * drawing model. + */ + +typedef struct NP_GLContext +{ + CGLContextObj context; +#ifdef NP_NO_CARBON + NPNSWindow *window; +#else + void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */ +#endif +} NP_GLContext; + +typedef enum { + NPCocoaEventDrawRect = 1, + NPCocoaEventMouseDown, + NPCocoaEventMouseUp, + NPCocoaEventMouseMoved, + NPCocoaEventMouseEntered, + NPCocoaEventMouseExited, + NPCocoaEventMouseDragged, + NPCocoaEventKeyDown, + NPCocoaEventKeyUp, + NPCocoaEventFlagsChanged, + NPCocoaEventFocusChanged, + NPCocoaEventWindowFocusChanged, + NPCocoaEventScrollWheel, + NPCocoaEventTextInput +} NPCocoaEventType; + +typedef struct _NPCocoaEvent { + NPCocoaEventType type; + uint32_t version; + union { + struct { + uint32_t modifierFlags; + double pluginX; + double pluginY; + int32_t buttonNumber; + int32_t clickCount; + double deltaX; + double deltaY; + double deltaZ; + } mouse; + struct { + uint32_t modifierFlags; + NPNSString *characters; + NPNSString *charactersIgnoringModifiers; + NPBool isARepeat; + uint16_t keyCode; + } key; + struct { + CGContextRef context; + double x; + double y; + double width; + double height; + } draw; + struct { + NPBool hasFocus; + } focus; + struct { + NPNSString *text; + } text; + } data; +} NPCocoaEvent; + +#ifndef NP_NO_CARBON +/* Non-standard event types that can be passed to HandleEvent */ +enum NPEventType { + NPEventType_GetFocusEvent = (osEvt + 16), + NPEventType_LoseFocusEvent, + NPEventType_AdjustCursorEvent, + NPEventType_MenuCommandEvent, + NPEventType_ClippingChangedEvent, + NPEventType_ScrollingBeginsEvent = 1000, + NPEventType_ScrollingEndsEvent +}; +#endif /* NP_NO_CARBON */ + +#endif /* XP_MACOSX */ + +/* + * Values for mode passed to NPP_New: + */ +#define NP_EMBED 1 +#define NP_FULL 2 + +/* + * Values for stream type passed to NPP_NewStream: + */ +#define NP_NORMAL 1 +#define NP_SEEK 2 +#define NP_ASFILE 3 +#define NP_ASFILEONLY 4 + +#define NP_MAXREADY (((unsigned)(~0)<<1)>>1) + +#if !defined(__LP64__) +#if defined(XP_MACOSX) +#pragma options align=reset +#endif +#endif /* __LP64__ */ + +/*----------------------------------------------------------------------*/ +/* Error and Reason Code definitions */ +/*----------------------------------------------------------------------*/ + +/* + * Values of type NPError: + */ +#define NPERR_BASE 0 +#define NPERR_NO_ERROR (NPERR_BASE + 0) +#define NPERR_GENERIC_ERROR (NPERR_BASE + 1) +#define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2) +#define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3) +#define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4) +#define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5) +#define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6) +#define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7) +#define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8) +#define NPERR_INVALID_PARAM (NPERR_BASE + 9) +#define NPERR_INVALID_URL (NPERR_BASE + 10) +#define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11) +#define NPERR_NO_DATA (NPERR_BASE + 12) +#define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13) + +/* + * Values of type NPReason: + */ +#define NPRES_BASE 0 +#define NPRES_DONE (NPRES_BASE + 0) +#define NPRES_NETWORK_ERR (NPRES_BASE + 1) +#define NPRES_USER_BREAK (NPRES_BASE + 2) + +/* + * Don't use these obsolete error codes any more. + */ +#define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR +#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR +#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK + +/* + * Version feature information + */ +#define NPVERS_HAS_STREAMOUTPUT 8 +#define NPVERS_HAS_NOTIFICATION 9 +#define NPVERS_HAS_LIVECONNECT 9 +#define NPVERS_WIN16_HAS_LIVECONNECT 9 +#define NPVERS_68K_HAS_LIVECONNECT 11 +#define NPVERS_HAS_WINDOWLESS 11 +#define NPVERS_HAS_XPCONNECT_SCRIPTING 13 /* Not implemented in WebKit */ +#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14 +#define NPVERS_HAS_FORM_VALUES 15 /* Not implemented in WebKit; see bug 13061 */ +#define NPVERS_HAS_POPUPS_ENABLED_STATE 16 /* Not implemented in WebKit */ +#define NPVERS_HAS_RESPONSE_HEADERS 17 +#define NPVERS_HAS_NPOBJECT_ENUM 18 +#define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19 +#define NPVERS_HAS_ALL_NETWORK_STREAMS 20 +#define NPVERS_HAS_URL_AND_AUTH_INFO 21 +#define NPVERS_HAS_PRIVATE_MODE 22 +#define NPVERS_MACOSX_HAS_EVENT_MODELS 23 +#define NPVERS_HAS_CANCEL_SRC_STREAM 24 + +/*----------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------*/ + +#if defined(__OS2__) +#define NP_LOADDS _System +#else +#define NP_LOADDS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* NPP_* functions are provided by the plugin and called by the navigator. */ + +#if defined(XP_UNIX) +char* NPP_GetMIMEDescription(void); +#endif + +NPError NP_LOADDS NPP_Initialize(void); +void NP_LOADDS NPP_Shutdown(void); +NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance, + uint16_t mode, int16_t argc, char* argn[], + char* argv[], NPSavedData* saved); +NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save); +NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window); +NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type, + NPStream* stream, NPBool seekable, + uint16_t* stype); +NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +int32_t NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream); +int32_t NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32_t offset, + int32_t len, void* buffer); +void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream, + const char* fname); +void NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint); +int16_t NP_LOADDS NPP_HandleEvent(NPP instance, void* event); +void NP_LOADDS NPP_URLNotify(NPP instance, const char* url, + NPReason reason, void* notifyData); +jref NP_LOADDS NPP_GetJavaClass(void); +NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value); +NPError NP_LOADDS NPP_SetValue(NPP instance, NPNVariable variable, void *value); + +/* NPN_* functions are provided by the navigator and called by the plugin. */ +void NP_LOADDS NPN_Version(int* plugin_major, int* plugin_minor, + int* netscape_major, int* netscape_minor); +NPError NP_LOADDS NPN_GetURLNotify(NPP instance, const char* url, + const char* target, void* notifyData); +NPError NP_LOADDS NPN_GetURL(NPP instance, const char* url, + const char* target); +NPError NP_LOADDS NPN_PostURLNotify(NPP instance, const char* url, + const char* target, uint32_t len, + const char* buf, NPBool file, + void* notifyData); +NPError NP_LOADDS NPN_PostURL(NPP instance, const char* url, + const char* target, uint32_t len, + const char* buf, NPBool file); +NPError NP_LOADDS NPN_RequestRead(NPStream* stream, NPByteRange* rangeList); +NPError NP_LOADDS NPN_NewStream(NPP instance, NPMIMEType type, + const char* target, NPStream** stream); +int32_t NP_LOADDS NPN_Write(NPP instance, NPStream* stream, int32_t len, + void* buffer); +NPError NP_LOADDS NPN_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +void NP_LOADDS NPN_Status(NPP instance, const char* message); +const char* NP_LOADDS NPN_UserAgent(NPP instance); +void* NP_LOADDS NPN_MemAlloc(uint32_t size); +void NP_LOADDS NPN_MemFree(void* ptr); +uint32_t NP_LOADDS NPN_MemFlush(uint32_t size); +void NP_LOADDS NPN_ReloadPlugins(NPBool reloadPages); +JRIEnv* NP_LOADDS NPN_GetJavaEnv(void); +jref NP_LOADDS NPN_GetJavaPeer(NPP instance); +NPError NP_LOADDS NPN_GetValue(NPP instance, NPNVariable variable, + void *value); +NPError NP_LOADDS NPN_SetValue(NPP instance, NPPVariable variable, + void *value); +void NP_LOADDS NPN_InvalidateRect(NPP instance, NPRect *invalidRect); +void NP_LOADDS NPN_InvalidateRegion(NPP instance, + NPRegion invalidRegion); +void NP_LOADDS NPN_ForceRedraw(NPP instance); +void NP_LOADDS NPN_PushPopupsEnabledState(NPP instance, NPBool enabled); +void NP_LOADDS NPN_PopPopupsEnabledState(NPP instance); +void NP_LOADDS NPN_PluginThreadAsyncCall(NPP instance, + void (*func) (void *), + void *userData); +NPError NP_LOADDS NPN_GetValueForURL(NPP instance, NPNURLVariable variable, + const char *url, char **value, + uint32_t *len); +NPError NP_LOADDS NPN_SetValueForURL(NPP instance, NPNURLVariable variable, + const char *url, const char *value, + uint32_t len); +NPError NP_LOADDS NPN_GetAuthenticationInfo(NPP instance, + const char *protocol, + const char *host, int32_t port, + const char *scheme, + const char *realm, + char **username, uint32_t *ulen, + char **password, + uint32_t *plen); +uint32_t NP_LOADDS NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)); +void NP_LOADDS NPN_UnscheduleTimer(NPP instance, uint32_t timerID); +NPError NP_LOADDS NPN_PopUpContextMenu(NPP instance, NPMenu* menu); +NPBool NP_LOADDS NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* RC_INVOKED */ +#if defined(__OS2__) +#pragma pack() +#endif + +#endif /* npapi_h_ */ diff --git a/Source/WebCore/bridge/npruntime.cpp b/Source/WebCore/bridge/npruntime.cpp new file mode 100644 index 0000000..ab67076 --- /dev/null +++ b/Source/WebCore/bridge/npruntime.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "IdentifierRep.h" +#include "npruntime_internal.h" +#include "npruntime_impl.h" +#include "npruntime_priv.h" + +#include "c_utility.h" +#include <runtime/Identifier.h> +#include <runtime/JSLock.h> +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> + +using namespace JSC::Bindings; +using namespace WebCore; + +NPIdentifier _NPN_GetStringIdentifier(const NPUTF8* name) +{ + return static_cast<NPIdentifier>(IdentifierRep::get(name)); +} + +void _NPN_GetStringIdentifiers(const NPUTF8** names, int32_t nameCount, NPIdentifier* identifiers) +{ + ASSERT(names); + ASSERT(identifiers); + + if (names && identifiers) { + for (int i = 0; i < nameCount; i++) + identifiers[i] = _NPN_GetStringIdentifier(names[i]); + } +} + +NPIdentifier _NPN_GetIntIdentifier(int32_t intid) +{ + return static_cast<NPIdentifier>(IdentifierRep::get(intid)); +} + +bool _NPN_IdentifierIsString(NPIdentifier identifier) +{ + return static_cast<IdentifierRep*>(identifier)->isString(); +} + +NPUTF8 *_NPN_UTF8FromIdentifier(NPIdentifier identifier) +{ + const char* string = static_cast<IdentifierRep*>(identifier)->string(); + if (!string) + return 0; + + return strdup(string); +} + +int32_t _NPN_IntFromIdentifier(NPIdentifier identifier) +{ + return static_cast<IdentifierRep*>(identifier)->number(); +} + +void NPN_InitializeVariantWithStringCopy(NPVariant* variant, const NPString* value) +{ + variant->type = NPVariantType_String; + variant->value.stringValue.UTF8Length = value->UTF8Length; + variant->value.stringValue.UTF8Characters = (NPUTF8 *)malloc(sizeof(NPUTF8) * value->UTF8Length); + if (!variant->value.stringValue.UTF8Characters) + CRASH(); + memcpy((void*)variant->value.stringValue.UTF8Characters, value->UTF8Characters, sizeof(NPUTF8) * value->UTF8Length); +} + +void _NPN_ReleaseVariantValue(NPVariant* variant) +{ + ASSERT(variant); + + if (variant->type == NPVariantType_Object) { + _NPN_ReleaseObject(variant->value.objectValue); + variant->value.objectValue = 0; + } else if (variant->type == NPVariantType_String) { + free((void*)variant->value.stringValue.UTF8Characters); + variant->value.stringValue.UTF8Characters = 0; + variant->value.stringValue.UTF8Length = 0; + } + + variant->type = NPVariantType_Void; +} + +NPObject *_NPN_CreateObject(NPP npp, NPClass* aClass) +{ + ASSERT(aClass); + + if (aClass) { + NPObject* obj; + if (aClass->allocate != NULL) + obj = aClass->allocate(npp, aClass); + else + obj = (NPObject*)malloc(sizeof(NPObject)); + if (!obj) + CRASH(); + obj->_class = aClass; + obj->referenceCount = 1; + + return obj; + } + + return 0; +} + +NPObject* _NPN_RetainObject(NPObject* obj) +{ + ASSERT(obj); + + if (obj) + obj->referenceCount++; + + return obj; +} + +void _NPN_ReleaseObject(NPObject* obj) +{ + ASSERT(obj); + ASSERT(obj->referenceCount >= 1); + + if (obj && obj->referenceCount >= 1) { + if (--obj->referenceCount == 0) + _NPN_DeallocateObject(obj); + } +} + +void _NPN_DeallocateObject(NPObject *obj) +{ + ASSERT(obj); + + if (obj) { + if (obj->_class->deallocate) + obj->_class->deallocate(obj); + else + free(obj); + } +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebCore/bridge/npruntime.h b/Source/WebCore/bridge/npruntime.h new file mode 100644 index 0000000..468cf92 --- /dev/null +++ b/Source/WebCore/bridge/npruntime.h @@ -0,0 +1,393 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (c) 2004, Apple Computer, Inc. and The Mozilla Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla + * Foundation ("Mozilla") nor the names of their contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, MOZILLA OR + * THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _NP_RUNTIME_H_ +#define _NP_RUNTIME_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "npapi.h" + +/* + This API is used to facilitate binding code written in C to script + objects. The API in this header does not assume the presence of a + user agent. That is, it can be used to bind C code to scripting + environments outside of the context of a user agent. + + However, the normal use of the this API is in the context of a + scripting environment running in a browser or other user agent. + In particular it is used to support the extended Netscape + script-ability API for plugins (NP-SAP). NP-SAP is an extension + of the Netscape plugin API. As such we have adopted the use of + the "NP" prefix for this API. + + The following NP{N|P}Variables were added to the Netscape plugin + API (in npapi.h): + + NPNVWindowNPObject + NPNVPluginElementNPObject + NPPVpluginScriptableNPObject + + These variables are exposed through NPN_GetValue() and + NPP_GetValue() (respectively) and are used to establish the + initial binding between the user agent and native code. The DOM + objects in the user agent can be examined and manipulated using + the NPN_ functions that operate on NPObjects described in this + header. + + To the extent possible the assumptions about the scripting + language used by the scripting environment have been minimized. +*/ + +#define NP_BEGIN_MACRO do { +#define NP_END_MACRO } while (0) + +/* + Objects (non-primitive data) passed between 'C' and script is + always wrapped in an NPObject. The 'interface' of an NPObject is + described by an NPClass. +*/ +typedef struct NPObject NPObject; +typedef struct NPClass NPClass; + +typedef char NPUTF8; +typedef struct _NPString { + const NPUTF8 *UTF8Characters; + uint32_t UTF8Length; +} NPString; + +typedef enum { + NPVariantType_Void, + NPVariantType_Null, + NPVariantType_Bool, + NPVariantType_Int32, + NPVariantType_Double, + NPVariantType_String, + NPVariantType_Object +} NPVariantType; + +typedef struct _NPVariant { + NPVariantType type; + union { + bool boolValue; + int32_t intValue; + double doubleValue; + NPString stringValue; + NPObject *objectValue; + } value; +} NPVariant; + +/* + NPN_ReleaseVariantValue is called on all 'out' parameters + references. Specifically it is to be called on variants that own + their value, as is the case with all non-const NPVariant* + arguments after a successful call to any methods (except this one) + in this API. + + After calling NPN_ReleaseVariantValue, the type of the variant + will be NPVariantType_Void. +*/ +void NPN_ReleaseVariantValue(NPVariant *variant); + +#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void) +#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null) +#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool) +#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32) +#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double) +#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String) +#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object) + +#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue) +#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue) +#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue) +#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue) +#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue) + +#define VOID_TO_NPVARIANT(_v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Void; \ + (_v).value.objectValue = NULL; \ +NP_END_MACRO + +#define NULL_TO_NPVARIANT(_v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Null; \ + (_v).value.objectValue = NULL; \ +NP_END_MACRO + +#define BOOLEAN_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Bool; \ + (_v).value.boolValue = !!(_val); \ +NP_END_MACRO + +#define INT32_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Int32; \ + (_v).value.intValue = _val; \ +NP_END_MACRO + +#define DOUBLE_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Double; \ + (_v).value.doubleValue = _val; \ +NP_END_MACRO + +#define STRINGZ_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_String; \ + NPString str = { _val, uint32_t(strlen(_val)) }; \ + (_v).value.stringValue = str; \ +NP_END_MACRO + +#define STRINGN_TO_NPVARIANT(_val, _len, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_String; \ + NPString str = { _val, uint32_t(_len) }; \ + (_v).value.stringValue = str; \ +NP_END_MACRO + +#define OBJECT_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Object; \ + (_v).value.objectValue = _val; \ +NP_END_MACRO + + +/* + Type mappings (JavaScript types have been used for illustration + purposes): + + JavaScript to C (NPVariant with type:) + undefined NPVariantType_Void + null NPVariantType_Null + Boolean NPVariantType_Bool + Number NPVariantType_Double or NPVariantType_Int32 + String NPVariantType_String + Object NPVariantType_Object + + C (NPVariant with type:) to JavaScript + NPVariantType_Void undefined + NPVariantType_Null null + NPVariantType_Bool Boolean + NPVariantType_Int32 Number + NPVariantType_Double Number + NPVariantType_String String + NPVariantType_Object Object +*/ + +typedef void *NPIdentifier; + +/* + NPObjects have methods and properties. Methods and properties are + identified with NPIdentifiers. These identifiers may be reflected + in script. NPIdentifiers can be either strings or integers, IOW, + methods and properties can be identified by either strings or + integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be + compared using ==. In case of any errors, the requested + NPIdentifier(s) will be NULL. NPIdentifier lifetime is controlled + by the browser. Plugins do not need to worry about memory management + with regards to NPIdentifiers. +*/ +NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name); +void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, + NPIdentifier *identifiers); +NPIdentifier NPN_GetIntIdentifier(int32_t intid); +bool NPN_IdentifierIsString(NPIdentifier identifier); + +/* + The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed. +*/ +NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier); + +/* + Get the integer represented by identifier. If identifier is not an + integer identifier, the behaviour is undefined. +*/ +int32_t NPN_IntFromIdentifier(NPIdentifier identifier); + +/* + NPObject behavior is implemented using the following set of + callback functions. + + The NPVariant *result argument of these functions (where + applicable) should be released using NPN_ReleaseVariantValue(). +*/ +typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass); +typedef void (*NPDeallocateFunctionPtr)(NPObject *npobj); +typedef void (*NPInvalidateFunctionPtr)(NPObject *npobj); +typedef bool (*NPHasMethodFunctionPtr)(NPObject *npobj, NPIdentifier name); +typedef bool (*NPInvokeFunctionPtr)(NPObject *npobj, NPIdentifier name, + const NPVariant *args, uint32_t argCount, + NPVariant *result); +typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result); +typedef bool (*NPHasPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name); +typedef bool (*NPGetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name, + NPVariant *result); +typedef bool (*NPSetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name, + const NPVariant *value); +typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj, + NPIdentifier name); +typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value, + uint32_t *count); +typedef bool (*NPConstructFunctionPtr)(NPObject *npobj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result); + +/* + NPObjects returned by create, retain, invoke, and getProperty pass + a reference count to the caller. That is, the callee adds a + reference count which passes to the caller. It is the caller's + responsibility to release the returned object. + + NPInvokeFunctionPtr function may return 0 to indicate a void + result. + + NPInvalidateFunctionPtr is called by the scripting environment + when the native code is shutdown. Any attempt to message a + NPObject instance after the invalidate callback has been + called will result in undefined behavior, even if the native code + is still retaining those NPObject instances. (The runtime + will typically return immediately, with 0 or NULL, from an attempt + to dispatch to a NPObject, but this behavior should not be + depended upon.) + + The NPEnumerationFunctionPtr function may pass an array of + NPIdentifiers back to the caller. The callee allocs the memory of + the array using NPN_MemAlloc(), and it's the caller's responsibility + to release it using NPN_MemFree(). +*/ +struct NPClass +{ + uint32_t structVersion; + NPAllocateFunctionPtr allocate; + NPDeallocateFunctionPtr deallocate; + NPInvalidateFunctionPtr invalidate; + NPHasMethodFunctionPtr hasMethod; + NPInvokeFunctionPtr invoke; + NPInvokeDefaultFunctionPtr invokeDefault; + NPHasPropertyFunctionPtr hasProperty; + NPGetPropertyFunctionPtr getProperty; + NPSetPropertyFunctionPtr setProperty; + NPRemovePropertyFunctionPtr removeProperty; + NPEnumerationFunctionPtr enumerate; + NPConstructFunctionPtr construct; +}; + +#define NP_CLASS_STRUCT_VERSION 3 + +#define NP_CLASS_STRUCT_VERSION_ENUM 2 +#define NP_CLASS_STRUCT_VERSION_CTOR 3 + +#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \ + ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) + +#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass) \ + ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) + +struct NPObject { + NPClass *_class; + uint32_t referenceCount; + /* + * Additional space may be allocated here by types of NPObjects + */ +}; + +/* + If the class has an allocate function, NPN_CreateObject invokes + that function, otherwise a NPObject is allocated and + returned. This method will initialize the referenceCount member of + the NPObject to 1. +*/ +NPObject *NPN_CreateObject(NPP npp, NPClass *aClass); + +/* + Increment the NPObject's reference count. +*/ +NPObject *NPN_RetainObject(NPObject *npobj); + +/* + Decremented the NPObject's reference count. If the reference + count goes to zero, the class's destroy function is invoke if + specified, otherwise the object is freed directly. +*/ +void NPN_ReleaseObject(NPObject *npobj); + +/* + Functions to access script objects represented by NPObject. + + Calls to script objects are synchronous. If a function returns a + value, it will be supplied via the result NPVariant + argument. Successful calls will return true, false will be + returned in case of an error. + + Calls made from plugin code to script must be made from the thread + on which the plugin was initialized. +*/ + +bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, + const NPVariant *args, uint32_t argCount, NPVariant *result); +bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); +bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, + NPVariant *result); +bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, + NPVariant *result); +bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, + const NPVariant *value); +bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName); +bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, + uint32_t *count); +bool NPN_Construct(NPP npp, NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + +/* + NPN_SetException may be called to trigger a script exception upon + return from entry points into NPObjects. Typical usage: + + NPN_SetException (npobj, message); +*/ +void NPN_SetException(NPObject *npobj, const NPUTF8 *message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Source/WebCore/bridge/npruntime_impl.h b/Source/WebCore/bridge/npruntime_impl.h new file mode 100644 index 0000000..559f340 --- /dev/null +++ b/Source/WebCore/bridge/npruntime_impl.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NP_RUNTIME_IMPL_H_ +#define _NP_RUNTIME_IMPL_H_ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "npruntime_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void _NPN_ReleaseVariantValue(NPVariant*); +extern NPIdentifier _NPN_GetStringIdentifier(const NPUTF8*); +extern void _NPN_GetStringIdentifiers(const NPUTF8** names, int32_t nameCount, NPIdentifier* identifiers); +extern NPIdentifier _NPN_GetIntIdentifier(int32_t); +extern bool _NPN_IdentifierIsString(NPIdentifier); +extern NPUTF8* _NPN_UTF8FromIdentifier(NPIdentifier); +extern int32_t _NPN_IntFromIdentifier(NPIdentifier); +extern NPObject* _NPN_CreateObject(NPP, NPClass*); +extern NPObject* _NPN_RetainObject(NPObject*); +extern void _NPN_ReleaseObject(NPObject*); +extern void _NPN_DeallocateObject(NPObject*); +extern bool _NPN_Invoke(NPP, NPObject*, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result); +extern bool _NPN_InvokeDefault(NPP, NPObject*, const NPVariant* args, uint32_t argCount, NPVariant* result); +extern bool _NPN_Evaluate(NPP, NPObject*, NPString*, NPVariant* result); +extern bool _NPN_GetProperty(NPP, NPObject*, NPIdentifier, NPVariant* result); +extern bool _NPN_SetProperty(NPP, NPObject*, NPIdentifier, const NPVariant*); +extern bool _NPN_RemoveProperty(NPP, NPObject*, NPIdentifier); +extern bool _NPN_HasProperty(NPP, NPObject*, NPIdentifier); +extern bool _NPN_HasMethod(NPP, NPObject*, NPIdentifier); +extern void _NPN_SetException(NPObject*, const NPUTF8*); +extern bool _NPN_Enumerate(NPP, NPObject*, NPIdentifier**, uint32_t* count); +extern bool _NPN_Construct(NPP, NPObject*, const NPVariant* args, uint32_t argCount, NPVariant *result); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/Source/WebCore/bridge/npruntime_internal.h b/Source/WebCore/bridge/npruntime_internal.h new file mode 100644 index 0000000..550c34c --- /dev/null +++ b/Source/WebCore/bridge/npruntime_internal.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007-2008 Collabora Ltd. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * This is a internal include header for npapi.h + * + * Some of the #defines which are in X11 headers conflict with type and enum + * names in JavaScriptCore and WebCore + * This header #undefs those defines to fix the conflicts + * If you need to include npapi.h or npruntime.h when building on X11, + * include this file instead of the actual npapi.h or npruntime.h + */ + +#include "npapi.h" +#include "npfunctions.h" +#include "npruntime.h" + +#ifdef XP_UNIX + #include <X11/Xresource.h> + + #undef None + #undef Above + #undef Below + #undef Auto + #undef Complex + #undef Status + #undef CursorShape + #undef FocusIn + #undef FocusOut + #undef KeyPress + #undef KeyRelease + #undef Unsorted + #undef Bool + #undef FontChange + #undef GrayScale + #undef NormalState + #undef True + #undef False +#endif diff --git a/Source/WebCore/bridge/npruntime_priv.h b/Source/WebCore/bridge/npruntime_priv.h new file mode 100644 index 0000000..301c163 --- /dev/null +++ b/Source/WebCore/bridge/npruntime_priv.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NP_RUNTIME_PRIV_H_ +#define NP_RUNTIME_PRIV_H_ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#include "npruntime_internal.h" + +/* + NPN_InitializeVariantWithStringCopy() will copy string data. The string data + will be deallocated by calls to NPReleaseVariantValue(). +*/ +void NPN_InitializeVariantWithStringCopy(NPVariant*, const NPString*); + +#endif // ENABLE(NETSCAPE_PLUGIN_API) +#endif + diff --git a/Source/WebCore/bridge/nptypes.h b/Source/WebCore/bridge/nptypes.h new file mode 100644 index 0000000..11e9683 --- /dev/null +++ b/Source/WebCore/bridge/nptypes.h @@ -0,0 +1,214 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Johnny Stenback <jst@mozilla.org> (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Header file for ensuring that C99 types ([u]int32_t and bool) are + * available. + */ + +#if defined(WIN32) || defined(OS2) + /* + * Win32 and OS/2 don't know C99, so define [u]int_16/32 here. The bool + * is predefined tho, both in C and C++. + */ + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; +#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX) + /* + * AIX and SunOS ship a inttypes.h header that defines [u]int32_t, + * but not bool for C. + */ + #include <inttypes.h> + + #ifndef __cplusplus + typedef int bool; + #endif +#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD) + /* + * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and + * u_int32_t. + */ + #include <sys/types.h> + + /* + * BSD/OS ships no header that defines uint32_t, nor bool (for C) + */ + #if defined(bsdi) + typedef u_int32_t uint32_t; + + #if !defined(__cplusplus) + typedef int bool; + #endif + #else + /* + * FreeBSD and OpenBSD define uint32_t and bool. + */ + #include <inttypes.h> + #include <stdbool.h> + #endif +#elif defined(BEOS) + #include <inttypes.h> +#else + /* + * For those that ship a standard C99 stdint.h header file, include + * it. Can't do the same for stdbool.h tho, since some systems ship + * with a stdbool.h file that doesn't compile! + */ + #include <stdint.h> + + #ifndef __cplusplus + #if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95) + #include <stdbool.h> + #else + /* + * GCC 2.91 can't deal with a typedef for bool, but a #define + * works. + */ + #define bool int + #endif + #endif +#endif +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Johnny Stenback <jst@mozilla.org> (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Header file for ensuring that C99 types ([u]int32_t and bool) are + * available. + */ + +#if defined(WIN32) || defined(OS2) + /* + * Win32 and OS/2 don't know C99, so define [u]int_16/32 here. The bool + * is predefined tho, both in C and C++. + */ + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; +#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX) + /* + * AIX and SunOS ship a inttypes.h header that defines [u]int32_t, + * but not bool for C. + */ + #include <inttypes.h> + + #ifndef __cplusplus + typedef int bool; + #endif +#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD) + /* + * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and + * u_int32_t. + */ + #include <sys/types.h> + + /* + * BSD/OS ships no header that defines uint32_t, nor bool (for C) + */ + #if defined(bsdi) + typedef u_int32_t uint32_t; + + #if !defined(__cplusplus) + typedef int bool; + #endif + #else + /* + * FreeBSD and OpenBSD define uint32_t and bool. + */ + #include <inttypes.h> + #include <stdbool.h> + #endif +#elif defined(BEOS) + #include <inttypes.h> +#else + /* + * For those that ship a standard C99 stdint.h header file, include + * it. Can't do the same for stdbool.h tho, since some systems ship + * with a stdbool.h file that doesn't compile! + */ + #include <stdint.h> + + #ifndef __cplusplus + #if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95) + #include <stdbool.h> + #else + /* + * GCC 2.91 can't deal with a typedef for bool, but a #define + * works. + */ + #define bool int + #endif + #endif +#endif diff --git a/Source/WebCore/bridge/objc/ObjCRuntimeObject.h b/Source/WebCore/bridge/objc/ObjCRuntimeObject.h new file mode 100644 index 0000000..78550b9 --- /dev/null +++ b/Source/WebCore/bridge/objc/ObjCRuntimeObject.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ObjCRuntimeObject_h +#define ObjCRuntimeObject_h + +#include "runtime_object.h" + +namespace JSC { +namespace Bindings { + +class ObjcInstance; + +class ObjCRuntimeObject : public RuntimeObject { +public: + ObjCRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<ObjcInstance>); + virtual ~ObjCRuntimeObject(); + + ObjcInstance* getInternalObjCInstance() const; + + static const ClassInfo s_info; + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } +}; + +} +} + +#endif diff --git a/Source/WebCore/bridge/objc/ObjCRuntimeObject.mm b/Source/WebCore/bridge/objc/ObjCRuntimeObject.mm new file mode 100644 index 0000000..d9afdf2 --- /dev/null +++ b/Source/WebCore/bridge/objc/ObjCRuntimeObject.mm @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" + +#import "ObjCRuntimeObject.h" +#import "objc_instance.h" + +namespace JSC { +namespace Bindings { + +const ClassInfo ObjCRuntimeObject::s_info = { "ObjCRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +ObjCRuntimeObject::ObjCRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<ObjcInstance> instance) + : RuntimeObject(exec, globalObject, instance) +{ +} + +ObjCRuntimeObject::~ObjCRuntimeObject() +{ +} + +ObjcInstance* ObjCRuntimeObject::getInternalObjCInstance() const +{ + return static_cast<ObjcInstance*>(getInternalInstance()); +} + + +} +} diff --git a/Source/WebCore/bridge/objc/WebScriptObject.h b/Source/WebCore/bridge/objc/WebScriptObject.h new file mode 100644 index 0000000..6d9ff2c --- /dev/null +++ b/Source/WebCore/bridge/objc/WebScriptObject.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2006 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> +#import "runtime_root.h" + +@class WebUndefined; + +@protocol WebScriptObject ++ (NSString *)webScriptNameForSelector:(SEL)aSelector; ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector; ++ (NSString *)webScriptNameForKey:(const char *)name; ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name; + ++ (id)_convertValueToObjcValue:(JSC::JSValue)value originRootObject:(JSC::Bindings::RootObject*)originRootObject rootObject:(JSC::Bindings::RootObject*)rootObject; +- _initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject; +- (JSC::JSObject *)_imp; +@end + +@protocol WebUndefined ++ (WebUndefined *)undefined; +@end diff --git a/Source/WebCore/bridge/objc/objc_class.h b/Source/WebCore/bridge/objc/objc_class.h new file mode 100644 index 0000000..eebfd2a --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_class.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KJS_BINDINGS_OBJC_CLASS_H +#define KJS_BINDINGS_OBJC_CLASS_H + +#include "objc_runtime.h" + +namespace JSC { +namespace Bindings { + +class ObjcClass : public Class +{ +protected: + ObjcClass (ClassStructPtr aClass); // Use classForIsA to create an ObjcClass. + +public: + // Return the cached ObjC of the specified name. + static ObjcClass *classForIsA(ClassStructPtr); + + virtual MethodList methodsNamed(const Identifier&, Instance *instance) const; + virtual Field *fieldNamed(const Identifier&, Instance *instance) const; + + virtual JSValue fallbackObject(ExecState *exec, Instance *instance, const Identifier &propertyName); + + ClassStructPtr isa() { return _isa; } + +private: + ClassStructPtr _isa; + RetainPtr<CFMutableDictionaryRef> _methods; + RetainPtr<CFMutableDictionaryRef> _fields; +}; + +} // namespace Bindings +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/objc/objc_class.mm b/Source/WebCore/bridge/objc/objc_class.mm new file mode 100644 index 0000000..2d29499 --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_class.mm @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "objc_class.h" + +#include "objc_instance.h" +#include "WebScriptObject.h" + +namespace JSC { +namespace Bindings { + +static void deleteMethod(CFAllocatorRef, const void* value) +{ + delete static_cast<const Method*>(value); +} + +static void deleteField(CFAllocatorRef, const void* value) +{ + delete static_cast<const Field*>(value); +} + +const CFDictionaryValueCallBacks MethodDictionaryValueCallBacks = { 0, 0, &deleteMethod, 0 , 0 }; +const CFDictionaryValueCallBacks FieldDictionaryValueCallBacks = { 0, 0, &deleteField, 0 , 0 }; + +ObjcClass::ObjcClass(ClassStructPtr aClass) + : _isa(aClass) + , _methods(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &MethodDictionaryValueCallBacks)) + , _fields(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &FieldDictionaryValueCallBacks)) +{ +} + +static CFMutableDictionaryRef classesByIsA = 0; + +static void _createClassesByIsAIfNecessary() +{ + if (!classesByIsA) + classesByIsA = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); +} + +ObjcClass* ObjcClass::classForIsA(ClassStructPtr isa) +{ + _createClassesByIsAIfNecessary(); + + ObjcClass* aClass = (ObjcClass*)CFDictionaryGetValue(classesByIsA, isa); + if (!aClass) { + aClass = new ObjcClass(isa); + CFDictionaryAddValue(classesByIsA, isa, aClass); + } + + return aClass; +} + +MethodList ObjcClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList methodList; + char fixedSizeBuffer[1024]; + char* buffer = fixedSizeBuffer; + CString jsName = identifier.ascii(); + if (!convertJSMethodNameToObjc(jsName.data(), buffer, sizeof(fixedSizeBuffer))) { + int length = jsName.length() + 1; + buffer = new char[length]; + if (!buffer || !convertJSMethodNameToObjc(jsName.data(), buffer, length)) + return methodList; + } + + + RetainPtr<CFStringRef> methodName(AdoptCF, CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII)); + Method* method = (Method*)CFDictionaryGetValue(_methods.get(), methodName.get()); + if (method) { + methodList.append(method); + return methodList; + } + + ClassStructPtr thisClass = _isa; + while (thisClass && methodList.isEmpty()) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + unsigned numMethodsInClass = 0; + MethodStructPtr* objcMethodList = class_copyMethodList(thisClass, &numMethodsInClass); +#else + void* iterator = 0; + struct objc_method_list* objcMethodList; + while ((objcMethodList = class_nextMethodList(thisClass, &iterator))) { + unsigned numMethodsInClass = objcMethodList->method_count; +#endif + for (unsigned i = 0; i < numMethodsInClass; i++) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + MethodStructPtr objcMethod = objcMethodList[i]; + SEL objcMethodSelector = method_getName(objcMethod); +#else + struct objc_method* objcMethod = &objcMethodList->method_list[i]; + SEL objcMethodSelector = objcMethod->method_name; +#endif + const char* objcMethodSelectorName = sel_getName(objcMethodSelector); + NSString* mappedName = nil; + + // See if the class wants to exclude the selector from visibility in JavaScript. + if ([thisClass respondsToSelector:@selector(isSelectorExcludedFromWebScript:)]) + if ([thisClass isSelectorExcludedFromWebScript:objcMethodSelector]) + continue; + + // See if the class want to provide a different name for the selector in JavaScript. + // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity + // of the class. + if ([thisClass respondsToSelector:@selector(webScriptNameForSelector:)]) + mappedName = [thisClass webScriptNameForSelector:objcMethodSelector]; + + if ((mappedName && [mappedName isEqual:(NSString*)methodName.get()]) || strcmp(objcMethodSelectorName, buffer) == 0) { + Method* aMethod = new ObjcMethod(thisClass, objcMethodSelector); // deleted when the dictionary is destroyed + CFDictionaryAddValue(_methods.get(), methodName.get(), aMethod); + methodList.append(aMethod); + break; + } + } +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + thisClass = class_getSuperclass(thisClass); + free(objcMethodList); +#else + } + thisClass = thisClass->super_class; +#endif + } + + if (buffer != fixedSizeBuffer) + delete [] buffer; + + return methodList; +} + +Field* ObjcClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + ClassStructPtr thisClass = _isa; + + CString jsName = identifier.ascii(); + RetainPtr<CFStringRef> fieldName(AdoptCF, CFStringCreateWithCString(NULL, jsName.data(), kCFStringEncodingASCII)); + Field* aField = (Field*)CFDictionaryGetValue(_fields.get(), fieldName.get()); + if (aField) + return aField; + + id targetObject = (static_cast<ObjcInstance*>(instance))->getObject(); + id attributes = [targetObject attributeKeys]; + if (attributes) { + // Class overrides attributeKeys, use that array of key names. + unsigned count = [attributes count]; + for (unsigned i = 0; i < count; i++) { + NSString* keyName = [attributes objectAtIndex:i]; + const char* UTF8KeyName = [keyName UTF8String]; // ObjC actually only supports ASCII names. + + // See if the class wants to exclude the selector from visibility in JavaScript. + if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)]) + if ([thisClass isKeyExcludedFromWebScript:UTF8KeyName]) + continue; + + // See if the class want to provide a different name for the selector in JavaScript. + // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity + // of the class. + NSString* mappedName = nil; + if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)]) + mappedName = [thisClass webScriptNameForKey:UTF8KeyName]; + + if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || [keyName isEqual:(NSString*)fieldName.get()]) { + aField = new ObjcField((CFStringRef)keyName); // deleted when the dictionary is destroyed + CFDictionaryAddValue(_fields.get(), fieldName.get(), aField); + break; + } + } + } else { + // Class doesn't override attributeKeys, so fall back on class runtime + // introspection. + + while (thisClass) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + unsigned numFieldsInClass = 0; + IvarStructPtr* ivarsInClass = class_copyIvarList(thisClass, &numFieldsInClass); +#else + struct objc_ivar_list* fieldsInClass = thisClass->ivars; + if (fieldsInClass) { + unsigned numFieldsInClass = fieldsInClass->ivar_count; +#endif + for (unsigned i = 0; i < numFieldsInClass; i++) { +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + IvarStructPtr objcIVar = ivarsInClass[i]; + const char* objcIvarName = ivar_getName(objcIVar); +#else + IvarStructPtr objcIVar = &fieldsInClass->ivar_list[i]; + const char* objcIvarName = objcIVar->ivar_name; +#endif + NSString* mappedName = 0; + + // See if the class wants to exclude the selector from visibility in JavaScript. + if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)]) + if ([thisClass isKeyExcludedFromWebScript:objcIvarName]) + continue; + + // See if the class want to provide a different name for the selector in JavaScript. + // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity + // of the class. + if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)]) + mappedName = [thisClass webScriptNameForKey:objcIvarName]; + + if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || strcmp(objcIvarName, jsName.data()) == 0) { + aField = new ObjcField(objcIVar); // deleted when the dictionary is destroyed + CFDictionaryAddValue(_fields.get(), fieldName.get(), aField); + break; + } + } +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + thisClass = class_getSuperclass(thisClass); + free(ivarsInClass); +#else + } + thisClass = thisClass->super_class; +#endif + } + } + + return aField; +} + +JSValue ObjcClass::fallbackObject(ExecState* exec, Instance* instance, const Identifier &propertyName) +{ + ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance); + id targetObject = objcInstance->getObject(); + + if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) + return jsUndefined(); + return new (exec) ObjcFallbackObjectImp(exec, exec->lexicalGlobalObject(), objcInstance, propertyName); +} + +} +} diff --git a/Source/WebCore/bridge/objc/objc_header.h b/Source/WebCore/bridge/objc/objc_header.h new file mode 100644 index 0000000..07954a1 --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_header.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KJS_BINDINGS_OBJC_HEADER_H +#define KJS_BINDINGS_OBJC_HEADER_H + +#ifdef __OBJC__ + +#include <objc/objc.h> +#include <objc/objc-class.h> +#include <objc/objc-runtime.h> + +typedef Class ClassStructPtr; +typedef id ObjectStructPtr; +typedef Method MethodStructPtr; +typedef Ivar IvarStructPtr; + +@class NSMethodSignature; + +#else + +typedef struct objc_class* ClassStructPtr; +typedef struct objc_object* ObjectStructPtr; +typedef struct objc_method* MethodStructPtr; +typedef struct objc_ivar* IvarStructPtr; + +class NSMethodSignature; + +#endif + +#endif diff --git a/Source/WebCore/bridge/objc/objc_instance.h b/Source/WebCore/bridge/objc/objc_instance.h new file mode 100644 index 0000000..ae6972a --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_instance.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2003, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BINDINGS_OBJC_INSTANCE_H_ +#define BINDINGS_OBJC_INSTANCE_H_ + +#include "objc_class.h" +#include "objc_utility.h" + +namespace JSC { + +namespace Bindings { + +class ObjcClass; + +class ObjcInstance : public Instance { +public: + static PassRefPtr<ObjcInstance> create(ObjectStructPtr, PassRefPtr<RootObject>); + virtual ~ObjcInstance(); + + static void setGlobalException(NSString*, JSGlobalObject* exceptionEnvironment = 0); // A null exceptionEnvironment means the exception should propogate to any execution environment. + + virtual Class* getClass() const; + + virtual JSValue valueOf(ExecState*) const; + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName); + JSValue invokeObjcMethod(ExecState*, ObjcMethod* method); + virtual JSValue invokeMethod(ExecState*, RuntimeMethod* method); + virtual bool supportsInvokeDefaultMethod() const; + virtual JSValue invokeDefaultMethod(ExecState*); + + JSValue getValueOfUndefinedField(ExecState*, const Identifier&) const; + virtual bool setValueOfUndefinedField(ExecState*, const Identifier&, JSValue); + + ObjectStructPtr getObject() const { return _instance.get(); } + + JSValue stringValue(ExecState*) const; + JSValue numberValue(ExecState*) const; + JSValue booleanValue() const; + +protected: + virtual void virtualBegin(); + virtual void virtualEnd(); + +private: + static void moveGlobalExceptionToExecState(ExecState*); + + ObjcInstance(ObjectStructPtr, PassRefPtr<RootObject>); + + virtual RuntimeObject* newRuntimeObject(ExecState*); + + RetainPtr<ObjectStructPtr> _instance; + mutable ObjcClass *_class; + ObjectStructPtr _pool; + int _beginCount; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // BINDINGS_OBJC_INSTANCE_H_ diff --git a/Source/WebCore/bridge/objc/objc_instance.mm b/Source/WebCore/bridge/objc/objc_instance.mm new file mode 100644 index 0000000..ae9d95d --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_instance.mm @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2004, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "objc_instance.h" + +#import "runtime_method.h" +#import "ObjCRuntimeObject.h" +#import "WebScriptObject.h" +#import <objc/objc-auto.h> +#import <runtime/Error.h> +#import <runtime/JSLock.h> +#import <wtf/Assertions.h> + +#ifdef NDEBUG +#define OBJC_LOG(formatAndArgs...) ((void)0) +#else +#define OBJC_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +using namespace JSC::Bindings; +using namespace JSC; + +static NSString *s_exception; +static JSGlobalObject* s_exceptionEnvironment; // No need to protect this value, since we just use it for a pointer comparison. +static NSMapTable *s_instanceWrapperCache; + +static NSMapTable *createInstanceWrapperCache() +{ +#ifdef BUILDING_ON_TIGER + return NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0); +#else + // NSMapTable with zeroing weak pointers is the recommended way to build caches like this under garbage collection. + NSPointerFunctionsOptions keyOptions = NSPointerFunctionsZeroingWeakMemory | NSPointerFunctionsOpaquePersonality; + NSPointerFunctionsOptions valueOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality; + return [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0]; +#endif +} + +RuntimeObject* ObjcInstance::newRuntimeObject(ExecState* exec) +{ + return new (exec) ObjCRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +void ObjcInstance::setGlobalException(NSString* exception, JSGlobalObject* exceptionEnvironment) +{ + NSString *oldException = s_exception; + s_exception = [exception copy]; + [oldException release]; + + s_exceptionEnvironment = exceptionEnvironment; +} + +void ObjcInstance::moveGlobalExceptionToExecState(ExecState* exec) +{ + if (!s_exception) { + ASSERT(!s_exceptionEnvironment); + return; + } + + if (!s_exceptionEnvironment || s_exceptionEnvironment == exec->dynamicGlobalObject()) { + JSLock lock(SilenceAssertionsOnly); + throwError(exec, s_exception); + } + + [s_exception release]; + s_exception = nil; + s_exceptionEnvironment = 0; +} + +ObjcInstance::ObjcInstance(id instance, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) + , _instance(instance) + , _class(0) + , _pool(0) + , _beginCount(0) +{ +} + +PassRefPtr<ObjcInstance> ObjcInstance::create(id instance, PassRefPtr<RootObject> rootObject) +{ + if (!s_instanceWrapperCache) + s_instanceWrapperCache = createInstanceWrapperCache(); + if (void* existingWrapper = NSMapGet(s_instanceWrapperCache, instance)) + return static_cast<ObjcInstance*>(existingWrapper); + RefPtr<ObjcInstance> wrapper = adoptRef(new ObjcInstance(instance, rootObject)); + NSMapInsert(s_instanceWrapperCache, instance, wrapper.get()); + return wrapper.release(); +} + +ObjcInstance::~ObjcInstance() +{ + // Both -finalizeForWebScript and -dealloc/-finalize of _instance may require autorelease pools. + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + ASSERT(s_instanceWrapperCache); + ASSERT(_instance); + NSMapRemove(s_instanceWrapperCache, _instance.get()); + + if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)]) + [_instance.get() performSelector:@selector(finalizeForWebScript)]; + _instance = 0; + + [pool drain]; +} + +static NSAutoreleasePool* allocateAutoReleasePool() +{ +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + // If GC is enabled an autorelease pool is unnecessary, and the + // pool cannot be protected from GC so may be collected leading + // to a crash when we try to drain the release pool. + if (objc_collectingEnabled()) + return nil; +#endif + return [[NSAutoreleasePool alloc] init]; +} + +void ObjcInstance::virtualBegin() +{ + if (!_pool) + _pool = allocateAutoReleasePool(); + _beginCount++; +} + +void ObjcInstance::virtualEnd() +{ + _beginCount--; + ASSERT(_beginCount >= 0); + if (!_beginCount) { + [_pool drain]; + _pool = 0; + } +} + +Bindings::Class* ObjcInstance::getClass() const +{ + if (!_instance) + return 0; + if (!_class) + _class = ObjcClass::classForIsA(_instance->isa); + return static_cast<Bindings::Class*>(_class); +} + +bool ObjcInstance::supportsInvokeDefaultMethod() const +{ + return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)]; +} + +class ObjCRuntimeMethod : public RuntimeMethod { +public: + ObjCRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list) + : RuntimeMethod(exec, globalObject, name, list) + { + } + + virtual const ClassInfo* classInfo() const { return &s_info; } + + static const ClassInfo s_info; +}; + +const ClassInfo ObjCRuntimeMethod::s_info = { "ObjCRuntimeMethod", &RuntimeMethod::s_info, 0, 0 }; + +JSValue ObjcInstance::getMethod(ExecState* exec, const Identifier& propertyName) +{ + MethodList methodList = getClass()->methodsNamed(propertyName, this); + return new (exec) ObjCRuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue ObjcInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod) +{ + if (!asObject(runtimeMethod)->inherits(&ObjCRuntimeMethod::s_info)) + return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object.")); + + const MethodList& methodList = *runtimeMethod->methods(); + + // Overloading methods is not allowed in ObjectiveC. Should only be one + // name match for a particular method. + ASSERT(methodList.size() == 1); + + return invokeObjcMethod(exec, static_cast<ObjcMethod*>(methodList[0])); +} + +JSValue ObjcInstance::invokeObjcMethod(ExecState* exec, ObjcMethod* method) +{ + JSValue result = jsUndefined(); + + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly. + + setGlobalException(nil); + +@try { + NSMethodSignature* signature = method->getMethodSignature(); + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setSelector:method->selector()]; + [invocation setTarget:_instance.get()]; + + if (method->isFallbackMethod()) { + if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) { + NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object."); + return result; + } + + // Invoke invokeUndefinedMethodFromWebScript:withArguments:, pass JavaScript function + // name as first (actually at 2) argument and array of args as second. + NSString* jsName = (NSString* )method->javaScriptName(); + [invocation setArgument:&jsName atIndex:2]; + + NSMutableArray* objcArgs = [NSMutableArray array]; + int count = exec->argumentCount(); + for (int i = 0; i < count; i++) { + ObjcValue value = convertValueToObjcValue(exec, exec->argument(i), ObjcObjectType); + [objcArgs addObject:value.objectValue]; + } + [invocation setArgument:&objcArgs atIndex:3]; + } else { + unsigned count = [signature numberOfArguments]; + for (unsigned i = 2; i < count ; i++) { + const char* type = [signature getArgumentTypeAtIndex:i]; + ObjcValueType objcValueType = objcValueTypeForType(type); + + // Must have a valid argument type. This method signature should have + // been filtered already to ensure that it has acceptable argument + // types. + ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType); + + ObjcValue value = convertValueToObjcValue(exec, exec->argument(i-2), objcValueType); + + switch (objcValueType) { + case ObjcObjectType: + [invocation setArgument:&value.objectValue atIndex:i]; + break; + case ObjcCharType: + case ObjcUnsignedCharType: + [invocation setArgument:&value.charValue atIndex:i]; + break; + case ObjcShortType: + case ObjcUnsignedShortType: + [invocation setArgument:&value.shortValue atIndex:i]; + break; + case ObjcIntType: + case ObjcUnsignedIntType: + [invocation setArgument:&value.intValue atIndex:i]; + break; + case ObjcLongType: + case ObjcUnsignedLongType: + [invocation setArgument:&value.longValue atIndex:i]; + break; + case ObjcLongLongType: + case ObjcUnsignedLongLongType: + [invocation setArgument:&value.longLongValue atIndex:i]; + break; + case ObjcFloatType: + [invocation setArgument:&value.floatValue atIndex:i]; + break; + case ObjcDoubleType: + [invocation setArgument:&value.doubleValue atIndex:i]; + break; + default: + // Should never get here. Argument types are filtered (and + // the assert above should have fired in the impossible case + // of an invalid type anyway). + fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)objcValueType); + ASSERT(false); + } + } + } + + [invocation invoke]; + + // Get the return value type. + const char* type = [signature methodReturnType]; + ObjcValueType objcValueType = objcValueTypeForType(type); + + // Must have a valid return type. This method signature should have + // been filtered already to ensure that it have an acceptable return + // type. + ASSERT(objcValueType != ObjcInvalidType); + + // Get the return value and convert it to a JavaScript value. Length + // of return value will never exceed the size of largest scalar + // or a pointer. + char buffer[1024]; + ASSERT([signature methodReturnLength] < 1024); + + if (*type != 'v') { + [invocation getReturnValue:buffer]; + result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get()); + } +} @catch(NSException* localException) { +} + moveGlobalExceptionToExecState(exec); + + // Work around problem in some versions of GCC where result gets marked volatile and + // it can't handle copying from a volatile to non-volatile. + return const_cast<JSValue&>(result); +} + +JSValue ObjcInstance::invokeDefaultMethod(ExecState* exec) +{ + JSValue result = jsUndefined(); + + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly. + setGlobalException(nil); + +@try { + if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)]) + return result; + + NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)]; + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)]; + [invocation setTarget:_instance.get()]; + + if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) { + NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object."); + return result; + } + + NSMutableArray* objcArgs = [NSMutableArray array]; + unsigned count = exec->argumentCount(); + for (unsigned i = 0; i < count; i++) { + ObjcValue value = convertValueToObjcValue(exec, exec->argument(i), ObjcObjectType); + [objcArgs addObject:value.objectValue]; + } + [invocation setArgument:&objcArgs atIndex:2]; + + [invocation invoke]; + + // Get the return value type, should always be "@" because of + // check above. + const char* type = [signature methodReturnType]; + ObjcValueType objcValueType = objcValueTypeForType(type); + + // Get the return value and convert it to a JavaScript value. Length + // of return value will never exceed the size of a pointer, so we're + // OK with 32 here. + char buffer[32]; + [invocation getReturnValue:buffer]; + result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get()); +} @catch(NSException* localException) { +} + moveGlobalExceptionToExecState(exec); + + // Work around problem in some versions of GCC where result gets marked volatile and + // it can't handle copying from a volatile to non-volatile. + return const_cast<JSValue&>(result); +} + +bool ObjcInstance::setValueOfUndefinedField(ExecState* exec, const Identifier& property, JSValue aValue) +{ + id targetObject = getObject(); + if (![targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]) + return false; + + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly. + + // This check is not really necessary because NSObject implements + // setValue:forUndefinedKey:, and unfortunately the default implementation + // throws an exception. + if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){ + setGlobalException(nil); + + ObjcValue objcValue = convertValueToObjcValue(exec, aValue, ObjcObjectType); + + @try { + [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:property.ascii().data() encoding:NSASCIIStringEncoding]]; + } @catch(NSException* localException) { + // Do nothing. Class did not override valueForUndefinedKey:. + } + + moveGlobalExceptionToExecState(exec); + } + + return true; +} + +JSValue ObjcInstance::getValueOfUndefinedField(ExecState* exec, const Identifier& property) const +{ + JSValue result = jsUndefined(); + + id targetObject = getObject(); + + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly. + + // This check is not really necessary because NSObject implements + // valueForUndefinedKey:, and unfortunately the default implementation + // throws an exception. + if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){ + setGlobalException(nil); + + @try { + id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:property.ascii().data() encoding:NSASCIIStringEncoding]]; + result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, m_rootObject.get()); + } @catch(NSException* localException) { + // Do nothing. Class did not override valueForUndefinedKey:. + } + + moveGlobalExceptionToExecState(exec); + } + + // Work around problem in some versions of GCC where result gets marked volatile and + // it can't handle copying from a volatile to non-volatile. + return const_cast<JSValue&>(result); +} + +JSValue ObjcInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (hint == PreferString) + return stringValue(exec); + if (hint == PreferNumber) + return numberValue(exec); + if ([_instance.get() isKindOfClass:[NSString class]]) + return stringValue(exec); + if ([_instance.get() isKindOfClass:[NSNumber class]]) + return numberValue(exec); + return valueOf(exec); +} + +JSValue ObjcInstance::stringValue(ExecState* exec) const +{ + return convertNSStringToString(exec, [getObject() description]); +} + +JSValue ObjcInstance::numberValue(ExecState*) const +{ + // FIXME: Implement something sensible + return jsNumber(0); +} + +JSValue ObjcInstance::booleanValue() const +{ + // FIXME: Implement something sensible + return jsBoolean(false); +} + +JSValue ObjcInstance::valueOf(ExecState* exec) const +{ + return stringValue(exec); +} diff --git a/Source/WebCore/bridge/objc/objc_runtime.h b/Source/WebCore/bridge/objc/objc_runtime.h new file mode 100644 index 0000000..60fbdac --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_runtime.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KJS_BINDINGS_OBJC_RUNTIME_H +#define KJS_BINDINGS_OBJC_RUNTIME_H + +#include "Bridge.h" +#include "objc_header.h" +#include <runtime/JSGlobalObject.h> +#include <runtime/JSObjectWithGlobalObject.h> +#include <wtf/RetainPtr.h> + +namespace JSC { +namespace Bindings { + +ClassStructPtr webScriptObjectClass(); +ClassStructPtr webUndefinedClass(); + +class ObjcInstance; + +class ObjcField : public Field { +public: + ObjcField(IvarStructPtr); + ObjcField(CFStringRef name); + + virtual JSValue valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const; + +private: + IvarStructPtr _ivar; + RetainPtr<CFStringRef> _name; +}; + +class ObjcMethod : public Method { +public: + ObjcMethod() : _objcClass(0), _selector(0), _javaScriptName(0) {} + ObjcMethod(ClassStructPtr aClass, SEL _selector); + + virtual int numParameters() const; + + NSMethodSignature *getMethodSignature() const; + + bool isFallbackMethod() const { return _selector == @selector(invokeUndefinedMethodFromWebScript:withArguments:); } + void setJavaScriptName(CFStringRef n) { _javaScriptName = n; } + CFStringRef javaScriptName() const { return _javaScriptName.get(); } + + SEL selector() const { return _selector; } + +private: + ClassStructPtr _objcClass; + SEL _selector; + RetainPtr<CFStringRef> _javaScriptName; +}; + +class ObjcArray : public Array { +public: + ObjcArray(ObjectStructPtr, PassRefPtr<RootObject>); + + virtual void setValueAt(ExecState *exec, unsigned int index, JSValue aValue) const; + virtual JSValue valueAt(ExecState *exec, unsigned int index) const; + virtual unsigned int getLength() const; + + ObjectStructPtr getObjcArray() const { return _array.get(); } + + static JSValue convertObjcArrayToArray(ExecState *exec, ObjectStructPtr anObject); + +private: + RetainPtr<ObjectStructPtr> _array; +}; + +class ObjcFallbackObjectImp : public JSObjectWithGlobalObject { +public: + ObjcFallbackObjectImp(ExecState*, JSGlobalObject*, ObjcInstance*, const Identifier& propertyName); + + static const ClassInfo s_info; + + const Identifier& propertyName() const { return _item; } + + static ObjectPrototype* createPrototype(ExecState*, JSGlobalObject* globalObject) + { + return globalObject->objectPrototype(); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +private: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSObject::StructureFlags; + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + virtual CallType getCallData(CallData&); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual bool toBoolean(ExecState*) const; + + virtual const ClassInfo* classInfo() const { return &s_info; } + + RefPtr<ObjcInstance> _instance; + Identifier _item; +}; + +} // namespace Bindings +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/objc/objc_runtime.mm b/Source/WebCore/bridge/objc/objc_runtime.mm new file mode 100644 index 0000000..3c4ba23 --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_runtime.mm @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "objc_runtime.h" + +#include "JSDOMBinding.h" +#include "ObjCRuntimeObject.h" +#include "WebScriptObject.h" +#include "objc_instance.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include <runtime/Error.h> +#include <runtime/JSGlobalObject.h> +#include <runtime/JSLock.h> +#include <runtime/ObjectPrototype.h> +#include <wtf/RetainPtr.h> + +using namespace WebCore; + +namespace JSC { +namespace Bindings { + +ClassStructPtr webScriptObjectClass() +{ + static ClassStructPtr<WebScriptObject> webScriptObjectClass = NSClassFromString(@"WebScriptObject"); + return webScriptObjectClass; +} + +ClassStructPtr webUndefinedClass() +{ + static ClassStructPtr<WebUndefined> webUndefinedClass = NSClassFromString(@"WebUndefined"); + return webUndefinedClass; +} + +// ---------------------- ObjcMethod ---------------------- + +ObjcMethod::ObjcMethod(ClassStructPtr aClass, SEL selector) + : _objcClass(aClass) + , _selector(selector) +{ +} + +int ObjcMethod::numParameters() const +{ + return [getMethodSignature() numberOfArguments] - 2; +} + +NSMethodSignature* ObjcMethod::getMethodSignature() const +{ + return [_objcClass instanceMethodSignatureForSelector:_selector]; +} + +// ---------------------- ObjcField ---------------------- + +ObjcField::ObjcField(Ivar ivar) + : _ivar(ivar) +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + , _name(AdoptCF, CFStringCreateWithCString(0, ivar_getName(_ivar), kCFStringEncodingASCII)) +#else + , _name(AdoptCF, CFStringCreateWithCString(0, _ivar->ivar_name, kCFStringEncodingASCII)) +#endif +{ +} + +ObjcField::ObjcField(CFStringRef name) + : _ivar(0) + , _name(name) +{ +} + +JSValue ObjcField::valueFromInstance(ExecState* exec, const Instance* instance) const +{ + JSValue result = jsUndefined(); + + id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject(); + + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly. + + @try { + if (id objcValue = [targetObject valueForKey:(NSString *)_name.get()]) + result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, instance->rootObject()); + } @catch(NSException* localException) { + JSLock::lock(SilenceAssertionsOnly); + throwError(exec, [localException reason]); + JSLock::unlock(SilenceAssertionsOnly); + } + + // Work around problem in some versions of GCC where result gets marked volatile and + // it can't handle copying from a volatile to non-volatile. + return const_cast<JSValue&>(result); +} + +static id convertValueToObjcObject(ExecState* exec, JSValue value) +{ + RefPtr<RootObject> rootObject = findRootObject(exec->dynamicGlobalObject()); + if (!rootObject) + return nil; + return [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:rootObject.get() rootObject:rootObject.get()]; +} + +void ObjcField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue aValue) const +{ + id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject(); + id value = convertValueToObjcObject(exec, aValue); + + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly. + + @try { + [targetObject setValue:value forKey:(NSString *)_name.get()]; + } @catch(NSException* localException) { + JSLock::lock(SilenceAssertionsOnly); + throwError(exec, [localException reason]); + JSLock::unlock(SilenceAssertionsOnly); + } +} + +// ---------------------- ObjcArray ---------------------- + +ObjcArray::ObjcArray(ObjectStructPtr a, PassRefPtr<RootObject> rootObject) + : Array(rootObject) + , _array(a) +{ +} + +void ObjcArray::setValueAt(ExecState* exec, unsigned int index, JSValue aValue) const +{ + if (![_array.get() respondsToSelector:@selector(insertObject:atIndex:)]) { + throwError(exec, createTypeError(exec, "Array is not mutable.")); + return; + } + + if (index > [_array.get() count]) { + throwError(exec, createRangeError(exec, "Index exceeds array size.")); + return; + } + + // Always try to convert the value to an ObjC object, so it can be placed in the + // array. + ObjcValue oValue = convertValueToObjcValue (exec, aValue, ObjcObjectType); + + @try { + [_array.get() insertObject:oValue.objectValue atIndex:index]; + } @catch(NSException* localException) { + throwError(exec, createError(exec, "Objective-C exception.")); + } +} + +JSValue ObjcArray::valueAt(ExecState* exec, unsigned int index) const +{ + if (index > [_array.get() count]) + return throwError(exec, createRangeError(exec, "Index exceeds array size.")); + @try { + id obj = [_array.get() objectAtIndex:index]; + if (obj) + return convertObjcValueToValue (exec, &obj, ObjcObjectType, m_rootObject.get()); + } @catch(NSException* localException) { + return throwError(exec, createError(exec, "Objective-C exception.")); + } + return jsUndefined(); +} + +unsigned int ObjcArray::getLength() const +{ + return [_array.get() count]; +} + +const ClassInfo ObjcFallbackObjectImp::s_info = { "ObjcFallbackObject", 0, 0, 0 }; + +ObjcFallbackObjectImp::ObjcFallbackObjectImp(ExecState* exec, JSGlobalObject* globalObject, ObjcInstance* i, const Identifier& propertyName) + // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object + : JSObjectWithGlobalObject(globalObject, deprecatedGetDOMStructure<ObjcFallbackObjectImp>(exec)) + , _instance(i) + , _item(propertyName) +{ +} + +bool ObjcFallbackObjectImp::getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot& slot) +{ + // keep the prototype from getting called instead of just returning false + slot.setUndefined(); + return true; +} + +bool ObjcFallbackObjectImp::getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor& descriptor) +{ + // keep the prototype from getting called instead of just returning false + descriptor.setUndefined(); + return true; +} + +void ObjcFallbackObjectImp::put(ExecState*, const Identifier&, JSValue, PutPropertySlot&) +{ +} + +static EncodedJSValue JSC_HOST_CALL callObjCFallbackObject(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&ObjCRuntimeObject::s_info)) + return throwVMTypeError(exec); + + JSValue result = jsUndefined(); + + ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(asObject(thisValue)); + ObjcInstance* objcInstance = runtimeObject->getInternalObjCInstance(); + + if (!objcInstance) + return JSValue::encode(RuntimeObject::throwInvalidAccessError(exec)); + + objcInstance->begin(); + + id targetObject = objcInstance->getObject(); + + if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){ + ObjcClass* objcClass = static_cast<ObjcClass*>(objcInstance->getClass()); + OwnPtr<ObjcMethod> fallbackMethod(new ObjcMethod(objcClass->isa(), @selector(invokeUndefinedMethodFromWebScript:withArguments:))); + const Identifier& nameIdentifier = static_cast<ObjcFallbackObjectImp*>(exec->callee())->propertyName(); + RetainPtr<CFStringRef> name(AdoptCF, CFStringCreateWithCharacters(0, nameIdentifier.characters(), nameIdentifier.length())); + fallbackMethod->setJavaScriptName(name.get()); + result = objcInstance->invokeObjcMethod(exec, fallbackMethod.get()); + } + + objcInstance->end(); + + return JSValue::encode(result); +} + +CallType ObjcFallbackObjectImp::getCallData(CallData& callData) +{ + id targetObject = _instance->getObject(); + if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) + return CallTypeNone; + callData.native.function = callObjCFallbackObject; + return CallTypeHost; +} + +bool ObjcFallbackObjectImp::deleteProperty(ExecState*, const Identifier&) +{ + return false; +} + +JSValue ObjcFallbackObjectImp::defaultValue(ExecState* exec, PreferredPrimitiveType) const +{ + return _instance->getValueOfUndefinedField(exec, _item); +} + +bool ObjcFallbackObjectImp::toBoolean(ExecState *) const +{ + id targetObject = _instance->getObject(); + + if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) + return true; + + return false; +} + +} +} diff --git a/Source/WebCore/bridge/objc/objc_utility.h b/Source/WebCore/bridge/objc/objc_utility.h new file mode 100644 index 0000000..4810752 --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_utility.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KJS_BINDINGS_OBJC_UTILITY_H +#define KJS_BINDINGS_OBJC_UTILITY_H + +#include <CoreFoundation/CoreFoundation.h> + +#include "objc_header.h" +#include <runtime/Error.h> +#include <runtime/JSObject.h> + +#ifdef __OBJC__ +@class NSString; +#else +class NSString; +#endif + +namespace JSC { +namespace Bindings { + +typedef union { + ObjectStructPtr objectValue; + bool booleanValue; + char charValue; + short shortValue; + int intValue; + long longValue; + long long longLongValue; + float floatValue; + double doubleValue; +} ObjcValue; + +typedef enum { + ObjcVoidType, + ObjcObjectType, + ObjcCharType, + ObjcUnsignedCharType, + ObjcShortType, + ObjcUnsignedShortType, + ObjcIntType, + ObjcUnsignedIntType, + ObjcLongType, + ObjcUnsignedLongType, + ObjcLongLongType, + ObjcUnsignedLongLongType, + ObjcFloatType, + ObjcDoubleType, + ObjcInvalidType +} ObjcValueType; + +class RootObject; + +ObjcValue convertValueToObjcValue(ExecState*, JSValue, ObjcValueType); +JSValue convertNSStringToString(ExecState* exec, NSString *nsstring); +JSValue convertObjcValueToValue(ExecState*, void* buffer, ObjcValueType, RootObject*); +ObjcValueType objcValueTypeForType(const char *type); + +bool convertJSMethodNameToObjc(const char *JSName, char *buffer, size_t bufferSize); + +JSObject *throwError(ExecState *, NSString *message); + +} // namespace Bindings +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/objc/objc_utility.mm b/Source/WebCore/bridge/objc/objc_utility.mm new file mode 100644 index 0000000..dfba852 --- /dev/null +++ b/Source/WebCore/bridge/objc/objc_utility.mm @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "objc_utility.h" + +#include "objc_instance.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include "WebScriptObject.h" +#include <runtime/JSGlobalObject.h> +#include <runtime/JSLock.h> +#include <wtf/Assertions.h> + +#if !defined(_C_LNG_LNG) +#define _C_LNG_LNG 'q' +#endif + +#if !defined(_C_ULNG_LNG) +#define _C_ULNG_LNG 'Q' +#endif + +#if !defined(_C_CONST) +#define _C_CONST 'r' +#endif + +#if !defined(_C_BYCOPY) +#define _C_BYCOPY 'O' +#endif + +#if !defined(_C_BYREF) +#define _C_BYREF 'R' +#endif + +#if !defined(_C_ONEWAY) +#define _C_ONEWAY 'V' +#endif + +#if !defined(_C_GCINVISIBLE) +#define _C_GCINVISIBLE '!' +#endif + +namespace JSC { +namespace Bindings { + +/* + By default, a JavaScript method name is produced by concatenating the + components of an ObjectiveC method name, replacing ':' with '_', and + escaping '_' and '$' with a leading '$', such that '_' becomes "$_" and + '$' becomes "$$". For example: + + ObjectiveC name Default JavaScript name + moveTo:: moveTo__ + moveTo_ moveTo$_ + moveTo$_ moveTo$$$_ + + This function performs the inverse of that operation. + + @result Fills 'buffer' with the ObjectiveC method name that corresponds to 'JSName'. + Returns true for success, false for failure. (Failure occurs when 'buffer' + is not big enough to hold the result.) +*/ +bool convertJSMethodNameToObjc(const char *JSName, char *buffer, size_t bufferSize) +{ + ASSERT(JSName && buffer); + + const char *sp = JSName; // source pointer + char *dp = buffer; // destination pointer + + char *end = buffer + bufferSize; + while (dp < end) { + if (*sp == '$') { + ++sp; + *dp = *sp; + } else if (*sp == '_') + *dp = ':'; + else + *dp = *sp; + + // If a future coder puts funny ++ operators above, we might write off the end + // of the buffer in the middle of this loop. Let's make sure to check for that. + ASSERT(dp < end); + + if (*sp == 0) { // We finished converting JSName + ASSERT(strlen(JSName) < bufferSize); + return true; + } + + ++sp; + ++dp; + } + + return false; // We ran out of buffer before converting JSName +} + +/* + + JavaScript to ObjC + Number coerced to char, short, int, long, float, double, or NSNumber, as appropriate + String NSString + wrapper id + Object WebScriptObject + null NSNull + [], other exception + +*/ +ObjcValue convertValueToObjcValue(ExecState* exec, JSValue value, ObjcValueType type) +{ + ObjcValue result; + double d = 0; + + if (value.isNumber() || value.isString() || value.isBoolean()) + d = value.toNumber(exec); + + switch (type) { + case ObjcObjectType: { + JSLock lock(SilenceAssertionsOnly); + + JSGlobalObject *originGlobalObject = exec->dynamicGlobalObject(); + RootObject* originRootObject = findRootObject(originGlobalObject); + + JSGlobalObject* globalObject = 0; + if (value.isObject() && asObject(value)->isGlobalObject()) + globalObject = static_cast<JSGlobalObject*>(asObject(value)); + + if (!globalObject) + globalObject = originGlobalObject; + + RootObject* rootObject = findRootObject(globalObject); + result.objectValue = rootObject + ? [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:originRootObject rootObject:rootObject] + : nil; + } + break; + + case ObjcCharType: + case ObjcUnsignedCharType: + result.charValue = (char)d; + break; + case ObjcShortType: + case ObjcUnsignedShortType: + result.shortValue = (short)d; + break; + case ObjcIntType: + case ObjcUnsignedIntType: + result.intValue = (int)d; + break; + case ObjcLongType: + case ObjcUnsignedLongType: + result.longValue = (long)d; + break; + case ObjcLongLongType: + case ObjcUnsignedLongLongType: + result.longLongValue = (long long)d; + break; + case ObjcFloatType: + result.floatValue = (float)d; + break; + case ObjcDoubleType: + result.doubleValue = (double)d; + break; + case ObjcVoidType: + bzero(&result, sizeof(ObjcValue)); + break; + + case ObjcInvalidType: + default: + // FIXME: throw an exception? + break; + } + + return result; +} + +JSValue convertNSStringToString(ExecState* exec, NSString *nsstring) +{ + JSLock lock(SilenceAssertionsOnly); + + unichar *chars; + unsigned int length = [nsstring length]; + chars = (unichar *)malloc(sizeof(unichar)*length); + [nsstring getCharacters:chars]; + UString u((const UChar*)chars, length); + JSValue aValue = jsString(exec, u); + free((void *)chars); + return aValue; +} + +/* + ObjC to JavaScript + ---- ---------- + char number + short number + int number + long number + float number + double number + NSNumber boolean or number + NSString string + NSArray array + NSNull null + WebScriptObject underlying JavaScript object + WebUndefined undefined + id object wrapper + other should not happen +*/ +JSValue convertObjcValueToValue(ExecState* exec, void* buffer, ObjcValueType type, RootObject* rootObject) +{ + JSLock lock(SilenceAssertionsOnly); + + switch (type) { + case ObjcObjectType: { + id obj = *(id*)buffer; + if ([obj isKindOfClass:[NSString class]]) + return convertNSStringToString(exec, (NSString *)obj); + if ([obj isKindOfClass:webUndefinedClass()]) + return jsUndefined(); + if ((CFBooleanRef)obj == kCFBooleanTrue) + return jsBoolean(true); + if ((CFBooleanRef)obj == kCFBooleanFalse) + return jsBoolean(false); + if ([obj isKindOfClass:[NSNumber class]]) + return jsNumber([obj doubleValue]); + if ([obj isKindOfClass:[NSArray class]]) + return new (exec) RuntimeArray(exec, new ObjcArray(obj, rootObject)); + if ([obj isKindOfClass:webScriptObjectClass()]) { + JSObject* imp = [obj _imp]; + return imp ? imp : jsUndefined(); + } + if ([obj isKindOfClass:[NSNull class]]) + return jsNull(); + if (obj == 0) + return jsUndefined(); + return ObjcInstance::create(obj, rootObject)->createRuntimeObject(exec); + } + case ObjcCharType: + return jsNumber(*(char*)buffer); + case ObjcUnsignedCharType: + return jsNumber(*(unsigned char*)buffer); + case ObjcShortType: + return jsNumber(*(short*)buffer); + case ObjcUnsignedShortType: + return jsNumber(*(unsigned short*)buffer); + case ObjcIntType: + return jsNumber(*(int*)buffer); + case ObjcUnsignedIntType: + return jsNumber(*(unsigned int*)buffer); + case ObjcLongType: + return jsNumber(*(long*)buffer); + case ObjcUnsignedLongType: + return jsNumber(*(unsigned long*)buffer); + case ObjcLongLongType: + return jsNumber(*(long long*)buffer); + case ObjcUnsignedLongLongType: + return jsNumber(*(unsigned long long*)buffer); + case ObjcFloatType: + return jsNumber(*(float*)buffer); + case ObjcDoubleType: + return jsNumber(*(double*)buffer); + default: + // Should never get here. Argument types are filtered. + fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)type); + ASSERT(false); + } + + return jsUndefined(); +} + +ObjcValueType objcValueTypeForType(const char *type) +{ + int typeLength = strlen(type); + ObjcValueType objcValueType = ObjcInvalidType; + + for (int i = 0; i < typeLength; ++i) { + char typeChar = type[i]; + switch (typeChar) { + case _C_CONST: + case _C_BYCOPY: + case _C_BYREF: + case _C_ONEWAY: + case _C_GCINVISIBLE: + // skip these type modifiers + break; + case _C_ID: + objcValueType = ObjcObjectType; + break; + case _C_CHR: + objcValueType = ObjcCharType; + break; + case _C_UCHR: + objcValueType = ObjcUnsignedCharType; + break; + case _C_SHT: + objcValueType = ObjcShortType; + break; + case _C_USHT: + objcValueType = ObjcUnsignedShortType; + break; + case _C_INT: + objcValueType = ObjcIntType; + break; + case _C_UINT: + objcValueType = ObjcUnsignedIntType; + break; + case _C_LNG: + objcValueType = ObjcLongType; + break; + case _C_ULNG: + objcValueType = ObjcUnsignedLongType; + break; + case _C_LNG_LNG: + objcValueType = ObjcLongLongType; + break; + case _C_ULNG_LNG: + objcValueType = ObjcUnsignedLongLongType; + break; + case _C_FLT: + objcValueType = ObjcFloatType; + break; + case _C_DBL: + objcValueType = ObjcDoubleType; + break; + case _C_VOID: + objcValueType = ObjcVoidType; + break; + default: + // Unhandled type. We don't handle C structs, unions, etc. + // FIXME: throw an exception? + ASSERT(false); + } + + if (objcValueType != ObjcInvalidType) + break; + } + + return objcValueType; +} + +JSObject *throwError(ExecState *exec, NSString *message) +{ + ASSERT(message); + size_t length = [message length]; + unichar *buffer = new unichar[length]; + [message getCharacters:buffer]; + JSObject *error = JSC::throwError(exec, JSC::createError(exec, UString(buffer, length))); + delete [] buffer; + return error; +} + +} +} diff --git a/Source/WebCore/bridge/qt/qt_class.cpp b/Source/WebCore/bridge/qt/qt_class.cpp new file mode 100644 index 0000000..4c29c69 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_class.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "qt_class.h" + +#include "Identifier.h" +#include "qt_instance.h" +#include "qt_runtime.h" + +#include <qdebug.h> +#include <qmetaobject.h> + +namespace JSC { +namespace Bindings { + +QtClass::QtClass(const QMetaObject* mo) + : m_metaObject(mo) +{ +} + +QtClass::~QtClass() +{ +} + +typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject; +static ClassesByMetaObject* classesByMetaObject = 0; + +QtClass* QtClass::classForObject(QObject* o) +{ + if (!classesByMetaObject) + classesByMetaObject = new ClassesByMetaObject; + + const QMetaObject* mo = o->metaObject(); + QtClass* aClass = classesByMetaObject->get(mo); + if (!aClass) { + aClass = new QtClass(mo); + classesByMetaObject->set(mo, aClass); + } + + return aClass; +} + +const char* QtClass::name() const +{ + return m_metaObject->className(); +} + +// We use this to get at signals (so we can return a proper function object, +// and not get wrapped in RuntimeMethod). Also, use this for methods, +// so we can cache the object and return the same object for the same +// identifier. +JSValue QtClass::fallbackObject(ExecState* exec, Instance* inst, const Identifier& identifier) +{ + QtInstance* qtinst = static_cast<QtInstance*>(inst); + + const UString& ustring = identifier.ustring(); + const QByteArray name = QString(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length()).toAscii(); + + // First see if we have a cache hit + JSObject* val = qtinst->m_methods.value(name); + if (val) + return val; + + // Nope, create an entry + const QByteArray normal = QMetaObject::normalizedSignature(name.constData()); + + // See if there is an exact match + int index = -1; + if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) { + QMetaMethod m = m_metaObject->method(index); + if (m.access() != QMetaMethod::Private) { + QtRuntimeMetaMethod* val = new (exec) QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false); + qtinst->m_methods.insert(name, val); + return val; + } + } + + // Nope.. try a basename match + const int count = m_metaObject->methodCount(); + for (index = count - 1; index >= 0; --index) { + const QMetaMethod m = m_metaObject->method(index); + if (m.access() == QMetaMethod::Private) + continue; + + int iter = 0; + const char* signature = m.signature(); + while (signature[iter] && signature[iter] != '(') + ++iter; + + if (normal == QByteArray::fromRawData(signature, iter)) { + QtRuntimeMetaMethod* val = new (exec) QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false); + qtinst->m_methods.insert(name, val); + return val; + } + } + + return jsUndefined(); +} + +// This functionality is handled by the fallback case above... +MethodList QtClass::methodsNamed(const Identifier&, Instance*) const +{ + return MethodList(); +} + +// ### we may end up with a different search order than QtScript by not +// folding this code into the fallbackMethod above, but Fields propagate out +// of the binding code +Field* QtClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + // Check static properties first + QtInstance* qtinst = static_cast<QtInstance*>(instance); + + QObject* obj = qtinst->getObject(); + const UString& ustring = identifier.ustring(); + const QString name(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length()); + const QByteArray ascii = name.toAscii(); + + // First check for a cached field + QtField* f = qtinst->m_fields.value(name); + + if (obj) { + if (f) { + // We only cache real metaproperties, but we do store the + // other types so we can delete them later + if (f->fieldType() == QtField::MetaProperty) + return f; +#ifndef QT_NO_PROPERTIES + if (f->fieldType() == QtField::DynamicProperty) { + if (obj->dynamicPropertyNames().indexOf(ascii) >= 0) + return f; + // Dynamic property that disappeared + qtinst->m_fields.remove(name); + delete f; + } +#endif + else { + const QList<QObject*>& children = obj->children(); + const int count = children.size(); + for (int index = 0; index < count; ++index) { + QObject* child = children.at(index); + if (child->objectName() == name) + return f; + } + + // Didn't find it, delete it from the cache + qtinst->m_fields.remove(name); + delete f; + } + } + + int index = m_metaObject->indexOfProperty(ascii); + if (index >= 0) { + const QMetaProperty prop = m_metaObject->property(index); + + if (prop.isScriptable(obj)) { + f = new QtField(prop); + qtinst->m_fields.insert(name, f); + return f; + } + } + +#ifndef QT_NO_PROPERTIES + // Dynamic properties + index = obj->dynamicPropertyNames().indexOf(ascii); + if (index >= 0) { + f = new QtField(ascii); + qtinst->m_fields.insert(name, f); + return f; + } +#endif + + // Child objects + + const QList<QObject*>& children = obj->children(); + const int count = children.count(); + for (index = 0; index < count; ++index) { + QObject* child = children.at(index); + if (child->objectName() == name) { + f = new QtField(child); + qtinst->m_fields.insert(name, f); + return f; + } + } + + // Nothing named this + return 0; + } + // For compatibility with qtscript, cached methods don't cause + // errors until they are accessed, so don't blindly create an error + // here. + if (qtinst->m_methods.contains(ascii)) + return 0; + +#ifndef QT_NO_PROPERTIES + // deleted qobject, but can't throw an error from here (no exec) + // create a fake QtField that will throw upon access + if (!f) { + f = new QtField(ascii); + qtinst->m_fields.insert(name, f); + } +#endif + return f; +} + +} +} + diff --git a/Source/WebCore/bridge/qt/qt_class.h b/Source/WebCore/bridge/qt/qt_class.h new file mode 100644 index 0000000..9d55cc5 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_class.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef qt_class_h +#define qt_class_h + +#include "Bridge.h" +#include "qglobal.h" + +QT_BEGIN_NAMESPACE +class QObject; +struct QMetaObject; +QT_END_NAMESPACE + +namespace JSC { +namespace Bindings { + + +class QtClass : public Class { +protected: + QtClass(const QMetaObject*); + +public: + static QtClass* classForObject(QObject*); + virtual ~QtClass(); + + virtual const char* name() const; + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; + + virtual JSValue fallbackObject(ExecState*, Instance*, const Identifier&); + +private: + QtClass(const QtClass&); // prohibit copying + QtClass& operator=(const QtClass&); // prohibit assignment + + const QMetaObject* m_metaObject; +}; + +} // namespace Bindings +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/qt/qt_instance.cpp b/Source/WebCore/bridge/qt/qt_instance.cpp new file mode 100644 index 0000000..78263e9 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_instance.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "qt_instance.h" + +#include "Error.h" +#include "JSDOMBinding.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "ObjectPrototype.h" +#include "PropertyNameArray.h" +#include "qt_class.h" +#include "qt_runtime.h" +#include "runtime_object.h" + +#include <qdebug.h> +#include <qhash.h> +#include <qmetaobject.h> +#include <qmetatype.h> + +namespace JSC { +namespace Bindings { + +// Cache QtInstances +typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap; +static QObjectInstanceMap cachedInstances; + +// Derived RuntimeObject +class QtRuntimeObject : public RuntimeObject { +public: + QtRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>); + + static const ClassInfo s_info; + + virtual void markChildren(MarkStack& markStack) + { + RuntimeObject::markChildren(markStack); + QtInstance* instance = static_cast<QtInstance*>(getInternalInstance()); + if (instance) + instance->markAggregate(markStack); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +protected: + static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren; + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } +}; + +const ClassInfo QtRuntimeObject::s_info = { "QtRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +QtRuntimeObject::QtRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance) + : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtRuntimeObject>(exec), instance) +{ +} + +// QtInstance +QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership) + : Instance(rootObject) + , m_class(0) + , m_object(o) + , m_hashkey(o) + , m_defaultMethod(0) + , m_ownership(ownership) +{ +} + +QtInstance::~QtInstance() +{ + JSLock lock(SilenceAssertionsOnly); + + cachedInstances.remove(m_hashkey); + + // clean up (unprotect from gc) the JSValues we've created + m_methods.clear(); + + qDeleteAll(m_fields); + m_fields.clear(); + + if (m_object) { + switch (m_ownership) { + case QScriptEngine::QtOwnership: + break; + case QScriptEngine::AutoOwnership: + if (m_object->parent()) + break; + // fall through! + case QScriptEngine::ScriptOwnership: + delete m_object; + break; + } + } +} + +PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership) +{ + JSLock lock(SilenceAssertionsOnly); + + foreach (QtInstance* instance, cachedInstances.values(o)) + if (instance->rootObject() == rootObject) { + // The garbage collector removes instances, but it may happen that the wrapped + // QObject dies before the gc kicks in. To handle that case we have to do an additional + // check if to see if the instance's wrapped object is still alive. If it isn't, then + // we have to create a new wrapper. + if (!instance->getObject()) + cachedInstances.remove(instance->hashKey()); + else + return instance; + } + + RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership); + cachedInstances.insert(o, ret.get()); + + return ret.release(); +} + +bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return object->JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + object->JSObject::put(exec, propertyName, value, slot); +} + +void QtInstance::removeCachedMethod(JSObject* method) +{ + if (m_defaultMethod == method) + m_defaultMethod = 0; + + for (QHash<QByteArray, JSObject*>::Iterator it = m_methods.begin(), + end = m_methods.end(); it != end; ++it) + if (it.value() == method) { + m_methods.erase(it); + return; + } +} + +QtInstance* QtInstance::getInstance(JSObject* object) +{ + if (!object) + return 0; + if (!object->inherits(&QtRuntimeObject::s_info)) + return 0; + return static_cast<QtInstance*>(static_cast<RuntimeObject*>(object)->getInternalInstance()); +} + +Class* QtInstance::getClass() const +{ + if (!m_class) { + if (!m_object) + return 0; + m_class = QtClass::classForObject(m_object); + } + return m_class; +} + +RuntimeObject* QtInstance::newRuntimeObject(ExecState* exec) +{ + JSLock lock(SilenceAssertionsOnly); + m_methods.clear(); + return new (exec) QtRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +void QtInstance::markAggregate(MarkStack& markStack) +{ + if (m_defaultMethod) + markStack.append(m_defaultMethod); + foreach (JSObject* val, m_methods.values()) { + if (val) + markStack.append(val); + } +} + +void QtInstance::begin() +{ + // Do nothing. +} + +void QtInstance::end() +{ + // Do nothing. +} + +void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array) +{ + // This is the enumerable properties, so put: + // properties + // dynamic properties + // slots + QObject* obj = getObject(); + if (obj) { + const QMetaObject* meta = obj->metaObject(); + + int i; + for (i = 0; i < meta->propertyCount(); i++) { + QMetaProperty prop = meta->property(i); + if (prop.isScriptable()) + array.add(Identifier(exec, prop.name())); + } + +#ifndef QT_NO_PROPERTIES + QList<QByteArray> dynProps = obj->dynamicPropertyNames(); + foreach (const QByteArray& ba, dynProps) + array.add(Identifier(exec, ba.constData())); +#endif + + const int methodCount = meta->methodCount(); + for (i = 0; i < methodCount; i++) { + QMetaMethod method = meta->method(i); + if (method.access() != QMetaMethod::Private) + array.add(Identifier(exec, method.signature())); + } + } +} + +JSValue QtInstance::getMethod(ExecState* exec, const Identifier& propertyName) +{ + if (!getClass()) + return jsNull(); + MethodList methodList = m_class->methodsNamed(propertyName, this); + return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue QtInstance::invokeMethod(ExecState*, RuntimeMethod*) +{ + // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction + return jsUndefined(); +} + +JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (hint == PreferString) + return stringValue(exec); + if (hint == PreferNumber) + return numberValue(exec); + return valueOf(exec); +} + +JSValue QtInstance::stringValue(ExecState* exec) const +{ + QObject* obj = getObject(); + if (!obj) + return jsNull(); + + // Hmm.. see if there is a toString defined + QByteArray buf; + bool useDefault = true; + getClass(); + if (m_class) { + // Cheat and don't use the full name resolution + int index = obj->metaObject()->indexOfMethod("toString()"); + if (index >= 0) { + QMetaMethod m = obj->metaObject()->method(index); + // Check to see how much we can call it + if (m.access() != QMetaMethod::Private + && m.methodType() != QMetaMethod::Signal + && m.parameterTypes().isEmpty()) { + const char* retsig = m.typeName(); + if (retsig && *retsig) { + QVariant ret(QMetaType::type(retsig), (void*)0); + void * qargs[1]; + qargs[0] = ret.data(); + + if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, qargs) < 0) { + if (ret.isValid() && ret.canConvert(QVariant::String)) { + buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii? + useDefault = false; + } + } + } + } + } + } + + if (useDefault) { + const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject; + QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed"); + QString str = QString::fromUtf8("%0(name = \"%1\")") + .arg(QLatin1String(meta->className())).arg(name); + + buf = str.toLatin1(); + } + return jsString(exec, buf.constData()); +} + +JSValue QtInstance::numberValue(ExecState*) const +{ + return jsNumber(0); +} + +JSValue QtInstance::booleanValue() const +{ + // ECMA 9.2 + return jsBoolean(getObject()); +} + +JSValue QtInstance::valueOf(ExecState* exec) const +{ + return stringValue(exec); +} + +// In qt_runtime.cpp +JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant); +QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance); + +QByteArray QtField::name() const +{ + if (m_type == MetaProperty) + return m_property.name(); + if (m_type == ChildObject && m_childObject) + return m_childObject->objectName().toLatin1(); +#ifndef QT_NO_PROPERTIES + if (m_type == DynamicProperty) + return m_dynamicProperty; +#endif + return QByteArray(); // deleted child object +} + +JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const +{ + const QtInstance* instance = static_cast<const QtInstance*>(inst); + QObject* obj = instance->getObject(); + + if (obj) { + QVariant val; + if (m_type == MetaProperty) { + if (m_property.isReadable()) + val = m_property.read(obj); + else + return jsUndefined(); + } else if (m_type == ChildObject) + val = QVariant::fromValue((QObject*) m_childObject); +#ifndef QT_NO_PROPERTIES + else if (m_type == DynamicProperty) + val = obj->property(m_dynamicProperty); +#endif + return convertQVariantToValue(exec, inst->rootObject(), val); + } + QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name())); + return throwError(exec, createError(exec, msg.toLatin1().constData())); +} + +void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const +{ + if (m_type == ChildObject) // QtScript doesn't allow setting to a named child + return; + + const QtInstance* instance = static_cast<const QtInstance*>(inst); + QObject* obj = instance->getObject(); + if (obj) { + QMetaType::Type argtype = QMetaType::Void; + if (m_type == MetaProperty) + argtype = (QMetaType::Type) QMetaType::type(m_property.typeName()); + + // dynamic properties just get any QVariant + QVariant val = convertValueToQVariant(exec, aValue, argtype, 0); + if (m_type == MetaProperty) { + if (m_property.isWritable()) + m_property.write(obj, val); + } +#ifndef QT_NO_PROPERTIES + else if (m_type == DynamicProperty) + obj->setProperty(m_dynamicProperty.constData(), val); +#endif + } else { + QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name())); + throwError(exec, createError(exec, msg.toLatin1().constData())); + } +} + + +} +} diff --git a/Source/WebCore/bridge/qt/qt_instance.h b/Source/WebCore/bridge/qt/qt_instance.h new file mode 100644 index 0000000..dd24a92 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_instance.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef qt_instance_h +#define qt_instance_h + +#include "Bridge.h" +#include "runtime_root.h" +#include <QtScript/qscriptengine.h> +#include <qhash.h> +#include <qpointer.h> +#include <qset.h> + +namespace JSC { + +namespace Bindings { + +class QtClass; +class QtField; +class QtRuntimeMetaMethod; + +class QtInstance : public Instance { +public: + ~QtInstance(); + + virtual Class* getClass() const; + virtual RuntimeObject* newRuntimeObject(ExecState*); + + virtual void begin(); + virtual void end(); + + virtual JSValue valueOf(ExecState*) const; + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + + void markAggregate(MarkStack&); + + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName); + virtual JSValue invokeMethod(ExecState*, RuntimeMethod*); + + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + JSValue stringValue(ExecState* exec) const; + JSValue numberValue(ExecState* exec) const; + JSValue booleanValue() const; + + QObject* getObject() const { return m_object; } + QObject* hashKey() const { return m_hashkey; } + + static PassRefPtr<QtInstance> getQtInstance(QObject*, PassRefPtr<RootObject>, QScriptEngine::ValueOwnership ownership); + + virtual bool getOwnPropertySlot(JSObject*, ExecState*, const Identifier&, PropertySlot&); + virtual void put(JSObject*, ExecState*, const Identifier&, JSValue, PutPropertySlot&); + + void removeCachedMethod(JSObject*); + + static QtInstance* getInstance(JSObject*); + +private: + static PassRefPtr<QtInstance> create(QObject *instance, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership) + { + return adoptRef(new QtInstance(instance, rootObject, ownership)); + } + + friend class QtClass; + friend class QtField; + QtInstance(QObject*, PassRefPtr<RootObject>, QScriptEngine::ValueOwnership ownership); // Factory produced only.. + mutable QtClass* m_class; + QPointer<QObject> m_object; + QObject* m_hashkey; + mutable QHash<QByteArray, JSObject*> m_methods; + mutable QHash<QString, QtField*> m_fields; + mutable QtRuntimeMetaMethod* m_defaultMethod; + QScriptEngine::ValueOwnership m_ownership; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/qt/qt_pixmapruntime.cpp b/Source/WebCore/bridge/qt/qt_pixmapruntime.cpp new file mode 100644 index 0000000..1ef20c3 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_pixmapruntime.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include "config.h" +#include "qt_pixmapruntime.h" + +#include "CachedImage.h" +#include "HTMLImageElement.h" +#include "JSGlobalObject.h" +#include "JSHTMLImageElement.h" +#include "JSLock.h" +#include "ObjectPrototype.h" +#include "StillImageQt.h" +#include <QBuffer> +#include <QByteArray> +#include <QImage> +#include <QPixmap> +#include <QVariant> +#include <runtime_method.h> +#include <runtime_object.h> +#include <runtime_root.h> + +using namespace WebCore; +namespace JSC { + +namespace Bindings { + +class QtPixmapClass : public Class { +public: + QtPixmapClass(); + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; +}; + + +class QtPixmapWidthField : public Field { +public: + static const char* name() { return "width"; } + virtual JSValue valueFromInstance(ExecState*, const Instance* instance) const + { + return jsNumber(static_cast<const QtPixmapInstance*>(instance)->width()); + } + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const {} +}; + +class QtPixmapHeightField : public Field { +public: + static const char* name() { return "height"; } + virtual JSValue valueFromInstance(ExecState*, const Instance* instance) const + { + return jsNumber(static_cast<const QtPixmapInstance*>(instance)->height()); + } + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const {} +}; + +class QtPixmapRuntimeMethod : public Method { +public: + virtual int numParameters() const + { + return 0; + } + virtual JSValue invoke(ExecState* exec, QtPixmapInstance*) = 0; + +}; + +// this function receives an HTML image element as a parameter, makes it display the pixmap/image from Qt +class QtPixmapAssignToElementMethod : public QtPixmapRuntimeMethod { +public: + static const char* name() { return "assignToHTMLImageElement"; } + JSValue invoke(ExecState* exec, QtPixmapInstance* instance) + { + if (!exec->argumentCount()) + return jsUndefined(); + + JSObject* objectArg = exec->argument(0).toObject(exec); + if (!objectArg) + return jsUndefined(); + + if (!objectArg->inherits(&JSHTMLImageElement::s_info)) + return jsUndefined(); + + // we now know that we have a valid <img> element as the argument, we can attach the pixmap to it. + PassRefPtr<StillImage> stillImage = WebCore::StillImage::create(instance->toPixmap()); + HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(static_cast<JSHTMLImageElement*>(objectArg)->impl()); + imageElement->setCachedImage(new CachedImage(stillImage.get())); + JSDOMGlobalObject* global = static_cast<JSDOMGlobalObject*>(instance->rootObject()->globalObject()); + toJS(exec, global, imageElement->document()); + return jsUndefined(); + } + + virtual int numParameters() const + { + return 1; + } +}; + +// this function encodes the image to a dataUrl, to be used in background etc. Note: very slow. +class QtPixmapToDataUrlMethod : public QtPixmapRuntimeMethod { +public: + static const char* name() { return "toDataUrl"; } + JSValue invoke(ExecState* exec, QtPixmapInstance* instance) + { + QByteArray byteArray; + QBuffer buffer(&byteArray); + instance->toImage().save(&buffer, "PNG"); + const QString encodedString = QString("data:image/png;base64,") + byteArray.toBase64(); + const UString ustring((UChar*)encodedString.utf16(), encodedString.length()); + return jsString(exec, ustring); + } +}; + +class QtPixmapToStringMethod : public QtPixmapRuntimeMethod { + public: + static const char* name() { return "toString"; } + JSValue invoke(ExecState* exec, QtPixmapInstance* instance) + { + return instance->valueOf(exec); + } +}; + +struct QtPixmapMetaData { + QtPixmapToDataUrlMethod toDataUrlMethod; + QtPixmapAssignToElementMethod assignToElementMethod; + QtPixmapToStringMethod toStringMethod; + QtPixmapHeightField heightField; + QtPixmapWidthField widthField; + QtPixmapClass cls; +} qt_pixmap_metaData; + +// Derived RuntimeObject +class QtPixmapRuntimeObject : public RuntimeObject { +public: + QtPixmapRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>); + + static const ClassInfo s_info; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +protected: + static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren; + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } +}; + +QtPixmapRuntimeObject::QtPixmapRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance) + : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtPixmapRuntimeObject>(exec), instance) +{ +} + +const ClassInfo QtPixmapRuntimeObject::s_info = { "QtPixmapRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +QtPixmapClass::QtPixmapClass() +{ +} + + +Class* QtPixmapInstance::getClass() const +{ + return &qt_pixmap_metaData.cls; +} + +JSValue QtPixmapInstance::getMethod(ExecState* exec, const Identifier& propertyName) +{ + MethodList methodList = getClass()->methodsNamed(propertyName, this); + return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue QtPixmapInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod) +{ + const MethodList& methods = *runtimeMethod->methods(); + + if (methods.size() == 1) { + QtPixmapRuntimeMethod* method = static_cast<QtPixmapRuntimeMethod*>(methods[0]); + return method->invoke(exec, this); + } + return jsUndefined(); +} + +MethodList QtPixmapClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList methods; + if (identifier == QtPixmapToDataUrlMethod::name()) + methods.append(&qt_pixmap_metaData.toDataUrlMethod); + else if (identifier == QtPixmapAssignToElementMethod::name()) + methods.append(&qt_pixmap_metaData.assignToElementMethod); + else if (identifier == QtPixmapToStringMethod::name()) + methods.append(&qt_pixmap_metaData.toStringMethod); + return methods; +} + +Field* QtPixmapClass::fieldNamed(const Identifier& identifier, Instance*) const +{ + if (identifier == QtPixmapWidthField::name()) + return &qt_pixmap_metaData.widthField; + if (identifier == QtPixmapHeightField::name()) + return &qt_pixmap_metaData.heightField; + return 0; +} + +void QtPixmapInstance::getPropertyNames(ExecState*exec, PropertyNameArray& arr) +{ + arr.add(Identifier(exec, UString(QtPixmapToDataUrlMethod::name()))); + arr.add(Identifier(exec, UString(QtPixmapAssignToElementMethod::name()))); + arr.add(Identifier(exec, UString(QtPixmapToStringMethod::name()))); + arr.add(Identifier(exec, UString(QtPixmapWidthField::name()))); + arr.add(Identifier(exec, UString(QtPixmapHeightField::name()))); +} + +JSValue QtPixmapInstance::defaultValue(ExecState* exec, PreferredPrimitiveType ptype) const +{ + if (ptype == PreferNumber) { + return jsBoolean( + (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()) && !(data.value<QImage>()).isNull()) + || (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()) && !data.value<QPixmap>().isNull())); + } + + if (ptype == PreferString) + return valueOf(exec); + + return jsUndefined(); +} + +JSValue QtPixmapInstance::valueOf(ExecState* exec) const +{ + const QString stringValue = QString("[Qt Native Pixmap %1,%2]").arg(width()).arg(height()); + UString ustring((UChar*)stringValue.utf16(), stringValue.length()); + return jsString(exec, ustring); +} + +QtPixmapInstance::QtPixmapInstance(PassRefPtr<RootObject> rootObj, const QVariant& d) + :Instance(rootObj), data(d) +{ +} + +int QtPixmapInstance::width() const +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) + return data.value<QPixmap>().width(); + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) + return data.value<QImage>().width(); + return 0; +} + +int QtPixmapInstance::height() const +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) + return data.value<QPixmap>().height(); + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) + return data.value<QImage>().height(); + return 0; +} + +QPixmap QtPixmapInstance::toPixmap() +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) + return data.value<QPixmap>(); + + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) { + const QPixmap pixmap = QPixmap::fromImage(data.value<QImage>()); + data = QVariant::fromValue<QPixmap>(pixmap); + return pixmap; + } + + return QPixmap(); +} + +QImage QtPixmapInstance::toImage() +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) + return data.value<QImage>(); + + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) { + const QImage image = data.value<QPixmap>().toImage(); + data = QVariant::fromValue<QImage>(image); + return image; + } + + return QImage(); +} + +QVariant QtPixmapInstance::variantFromObject(JSObject* object, QMetaType::Type hint) +{ + if (!object) + goto returnEmptyVariant; + + if (object->inherits(&JSHTMLImageElement::s_info)) { + JSHTMLImageElement* elementJSWrapper = static_cast<JSHTMLImageElement*>(object); + HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(elementJSWrapper->impl()); + + if (!imageElement) + goto returnEmptyVariant; + + CachedImage* cachedImage = imageElement->cachedImage(); + if (!cachedImage) + goto returnEmptyVariant; + + Image* image = cachedImage->image(); + if (!image) + goto returnEmptyVariant; + + QPixmap* pixmap = image->nativeImageForCurrentFrame(); + if (!pixmap) + goto returnEmptyVariant; + + return (hint == static_cast<QMetaType::Type>(qMetaTypeId<QPixmap>())) + ? QVariant::fromValue<QPixmap>(*pixmap) + : QVariant::fromValue<QImage>(pixmap->toImage()); + } + + if (object->inherits(&QtPixmapRuntimeObject::s_info)) { + QtPixmapRuntimeObject* runtimeObject = static_cast<QtPixmapRuntimeObject*>(object); + QtPixmapInstance* instance = static_cast<QtPixmapInstance*>(runtimeObject->getInternalInstance()); + if (!instance) + goto returnEmptyVariant; + + if (hint == qMetaTypeId<QPixmap>()) + return QVariant::fromValue<QPixmap>(instance->toPixmap()); + + if (hint == qMetaTypeId<QImage>()) + return QVariant::fromValue<QImage>(instance->toImage()); + } + +returnEmptyVariant: + if (hint == qMetaTypeId<QPixmap>()) + return QVariant::fromValue<QPixmap>(QPixmap()); + if (hint == qMetaTypeId<QImage>()) + return QVariant::fromValue<QImage>(QImage()); + return QVariant(); +} + +RuntimeObject* QtPixmapInstance::newRuntimeObject(ExecState* exec) +{ + return new(exec) QtPixmapRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +JSObject* QtPixmapInstance::createPixmapRuntimeObject(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& data) +{ + JSLock lock(SilenceAssertionsOnly); + RefPtr<QtPixmapInstance> instance = adoptRef(new QtPixmapInstance(root, data)); + return instance->createRuntimeObject(exec); +} + +bool QtPixmapInstance::canHandle(QMetaType::Type hint) +{ + return hint == qMetaTypeId<QImage>() || hint == qMetaTypeId<QPixmap>(); +} + +} + +} diff --git a/Source/WebCore/bridge/qt/qt_pixmapruntime.h b/Source/WebCore/bridge/qt/qt_pixmapruntime.h new file mode 100644 index 0000000..e2ae5e9 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_pixmapruntime.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef qt_pixmapruntime_h +#define qt_pixmapruntime_h + +#include "Bridge.h" +#include <QVariant> + +namespace JSC { + +namespace Bindings { + +class QtPixmapInstance : public Instance { + QVariant data; +public: + QtPixmapInstance(PassRefPtr<RootObject> rootObj, const QVariant& newData); + virtual Class* getClass() const; + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName); + virtual JSValue invokeMethod(ExecState*, RuntimeMethod*); + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + virtual JSValue valueOf(ExecState* exec) const; + int width() const; + int height() const; + QPixmap toPixmap(); + QImage toImage(); + RuntimeObject* newRuntimeObject(ExecState* exec); + static JSObject* createPixmapRuntimeObject(ExecState*, PassRefPtr<RootObject>, const QVariant&); + static QVariant variantFromObject(JSObject*, QMetaType::Type hint); + static bool canHandle(QMetaType::Type hint); +}; + +} + +} +#endif diff --git a/Source/WebCore/bridge/qt/qt_runtime.cpp b/Source/WebCore/bridge/qt/qt_runtime.cpp new file mode 100644 index 0000000..0fb811b --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_runtime.cpp @@ -0,0 +1,1914 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "qt_runtime.h" + +#include "BooleanObject.h" +#include "DateInstance.h" +#include "DateMath.h" +#include "DatePrototype.h" +#include "DumpRenderTreeSupportQt.h" +#include "FunctionPrototype.h" +#include "Interpreter.h" +#include "JSArray.h" +#include "JSByteArray.h" +#include "JSDocument.h" +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include <JSFunction.h> +#include "JSGlobalObject.h" +#include "JSHTMLElement.h" +#include "JSLock.h" +#include "JSObject.h" +#include "ObjectPrototype.h" +#include "PropertyNameArray.h" +#include "RegExpConstructor.h" +#include "RegExpObject.h" +#include "qdatetime.h" +#include "qdebug.h" +#include "qmetaobject.h" +#include "qmetatype.h" +#include "qobject.h" +#include "qstringlist.h" +#include "qt_instance.h" +#include "qt_pixmapruntime.h" +#include "qvarlengtharray.h" +#include "qwebelement.h" +#include <limits.h> +#include <runtime/Error.h> +#include <runtime_array.h> +#include <runtime_object.h> + +// QtScript has these +Q_DECLARE_METATYPE(QObjectList); +Q_DECLARE_METATYPE(QList<int>); +Q_DECLARE_METATYPE(QVariant); + +using namespace WebCore; + +namespace JSC { +namespace Bindings { + +// Debugging +//#define QTWK_RUNTIME_CONVERSION_DEBUG +//#define QTWK_RUNTIME_MATCH_DEBUG + +class QWKNoDebug +{ +public: + inline QWKNoDebug(){} + inline ~QWKNoDebug(){} + + template<typename T> + inline QWKNoDebug &operator<<(const T &) { return *this; } +}; + +#ifdef QTWK_RUNTIME_CONVERSION_DEBUG +#define qConvDebug() qDebug() +#else +#define qConvDebug() QWKNoDebug() +#endif + +#ifdef QTWK_RUNTIME_MATCH_DEBUG +#define qMatchDebug() qDebug() +#else +#define qMatchDebug() QWKNoDebug() +#endif + +typedef enum { + Variant = 0, + Number, + Boolean, + String, + Date, + RegExp, + Array, + QObj, + Object, + Null, + RTArray, + JSByteArray +} JSRealType; + +#if defined(QTWK_RUNTIME_CONVERSION_DEBUG) || defined(QTWK_RUNTIME_MATCH_DEBUG) +QDebug operator<<(QDebug dbg, const JSRealType &c) +{ + const char *map[] = { "Variant", "Number", "Boolean", "String", "Date", + "RegExp", "Array", "RTObject", "Object", "Null", "RTArray"}; + + dbg.nospace() << "JSType(" << ((int)c) << ", " << map[c] << ")"; + + return dbg.space(); +} +#endif + +// this is here as a proxy, so we'd have a class to friend in QWebElement, +// as getting/setting a WebCore in QWebElement is private +class QtWebElementRuntime { +public: + static QWebElement create(Element* element) + { + return QWebElement(element); + } + + static Element* get(const QWebElement& element) + { + return element.m_element; + } +}; + +// this is here as a proxy, so we'd have a class to friend in QDRTNode, +// as getting/setting a WebCore in QDRTNode is private. +// We only need to pass WebCore Nodes for layout tests. +class QtDRTNodeRuntime { +public: + static QDRTNode create(Node* node) + { + return QDRTNode(node); + } + + static Node* get(const QDRTNode& node) + { + return node.m_node; + } +}; + +static JSRealType valueRealType(ExecState* exec, JSValue val) +{ + if (val.isNumber()) + return Number; + else if (val.isString()) + return String; + else if (val.isBoolean()) + return Boolean; + else if (val.isNull()) + return Null; + else if (isJSByteArray(&exec->globalData(), val)) + return JSByteArray; + else if (val.isObject()) { + JSObject *object = val.toObject(exec); + if (object->inherits(&RuntimeArray::s_info)) // RuntimeArray 'inherits' from Array, but not in C++ + return RTArray; + else if (object->inherits(&JSArray::info)) + return Array; + else if (object->inherits(&DateInstance::info)) + return Date; + else if (object->inherits(&RegExpObject::info)) + return RegExp; + else if (object->inherits(&RuntimeObject::s_info)) + return QObj; + return Object; + } + + return String; // I don't know. +} + +QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance, HashSet<JSObject*>* visitedObjects, int recursionLimit) +{ + --recursionLimit; + + if (!value || !recursionLimit) + return QVariant(); + + JSObject* object = 0; + if (value.isObject()) { + object = value.toObject(exec); + if (visitedObjects->contains(object)) + return QVariant(); + + visitedObjects->add(object); + } + + // check magic pointer values before dereferencing value + if (value == jsNaN() + || (value == jsUndefined() + && hint != QMetaType::QString + && hint != (QMetaType::Type) qMetaTypeId<QVariant>())) { + if (distance) + *distance = -1; + return QVariant(); + } + + JSLock lock(SilenceAssertionsOnly); + JSRealType type = valueRealType(exec, value); + if (hint == QMetaType::Void) { + switch(type) { + case Number: + hint = QMetaType::Double; + break; + case Boolean: + hint = QMetaType::Bool; + break; + case String: + default: + hint = QMetaType::QString; + break; + case Date: + hint = QMetaType::QDateTime; + break; + case RegExp: + hint = QMetaType::QRegExp; + break; + case Object: + if (object->inherits(&NumberObject::info)) + hint = QMetaType::Double; + else if (object->inherits(&BooleanObject::info)) + hint = QMetaType::Bool; + else + hint = QMetaType::QVariantMap; + break; + case QObj: + hint = QMetaType::QObjectStar; + break; + case JSByteArray: + hint = QMetaType::QByteArray; + break; + case Array: + case RTArray: + hint = QMetaType::QVariantList; + break; + } + } + + qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint; + + if (value == jsNull() + && hint != QMetaType::QObjectStar + && hint != QMetaType::VoidStar + && hint != QMetaType::QString + && hint != (QMetaType::Type) qMetaTypeId<QVariant>()) { + if (distance) + *distance = -1; + return QVariant(); + } + + QVariant ret; + int dist = -1; + switch (hint) { + case QMetaType::Bool: + if (type == Object && object->inherits(&BooleanObject::info)) + ret = QVariant(asBooleanObject(value)->internalValue().toBoolean(exec)); + else + ret = QVariant(value.toBoolean(exec)); + if (type == Boolean) + dist = 0; + else + dist = 10; + break; + + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::Long: + case QMetaType::ULong: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::Float: + case QMetaType::Double: + ret = QVariant(value.toNumber(exec)); + ret.convert((QVariant::Type)hint); + if (type == Number) { + switch (hint) { + case QMetaType::Double: + dist = 0; + break; + case QMetaType::Float: + dist = 1; + break; + case QMetaType::LongLong: + case QMetaType::ULongLong: + dist = 2; + break; + case QMetaType::Long: + case QMetaType::ULong: + dist = 3; + break; + case QMetaType::Int: + case QMetaType::UInt: + dist = 4; + break; + case QMetaType::Short: + case QMetaType::UShort: + dist = 5; + break; + break; + default: + dist = 10; + break; + } + } else { + dist = 10; + } + break; + + case QMetaType::QChar: + if (type == Number || type == Boolean) { + ret = QVariant(QChar((ushort)value.toNumber(exec))); + if (type == Boolean) + dist = 3; + else + dist = 6; + } else { + UString str = value.toString(exec); + ret = QVariant(QChar(str.length() ? *(const ushort*)str.impl()->characters() : 0)); + if (type == String) + dist = 3; + else + dist = 10; + } + break; + + case QMetaType::QString: { + if (value.isUndefinedOrNull()) { + if (distance) + *distance = 1; + return QString(); + } else { + UString ustring = value.toString(exec); + ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length())); + if (type == String) + dist = 0; + else + dist = 10; + } + break; + } + + case QMetaType::QVariantMap: + if (type == Object || type == Array || type == RTArray) { + // Enumerate the contents of the object + PropertyNameArray properties(exec); + object->getPropertyNames(exec, properties); + PropertyNameArray::const_iterator it = properties.begin(); + + QVariantMap result; + int objdist = 0; + while(it != properties.end()) { + if (object->propertyIsEnumerable(exec, *it)) { + JSValue val = object->get(exec, *it); + QVariant v = convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit); + if (objdist >= 0) { + UString ustring = (*it).ustring(); + QString id = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + result.insert(id, v); + } + } + ++it; + } + dist = 1; + ret = QVariant(result); + } + break; + + case QMetaType::QVariantList: + if (type == RTArray) { + RuntimeArray* rtarray = static_cast<RuntimeArray*>(object); + + QVariantList result; + int len = rtarray->getLength(); + int objdist = 0; + qConvDebug() << "converting a " << len << " length Array"; + for (int i = 0; i < len; ++i) { + JSValue val = rtarray->getConcreteArray()->valueAt(exec, i); + result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit)); + if (objdist == -1) { + qConvDebug() << "Failed converting element at index " << i; + break; // Failed converting a list entry, so fail the array + } + } + if (objdist != -1) { + dist = 5; + ret = QVariant(result); + } + } else if (type == Array) { + JSArray* array = static_cast<JSArray*>(object); + + QVariantList result; + int len = array->length(); + int objdist = 0; + qConvDebug() << "converting a " << len << " length Array"; + for (int i = 0; i < len; ++i) { + JSValue val = array->get(exec, i); + result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit)); + if (objdist == -1) { + qConvDebug() << "Failed converting element at index " << i; + break; // Failed converting a list entry, so fail the array + } + } + if (objdist != -1) { + dist = 5; + ret = QVariant(result); + } + } else { + // Make a single length array + int objdist; + qConvDebug() << "making a single length variantlist"; + QVariant var = convertValueToQVariant(exec, value, QMetaType::Void, &objdist, visitedObjects, recursionLimit); + if (objdist != -1) { + QVariantList result; + result << var; + ret = QVariant(result); + dist = 10; + } else { + qConvDebug() << "failed making single length varlist"; + } + } + break; + + case QMetaType::QStringList: { + if (type == RTArray) { + RuntimeArray* rtarray = static_cast<RuntimeArray*>(object); + + QStringList result; + int len = rtarray->getLength(); + for (int i = 0; i < len; ++i) { + JSValue val = rtarray->getConcreteArray()->valueAt(exec, i); + UString ustring = val.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + + result.append(qstring); + } + dist = 5; + ret = QVariant(result); + } else if (type == Array) { + JSArray* array = static_cast<JSArray*>(object); + + QStringList result; + int len = array->length(); + for (int i = 0; i < len; ++i) { + JSValue val = array->get(exec, i); + UString ustring = val.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + + result.append(qstring); + } + dist = 5; + ret = QVariant(result); + } else { + // Make a single length array + UString ustring = value.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + QStringList result; + result.append(qstring); + ret = QVariant(result); + dist = 10; + } + break; + } + + case QMetaType::QByteArray: { + if (type == JSByteArray) { + WTF::ByteArray* arr = asByteArray(value)->storage(); + ret = QVariant(QByteArray(reinterpret_cast<const char*>(arr->data()), arr->length())); + dist = 0; + } else { + UString ustring = value.toString(exec); + ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length()).toLatin1()); + if (type == String) + dist = 5; + else + dist = 10; + } + break; + } + + case QMetaType::QDateTime: + case QMetaType::QDate: + case QMetaType::QTime: + if (type == Date) { + DateInstance* date = static_cast<DateInstance*>(object); + GregorianDateTime gdt; + msToGregorianDateTime(exec, date->internalNumber(), true, gdt); + if (hint == QMetaType::QDateTime) { + ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC); + dist = 0; + } else if (hint == QMetaType::QDate) { + ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay); + dist = 1; + } else { + ret = QTime(gdt.hour + 1900, gdt.minute, gdt.second); + dist = 2; + } + } else if (type == Number) { + double b = value.toNumber(exec); + GregorianDateTime gdt; + msToGregorianDateTime(exec, b, true, gdt); + if (hint == QMetaType::QDateTime) { + ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC); + dist = 6; + } else if (hint == QMetaType::QDate) { + ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay); + dist = 8; + } else { + ret = QTime(gdt.hour, gdt.minute, gdt.second); + dist = 10; + } +#ifndef QT_NO_DATESTRING + } else if (type == String) { + UString ustring = value.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + + if (hint == QMetaType::QDateTime) { + QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate); + if (!dt.isValid()) + dt = QDateTime::fromString(qstring, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate); + if (!dt.isValid()) + dt = QDateTime::fromString(qstring, Qt::LocaleDate); + if (dt.isValid()) { + ret = dt; + dist = 2; + } + } else if (hint == QMetaType::QDate) { + QDate dt = QDate::fromString(qstring, Qt::ISODate); + if (!dt.isValid()) + dt = QDate::fromString(qstring, Qt::TextDate); + if (!dt.isValid()) + dt = QDate::fromString(qstring, Qt::SystemLocaleDate); + if (!dt.isValid()) + dt = QDate::fromString(qstring, Qt::LocaleDate); + if (dt.isValid()) { + ret = dt; + dist = 3; + } + } else { + QTime dt = QTime::fromString(qstring, Qt::ISODate); + if (!dt.isValid()) + dt = QTime::fromString(qstring, Qt::TextDate); + if (!dt.isValid()) + dt = QTime::fromString(qstring, Qt::SystemLocaleDate); + if (!dt.isValid()) + dt = QTime::fromString(qstring, Qt::LocaleDate); + if (dt.isValid()) { + ret = dt; + dist = 3; + } + } +#endif // QT_NO_DATESTRING + } + break; + + case QMetaType::QRegExp: + if (type == RegExp) { +/* + RegExpObject *re = static_cast<RegExpObject*>(object); +*/ + // Attempt to convert.. a bit risky + UString ustring = value.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + + // this is of the form '/xxxxxx/i' + int firstSlash = qstring.indexOf(QLatin1Char('/')); + int lastSlash = qstring.lastIndexOf(QLatin1Char('/')); + if (firstSlash >=0 && lastSlash > firstSlash) { + QRegExp realRe; + + realRe.setPattern(qstring.mid(firstSlash + 1, lastSlash - firstSlash - 1)); + + if (qstring.mid(lastSlash + 1).contains(QLatin1Char('i'))) + realRe.setCaseSensitivity(Qt::CaseInsensitive); + + ret = qVariantFromValue(realRe); + dist = 0; + } else { + qConvDebug() << "couldn't parse a JS regexp"; + } + } else if (type == String) { + UString ustring = value.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + + QRegExp re(qstring); + if (re.isValid()) { + ret = qVariantFromValue(re); + dist = 10; + } + } + break; + + case QMetaType::QObjectStar: + if (type == QObj) { + QtInstance* qtinst = QtInstance::getInstance(object); + if (qtinst) { + if (qtinst->getObject()) { + qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); + ret = qVariantFromValue(qtinst->getObject()); + qConvDebug() << ret; + dist = 0; + } else { + qConvDebug() << "can't convert deleted qobject"; + } + } else { + qConvDebug() << "wasn't a qtinstance"; + } + } else if (type == Null) { + QObject* nullobj = 0; + ret = qVariantFromValue(nullobj); + dist = 0; + } else { + qConvDebug() << "previous type was not an object:" << type; + } + break; + + case QMetaType::VoidStar: + if (type == QObj) { + QtInstance* qtinst = QtInstance::getInstance(object); + if (qtinst) { + if (qtinst->getObject()) { + qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); + ret = qVariantFromValue((void *)qtinst->getObject()); + qConvDebug() << ret; + dist = 0; + } else { + qConvDebug() << "can't convert deleted qobject"; + } + } else { + qConvDebug() << "wasn't a qtinstance"; + } + } else if (type == Null) { + ret = qVariantFromValue((void*)0); + dist = 0; + } else if (type == Number) { + // I don't think that converting a double to a pointer is a wise + // move. Except maybe 0. + qConvDebug() << "got number for void * - not converting, seems unsafe:" << value.toNumber(exec); + } else { + qConvDebug() << "void* - unhandled type" << type; + } + break; + + default: + // Non const type ids + if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>()) + { + if (type == RTArray) { + RuntimeArray* rtarray = static_cast<RuntimeArray*>(object); + + QObjectList result; + int len = rtarray->getLength(); + for (int i = 0; i < len; ++i) { + JSValue val = rtarray->getConcreteArray()->valueAt(exec, i); + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit); + if (itemdist >= 0) + result.append(item.value<QObject*>()); + else + break; + } + // If we didn't fail conversion + if (result.count() == len) { + dist = 5; + ret = QVariant::fromValue(result); + } + } else if (type == Array) { + JSObject* object = value.toObject(exec); + JSArray* array = static_cast<JSArray *>(object); + QObjectList result; + int len = array->length(); + for (int i = 0; i < len; ++i) { + JSValue val = array->get(exec, i); + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit); + if (itemdist >= 0) + result.append(item.value<QObject*>()); + else + break; + } + // If we didn't fail conversion + if (result.count() == len) { + dist = 5; + ret = QVariant::fromValue(result); + } + } else { + // Make a single length array + QObjectList result; + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, value, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit); + if (itemdist >= 0) { + result.append(item.value<QObject*>()); + dist = 10; + ret = QVariant::fromValue(result); + } + } + break; + } else if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) { + if (type == RTArray) { + RuntimeArray* rtarray = static_cast<RuntimeArray*>(object); + + QList<int> result; + int len = rtarray->getLength(); + for (int i = 0; i < len; ++i) { + JSValue val = rtarray->getConcreteArray()->valueAt(exec, i); + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects, recursionLimit); + if (itemdist >= 0) + result.append(item.value<int>()); + else + break; + } + // If we didn't fail conversion + if (result.count() == len) { + dist = 5; + ret = QVariant::fromValue(result); + } + } else if (type == Array) { + JSArray* array = static_cast<JSArray *>(object); + + QList<int> result; + int len = array->length(); + for (int i = 0; i < len; ++i) { + JSValue val = array->get(exec, i); + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects, recursionLimit); + if (itemdist >= 0) + result.append(item.value<int>()); + else + break; + } + // If we didn't fail conversion + if (result.count() == len) { + dist = 5; + ret = QVariant::fromValue(result); + } + } else { + // Make a single length array + QList<int> result; + int itemdist = -1; + QVariant item = convertValueToQVariant(exec, value, QMetaType::Int, &itemdist, visitedObjects, recursionLimit); + if (itemdist >= 0) { + result.append(item.value<int>()); + dist = 10; + ret = QVariant::fromValue(result); + } + } + break; + } else if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(hint))) { + ret = QtPixmapInstance::variantFromObject(object, static_cast<QMetaType::Type>(hint)); + } else if (hint == (QMetaType::Type) qMetaTypeId<QWebElement>()) { + if (object && object->inherits(&JSHTMLElement::s_info)) + ret = QVariant::fromValue<QWebElement>(QtWebElementRuntime::create((static_cast<JSHTMLElement*>(object))->impl())); + else if (object && object->inherits(&JSDocument::s_info)) + ret = QVariant::fromValue<QWebElement>(QtWebElementRuntime::create((static_cast<JSDocument*>(object))->impl()->documentElement())); + else + ret = QVariant::fromValue<QWebElement>(QWebElement()); + } else if (hint == (QMetaType::Type) qMetaTypeId<QDRTNode>()) { + if (object && object->inherits(&JSNode::s_info)) + ret = QVariant::fromValue<QDRTNode>(QtDRTNodeRuntime::create((static_cast<JSNode*>(object))->impl())); + } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) { + if (value.isUndefinedOrNull()) { + if (distance) + *distance = 1; + return QVariant(); + } else { + if (type == Object) { + // Since we haven't really visited this object yet, we remove it + visitedObjects->remove(object); + } + + // And then recurse with the autodetect flag + ret = convertValueToQVariant(exec, value, QMetaType::Void, distance, visitedObjects, recursionLimit); + dist = 10; + } + break; + } + + dist = 10; + break; + } + + if (!ret.isValid()) + dist = -1; + if (distance) + *distance = dist; + + return ret; +} + +QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance) +{ + const int recursionLimit = 200; + HashSet<JSObject*> visitedObjects; + return convertValueToQVariant(exec, value, hint, distance, &visitedObjects, recursionLimit); +} + +JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant) +{ + // Variants with QObject * can be isNull but not a null pointer + // An empty QString variant is also null + QMetaType::Type type = (QMetaType::Type) variant.userType(); + + qConvDebug() << "convertQVariantToValue: metatype:" << type << ", isnull: " << variant.isNull(); + if (variant.isNull() && + type != QMetaType::QObjectStar && + type != QMetaType::VoidStar && + type != QMetaType::QWidgetStar && + type != QMetaType::QString) { + return jsNull(); + } + + JSLock lock(SilenceAssertionsOnly); + + if (type == QMetaType::Bool) + return jsBoolean(variant.toBool()); + + if (type == QMetaType::Int || + type == QMetaType::UInt || + type == QMetaType::Long || + type == QMetaType::ULong || + type == QMetaType::LongLong || + type == QMetaType::ULongLong || + type == QMetaType::Short || + type == QMetaType::UShort || + type == QMetaType::Float || + type == QMetaType::Double) + return jsNumber(variant.toDouble()); + + if (type == QMetaType::QRegExp) { + QRegExp re = variant.value<QRegExp>(); + + if (re.isValid()) { + UString uflags; + if (re.caseSensitivity() == Qt::CaseInsensitive) + uflags = "i"; // ### Can't do g or m + + UString pattern((UChar*)re.pattern().utf16(), re.pattern().length()); + + RefPtr<JSC::RegExp> regExp = JSC::RegExp::create(&exec->globalData(), pattern, uflags); + if (regExp->isValid()) + return new (exec) RegExpObject(exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp.release()); + else + return jsNull(); + } + } + + if (type == QMetaType::QDateTime || + type == QMetaType::QDate || + type == QMetaType::QTime) { + + QDate date = QDate::currentDate(); + QTime time(0,0,0); // midnight + + if (type == QMetaType::QDate) + date = variant.value<QDate>(); + else if (type == QMetaType::QTime) + time = variant.value<QTime>(); + else { + QDateTime dt = variant.value<QDateTime>().toLocalTime(); + date = dt.date(); + time = dt.time(); + } + + // Dates specified this way are in local time (we convert DateTimes above) + GregorianDateTime dt; + dt.year = date.year() - 1900; + dt.month = date.month() - 1; + dt.monthDay = date.day(); + dt.hour = time.hour(); + dt.minute = time.minute(); + dt.second = time.second(); + dt.isDST = -1; + double ms = gregorianDateTimeToMS(exec, dt, time.msec(), /*inputIsUTC*/ false); + + return new (exec) DateInstance(exec, trunc(ms)); + } + + if (type == QMetaType::QByteArray) { + QByteArray qtByteArray = variant.value<QByteArray>(); + WTF::RefPtr<WTF::ByteArray> wtfByteArray = WTF::ByteArray::create(qtByteArray.length()); + memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length()); + return new (exec) JSC::JSByteArray(exec, JSC::JSByteArray::createStructure(jsNull()), wtfByteArray.get()); + } + + if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) { + QObject* obj = variant.value<QObject*>(); + if (!obj) + return jsNull(); + return QtInstance::getQtInstance(obj, root, QScriptEngine::QtOwnership)->createRuntimeObject(exec); + } + + if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(variant.type()))) + return QtPixmapInstance::createPixmapRuntimeObject(exec, root, variant); + + if (type == qMetaTypeId<QWebElement>()) { + if (!root->globalObject()->inherits(&JSDOMWindow::s_info)) + return jsUndefined(); + + Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document(); + if (!document) + return jsUndefined(); + + return toJS(exec, toJSDOMGlobalObject(document, exec), QtWebElementRuntime::get(variant.value<QWebElement>())); + } + + if (type == qMetaTypeId<QDRTNode>()) { + if (!root->globalObject()->inherits(&JSDOMWindow::s_info)) + return jsUndefined(); + + Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document(); + if (!document) + return jsUndefined(); + + return toJS(exec, toJSDOMGlobalObject(document, exec), QtDRTNodeRuntime::get(variant.value<QDRTNode>())); + } + + if (type == QMetaType::QVariantMap) { + // create a new object, and stuff properties into it + JSObject* ret = constructEmptyObject(exec); + QVariantMap map = variant.value<QVariantMap>(); + QVariantMap::const_iterator i = map.constBegin(); + while (i != map.constEnd()) { + QString s = i.key(); + JSValue val = convertQVariantToValue(exec, root.get(), i.value()); + if (val) { + PutPropertySlot slot; + ret->put(exec, Identifier(exec, reinterpret_cast_ptr<const UChar *>(s.constData()), s.length()), val, slot); + // ### error case? + } + ++i; + } + + return ret; + } + + // List types + if (type == QMetaType::QVariantList) { + QVariantList vl = variant.toList(); + qConvDebug() << "got a " << vl.count() << " length list:" << vl; + return new (exec) RuntimeArray(exec, new QtArray<QVariant>(vl, QMetaType::Void, root)); + } else if (type == QMetaType::QStringList) { + QStringList sl = variant.value<QStringList>(); + return new (exec) RuntimeArray(exec, new QtArray<QString>(sl, QMetaType::QString, root)); + } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) { + QObjectList ol= variant.value<QObjectList>(); + return new (exec) RuntimeArray(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root)); + } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) { + QList<int> il= variant.value<QList<int> >(); + return new (exec) RuntimeArray(exec, new QtArray<int>(il, QMetaType::Int, root)); + } + + if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) { + QVariant real = variant.value<QVariant>(); + qConvDebug() << "real variant is:" << real; + return convertQVariantToValue(exec, root, real); + } + + qConvDebug() << "fallback path for" << variant << variant.userType(); + + QString string = variant.toString(); + UString ustring((UChar*)string.utf16(), string.length()); + return jsString(exec, ustring); +} + +// =============== + +// Qt-like macros +#define QW_D(Class) Class##Data* d = d_func() +#define QW_DS(Class,Instance) Class##Data* d = Instance->d_func() + +const ClassInfo QtRuntimeMethod::s_info = { "QtRuntimeMethod", 0, 0, 0 }; + +QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState* exec, const Identifier& ident, PassRefPtr<QtInstance> inst) + : InternalFunction(&exec->globalData(), exec->lexicalGlobalObject(), deprecatedGetDOMStructure<QtRuntimeMethod>(exec), ident) + , d_ptr(dd) +{ + QW_D(QtRuntimeMethod); + d->m_instance = inst; +} + +QtRuntimeMethod::~QtRuntimeMethod() +{ + QW_D(QtRuntimeMethod); + d->m_instance->removeCachedMethod(this); + delete d_ptr; +} + +// =============== + +QtRuntimeMethodData::~QtRuntimeMethodData() +{ +} + +QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData() +{ + +} + +QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData() +{ + +} + +// =============== + +// Type conversion metadata (from QtScript originally) +class QtMethodMatchType +{ +public: + enum Kind { + Invalid, + Variant, + MetaType, + Unresolved, + MetaEnum + }; + + + QtMethodMatchType() + : m_kind(Invalid) { } + + Kind kind() const + { return m_kind; } + + QMetaType::Type typeId() const; + + bool isValid() const + { return (m_kind != Invalid); } + + bool isVariant() const + { return (m_kind == Variant); } + + bool isMetaType() const + { return (m_kind == MetaType); } + + bool isUnresolved() const + { return (m_kind == Unresolved); } + + bool isMetaEnum() const + { return (m_kind == MetaEnum); } + + QByteArray name() const; + + int enumeratorIndex() const + { Q_ASSERT(isMetaEnum()); return m_typeId; } + + static QtMethodMatchType variant() + { return QtMethodMatchType(Variant); } + + static QtMethodMatchType metaType(int typeId, const QByteArray &name) + { return QtMethodMatchType(MetaType, typeId, name); } + + static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name) + { return QtMethodMatchType(MetaEnum, enumIndex, name); } + + static QtMethodMatchType unresolved(const QByteArray &name) + { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); } + +private: + QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray()) + : m_kind(kind), m_typeId(typeId), m_name(name) { } + + Kind m_kind; + int m_typeId; + QByteArray m_name; +}; + +QMetaType::Type QtMethodMatchType::typeId() const +{ + if (isVariant()) + return (QMetaType::Type) QMetaType::type("QVariant"); + return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId); +} + +QByteArray QtMethodMatchType::name() const +{ + if (!m_name.isEmpty()) + return m_name; + else if (m_kind == Variant) + return "QVariant"; + return QByteArray(); +} + +struct QtMethodMatchData +{ + int matchDistance; + int index; + QVector<QtMethodMatchType> types; + QVarLengthArray<QVariant, 10> args; + + QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs, + const QVarLengthArray<QVariant, 10> &as) + : matchDistance(dist), index(idx), types(typs), args(as) { } + QtMethodMatchData() + : index(-1) { } + + bool isValid() const + { return (index != -1); } + + int firstUnresolvedIndex() const + { + for (int i=0; i < types.count(); i++) { + if (types.at(i).isUnresolved()) + return i; + } + return -1; + } +}; + +static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str) +{ + QByteArray scope; + QByteArray name; + int scopeIdx = str.indexOf("::"); + if (scopeIdx != -1) { + scope = str.left(scopeIdx); + name = str.mid(scopeIdx + 2); + } else { + name = str; + } + for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = meta->enumerator(i); + if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/) + return i; + } + return -1; +} + +// Helper function for resolving methods +// Largely based on code in QtScript for compatibility reasons +static int findMethodIndex(ExecState* exec, + const QMetaObject* meta, + const QByteArray& signature, + bool allowPrivate, + QVarLengthArray<QVariant, 10> &vars, + void** vvars, + JSObject **pError) +{ + QList<int> matchingIndices; + + bool overloads = !signature.contains('('); + + int count = meta->methodCount(); + for (int i = count - 1; i >= 0; --i) { + const QMetaMethod m = meta->method(i); + + // Don't choose private methods + if (m.access() == QMetaMethod::Private && !allowPrivate) + continue; + + // try and find all matching named methods + if (m.signature() == signature) + matchingIndices.append(i); + else if (overloads) { + QByteArray rawsignature = m.signature(); + rawsignature.truncate(rawsignature.indexOf('(')); + if (rawsignature == signature) + matchingIndices.append(i); + } + } + + int chosenIndex = -1; + *pError = 0; + QVector<QtMethodMatchType> chosenTypes; + + QVarLengthArray<QVariant, 10> args; + QVector<QtMethodMatchData> candidates; + QVector<QtMethodMatchData> unresolved; + QVector<int> tooFewArgs; + QVector<int> conversionFailed; + + foreach(int index, matchingIndices) { + QMetaMethod method = meta->method(index); + + QVector<QtMethodMatchType> types; + bool unresolvedTypes = false; + + // resolve return type + QByteArray returnTypeName = method.typeName(); + int rtype = QMetaType::type(returnTypeName); + if ((rtype == 0) && !returnTypeName.isEmpty()) { + if (returnTypeName == "QVariant") { + types.append(QtMethodMatchType::variant()); + } else if (returnTypeName.endsWith('*')) { + types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName)); + } else { + int enumIndex = indexOfMetaEnum(meta, returnTypeName); + if (enumIndex != -1) + types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName)); + else { + unresolvedTypes = true; + types.append(QtMethodMatchType::unresolved(returnTypeName)); + } + } + } else { + if (returnTypeName == "QVariant") + types.append(QtMethodMatchType::variant()); + else + types.append(QtMethodMatchType::metaType(rtype, returnTypeName)); + } + + // resolve argument types + QList<QByteArray> parameterTypeNames = method.parameterTypes(); + for (int i = 0; i < parameterTypeNames.count(); ++i) { + QByteArray argTypeName = parameterTypeNames.at(i); + int atype = QMetaType::type(argTypeName); + if (atype == 0) { + if (argTypeName == "QVariant") { + types.append(QtMethodMatchType::variant()); + } else { + int enumIndex = indexOfMetaEnum(meta, argTypeName); + if (enumIndex != -1) + types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName)); + else { + unresolvedTypes = true; + types.append(QtMethodMatchType::unresolved(argTypeName)); + } + } + } else { + if (argTypeName == "QVariant") + types.append(QtMethodMatchType::variant()); + else + types.append(QtMethodMatchType::metaType(atype, argTypeName)); + } + } + + // If the native method requires more arguments than what was passed from JavaScript + if (exec->argumentCount() + 1 < static_cast<unsigned>(types.count())) { + qMatchDebug() << "Match:too few args for" << method.signature(); + tooFewArgs.append(index); + continue; + } + + if (unresolvedTypes) { + qMatchDebug() << "Match:unresolved arg types for" << method.signature(); + // remember it so we can give an error message later, if necessary + unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index, + types, QVarLengthArray<QVariant, 10>())); + continue; + } + + // Now convert arguments + if (args.count() != types.count()) + args.resize(types.count()); + + QtMethodMatchType retType = types[0]; + args[0] = QVariant(retType.typeId(), (void *)0); // the return value + + bool converted = true; + int matchDistance = 0; + for (unsigned i = 0; converted && i + 1 < static_cast<unsigned>(types.count()); ++i) { + JSValue arg = i < exec->argumentCount() ? exec->argument(i) : jsUndefined(); + + int argdistance = -1; + QVariant v = convertValueToQVariant(exec, arg, types.at(i+1).typeId(), &argdistance); + if (argdistance >= 0) { + matchDistance += argdistance; + args[i+1] = v; + } else { + qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId()); + converted = false; + } + } + + qMatchDebug() << "Match: " << method.signature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance; + + if (converted) { + if ((exec->argumentCount() + 1 == static_cast<unsigned>(types.count())) + && (matchDistance == 0)) { + // perfect match, use this one + chosenIndex = index; + break; + } else { + QtMethodMatchData currentMatch(matchDistance, index, types, args); + if (candidates.isEmpty()) { + candidates.append(currentMatch); + } else { + QtMethodMatchData bestMatchSoFar = candidates.at(0); + if ((args.count() > bestMatchSoFar.args.count()) + || ((args.count() == bestMatchSoFar.args.count()) + && (matchDistance <= bestMatchSoFar.matchDistance))) { + candidates.prepend(currentMatch); + } else { + candidates.append(currentMatch); + } + } + } + } else { + conversionFailed.append(index); + } + + if (!overloads) + break; + } + + if (chosenIndex == -1 && candidates.count() == 0) { + // No valid functions at all - format an error message + if (!conversionFailed.isEmpty()) { + QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n") + .arg(QLatin1String(signature)); + for (int i = 0; i < conversionFailed.size(); ++i) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = meta->method(conversionFailed.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + *pError = throwError(exec, createTypeError(exec, message.toLatin1().constData())); + } else if (!unresolved.isEmpty()) { + QtMethodMatchData argsInstance = unresolved.first(); + int unresolvedIndex = argsInstance.firstUnresolvedIndex(); + Q_ASSERT(unresolvedIndex != -1); + QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex); + QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'") + .arg(QString::fromLatin1(signature)) + .arg(QLatin1String(unresolvedType.name())); + *pError = throwError(exec, createTypeError(exec, message.toLatin1().constData())); + } else { + QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n") + .arg(QLatin1String(signature)); + for (int i = 0; i < tooFewArgs.size(); ++i) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = meta->method(tooFewArgs.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + *pError = throwError(exec, createSyntaxError(exec, message.toLatin1().constData())); + } + } + + if (chosenIndex == -1 && candidates.count() > 0) { + QtMethodMatchData bestMatch = candidates.at(0); + if ((candidates.size() > 1) + && (bestMatch.args.count() == candidates.at(1).args.count()) + && (bestMatch.matchDistance == candidates.at(1).matchDistance)) { + // ambiguous call + QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n") + .arg(QLatin1String(signature)); + for (int i = 0; i < candidates.size(); ++i) { + // Only candidate for overload if argument count and match distance is same as best match + if (candidates.at(i).args.count() == bestMatch.args.count() + || candidates.at(i).matchDistance == bestMatch.matchDistance) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = meta->method(candidates.at(i).index); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + } + *pError = throwError(exec, createTypeError(exec, message.toLatin1().constData())); + } else { + chosenIndex = bestMatch.index; + args = bestMatch.args; + } + } + + if (chosenIndex != -1) { + /* Copy the stuff over */ + int i; + vars.resize(args.count()); + for (i=0; i < args.count(); i++) { + vars[i] = args[i]; + vvars[i] = vars[i].data(); + } + } + + return chosenIndex; +} + +// Signals are not fuzzy matched as much as methods +static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature) +{ + int index = initialIndex; + QMetaMethod method = meta->method(index); + bool overloads = !signature.contains('('); + if (overloads && (method.attributes() & QMetaMethod::Cloned)) { + // find the most general method + do { + method = meta->method(--index); + } while (method.attributes() & QMetaMethod::Cloned); + } + return index; +} + +QtRuntimeMetaMethod::QtRuntimeMetaMethod(ExecState* exec, const Identifier& ident, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature, bool allowPrivate) + : QtRuntimeMethod (new QtRuntimeMetaMethodData(), exec, ident, inst) +{ + QW_D(QtRuntimeMetaMethod); + d->m_signature = signature; + d->m_index = index; + d->m_connect = 0; + d->m_disconnect = 0; + d->m_allowPrivate = allowPrivate; +} + +void QtRuntimeMetaMethod::markChildren(MarkStack& markStack) +{ + QtRuntimeMethod::markChildren(markStack); + QW_D(QtRuntimeMetaMethod); + if (d->m_connect) + markStack.append(d->m_connect); + if (d->m_disconnect) + markStack.append(d->m_disconnect); +} + +EncodedJSValue QtRuntimeMetaMethod::call(ExecState* exec) +{ + QtRuntimeMetaMethodData* d = static_cast<QtRuntimeMetaMethod *>(exec->callee())->d_func(); + + // We're limited to 10 args + if (exec->argumentCount() > 10) + return JSValue::encode(jsUndefined()); + + // We have to pick a method that matches.. + JSLock lock(SilenceAssertionsOnly); + + QObject *obj = d->m_instance->getObject(); + if (obj) { + QVarLengthArray<QVariant, 10> vargs; + void *qargs[11]; + + int methodIndex; + JSObject* errorObj = 0; + if ((methodIndex = findMethodIndex(exec, obj->metaObject(), d->m_signature, d->m_allowPrivate, vargs, (void **)qargs, &errorObj)) != -1) { + if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0) + return JSValue::encode(jsUndefined()); + + if (vargs[0].isValid()) + return JSValue::encode(convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0])); + } + + if (errorObj) + return JSValue::encode(errorObj); + } else { + return throwVMError(exec, createError(exec, "cannot call function of deleted QObject")); + } + + // void functions return undefined + return JSValue::encode(jsUndefined()); +} + +CallType QtRuntimeMetaMethod::getCallData(CallData& callData) +{ + callData.native.function = call; + return CallTypeHost; +} + +bool QtRuntimeMetaMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == "connect") { + slot.setCustom(this, connectGetter); + return true; + } else if (propertyName == "disconnect") { + slot.setCustom(this, disconnectGetter); + return true; + } else if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot); +} + +bool QtRuntimeMetaMethod::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == "connect") { + PropertySlot slot; + slot.setCustom(this, connectGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + if (propertyName == "disconnect") { + PropertySlot slot; + slot.setCustom(this, disconnectGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + if (propertyName == exec->propertyNames().length) { + PropertySlot slot; + slot.setCustom(this, lengthGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + return QtRuntimeMethod::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void QtRuntimeMetaMethod::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode == IncludeDontEnumProperties) { + propertyNames.add(Identifier(exec, "connect")); + propertyNames.add(Identifier(exec, "disconnect")); + propertyNames.add(exec->propertyNames().length); + } + + QtRuntimeMethod::getOwnPropertyNames(exec, propertyNames, mode); +} + +JSValue QtRuntimeMetaMethod::lengthGetter(ExecState*, JSValue, const Identifier&) +{ + // QtScript always returns 0 + return jsNumber(0); +} + +JSValue QtRuntimeMetaMethod::connectGetter(ExecState* exec, JSValue slotBase, const Identifier& ident) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slotBase)); + QW_DS(QtRuntimeMetaMethod, thisObj); + + if (!d->m_connect) + d->m_connect = new (exec) QtRuntimeConnectionMethod(exec, ident, true, d->m_instance, d->m_index, d->m_signature); + return d->m_connect; +} + +JSValue QtRuntimeMetaMethod::disconnectGetter(ExecState* exec, JSValue slotBase, const Identifier& ident) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slotBase)); + QW_DS(QtRuntimeMetaMethod, thisObj); + + if (!d->m_disconnect) + d->m_disconnect = new (exec) QtRuntimeConnectionMethod(exec, ident, false, d->m_instance, d->m_index, d->m_signature); + return d->m_disconnect; +} + +// =============== + +QMultiMap<QObject*, QtConnectionObject*> QtRuntimeConnectionMethod::connections; + +QtRuntimeConnectionMethod::QtRuntimeConnectionMethod(ExecState* exec, const Identifier& ident, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature) + : QtRuntimeMethod (new QtRuntimeConnectionMethodData(), exec, ident, inst) +{ + QW_D(QtRuntimeConnectionMethod); + + d->m_signature = signature; + d->m_index = index; + d->m_isConnect = isConnect; +} + +EncodedJSValue QtRuntimeConnectionMethod::call(ExecState* exec) +{ + QtRuntimeConnectionMethodData* d = static_cast<QtRuntimeConnectionMethod *>(exec->callee())->d_func(); + + JSLock lock(SilenceAssertionsOnly); + + QObject* sender = d->m_instance->getObject(); + + if (sender) { + + JSObject* thisObject = exec->lexicalGlobalObject(); + JSObject* funcObject = 0; + + // QtScript checks signalness first, arguments second + int signalIndex = -1; + + // Make sure the initial index is a signal + QMetaMethod m = sender->metaObject()->method(d->m_index); + if (m.methodType() == QMetaMethod::Signal) + signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_signature); + + if (signalIndex != -1) { + if (exec->argumentCount() == 1) { + funcObject = exec->argument(0).toObject(exec); + CallData callData; + if (funcObject->getCallData(callData) == CallTypeNone) { + if (d->m_isConnect) + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function")); + else + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function")); + } + } else if (exec->argumentCount() >= 2) { + if (exec->argument(0).isObject()) { + thisObject = exec->argument(0).toObject(exec); + + // Get the actual function to call + JSObject *asObj = exec->argument(1).toObject(exec); + CallData callData; + if (asObj->getCallData(callData) != CallTypeNone) { + // Function version + funcObject = asObj; + } else { + // Convert it to a string + UString funcName = exec->argument(1).toString(exec); + Identifier funcIdent(exec, funcName); + + // ### DropAllLocks + // This is resolved at this point in QtScript + JSValue val = thisObject->get(exec, funcIdent); + JSObject* asFuncObj = val.toObject(exec); + + if (asFuncObj->getCallData(callData) != CallTypeNone) { + funcObject = asFuncObj; + } else { + if (d->m_isConnect) + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function")); + else + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function")); + } + } + } else { + if (d->m_isConnect) + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: thisObject is not an object")); + else + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: thisObject is not an object")); + } + } else { + if (d->m_isConnect) + return throwVMError(exec, createError(exec, "QtMetaMethod.connect: no arguments given")); + else + return throwVMError(exec, createError(exec, "QtMetaMethod.disconnect: no arguments given")); + } + + if (d->m_isConnect) { + // to connect, we need: + // target object [from ctor] + // target signal index etc. [from ctor] + // receiver function [from arguments] + // receiver this object [from arguments] + + QtConnectionObject* conn = new QtConnectionObject(d->m_instance, signalIndex, thisObject, funcObject); + bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); + if (!ok) { + delete conn; + QString msg = QString(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()")) + .arg(QLatin1String(sender->metaObject()->className())) + .arg(QLatin1String(d->m_signature)); + return throwVMError(exec, createError(exec, msg.toLatin1().constData())); + } + else { + // Store connection + connections.insert(sender, conn); + } + } else { + // Now to find our previous connection object. Hmm. + QList<QtConnectionObject*> conns = connections.values(sender); + bool ret = false; + + foreach(QtConnectionObject* conn, conns) { + // Is this the right connection? + if (conn->match(sender, signalIndex, thisObject, funcObject)) { + // Yep, disconnect it + QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); + delete conn; // this will also remove it from the map + ret = true; + break; + } + } + + if (!ret) { + QString msg = QString(QLatin1String("QtMetaMethod.disconnect: failed to disconnect from %1::%2()")) + .arg(QLatin1String(sender->metaObject()->className())) + .arg(QLatin1String(d->m_signature)); + return throwVMError(exec, createError(exec, msg.toLatin1().constData())); + } + } + } else { + QString msg = QString(QLatin1String("QtMetaMethod.%1: %2::%3() is not a signal")) + .arg(QLatin1String(d->m_isConnect ? "connect": "disconnect")) + .arg(QLatin1String(sender->metaObject()->className())) + .arg(QLatin1String(d->m_signature)); + return throwVMError(exec, createTypeError(exec, msg.toLatin1().constData())); + } + } else { + return throwVMError(exec, createError(exec, "cannot call function of deleted QObject")); + } + + return JSValue::encode(jsUndefined()); +} + +CallType QtRuntimeConnectionMethod::getCallData(CallData& callData) +{ + callData.native.function = call; + return CallTypeHost; +} + +bool QtRuntimeConnectionMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot); +} + +bool QtRuntimeConnectionMethod::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + PropertySlot slot; + slot.setCustom(this, lengthGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + return QtRuntimeMethod::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void QtRuntimeConnectionMethod::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().length); + + QtRuntimeMethod::getOwnPropertyNames(exec, propertyNames, mode); +} + +JSValue QtRuntimeConnectionMethod::lengthGetter(ExecState*, JSValue, const Identifier&) +{ + // we have one formal argument, and one optional + return jsNumber(1); +} + +// =============== + +QtConnectionObject::QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject) + : m_instance(instance) + , m_signalIndex(signalIndex) + , m_originalObject(m_instance->getObject()) + , m_thisObject(thisObject) + , m_funcObject(funcObject) +{ + setParent(m_originalObject); + ASSERT(JSLock::currentThreadIsHoldingLock()); // so our ProtectedPtrs are safe +} + +QtConnectionObject::~QtConnectionObject() +{ + // Remove us from the map of active connections + QtRuntimeConnectionMethod::connections.remove(m_originalObject, this); +} + +static const uint qt_meta_data_QtConnectionObject[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 1, 10, // methods + 0, 0, // properties + 0, 0, // enums/sets + + // slots: signature, parameters, type, tag, flags + 28, 27, 27, 27, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_QtConnectionObject[] = { + "JSC::Bindings::QtConnectionObject\0\0execute()\0" +}; + +const QMetaObject QtConnectionObject::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject, + qt_meta_data_QtConnectionObject, 0 } +}; + +const QMetaObject *QtConnectionObject::metaObject() const +{ + return &staticMetaObject; +} + +void *QtConnectionObject::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject)) + return static_cast<void*>(const_cast<QtConnectionObject*>(this)); + return QObject::qt_metacast(_clname); +} + +int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: execute(_a); break; + } + _id -= 1; + } + return _id; +} + +void QtConnectionObject::execute(void **argv) +{ + QObject* obj = m_instance->getObject(); + if (obj) { + const QMetaObject* meta = obj->metaObject(); + const QMetaMethod method = meta->method(m_signalIndex); + + QList<QByteArray> parameterTypes = method.parameterTypes(); + + int argc = parameterTypes.count(); + + JSLock lock(SilenceAssertionsOnly); + + // ### Should the Interpreter/ExecState come from somewhere else? + RefPtr<RootObject> ro = m_instance->rootObject(); + if (ro) { + JSGlobalObject* globalobj = ro->globalObject(); + if (globalobj) { + ExecState* exec = globalobj->globalExec(); + if (exec) { + // Build the argument list (up to the formal argument length of the slot) + MarkedArgumentBuffer l; + // ### DropAllLocks? + int funcArgC = m_funcObject->get(exec, exec->propertyNames().length).toInt32(exec); + int argTotal = qMax(funcArgC, argc); + for(int i=0; i < argTotal; i++) { + if (i < argc) { + int argType = QMetaType::type(parameterTypes.at(i)); + l.append(convertQVariantToValue(exec, ro, QVariant(argType, argv[i+1]))); + } else { + l.append(jsUndefined()); + } + } + // Stuff in the __qt_sender property, if we can + ScopeChain oldsc = ScopeChain(NoScopeChain()); + JSFunction* fimp = 0; + if (m_funcObject->inherits(&JSFunction::info)) { + fimp = static_cast<JSFunction*>(m_funcObject.get()); + + JSObject* qt_sender = QtInstance::getQtInstance(sender(), ro, QScriptEngine::QtOwnership)->createRuntimeObject(exec); + JSObject* wrapper = new (exec) JSObject(JSObject::createStructure(jsNull())); + PutPropertySlot slot; + wrapper->put(exec, Identifier(exec, "__qt_sender__"), qt_sender, slot); + oldsc = fimp->scope(); + ScopeChain sc = oldsc; + sc.push(wrapper); + fimp->setScope(sc); + } + + CallData callData; + CallType callType = m_funcObject->getCallData(callData); + call(exec, m_funcObject, callType, callData, m_thisObject, l); + + if (fimp) + fimp->setScope(oldsc); + } + } + } + } else { + // A strange place to be - a deleted object emitted a signal here. + qWarning() << "sender deleted, cannot deliver signal"; + } +} + +bool QtConnectionObject::match(QObject* sender, int signalIndex, JSObject* thisObject, JSObject *funcObject) +{ + if (m_originalObject == sender && m_signalIndex == signalIndex + && thisObject == (JSObject*)m_thisObject && funcObject == (JSObject*)m_funcObject) + return true; + return false; +} + +// =============== + +template <typename T> QtArray<T>::QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject> rootObject) + : Array(rootObject) + , m_list(list) + , m_type(type) +{ + m_length = m_list.count(); +} + +template <typename T> QtArray<T>::~QtArray () +{ +} + +template <typename T> RootObject* QtArray<T>::rootObject() const +{ + return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0; +} + +template <typename T> void QtArray<T>::setValueAt(ExecState* exec, unsigned index, JSValue aValue) const +{ + // QtScript sets the value, but doesn't forward it to the original source + // (e.g. if you do 'object.intList[5] = 6', the object is not updated, but the + // copy of the list is). + int dist = -1; + QVariant val = convertValueToQVariant(exec, aValue, m_type, &dist); + + if (dist >= 0) { + m_list[index] = val.value<T>(); + } +} + + +template <typename T> JSValue QtArray<T>::valueAt(ExecState *exec, unsigned int index) const +{ + if (index < m_length) { + T val = m_list.at(index); + return convertQVariantToValue(exec, rootObject(), QVariant::fromValue(val)); + } + + return jsUndefined(); +} + +// =============== + +} } diff --git a/Source/WebCore/bridge/qt/qt_runtime.h b/Source/WebCore/bridge/qt/qt_runtime.h new file mode 100644 index 0000000..68bf865 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_runtime.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BINDINGS_QT_RUNTIME_H_ +#define BINDINGS_QT_RUNTIME_H_ + +#include "Bridge.h" +#include "Completion.h" +#include "Protect.h" +#include "runtime_method.h" + +#include <qbytearray.h> +#include <qmetaobject.h> +#include <qpointer.h> +#include <qvariant.h> + +namespace JSC { +namespace Bindings { + +class QtInstance; + +class QtField : public Field { +public: + + typedef enum { + MetaProperty, +#ifndef QT_NO_PROPERTIES + DynamicProperty, +#endif + ChildObject + } QtFieldType; + + QtField(const QMetaProperty &p) + : m_type(MetaProperty), m_property(p) + {} + +#ifndef QT_NO_PROPERTIES + QtField(const QByteArray &b) + : m_type(DynamicProperty), m_dynamicProperty(b) + {} +#endif + + QtField(QObject *child) + : m_type(ChildObject), m_childObject(child) + {} + + virtual JSValue valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const; + QByteArray name() const; + QtFieldType fieldType() const {return m_type;} +private: + QtFieldType m_type; + QByteArray m_dynamicProperty; + QMetaProperty m_property; + QPointer<QObject> m_childObject; +}; + + +class QtMethod : public Method +{ +public: + QtMethod(const QMetaObject *mo, int i, const QByteArray &ident, int numParameters) + : m_metaObject(mo), + m_index(i), + m_identifier(ident), + m_nParams(numParameters) + { } + + virtual const char* name() const { return m_identifier.constData(); } + virtual int numParameters() const { return m_nParams; } + +private: + friend class QtInstance; + const QMetaObject *m_metaObject; + int m_index; + QByteArray m_identifier; + int m_nParams; +}; + + +template <typename T> class QtArray : public Array +{ +public: + QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject>); + virtual ~QtArray(); + + RootObject* rootObject() const; + + virtual void setValueAt(ExecState*, unsigned index, JSValue) const; + virtual JSValue valueAt(ExecState*, unsigned index) const; + virtual unsigned int getLength() const {return m_length;} + +private: + mutable QList<T> m_list; // setValueAt is const! + unsigned int m_length; + QMetaType::Type m_type; +}; + +// Based on RuntimeMethod + +// Extra data classes (to avoid the CELL_SIZE limit on JS objects) + +class QtRuntimeMethodData { + public: + virtual ~QtRuntimeMethodData(); + RefPtr<QtInstance> m_instance; +}; + +class QtRuntimeConnectionMethod; +class QtRuntimeMetaMethodData : public QtRuntimeMethodData { + public: + ~QtRuntimeMetaMethodData(); + QByteArray m_signature; + bool m_allowPrivate; + int m_index; + QtRuntimeConnectionMethod *m_connect; + QtRuntimeConnectionMethod *m_disconnect; +}; + +class QtRuntimeConnectionMethodData : public QtRuntimeMethodData { + public: + ~QtRuntimeConnectionMethodData(); + QByteArray m_signature; + int m_index; + bool m_isConnect; +}; + +// Common base class (doesn't really do anything interesting) +class QtRuntimeMethod : public InternalFunction { +public: + virtual ~QtRuntimeMethod(); + + static const ClassInfo s_info; + + static FunctionPrototype* createPrototype(ExecState*, JSGlobalObject* globalObject) + { + return globalObject->functionPrototype(); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | InternalFunction::StructureFlags | OverridesMarkChildren; + + QtRuntimeMethodData *d_func() const {return d_ptr;} + QtRuntimeMethod(QtRuntimeMethodData *dd, ExecState *exec, const Identifier &n, PassRefPtr<QtInstance> inst); + QtRuntimeMethodData *d_ptr; +}; + +class QtRuntimeMetaMethod : public QtRuntimeMethod +{ +public: + QtRuntimeMetaMethod(ExecState *exec, const Identifier &n, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature, bool allowPrivate); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + + virtual void markChildren(MarkStack& markStack); + +protected: + QtRuntimeMetaMethodData* d_func() const {return reinterpret_cast<QtRuntimeMetaMethodData*>(d_ptr);} + +private: + virtual CallType getCallData(CallData&); + static EncodedJSValue JSC_HOST_CALL call(ExecState* exec); + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + static JSValue connectGetter(ExecState*, JSValue, const Identifier&); + static JSValue disconnectGetter(ExecState*, JSValue, const Identifier&); +}; + +class QtConnectionObject; +class QtRuntimeConnectionMethod : public QtRuntimeMethod +{ +public: + QtRuntimeConnectionMethod(ExecState *exec, const Identifier &n, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature ); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + +protected: + QtRuntimeConnectionMethodData* d_func() const {return reinterpret_cast<QtRuntimeConnectionMethodData*>(d_ptr);} + +private: + virtual CallType getCallData(CallData&); + static EncodedJSValue JSC_HOST_CALL call(ExecState* exec); + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + static QMultiMap<QObject *, QtConnectionObject *> connections; + friend class QtConnectionObject; +}; + +class QtConnectionObject: public QObject +{ +public: + QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject); + ~QtConnectionObject(); + + static const QMetaObject staticMetaObject; + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **argv); + + bool match(QObject *sender, int signalIndex, JSObject* thisObject, JSObject *funcObject); + + // actual slot: + void execute(void **argv); + +private: + RefPtr<QtInstance> m_instance; + int m_signalIndex; + QObject* m_originalObject; // only used as a key, not dereferenced + ProtectedPtr<JSObject> m_thisObject; + ProtectedPtr<JSObject> m_funcObject; +}; + +QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance); +JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant); + +} // namespace Bindings +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/runtime_array.cpp b/Source/WebCore/bridge/runtime_array.cpp new file mode 100644 index 0000000..2d0b7e3 --- /dev/null +++ b/Source/WebCore/bridge/runtime_array.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "runtime_array.h" + +#include <runtime/ArrayPrototype.h> +#include <runtime/Error.h> +#include <runtime/PropertyNameArray.h> +#include "JSDOMBinding.h" + +using namespace WebCore; + +namespace JSC { + +const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &JSArray::info, 0, 0 }; + +RuntimeArray::RuntimeArray(ExecState* exec, Bindings::Array* array) + // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object + // We need to pass in the right global object for "array". + : JSArray(deprecatedGetDOMStructure<RuntimeArray>(exec)) +{ + setSubclassData(array); +} + +RuntimeArray::~RuntimeArray() +{ + delete getConcreteArray(); +} + +JSValue RuntimeArray::lengthGetter(ExecState*, JSValue slotBase, const Identifier&) +{ + RuntimeArray* thisObj = static_cast<RuntimeArray*>(asObject(slotBase)); + return jsNumber(thisObj->getLength()); +} + +JSValue RuntimeArray::indexGetter(ExecState* exec, JSValue slotBase, unsigned index) +{ + RuntimeArray* thisObj = static_cast<RuntimeArray*>(asObject(slotBase)); + return thisObj->getConcreteArray()->valueAt(exec, index); +} + +void RuntimeArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + unsigned length = getLength(); + for (unsigned i = 0; i < length; ++i) + propertyNames.add(Identifier::from(exec, i)); + + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().length); + + JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +bool RuntimeArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCacheableCustom(this, lengthGetter); + return true; + } + + bool ok; + unsigned index = propertyName.toArrayIndex(ok); + if (ok) { + if (index < getLength()) { + slot.setCustomIndex(this, index, indexGetter); + return true; + } + } + + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool RuntimeArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + PropertySlot slot; + slot.setCustom(this, lengthGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); + return true; + } + + bool ok; + unsigned index = propertyName.toArrayIndex(ok); + if (ok) { + if (index < getLength()) { + PropertySlot slot; + slot.setCustomIndex(this, index, indexGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | DontEnum); + return true; + } + } + + return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +bool RuntimeArray::getOwnPropertySlot(ExecState *exec, unsigned index, PropertySlot& slot) +{ + if (index < getLength()) { + slot.setCustomIndex(this, index, indexGetter); + return true; + } + + return JSObject::getOwnPropertySlot(exec, index, slot); +} + +void RuntimeArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + throwError(exec, createRangeError(exec, "Range error")); + return; + } + + bool ok; + unsigned index = propertyName.toArrayIndex(ok); + if (ok) { + getConcreteArray()->setValueAt(exec, index, value); + return; + } + + JSObject::put(exec, propertyName, value, slot); +} + +void RuntimeArray::put(ExecState* exec, unsigned index, JSValue value) +{ + if (index >= getLength()) { + throwError(exec, createRangeError(exec, "Range error")); + return; + } + + getConcreteArray()->setValueAt(exec, index, value); +} + +bool RuntimeArray::deleteProperty(ExecState*, const Identifier&) +{ + return false; +} + +bool RuntimeArray::deleteProperty(ExecState*, unsigned) +{ + return false; +} + +} diff --git a/Source/WebCore/bridge/runtime_array.h b/Source/WebCore/bridge/runtime_array.h new file mode 100644 index 0000000..e301268 --- /dev/null +++ b/Source/WebCore/bridge/runtime_array.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RUNTIME_ARRAY_H_ +#define RUNTIME_ARRAY_H_ + +#include "Bridge.h" +#include <runtime/ArrayPrototype.h> + +namespace JSC { + +class RuntimeArray : public JSArray { +public: + RuntimeArray(ExecState*, Bindings::Array*); + virtual ~RuntimeArray(); + + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertySlot(ExecState*, unsigned, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + virtual void put(ExecState*, unsigned propertyName, JSValue); + + virtual bool deleteProperty(ExecState* exec, const Identifier &propertyName); + virtual bool deleteProperty(ExecState* exec, unsigned propertyName); + + virtual const ClassInfo* classInfo() const { return &s_info; } + + unsigned getLength() const { return getConcreteArray()->getLength(); } + + Bindings::Array* getConcreteArray() const { return static_cast<Bindings::Array*>(subclassData()); } + + static const ClassInfo s_info; + + static ArrayPrototype* createPrototype(ExecState*, JSGlobalObject* globalObject) + { + return globalObject->arrayPrototype(); + } + +private: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + static JSValue indexGetter(ExecState*, JSValue, unsigned); +}; + +} // namespace JSC + +#endif // RUNTIME_ARRAY_H_ diff --git a/Source/WebCore/bridge/runtime_method.cpp b/Source/WebCore/bridge/runtime_method.cpp new file mode 100644 index 0000000..8a61f2e --- /dev/null +++ b/Source/WebCore/bridge/runtime_method.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "runtime_method.h" + +#include "JSDOMBinding.h" +#include "JSHTMLElement.h" +#include "JSPluginElementFunctions.h" +#include "runtime_object.h" +#include <runtime/Error.h> +#include <runtime/FunctionPrototype.h> + +using namespace WebCore; + +namespace JSC { + +using namespace Bindings; + +ASSERT_CLASS_FITS_IN_CELL(RuntimeMethod); + +const ClassInfo RuntimeMethod::s_info = { "RuntimeMethod", &InternalFunction::info, 0, 0 }; + +RuntimeMethod::RuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, const Identifier& ident, Bindings::MethodList& m) + // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object + // exec-globalData() is also likely wrong. + // Callers will need to pass in the right global object corresponding to this native object "m". + : InternalFunction(&exec->globalData(), globalObject, deprecatedGetDOMStructure<RuntimeMethod>(exec), ident) + , _methodList(new MethodList(m)) +{ +} + +JSValue RuntimeMethod::lengthGetter(ExecState*, JSValue slotBase, const Identifier&) +{ + RuntimeMethod* thisObj = static_cast<RuntimeMethod*>(asObject(slotBase)); + + // Ick! There may be more than one method with this name. Arbitrarily + // just pick the first method. The fundamental problem here is that + // JavaScript doesn't have the notion of method overloading and + // Java does. + // FIXME: a better solution might be to give the maximum number of parameters + // of any method + return jsNumber(thisObj->_methodList->at(0)->numParameters()); +} + +bool RuntimeMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCacheableCustom(this, lengthGetter); + return true; + } + + return InternalFunction::getOwnPropertySlot(exec, propertyName, slot); +} + +bool RuntimeMethod::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor &descriptor) +{ + if (propertyName == exec->propertyNames().length) { + PropertySlot slot; + slot.setCustom(this, lengthGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); + return true; + } + + return InternalFunction::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +static EncodedJSValue JSC_HOST_CALL callRuntimeMethod(ExecState* exec) +{ + RuntimeMethod* method = static_cast<RuntimeMethod*>(exec->callee()); + + if (method->methods()->isEmpty()) + return JSValue::encode(jsUndefined()); + + RefPtr<Instance> instance; + + JSValue thisValue = exec->hostThisValue(); + if (thisValue.inherits(&RuntimeObject::s_info)) { + RuntimeObject* runtimeObject = static_cast<RuntimeObject*>(asObject(thisValue)); + instance = runtimeObject->getInternalInstance(); + if (!instance) + return JSValue::encode(RuntimeObject::throwInvalidAccessError(exec)); + } else { + // Calling a runtime object of a plugin element? + if (thisValue.inherits(&JSHTMLElement::s_info)) { + HTMLElement* element = static_cast<JSHTMLElement*>(asObject(thisValue))->impl(); + instance = pluginInstance(element); + } + if (!instance) + return throwVMTypeError(exec); + } + ASSERT(instance); + + instance->begin(); + JSValue result = instance->invokeMethod(exec, method); + instance->end(); + return JSValue::encode(result); +} + +CallType RuntimeMethod::getCallData(CallData& callData) +{ + callData.native.function = callRuntimeMethod; + return CallTypeHost; +} + +} diff --git a/Source/WebCore/bridge/runtime_method.h b/Source/WebCore/bridge/runtime_method.h new file mode 100644 index 0000000..96d12aa --- /dev/null +++ b/Source/WebCore/bridge/runtime_method.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RUNTIME_FUNCTION_H_ +#define RUNTIME_FUNCTION_H_ + +#include "Bridge.h" +#include <runtime/InternalFunction.h> +#include <runtime/JSGlobalObject.h> +#include <wtf/OwnPtr.h> + +namespace JSC { + +class RuntimeMethod : public InternalFunction { +public: + RuntimeMethod(ExecState*, JSGlobalObject*, const Identifier& name, Bindings::MethodList&); + Bindings::MethodList* methods() const { return _methodList.get(); } + + static const ClassInfo s_info; + + static FunctionPrototype* createPrototype(ExecState*, JSGlobalObject* globalObject) + { + return globalObject->functionPrototype(); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + virtual const ClassInfo* classInfo() const { return &s_info; } + +private: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesMarkChildren | InternalFunction::StructureFlags; + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual CallType getCallData(CallData&); + + OwnPtr<Bindings::MethodList> _methodList; +}; + +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/runtime_object.cpp b/Source/WebCore/bridge/runtime_object.cpp new file mode 100644 index 0000000..368f7b0 --- /dev/null +++ b/Source/WebCore/bridge/runtime_object.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2003, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "runtime_object.h" + +#include "JSDOMBinding.h" +#include "runtime_method.h" +#include <runtime/Error.h> +#include <runtime/ObjectPrototype.h> + +using namespace WebCore; + +namespace JSC { +namespace Bindings { + +const ClassInfo RuntimeObject::s_info = { "RuntimeObject", 0, 0, 0 }; + +RuntimeObject::RuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance) + // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object + // We need to pass in the right global object for "i". + : JSObjectWithGlobalObject(globalObject, deprecatedGetDOMStructure<RuntimeObject>(exec)) + , m_instance(instance) +{ +} + +RuntimeObject::RuntimeObject(ExecState*, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, PassRefPtr<Instance> instance) + : JSObjectWithGlobalObject(globalObject, structure) + , m_instance(instance) +{ +} + +RuntimeObject::~RuntimeObject() +{ + if (m_instance) + m_instance->willDestroyRuntimeObject(this); +} + +void RuntimeObject::invalidate() +{ + ASSERT(m_instance); + if (m_instance) + m_instance->willInvalidateRuntimeObject(this); + m_instance = 0; +} + +JSValue RuntimeObject::fallbackObjectGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName) +{ + RuntimeObject* thisObj = static_cast<RuntimeObject*>(asObject(slotBase)); + RefPtr<Instance> instance = thisObj->m_instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + Class *aClass = instance->getClass(); + JSValue result = aClass->fallbackObject(exec, instance.get(), propertyName); + + instance->end(); + + return result; +} + +JSValue RuntimeObject::fieldGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName) +{ + RuntimeObject* thisObj = static_cast<RuntimeObject*>(asObject(slotBase)); + RefPtr<Instance> instance = thisObj->m_instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + Class *aClass = instance->getClass(); + Field* aField = aClass->fieldNamed(propertyName, instance.get()); + JSValue result = aField->valueFromInstance(exec, instance.get()); + + instance->end(); + + return result; +} + +JSValue RuntimeObject::methodGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName) +{ + RuntimeObject* thisObj = static_cast<RuntimeObject*>(asObject(slotBase)); + RefPtr<Instance> instance = thisObj->m_instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + JSValue method = instance->getMethod(exec, propertyName); + + instance->end(); + + return method; +} + +bool RuntimeObject::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (!m_instance) { + throwInvalidAccessError(exec); + return false; + } + + RefPtr<Instance> instance = m_instance; + + instance->begin(); + + Class *aClass = instance->getClass(); + + if (aClass) { + // See if the instance has a field with the specified name. + Field *aField = aClass->fieldNamed(propertyName, instance.get()); + if (aField) { + slot.setCustom(this, fieldGetter); + instance->end(); + return true; + } else { + // Now check if a method with specified name exists, if so return a function object for + // that method. + MethodList methodList = aClass->methodsNamed(propertyName, instance.get()); + if (methodList.size() > 0) { + slot.setCustom(this, methodGetter); + + instance->end(); + return true; + } + } + + // Try a fallback object. + if (!aClass->fallbackObject(exec, instance.get(), propertyName).isUndefined()) { + slot.setCustom(this, fallbackObjectGetter); + instance->end(); + return true; + } + } + + instance->end(); + + return instance->getOwnPropertySlot(this, exec, propertyName, slot); +} + +bool RuntimeObject::getOwnPropertyDescriptor(ExecState *exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (!m_instance) { + throwInvalidAccessError(exec); + return false; + } + + RefPtr<Instance> instance = m_instance; + instance->begin(); + + Class *aClass = instance->getClass(); + + if (aClass) { + // See if the instance has a field with the specified name. + Field *aField = aClass->fieldNamed(propertyName, instance.get()); + if (aField) { + PropertySlot slot; + slot.setCustom(this, fieldGetter); + instance->end(); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete); + return true; + } else { + // Now check if a method with specified name exists, if so return a function object for + // that method. + MethodList methodList = aClass->methodsNamed(propertyName, instance.get()); + if (methodList.size() > 0) { + PropertySlot slot; + slot.setCustom(this, methodGetter); + instance->end(); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly); + return true; + } + } + + // Try a fallback object. + if (!aClass->fallbackObject(exec, instance.get(), propertyName).isUndefined()) { + PropertySlot slot; + slot.setCustom(this, fallbackObjectGetter); + instance->end(); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + } + + instance->end(); + + return instance->getOwnPropertyDescriptor(this, exec, propertyName, descriptor); +} + +void RuntimeObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + if (!m_instance) { + throwInvalidAccessError(exec); + return; + } + + RefPtr<Instance> instance = m_instance; + instance->begin(); + + // Set the value of the property. + Field *aField = instance->getClass()->fieldNamed(propertyName, instance.get()); + if (aField) + aField->setValueToInstance(exec, instance.get(), value); + else if (!instance->setValueOfUndefinedField(exec, propertyName, value)) + instance->put(this, exec, propertyName, value, slot); + + instance->end(); +} + +bool RuntimeObject::deleteProperty(ExecState*, const Identifier&) +{ + // Can never remove a property of a RuntimeObject. + return false; +} + +JSValue RuntimeObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (!m_instance) + return throwInvalidAccessError(exec); + + RefPtr<Instance> instance = m_instance; + + instance->begin(); + JSValue result = instance->defaultValue(exec, hint); + instance->end(); + return result; +} + +static EncodedJSValue JSC_HOST_CALL callRuntimeObject(ExecState* exec) +{ + ASSERT(exec->callee()->inherits(&RuntimeObject::s_info)); + RefPtr<Instance> instance(static_cast<RuntimeObject*>(exec->callee())->getInternalInstance()); + instance->begin(); + JSValue result = instance->invokeDefaultMethod(exec); + instance->end(); + return JSValue::encode(result); +} + +CallType RuntimeObject::getCallData(CallData& callData) +{ + if (!m_instance) + return CallTypeNone; + + RefPtr<Instance> instance = m_instance; + if (!instance->supportsInvokeDefaultMethod()) + return CallTypeNone; + + callData.native.function = callRuntimeObject; + return CallTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callRuntimeConstructor(ExecState* exec) +{ + JSObject* constructor = exec->callee(); + ASSERT(constructor->inherits(&RuntimeObject::s_info)); + RefPtr<Instance> instance(static_cast<RuntimeObject*>(exec->callee())->getInternalInstance()); + instance->begin(); + ArgList args(exec); + JSValue result = instance->invokeConstruct(exec, args); + instance->end(); + + ASSERT(result); + return JSValue::encode(result.isObject() ? static_cast<JSObject*>(result.asCell()) : constructor); +} + +ConstructType RuntimeObject::getConstructData(ConstructData& constructData) +{ + if (!m_instance) + return ConstructTypeNone; + + RefPtr<Instance> instance = m_instance; + if (!instance->supportsConstruct()) + return ConstructTypeNone; + + constructData.native.function = callRuntimeConstructor; + return ConstructTypeHost; +} + +void RuntimeObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode) +{ + if (!m_instance) { + throwInvalidAccessError(exec); + return; + } + + RefPtr<Instance> instance = m_instance; + + instance->begin(); + instance->getPropertyNames(exec, propertyNames); + instance->end(); +} + +JSObject* RuntimeObject::throwInvalidAccessError(ExecState* exec) +{ + return throwError(exec, createReferenceError(exec, "Trying to access object from destroyed plug-in.")); +} + +} +} diff --git a/Source/WebCore/bridge/runtime_object.h b/Source/WebCore/bridge/runtime_object.h new file mode 100644 index 0000000..64c8049 --- /dev/null +++ b/Source/WebCore/bridge/runtime_object.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2003, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KJS_RUNTIME_OBJECT_H +#define KJS_RUNTIME_OBJECT_H + +#include "Bridge.h" +#include <runtime/JSGlobalObject.h> +#include <runtime/JSObjectWithGlobalObject.h> + +namespace JSC { +namespace Bindings { + +class RuntimeObject : public JSObjectWithGlobalObject { +public: + RuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>); + virtual ~RuntimeObject(); + + virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + virtual CallType getCallData(CallData&); + virtual ConstructType getConstructData(ConstructData&); + + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + + void invalidate(); + + Instance* getInternalInstance() const { return m_instance.get(); } + + static JSObject* throwInvalidAccessError(ExecState*); + + static const ClassInfo s_info; + + static ObjectPrototype* createPrototype(ExecState*, JSGlobalObject* globalObject) + { + return globalObject->objectPrototype(); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; + RuntimeObject(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, PassRefPtr<Instance>); + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } + + static JSValue fallbackObjectGetter(ExecState*, JSValue, const Identifier&); + static JSValue fieldGetter(ExecState*, JSValue, const Identifier&); + static JSValue methodGetter(ExecState*, JSValue, const Identifier&); + + RefPtr<Instance> m_instance; +}; + +} +} + +#endif diff --git a/Source/WebCore/bridge/runtime_root.cpp b/Source/WebCore/bridge/runtime_root.cpp new file mode 100644 index 0000000..796354f --- /dev/null +++ b/Source/WebCore/bridge/runtime_root.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "runtime_root.h" + +#include "Bridge.h" +#include "runtime_object.h" +#include <runtime/JSGlobalObject.h> +#include <wtf/HashCountedSet.h> +#include <wtf/HashSet.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { namespace Bindings { + +// This code attempts to solve two problems: (1) plug-ins leaking references to +// JS and the DOM; (2) plug-ins holding stale references to JS and the DOM. Previous +// comments in this file claimed that problem #1 was an issue in Java, in particular, +// because Java, allegedly, didn't always call finalize when collecting an object. + +typedef HashSet<RootObject*> RootObjectSet; + +static RootObjectSet* rootObjectSet() +{ + DEFINE_STATIC_LOCAL(RootObjectSet, staticRootObjectSet, ()); + return &staticRootObjectSet; +} + +// FIXME: These two functions are a potential performance problem. We could +// fix them by adding a JSObject to RootObject dictionary. + +RootObject* findProtectingRootObject(JSObject* jsObject) +{ + RootObjectSet::const_iterator end = rootObjectSet()->end(); + for (RootObjectSet::const_iterator it = rootObjectSet()->begin(); it != end; ++it) { + if ((*it)->gcIsProtected(jsObject)) + return *it; + } + return 0; +} + +RootObject* findRootObject(JSGlobalObject* globalObject) +{ + RootObjectSet::const_iterator end = rootObjectSet()->end(); + for (RootObjectSet::const_iterator it = rootObjectSet()->begin(); it != end; ++it) { + if ((*it)->globalObject() == globalObject) + return *it; + } + return 0; +} + +RootObject::InvalidationCallback::~InvalidationCallback() +{ +} + +PassRefPtr<RootObject> RootObject::create(const void* nativeHandle, JSGlobalObject* globalObject) +{ + return adoptRef(new RootObject(nativeHandle, globalObject)); +} + +RootObject::RootObject(const void* nativeHandle, JSGlobalObject* globalObject) + : m_isValid(true) + , m_nativeHandle(nativeHandle) + , m_globalObject(globalObject) +{ + ASSERT(globalObject); + rootObjectSet()->add(this); +} + +RootObject::~RootObject() +{ + if (m_isValid) + invalidate(); +} + +void RootObject::invalidate() +{ + if (!m_isValid) + return; + + { + HashSet<RuntimeObject*>::iterator end = m_runtimeObjects.end(); + for (HashSet<RuntimeObject*>::iterator it = m_runtimeObjects.begin(); it != end; ++it) + (*it)->invalidate(); + + m_runtimeObjects.clear(); + } + + m_isValid = false; + + m_nativeHandle = 0; + m_globalObject = 0; + + { + HashSet<InvalidationCallback*>::iterator end = m_invalidationCallbacks.end(); + for (HashSet<InvalidationCallback*>::iterator iter = m_invalidationCallbacks.begin(); iter != end; ++iter) + (**iter)(this); + + m_invalidationCallbacks.clear(); + } + + ProtectCountSet::iterator end = m_protectCountSet.end(); + for (ProtectCountSet::iterator it = m_protectCountSet.begin(); it != end; ++it) + JSC::gcUnprotect(it->first); + m_protectCountSet.clear(); + + rootObjectSet()->remove(this); +} + +void RootObject::gcProtect(JSObject* jsObject) +{ + ASSERT(m_isValid); + + if (!m_protectCountSet.contains(jsObject)) + JSC::gcProtect(jsObject); + m_protectCountSet.add(jsObject); +} + +void RootObject::gcUnprotect(JSObject* jsObject) +{ + ASSERT(m_isValid); + + if (!jsObject) + return; + + if (m_protectCountSet.count(jsObject) == 1) + JSC::gcUnprotect(jsObject); + m_protectCountSet.remove(jsObject); +} + +bool RootObject::gcIsProtected(JSObject* jsObject) +{ + ASSERT(m_isValid); + return m_protectCountSet.contains(jsObject); +} + +const void* RootObject::nativeHandle() const +{ + ASSERT(m_isValid); + return m_nativeHandle; +} + +JSGlobalObject* RootObject::globalObject() const +{ + ASSERT(m_isValid); + return m_globalObject; +} + +void RootObject::updateGlobalObject(JSGlobalObject* globalObject) +{ + m_globalObject = globalObject; +} + +void RootObject::addRuntimeObject(RuntimeObject* object) +{ + ASSERT(m_isValid); + ASSERT(!m_runtimeObjects.contains(object)); + + m_runtimeObjects.add(object); +} + +void RootObject::removeRuntimeObject(RuntimeObject* object) +{ + ASSERT(m_isValid); + ASSERT(m_runtimeObjects.contains(object)); + + m_runtimeObjects.remove(object); +} + +} } // namespace JSC::Bindings diff --git a/Source/WebCore/bridge/runtime_root.h b/Source/WebCore/bridge/runtime_root.h new file mode 100644 index 0000000..babd7ad --- /dev/null +++ b/Source/WebCore/bridge/runtime_root.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RUNTIME_ROOT_H_ +#define RUNTIME_ROOT_H_ + +#if PLATFORM(MAC) +#include "jni_jsobject.h" +#endif +#include <runtime/Protect.h> + +#include <wtf/Forward.h> +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace JSC { + +class Interpreter; +class JSGlobalObject; + +namespace Bindings { + +class RootObject; +class RuntimeObject; + +typedef HashCountedSet<JSObject*> ProtectCountSet; + +extern RootObject* findProtectingRootObject(JSObject*); +extern RootObject* findRootObject(JSGlobalObject*); + +class RootObject : public RefCounted<RootObject> { + friend class JavaJSObject; + +public: + ~RootObject(); + + static PassRefPtr<RootObject> create(const void* nativeHandle, JSGlobalObject*); + + bool isValid() { return m_isValid; } + void invalidate(); + + void gcProtect(JSObject*); + void gcUnprotect(JSObject*); + bool gcIsProtected(JSObject*); + + const void* nativeHandle() const; + JSGlobalObject* globalObject() const; + void updateGlobalObject(JSGlobalObject*); + + void addRuntimeObject(RuntimeObject*); + void removeRuntimeObject(RuntimeObject*); + + struct InvalidationCallback { + virtual void operator()(RootObject*) = 0; + virtual ~InvalidationCallback(); + }; + void addInvalidationCallback(InvalidationCallback* callback) { m_invalidationCallbacks.add(callback); } + +private: + RootObject(const void* nativeHandle, JSGlobalObject*); + + bool m_isValid; + + const void* m_nativeHandle; + ProtectedPtr<JSGlobalObject> m_globalObject; + + ProtectCountSet m_protectCountSet; + HashSet<RuntimeObject*> m_runtimeObjects; + + HashSet<InvalidationCallback*> m_invalidationCallbacks; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // RUNTIME_ROOT_H_ diff --git a/Source/WebCore/bridge/test.js b/Source/WebCore/bridge/test.js new file mode 100644 index 0000000..5d4f79f --- /dev/null +++ b/Source/WebCore/bridge/test.js @@ -0,0 +1,19 @@ +myInterface.logMessage ("Starting test"); + +myInterface.logMessage ("Testing properties:"); +myInterface.logMessage ("myInterface.doubleValue = " + myInterface.doubleValue); +myInterface.logMessage ("myInterface.intValue = " + myInterface.intValue); +myInterface.logMessage ("myInterface.stringValue = " + myInterface.stringValue); +myInterface.logMessage ("myInterface.booleanValue = " + myInterface.booleanValue); +myInterface.logMessage ("myInterface.nullValue = " + myInterface.nullValue); +myInterface.logMessage ("myInterface.undefinedValue = " + myInterface.undefinedValue); + +myInterface.logMessage ("myInterface.setInt_(666) = " + myInterface.setInt_(666)); +myInterface.logMessage ("myInterface.getInt() = " + myInterface.getInt()); +myInterface.logMessage ("myInterface.getString() = " + myInterface.getString()); +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("setting myInterface.myInt = 777"); +myInterface.myInt = 777; +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("myInterface.getMySecondInterface().doubleValue = " + myInterface.getMySecondInterface().doubleValue); +myInterface.logMessage ("myInterface.getMySecondInterface() = " + myInterface.getMySecondInterface()); diff --git a/Source/WebCore/bridge/testC.js b/Source/WebCore/bridge/testC.js new file mode 100644 index 0000000..44677c7 --- /dev/null +++ b/Source/WebCore/bridge/testC.js @@ -0,0 +1,21 @@ +myInterface.logMessage ("Starting test"); + +myInterface.logMessage ("Testing properties:"); +myInterface.logMessage (" myInterface.doubleValue = " + myInterface.doubleValue); +myInterface.logMessage (" myInterface.intValue = " + myInterface.intValue); +myInterface.logMessage (" myInterface.stringValue = " + myInterface.stringValue); +myInterface.logMessage (" myInterface.booleanValue = " + myInterface.booleanValue); +myInterface.logMessage (" myInterface.nullValue = " + myInterface.nullValue); +myInterface.logMessage (" myInterface.undefinedValue = " + myInterface.undefinedValue); + +myInterface.logMessage ("Testing methods:"); +myInterface.logMessage (" myInterface.setDoubleValue(1234.1234) = " + myInterface.setDoubleValue(1234.1234)); +myInterface.logMessage (" myInterface.setIntValue(5678) = " + myInterface.setIntValue(5678)); +myInterface.logMessage (" myInterface.setStringValue(Goodbye) = " + myInterface.setStringValue('Goodbye')); +myInterface.logMessage (" myInterface.setBooleanValue(false) = " + myInterface.setBooleanValue(false)); + +myInterface.logMessage ("Value of properties after calling setters:"); +myInterface.logMessage (" myInterface.getDoubleValue() = " + myInterface.getDoubleValue()); +myInterface.logMessage (" myInterface.getIntValue() = " + myInterface.getIntValue()); +myInterface.logMessage (" myInterface.getStringValue() = " + myInterface.getStringValue()); +myInterface.logMessage (" myInterface.getBooleanValue() = " + myInterface.getBooleanValue()); diff --git a/Source/WebCore/bridge/testM.js b/Source/WebCore/bridge/testM.js new file mode 100644 index 0000000..7985d21 --- /dev/null +++ b/Source/WebCore/bridge/testM.js @@ -0,0 +1,29 @@ +myInterface.logMessage ("Starting test"); + +myInterface.logMessage ("Testing properties:"); + +myInterface.jsobject = new Function ("arg1","arg2","return arg1 + arg2;"); +myInterface.logMessage ("myInterface.jsobject =" + myInterface.jsobject); + +var functionBody = 'return arg1*arg2;' + +myInterface.setJSObject_(new Function ("arg1","arg2",functionBody)); +myInterface.logMessage ("myInterface.jsobject =" + myInterface.jsobject); +myInterface.callJSObject__(5,6); +myInterface.callJSObject__(8,9); + +myInterface.logMessage ("myInterface.setInt_(666) = " + myInterface.setInt_(666)); +myInterface.logMessage ("myInterface.getInt() = " + myInterface.getInt()); +myInterface.logMessage ("myInterface.getString().foo() = " + myInterface.getString().foo()); +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("setting myInterface.myInt = 777"); +myInterface.myInt = 777; +myInterface.logMessage ("myInterface.myInt = " + myInterface.myInt); +myInterface.logMessage ("myInterface.getMySecondInterface().doubleValue = " + myInterface.getMySecondInterface().doubleValue); +myInterface.logMessage ("myInterface.getMySecondInterface() = " + myInterface.getMySecondInterface()); + +myInterface.logMessageWithPrefix ("msg", "prefix"); + +var strings = [ "one", "two", "three" ]; + +myInterface.logMessages (strings);
\ No newline at end of file diff --git a/Source/WebCore/bridge/testbindings.cpp b/Source/WebCore/bridge/testbindings.cpp new file mode 100644 index 0000000..bcba115 --- /dev/null +++ b/Source/WebCore/bridge/testbindings.cpp @@ -0,0 +1,419 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#include "config.h" + +#include "Bridge.h" +#include "JSObject.h" +#include "JSValue.h" +#include "interpreter.h" +#include "npruntime_internal.h" +#include "runtime_object.h" +#include "types.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> + + +#define LOG(formatAndArgs...) { \ + fprintf (stderr, "%s: ", __PRETTY_FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} + + +// ------------------ NP Interface definition -------------------- +typedef struct +{ + NPObject object; + double doubleValue; + int intValue; + NPVariant stringValue; + bool boolValue; +} MyObject; + + +static bool identifiersInitialized = false; + +#define ID_DOUBLE_VALUE 0 +#define ID_INT_VALUE 1 +#define ID_STRING_VALUE 2 +#define ID_BOOLEAN_VALUE 3 +#define ID_NULL_VALUE 4 +#define ID_UNDEFINED_VALUE 5 +#define NUM_PROPERTY_IDENTIFIERS 6 + +static NPIdentifier myPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS]; +static const NPUTF8 *myPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = { + "doubleValue", + "intValue", + "stringValue", + "booleanValue", + "nullValue", + "undefinedValue" +}; + +#define ID_LOG_MESSAGE 0 +#define ID_SET_DOUBLE_VALUE 1 +#define ID_SET_INT_VALUE 2 +#define ID_SET_STRING_VALUE 3 +#define ID_SET_BOOLEAN_VALUE 4 +#define ID_GET_DOUBLE_VALUE 5 +#define ID_GET_INT_VALUE 6 +#define ID_GET_STRING_VALUE 7 +#define ID_GET_BOOLEAN_VALUE 8 +#define NUM_METHOD_IDENTIFIERS 9 + +static NPIdentifier myMethodIdentifiers[NUM_METHOD_IDENTIFIERS]; +static const NPUTF8 *myMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = { + "logMessage", + "setDoubleValue", + "setIntValue", + "setStringValue", + "setBooleanValue", + "getDoubleValue", + "getIntValue", + "getStringValue", + "getBooleanValue" +}; + +static void initializeIdentifiers() +{ + NPN_GetStringIdentifiers (myPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, myPropertyIdentifiers); + NPN_GetStringIdentifiers (myMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, myMethodIdentifiers); +}; + +bool myHasProperty (NPClass *theClass, NPIdentifier name) +{ + int i; + for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++) { + if (name == myPropertyIdentifiers[i]){ + return true; + } + } + return false; +} + +bool myHasMethod (NPClass *theClass, NPIdentifier name) +{ + int i; + for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++) { + if (name == myMethodIdentifiers[i]){ + return true; + } + } + return false; +} + + +void logMessage (const NPVariant *message) +{ + if (message->type == NPVariantStringType) { + char msgBuf[1024]; + strncpy (msgBuf, message->value.stringValue.UTF8Characters, message->value.stringValue.UTF8Length); + msgBuf[message->value.stringValue.UTF8Length] = 0; + printf ("%s\n", msgBuf); + } + else if (message->type == NPVariantDoubleType) + printf ("%f\n", (float)message->value.doubleValue); + else if (message->type == NPVariantInt32Type) + printf ("%d\n", message->value.intValue); + else if (message->type == NPVariantObjectType) + printf ("%p\n", message->value.objectValue); +} + +void setDoubleValue (MyObject *obj, const NPVariant *variant) +{ + if (!NPN_VariantToDouble (variant, &obj->doubleValue)) { + NPUTF8 *msg = "Attempt to set double value with invalid type."; + NPString aString; + aString.UTF8Characters = msg; + aString.UTF8Length = strlen (msg); + NPN_SetException ((NPObject *)obj, &aString); + } +} + +void setIntValue (MyObject *obj, const NPVariant *variant) +{ + if (!NPN_VariantToInt32 (variant, &obj->intValue)) { + NPUTF8 *msg = "Attempt to set int value with invalid type."; + NPString aString; + aString.UTF8Characters = msg; + aString.UTF8Length = strlen (msg); + NPN_SetException ((NPObject *)obj, &aString); + } +} + +void setStringValue (MyObject *obj, const NPVariant *variant) +{ + NPN_ReleaseVariantValue (&obj->stringValue); + NPN_InitializeVariantWithVariant (&obj->stringValue, variant); +} + +void setBooleanValue (MyObject *obj, const NPVariant *variant) +{ + if (!NPN_VariantToBool (variant, (NPBool *)&obj->boolValue)) { + NPUTF8 *msg = "Attempt to set bool value with invalid type."; + NPString aString; + aString.UTF8Characters = msg; + aString.UTF8Length = strlen (msg); + NPN_SetException ((NPObject *)obj, &aString); + } +} + +void getDoubleValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithDouble (variant, obj->doubleValue); +} + +void getIntValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithInt32 (variant, obj->intValue); +} + +void getStringValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithVariant (variant, &obj->stringValue); +} + +void getBooleanValue (MyObject *obj, NPVariant *variant) +{ + NPN_InitializeVariantWithBool (variant, obj->boolValue); +} + +void myGetProperty (MyObject *obj, NPIdentifier name, NPVariant *variant) +{ + if (name == myPropertyIdentifiers[ID_DOUBLE_VALUE]){ + getDoubleValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_INT_VALUE]){ + getIntValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_STRING_VALUE]){ + getStringValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_BOOLEAN_VALUE]){ + getBooleanValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_NULL_VALUE]){ + return NPN_InitializeVariantAsNull (variant); + } + else if (name == myPropertyIdentifiers[ID_UNDEFINED_VALUE]){ + return NPN_InitializeVariantAsUndefined (variant); + } + else + NPN_InitializeVariantAsUndefined(variant); +} + +void mySetProperty (MyObject *obj, NPIdentifier name, const NPVariant *variant) +{ + if (name == myPropertyIdentifiers[ID_DOUBLE_VALUE]) { + setDoubleValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_INT_VALUE]) { + setIntValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_STRING_VALUE]) { + setStringValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_BOOLEAN_VALUE]) { + setBooleanValue (obj, variant); + } + else if (name == myPropertyIdentifiers[ID_NULL_VALUE]) { + // Do nothing! + } + else if (name == myPropertyIdentifiers[ID_UNDEFINED_VALUE]) { + // Do nothing! + } +} + +void myInvoke (MyObject *obj, NPIdentifier name, NPVariant *args, unsigned argCount, NPVariant *result) +{ + if (name == myMethodIdentifiers[ID_LOG_MESSAGE]) { + if (argCount == 1 && NPN_VariantIsString(&args[0])) + logMessage (&args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_DOUBLE_VALUE]) { + if (argCount == 1 && NPN_VariantIsDouble (&args[0])) + setDoubleValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_INT_VALUE]) { + if (argCount == 1 && (NPN_VariantIsDouble (&args[0]) || NPN_VariantIsInt32 (&args[0]))) + setIntValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_STRING_VALUE]) { + if (argCount == 1 && NPN_VariantIsString (&args[0])) + setStringValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_SET_BOOLEAN_VALUE]) { + if (argCount == 1 && NPN_VariantIsBool (&args[0])) + setBooleanValue (obj, &args[0]); + NPN_InitializeVariantAsVoid (result); + } + else if (name == myMethodIdentifiers[ID_GET_DOUBLE_VALUE]) { + getDoubleValue (obj, result); + } + else if (name == myMethodIdentifiers[ID_GET_INT_VALUE]) { + getIntValue (obj, result); + } + else if (name == myMethodIdentifiers[ID_GET_STRING_VALUE]) { + getStringValue (obj, result); + } + else if (name == myMethodIdentifiers[ID_GET_BOOLEAN_VALUE]) { + getBooleanValue (obj, result); + } + else + NPN_InitializeVariantAsUndefined (result); +} + +NPObject *myAllocate () +{ + MyObject *newInstance = (MyObject *)malloc (sizeof(MyObject)); + + if (!identifiersInitialized) { + identifiersInitialized = true; + initializeIdentifiers(); + } + + + newInstance->doubleValue = 666.666; + newInstance->intValue = 1234; + newInstance->boolValue = true; + newInstance->stringValue.type = NPVariantType_String; + newInstance->stringValue.value.stringValue.UTF8Length = strlen ("Hello world"); + newInstance->stringValue.value.stringValue.UTF8Characters = strdup ("Hello world"); + + return (NPObject *)newInstance; +} + +void myInvalidate () +{ + // Make sure we've released any remaining references to JavaScript objects. +} + +void myDeallocate (MyObject *obj) +{ + free ((void *)obj); +} + +static NPClass _myFunctionPtrs = { + kNPClassStructVersionCurrent, + (NPAllocateFunctionPtr) myAllocate, + (NPDeallocateFunctionPtr) myDeallocate, + (NPInvalidateFunctionPtr) myInvalidate, + (NPHasMethodFunctionPtr) myHasMethod, + (NPInvokeFunctionPtr) myInvoke, + (NPHasPropertyFunctionPtr) myHasProperty, + (NPGetPropertyFunctionPtr) myGetProperty, + (NPSetPropertyFunctionPtr) mySetProperty, +}; +static NPClass *myFunctionPtrs = &_myFunctionPtrs; + +// -------------------------------------------------------- + +using namespace JSC; +using namespace JSC::Bindings; + +class GlobalImp : public ObjectImp { +public: + virtual UString className() const { return "global"; } +}; + +#define BufferSize 200000 +static char code[BufferSize]; + +const char *readJavaScriptFromFile (const char *file) +{ + FILE *f = fopen(file, "r"); + if (!f) { + fprintf(stderr, "Error opening %s.\n", file); + return 0; + } + + int num = fread(code, 1, BufferSize, f); + code[num] = '\0'; + if(num >= BufferSize) + fprintf(stderr, "Warning: File may have been too long.\n"); + + fclose(f); + + return code; +} + +int main(int argc, char **argv) +{ + // expecting a filename + if (argc < 2) { + fprintf(stderr, "You have to specify at least one filename\n"); + return -1; + } + + bool ret = true; + { + JSLock lock; + + // create interpreter w/ global object + Object global(new GlobalImp()); + Interpreter interp; + interp.setGlobalObject(global); + ExecState *exec = interp.globalExec(); + + MyObject *myObject = (MyObject *)NPN_CreateObject (myFunctionPtrs); + + global.put(exec, Identifier("myInterface"), Instance::createRuntimeObject(Instance::CLanguage, (void *)myObject)); + + for (int i = 1; i < argc; i++) { + const char *code = readJavaScriptFromFile(argv[i]); + + if (code) { + // run + Completion comp(interp.evaluate(code)); + + if (comp.complType() == Throw) { + Value exVal = comp.value(); + char *msg = exVal.toString(exec).ascii(); + int lineno = -1; + if (exVal.type() == ObjectType) { + Value lineVal = Object::dynamicCast(exVal).get(exec,Identifier("line")); + if (lineVal.type() == NumberType) + lineno = int(lineVal.toNumber(exec)); + } + if (lineno != -1) + fprintf(stderr,"Exception, line %d: %s\n",lineno,msg); + else + fprintf(stderr,"Exception: %s\n",msg); + ret = false; + } + else if (comp.complType() == ReturnValue) { + char *msg = comp.value().toString(interp.globalExec()).ascii(); + fprintf(stderr,"Return value: %s\n",msg); + } + } + } + + NPN_ReleaseObject ((NPObject *)myObject); + + } // end block, so that Interpreter and global get deleted + + return ret ? 0 : 3; +} diff --git a/Source/WebCore/bridge/testbindings.mm b/Source/WebCore/bridge/testbindings.mm new file mode 100644 index 0000000..31564a8 --- /dev/null +++ b/Source/WebCore/bridge/testbindings.mm @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "Bridge.h" +#include <Foundation/Foundation.h> +#include "JSObject.h" +#include "JSValue.h" +#import <WebKit/WebScriptObject.h> +#include "interpreter.h" +#include "runtime_object.h" +#include <stdio.h> +#include <string.h> +#include "types.h" + + +#define LOG(formatAndArgs...) { \ + fprintf (stderr, "%s: ", __PRETTY_FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} + +@interface MySecondInterface : NSObject +{ + double doubleValue; +} + +- init; + +@end + +@implementation MySecondInterface + +- init +{ + LOG ("\n"); + doubleValue = 666.666; + return self; +} + +@end + +@interface MyFirstInterface : NSObject +{ + int myInt; + MySecondInterface *mySecondInterface; + id jsobject; + NSString *string; +} + +- (int)getInt; +- (void)setInt: (int)anInt; +- (MySecondInterface *)getMySecondInterface; +- (void)logMessage:(NSString *)message; +- (void)setJSObject:(id)jsobject; +@end + +@implementation MyFirstInterface + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(logMessage:)) + return @"logMessage"; + if (aSelector == @selector(logMessages:)) + return @"logMessages"; + if (aSelector == @selector(logMessage:prefix:)) + return @"logMessageWithPrefix"; + return nil; +} + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return NO; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name +{ + return NO; +} + +/* +- (id)invokeUndefinedMethodFromWebScript:(NSString *)name withArguments:(NSArray *)args +{ + NSLog (@"Call to undefined method %@", name); + NSLog (@"%d args\n", [args count]); + int i; + for (i = 0; i < [args count]; i++) { + NSLog (@"%d: %@\n", i, [args objectAtIndex:i]); + } + return @"success"; +} +*/ + +/* +- (id)valueForUndefinedKey:(NSString *)key +{ + NSLog (@"%s: key = %@", __PRETTY_FUNCTION__, key); + return @"aValue"; +} +*/ + +- (void)setValue:(id)value forUndefinedKey:(NSString *)key +{ + NSLog (@"%s: key = %@", __PRETTY_FUNCTION__, key); +} + +- init +{ + LOG ("\n"); + mySecondInterface = [[MySecondInterface alloc] init]; + return self; +} + +- (void)dealloc +{ + LOG ("\n"); + [mySecondInterface release]; + [super dealloc]; +} + +- (int)getInt +{ + LOG ("myInt = %d\n", myInt); + return myInt; +} + +- (void)setInt: (int)anInt +{ + LOG ("anInt = %d\n", anInt); + myInt = anInt; +} + +- (NSString *)getString +{ + return string; +} + +- (MySecondInterface *)getMySecondInterface +{ + LOG ("\n"); + return mySecondInterface; +} + +- (void)logMessage:(NSString *)message +{ + printf ("%s\n", [message lossyCString]); +} + +- (void)logMessages:(id)messages +{ + int i, count = [[messages valueForKey:@"length"] intValue]; + for (i = 0; i < count; i++) + printf ("%s\n", [[messages webScriptValueAtIndex:i] lossyCString]); +} + +- (void)logMessage:(NSString *)message prefix:(NSString *)prefix +{ + printf ("%s:%s\n", [prefix lossyCString], [message lossyCString]); +} + +- (void)setJSObject:(id)jso +{ + [jsobject autorelease]; + jsobject = [jso retain]; +} + +- (void)callJSObject:(int)arg1 :(int)arg2 +{ + id foo1 = [jsobject callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:jsobject, [NSNumber numberWithInt:arg1], [NSNumber numberWithInt:arg2], nil]]; + printf ("foo (via call) = %s\n", [[foo1 description] lossyCString] ); + id foo2 = [jsobject callWebScriptMethod:@"apply" withArguments:[NSArray arrayWithObjects:jsobject, [NSArray arrayWithObjects:[NSNumber numberWithInt:arg1], [NSNumber numberWithInt:arg2], nil], nil]]; + printf ("foo (via apply) = %s\n", [[foo2 description] lossyCString] ); +} + +@end + + +using namespace JSC; +using namespace JSC::Bindings; + +class GlobalImp : public ObjectImp { +public: + virtual UString className() const { return "global"; } +}; + +#define BufferSize 200000 +static char code[BufferSize]; + +const char *readJavaScriptFromFile (const char *file) +{ + FILE *f = fopen(file, "r"); + if (!f) { + fprintf(stderr, "Error opening %s.\n", file); + return 0; + } + + int num = fread(code, 1, BufferSize, f); + code[num] = '\0'; + if(num >= BufferSize) + fprintf(stderr, "Warning: File may have been too long.\n"); + + fclose(f); + + return code; +} + +int main(int argc, char **argv) +{ + // expecting a filename + if (argc < 2) { + fprintf(stderr, "You have to specify at least one filename\n"); + return -1; + } + + bool ret = true; + { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + JSLock lock; + + // create interpreter w/ global object + Object global(new GlobalImp()); + Interpreter interp; + interp.setGlobalObject(global); + ExecState *exec = interp.globalExec(); + + MyFirstInterface *myInterface = [[MyFirstInterface alloc] init]; + + global.put(exec, Identifier("myInterface"), Instance::createRuntimeObject(Instance::ObjectiveCLanguage, (void *)myInterface)); + + for (int i = 1; i < argc; i++) { + const char *code = readJavaScriptFromFile(argv[i]); + + if (code) { + // run + Completion comp(interp.evaluate(code)); + + if (comp.complType() == Throw) { + Value exVal = comp.value(); + char *msg = exVal.toString(exec).ascii(); + int lineno = -1; + if (exVal.type() == ObjectType) { + Value lineVal = Object::dynamicCast(exVal).get(exec,Identifier("line")); + if (lineVal.type() == NumberType) + lineno = int(lineVal.toNumber(exec)); + } + if (lineno != -1) + fprintf(stderr,"Exception, line %d: %s\n",lineno,msg); + else + fprintf(stderr,"Exception: %s\n",msg); + ret = false; + } + else if (comp.complType() == ReturnValue) { + char *msg = comp.value().toString(interp.globalExec()).ascii(); + fprintf(stderr,"Return value: %s\n",msg); + } + } + } + + [myInterface release]; + [pool drain]; + } // end block, so that Interpreter and global get deleted + + return ret ? 0 : 3; +} diff --git a/Source/WebCore/bridge/testbindings.pro b/Source/WebCore/bridge/testbindings.pro new file mode 100644 index 0000000..a854d4f --- /dev/null +++ b/Source/WebCore/bridge/testbindings.pro @@ -0,0 +1,7 @@ +QT -= gui + +include(../../WebKit.pri) +INCLUDEPATH += .. ../ . bindings/qt + +SOURCES += testqtbindings.cpp + diff --git a/Source/WebCore/bridge/testqtbindings.cpp b/Source/WebCore/bridge/testqtbindings.cpp new file mode 100644 index 0000000..73df155 --- /dev/null +++ b/Source/WebCore/bridge/testqtbindings.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" + +#include "Bridge.h" +#include "JSObject.h" +#include "JSValue.h" +#include "interpreter.h" +#include "qdebug.h" +#include "qobject.h" +#include "runtime_object.h" +#include "types.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> + + +class MyObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString testString READ testString WRITE setTestString) + Q_PROPERTY(int testInt READ testInt WRITE setTestInt) + +public: + MyObject() : QObject(0), integer(0){} + + void setTestString(const QString &str) { + qDebug() << "called setTestString" << str; + string = str; + } + void setTestInt(int i) { + qDebug() << "called setTestInt" << i; + integer = i; + } + QString testString() const { + qDebug() << "called testString" << string; + return string; + } + int testInt() const { + qDebug() << "called testInt" << integer; + return integer; + } + QString string; + int integer; + +public slots: + void foo() { qDebug() << "foo invoked"; } +}; + +// -------------------------------------------------------- + +using namespace JSC; +using namespace JSC::Bindings; + +class Global : public JSObject { +public: + virtual UString className() const { return "global"; } +}; + +static char code[] = + "myInterface.foo();\n" + "myInterface.testString = \"Hello\";\n" + "str = myInterface.testString;\n" + "myInterface.testInt = 10;\n" + "i = myInterface.testInt;\n"; + +int main(int argc, char** argv) +{ + // expecting a filename + bool ret = true; + { + JSLock lock; + + // create interpreter w/ global object + Global* global = new Global(); + + // create interpreter + RefPtr<Interpreter> interp = new Interpreter(global); + ExecState* exec = interp->globalExec(); + + MyObject* myObject = new MyObject; + + global->put(exec, Identifier("myInterface"), Instance::createRuntimeObject(Instance::QtLanguage, (void*)myObject)); + + + if (code) { + // run + Completion comp(interp->evaluate("", 0, code)); + + if (comp.complType() == Throw) { + qDebug() << "exception thrown"; + JSValue* exVal = comp.value(); + char* msg = exVal->toString(exec).ascii(); + int lineno = -1; + if (exVal->type() == ObjectType) { + JSValue* lineVal = exVal->getObject()->get(exec, Identifier("line")); + if (lineVal->type() == NumberType) + lineno = int(lineVal->toNumber(exec)); + } + if (lineno != -1) + fprintf(stderr,"Exception, line %d: %s\n",lineno,msg); + else + fprintf(stderr,"Exception: %s\n",msg); + ret = false; + } + else if (comp.complType() == ReturnValue) { + char* msg = comp.value()->toString(interp->globalExec()).ascii(); + fprintf(stderr,"Return value: %s\n",msg); + } + } + + } // end block, so that Interpreter and global get deleted + + return ret ? 0 : 1; +} + +#include "testqtbindings.moc" |