diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/bridge | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/bridge')
70 files changed, 12737 insertions, 4012 deletions
diff --git a/WebCore/bridge/AXObjectCache.h b/WebCore/bridge/AXObjectCache.h deleted file mode 100644 index d73a5a3..0000000 --- a/WebCore/bridge/AXObjectCache.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2003, 2006, 2007 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 AXObjectCache_h -#define AXObjectCache_h - -#include <limits.h> - -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> - -#ifdef __OBJC__ -@class WebCoreAXObject; -@class WebCoreTextMarker; -#else -class WebCoreAXObject; -class WebCoreTextMarker; -#endif - -namespace WebCore { - - class RenderObject; - class String; - class VisiblePosition; - - typedef unsigned AXID; - - struct AXIDHashTraits : WTF::GenericHashTraits<unsigned> { - static TraitType deletedValue() { return UINT_MAX; } - }; - - class AXObjectCache { - public: - ~AXObjectCache(); - - WebCoreAXObject* get(RenderObject*); - void remove(RenderObject*); - - void removeAXID(WebCoreAXObject*); - - WebCoreTextMarker* textMarkerForVisiblePosition(const VisiblePosition&); - VisiblePosition visiblePositionForTextMarker(WebCoreTextMarker*); - - void childrenChanged(RenderObject*); - void postNotification(RenderObject*, const String& message); - void postNotificationToElement(RenderObject*, const String& message); - void handleFocusedUIElementChanged(); - -#if PLATFORM(MAC) - static void enableAccessibility() { gAccessibilityEnabled = true; } - static bool accessibilityEnabled() { return gAccessibilityEnabled; } -#else - static bool accessibilityEnabled() { return false; } -#endif - - private: -#if PLATFORM(MAC) - static bool gAccessibilityEnabled; -#endif - - AXID getAXID(WebCoreAXObject*); - - HashMap<RenderObject*, WebCoreAXObject*> m_objects; - HashSet<AXID, IntHash<AXID>, AXIDHashTraits> m_idsInUse; - }; - -#if !PLATFORM(MAC) - inline AXObjectCache::~AXObjectCache() { } - inline WebCoreAXObject* AXObjectCache::get(RenderObject*) { return 0; } - inline void AXObjectCache::remove(RenderObject*) { } - inline void AXObjectCache::removeAXID(WebCoreAXObject*) { } - inline void AXObjectCache::childrenChanged(RenderObject*) { } - inline void AXObjectCache::postNotification(RenderObject*, const String&) { } - inline void AXObjectCache::postNotificationToElement(RenderObject*, const String&) { } - inline void AXObjectCache::handleFocusedUIElementChanged() { } -#endif - -} - -#endif diff --git a/WebCore/bridge/EditorClient.h b/WebCore/bridge/EditorClient.h deleted file mode 100644 index 802aa4f..0000000 --- a/WebCore/bridge/EditorClient.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2007 Trolltech ASA - * - * 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 EditorClient_h -#define EditorClient_h - -#include "EditorInsertAction.h" -#include "PlatformString.h" -#include "TextAffinity.h" -#include <wtf/Forward.h> -#include <wtf/Vector.h> - -#if PLATFORM(MAC) -class NSArray; -class NSData; -class NSString; -class NSURL; -#endif - -namespace WebCore { - -class CSSStyleDeclaration; -class EditCommand; -class Element; -class Frame; -class HTMLElement; -class KeyboardEvent; -class Node; -class Range; -class Selection; -class String; -class VisiblePosition; - -struct GrammarDetail { - int location; - int length; - Vector<String> guesses; - String userDescription; -}; - -class EditorClient { -public: - virtual ~EditorClient() { } - virtual void pageDestroyed() = 0; - - virtual bool shouldDeleteRange(Range*) = 0; - virtual bool shouldShowDeleteInterface(HTMLElement*) = 0; - virtual bool smartInsertDeleteEnabled() = 0; - virtual bool isContinuousSpellCheckingEnabled() = 0; - virtual void toggleContinuousSpellChecking() = 0; - virtual bool isGrammarCheckingEnabled() = 0; - virtual void toggleGrammarChecking() = 0; - virtual int spellCheckerDocumentTag() = 0; - - virtual bool isEditable() = 0; - - virtual bool shouldBeginEditing(Range*) = 0; - virtual bool shouldEndEditing(Range*) = 0; - virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction) = 0; - virtual bool shouldInsertText(String, Range*, EditorInsertAction) = 0; - virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting) = 0; - - virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*) = 0; -// virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle) = 0; -// virtual bool doCommandBySelector(SEL selector) = 0; - virtual bool shouldMoveRangeAfterDelete(Range*, Range*) = 0; - - virtual void didBeginEditing() = 0; - virtual void respondToChangedContents() = 0; - virtual void respondToChangedSelection() = 0; - virtual void didEndEditing() = 0; - virtual void didWriteSelectionToPasteboard() = 0; - virtual void didSetSelectionTypesForPasteboard() = 0; -// virtual void didChangeTypingStyle:(NSNotification *)notification = 0; -// virtual void didChangeSelection:(NSNotification *)notification = 0; -// virtual NSUndoManager* undoManager:(WebView *)webView = 0; - - virtual void registerCommandForUndo(PassRefPtr<EditCommand>) = 0; - virtual void registerCommandForRedo(PassRefPtr<EditCommand>) = 0; - virtual void clearUndoRedoOperations() = 0; - - virtual bool canUndo() const = 0; - virtual bool canRedo() const = 0; - - virtual void undo() = 0; - virtual void redo() = 0; - - virtual void handleKeyboardEvent(KeyboardEvent*) = 0; - virtual void handleInputMethodKeydown(KeyboardEvent*) = 0; - - virtual void textFieldDidBeginEditing(Element*) = 0; - virtual void textFieldDidEndEditing(Element*) = 0; - virtual void textDidChangeInTextField(Element*) = 0; - virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) = 0; - virtual void textWillBeDeletedInTextField(Element*) = 0; - virtual void textDidChangeInTextArea(Element*) = 0; - -#if PLATFORM(MAC) - // FIXME: This should become SelectionController::toWebArchive() - virtual NSData* dataForArchivedSelection(Frame*) = 0; - - virtual NSString* userVisibleString(NSURL*) = 0; -#ifdef BUILDING_ON_TIGER - virtual NSArray* pasteboardTypesForSelection(Frame*) = 0; -#endif -#endif - - virtual void ignoreWordInSpellDocument(const String&) = 0; - virtual void learnWord(const String&) = 0; - virtual void checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength) = 0; - virtual void checkGrammarOfString(const UChar*, int length, Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength) = 0; - virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail& detail) = 0; - virtual void updateSpellingUIWithMisspelledWord(const String&) = 0; - virtual void showSpellingUI(bool show) = 0; - virtual bool spellingUIIsShowing() = 0; - virtual void getGuessesForWord(const String&, Vector<String>& guesses) = 0; - virtual void setInputMethodState(bool enabled) = 0; -}; - -} - -#endif // EditorClient_h diff --git a/WebCore/bridge/NP_jsobject.cpp b/WebCore/bridge/NP_jsobject.cpp new file mode 100644 index 0000000..9734862 --- /dev/null +++ b/WebCore/bridge/NP_jsobject.cpp @@ -0,0 +1,465 @@ +/* + * 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 "StringSourceProvider.h" +#ifdef ANDROID_NPN_SETEXCEPTION +#include "c_runtime.h" +#endif // ANDROID_NPN_SETEXCEPTION +#include "c_utility.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 <kjs/SourceCode.h> +#include <kjs/completion.h> +#include <kjs/interpreter.h> + +using WebCore::String; +using WebCore::StringSourceProvider; +using namespace JSC; +using namespace JSC::Bindings; + +static void getListFromVariantArgs(ExecState* exec, const NPVariant* args, unsigned argCount, RootObject* rootObject, ArgList& 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()) + 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) +{ + JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(_NPN_CreateObject(npp, NPScriptObjectClass)); + + obj->rootObject = rootObject.releaseRef(); + + if (obj->rootObject) + obj->rootObject->gcProtect(imp); + 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(false); + + // Call the function object. + JSValue* function = obj->imp; + CallData callData; + CallType callType = function->getCallData(callData); + if (callType == CallTypeNone) + return false; + + ArgList argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue* resultV = call(exec, function, callType, callData, function, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + // 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); + + PrivateIdentifier* i = static_cast<PrivateIdentifier*>(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(false); + JSValue* function = obj->imp->get(exec, identifierFromNPIdentifier(i->value.string)); + CallData callData; + CallType callType = function->getCallData(callData); + if (callType == CallTypeNone) + return false; + + // Call the function object. + ArgList argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue* resultV = call(exec, function, callType, callData, obj->imp, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + // 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, 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; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(false); + String scriptString = convertNPStringToUTF16(s); + rootObject->globalObject()->startTimeoutCheck(); + Completion completion = Interpreter::evaluate(rootObject->globalObject()->globalExec(), rootObject->globalObject()->globalScopeChain(), makeSource(scriptString)); + rootObject->globalObject()->stopTimeoutCheck(); + 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(); + PrivateIdentifier* i = static_cast<PrivateIdentifier*>(propertyName); + + JSLock lock(false); + JSValue* result; + if (i->isString) + result = obj->imp->get(exec, identifierFromNPIdentifier(i->value.string)); + else + result = obj->imp->get(exec, i->value.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(false); + PrivateIdentifier* i = static_cast<PrivateIdentifier*>(propertyName); + + if (i->isString) { + PutPropertySlot slot; + obj->imp->put(exec, identifierFromNPIdentifier(i->value.string), convertNPVariantToValue(exec, variant, rootObject), slot); + } else + obj->imp->put(exec, i->value.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(); + PrivateIdentifier* i = static_cast<PrivateIdentifier*>(propertyName); + if (i->isString) { + if (!obj->imp->hasProperty(exec, identifierFromNPIdentifier(i->value.string))) { + exec->clearException(); + return false; + } + } else { + if (!obj->imp->hasProperty(exec, i->value.number)) { + exec->clearException(); + return false; + } + } + + JSLock lock(false); + if (i->isString) + obj->imp->deleteProperty(exec, identifierFromNPIdentifier(i->value.string)); + else + obj->imp->deleteProperty(exec, i->value.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(); + PrivateIdentifier* i = static_cast<PrivateIdentifier*>(propertyName); + JSLock lock(false); + if (i->isString) { + bool result = obj->imp->hasProperty(exec, identifierFromNPIdentifier(i->value.string)); + exec->clearException(); + return result; + } + + bool result = obj->imp->hasProperty(exec, i->value.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); + + PrivateIdentifier* i = static_cast<PrivateIdentifier*>(methodName); + if (!i->isString) + return false; + + RootObject* rootObject = obj->rootObject; + if (!rootObject || !rootObject->isValid()) + return false; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(false); + JSValue* func = obj->imp->get(exec, identifierFromNPIdentifier(i->value.string)); + exec->clearException(); + return !func->isUndefined(); + } + + if (o->_class->hasMethod) + return o->_class->hasMethod(o, methodName); + + return false; +} + +void _NPN_SetException(NPObject* o, const NPUTF8* message) +{ + // FIXME: + // Bug 19888: Implement _NPN_SetException() correctly + // <https://bugs.webkit.org/show_bug.cgi?id=19888> +#ifdef ANDROID_NPN_SETEXCEPTION + if (o->_class == NPScriptObjectClass) { + JSC::Bindings::SetGlobalException(message); + } +#endif // ANDROID_NPN_SETEXCEPTION +} + +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(false); + 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().UTF8String().c_str()); + + *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 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(false); + + // Call the constructor object. + JSValue* constructor = obj->imp; + ConstructData constructData; + ConstructType constructType = constructor->getConstructData(constructData); + if (constructType == ConstructTypeNone) + return false; + + ArgList argList; + getListFromVariantArgs(exec, args, argCount, rootObject, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue* resultV = construct(exec, constructor, constructType, constructData, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + // 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/WebCore/bridge/win/FrameCairoWin.cpp b/WebCore/bridge/NP_jsobject.h index a645a10..6c49d1b 100644 --- a/WebCore/bridge/win/FrameCairoWin.cpp +++ b/WebCore/bridge/NP_jsobject.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * 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 @@ -23,20 +23,33 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "FrameWin.h" +#ifndef NP_JSOBJECT_H +#define NP_JSOBJECT_H -#include "EditorClient.h" -#include "NotImplemented.h" +#if ENABLE(NETSCAPE_PLUGIN_API) -using std::min; +#include "npruntime_internal.h" +#include <wtf/Forward.h> -namespace WebCore { +namespace JSC { + class JSObject; + namespace Bindings { + class RootObject; + } +} + +extern NPClass* NPScriptObjectClass; -HBITMAP imageFromSelection(Frame* frame, bool forceBlackText) +struct JavaScriptObject { - notImplemented(); - return 0; -} + 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) -} // namespace WebCore +#endif diff --git a/WebCore/bridge/c/c_class.cpp b/WebCore/bridge/c/c_class.cpp new file mode 100644 index 0000000..1b72476 --- /dev/null +++ b/WebCore/bridge/c/c_class.cpp @@ -0,0 +1,124 @@ +/* + * 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 <kjs/identifier.h> +#include <runtime/JSLock.h> + +namespace JSC { namespace Bindings { + +CClass::CClass(NPClass* aClass) +{ + _isa = aClass; +} + +CClass::~CClass() +{ + JSLock lock(false); + + 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; +} + +const char* CClass::name() const +{ + return ""; +} + +MethodList CClass::methodsNamed(const Identifier& identifier, Instance* instance) const +{ + MethodList methodList; + + Method* method = _methods.get(identifier.ustring().rep()); + if (method) { + methodList.append(method); + return methodList; + } + + NPIdentifier ident = _NPN_GetStringIdentifier(identifier.ascii()); + 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(false); + _methods.set(identifier.ustring().rep(), aMethod); + } + methodList.append(aMethod); + } + + return methodList; +} + +Field* CClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + Field* aField = _fields.get(identifier.ustring().rep()); + if (aField) + return aField; + + NPIdentifier ident = _NPN_GetStringIdentifier(identifier.ascii()); + 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(false); + _fields.set(identifier.ustring().rep(), aField); + } + } + return aField; +} + +} } // namespace JSC::Bindings + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/WebCore/bridge/c/c_class.h b/WebCore/bridge/c/c_class.h new file mode 100644 index 0000000..9a08605 --- /dev/null +++ b/WebCore/bridge/c/c_class.h @@ -0,0 +1,61 @@ +/* + * 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 "npruntime_internal.h" +#include "runtime.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 const char* name() const; + 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/WebCore/bridge/c/c_instance.cpp b/WebCore/bridge/c/c_instance.cpp new file mode 100644 index 0000000..79ae520 --- /dev/null +++ b/WebCore/bridge/c/c_instance.cpp @@ -0,0 +1,246 @@ +/* + * 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 "c_class.h" +#include "c_runtime.h" +#include "c_utility.h" +#include "npruntime_impl.h" +#include "runtime_root.h" +#include <runtime/ArgList.h> +#include <runtime/ExecState.h> +#include <runtime/JSLock.h> +#include <runtime/JSNumberCell.h> +#include <runtime/PropertyNameArray.h> +#include <wtf/Assertions.h> +#include <wtf/StringExtras.h> +#include <wtf/Vector.h> + +namespace JSC { +namespace Bindings { + +CInstance::CInstance(NPObject* o, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) +{ + _object = _NPN_RetainObject(o); + _class = 0; +} + +CInstance::~CInstance() +{ + _NPN_ReleaseObject(_object); +} + +Class *CInstance::getClass() const +{ + if (!_class) + _class = CClass::classForIsA(_object->_class); + return _class; +} + +bool CInstance::supportsInvokeDefaultMethod() const +{ + return _object->_class->invokeDefault; +} + +JSValue* CInstance::invokeMethod(ExecState* exec, const MethodList& methodList, const ArgList& args) +{ + // 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 = _NPN_GetStringIdentifier(method->name()); + if (!_object->_class->hasMethod(_object, ident)) + return jsUndefined(); + + unsigned count = args.size(); + Vector<NPVariant, 8> cArgs(count); + + unsigned i; + for (i = 0; i < count; i++) + convertValueToNPVariant(exec, args.at(exec, i), &cArgs[i]); + + // Invoke the 'C' method. +#ifdef ANDROID_NPN_SETEXCEPTION + SetGlobalException(0); +#endif + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + + { + JSLock::DropAllLocks dropAllLocks(false); + _object->_class->invoke(_object, ident, cArgs.data(), count, &resultVariant); + } + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue* resultValue = convertNPVariantToValue(exec, &resultVariant, _rootObject.get()); + _NPN_ReleaseVariantValue(&resultVariant); +#ifdef ANDROID_NPN_SETEXCEPTION + MoveGlobalExceptionToExecState(exec); +#endif + return resultValue; +} + + +JSValue* CInstance::invokeDefaultMethod(ExecState* exec, const ArgList& args) +{ + if (!_object->_class->invokeDefault) + return jsUndefined(); + + unsigned count = args.size(); + Vector<NPVariant, 8> cArgs(count); + + unsigned i; + for (i = 0; i < count; i++) + convertValueToNPVariant(exec, args.at(exec, i), &cArgs[i]); + + // Invoke the 'C' method. +#ifdef ANDROID_NPN_SETEXCEPTION + SetGlobalException(0); +#endif + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + { + JSLock::DropAllLocks dropAllLocks(false); + _object->_class->invokeDefault(_object, cArgs.data(), count, &resultVariant); + } + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue* resultValue = convertNPVariantToValue(exec, &resultVariant, _rootObject.get()); + _NPN_ReleaseVariantValue(&resultVariant); +#ifdef ANDROID_NPN_SETEXCEPTION + MoveGlobalExceptionToExecState(exec); +#endif + 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(exec, i), &cArgs[i]); + + // Invoke the 'C' method. + NPVariant resultVariant; + VOID_TO_NPVARIANT(resultVariant); + { + JSLock::DropAllLocks dropAllLocks(false); + _object->_class->construct(_object, cArgs.data(), count, &resultVariant); + } + + for (i = 0; i < count; i++) + _NPN_ReleaseVariantValue(&cArgs[i]); + + JSValue* resultValue = convertNPVariantToValue(exec, &resultVariant, _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* exec) const +{ + // FIXME: Implement something sensible. + return jsNumber(exec, 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(false); + if (!_object->_class->enumerate(_object, &identifiers, &count)) + return; + } + + for (uint32_t i = 0; i < count; i++) { + PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(identifiers[i]); + + if (identifier->isString) + nameArray.add(identifierFromNPIdentifier(identifier->value.string)); + else + nameArray.add(Identifier::from(exec, identifier->value.number)); + } + + // FIXME: This should really call NPN_MemFree but that's in WebKit + free(identifiers); +} + +} +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/WebCore/bridge/c/c_instance.h b/WebCore/bridge/c/c_instance.h new file mode 100644 index 0000000..6cc4b8d --- /dev/null +++ b/WebCore/bridge/c/c_instance.h @@ -0,0 +1,86 @@ +/* + * 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 "runtime.h" +#include <wtf/PassRefPtr.h> +#include "runtime_root.h" + +typedef struct NPObject NPObject; + +namespace JSC { + +namespace Bindings { + +class CClass; + +class CInstance : public Instance { +public: + static PassRefPtr<CInstance> create(NPObject* object, PassRefPtr<RootObject> rootObject) + { + return adoptRef(new CInstance(object, rootObject)); + } + ~CInstance (); + + virtual Class *getClass() const; + + virtual JSValue* valueOf(ExecState*) const; + virtual JSValue* defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual JSValue* invokeMethod(ExecState*, const MethodList&, const ArgList&); + virtual bool supportsInvokeDefaultMethod() const; + virtual JSValue* invokeDefaultMethod(ExecState*, const ArgList&); + + 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; } + + virtual BindingLanguage getBindingLanguage() const { return CLanguage; } + +private: + CInstance(NPObject*, PassRefPtr<RootObject>); + + mutable CClass *_class; + NPObject *_object; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/WebCore/bridge/c/c_runtime.cpp b/WebCore/bridge/c/c_runtime.cpp new file mode 100644 index 0000000..7cf5dc9 --- /dev/null +++ b/WebCore/bridge/c/c_runtime.cpp @@ -0,0 +1,145 @@ +/* + * 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" +#ifdef ANDROID_NPN_SETEXCEPTION +#include "runtime/Error.h" +#endif // ANDROID_NPN_SETEXCEPTION +#include <runtime/JSLock.h> + +namespace JSC { +namespace Bindings { + +#ifdef ANDROID_NPN_SETEXCEPTION +/* + * When throwing an exception, we need to use the current ExecState. + * The following two methods implement a similar solution to the + * Objective-C implementation, where _NPN_SetException set a global + * exception (using SetGlobalException). + * We then test (using MoveGlobalExceptionToExecState) if the exception + * is set, after each javascript call that might result in an exception. + * If the exception is set we throw it with the passed ExecState. + */ + +static UString* globalLastException = 0; + +void SetGlobalException(const NPUTF8* exception) +{ + if (globalLastException != 0) { + delete globalLastException; + globalLastException = 0; + } + if (exception != 0) + globalLastException = new UString(exception); +} + +void MoveGlobalExceptionToExecState(ExecState* exec) +{ + if (!globalLastException) + return; + JSLock lock(exec); + throwError(exec, GeneralError, *globalLastException); + SetGlobalException(0); +} +#endif // ANDROID_NPN_SETEXCEPTION + +// ---------------------- CMethod ---------------------- + +const char* CMethod::name() const +{ + PrivateIdentifier *i = (PrivateIdentifier *)_methodIdentifier; + return i->isString ? i->value.string : 0; +} + +// ---------------------- CField ---------------------- + +const char* CField::name() const +{ + PrivateIdentifier *i = (PrivateIdentifier *)_fieldIdentifier; + return i->isString ? i->value.string : 0; +} + +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); + +#ifdef ANDROID_NPN_SETEXCEPTION + SetGlobalException(0); +#endif // ANDROID_NPN_SETEXCEPTION + bool result; + { + JSLock::DropAllLocks dropAllLocks(false); + result = obj->_class->getProperty(obj, _fieldIdentifier, &property); + } +#ifdef ANDROID_NPN_SETEXCEPTION + MoveGlobalExceptionToExecState(exec); +#endif // ANDROID_NPN_SETEXCEPTION + 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); + +#ifdef ANDROID_NPN_SETEXCEPTION + SetGlobalException(0); +#endif // ANDROID_NPN_SETEXCEPTION + { + JSLock::DropAllLocks dropAllLocks(false); + obj->_class->setProperty(obj, _fieldIdentifier, &variant); + } + + _NPN_ReleaseVariantValue(&variant); +#ifdef ANDROID_NPN_SETEXCEPTION + MoveGlobalExceptionToExecState(exec); +#endif // ANDROID_NPN_SETEXCEPTION + } +} + +} } + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/WebCore/bridge/mac/WebCoreAXObject.h b/WebCore/bridge/c/c_runtime.h index 2014ca6..998e6d4 100644 --- a/WebCore/bridge/mac/WebCoreAXObject.h +++ b/WebCore/bridge/c/c_runtime.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * 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 @@ -20,46 +20,53 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "AXObjectCache.h" +#ifndef BINDINGS_C_RUNTIME_H_ +#define BINDINGS_C_RUNTIME_H_ -namespace WebCore { - class HTMLAreaElement; - class RenderObject; -} +#if ENABLE(NETSCAPE_PLUGIN_API) -@interface WebCoreAXObject : NSObject -{ - WebCore::RenderObject* m_renderer; - id m_data; - WebCore::HTMLAreaElement* m_areaElement; - NSMutableArray* m_children; - WebCore::AXID m_id; -} +#include "npruntime_internal.h" +#include "runtime.h" + +namespace JSC { +namespace Bindings { + +class CField : public Field { +public: + CField(NPIdentifier ident) : _fieldIdentifier(ident) { } -- (id)initWithRenderer:(WebCore::RenderObject*)renderer; + virtual JSValue* valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue*) const; + virtual const char* name() const; -- (BOOL)detached; -- (void)detach; +private: + NPIdentifier _fieldIdentifier; +}; + + +class CMethod : public Method +{ +public: + CMethod(NPIdentifier ident) : _methodIdentifier(ident) { } -- (id)data; -- (void)setData:(id)data; + virtual const char* name() const; + virtual int numParameters() const { return 0; } -- (WebCore::AXID)axObjectID; -- (void)setAXObjectID:(WebCore::AXID)axObjectID; -- (void)removeAXObjectID; +private: + NPIdentifier _methodIdentifier; +}; -- (WebCoreAXObject*)firstChild; -- (WebCoreAXObject*)lastChild; -- (WebCoreAXObject*)previousSibling; -- (WebCoreAXObject*)nextSibling; -- (WebCoreAXObject*)parentObject; +#ifdef ANDROID_NPN_SETEXCEPTION +void SetGlobalException(const NPUTF8* exception); +void MoveGlobalExceptionToExecState(ExecState* exec); +#endif // ANDROID_NPN_SETEXCEPTION -- (WebCoreAXObject*)observableObject; +} // namespace Bindings +} // namespace JSC -- (void)childrenChanged; -- (void)clearChildren; +#endif // ENABLE(NETSCAPE_PLUGIN_API) -@end +#endif diff --git a/WebCore/bridge/c/c_utility.cpp b/WebCore/bridge/c/c_utility.cpp new file mode 100644 index 0000000..dced048 --- /dev/null +++ b/WebCore/bridge/c/c_utility.cpp @@ -0,0 +1,152 @@ +/* + * 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 "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> + +using WebCore::String; + +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(false); + + VOID_TO_NPVARIANT(*result); + + if (value->isString()) { + UString ustring = value->toString(exec); + CString cstring = ustring.UTF8String(); + NPString string = { (const NPUTF8*)cstring.c_str(), static_cast<uint32_t>(cstring.size()) }; + 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() == &RuntimeObjectImp::s_info) { + RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(object); + CInstance* instance = static_cast<CInstance*>(imp->getInternalInstance()); + if (instance) { + NPObject* obj = instance->getObject(); + _NPN_RetainObject(obj); + OBJECT_TO_NPVARIANT(obj, *result); + } + } else { + JSGlobalObject* globalObject = exec->dynamicGlobalObject(); + + RootObject* rootObject = findRootObject(globalObject); + 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(false); + + 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(exec, NPVARIANT_TO_INT32(*variant)); + if (type == NPVariantType_Double) + return jsNumber(exec, NPVARIANT_TO_DOUBLE(*variant)); + if (type == NPVariantType_String) + return 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 Instance::createRuntimeObject(exec, CInstance::create(obj, rootObject)); + } + + return jsUndefined(); +} + +String convertNPStringToUTF16(const NPString* string) +{ + return convertUTF8ToUTF16WithLatin1Fallback(string->UTF8Characters, string->UTF8Length); +} + +Identifier identifierFromNPIdentifier(const NPUTF8* name) +{ + return Identifier(WebCore::JSDOMWindow::commonJSGlobalData(), convertUTF8ToUTF16WithLatin1Fallback(name, -1)); +} + +} } + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/WebCore/bridge/win/GlobalHistoryWin.cpp b/WebCore/bridge/c/c_utility.h index c81f09b..1e0dd22 100644 --- a/WebCore/bridge/win/GlobalHistoryWin.cpp +++ b/WebCore/bridge/c/c_utility.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * 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 @@ -23,17 +23,54 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "GlobalHistory.h" +#ifndef C_UTILITY_H_ +#define C_UTILITY_H_ -#include "WebCoreHistory.h" +#if ENABLE(NETSCAPE_PLUGIN_API) -namespace WebCore { +#include "npruntime_internal.h" +#include <runtime/JSValue.h> -bool historyContains(const UChar* characters, unsigned length) -{ - WebCoreHistoryProvider* provider = WebCoreHistory::historyProvider(); - return provider && provider->containsURL(characters, length); +namespace WebCore { + class String; } -} // namespace WebCore +namespace JSC { + +class ExecState; +class Identifier; + +namespace Bindings { + +class RootObject; + +typedef uint16_t NPUTF16; + +enum NP_ValueType { + NP_NumberValueType, + NP_StringValueType, + NP_BooleanValueType, + NP_NullValueType, + NP_UndefinedValueType, + NP_ObjectValueType, + NP_InvalidValueType +}; + +WebCore::String convertNPStringToUTF16(const NPString *string); +void convertValueToNPVariant(ExecState*, JSValue*, NPVariant* result); +JSValue* convertNPVariantToValue(ExecState*, const NPVariant*, RootObject*); +Identifier identifierFromNPIdentifier(const NPUTF8* name); + +struct PrivateIdentifier { + union { + const NPUTF8* string; + int32_t number; + } value; + bool isString; +}; + +} } + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif diff --git a/WebCore/bridge/jni/jni_class.cpp b/WebCore/bridge/jni/jni_class.cpp new file mode 100644 index 0000000..4140524 --- /dev/null +++ b/WebCore/bridge/jni/jni_class.cpp @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#include "config.h" +#include "jni_class.h" + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "JSDOMWindow.h" +#include <kjs/identifier.h> +#include <runtime/JSLock.h> +#include "jni_utility.h" +#include "jni_runtime.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; + } + + jstring className = (jstring)callJNIMethod<jobject>(aClass, "getName", "()Ljava/lang/String;"); + const char *classNameC = getCharactersFromJString(className); + _name = strdup(classNameC); + releaseCharactersForJString(className, classNameC); + + int i; + JNIEnv *env = getJNIEnv(); + + JSGlobalData* globalData = WebCore::JSDOMWindow::commonJSGlobalData(); + + // Get the fields + 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); + Field *aField = new JavaField(env, aJField); // deleted in the JavaClass destructor + { + JSLock lock(false); + _fields.set(Identifier(globalData, UString(aField->name())).ustring().rep(), aField); + } + env->DeleteLocalRef(aJField); + } + + // Get the methods + 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); + Method *aMethod = new JavaMethod(env, aJMethod); // deleted in the JavaClass destructor + MethodList* methodList; + { + JSLock lock(false); + + methodList = _methods.get(Identifier(globalData, UString(aMethod->name())).ustring().rep()); + if (!methodList) { + methodList = new MethodList(); + _methods.set(Identifier(globalData, UString(aMethod->name())).ustring().rep(), methodList); + } + } + methodList->append(aMethod); + env->DeleteLocalRef(aJMethod); + } +} + +JavaClass::~JavaClass() { + free((void *)_name); + + JSLock lock(false); + + deleteAllValues(_fields); + _fields.clear(); + + MethodListMap::const_iterator end = _methods.end(); + for (MethodListMap::const_iterator it = _methods.begin(); it != end; ++it) { + const MethodList* methodList = it->second; + deleteAllValues(*methodList); + delete methodList; + } + _methods.clear(); +} + +MethodList JavaClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList *methodList = _methods.get(identifier.ustring().rep()); + + if (methodList) + return *methodList; + return MethodList(); +} + +Field *JavaClass::fieldNamed(const Identifier& identifier, Instance*) const +{ + return _fields.get(identifier.ustring().rep()); +} + +bool JavaClass::isNumberClass() const +{ + return ((strcmp(_name, "java.lang.Byte") == 0 || + strcmp(_name, "java.lang.Short") == 0 || + strcmp(_name, "java.lang.Integer") == 0 || + strcmp(_name, "java.lang.Long") == 0 || + strcmp(_name, "java.lang.Float") == 0 || + strcmp(_name, "java.lang.Double") == 0) ); +} + +bool JavaClass::isBooleanClass() const +{ + return strcmp(_name, "java.lang.Boolean") == 0; +} + +bool JavaClass::isStringClass() const +{ + return strcmp(_name, "java.lang.String") == 0; +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_class.h b/WebCore/bridge/jni/jni_class.h new file mode 100644 index 0000000..75cfd89 --- /dev/null +++ b/WebCore/bridge/jni/jni_class.h @@ -0,0 +1,66 @@ +/* + * 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 JNI_CLASS_H_ +#define JNI_CLASS_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <jni_runtime.h> +#include <wtf/HashMap.h> + +namespace JSC { + +namespace Bindings { + +class JavaClass : public Class { +public: + JavaClass (jobject anInstance); + ~JavaClass (); + + virtual const char *name() const { return _name; }; + + virtual MethodList methodsNamed(const Identifier&, Instance* instance) const; + virtual Field *fieldNamed(const Identifier&, Instance* instance) const; + + bool isNumberClass() const; + bool isBooleanClass() const; + bool isStringClass() const; + +private: + JavaClass (); // prevent default construction + + const char *_name; + FieldMap _fields; + MethodListMap _methods; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // JNI_CLASS_H_ diff --git a/WebCore/bridge/jni/jni_instance.cpp b/WebCore/bridge/jni/jni_instance.cpp new file mode 100644 index 0000000..6b23900 --- /dev/null +++ b/WebCore/bridge/jni/jni_instance.cpp @@ -0,0 +1,334 @@ +/* + * 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_instance.h" + +#ifdef ANDROID +#include <assert.h> +#endif + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "jni_class.h" +#include "jni_runtime.h" +#include "jni_utility.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/ArgList.h> +#include <runtime/Error.h> +#include <runtime/JSLock.h> + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +using namespace JSC::Bindings; +using namespace JSC; + +JavaInstance::JavaInstance (jobject instance, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) +{ + _instance = new JObjectWrapper (instance); + _class = 0; +} + +JavaInstance::~JavaInstance () +{ + delete _class; +} + +#define NUM_LOCAL_REFS 64 + +void JavaInstance::virtualBegin() +{ + getJNIEnv()->PushLocalFrame (NUM_LOCAL_REFS); +} + +void JavaInstance::virtualEnd() +{ + getJNIEnv()->PopLocalFrame (NULL); +} + +Class *JavaInstance::getClass() const +{ + if (_class == 0) + _class = new JavaClass (_instance->_instance); + return _class; +} + +JSValue* JavaInstance::stringValue(ExecState* exec) const +{ + JSLock lock(false); + + jstring stringValue = (jstring)callJNIMethod<jobject>(_instance->_instance, "toString", "()Ljava/lang/String;"); + 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* exec) const +{ + jdouble doubleValue = callJNIMethod<jdouble>(_instance->_instance, "doubleValue", "()D"); + return jsNumber(exec, doubleValue); +} + +JSValue* JavaInstance::booleanValue() const +{ + jboolean booleanValue = callJNIMethod<jboolean>(_instance->_instance, "booleanValue", "()Z"); + return jsBoolean(booleanValue); +} + +JSValue* JavaInstance::invokeMethod (ExecState *exec, const MethodList &methodList, const ArgList &args) +{ + int i, count = args.size(); + jvalue *jArgs; + 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 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. + Method *aMethod; + for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { + aMethod = methodList[methodIndex]; + if (aMethod->numParameters() == count) { + method = aMethod; + break; + } + } + if (method == 0) { + JS_LOG ("unable to find an appropiate method\n"); + return jsUndefined(); + } + + const JavaMethod *jMethod = static_cast<const JavaMethod*>(method); + JS_LOG ("call %s %s on %p\n", method->name(), jMethod->signature(), _instance->_instance); + + if (count > 0) { + jArgs = (jvalue *)malloc (count * sizeof(jvalue)); + } + else + jArgs = 0; + + for (i = 0; i < count; i++) { + JavaParameter* aParameter = jMethod->parameterAt(i); + jArgs[i] = convertValueToJValue(exec, args.at(exec, i), aParameter->getJNIType(), aParameter->type()); + JS_LOG("arg[%d] = %s\n", i, args.at(exec, i)->toString(exec).ascii()); + } + + jvalue result; + + // Try to use the JNI abstraction first, otherwise fall back to + // nornmal 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 = _instance->_instance; + JSValue* exceptionDescription = noValue(); + 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, result, callingURL, exceptionDescription); + if (exceptionDescription) { + throwError(exec, GeneralError, exceptionDescription->toString(exec)); + free (jArgs); + return jsUndefined(); + } + } + + // 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. + if (!handled) { + jobject obj = _instance->_instance; + 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; + } + } + + switch (jMethod->JNIReturnType()){ + case void_type: { + resultValue = jsUndefined(); + } + break; + + case object_type: { + if (result.l != 0) { + const char *arrayType = jMethod->returnType(); + if (arrayType[0] == '[') { + resultValue = JavaArray::convertJObjectToArray(exec, result.l, arrayType, rootObject); + } + else { + resultValue = Instance::createRuntimeObject(exec, JavaInstance::create(result.l, rootObject)); + } + } + else { + resultValue = jsUndefined(); + } + } + break; + + case boolean_type: { + resultValue = jsBoolean(result.z); + } + break; + + case byte_type: { + resultValue = jsNumber(exec, result.b); + } + break; + + case char_type: { + resultValue = jsNumber(exec, result.c); + } + break; + + case short_type: { + resultValue = jsNumber(exec, result.s); + } + break; + + case int_type: { + resultValue = jsNumber(exec, result.i); + } + break; + + case long_type: { + resultValue = jsNumber(exec, result.j); + } + break; + + case float_type: { + resultValue = jsNumber(exec, result.f); + } + break; + + case double_type: { + resultValue = jsNumber(exec, result.d); + } + break; + + case invalid_type: + default: { + resultValue = jsUndefined(); + } + break; + } + + free (jArgs); + + 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) +: _refCount(0) +{ + assert (instance != 0); + + // Cache the JNIEnv used to get the global ref for this java instanace. + // It'll be used to delete the reference. + _env = getJNIEnv(); + + _instance = _env->NewGlobalRef (instance); + + JS_LOG ("new global ref %p for %p\n", _instance, instance); + + if (_instance == NULL) { + fprintf (stderr, "%s: could not get GlobalRef for %p\n", __PRETTY_FUNCTION__, instance); + } +} + +JObjectWrapper::~JObjectWrapper() { + JS_LOG ("deleting global ref %p\n", _instance); + _env->DeleteGlobalRef (_instance); +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_instance.h b/WebCore/bridge/jni/jni_instance.h new file mode 100644 index 0000000..7104865 --- /dev/null +++ b/WebCore/bridge/jni/jni_instance.h @@ -0,0 +1,110 @@ +/* + * 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 _JNI_INSTANCE_H_ +#define _JNI_INSTANCE_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "runtime.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; + +protected: + JObjectWrapper(jobject instance); + ~JObjectWrapper(); + + void ref() { _refCount++; } + void deref() + { + if (--_refCount == 0) + delete this; + } + + jobject _instance; + +private: + JNIEnv *_env; + unsigned int _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* invokeMethod(ExecState* exec, const MethodList& method, const ArgList& args); + + jobject javaInstance() const { return _instance->_instance; } + + JSValue* stringValue(ExecState*) const; + JSValue* numberValue(ExecState*) const; + JSValue* booleanValue() const; + + virtual BindingLanguage getBindingLanguage() const { return JavaLanguage; } + +protected: + virtual void virtualBegin(); + virtual void virtualEnd(); + +private: + JavaInstance(jobject instance, PassRefPtr<RootObject>); + + RefPtr<JObjectWrapper> _instance; + mutable JavaClass *_class; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // _JNI_INSTANCE_H_ diff --git a/WebCore/bridge/jni/jni_jsobject.h b/WebCore/bridge/jni/jni_jsobject.h new file mode 100644 index 0000000..812317d --- /dev/null +++ b/WebCore/bridge/jni/jni_jsobject.h @@ -0,0 +1,129 @@ +/* + * 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(MAC_JAVA_BRIDGE) + +#include <CoreFoundation/CoreFoundation.h> +#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)) + +namespace JSC { + +class ArgList; +class ExecState; +class JSObject; + +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, ArgList&) 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 // ENABLE(MAC_JAVA_BRIDGE) + +#endif // JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H diff --git a/WebCore/bridge/jni/jni_jsobject.mm b/WebCore/bridge/jni/jni_jsobject.mm new file mode 100644 index 0000000..042d1ed --- /dev/null +++ b/WebCore/bridge/jni/jni_jsobject.mm @@ -0,0 +1,720 @@ +/* + * 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(MAC_JAVA_BRIDGE) + +#include "Frame.h" +#include "WebCoreFrameView.h" +#include "jni_runtime.h" +#include "jni_utility.h" +#include "ScriptController.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/ExecState.h> +#include <runtime/JSGlobalObject.h> +#include <runtime/JSLock.h> +#include <kjs/completion.h> +#include <kjs/interpreter.h> +#include <wtf/Assertions.h> +#include <kjs/SourceProvider.h> + +using WebCore::Frame; + +using namespace JSC::Bindings; +using namespace JSC; + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, _performJavaScriptRunLoop, CFRunLoopGetCurrent()); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +#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 it's 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)) { + fprintf (stderr, "%s:%d: Attempt to access JavaScript from destroyed applet, type %d.\n", __FILE__, __LINE__, 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: { + fprintf (stderr, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__); + } + } + } + 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 +{ + JS_LOG ("methodName = %s\n", JavaString(methodName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + // Lookup the function object. + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(false); + + Identifier identifier(exec, JavaString(methodName)); + JSValue* function = _imp->get(exec, identifier); + CallData callData; + CallType callType = function->getCallData(callData); + if (callType == CallTypeNone) + return 0; + + // Call the function object. + ArgList argList; + getListFromJArray(exec, args, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue* result = JSC::call(exec, function, callType, callData, _imp, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + return convertValueToJObject(result); +} + +jobject JavaJSObject::eval(jstring script) const +{ + JS_LOG ("script = %s\n", JavaString(script).UTF8String()); + + JSValue* result; + + JSLock lock(false); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + rootObject->globalObject()->startTimeoutCheck(); + Completion completion = Interpreter::evaluate(rootObject->globalObject()->globalExec(), rootObject->globalObject()->globalScopeChain(), makeSource(JavaString(script))); + rootObject->globalObject()->stopTimeoutCheck(); + 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 +{ + JS_LOG ("(%p) memberName = %s\n", _imp, JavaString(memberName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(false); + JSValue* result = _imp->get(exec, Identifier(exec, JavaString(memberName))); + + return convertValueToJObject(result); +} + +void JavaJSObject::setMember(jstring memberName, jobject value) const +{ + JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName).UTF8String(), value); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(false); + PutPropertySlot slot; + _imp->put(exec, Identifier(exec, JavaString(memberName)), convertJObjectToValue(exec, value), slot); +} + + +void JavaJSObject::removeMember(jstring memberName) const +{ + JS_LOG ("memberName = %s\n", JavaString(memberName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(false); + _imp->deleteProperty(exec, Identifier(exec, JavaString(memberName))); +} + + +jobject JavaJSObject::getSlot(jint index) const +{ +#ifdef __LP64__ + JS_LOG ("index = %d\n", index); +#else + JS_LOG ("index = %ld\n", index); +#endif + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(false); + JSValue* result = _imp->get(exec, index); + + return convertValueToJObject(result); +} + + +void JavaJSObject::setSlot(jint index, jobject value) const +{ +#ifdef __LP64__ + JS_LOG ("index = %d, value = %p\n", index, value); +#else + JS_LOG ("index = %ld, value = %p\n", index, value); +#endif + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(false); + _imp->put(exec, (unsigned)index, convertJObjectToValue(exec, value)); +} + + +jstring JavaJSObject::toString() const +{ + JS_LOG ("\n"); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + JSLock lock(false); + JSObject *thisObj = const_cast<JSObject*>(_imp); + ExecState* exec = rootObject->globalObject()->globalExec(); + + return (jstring)convertValueToJValue (exec, 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) +{ + JS_LOG ("nativeHandle = %d\n", (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(false); + + 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.data(), stringValue.size()); + } 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* imp = 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 it's nativeHandle. + if (imp->classInfo() && strcmp(imp->classInfo()->className, "RuntimeObject") == 0) { + RuntimeObjectImp* runtimeImp = static_cast<RuntimeObjectImp*>(imp); + JavaInstance *runtimeInstance = static_cast<JavaInstance *>(runtimeImp->getInternalInstance()); + if (!runtimeInstance) + return 0; + + return runtimeInstance->javaInstance(); + } + else { + nativeHandle = ptr_to_jlong(imp); + rootObject->gcProtect(imp); + } + } + // All other types will result in an undefined object. + else { + nativeHandle = UndefinedHandle; + } + + // Now create the Java JavaJSObject. Look for the JavaJSObject in it's 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;"); + jstring className = (jstring)callJNIMethod<jobject>(classOfInstance, "getName", "()Ljava/lang/String;"); + + // 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. + if (strcmp(JavaString(className).UTF8String(), "sun.plugin.javascript.webkit.JSObject") == 0) { + // 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; + } + + JSLock lock(false); + + return JSC::Bindings::Instance::createRuntimeObject(exec, JavaInstance::create(theObject, _rootObject)); +} + +void JavaJSObject::getListFromJArray(ExecState* exec, jobjectArray jArray, ArgList& 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(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_objc.mm b/WebCore/bridge/jni/jni_objc.mm new file mode 100644 index 0000000..45ee9f5 --- /dev/null +++ b/WebCore/bridge/jni/jni_objc.mm @@ -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(MAC_JAVA_BRIDGE) + +#import <Foundation/Foundation.h> +#import "jni_utility.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(false); + 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(false); + result = [view webPlugInCallJava:obj method:methodID returnType:returnType arguments:args]; + return true; + } + + bzero (&result, sizeof(jvalue)); + return false; +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_runtime.cpp b/WebCore/bridge/jni/jni_runtime.cpp new file mode 100644 index 0000000..3a9b950 --- /dev/null +++ b/WebCore/bridge/jni/jni_runtime.cpp @@ -0,0 +1,547 @@ +/* + * 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. + */ + +#include "config.h" +#include <jni_runtime.h> + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <jni_utility.h> + +#include "runtime_array.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/Error.h> +#include <runtime/JSLock.h> + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +using namespace JSC; +using namespace JSC::Bindings; + + +JavaParameter::JavaParameter (JNIEnv *env, jstring type) +{ + _type = JavaString (env, type); + _JNIType = JNITypeFromClassName (_type.UTF8String()); +} + +JavaField::JavaField (JNIEnv *env, jobject aField) +{ + // Get field type + jobject fieldType = callJNIMethod<jobject>(aField, "getType", "()Ljava/lang/Class;"); + jstring fieldTypeName = (jstring)callJNIMethod<jobject>(fieldType, "getName", "()Ljava/lang/String;"); + _type = JavaString(env, fieldTypeName); + _JNIType = JNITypeFromClassName (_type.UTF8String()); + + // Get field name + jstring fieldName = (jstring)callJNIMethod<jobject>(aField, "getName", "()Ljava/lang/String;"); + _name = JavaString(env, fieldName); + + _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((jobject)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 = _field->_instance; + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + jclass cls = env->GetObjectClass(fieldJInstance); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue* exceptionDescription = noValue(); + jvalue args[1]; + + args[0].l = jinstance; + dispatchJNICall(exec, rootObject->nativeHandle(), fieldJInstance, false, returnType, mid, args, result, 0, exceptionDescription); + if (exceptionDescription) + throwError(exec, GeneralError, 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 (_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; + + const char *arrayType = type(); + if (arrayType[0] == '[') { + jsresult = JavaArray::convertJObjectToArray(exec, anObject, arrayType, instance->rootObject()); + } + else if (anObject != 0){ + jsresult = Instance::createRuntimeObject(exec, JavaInstance::create(anObject, instance->rootObject())); + } + } + 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(exec, (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(exec, (double)value); + } + break; + default: + break; + } + + JS_LOG ("getting %s = %s\n", name(), jsresult->toString(exec).ascii()); + + 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 = _field->_instance; + JNIEnv *env = getJNIEnv(); + + jclass cls = env->GetObjectClass(fieldJInstance); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue* exceptionDescription = noValue(); + 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, GeneralError, 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, aValue, _JNIType, type()); + + JS_LOG ("setting value %s to %s\n", name(), aValue->toString(exec).ascii()); + + switch (_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; + } +} + +JavaMethod::JavaMethod (JNIEnv *env, jobject aMethod) +{ + // Get return type + jobject returnType = callJNIMethod<jobject>(aMethod, "getReturnType", "()Ljava/lang/Class;"); + jstring returnTypeName = (jstring)callJNIMethod<jobject>(returnType, "getName", "()Ljava/lang/String;"); + _returnType =JavaString (env, returnTypeName); + _JNIReturnType = JNITypeFromClassName (_returnType.UTF8String()); + env->DeleteLocalRef (returnType); + env->DeleteLocalRef (returnTypeName); + + // Get method name + jstring methodName = (jstring)callJNIMethod<jobject>(aMethod, "getName", "()Ljava/lang/String;"); + _name = JavaString (env, methodName); + env->DeleteLocalRef (methodName); + + // Get parameters + jarray jparameters = (jarray)callJNIMethod<jobject>(aMethod, "getParameterTypes", "()[Ljava/lang/Class;"); + _numParameters = env->GetArrayLength (jparameters); + _parameters = new JavaParameter[_numParameters]; + + int i; + for (i = 0; i < _numParameters; i++) { + jobject aParameter = env->GetObjectArrayElement ((jobjectArray)jparameters, i); + jstring parameterName = (jstring)callJNIMethod<jobject>(aParameter, "getName", "()Ljava/lang/String;"); + _parameters[i] = JavaParameter(env, parameterName); + env->DeleteLocalRef (aParameter); + env->DeleteLocalRef (parameterName); + } + env->DeleteLocalRef (jparameters); + + // Created lazily. + _signature = 0; + _methodID = 0; + + jclass modifierClass = env->FindClass("java/lang/reflect/Modifier"); + int modifiers = callJNIMethod<jint>(aMethod, "getModifiers", "()I"); + _isStatic = (bool)callJNIStaticMethod<jboolean>(modifierClass, "isStatic", "(I)Z", modifiers); +} + +JavaMethod::~JavaMethod() +{ + if (_signature) + free(_signature); + delete [] _parameters; +}; + +// JNI method signatures use '/' between components of a class name, but +// we get '.' between components from the reflection API. +static void appendClassName(UString& aString, const char* className) +{ + ASSERT(JSLock::lockCount() > 0); + + char *result, *cp = strdup(className); + + result = cp; + while (*cp) { + if (*cp == '.') + *cp = '/'; + cp++; + } + + aString.append(result); + + free (result); +} + +const char *JavaMethod::signature() const +{ + if (!_signature) { + JSLock lock(false); + + UString signatureBuilder("("); + for (int i = 0; i < _numParameters; i++) { + JavaParameter* aParameter = parameterAt(i); + JNIType _JNIType = aParameter->getJNIType(); + if (_JNIType == array_type) + appendClassName(signatureBuilder, aParameter->type()); + else { + signatureBuilder.append(signatureFromPrimitiveType(_JNIType)); + if (_JNIType == object_type) { + appendClassName(signatureBuilder, aParameter->type()); + signatureBuilder.append(";"); + } + } + } + signatureBuilder.append(")"); + + const char *returnType = _returnType.UTF8String(); + if (_JNIReturnType == array_type) { + appendClassName(signatureBuilder, returnType); + } else { + signatureBuilder.append(signatureFromPrimitiveType(_JNIReturnType)); + if (_JNIReturnType == object_type) { + appendClassName(signatureBuilder, returnType); + signatureBuilder.append(";"); + } + } + + _signature = strdup(signatureBuilder.ascii()); + } + + return _signature; +} + +JNIType JavaMethod::JNIReturnType() const +{ + return _JNIReturnType; +} + +jmethodID JavaMethod::methodID (jobject obj) const +{ + if (_methodID == 0) { + _methodID = getMethodID (obj, name(), signature()); + } + return _methodID; +} + + +JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject) + : Array(rootObject) +{ + _array = new JObjectWrapper(array); + // Java array are fixed length, so we can cache length. + JNIEnv *env = getJNIEnv(); + _length = env->GetArrayLength((jarray)_array->_instance); + _type = strdup(type); + _rootObject = rootObject; +} + +JavaArray::~JavaArray () +{ + free ((void *)_type); +} + +RootObject* JavaArray::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +void JavaArray::setValueAt(ExecState* exec, unsigned index, JSValue* aValue) const +{ + JNIEnv *env = getJNIEnv(); + char *javaClassName = 0; + + JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); + if (_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(&_type[2]); + javaClassName[strchr(javaClassName, ';')-javaClassName] = 0; + } + jvalue aJValue = convertValueToJValue (exec, aValue, arrayType, javaClassName); + + switch (arrayType) { + case object_type: { + env->SetObjectArrayElement((jobjectArray)javaArray(), index, aJValue.l); + break; + } + + case boolean_type: { + env->SetBooleanArrayRegion((jbooleanArray)javaArray(), index, 1, &aJValue.z); + break; + } + + case byte_type: { + env->SetByteArrayRegion((jbyteArray)javaArray(), index, 1, &aJValue.b); + break; + } + + case char_type: { + env->SetCharArrayRegion((jcharArray)javaArray(), index, 1, &aJValue.c); + break; + } + + case short_type: { + env->SetShortArrayRegion((jshortArray)javaArray(), index, 1, &aJValue.s); + break; + } + + case int_type: { + env->SetIntArrayRegion((jintArray)javaArray(), index, 1, &aJValue.i); + break; + } + + case long_type: { + env->SetLongArrayRegion((jlongArray)javaArray(), index, 1, &aJValue.j); + } + + case float_type: { + env->SetFloatArrayRegion((jfloatArray)javaArray(), index, 1, &aJValue.f); + break; + } + + case double_type: { + env->SetDoubleArrayRegion((jdoubleArray)javaArray(), index, 1, &aJValue.d); + break; + } + default: + break; + } + + if (javaClassName) + free ((void *)javaClassName); +} + + +JSValue* JavaArray::valueAt(ExecState* exec, unsigned index) const +{ + JNIEnv *env = getJNIEnv(); + JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); + switch (arrayType) { + case object_type: { + jobjectArray objectArray = (jobjectArray)javaArray(); + jobject anObject; + anObject = env->GetObjectArrayElement(objectArray, index); + + // No object? + if (!anObject) { + return jsNull(); + } + + // Nested array? + if (_type[1] == '[') { + return JavaArray::convertJObjectToArray(exec, anObject, _type+1, rootObject()); + } + // or array of other object type? + return Instance::createRuntimeObject(exec, JavaInstance::create(anObject, rootObject())); + } + + case boolean_type: { + jbooleanArray booleanArray = (jbooleanArray)javaArray(); + jboolean aBoolean; + env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean); + return jsBoolean(aBoolean); + } + + case byte_type: { + jbyteArray byteArray = (jbyteArray)javaArray(); + jbyte aByte; + env->GetByteArrayRegion(byteArray, index, 1, &aByte); + return jsNumber(exec, aByte); + } + + case char_type: { + jcharArray charArray = (jcharArray)javaArray(); + jchar aChar; + env->GetCharArrayRegion(charArray, index, 1, &aChar); + return jsNumber(exec, aChar); + break; + } + + case short_type: { + jshortArray shortArray = (jshortArray)javaArray(); + jshort aShort; + env->GetShortArrayRegion(shortArray, index, 1, &aShort); + return jsNumber(exec, aShort); + } + + case int_type: { + jintArray intArray = (jintArray)javaArray(); + jint anInt; + env->GetIntArrayRegion(intArray, index, 1, &anInt); + return jsNumber(exec, anInt); + } + + case long_type: { + jlongArray longArray = (jlongArray)javaArray(); + jlong aLong; + env->GetLongArrayRegion(longArray, index, 1, &aLong); + return jsNumber(exec, aLong); + } + + case float_type: { + jfloatArray floatArray = (jfloatArray)javaArray(); + jfloat aFloat; + env->GetFloatArrayRegion(floatArray, index, 1, &aFloat); + return jsNumber(exec, aFloat); + } + + case double_type: { + jdoubleArray doubleArray = (jdoubleArray)javaArray(); + jdouble aDouble; + env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble); + return jsNumber(exec, aDouble); + } + default: + break; + } + return jsUndefined(); +} + +unsigned int JavaArray::getLength() const +{ + return _length; +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_runtime.h b/WebCore/bridge/jni/jni_runtime.h new file mode 100644 index 0000000..67bc06e --- /dev/null +++ b/WebCore/bridge/jni/jni_runtime.h @@ -0,0 +1,191 @@ +/* + * 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 _JNI_RUNTIME_H_ +#define _JNI_RUNTIME_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <jni_utility.h> +#include <jni_instance.h> +#include <runtime/JSLock.h> + + +namespace JSC +{ + +namespace Bindings +{ + +typedef const char* RuntimeType; + +class JavaString +{ +public: + JavaString() + { + JSLock lock(false); + _rep = UString().rep(); + } + + void _commonInit (JNIEnv *e, jstring s) + { + int _size = e->GetStringLength (s); + const jchar *uc = getUCharactersFromJStringInEnv (e, s); + { + JSLock lock(false); + _rep = UString((UChar *)uc,_size).rep(); + } + releaseUCharactersForJStringInEnv (e, s, uc); + } + + JavaString (JNIEnv *e, jstring s) { + _commonInit (e, s); + } + + JavaString (jstring s) { + _commonInit (getJNIEnv(), s); + } + + ~JavaString() + { + JSLock lock(false); + _rep = 0; + } + + const char *UTF8String() const { + if (_utf8String.c_str() == 0) { + JSLock lock(false); + _utf8String = UString(_rep).UTF8String(); + } + return _utf8String.c_str(); + } + const jchar *uchars() const { return (const jchar *)_rep->data(); } + int length() const { return _rep->size(); } + operator UString() const { return UString(_rep); } + +private: + RefPtr<UString::Rep> _rep; + mutable CString _utf8String; +}; + +class JavaParameter +{ +public: + JavaParameter () : _JNIType(invalid_type) {}; + JavaParameter (JNIEnv *env, jstring type); + virtual ~JavaParameter() { } + + RuntimeType type() const { return _type.UTF8String(); } + JNIType getJNIType() const { return _JNIType; } + +private: + JavaString _type; + JNIType _JNIType; +}; + + +class JavaField : public Field +{ +public: + JavaField (JNIEnv *env, jobject aField); + + virtual JSValue* valueFromInstance(ExecState *exec, const Instance *instance) const; + virtual void setValueToInstance(ExecState *exec, const Instance *instance, JSValue* aValue) const; + + virtual const char *name() const { return _name.UTF8String(); } + virtual RuntimeType type() const { return _type.UTF8String(); } + + JNIType getJNIType() const { return _JNIType; } + +private: + void dispatchSetValueToInstance(ExecState *exec, const JavaInstance *instance, jvalue javaValue, const char *name, const char *sig) const; + jvalue dispatchValueFromInstance(ExecState *exec, const JavaInstance *instance, const char *name, const char *sig, JNIType returnType) const; + + JavaString _name; + JavaString _type; + JNIType _JNIType; + RefPtr<JObjectWrapper> _field; +}; + + +class JavaMethod : public Method +{ +public: + JavaMethod(JNIEnv* env, jobject aMethod); + ~JavaMethod(); + + virtual const char *name() const { return _name.UTF8String(); }; + RuntimeType returnType() const { return _returnType.UTF8String(); }; + JavaParameter* parameterAt(int i) const { return &_parameters[i]; }; + int numParameters() const { return _numParameters; }; + + const char *signature() const; + JNIType JNIReturnType() const; + + jmethodID methodID (jobject obj) const; + + bool isStatic() const { return _isStatic; } + +private: + JavaParameter* _parameters; + int _numParameters; + JavaString _name; + mutable char* _signature; + JavaString _returnType; + JNIType _JNIReturnType; + mutable jmethodID _methodID; + bool _isStatic; +}; + +class JavaArray : public Array +{ +public: + JavaArray(jobject array, const char* type, PassRefPtr<RootObject>); + virtual ~JavaArray(); + + RootObject* rootObject() const; + + 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; + + jobject javaArray() const { return _array->_instance; } + + static JSValue* convertJObjectToArray (ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject>); + +private: + RefPtr<JObjectWrapper> _array; + unsigned int _length; + const char *_type; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // _JNI_RUNTIME_H_ diff --git a/WebCore/bridge/jni/jni_utility.cpp b/WebCore/bridge/jni/jni_utility.cpp new file mode 100644 index 0000000..0fe7174 --- /dev/null +++ b/WebCore/bridge/jni/jni_utility.cpp @@ -0,0 +1,584 @@ +/* + * 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. + */ + +#include "config.h" +#include "jni_utility.h" + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "jni_runtime.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include <runtime/JSArray.h> +#include <runtime/JSLock.h> +#include <dlfcn.h> + +namespace JSC { + +namespace Bindings { + +static jint KJS_GetCreatedJavaVMs(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; + + static jint(*functionPointer)(JavaVM**, jsize, jsize *) = 0; + if (!functionPointer) + functionPointer = (jint(*)(JavaVM**, jsize, jsize *))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 = KJS_GetCreatedJavaVMs(jvmArray, bufLen, &nJVMs); + if ( jniError == JNI_OK && nJVMs > 0 ) { + jvm = jvmArray[0]; + } + else + fprintf(stderr, "%s: JNI_GetCreatedJavaVMs failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError); + + return jvm; +} + +JNIEnv* getJNIEnv() +{ + union { + JNIEnv* env; + void* dummy; + } u; + jint jniError = 0; + + jniError = (getJavaVM())->AttachCurrentThread(&u.dummy, NULL); + if (jniError == JNI_OK) + return u.env; + else + fprintf(stderr, "%s: AttachCurrentThread failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError); + return NULL; +} + +jmethodID getMethodID (jobject obj, const char *name, const char *sig) +{ + JNIEnv *env = getJNIEnv(); + jmethodID mid = 0; + + if ( env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + 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((jstring)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((jstring)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) == 0) + type = byte_type; + else if (strcmp("short",name) == 0) + type = short_type; + else if (strcmp("int",name) == 0) + type = int_type; + else if (strcmp("long",name) == 0) + type = long_type; + else if (strcmp("float",name) == 0) + type = float_type; + else if (strcmp("double",name) == 0) + type = double_type; + else if (strcmp("char",name) == 0) + type = char_type; + else if (strcmp("boolean",name) == 0) + type = boolean_type; + else if (strcmp("void",name) == 0) + 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; + + bzero (&result, sizeof(jvalue)); + if ( obj != NULL && jvm != NULL && env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + jfieldID field = env->GetFieldID(cls, name, signature); + if ( field != NULL ) { + 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: + fprintf(stderr, "%s: invalid field type (%d)\n", __PRETTY_FUNCTION__, (int)type); + } + } + else + { + fprintf(stderr, "%s: Could not find field: %s\n", __PRETTY_FUNCTION__, name); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } + else { + fprintf(stderr, "%s: Could not find class for object\n", __PRETTY_FUNCTION__); + } + } + + return result; +} + +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 (0 == 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.data(), stringValue.size())); + } + } + 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.size() > 0) + value = ((const jchar*)stringValue.data())[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, JSValue* value, JNIType _JNIType, const char* javaClassName) +{ + JSLock lock(false); + + jvalue result; + + switch (_JNIType){ + case array_type: + case object_type: { + result.l = (jobject)0; + + // First see if we have a Java instance. + if (value->isObject()){ + JSObject* objectImp = asObject(value); + if (objectImp->classInfo() == &RuntimeObjectImp::s_info) { + RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(objectImp); + JavaInstance *instance = static_cast<JavaInstance*>(imp->getInternalInstance()); + if (instance) + result.l = instance->javaInstance(); + } + else if (objectImp->classInfo() == &RuntimeArray::s_info) { + // Input is a JavaScript Array that was originally created from a Java Array + RuntimeArray* imp = static_cast<RuntimeArray*>(objectImp); + JavaArray *array = static_cast<JavaArray*>(imp->getConcreteArray()); + result.l = array->javaArray(); + } + else if (objectImp->classInfo() == &JSArray::info) { + // Input is a Javascript Array. We need to create it to a Java Array. + result.l = convertArrayInstanceToJavaArray(exec, asArray(value), javaClassName); + } + } + + // 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 == 0 && strcmp(javaClassName, "java.lang.String") == 0) { +#ifdef CONVERT_NULL_TO_EMPTY_STRING + if (value->isNull()) { + JNIEnv *env = getJNIEnv(); + jchar buf[2]; + jobject javaString = env->functions->NewString (env, buf, 0); + result.l = javaString; + } + else +#else + if (!value->isNull()) +#endif + { + UString stringValue = value->toString(exec); + JNIEnv *env = getJNIEnv(); + jobject javaString = env->functions->NewString (env, (const jchar *)stringValue.data(), stringValue.size()); + result.l = javaString; + } + } else if (result.l == 0) + bzero (&result, sizeof(jvalue)); // Handle it the same as a void case + } + 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; + + break; + + case invalid_type: + default: + case void_type: { + bzero (&result, sizeof(jvalue)); + } + break; + } + return result; +} + +} // end of namespace Bindings + +} // end of namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_utility.h b/WebCore/bridge/jni/jni_utility.h new file mode 100644 index 0000000..b30c654 --- /dev/null +++ b/WebCore/bridge/jni/jni_utility.h @@ -0,0 +1,288 @@ +/* + * 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 _JNI_UTILITY_H_ +#define _JNI_UTILITY_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <runtime/JSValue.h> +#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 { + +class ExecState; +class JSObject; + +namespace Bindings { + +class JavaParameter; + +const char *getCharactersFromJString(jstring aJString); +void releaseCharactersForJString(jstring aJString, const char *s); + +const char *getCharactersFromJStringInEnv(JNIEnv *env, jstring aJString); +void releaseCharactersForJStringInEnv(JNIEnv *env, jstring aJString, const char *s); +const jchar *getUCharactersFromJStringInEnv(JNIEnv *env, jstring aJString); +void releaseUCharactersForJStringInEnv(JNIEnv *env, jstring aJString, const jchar *s); + +JNIType JNITypeFromClassName(const char *name); +JNIType JNITypeFromPrimitiveType(char type); +const char *signatureFromPrimitiveType(JNIType type); + +jvalue convertValueToJValue(ExecState*, JSValue*, JNIType, const char* javaClassName); + +jvalue getJNIField(jobject obj, JNIType type, const char *name, const char *signature); + +jmethodID getMethodID(jobject obj, 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) + { + return getJNIEnv()->CallVoidMethodA(obj, mid, args); + } + static void callV(jobject obj, jmethodID mid, va_list args) + { + return 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 != NULL && jvm != NULL && env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + return JNICaller<T>::callV(obj, mid, args); + } + else + { + fprintf(stderr, "%s: Could not find method: %s for %p\n", __PRETTY_FUNCTION__, name, obj); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } + else { + fprintf(stderr, "%s: Could not find class for %p\n", __PRETTY_FUNCTION__, 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 != NULL && jvm != NULL && env != NULL) { + jmethodID mid = env->GetStaticMethodID(cls, methodName, methodSignature); + if (mid != NULL) + result = JNICaller<T>::callStaticV(cls, mid, args); + else { + fprintf(stderr, "%s: Could not find method: %s for %p\n", __PRETTY_FUNCTION__, methodName, cls); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + } + + va_end(args); + + return result; +} + +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(MAC_JAVA_BRIDGE) + +#endif // _JNI_UTILITY_H_ diff --git a/WebCore/bridge/mac/AXObjectCacheMac.mm b/WebCore/bridge/mac/AXObjectCacheMac.mm deleted file mode 100644 index 61d0319..0000000 --- a/WebCore/bridge/mac/AXObjectCacheMac.mm +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 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. - */ - -#import "config.h" -#import "AXObjectCache.h" - -#import "Document.h" -#import "FoundationExtras.h" -#import "RenderObject.h" -#import "WebCoreAXObject.h" -#import "WebCoreViewFactory.h" - -// The simple Cocoa calls in this file don't throw exceptions. - -namespace WebCore { - -struct TextMarkerData { - AXID axID; - Node* node; - int offset; - EAffinity affinity; -}; - -bool AXObjectCache::gAccessibilityEnabled = false; - -AXObjectCache::~AXObjectCache() -{ - HashMap<RenderObject*, WebCoreAXObject*>::iterator end = m_objects.end(); - for (HashMap<RenderObject*, WebCoreAXObject*>::iterator it = m_objects.begin(); it != end; ++it) { - WebCoreAXObject* obj = (*it).second; - [obj detach]; - HardRelease(obj); - } -} - -WebCoreAXObject* AXObjectCache::get(RenderObject* renderer) -{ - WebCoreAXObject* obj = m_objects.get(renderer); - if (obj) - return obj; - - obj = [[WebCoreAXObject alloc] initWithRenderer:renderer]; - HardRetainWithNSRelease(obj); - m_objects.set(renderer, obj); - return obj; -} - -void AXObjectCache::remove(RenderObject* renderer) -{ - WebCoreAXObject* obj = m_objects.take(renderer); - if (!obj) - return; - [obj detach]; - HardRelease(obj); - - ASSERT(m_objects.size() >= m_idsInUse.size()); -} - -AXID AXObjectCache::getAXID(WebCoreAXObject* obj) -{ - // check for already-assigned ID - AXID objID = [obj axObjectID]; - if (objID) { - ASSERT(m_idsInUse.contains(objID)); - return objID; - } - - // generate a new ID - static AXID lastUsedID = 0; - objID = lastUsedID; - do - ++objID; - while (objID == 0 || objID == AXIDHashTraits::deletedValue() || m_idsInUse.contains(objID)); - m_idsInUse.add(objID); - lastUsedID = objID; - [obj setAXObjectID:objID]; - - return objID; -} - -void AXObjectCache::removeAXID(WebCoreAXObject* obj) -{ - AXID objID = [obj axObjectID]; - if (objID == 0) - return; - ASSERT(objID != AXIDHashTraits::deletedValue()); - ASSERT(m_idsInUse.contains(objID)); - [obj setAXObjectID:0]; - m_idsInUse.remove(objID); -} - -WebCoreTextMarker* AXObjectCache::textMarkerForVisiblePosition(const VisiblePosition& visiblePos) -{ - Position deepPos = visiblePos.deepEquivalent(); - Node* domNode = deepPos.node(); - ASSERT(domNode); - if (!domNode) - return nil; - - // locate the renderer, which must exist for a visible dom node - RenderObject* renderer = domNode->renderer(); - ASSERT(renderer); - - // find or create an accessibility object for this renderer - WebCoreAXObject* obj = get(renderer); - - // create a text marker, adding an ID for the WebCoreAXObject if needed - TextMarkerData textMarkerData; - textMarkerData.axID = getAXID(obj); - textMarkerData.node = domNode; - textMarkerData.offset = deepPos.offset(); - textMarkerData.affinity = visiblePos.affinity(); - return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)]; -} - -VisiblePosition AXObjectCache::visiblePositionForTextMarker(WebCoreTextMarker* textMarker) -{ - TextMarkerData textMarkerData; - - if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)]) - return VisiblePosition(); - - // return empty position if the text marker is no longer valid - if (!m_idsInUse.contains(textMarkerData.axID)) - return VisiblePosition(); - - // generate a VisiblePosition from the data we stored earlier - VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity); - - // make sure the node and offset still match (catches stale markers). affinity is not critical for this. - Position deepPos = visiblePos.deepEquivalent(); - if (deepPos.node() != textMarkerData.node || deepPos.offset() != textMarkerData.offset) - return VisiblePosition(); - - return visiblePos; -} - -void AXObjectCache::childrenChanged(RenderObject* renderer) -{ - WebCoreAXObject* obj = m_objects.get(renderer); - if (obj) - [obj childrenChanged]; -} - -void AXObjectCache::postNotification(RenderObject* renderer, const String& message) -{ - if (!renderer) - return; - - // notifications for text input objects are sent to that object - // all others are sent to the top WebArea - WebCoreAXObject* obj = [get(renderer) observableObject]; - if (obj) - NSAccessibilityPostNotification(obj, message); - else - NSAccessibilityPostNotification(get(renderer->document()->renderer()), message); -} - -void AXObjectCache::postNotificationToElement(RenderObject* renderer, const String& message) -{ - // send the notification to the specified element itself, not one of its ancestors - if (renderer) - NSAccessibilityPostNotification(get(renderer), message); -} - -void AXObjectCache::handleFocusedUIElementChanged() -{ - [[WebCoreViewFactory sharedFactory] accessibilityHandleFocusChanged]; -} - -} diff --git a/WebCore/bridge/mac/WebCoreAXObject.mm b/WebCore/bridge/mac/WebCoreAXObject.mm deleted file mode 100644 index bc5efda..0000000 --- a/WebCore/bridge/mac/WebCoreAXObject.mm +++ /dev/null @@ -1,2744 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006, 2007 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 "WebCoreAXObject.h" - -#import "DOMInternal.h" -#import "ColorMac.h" -#import "Document.h" -#import "EventNames.h" -#import "FocusController.h" -#import "Frame.h" -#import "FrameLoader.h" -#import "FrameView.h" -#import "HTMLAreaElement.h" -#import "HTMLCollection.h" -#import "HTMLFrameElementBase.h" -#import "HTMLImageElement.h" -#import "HTMLInputElement.h" -#import "HTMLLabelElement.h" -#import "HTMLMapElement.h" -#import "HTMLNames.h" -#import "HTMLSelectElement.h" -#import "HTMLTextAreaElement.h" -#import "HitTestRequest.h" -#import "HitTestResult.h" -#import "LocalizedStrings.h" -#import "NodeList.h" -#import "Page.h" -#import "RenderImage.h" -#import "RenderListMarker.h" -#import "RenderMenuList.h" -#import "RenderTextControl.h" -#import "RenderTheme.h" -#import "RenderView.h" -#import "RenderWidget.h" -#import "SelectionController.h" -#import "SimpleFontData.h" -#import "TextIterator.h" -#import "WebCoreFrameBridge.h" -#import "WebCoreFrameView.h" -#import "WebCoreObjCExtras.h" -#import "WebCoreViewFactory.h" -#import "htmlediting.h" -#import "kjs_html.h" -#import "visible_units.h" -#include <mach-o/dyld.h> - -using namespace WebCore; -using namespace EventNames; -using namespace HTMLNames; - -@interface WebCoreAXObject (PrivateWebCoreAXObject) -// forward declarations as needed -- (WebCoreTextMarker*)textMarkerForIndex: (NSNumber*) index lastIndexOK: (BOOL)lastIndexOK; -- (id)doAXLineForTextMarker: (WebCoreTextMarker* ) textMarker; -@end - -@implementation WebCoreAXObject - -#ifndef BUILDING_ON_TIGER -+ (void)initialize -{ - WebCoreObjCFinalizeOnMainThread(self); -} -#endif - --(id)initWithRenderer:(RenderObject*)renderer -{ - [super init]; - m_renderer = renderer; - return self; -} - --(BOOL)detached -{ - return !m_renderer; -} - --(void)detach -{ - // Send unregisterUniqueIdForUIElement unconditionally because if it is - // ever accidently not done (via other bugs in our AX implementation) you - // end up with a crash like <rdar://problem/4273149>. It is safe and not - // expensive to send even if the object is not registered. - [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self]; - [m_data release]; - m_data = 0; - [self removeAXObjectID]; - m_renderer = 0; - [self clearChildren]; -} - -- (void)dealloc -{ - [self detach]; - [super dealloc]; -} - -- (void)finalize -{ - [self detach]; - [super finalize]; -} - --(id)data -{ - return m_data; -} - --(void)setData:(id)data -{ - if (!m_renderer) - return; - - [data retain]; - [m_data release]; - m_data = data; -} - --(HTMLAnchorElement*)anchorElement -{ - // return already-known anchor for image areas - if (m_areaElement) - return m_areaElement; - - // search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though. - RenderObject* currRenderer; - for (currRenderer = m_renderer; currRenderer && !currRenderer->element(); currRenderer = currRenderer->parent()) { - if (currRenderer->continuation()) - return [currRenderer->document()->axObjectCache()->get(currRenderer->continuation()) anchorElement]; - } - - // bail of none found - if (!currRenderer) - return 0; - - // search up the DOM tree for an anchor element - // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement - Node* elt = currRenderer->element(); - for ( ; elt; elt = elt->parentNode()) { - if (elt->isLink() && elt->renderer() && !elt->renderer()->isImage()) - return static_cast<HTMLAnchorElement*>(elt); - } - - return 0; -} - --(BOOL)isImageButton -{ - return m_renderer->isImage() && m_renderer->element() && m_renderer->element()->hasTagName(inputTag); -} - --(Element*)mouseButtonListener -{ - // FIXME: Do the continuation search like anchorElement does - for (EventTargetNode* elt = static_cast<EventTargetNode*>(m_renderer->element()); elt; elt = static_cast<EventTargetNode*>(elt->parentNode())) { - if (elt->getHTMLEventListener(clickEvent) || elt->getHTMLEventListener(mousedownEvent) || elt->getHTMLEventListener(mouseupEvent)) - return static_cast<Element*>(elt); - } - - return 0; -} - --(Element*)actionElement -{ - if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); - if (!input->disabled() && (input->inputType() == HTMLInputElement::CHECKBOX || - input->inputType() == HTMLInputElement::RADIO || - input->isTextButton())) - return input; - } - - if ([self isImageButton] || m_renderer->isMenuList()) - return static_cast<Element*>(m_renderer->element()); - - Element* elt = [self anchorElement]; - if (!elt) - elt = [self mouseButtonListener]; - - return elt; -} - --(WebCoreAXObject*)firstChild -{ - if (!m_renderer || !m_renderer->firstChild()) - return nil; - - return m_renderer->document()->axObjectCache()->get(m_renderer->firstChild()); -} - --(WebCoreAXObject*)lastChild -{ - if (!m_renderer || !m_renderer->lastChild()) - return nil; - - return m_renderer->document()->axObjectCache()->get(m_renderer->lastChild()); -} - --(WebCoreAXObject*)previousSibling -{ - if (!m_renderer || !m_renderer->previousSibling()) - return nil; - - return m_renderer->document()->axObjectCache()->get(m_renderer->previousSibling()); -} - --(WebCoreAXObject*)nextSibling -{ - if (!m_renderer || !m_renderer->nextSibling()) - return nil; - - return m_renderer->document()->axObjectCache()->get(m_renderer->nextSibling()); -} - --(WebCoreAXObject*)parentObject -{ - if (m_areaElement) - return m_renderer->document()->axObjectCache()->get(m_renderer); - - if (!m_renderer || !m_renderer->parent()) - return nil; - - return m_renderer->document()->axObjectCache()->get(m_renderer->parent()); -} - --(WebCoreAXObject*)parentObjectUnignored -{ - WebCoreAXObject* obj = [self parentObject]; - if ([obj accessibilityIsIgnored]) - return [obj parentObjectUnignored]; - - return obj; -} - --(void)addChildrenToArray:(NSMutableArray*)array -{ - // nothing to add if there is no RenderObject - if (!m_renderer) - return; - - // try to add RenderWidget's children, but fall thru if there are none - if (m_renderer->isWidget()) { - RenderWidget* renderWidget = static_cast<RenderWidget*>(m_renderer); - Widget* widget = renderWidget->widget(); - if (widget) { - NSArray* childArr = [(widget->getOuterView()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute]; - [array addObjectsFromArray: childArr]; - return; - } - } - - // add all unignored acc children - for (WebCoreAXObject* obj = [self firstChild]; obj; obj = [obj nextSibling]) { - if ([obj accessibilityIsIgnored]) - [obj addChildrenToArray: array]; - else - [array addObject: obj]; - } - - // for a RenderImage, add the <area> elements as individual accessibility objects - if (m_renderer->isImage() && !m_areaElement) { - HTMLMapElement* map = static_cast<RenderImage*>(m_renderer)->imageMap(); - if (map) { - for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) { - // add an <area> element for this child if it has a link - // NOTE: can't cache these because they all have the same renderer, which is the cache key, right? - // plus there may be little reason to since they are being added to the handy array - if (current->isLink()) { - WebCoreAXObject* obj = [[[WebCoreAXObject alloc] initWithRenderer: m_renderer] autorelease]; - obj->m_areaElement = static_cast<HTMLAreaElement*>(current); - [array addObject: obj]; - } - } - } - } -} - --(BOOL)isWebArea -{ - return m_renderer->isRenderView(); -} - --(BOOL)isAnchor -{ - return m_areaElement || (!m_renderer->isImage() && m_renderer->element() && m_renderer->element()->isLink()); -} - --(BOOL)isTextControl -{ - return m_renderer->isTextField() || m_renderer->isTextArea(); -} - -static bool isPasswordFieldElement(Node* node) -{ - if (!node || !node->hasTagName(inputTag)) - return false; - - HTMLInputElement* input = static_cast<HTMLInputElement*>(node); - return input->inputType() == HTMLInputElement::PASSWORD; -} - --(BOOL)isPasswordField -{ - return m_renderer && isPasswordFieldElement(m_renderer->element()); -} - --(BOOL)isAttachment -{ - // widgets are the replaced elements that we represent to AX as attachments - BOOL result = m_renderer && m_renderer->isWidget(); - - // assert that a widget is a replaced element that is not an image - ASSERT(!result || (m_renderer->isReplaced() && !m_renderer->isImage())); - return result; -} - --(NSView*)attachmentView -{ - ASSERT(m_renderer->isReplaced() && m_renderer->isWidget() && !m_renderer->isImage()); - - RenderWidget* renderWidget = static_cast<RenderWidget*>(m_renderer); - Widget* widget = renderWidget->widget(); - if (widget) - return widget->getView(); - - return nil; -} - -static int blockquoteLevel(RenderObject* renderer) -{ - int result = 0; - for (Node* node = renderer->element(); node; node = node->parent()) { - if (node->hasTagName(blockquoteTag)) - result += 1; - } - - return result; -} - -static int headingLevel(RenderObject* renderer) -{ - if (!renderer->isBlockFlow()) - return 0; - - Node* node = renderer->element(); - if (!node) - return 0; - - if (node->hasTagName(h1Tag)) - return 1; - - if (node->hasTagName(h2Tag)) - return 2; - - if (node->hasTagName(h3Tag)) - return 3; - - if (node->hasTagName(h4Tag)) - return 4; - - if (node->hasTagName(h5Tag)) - return 5; - - if (node->hasTagName(h6Tag)) - return 6; - - return 0; -} - --(int)headingLevel -{ - return headingLevel(m_renderer); -} - --(BOOL)isHeading -{ - return [self headingLevel] != 0; -} - --(NSString*)role -{ - if (!m_renderer) - return NSAccessibilityUnknownRole; - - if (m_areaElement) - return @"AXLink"; - if (m_renderer->element() && m_renderer->element()->isLink()) { - if (m_renderer->isImage()) - return @"AXImageMap"; - return @"AXLink"; - } - if (m_renderer->isListMarker()) - return @"AXListMarker"; - if (m_renderer->element() && m_renderer->element()->hasTagName(buttonTag)) - return NSAccessibilityButtonRole; - if (m_renderer->isText()) - return NSAccessibilityStaticTextRole; - if (m_renderer->isImage()) { - if ([self isImageButton]) - return NSAccessibilityButtonRole; - return NSAccessibilityImageRole; - } - if ([self isWebArea]) - return @"AXWebArea"; - - if (m_renderer->isTextField()) - return NSAccessibilityTextFieldRole; - - if (m_renderer->isTextArea()) - return NSAccessibilityTextAreaRole; - - if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); - if (input->inputType() == HTMLInputElement::CHECKBOX) - return NSAccessibilityCheckBoxRole; - if (input->inputType() == HTMLInputElement::RADIO) - return NSAccessibilityRadioButtonRole; - if (input->isTextButton()) - return NSAccessibilityButtonRole; - } - - if (m_renderer->isMenuList()) - return NSAccessibilityPopUpButtonRole; - - if ([self isHeading]) - return @"AXHeading"; - - if (m_renderer->isBlockFlow()) - return NSAccessibilityGroupRole; - if ([self isAttachment]) - return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute]; - - return NSAccessibilityUnknownRole; -} - --(NSString*)subrole -{ - if ([self isPasswordField]) - return NSAccessibilitySecureTextFieldSubrole; - - if ([self isAttachment]) { - NSView* attachmentView = [self attachmentView]; - if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) { - return [attachmentView accessibilityAttributeValue:NSAccessibilitySubroleAttribute]; - } - } - - return nil; -} - --(NSString*)roleDescription -{ - if (!m_renderer) - return nil; - - // attachments have the AXImage role, but a different subrole - if ([self isAttachment]) - return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute]; - - // FIXME 3447564: It would be better to call some AppKit API to get these strings - // (which would be the best way to localize them) - - NSString* role = [self role]; - if ([role isEqualToString:NSAccessibilityButtonRole]) - return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityPopUpButtonRole]) - return NSAccessibilityRoleDescription(NSAccessibilityPopUpButtonRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityStaticTextRole]) - return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityImageRole]) - return NSAccessibilityRoleDescription(NSAccessibilityImageRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityGroupRole]) - return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityCheckBoxRole]) - return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityRadioButtonRole]) - return NSAccessibilityRoleDescription(NSAccessibilityRadioButtonRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityTextFieldRole]) - return NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, [self subrole]); - - if ([role isEqualToString:NSAccessibilityTextAreaRole]) - return NSAccessibilityRoleDescription(NSAccessibilityTextAreaRole, [self subrole]); - - if ([role isEqualToString:@"AXWebArea"]) - return AXWebAreaText(); - - if ([role isEqualToString:@"AXLink"]) - return AXLinkText(); - - if ([role isEqualToString:@"AXListMarker"]) - return AXListMarkerText(); - - if ([role isEqualToString:@"AXImageMap"]) - return AXImageMapText(); - - if ([role isEqualToString:@"AXHeading"]) - return AXHeadingText(); - - return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil); -} - --(NSString*)helpText -{ - if (!m_renderer) - return nil; - - if (m_areaElement) { - const AtomicString& summary = static_cast<Element*>(m_areaElement)->getAttribute(summaryAttr); - if (!summary.isEmpty()) - return summary; - const AtomicString& title = static_cast<Element*>(m_areaElement)->getAttribute(titleAttr); - if (!title.isEmpty()) - return title; - } - - for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) { - if (curr->element() && curr->element()->isHTMLElement()) { - const AtomicString& summary = static_cast<Element*>(curr->element())->getAttribute(summaryAttr); - if (!summary.isEmpty()) - return summary; - const AtomicString& title = static_cast<Element*>(curr->element())->getAttribute(titleAttr); - if (!title.isEmpty()) - return title; - } - } - - return nil; -} - --(NSString*)textUnderElement -{ - if (!m_renderer) - return nil; - - Node* e = m_renderer->element(); - Document* d = m_renderer->document(); - if (e && d) { - Frame* p = d->frame(); - if (p) { - // catch stale WebCoreAXObject (see <rdar://problem/3960196>) - if (p->document() != d) - return nil; - return plainText(rangeOfContents(e).get()); - } - } - - // return nil for anonymous text because it is non-trivial to get - // the actual text and, so far, that is not needed - return nil; -} - --(id)value -{ - if (!m_renderer || m_areaElement || [self isPasswordField]) - return nil; - - if (m_renderer->isText()) - return [self textUnderElement]; - - if (m_renderer->isMenuList()) - return static_cast<RenderMenuList*>(m_renderer)->text(); - - if (m_renderer->isListMarker()) - return static_cast<RenderListMarker*>(m_renderer)->text(); - - if ([self isWebArea]) { - if (m_renderer->document()->frame()) - return nil; - - // FIXME: should use startOfDocument and endOfDocument (or rangeForDocument?) here - VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates(0, 0); - VisiblePosition endVisiblePosition = m_renderer->positionForCoordinates(INT_MAX, INT_MAX); - if (startVisiblePosition.isNull() || endVisiblePosition.isNull()) - return nil; - - return plainText(makeRange(startVisiblePosition, endVisiblePosition).get()); - } - - if ([self isAttachment]) { - NSView* attachmentView = [self attachmentView]; - if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute]) - return [attachmentView accessibilityAttributeValue:NSAccessibilityValueAttribute]; - return nil; - } - - if ([self isHeading]) - return [NSNumber numberWithInt:[self headingLevel]]; - - if ([self isTextControl]) - return (NSString*)(static_cast<RenderTextControl*>(m_renderer)->text()); - - if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); - - // Checkboxes return their state as an integer. 0 for off, 1 for on. - if (input->inputType() == HTMLInputElement::CHECKBOX || - input->inputType() == HTMLInputElement::RADIO) - return [NSNumber numberWithInt:input->checked()]; - } - - // FIXME: We might need to implement a value here for more types - // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one; - // this would require subclassing or making accessibilityAttributeNames do something other than return a - // single static array. - return nil; -} - -static HTMLLabelElement* labelForElement(Element* element) -{ - RefPtr<NodeList> list = element->document()->getElementsByTagName("label"); - unsigned len = list->length(); - for (unsigned i = 0; i < len; i++) { - HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i)); - if (label->correspondingControl() == element) - return label; - } - - return 0; -} - --(NSString*)title -{ - if (!m_renderer || m_areaElement || !m_renderer->element()) - return nil; - - if (m_renderer->element()->hasTagName(buttonTag)) - return [self textUnderElement]; - - if (m_renderer->element()->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); - if (input->isTextButton()) - return input->value(); - - HTMLLabelElement* label = labelForElement(input); - if (label) - return label->innerText(); - } - - if (m_renderer->element()->isLink() || [self isHeading]) - return [self textUnderElement]; - - if ([self isAttachment]) { - NSView* attachmentView = [self attachmentView]; - if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute]) - return [attachmentView accessibilityAttributeValue:NSAccessibilityTitleAttribute]; - } - - return nil; -} - -- (NSString*)accessibilityDescription -{ - if (!m_renderer || m_areaElement) - return nil; - - if (m_renderer->isImage()) { - if (m_renderer->element() && m_renderer->element()->isHTMLElement()) { - const AtomicString& alt = static_cast<Element*>(m_renderer->element())->getAttribute(altAttr); - if (alt.isEmpty()) - return nil; - return alt; - } - } else if ([self isAttachment]) { - NSView* attachmentView = [self attachmentView]; - if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute]) - return [attachmentView accessibilityAttributeValue:NSAccessibilityDescriptionAttribute]; - } - - if ([self isWebArea]) { - Document *document = m_renderer->document(); - Node* owner = document->ownerElement(); - if (owner) { - if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) { - HTMLFrameElementBase* frameElement = static_cast<HTMLFrameElementBase*>(owner); - return frameElement->name(); - } else if (owner->isHTMLElement()) { - return static_cast<Element*>(owner)->getAttribute(nameAttr); - } - } else { - owner = document->body(); - if (owner && owner->isHTMLElement()) - return static_cast<Element*>(owner)->getAttribute(nameAttr); - } - } - - return nil; -} - -static IntRect boundingBoxRect(RenderObject* obj) -{ - IntRect rect; - if (obj) { - if (obj->isInlineContinuation()) - obj = obj->element()->renderer(); - Vector<IntRect> rects; - int x, y; - obj->absolutePosition(x, y); - obj->absoluteRects(rects, x, y); - const size_t n = rects.size(); - for (size_t i = 0; i < n; ++i) { - IntRect r = rects[i]; - if (!r.isEmpty()) { - if (obj->style()->hasAppearance()) - theme()->adjustRepaintRect(obj, r); - rect.unite(r); - } - } - } - return rect; -} - --(NSValue*)position -{ - IntRect rect = m_areaElement ? m_areaElement->getRect(m_renderer) : boundingBoxRect(m_renderer); - - // The Cocoa accessibility API wants the lower-left corner. - NSPoint point = NSMakePoint(rect.x(), rect.bottom()); - if (m_renderer && m_renderer->view() && m_renderer->view()->frameView()) { - NSView* view = m_renderer->view()->frameView()->getDocumentView(); - point = [[view window] convertBaseToScreen: [view convertPoint: point toView:nil]]; - } - - return [NSValue valueWithPoint: point]; -} - --(NSValue*)size -{ - IntRect rect = m_areaElement ? m_areaElement->getRect(m_renderer) : boundingBoxRect(m_renderer); - return [NSValue valueWithSize: NSMakeSize(rect.width(), rect.height())]; -} - -// accessibilityShouldUseUniqueId is an AppKit method we override so that -// objects will be given a unique ID, and therefore allow AppKit to know when they -// become obsolete (e.g. when the user navigates to a new web page, making this one -// unrendered but not deallocated because it is in the back/forward cache). -// It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the -// appropriate place (e.g. dealloc) to remove these non-retained references from -// AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement. -// -// Registering an object is also required for observing notifications. Only registered objects can be observed. -- (BOOL)accessibilityShouldUseUniqueId { - if (!m_renderer) - return NO; - - if ([self isWebArea]) - return YES; - - if ([self isTextControl]) - return YES; - - return NO; -} - --(BOOL)accessibilityIsIgnored -{ - // ignore invisible element - if (!m_renderer || m_renderer->style()->visibility() != VISIBLE) - return YES; - - // ignore popup menu items because AppKit does - for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) { - if (parent->isMenuList()) - return YES; - } - - // NOTE: BRs always have text boxes now, so the text box check here can be removed - if (m_renderer->isText()) - return m_renderer->isBR() || !static_cast<RenderText*>(m_renderer)->firstTextBox(); - - // delegate to the attachment - if ([self isAttachment]) - return [[self attachmentView] accessibilityIsIgnored]; - - if (m_areaElement || (m_renderer->element() && m_renderer->element()->isLink())) - return NO; - - // all controls are accessible - if (m_renderer->element() && m_renderer->element()->isControl()) - return NO; - - if (m_renderer->isBlockFlow() && m_renderer->childrenInline()) - return !static_cast<RenderBlock*>(m_renderer)->firstLineBox() && ![self mouseButtonListener]; - - // ignore images seemingly used as spacers - if (m_renderer->isImage()) { - // informal standard is to ignore images with zero-length alt strings - Element* elt = static_cast<Element*>(m_renderer->element()); - if (elt) { - const AtomicString& alt = elt->getAttribute(altAttr); - if (alt.isEmpty() && !alt.isNull()) - return YES; - } - - // check for one-dimensional image - if (m_renderer->height() <= 1 || m_renderer->width() <= 1) - return YES; - - // check whether rendered image was stretched from one-dimensional file image - RenderImage* image = static_cast<RenderImage*>(m_renderer); - if (image->cachedImage()) { - IntSize imageSize = image->cachedImage()->imageSize(); - return (imageSize.height() <= 1 || imageSize.width() <= 1); - } - - return NO; - } - - return (!m_renderer->isListMarker() && ![self isWebArea]); -} - -- (NSArray*)accessibilityAttributeNames -{ - if ([self isAttachment]) - return [[self attachmentView] accessibilityAttributeNames]; - - static NSArray* attributes = nil; - static NSArray* anchorAttrs = nil; - static NSArray* webAreaAttrs = nil; - static NSArray* textAttrs = nil; - NSMutableArray* tempArray; - if (attributes == nil) { - attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute, - NSAccessibilitySubroleAttribute, - NSAccessibilityRoleDescriptionAttribute, - NSAccessibilityChildrenAttribute, - NSAccessibilityHelpAttribute, - NSAccessibilityParentAttribute, - NSAccessibilityPositionAttribute, - NSAccessibilitySizeAttribute, - NSAccessibilityTitleAttribute, - NSAccessibilityDescriptionAttribute, - NSAccessibilityValueAttribute, - NSAccessibilityFocusedAttribute, - NSAccessibilityEnabledAttribute, - NSAccessibilityWindowAttribute, - @"AXSelectedTextMarkerRange", - @"AXStartTextMarker", - @"AXEndTextMarker", - @"AXVisited", - nil]; - } - if (anchorAttrs == nil) { - tempArray = [[NSMutableArray alloc] initWithArray:attributes]; - [tempArray addObject: NSAccessibilityURLAttribute]; - anchorAttrs = [[NSArray alloc] initWithArray:tempArray]; - [tempArray release]; - } - if (webAreaAttrs == nil) { - tempArray = [[NSMutableArray alloc] initWithArray:attributes]; - [tempArray addObject: @"AXLinkUIElements"]; - [tempArray addObject: @"AXLoaded"]; - [tempArray addObject: @"AXLayoutCount"]; - webAreaAttrs = [[NSArray alloc] initWithArray:tempArray]; - [tempArray release]; - } - if (textAttrs == nil) { - tempArray = [[NSMutableArray alloc] initWithArray:attributes]; - [tempArray addObject: NSAccessibilityNumberOfCharactersAttribute]; - [tempArray addObject: NSAccessibilitySelectedTextAttribute]; - [tempArray addObject: NSAccessibilitySelectedTextRangeAttribute]; - [tempArray addObject: NSAccessibilityVisibleCharacterRangeAttribute]; - [tempArray addObject: NSAccessibilityInsertionPointLineNumberAttribute]; - textAttrs = [[NSArray alloc] initWithArray:tempArray]; - [tempArray release]; - } - - if (!m_renderer || [self isPasswordField]) - return attributes; - - if ([self isWebArea]) - return webAreaAttrs; - - if ([self isTextControl]) - return textAttrs; - - if ([self isAnchor] || m_renderer->isImage()) - return anchorAttrs; - - return attributes; -} - -- (NSArray*)accessibilityActionNames -{ - static NSArray* actions = nil; - - if (actions == nil) { - if ([self actionElement]) - actions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, nil]; - else if ([self isAttachment]) - actions = [[[self attachmentView] accessibilityActionNames] retain]; - } - - return actions; -} - -- (NSString*)accessibilityActionDescription:(NSString*)action -{ - // we have no custom actions - return NSAccessibilityActionDescription(action); -} - -- (void)accessibilityPerformAction:(NSString*)action -{ - if ([action isEqualToString:NSAccessibilityPressAction]) { - if ([self isAttachment]) { - [[self attachmentView] accessibilityPerformAction:action]; - return; - } - - Element* actionElement = [self actionElement]; - if (!actionElement) - return; - if (Frame* f = actionElement->document()->frame()) - f->loader()->resetMultipleFormSubmissionProtection(); - actionElement->accessKeyAction(true); - } -} - -- (WebCoreTextMarkerRange*) textMarkerRangeFromMarkers: (WebCoreTextMarker*) textMarker1 andEndMarker:(WebCoreTextMarker*) textMarker2 -{ - return [[WebCoreViewFactory sharedFactory] textMarkerRangeWithStart:textMarker1 end:textMarker2]; -} - -- (WebCoreTextMarker*) textMarkerForVisiblePosition: (VisiblePosition)visiblePos -{ - if (visiblePos.isNull()) - return nil; - - if (isPasswordFieldElement(visiblePos.deepEquivalent().node())) - return nil; - - return m_renderer->document()->axObjectCache()->textMarkerForVisiblePosition(visiblePos); -} - -- (VisiblePosition) visiblePositionForTextMarker: (WebCoreTextMarker*)textMarker -{ - return m_renderer->document()->axObjectCache()->visiblePositionForTextMarker(textMarker); -} - -- (VisiblePosition) visiblePositionForStartOfTextMarkerRange: (WebCoreTextMarkerRange*)textMarkerRange -{ - return [self visiblePositionForTextMarker:[[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]]; -} - -- (VisiblePosition) visiblePositionForEndOfTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange -{ - return [self visiblePositionForTextMarker:[[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]]; -} - -- (WebCoreTextMarkerRange*) textMarkerRangeFromVisiblePositions: (VisiblePosition) startPosition andEndPos: (VisiblePosition) endPosition -{ - WebCoreTextMarker* startTextMarker = [self textMarkerForVisiblePosition: startPosition]; - WebCoreTextMarker* endTextMarker = [self textMarkerForVisiblePosition: endPosition]; - return [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker]; -} - -- (WebCoreTextMarkerRange*)textMarkerRange -{ - if (!m_renderer) - return nil; - - // construct VisiblePositions for start and end - Node* node = m_renderer->element(); - VisiblePosition visiblePos1 = VisiblePosition(node, 0, VP_DEFAULT_AFFINITY); - VisiblePosition visiblePos2 = VisiblePosition(node, maxDeepOffset(node), VP_DEFAULT_AFFINITY); - - // the VisiblePositions are equal for nodes like buttons, so adjust for that - if (visiblePos1 == visiblePos2) { - visiblePos2 = visiblePos2.next(); - if (visiblePos2.isNull()) - visiblePos2 = visiblePos1; - } - - WebCoreTextMarker* startTextMarker = [self textMarkerForVisiblePosition: visiblePos1]; - WebCoreTextMarker* endTextMarker = [self textMarkerForVisiblePosition: visiblePos2]; - return [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker]; -} - -- (RenderObject*)topRenderer -{ - return m_renderer->document()->topDocument()->renderer(); -} - -- (FrameView*)frameView -{ - return m_renderer->document()->view(); -} - -- (FrameView*)topFrameView -{ - return m_renderer->document()->topDocument()->renderer()->view()->frameView(); -} - -- (id)accessibilityAttributeValue:(NSString*)attributeName -{ - if (!m_renderer) - return nil; - - if ([attributeName isEqualToString: NSAccessibilityRoleAttribute]) - return [self role]; - - if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute]) - return [self subrole]; - - if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute]) - return [self roleDescription]; - - if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) { - if (m_renderer->isRenderView() && m_renderer->view() && m_renderer->view()->frameView()) - return m_renderer->view()->frameView()->getView(); - return [self parentObjectUnignored]; - } - - if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) { - if (!m_children) { - m_children = [NSMutableArray arrayWithCapacity: 8]; - [m_children retain]; - [self addChildrenToArray: m_children]; - } - return m_children; - } - - if ([self isWebArea]) { - if ([attributeName isEqualToString: @"AXLinkUIElements"]) { - NSMutableArray* links = [NSMutableArray arrayWithCapacity: 32]; - RefPtr<HTMLCollection> coll = m_renderer->document()->links(); - Node* curr = coll->firstItem(); - while (curr) { - RenderObject* obj = curr->renderer(); - if (obj) { - WebCoreAXObject* axobj = obj->document()->axObjectCache()->get(obj); - ASSERT([[axobj role] isEqualToString:@"AXLink"]); - if (![axobj accessibilityIsIgnored]) - [links addObject: axobj]; - } - curr = coll->nextItem(); - } - return links; - } - if ([attributeName isEqualToString: @"AXLoaded"]) - return [NSNumber numberWithBool: (!m_renderer->document()->tokenizer())]; - if ([attributeName isEqualToString: @"AXLayoutCount"]) - return [NSNumber numberWithInt: (static_cast<RenderView*>(m_renderer)->frameView()->layoutCount())]; - } - - if ([self isTextControl]) { - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) - return [self isPasswordField] ? nil : [NSNumber numberWithUnsignedInt: textControl->text().length()]; - if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) { - if ([self isPasswordField]) - return nil; - NSString* text = textControl->text(); - return [text substringWithRange: NSMakeRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart())]; - } - if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) - return [self isPasswordField] ? nil : [NSValue valueWithRange: NSMakeRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart())]; - // TODO: Get actual visible range. <rdar://problem/4712101> - if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) - return [self isPasswordField] ? nil : [NSValue valueWithRange: NSMakeRange(0, textControl->text().length())]; - if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) { - if ([self isPasswordField] || textControl->selectionStart() != textControl->selectionEnd()) - return nil; - NSNumber* index = [NSNumber numberWithInt: textControl->selectionStart()]; - return [self doAXLineForTextMarker: [self textMarkerForIndex: index lastIndexOK: YES]]; - } - } - - if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) { - if ([self isAnchor]) { - HTMLAnchorElement* anchor = [self anchorElement]; - if (anchor) { - DeprecatedString s = anchor->getAttribute(hrefAttr).deprecatedString(); - if (!s.isNull()) { - s = anchor->document()->completeURL(s); - return KURL(s).getNSURL(); - } - } - } - else if (m_renderer->isImage() && m_renderer->element() && m_renderer->element()->hasTagName(imgTag)) { - DeprecatedString src = static_cast<HTMLImageElement*>(m_renderer->element())->src().deprecatedString(); - if (!src.isNull()) - return KURL(src).getNSURL(); - } - return nil; - } - - if ([attributeName isEqualToString: @"AXVisited"]) - return [NSNumber numberWithBool: m_renderer->style()->pseudoState() == PseudoVisited]; - - if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) - return [self title]; - - if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) - return [self accessibilityDescription]; - - if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) - return [self value]; - - if ([attributeName isEqualToString: NSAccessibilityHelpAttribute]) - return [self helpText]; - - if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) - return [NSNumber numberWithBool: (m_renderer->element() && m_renderer->document()->focusedNode() == m_renderer->element())]; - - if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute]) - return [NSNumber numberWithBool: m_renderer->element() ? m_renderer->element()->isEnabled() : YES]; - - if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) - return [self size]; - - if ([attributeName isEqualToString: NSAccessibilityPositionAttribute]) - return [self position]; - - if ([attributeName isEqualToString: NSAccessibilityWindowAttribute]) { - if (m_renderer && m_renderer->view() && m_renderer->view()->frameView()) - return [m_renderer->view()->frameView()->getView() window]; - - return nil; - } - - if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) { - // get the selection from the document - Selection selection = [self frameView]->frame()->selectionController()->selection(); - if (selection.isNone()) - return nil; - - return (id) [self textMarkerRangeFromVisiblePositions:selection.visibleStart() andEndPos:selection.visibleEnd()]; - } - - if ([attributeName isEqualToString: @"AXStartTextMarker"]) - return (id) [self textMarkerForVisiblePosition: startOfDocument(m_renderer->document())]; - - if ([attributeName isEqualToString: @"AXEndTextMarker"]) - return (id) [self textMarkerForVisiblePosition: endOfDocument(m_renderer->document())]; - - return nil; -} - -- (NSArray* )accessibilityParameterizedAttributeNames -{ - if ([self isAttachment]) - return nil; - - static NSArray* paramAttrs = nil; - static NSArray* textParamAttrs = nil; - if (paramAttrs == nil) { - paramAttrs = [[NSArray alloc] initWithObjects: - @"AXUIElementForTextMarker", - @"AXTextMarkerRangeForUIElement", - @"AXLineForTextMarker", - @"AXTextMarkerRangeForLine", - @"AXStringForTextMarkerRange", - @"AXTextMarkerForPosition", - @"AXBoundsForTextMarkerRange", - @"AXAttributedStringForTextMarkerRange", - @"AXTextMarkerRangeForUnorderedTextMarkers", - @"AXNextTextMarkerForTextMarker", - @"AXPreviousTextMarkerForTextMarker", - @"AXLeftWordTextMarkerRangeForTextMarker", - @"AXRightWordTextMarkerRangeForTextMarker", - @"AXLeftLineTextMarkerRangeForTextMarker", - @"AXRightLineTextMarkerRangeForTextMarker", - @"AXSentenceTextMarkerRangeForTextMarker", - @"AXParagraphTextMarkerRangeForTextMarker", - @"AXNextWordEndTextMarkerForTextMarker", - @"AXPreviousWordStartTextMarkerForTextMarker", - @"AXNextLineEndTextMarkerForTextMarker", - @"AXPreviousLineStartTextMarkerForTextMarker", - @"AXNextSentenceEndTextMarkerForTextMarker", - @"AXPreviousSentenceStartTextMarkerForTextMarker", - @"AXNextParagraphEndTextMarkerForTextMarker", - @"AXPreviousParagraphStartTextMarkerForTextMarker", - @"AXStyleTextMarkerRangeForTextMarker", - @"AXLengthForTextMarkerRange", - nil]; - } - - if (textParamAttrs == nil) { - NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs]; - [tempArray addObject: (NSString*)kAXLineForIndexParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXRangeForLineParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXStringForRangeParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXRangeForPositionParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXRangeForIndexParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXBoundsForRangeParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXRTFForRangeParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXAttributedStringForRangeParameterizedAttribute]; - [tempArray addObject: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]; - textParamAttrs = [[NSArray alloc] initWithArray:tempArray]; - [tempArray release]; - } - - if ([self isPasswordField]) - return [NSArray array]; - - if (!m_renderer) - return paramAttrs; - - if ([self isTextControl]) - return textParamAttrs; - - return paramAttrs; -} - -- (id)doAXUIElementForTextMarker: (WebCoreTextMarker* ) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - RenderObject* obj = visiblePos.deepEquivalent().node()->renderer(); - if (!obj) - return nil; - - return obj->document()->axObjectCache()->get(obj); -} - -- (id)doAXTextMarkerRangeForUIElement: (id) uiElement -{ - return (id)[uiElement textMarkerRange]; -} - -- (id)doAXLineForTextMarker: (WebCoreTextMarker* ) textMarker -{ - unsigned int lineCount = 0; - VisiblePosition savedVisiblePos; - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // move up until we get to the top - // NOTE: BUG This only takes us to the top of the rootEditableElement, not the top of the - // top document. - while (visiblePos.isNotNull() && !(inSameLine(visiblePos, savedVisiblePos))) { - lineCount += 1; - savedVisiblePos = visiblePos; - visiblePos = previousLinePosition(visiblePos, 0); - } - - return [NSNumber numberWithUnsignedInt:(lineCount - 1)]; -} - -- (id)doAXTextMarkerRangeForLine: (NSNumber*) lineNumber -{ - unsigned lineCount = [lineNumber unsignedIntValue]; - if (lineCount == 0 || !m_renderer) - return nil; - - // iterate over the lines - // NOTE: BUG this is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the - // last offset of the last line - VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForCoordinates(0, 0); - VisiblePosition savedVisiblePos; - while (--lineCount != 0) { - savedVisiblePos = visiblePos; - visiblePos = nextLinePosition(visiblePos, 0); - if (visiblePos.isNull() || visiblePos == savedVisiblePos) - return nil; - } - - // make a caret selection for the marker position, then extend it to the line - // NOTE: ignores results of sel.modify because it returns false when - // starting at an empty line. The resulting selection in that case - // will be a caret at visiblePos. - SelectionController selectionController; - selectionController.setSelection(Selection(visiblePos)); - selectionController.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary); - - // return a marker range for the selection start to end - VisiblePosition startPosition = selectionController.selection().visibleStart(); - VisiblePosition endPosition = selectionController.selection().visibleEnd(); - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -static NSString *nsStringForReplacedNode(Node* replacedNode) -{ - // we should always be given a rendered node and a replaced node, but be safe - // replaced nodes are either attachments (widgets) or images - if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) { - ASSERT_NOT_REACHED(); - return nil; - } - - // create an AX object, but skip it if it is not supposed to be seen - WebCoreAXObject* obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer()); - if ([obj accessibilityIsIgnored]) - return nil; - - // use the attachmentCharacter to represent the replaced node - const UniChar attachmentChar = NSAttachmentCharacter; - return [NSString stringWithCharacters:&attachmentChar length:1]; -} - -- (id)doAXStringForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange -{ - // extract the start and end VisiblePosition - VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange]; - if (startVisiblePosition.isNull()) - return nil; - - VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange]; - if (endVisiblePosition.isNull()) - return nil; - - NSMutableString* resultString = [[[NSMutableString alloc] init] autorelease]; - TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get()); - while (!it.atEnd()) { - // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) - if (it.length() != 0) { - [resultString appendString:[NSString stringWithCharacters:it.characters() length:it.length()]]; - } else { - // locate the node and starting offset for this replaced range - int exception = 0; - Node* node = it.range()->startContainer(exception); - ASSERT(node == it.range()->endContainer(exception)); - int offset = it.range()->startOffset(exception); - NSString* attachmentString = nsStringForReplacedNode(node->childNode(offset)); - - // append the replacement string - if (attachmentString) - [resultString appendString:attachmentString]; - } - it.advance(); - } - - return [resultString length] > 0 ? resultString : nil; -} - -- (id)doAXTextMarkerForPosition: (NSPoint) point -{ - // convert absolute point to view coordinates - FrameView* frameView = [self topFrameView]; - NSView* view = frameView->getDocumentView(); - RenderObject* renderer = [self topRenderer]; - Node* innerNode = 0; - - // locate the node containing the point - IntPoint pointResult; - while (1) { - // ask the document layer to hitTest - NSPoint windowCoord = [[view window] convertScreenToBase: point]; - IntPoint ourpoint([view convertPoint:windowCoord fromView:nil]); - - HitTestRequest request(true, true); - HitTestResult result(ourpoint); - renderer->layer()->hitTest(request, result); - innerNode = result.innerNode(); - if (!innerNode || !innerNode->renderer()) - return nil; - - pointResult = result.localPoint(); - - // done if hit something other than a widget - renderer = innerNode->renderer(); - if (!renderer->isWidget()) - break; - - // descend into widget (FRAME, IFRAME, OBJECT...) - Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); - if (!widget || !widget->isFrameView()) - break; - Frame* frame = static_cast<FrameView*>(widget)->frame(); - if (!frame) - break; - Document* document = frame->document(); - if (!document) - break; - renderer = document->renderer(); - frameView = static_cast<FrameView*>(widget); - view = frameView->getDocumentView(); - } - - // get position within the node - VisiblePosition pos = innerNode->renderer()->positionForPoint(pointResult); - return (id) [self textMarkerForVisiblePosition:pos]; -} - -- (id)doAXBoundsForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange -{ - // extract the start and end VisiblePosition - VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange]; - if (startVisiblePosition.isNull()) - return nil; - - VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange]; - if (endVisiblePosition.isNull()) - return nil; - - IntRect rect1 = startVisiblePosition.caretRect(); - IntRect rect2 = endVisiblePosition.caretRect(); - - // readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds - if (rect2.y() != rect1.y()) { - VisiblePosition endOfFirstLine = endOfLine(startVisiblePosition); - if (startVisiblePosition == endOfFirstLine) { - startVisiblePosition.setAffinity(DOWNSTREAM); - rect1 = startVisiblePosition.caretRect(); - } - if (endVisiblePosition == endOfFirstLine) { - endVisiblePosition.setAffinity(UPSTREAM); - rect2 = endVisiblePosition.caretRect(); - } - } - - IntRect ourrect = rect1; - ourrect.unite(rect2); - - // try to use the document view from the first position, so that nested WebAreas work, - // but fall back to the top level doc if we do not find it easily - RenderObject* renderer = startVisiblePosition.deepEquivalent().node()->renderer(); - FrameView* frameView = renderer ? renderer->document()->view() : 0; - if (!frameView) - frameView = [self frameView]; - NSView *view = frameView->getView(); - - // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead - if (rect1.bottom() != rect2.bottom()) { - RefPtr<Range> dataRange = makeRange(startVisiblePosition, endVisiblePosition); - IntRect boundingBox = dataRange->boundingBox(); - String rangeString = plainText(dataRange.get()); - if (rangeString.length() > 1 && !boundingBox.isEmpty()) - ourrect = boundingBox; - } - - // convert our rectangle to screen coordinates - NSRect rect = ourrect; - rect = NSOffsetRect(rect, -frameView->contentsX(), -frameView->contentsY()); - rect = [view convertRect:rect toView:nil]; - rect.origin = [[view window] convertBaseToScreen:rect.origin]; - - // return the converted rect - return [NSValue valueWithRect:rect]; -} - -static CGColorRef CreateCGColorIfDifferent(NSColor* nsColor, CGColorRef existingColor) -{ - // get color information assuming NSDeviceRGBColorSpace - NSColor* rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - if (rgbColor == nil) - rgbColor = [NSColor blackColor]; - CGFloat components[4]; - [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]]; - - // create a new CGColorRef to return - CGColorSpaceRef cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - CGColorRef cgColor = CGColorCreate(cgColorSpace, components); - CGColorSpaceRelease(cgColorSpace); - CFMakeCollectable(cgColor); - - // check for match with existing color - if (existingColor && CGColorEqualToColor(cgColor, existingColor)) - cgColor = nil; - - return cgColor; -} - -static void AXAttributeStringSetColor(NSMutableAttributedString* attrString, NSString* attribute, NSColor* color, NSRange range) -{ - if (color) { - CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil]; - CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor); - if (cgColor) { - [attrString addAttribute:attribute value:(id)cgColor range:range]; - CGColorRelease(cgColor); - } - } else - [attrString removeAttribute:attribute range:range]; -} - -static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range) -{ - if (number) - [attrString addAttribute:attribute value:number range:range]; - else - [attrString removeAttribute:attribute range:range]; -} - -static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, NSString* attribute, NSFont* font, NSRange range) -{ - NSDictionary* dict; - - if (font) { - dict = [NSDictionary dictionaryWithObjectsAndKeys: - [font fontName] , NSAccessibilityFontNameKey, - [font familyName] , NSAccessibilityFontFamilyKey, - [font displayName] , NSAccessibilityVisibleNameKey, - [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey, - nil]; - - [attrString addAttribute:attribute value:dict range:range]; - } else - [attrString removeAttribute:attribute range:range]; - -} - -static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) -{ - RenderStyle* style = renderer->style(); - - // set basic font info - AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().primaryFont()->getNSFont(), range); - - // set basic colors - AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttribute, nsColor(style->color()), range); - AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, nsColor(style->backgroundColor()), range); - - // set super/sub scripting - EVerticalAlign alignment = style->verticalAlign(); - if (alignment == SUB) - AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range); - else if (alignment == SUPER) - AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range); - else - [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range]; - - // set shadow - if (style->textShadow()) - AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range); - else - [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range]; - - // set underline and strikethrough - int decor = style->textDecorationsInEffect(); - if ((decor & UNDERLINE) == 0) { - [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range]; - [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range]; - } - - if ((decor & LINE_THROUGH) == 0) { - [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range]; - [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range]; - } - - if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) { - // find colors using quirk mode approach (strict mode would use current - // color for all but the root line box, which would use getTextDecorationColors) - Color underline, overline, linethrough; - renderer->getTextDecorationColors(decor, underline, overline, linethrough); - - if ((decor & UNDERLINE) != 0) { - AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range); - AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, nsColor(underline), range); - } - - if ((decor & LINE_THROUGH) != 0) { - AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range); - AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, nsColor(linethrough), range); - } - } -} - -static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) -{ - int parentHeadingLevel = headingLevel(renderer->parent()); - - if (parentHeadingLevel) - [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range]; - else - [attrString removeAttribute:@"AXHeadingLevel" range:range]; -} - -static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) -{ - int quoteLevel = blockquoteLevel(renderer); - - if (quoteLevel) - [attrString addAttribute:@"AXBlockQuoteLevel" value:[NSNumber numberWithInt:quoteLevel] range:range]; - else - [attrString removeAttribute:@"AXBlockQuoteLevel" range:range]; -} - -static void AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, id element, NSRange range) -{ - if (element) { - // make a serialiazable AX object - AXUIElementRef axElement = [[WebCoreViewFactory sharedFactory] AXUIElementForElement:element]; - if (axElement) { - [attrString addAttribute:attribute value:(id)axElement range:range]; - CFRelease(axElement); - } - } else - [attrString removeAttribute:attribute range:range]; -} - -static WebCoreAXObject* AXLinkElementForNode (Node* node) -{ - RenderObject* obj = node->renderer(); - if (!obj) - return nil; - - WebCoreAXObject* axObj = obj->document()->axObjectCache()->get(obj); - HTMLAnchorElement* anchor = [axObj anchorElement]; - if (!anchor || !anchor->renderer()) - return nil; - - return anchor->renderer()->document()->axObjectCache()->get(anchor->renderer()); -} - -static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, Node* node, int offset, NSRange range) -{ - Vector<DocumentMarker> markers = node->renderer()->document()->markersForNode(node); - Vector<DocumentMarker>::iterator markerIt = markers.begin(); - - unsigned endOffset = (unsigned)offset + range.length; - for ( ; markerIt != markers.end(); markerIt++) { - DocumentMarker marker = *markerIt; - - if (marker.type != DocumentMarker::Spelling) - continue; - - if (marker.endOffset <= (unsigned)offset) - continue; - - if (marker.startOffset > endOffset) - break; - - // add misspelling attribute for the intersection of the marker and the range - int rStart = range.location + (marker.startOffset - offset); - int rLength = MIN(marker.endOffset, endOffset) - marker.startOffset; - NSRange spellRange = NSMakeRange(rStart, rLength); - AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange); - - if (marker.endOffset > endOffset + 1) - break; - } -} - -static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, int offset, const UChar* chars, int length) -{ - // skip invisible text - if (!node->renderer()) - return; - - // easier to calculate the range before appending the string - NSRange attrStringRange = NSMakeRange([attrString length], length); - - // append the string from this node - [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]]; - - // add new attributes and remove irrelevant inherited ones - // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge - // identical colors. Workaround is to not replace an existing color attribute if it matches what we are adding. This also means - // we can not just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually. - - // remove inherited attachment from prior AXAttributedStringAppendReplaced - [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange]; - - // set new attributes - AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange); - AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange); - AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange); - AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AXLinkElementForNode(node), attrStringRange); - - // do spelling last because it tends to break up the range - AXAttributeStringSetSpelling(attrString, node, offset, attrStringRange); -} - -- (id)doAXAttributedStringForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange -{ - // extract the start and end VisiblePosition - VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange]; - if (startVisiblePosition.isNull()) - return nil; - - VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange]; - if (endVisiblePosition.isNull()) - return nil; - - // iterate over the range to build the AX attributed string - NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init]; - TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get()); - while (!it.atEnd()) { - // locate the node and starting offset for this range - int exception = 0; - Node* node = it.range()->startContainer(exception); - ASSERT(node == it.range()->endContainer(exception)); - int offset = it.range()->startOffset(exception); - - // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) - if (it.length() != 0) { - AXAttributedStringAppendText(attrString, node, offset, it.characters(), it.length()); - } else { - Node* replacedNode = node->childNode(offset); - NSString *attachmentString = nsStringForReplacedNode(replacedNode); - if (attachmentString) { - NSRange attrStringRange = NSMakeRange([attrString length], [attachmentString length]); - - // append the placeholder string - [[attrString mutableString] appendString:attachmentString]; - - // remove all inherited attributes - [attrString setAttributes:nil range:attrStringRange]; - - // add the attachment attribute - WebCoreAXObject* obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer()); - AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange); - } - } - it.advance(); - } - - return [attrString autorelease]; -} - -- (id)doAXTextMarkerRangeForUnorderedTextMarkers: (NSArray*) markers -{ - // get and validate the markers - if ([markers count] < 2) - return nil; - - WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [markers objectAtIndex:0]; - WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [markers objectAtIndex:1]; - if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1] || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2]) - return nil; - - // convert to VisiblePosition - VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:textMarker1]; - VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:textMarker2]; - if (visiblePos1.isNull() || visiblePos2.isNull()) - return nil; - - WebCoreTextMarker* startTextMarker; - WebCoreTextMarker* endTextMarker; - bool alreadyInOrder; - - // upstream is ordered before downstream for the same position - if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM) - alreadyInOrder = false; - - // use selection order to see if the positions are in order - else - alreadyInOrder = Selection(visiblePos1, visiblePos2).isBaseFirst(); - - if (alreadyInOrder) { - startTextMarker = textMarker1; - endTextMarker = textMarker2; - } else { - startTextMarker = textMarker2; - endTextMarker = textMarker1; - } - - return (id) [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker]; -} - -- (id)doAXNextTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - VisiblePosition nextVisiblePos = visiblePos.next(); - if (nextVisiblePos.isNull()) - return nil; - - return (id) [self textMarkerForVisiblePosition:nextVisiblePos]; -} - -- (id)doAXPreviousTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - VisiblePosition previousVisiblePos = visiblePos.previous(); - if (previousVisiblePos.isNull()) - return nil; - - return (id) [self textMarkerForVisiblePosition:previousVisiblePos]; -} - -- (id)doAXLeftWordTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary); - VisiblePosition endPosition = endOfWord(startPosition); - - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -- (id)doAXRightWordTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary); - VisiblePosition endPosition = endOfWord(startPosition); - - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - - -static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition) -{ - // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line. - // So let's update the position to include that. - VisiblePosition tempPosition; - VisiblePosition startPosition = visiblePosition; - Position p; - RenderObject* renderer; - while (true) { - tempPosition = startPosition.previous(); - if (tempPosition.isNull()) - break; - p = tempPosition.deepEquivalent(); - if (!p.node()) - break; - renderer = p.node()->renderer(); - if (!renderer || renderer->inlineBox(p.offset(), tempPosition.affinity()) || (renderer->isRenderBlock() && p.offset() == 0)) - break; - startPosition = tempPosition; - } - - return startPosition; -} - -- (id)doAXLeftLineTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make a caret selection for the position before marker position (to make sure - // we move off of a line start) - VisiblePosition prevVisiblePos = visiblePos.previous(); - if (prevVisiblePos.isNull()) - return nil; - - VisiblePosition startPosition = startOfLine(prevVisiblePos); - - // keep searching for a valid line start position. Unless the textmarker is at the very beginning, there should - // always be a valid line range. However, startOfLine will return null for position next to a floating object, - // since floating object doesn't really belong to any line. - // This check will reposition the marker before the floating object, to ensure we get a line start. - if (startPosition.isNull()) { - while (startPosition.isNull() && prevVisiblePos.isNotNull()) { - prevVisiblePos = prevVisiblePos.previous(); - startPosition = startOfLine(prevVisiblePos); - } - } else - startPosition = updateAXLineStartForVisiblePosition(startPosition); - - VisiblePosition endPosition = endOfLine(prevVisiblePos); - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -- (id)doAXRightLineTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a line end - VisiblePosition nextVisiblePos = visiblePos.next(); - if (nextVisiblePos.isNull()) - return nil; - - VisiblePosition startPosition = startOfLine(nextVisiblePos); - - // fetch for a valid line start position - if (startPosition.isNull() ) { - startPosition = visiblePos; - nextVisiblePos = nextVisiblePos.next(); - } else - startPosition = updateAXLineStartForVisiblePosition(startPosition); - - VisiblePosition endPosition = endOfLine(nextVisiblePos); - - // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position - // Unless the textmarker is at the very end, there should always be a valid line range. However, endOfLine will - // return null for position by a floating object, since floating object doesn't really belong to any line. - // This check will reposition the marker after the floating object, to ensure we get a line end. - while (endPosition.isNull() && nextVisiblePos.isNotNull()) { - nextVisiblePos = nextVisiblePos.next(); - endPosition = endOfLine(nextVisiblePos); - } - - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -- (id)doAXSentenceTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker -{ - // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer) - // Related? <rdar://problem/3927736> Text selection broken in 8A336 - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - VisiblePosition startPosition = startOfSentence(visiblePos); - VisiblePosition endPosition = endOfSentence(startPosition); - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -- (id)doAXParagraphTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - VisiblePosition startPosition = startOfParagraph(visiblePos); - VisiblePosition endPosition = endOfParagraph(startPosition); - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -- (id)doAXNextWordEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a word end - visiblePos = visiblePos.next(); - if (visiblePos.isNull()) - return nil; - - VisiblePosition endPosition = endOfWord(visiblePos, LeftWordIfOnBoundary); - return (id) [self textMarkerForVisiblePosition:endPosition]; -} - -- (id)doAXPreviousWordStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a word start - visiblePos = visiblePos.previous(); - if (visiblePos.isNull()) - return nil; - - VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary); - return (id) [self textMarkerForVisiblePosition:startPosition]; -} - -- (id)doAXNextLineEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // to make sure we move off of a line end - VisiblePosition nextVisiblePos = visiblePos.next(); - if (nextVisiblePos.isNull()) - return nil; - - VisiblePosition endPosition = endOfLine(nextVisiblePos); - - // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position - // There are cases like when the position is next to a floating object that'll return null for end of line. This code will avoid returning null. - while (endPosition.isNull() && nextVisiblePos.isNotNull()) { - nextVisiblePos = nextVisiblePos.next(); - endPosition = endOfLine(nextVisiblePos); - } - - return (id) [self textMarkerForVisiblePosition: endPosition]; -} - -- (id)doAXPreviousLineStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a line start - VisiblePosition prevVisiblePos = visiblePos.previous(); - if (prevVisiblePos.isNull()) - return nil; - - VisiblePosition startPosition = startOfLine(prevVisiblePos); - - // as long as the position hasn't reached the beginning of the doc, keep searching for a valid line start position - // There are cases like when the position is next to a floating object that'll return null for start of line. This code will avoid returning null. - if (startPosition.isNull()) { - while (startPosition.isNull() && prevVisiblePos.isNotNull()) { - prevVisiblePos = prevVisiblePos.previous(); - startPosition = startOfLine(prevVisiblePos); - } - } else - startPosition = updateAXLineStartForVisiblePosition(startPosition); - - return (id) [self textMarkerForVisiblePosition: startPosition]; -} - -- (id)doAXNextSentenceEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer) - // Related? <rdar://problem/3927736> Text selection broken in 8A336 - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a sentence end - VisiblePosition nextVisiblePos = visiblePos.next(); - if (nextVisiblePos.isNull()) - return nil; - - // an empty line is considered a sentence. If it's skipped, then the sentence parser will not - // see this empty line. Instead, return the end position of the empty line. - VisiblePosition endPosition; - String lineString = plainText(makeRange(startOfLine(visiblePos), endOfLine(visiblePos)).get()); - if (lineString.isEmpty()) - endPosition = nextVisiblePos; - else - endPosition = endOfSentence(nextVisiblePos); - - return (id) [self textMarkerForVisiblePosition: endPosition]; -} - -- (id)doAXPreviousSentenceStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer) - // Related? <rdar://problem/3927736> Text selection broken in 8A336 - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a sentence start - VisiblePosition previousVisiblePos = visiblePos.previous(); - if (previousVisiblePos.isNull()) - return nil; - - // treat empty line as a separate sentence. - VisiblePosition startPosition; - String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get()); - if (lineString.isEmpty()) - startPosition = previousVisiblePos; - else - startPosition = startOfSentence(previousVisiblePos); - - return (id) [self textMarkerForVisiblePosition: startPosition]; -} - -- (id)doAXNextParagraphEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a paragraph end - visiblePos = visiblePos.next(); - if (visiblePos.isNull()) - return nil; - - VisiblePosition endPosition = endOfParagraph(visiblePos); - return (id) [self textMarkerForVisiblePosition: endPosition]; -} - -- (id)doAXPreviousParagraphStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - // make sure we move off of a paragraph start - visiblePos = visiblePos.previous(); - if (visiblePos.isNull()) - return nil; - - VisiblePosition startPosition = startOfParagraph(visiblePos); - return (id) [self textMarkerForVisiblePosition: startPosition]; -} - -static VisiblePosition startOfStyleRange (const VisiblePosition visiblePos) -{ - RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer(); - RenderObject* startRenderer = renderer; - RenderStyle* style = renderer->style(); - - // traverse backward by renderer to look for style change - for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) { - // skip non-leaf nodes - if (r->firstChild()) - continue; - - // stop at style change - if (r->style() != style) - break; - - // remember match - startRenderer = r; - } - - return VisiblePosition(startRenderer->node(), 0, VP_DEFAULT_AFFINITY); -} - -static VisiblePosition endOfStyleRange (const VisiblePosition visiblePos) -{ - RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer(); - RenderObject* endRenderer = renderer; - RenderStyle* style = renderer->style(); - - // traverse forward by renderer to look for style change - for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) { - // skip non-leaf nodes - if (r->firstChild()) - continue; - - // stop at style change - if (r->style() != style) - break; - - // remember match - endRenderer = r; - } - - return VisiblePosition(endRenderer->node(), maxDeepOffset(endRenderer->node()), VP_DEFAULT_AFFINITY); -} - -- (id)doAXStyleTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker -{ - VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker]; - if (visiblePos.isNull()) - return nil; - - VisiblePosition startPosition = startOfStyleRange(visiblePos); - VisiblePosition endPosition = endOfStyleRange(visiblePos); - return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -- (id)doAXLengthForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange -{ - // NOTE: BUG Multi-byte support - CFStringRef string = (CFStringRef) [self doAXStringForTextMarkerRange: textMarkerRange]; - if (!string) - return nil; - - return [NSNumber numberWithInt:CFStringGetLength(string)]; -} - -// NOTE: Consider providing this utility method as AX API -- (WebCoreTextMarker*)textMarkerForIndex: (NSNumber*) index lastIndexOK: (BOOL)lastIndexOK -{ - ASSERT(m_renderer->isTextField() || m_renderer->isTextArea()); - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - unsigned int indexValue = [index unsignedIntValue]; - - // lastIndexOK specifies whether the position after the last character is acceptable - if (indexValue >= textControl->text().length()) { - if (!lastIndexOK || indexValue > textControl->text().length()) - return nil; - } - VisiblePosition position = textControl->visiblePositionForIndex(indexValue); - position.setAffinity(DOWNSTREAM); - return [self textMarkerForVisiblePosition: position]; -} - -// NOTE: Consider providing this utility method as AX API -- (NSNumber*)indexForTextMarker: (WebCoreTextMarker*) marker -{ - ASSERT(m_renderer->isTextField() || m_renderer->isTextArea()); - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - - VisiblePosition position = [self visiblePositionForTextMarker: marker]; - Node* node = position.deepEquivalent().node(); - if (!node) - return nil; - - for (RenderObject* renderer = node->renderer(); renderer && renderer->element(); renderer = renderer->parent()) { - if (renderer == textControl) - return [NSNumber numberWithInt: textControl->indexForVisiblePosition(position)]; - } - - return nil; -} - -// NOTE: Consider providing this utility method as AX API -- (WebCoreTextMarkerRange*)textMarkerRangeForRange: (NSRange) range -{ - ASSERT(m_renderer->isTextField() || m_renderer->isTextArea()); - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - if (range.location + range.length > textControl->text().length()) - return nil; - - VisiblePosition startPosition = textControl->visiblePositionForIndex(range.location); - startPosition.setAffinity(DOWNSTREAM); - VisiblePosition endPosition = textControl->visiblePositionForIndex(range.location + range.length); - return [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition]; -} - -// NOTE: Consider providing this utility method as AX API -- (NSValue*)rangeForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange -{ - WebCoreTextMarker* textMarker1 = [[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]; - WebCoreTextMarker* textMarker2 = [[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]; - NSNumber* index1 = [self indexForTextMarker: textMarker1]; - NSNumber* index2 = [self indexForTextMarker: textMarker2]; - if (!index1 || !index2 || [index1 unsignedIntValue] > [index2 unsignedIntValue]) - return nil; - - return [NSValue valueWithRange: NSMakeRange([index1 unsignedIntValue], [index2 unsignedIntValue] - [index1 unsignedIntValue])]; -} - -// Given an indexed character, the line number of the text associated with this accessibility -// object that contains the character. -- (id)doAXLineForIndex: (NSNumber*) index -{ - return [self doAXLineForTextMarker: [self textMarkerForIndex: index lastIndexOK: NO]]; -} - -// Given a line number, the range of characters of the text associated with this accessibility -// object that contains the line number. -- (id)doAXRangeForLine: (NSNumber*) lineNumber -{ - ASSERT(m_renderer->isTextField() || m_renderer->isTextArea()); - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - - // iterate to the specified line - VisiblePosition visiblePos = textControl->visiblePositionForIndex(0); - VisiblePosition savedVisiblePos; - for (unsigned lineCount = [lineNumber unsignedIntValue]; lineCount != 0; lineCount -= 1) { - savedVisiblePos = visiblePos; - visiblePos = nextLinePosition(visiblePos, 0); - if (visiblePos.isNull() || visiblePos == savedVisiblePos) - return nil; - } - - // make a caret selection for the marker position, then extend it to the line - // NOTE: ignores results of selectionController.modify because it returns false when - // starting at an empty line. The resulting selection in that case - // will be a caret at visiblePos. - SelectionController selectionController; - selectionController.setSelection(Selection(visiblePos)); - selectionController.modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary); - selectionController.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary); - - // calculate the indices for the selection start and end - VisiblePosition startPosition = selectionController.selection().visibleStart(); - VisiblePosition endPosition = selectionController.selection().visibleEnd(); - int index1 = textControl->indexForVisiblePosition(startPosition); - int index2 = textControl->indexForVisiblePosition(endPosition); - - // add one to the end index for a line break not caused by soft line wrap (to match AppKit) - if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull()) - index2 += 1; - - // return nil rather than an zero-length range (to match AppKit) - if (index1 == index2) - return nil; - - return [NSValue valueWithRange: NSMakeRange(index1, index2 - index1)]; -} - -// A substring of the text associated with this accessibility object that is -// specified by the given character range. -- (id)doAXStringForRange: (NSRange) range -{ - if ([self isPasswordField]) - return nil; - - if (range.length == 0) - return @""; - - ASSERT(m_renderer->isTextField() || m_renderer->isTextArea()); - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - String text = textControl->text(); - if (range.location + range.length > text.length()) - return nil; - - return text.substring(range.location, range.length); -} - -// The composed character range in the text associated with this accessibility object that -// is specified by the given screen coordinates. This parameterized attribute returns the -// complete range of characters (including surrogate pairs of multi-byte glyphs) at the given -// screen coordinates. -// NOTE: This varies from AppKit when the point is below the last line. AppKit returns an -// an error in that case. We return textControl->text().length(), 1. Does this matter? -- (id)doAXRangeForPosition: (NSPoint) point -{ - NSNumber* index = [self indexForTextMarker: [self doAXTextMarkerForPosition: point]]; - if (!index) - return nil; - - return [NSValue valueWithRange: NSMakeRange([index unsignedIntValue], 1)]; -} - -// The composed character range in the text associated with this accessibility object that -// is specified by the given index value. This parameterized attribute returns the complete -// range of characters (including surrogate pairs of multi-byte glyphs) at the given index. -- (id)doAXRangeForIndex: (NSNumber*) number -{ - ASSERT(m_renderer->isTextField() || m_renderer->isTextArea()); - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - String text = textControl->text(); - if (!text.length() || [number unsignedIntValue] > text.length() - 1) - return nil; - - return [NSValue valueWithRange: NSMakeRange([number unsignedIntValue], 1)]; -} - -// The bounding rectangle of the text associated with this accessibility object that is -// specified by the given range. This is the bounding rectangle a sighted user would see -// on the display screen, in pixels. -- (id)doAXBoundsForRange: (NSRange) range -{ - return [self doAXBoundsForTextMarkerRange: [self textMarkerRangeForRange:range]]; -} - -// The CFAttributedStringType representation of the text associated with this accessibility -// object that is specified by the given range. -- (id)doAXAttributedStringForRange: (NSRange) range -{ - return [self doAXAttributedStringForTextMarkerRange: [self textMarkerRangeForRange:range]]; -} - -// The RTF representation of the text associated with this accessibility object that is -// specified by the given range. -- (id)doAXRTFForRange: (NSRange) range -{ - NSAttributedString* attrString = [self doAXAttributedStringForRange: range]; - return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil]; -} - -// Given a character index, the range of text associated with this accessibility object -// over which the style in effect at that character index applies. -- (id)doAXStyleRangeForIndex: (NSNumber*) index -{ - WebCoreTextMarkerRange* textMarkerRange = [self doAXStyleTextMarkerRangeForTextMarker: [self textMarkerForIndex: index lastIndexOK: NO]]; - return [self rangeForTextMarkerRange: textMarkerRange]; -} - -- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter -{ - WebCoreTextMarker* textMarker = nil; - WebCoreTextMarkerRange* textMarkerRange = nil; - NSNumber* number = nil; - NSArray* array = nil; - WebCoreAXObject* uiElement = nil; - NSPoint point = NSZeroPoint; - bool pointSet = false; - NSRange range = {0, 0}; - bool rangeSet = false; - - // basic parameter validation - if (!m_renderer || !attribute || !parameter) - return nil; - - // common parameter type check/casting. Nil checks in handlers catch wrong type case. - // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from - // a parameter of the wrong type. - if ([[WebCoreViewFactory sharedFactory] objectIsTextMarker:parameter]) - textMarker = (WebCoreTextMarker*) parameter; - - else if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:parameter]) - textMarkerRange = (WebCoreTextMarkerRange*) parameter; - - else if ([parameter isKindOfClass:[WebCoreAXObject self]]) - uiElement = (WebCoreAXObject*) parameter; - - else if ([parameter isKindOfClass:[NSNumber self]]) - number = parameter; - - else if ([parameter isKindOfClass:[NSArray self]]) - array = parameter; - - else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) { - pointSet = true; - point = [(NSValue*)parameter pointValue]; - - } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) { - rangeSet = true; - range = [(NSValue*)parameter rangeValue]; - - } else { - // got a parameter of a type we never use - // NOTE: No ASSERT_NOT_REACHED because this can happen accidentally - // while using accesstool (e.g.), forcing you to start over - return nil; - } - - // dispatch - if ([attribute isEqualToString: @"AXUIElementForTextMarker"]) - return [self doAXUIElementForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"]) - return [self doAXTextMarkerRangeForUIElement: uiElement]; - - if ([attribute isEqualToString: @"AXLineForTextMarker"]) - return [self doAXLineForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXTextMarkerRangeForLine"]) - return [self doAXTextMarkerRangeForLine: number]; - - if ([attribute isEqualToString: @"AXStringForTextMarkerRange"]) - return [self doAXStringForTextMarkerRange: textMarkerRange]; - - if ([attribute isEqualToString: @"AXTextMarkerForPosition"]) - return pointSet ? [self doAXTextMarkerForPosition: point] : nil; - - if ([attribute isEqualToString: @"AXBoundsForTextMarkerRange"]) - return [self doAXBoundsForTextMarkerRange: textMarkerRange]; - - if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"]) - return [self doAXAttributedStringForTextMarkerRange: textMarkerRange]; - - if ([attribute isEqualToString: @"AXTextMarkerRangeForUnorderedTextMarkers"]) - return [self doAXTextMarkerRangeForUnorderedTextMarkers: array]; - - if ([attribute isEqualToString: @"AXNextTextMarkerForTextMarker"]) - return [self doAXNextTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXPreviousTextMarkerForTextMarker"]) - return [self doAXPreviousTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXLeftWordTextMarkerRangeForTextMarker"]) - return [self doAXLeftWordTextMarkerRangeForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXRightWordTextMarkerRangeForTextMarker"]) - return [self doAXRightWordTextMarkerRangeForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXLeftLineTextMarkerRangeForTextMarker"]) - return [self doAXLeftLineTextMarkerRangeForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXRightLineTextMarkerRangeForTextMarker"]) - return [self doAXRightLineTextMarkerRangeForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXSentenceTextMarkerRangeForTextMarker"]) - return [self doAXSentenceTextMarkerRangeForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXParagraphTextMarkerRangeForTextMarker"]) - return [self doAXParagraphTextMarkerRangeForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXNextWordEndTextMarkerForTextMarker"]) - return [self doAXNextWordEndTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXPreviousWordStartTextMarkerForTextMarker"]) - return [self doAXPreviousWordStartTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXNextLineEndTextMarkerForTextMarker"]) - return [self doAXNextLineEndTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXPreviousLineStartTextMarkerForTextMarker"]) - return [self doAXPreviousLineStartTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXNextSentenceEndTextMarkerForTextMarker"]) - return [self doAXNextSentenceEndTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXPreviousSentenceStartTextMarkerForTextMarker"]) - return [self doAXPreviousSentenceStartTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXNextParagraphEndTextMarkerForTextMarker"]) - return [self doAXNextParagraphEndTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXPreviousParagraphStartTextMarkerForTextMarker"]) - return [self doAXPreviousParagraphStartTextMarkerForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXStyleTextMarkerRangeForTextMarker"]) - return [self doAXStyleTextMarkerRangeForTextMarker: textMarker]; - - if ([attribute isEqualToString: @"AXLengthForTextMarkerRange"]) - return [self doAXLengthForTextMarkerRange: textMarkerRange]; - - if ([self isTextControl]) { - if ([attribute isEqualToString: (NSString*)kAXLineForIndexParameterizedAttribute]) - return [self doAXLineForIndex: number]; - - if ([attribute isEqualToString: (NSString*)kAXRangeForLineParameterizedAttribute]) - return [self doAXRangeForLine: number]; - - if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute]) - return rangeSet ? [self doAXStringForRange: range] : nil; - - if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) - return pointSet ? [self doAXRangeForPosition: point] : nil; - - if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) - return [self doAXRangeForIndex: number]; - - if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) - return rangeSet ? [self doAXBoundsForRange: range] : nil; - - if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute]) - return rangeSet ? [self doAXRTFForRange: range] : nil; - - if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute]) - return rangeSet ? [self doAXAttributedStringForRange: range] : nil; - - if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) - return [self doAXStyleRangeForIndex: number]; - } - - return nil; -} - -- (id)accessibilityHitTest:(NSPoint)point -{ - if (!m_renderer) - return NSAccessibilityUnignoredAncestor(self); - - HitTestRequest request(true, true); - HitTestResult result = HitTestResult(IntPoint(point)); - m_renderer->layer()->hitTest(request, result); - if (!result.innerNode()) - return NSAccessibilityUnignoredAncestor(self); - Node* node = result.innerNode()->shadowAncestorNode(); - RenderObject* obj = node->renderer(); - if (!obj) - return NSAccessibilityUnignoredAncestor(self); - - return NSAccessibilityUnignoredAncestor(obj->document()->axObjectCache()->get(obj)); -} - -- (RenderObject*)rendererForView:(NSView*)view -{ - // check for WebKit NSView that lets us find its bridge - WebCoreFrameBridge* bridge = nil; - if ([view conformsToProtocol:@protocol(WebCoreBridgeHolder)]) { - NSView<WebCoreBridgeHolder>* bridgeHolder = (NSView<WebCoreBridgeHolder>*)view; - bridge = [bridgeHolder webCoreBridge]; - } - - Frame* frame = [bridge _frame]; - if (!frame) - return nil; - - Document* document = frame->document(); - if (!document) - return nil; - - Node* node = document->ownerElement(); - if (!node) - return nil; - - return node->renderer(); -} - -// _accessibilityParentForSubview is called by AppKit when moving up the tree -// we override it so that we can return our WebCoreAXObject parent of an AppKit AX object -- (id)_accessibilityParentForSubview:(NSView*)subview -{ - RenderObject* renderer = [self rendererForView:subview]; - if (!renderer) - return nil; - - WebCoreAXObject* obj = renderer->document()->axObjectCache()->get(renderer); - return [obj parentObjectUnignored]; -} - -- (id)accessibilityFocusedUIElement -{ - // get the focused node in the page - Page* page = m_renderer->document()->page(); - if (!page) - return nil; - - Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document(); - Node* focusedNode = focusedDocument->focusedNode(); - if (!focusedNode || !focusedNode->renderer()) - focusedNode = focusedDocument; - - WebCoreAXObject* obj = focusedNode->renderer()->document()->axObjectCache()->get(focusedNode->renderer()); - - // the HTML element, for example, is focusable but has an AX object that is ignored - if ([obj accessibilityIsIgnored]) - obj = [obj parentObjectUnignored]; - - return obj; -} - -- (void)doSetAXSelectedTextMarkerRange: (WebCoreTextMarkerRange*)textMarkerRange -{ - // extract the start and end VisiblePosition - VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange]; - if (startVisiblePosition.isNull()) - return; - - VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange]; - if (endVisiblePosition.isNull()) - return; - - // make selection and tell the document to use it - Selection newSelection = Selection(startVisiblePosition, endVisiblePosition); - m_renderer->document()->frame()->selectionController()->setSelection(newSelection); -} - -- (BOOL)canSetFocusAttribute -{ - // NOTE: It would be more accurate to ask the document whether setFocusedNode() would - // do anything. For example, it setFocusedNode() will do nothing if the current focused - // node will not relinquish the focus. - if (!m_renderer->element() || !m_renderer->element()->isEnabled()) - return NO; - - NSString* role = [self role]; - if ([role isEqualToString:@"AXLink"] || - [role isEqualToString:NSAccessibilityTextFieldRole] || - [role isEqualToString:NSAccessibilityTextAreaRole] || - [role isEqualToString:NSAccessibilityButtonRole] || - [role isEqualToString:NSAccessibilityPopUpButtonRole] || - [role isEqualToString:NSAccessibilityCheckBoxRole] || - [role isEqualToString:NSAccessibilityRadioButtonRole]) - return YES; - - return NO; -} - -- (BOOL)canSetValueAttribute -{ - return [self isTextControl]; -} - -- (BOOL)canSetTextRangeAttributes -{ - return [self isTextControl]; -} - -- (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName -{ - if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) - return YES; - - if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) - return [self canSetFocusAttribute]; - - if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) - return [self canSetValueAttribute]; - - if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] || - [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] || - [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) - return [self canSetTextRangeAttributes]; - - return NO; -} - -- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName -{ - WebCoreTextMarkerRange* textMarkerRange = nil; - NSNumber* number = nil; - NSString* string = nil; - NSRange range = {0, 0}; - - // decode the parameter - if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:value]) - textMarkerRange = (WebCoreTextMarkerRange*) value; - - else if ([value isKindOfClass:[NSNumber self]]) - number = value; - - else if ([value isKindOfClass:[NSString self]]) - string = value; - - else if ([value isKindOfClass:[NSValue self]]) - range = [value rangeValue]; - - // handle the command - if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) { - ASSERT(textMarkerRange); - [self doSetAXSelectedTextMarkerRange:textMarkerRange]; - - } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) { - ASSERT(number); - if ([self canSetFocusAttribute] && number) { - if ([number intValue] == 0) - m_renderer->document()->setFocusedNode(0); - else { - if (m_renderer->element()->isElementNode()) - static_cast<Element*>(m_renderer->element())->focus(); - else - m_renderer->document()->setFocusedNode(m_renderer->element()); - } - } - } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) { - if (!string) - return; - if (m_renderer->isTextField()) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); - input->setValue(string); - } else if (m_renderer->isTextArea()) { - HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->element()); - textArea->setValue(string); - } - } else if ([self isTextControl]) { - RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); - if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) { - // TODO: set selected text (ReplaceSelectionCommand). <rdar://problem/4712125> - } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) { - textControl->setSelectionRange(range.location, range.location + range.length); - } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) { - // TODO: make range visible (scrollRectToVisible). <rdar://problem/4712101> - } - } -} - -- (WebCoreAXObject*)observableObject -{ - for (RenderObject* renderer = m_renderer; renderer && renderer->element(); renderer = renderer->parent()) { - if (renderer->isTextField() || renderer->isTextArea()) - return renderer->document()->axObjectCache()->get(renderer); - } - - return nil; -} - -- (void)childrenChanged -{ - [self clearChildren]; - - if ([self accessibilityIsIgnored]) - [[self parentObject] childrenChanged]; -} - -- (void)clearChildren -{ - [m_children release]; - m_children = nil; -} - --(AXID)axObjectID -{ - return m_id; -} - --(void)setAXObjectID:(AXID) axObjectID -{ - m_id = axObjectID; -} - -- (void)removeAXObjectID -{ - if (!m_id) - return; - - m_renderer->document()->axObjectCache()->removeAXID(self); -} - -@end diff --git a/WebCore/bridge/mac/WebCoreScriptDebugger.h b/WebCore/bridge/mac/WebCoreScriptDebugger.h deleted file mode 100644 index fd56632..0000000 --- a/WebCore/bridge/mac/WebCoreScriptDebugger.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2005 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 OR ITS 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. - */ - -@class WebScriptObject; // from JavaScriptCore -@class WebCoreScriptCallFrame; // below - -#ifdef __cplusplus -class WebCoreScriptDebuggerImp; -namespace KJS { class ExecState; } -using KJS::ExecState; -#else -@class WebCoreScriptDebuggerImp; -@class ExecState; -#endif - - - -// "WebScriptDebugger" protocol - must be implemented by a delegate - -@protocol WebScriptDebugger - -- (WebScriptObject *)globalObject; // return the WebView's windowScriptObject -- (id)newWrapperForFrame:(WebCoreScriptCallFrame *)frame; // return a (retained) stack-frame object - -// debugger callbacks -- (void)parsedSource:(NSString *)source fromURL:(NSURL *)url sourceId:(int)sid startLine:(int)startLine errorLine:(int)errorLine errorMessage:(NSString *)errorMessage; -- (void)enteredFrame:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno; -- (void)hitStatement:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno; -- (void)leavingFrame:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno; -- (void)exceptionRaised:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno; - -@end - - - -@interface WebCoreScriptDebugger : NSObject -{ -@private - id<WebScriptDebugger> _delegate; // interface to WebKit (not retained) - WebScriptObject *_globalObj; // the global object's proxy (not retained) - WebCoreScriptCallFrame *_current; // top of stack - WebCoreScriptDebuggerImp *_debugger; // [KJS::Debugger] -} - -- (WebCoreScriptDebugger *)initWithDelegate:(id<WebScriptDebugger>)delegate; -- (id<WebScriptDebugger>)delegate; - -@end - - - -@interface WebCoreScriptCallFrame : NSObject -{ -@private - id _wrapper; // WebKit's version of this object - WebScriptObject *_globalObj; // the global object's proxy (not retained) - WebCoreScriptCallFrame *_caller; // previous stack frame - ExecState *_state; // [KJS::ExecState] -} - -- (id)wrapper; -- (WebCoreScriptCallFrame *)caller; - -- (NSArray *)scopeChain; -- (NSString *)functionName; -- (id)exception; -- (id)evaluateWebScript:(NSString *)script; - -@end diff --git a/WebCore/bridge/mac/WebCoreScriptDebugger.mm b/WebCore/bridge/mac/WebCoreScriptDebugger.mm deleted file mode 100644 index 2f88e81..0000000 --- a/WebCore/bridge/mac/WebCoreScriptDebugger.mm +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2005 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 OR ITS 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 "WebCoreScriptDebugger.h" - -#import "DeprecatedString.h" -#import "KURL.h" -#import "PlatformString.h" -#import "WebCoreObjCExtras.h" -#import "WebScriptObjectPrivate.h" -#import <JavaScriptCore/ExecState.h> -#import <JavaScriptCore/JSGlobalObject.h> -#import <JavaScriptCore/debugger.h> -#import <JavaScriptCore/function.h> -#import <JavaScriptCore/interpreter.h> - -using namespace KJS; -using namespace WebCore; - -@interface WebCoreScriptDebugger (WebCoreScriptDebuggerInternal) - -- (WebCoreScriptCallFrame *)_enterFrame:(ExecState *)state; -- (WebCoreScriptCallFrame *)_leaveFrame; - -@end - -@interface WebCoreScriptCallFrame (WebCoreScriptDebuggerInternal) - -- (WebCoreScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebCoreScriptCallFrame *)caller state:(ExecState *)state; -- (void)_setWrapper:(id)wrapper; -- (id)_convertValueToObjcValue:(JSValue *)value; - -@end - -// convert UString to NSString -static NSString *toNSString(const UString &s) -{ - if (s.isEmpty()) return nil; - return [NSString stringWithCharacters:(const unichar *)s.data() length:s.size()]; -} - -// convert UString to NSURL -static NSURL *toNSURL(const UString &s) -{ - if (s.isEmpty()) return nil; - return KURL(DeprecatedString(s)).getNSURL(); -} - -// C++ interface to KJS debugger callbacks - -class WebCoreScriptDebuggerImp : public KJS::Debugger { - - private: - WebCoreScriptDebugger *_objc; // our ObjC half - bool _nested; // true => this is a nested call - WebCoreScriptCallFrame *_current; // top stack frame (copy of same field from ObjC side) - - public: - // constructor - WebCoreScriptDebuggerImp(WebCoreScriptDebugger *objc, JSGlobalObject* globalObject) : _objc(objc) { - _nested = true; - _current = [_objc _enterFrame:globalObject->globalExec()]; - attach(globalObject); - [[_objc delegate] enteredFrame:_current sourceId:-1 line:-1]; - _nested = false; - } - - // callbacks - relay to delegate - virtual bool sourceParsed(ExecState *state, int sid, const UString &url, const UString &source, int lineNumber, int errorLine, const UString &errorMsg) { - if (!_nested) { - _nested = true; - [[_objc delegate] parsedSource:toNSString(source) fromURL:toNSURL(url) sourceId:sid startLine:lineNumber errorLine:errorLine errorMessage:toNSString(errorMsg)]; - _nested = false; - } - return true; - } - virtual bool callEvent(ExecState *state, int sid, int lineno, JSObject *func, const List &args) { - if (!_nested) { - _nested = true; - _current = [_objc _enterFrame:state]; - [[_objc delegate] enteredFrame:_current sourceId:sid line:lineno]; - _nested = false; - } - return true; - } - virtual bool atStatement(ExecState *state, int sid, int lineno, int lastLine) { - if (!_nested) { - _nested = true; - [[_objc delegate] hitStatement:_current sourceId:sid line:lineno]; - _nested = false; - } - return true; - } - virtual bool returnEvent(ExecState *state, int sid, int lineno, JSObject *func) { - if (!_nested) { - _nested = true; - [[_objc delegate] leavingFrame:_current sourceId:sid line:lineno]; - _current = [_objc _leaveFrame]; - _nested = false; - } - return true; - } - virtual bool exception(ExecState *state, int sid, int lineno, JSValue *exception) { - if (!_nested) { - _nested = true; - [[_objc delegate] exceptionRaised:_current sourceId:sid line:lineno]; - _nested = false; - } - return true; - } - -}; - - - -// WebCoreScriptDebugger -// -// This is the main (behind-the-scenes) debugger class in WebCore. -// -// The WebCoreScriptDebugger has two faces, one for Objective-C (this class), and another (WebCoreScriptDebuggerImp) -// for C++. The ObjC side creates the C++ side, which does the real work of attaching to the global object and -// forwarding the KJS debugger callbacks to the delegate. - -@implementation WebCoreScriptDebugger - -#ifndef BUILDING_ON_TIGER -+ (void)initialize -{ - WebCoreObjCFinalizeOnMainThread(self); -} -#endif - -- (WebCoreScriptDebugger *)initWithDelegate:(id<WebScriptDebugger>)delegate -{ - if ((self = [super init])) { - _delegate = delegate; - _globalObj = [_delegate globalObject]; - _debugger = new WebCoreScriptDebuggerImp(self, [_globalObj _rootObject]->globalObject()); - } - return self; -} - -- (void)dealloc -{ - [_current release]; - delete _debugger; - [super dealloc]; -} - -- (void)finalize -{ - delete _debugger; - [super finalize]; -} - -- (id<WebScriptDebugger>)delegate -{ - return _delegate; -} - -@end - - - -@implementation WebCoreScriptDebugger (WebCoreScriptDebuggerInternal) - -- (WebCoreScriptCallFrame *)_enterFrame:(ExecState *)state; -{ - WebCoreScriptCallFrame *callee = [[WebCoreScriptCallFrame alloc] _initWithGlobalObject:_globalObj caller:_current state:state]; - [callee _setWrapper:[_delegate newWrapperForFrame:callee]]; - return _current = callee; -} - -- (WebCoreScriptCallFrame *)_leaveFrame; -{ - WebCoreScriptCallFrame *caller = [[_current caller] retain]; - [_current release]; - return _current = caller; -} - -@end - - - -// WebCoreScriptCallFrame -// -// One of these is created to represent each stack frame. Additionally, there is a "global" -// frame to represent the outermost scope. This global frame is always the last frame in -// the chain of callers. -// -// The delegate can assign a "wrapper" to each frame object so it can relay calls through its -// own exported interface. This class is private to WebCore (and the delegate). - -@implementation WebCoreScriptCallFrame (WebCoreScriptDebuggerInternal) - -- (WebCoreScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebCoreScriptCallFrame *)caller state:(ExecState *)state -{ - if ((self = [super init])) { - _globalObj = globalObj; - _caller = caller; // (already retained) - _state = state; - } - return self; -} - -- (void)_setWrapper:(id)wrapper -{ - _wrapper = wrapper; // (already retained) -} - -- (id)_convertValueToObjcValue:(JSValue *)value -{ - if (!value) - return nil; - - if (value == [_globalObj _imp]) - return _globalObj; - - Bindings::RootObject* root1 = [_globalObj _originRootObject]; - if (!root1) - return nil; - - Bindings::RootObject* root2 = [_globalObj _rootObject]; - if (!root2) - return nil; - - return [WebScriptObject _convertValueToObjcValue:value originRootObject:root1 rootObject:root2]; -} - -@end - - - -@implementation WebCoreScriptCallFrame - -- (void)dealloc -{ - [_wrapper release]; - [_caller release]; - [super dealloc]; -} - -- (id)wrapper -{ - return _wrapper; -} - -- (WebCoreScriptCallFrame *)caller -{ - return _caller; -} - - -// Returns an array of scope objects (most local first). -// The properties of each scope object are the variables for that scope. -// Note that the last entry in the array will _always_ be the global object (windowScriptObject), -// whose properties are the global variables. - -- (NSArray *)scopeChain -{ - if (!_state->scopeNode()) { // global frame - return [NSArray arrayWithObject:_globalObj]; - } - - ScopeChain chain = _state->scopeChain(); - NSMutableArray *scopes = [[NSMutableArray alloc] init]; - - while (!chain.isEmpty()) { - [scopes addObject:[self _convertValueToObjcValue:chain.top()]]; - chain.pop(); - } - - NSArray *result = [NSArray arrayWithArray:scopes]; - [scopes release]; - return result; -} - - -// Returns the name of the function for this frame, if available. -// Returns nil for anonymous functions and for the global frame. - -- (NSString *)functionName -{ - if (_state->scopeNode()) { - FunctionImp *func = _state->function(); - if (func) { - Identifier fn = func->functionName(); - return toNSString(fn.ustring()); - } - } - return nil; -} - - -// Returns the pending exception for this frame (nil if none). - -- (id)exception -{ - if (!_state->hadException()) return nil; - return [self _convertValueToObjcValue:_state->exception()]; -} - - -// Evaluate some JavaScript code in the context of this frame. -// The code is evaluated as if by "eval", and the result is returned. -// If there is an (uncaught) exception, it is returned as though _it_ were the result. -// Calling this method on the global frame is not quite the same as calling the WebScriptObject -// method of the same name, due to the treatment of exceptions. - -// FIXME: If "script" contains var declarations, the machinery to handle local variables -// efficiently in JavaScriptCore will not work properly. This could lead to crashes or -// incorrect variable values. So this is not appropriate for evaluating arbitrary script. -- (id)evaluateWebScript:(NSString *)script -{ - JSLock lock; - - UString code = String(script); - - ExecState* state = _state; - JSGlobalObject* globalObject = state->dynamicGlobalObject(); - - // find "eval" - JSObject *eval = NULL; - if (state->scopeNode()) { // "eval" won't work without context (i.e. at global scope) - JSValue *v = globalObject->get(state, "eval"); - if (v->isObject() && static_cast<JSObject *>(v)->implementsCall()) - eval = static_cast<JSObject *>(v); - else - // no "eval" - fallback operates on global exec state - state = globalObject->globalExec(); - } - - JSValue *savedException = state->exception(); - state->clearException(); - - // evaluate - JSValue *result; - if (eval) { - List args; - args.append(jsString(code)); - result = eval->call(state, NULL, args); - } else - // no "eval", or no context (i.e. global scope) - use global fallback - result = Interpreter::evaluate(state, UString(), 0, code.data(), code.size(), globalObject).value(); - - if (state->hadException()) - result = state->exception(); // (may be redundant depending on which eval path was used) - state->setException(savedException); - - return [self _convertValueToObjcValue:result]; -} - -@end diff --git a/WebCore/bridge/make_testbindings b/WebCore/bridge/make_testbindings new file mode 100755 index 0000000..1f528fe --- /dev/null +++ b/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/WebCore/bridge/npapi.h b/WebCore/bridge/npapi.h new file mode 100644 index 0000000..84d266b --- /dev/null +++ b/WebCore/bridge/npapi.h @@ -0,0 +1,838 @@ +/* ***** 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 ***** */ + + + /* + * Netscape client plug-in API spec + */ + + +#ifndef _NPAPI_H_ +#define _NPAPI_H_ + +#ifdef INCLUDE_JAVA +#include "jri.h" /* Java Runtime Interface */ +#else +#define jref void * +#define JRIEnv void +#endif + +#ifdef _WIN32 +# ifndef XP_WIN +# define XP_WIN 1 +# endif /* XP_WIN */ +#endif /* _WIN32 */ + +#ifdef __MWERKS__ +# define _declspec __declspec +# ifdef macintosh +# ifndef XP_MAC +# define XP_MAC 1 +# endif /* XP_MAC */ +# endif /* macintosh */ +# ifdef __INTEL__ +# undef NULL +# ifndef XP_WIN +# define XP_WIN 1 +# endif /* __INTEL__ */ +# endif /* XP_PC */ +#endif /* __MWERKS__ */ + +#if defined(__APPLE_CC__) && !defined(__MACOS_CLASSIC__) && !defined(XP_UNIX) +# define XP_MACOSX +#endif + +#ifdef XP_MAC + #include <Quickdraw.h> + #include <Events.h> +#endif + +#if defined(XP_MACOSX) && defined(__LP64__) +#define NP_NO_QUICKDRAW +#define NP_NO_CARBON +#endif + +#ifdef XP_MACOSX + #include <ApplicationServices/ApplicationServices.h> + #include <OpenGL/OpenGL.h> +#ifndef NP_NO_CARBON + #include <Carbon/Carbon.h> +#endif +#endif + +#ifdef XP_UNIX + #include <X11/Xlib.h> + #include <X11/Xutil.h> + #include <stdio.h> +#endif + +#ifdef XP_WIN + #include <windows.h> +#endif + +/*----------------------------------------------------------------------*/ +/* Plugin Version Constants */ +/*----------------------------------------------------------------------*/ + +#define NP_VERSION_MAJOR 0 +#define NP_VERSION_MINOR 20 + + + +/*----------------------------------------------------------------------*/ +/* Definition of Basic Types */ +/*----------------------------------------------------------------------*/ + +#ifndef _UINT16 +#define _UINT16 +typedef unsigned short uint16; +#endif + +#ifndef _UINT32 +#define _UINT32 +#ifdef __LP64__ +typedef unsigned int uint32; +#else /* __LP64__ */ +typedef unsigned long uint32; +#endif /* __LP64__ */ +#endif + +#ifndef _INT16 +#define _INT16 +typedef short int16; +#endif + +#ifndef _INT32 +#define _INT32 +#ifdef __LP64__ +typedef int int32; +#else /* __LP64__ */ +typedef long int32; +#endif /* __LP64__ */ +#endif + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef NULL +#define NULL (0L) +#endif + +typedef unsigned char NPBool; +typedef int16 NPError; +typedef int16 NPReason; +typedef char* NPMIMEType; + + + +/*----------------------------------------------------------------------*/ +/* Structures and definitions */ +/*----------------------------------------------------------------------*/ + +#if !defined(__LP64__) +#if defined(XP_MAC) || 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 end; + uint32 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 offset; /* negative offset means from the end */ + uint32 length; + struct _NPByteRange* next; +} NPByteRange; + + +typedef struct _NPSavedData +{ + int32 len; + void* buf; +} NPSavedData; + + +typedef struct _NPRect +{ + uint16 top; + uint16 left; + uint16 bottom; + uint16 right; +} NPRect; + + +#ifdef 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 type; +} NPAnyCallbackStruct; + +typedef struct +{ + int32 type; + Display* display; + Visual* visual; + Colormap colormap; + unsigned int depth; +} NPSetWindowCallbackStruct; + +typedef struct +{ + int32 type; + FILE* fp; +} NPPrintCallbackStruct; + +#endif /* XP_UNIX */ + +/* + * 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 differenet 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 + +#define NP_ABI_MACHO_MASK 0x01000000 +/* + * On OSX, the Mach-O executable format is significantly + * different than CFM. In addition to having a different + * C++ ABI, it also has has different C calling convention. + * You must use glue code when calling between CFM and + * Mach-O C functions. + */ +#if (defined(TARGET_RT_MAC_MACHO)) +#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 */ + + /* 12 and over are available on Mozilla builds starting with 0.9.9 */ + 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. */ + 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. + */ + NPPVformValue = 16, /* Not implemented in WebKit */ + + NPPVpluginUrlRequestsDisplayedBool = 17, /* Not implemented in WebKit */ + + /* Checks if the plugin is interested in receiving the http body of + * failed http requests (http status != 200). + */ + NPPVpluginWantsAllNetworkStreams = 18, + +#ifdef XP_MACOSX + /* Used for negotiating drawing models */ + NPPVpluginDrawingModel = 1000, + /* Used for negotiating event models */ + NPPVpluginEventModel = 1001, + /* The plug-in text input vtable */ + NPPVpluginTextInputFuncs = 1002, + /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */ + NPPVpluginCoreAnimationLayer = 1003 +#endif + +#ifdef ANDROID + NPPFakeValueToForce32Bits = 0x7FFFFFFF +#endif +} NPPVariable; + +/* + * List of variable names for which NPN_GetValue is implemented by Mozilla + */ +typedef enum { + NPNVxDisplay = 1, + NPNVxtAppContext, + NPNVnetscapeWindow, + NPNVjavascriptEnabledBool, + NPNVasdEnabledBool, + NPNVisOfflineBool, + + /* 10 and over are available on Mozilla builds starting with 0.9.4 */ + 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 + +#ifdef XP_MACOSX + , NPNVpluginDrawingModel = 1000 /* The NPDrawingModel specified by the plugin */ + +#ifndef NP_NO_QUICKDRAW + , NPNVsupportsQuickDrawBool = 2000 /* TRUE if the browser supports the QuickDraw drawing model */ +#endif + , NPNVsupportsCoreGraphicsBool = 2001 /* TRUE if the browser supports the CoreGraphics drawing model */ + , NPNVsupportsOpenGLBool = 2002 /* TRUE if the browser supports the OpenGL drawing model (CGL on Mac) */ + , NPNVsupportsCoreAnimationBool = 2003 /* TRUE if the browser supports the CoreAnimation drawing model */ + +#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 */ + + , NPNVbrowserTextInputFuncs = 1002 /* The browser text input vtable */ +#endif /* XP_MACOSX */ + +#ifdef ANDROID + , NPNFakeValueToForce32Bits = 0x7FFFFFFF +#endif +} NPNVariable; + +/* + * The type of a NPWindow - it specifies the type of the data structure + * returned in the window field. + */ +typedef enum { + NPWindowTypeWindow = 1, + NPWindowTypeDrawable +} NPWindowType; + +#ifdef XP_MACOSX + +/* + * The drawing model for a Mac OS X plugin. These are the possible values for the NPNVpluginDrawingModel variable. + */ + +typedef enum { +#ifndef NP_NO_QUICKDRAW + NPDrawingModelQuickDraw = 0, +#endif + NPDrawingModelCoreGraphics = 1, + NPDrawingModelOpenGL = 2, + NPDrawingModelCoreAnimation +} NPDrawingModel; + +/* + * The event model for a Mac OS X plugin. These are the possible values for the NPNVpluginEventModel variable. + */ + +typedef enum { +#ifndef NP_NO_CARBON + NPEventModelCarbon = 0, +#endif + NPEventModelCocoa = 1, +} NPEventModel; + +typedef enum { + NPCocoaEventDrawRect = 1, + NPCocoaEventMouseDown, + NPCocoaEventMouseUp, + NPCocoaEventMouseMoved, + NPCocoaEventMouseEntered, + NPCocoaEventMouseExited, + NPCocoaEventMouseDragged, + NPCocoaEventKeyDown, + NPCocoaEventKeyUp, + NPCocoaEventFlagsChanged, + NPCocoaEventFocusChanged, + NPCocoaEventWindowFocusChanged, + NPCocoaEventScrollWheel, +} NPCocoaEventType; + +typedef struct _NPNSString NPNSString; +typedef struct _NPNSWindow NPNSWindow; +typedef struct _NPNSMenu NPNSMenu; + +typedef struct _NPCocoaEvent { + NPCocoaEventType type; + uint32 version; + + union { + struct { + uint32 modifierFlags; + double pluginX; + double pluginY; + int32 buttonNumber; + int32 clickCount; + double deltaX; + double deltaY; + double deltaZ; + } mouse; + struct { + uint32 modifierFlags; + NPNSString *characters; + NPNSString *charactersIgnoringModifiers; + NPBool isARepeat; + uint16 keyCode; + } key; + struct { + double x; + double y; + double width; + double height; + } draw; + struct { + NPBool hasFocus; + } focus; + } data; +} NPCocoaEvent; + +#endif + +typedef struct _NPWindow +{ + void* window; /* Platform specific window handle */ + int32 x; /* Position of top left corner relative */ + int32 y; /* to a netscape page. */ + uint32 width; /* Maximum window size */ + uint32 height; + NPRect clipRect; /* Clipping rectangle in port coordinates */ + /* Used by MAC only. */ +#ifdef XP_UNIX + void * ws_info; /* Platform-dependent additonal data */ +#endif /* XP_UNIX */ + NPWindowType type; /* Is this a window or a drawable? */ +} NPWindow; + + +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 mode; /* NP_FULL or NP_EMBED */ + union + { + NPFullPrint fullPrint; /* if mode is NP_FULL */ + NPEmbedPrint embedPrint; /* if mode is NP_EMBED */ + } print; +} NPPrint; + +#ifdef XP_MACOSX +typedef NPNSMenu NPMenu; +#else +typedef void * NPMenu; +#endif + +#if defined(XP_MAC) || defined(XP_MACOSX) + +#ifndef NP_NO_CARBON +typedef EventRecord NPEvent; +#endif + +#elif defined(XP_WIN) +typedef struct _NPEvent +{ + uint16 event; + uint32 wParam; + uint32 lParam; +} NPEvent; +#elif defined (XP_UNIX) +typedef XEvent NPEvent; +#else +typedef void* NPEvent; +#endif /* XP_MAC */ + +#if defined(XP_MAC) +typedef RgnHandle NPRegion; +#elif defined(XP_MACOSX) +/* + * NPRegion's type depends on the drawing model specified by the plugin (see NPNVpluginDrawingModel). + * NPQDRegion represents a QuickDraw RgnHandle and is used with the QuickDraw drawing model. + * NPCGRegion repesents a graphical region when using any other drawing model. + */ +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; +#else +typedef void *NPRegion; +#endif /* XP_MAC */ + +#ifdef XP_MACOSX + +/* + * 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; // Can be either an NSWindow or a WindowRef depending on the 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; + +#endif /* XP_MACOSX */ + +#if defined(XP_MAC) || defined(XP_MACOSX) + +/* + * Mac-specific structures and definitions. + */ + +#ifndef NP_NO_QUICKDRAW + +/* + * NP_Port is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelQuickDraw as its + * drawing model, or the plugin does not specify a drawing model. + * + * It is not recommended that new plugins use NPDrawingModelQuickDraw or NP_Port, as QuickDraw has been + * deprecated in Mac OS X 10.5. CoreGraphics is the preferred drawing API. + * + * NP_Port is not available in 64-bit. + */ + +typedef struct NP_Port +{ + CGrafPtr port; /* Grafport */ + int32 portx; /* position inside the topmost window */ + int32 porty; +} NP_Port; + +#endif /* NP_NO_QUICKDRAW */ + +/* + * Non-standard event types that can be passed to HandleEvent + */ +#define getFocusEvent (osEvt + 16) +#define loseFocusEvent (osEvt + 17) +#define adjustCursorEvent (osEvt + 18) + +#endif /* XP_MAC */ + + +/* + * 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_MAC) || 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_MACOSX_HAS_EVENT_MODELS 20 + +/*----------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------*/ + +#if defined(_WINDOWS) && !defined(WIN32) +#define NP_LOADDS _loadds +#else +#define NP_LOADDS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * NPP_* functions are provided by the plugin and called by the navigator. + */ + +#ifdef XP_UNIX +char* NPP_GetMIMEDescription(void); +#endif /* XP_UNIX */ + +NPError NPP_Initialize(void); +void NPP_Shutdown(void); +NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance, + uint16 mode, int16 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* stype); +NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +int32 NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream); +int32 NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32 offset, + int32 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 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 NPP_GetValue(NPP instance, NPPVariable variable, + void *value); +NPError NPP_SetValue(NPP instance, NPNVariable variable, + void *value); + +/* + * NPN_* functions are provided by the navigator and called by the plugin. + */ + +void NPN_Version(int* plugin_major, int* plugin_minor, + int* netscape_major, int* netscape_minor); +NPError NPN_GetURLNotify(NPP instance, const char* url, + const char* target, void* notifyData); +NPError NPN_GetURL(NPP instance, const char* url, + const char* target); +NPError NPN_PostURLNotify(NPP instance, const char* url, + const char* target, uint32 len, + const char* buf, NPBool file, + void* notifyData); +NPError NPN_PostURL(NPP instance, const char* url, + const char* target, uint32 len, + const char* buf, NPBool file); +NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList); +NPError NPN_NewStream(NPP instance, NPMIMEType type, + const char* target, NPStream** stream); +int32 NPN_Write(NPP instance, NPStream* stream, int32 len, + void* buffer); +NPError NPN_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +void NPN_Status(NPP instance, const char* message); +const char* NPN_UserAgent(NPP instance); +void* NPN_MemAlloc(uint32 size); +void NPN_MemFree(void* ptr); +uint32 NPN_MemFlush(uint32 size); +void NPN_ReloadPlugins(NPBool reloadPages); +JRIEnv* NPN_GetJavaEnv(void); +jref NPN_GetJavaPeer(NPP instance); +NPError NPN_GetValue(NPP instance, NPNVariable variable, + void *value); +NPError NPN_SetValue(NPP instance, NPPVariable variable, + void *value); +void NPN_InvalidateRect(NPP instance, NPRect *invalidRect); +void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion); +void NPN_ForceRedraw(NPP instance); +void NPN_PushPopupsEnabledState(NPP instance, NPBool enabled); +void NPN_PopPopupsEnabledState(NPP instance); +void NPN_PluginThreadAsyncCall(NPP instance, void (*func) (void *), void *userData); +uint32 NPN_ScheduleTimer(NPP instance, uint32 interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32 timerID)); +void NPN_UnscheduleTimer(NPP instance, uint32 timerID); +NPError NPN_PopUpContextMenu(NPP instance, NPMenu* menu); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* _NPAPI_H_ */ diff --git a/WebCore/bridge/npruntime.cpp b/WebCore/bridge/npruntime.cpp new file mode 100644 index 0000000..bef3676 --- /dev/null +++ b/WebCore/bridge/npruntime.cpp @@ -0,0 +1,226 @@ +/* + * 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 "npruntime_internal.h" +#include "npruntime_impl.h" +#include "npruntime_priv.h" + +#include "c_utility.h" +#include <kjs/identifier.h> +#include <runtime/JSLock.h> +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> + +using namespace JSC::Bindings; + +typedef HashMap<RefPtr<JSC::UString::Rep>, PrivateIdentifier*> StringIdentifierMap; + +static StringIdentifierMap* getStringIdentifierMap() +{ + static StringIdentifierMap* stringIdentifierMap = 0; + if (!stringIdentifierMap) + stringIdentifierMap = new StringIdentifierMap; + return stringIdentifierMap; +} + +typedef HashMap<int, PrivateIdentifier*> IntIdentifierMap; + +static IntIdentifierMap* getIntIdentifierMap() +{ + static IntIdentifierMap* intIdentifierMap = 0; + if (!intIdentifierMap) + intIdentifierMap = new IntIdentifierMap; + return intIdentifierMap; +} + +NPIdentifier _NPN_GetStringIdentifier(const NPUTF8* name) +{ + ASSERT(name); + + if (name) { + PrivateIdentifier* identifier = 0; + + JSC::JSLock lock(false); + + identifier = getStringIdentifierMap()->get(identifierFromNPIdentifier(name).ustring().rep()); + if (identifier == 0) { + identifier = (PrivateIdentifier*)malloc(sizeof(PrivateIdentifier)); + // We never release identifier names, so this dictionary will grow, as will + // the memory for the identifier name strings. + identifier->isString = true; + identifier->value.string = strdup(name); + + getStringIdentifierMap()->set(identifierFromNPIdentifier(name).ustring().rep(), identifier); + } + return (NPIdentifier)identifier; + } + + return 0; +} + +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) +{ + PrivateIdentifier* identifier; + + if (intid == 0 || intid == -1) { + static PrivateIdentifier* negativeOneAndZeroIdentifiers[2]; + + identifier = negativeOneAndZeroIdentifiers[intid + 1]; + if (!identifier) { + identifier = (PrivateIdentifier*)malloc(sizeof(PrivateIdentifier)); + identifier->isString = false; + identifier->value.number = intid; + + negativeOneAndZeroIdentifiers[intid + 1] = identifier; + } + } else { + identifier = getIntIdentifierMap()->get(intid); + if (!identifier) { + identifier = (PrivateIdentifier*)malloc(sizeof(PrivateIdentifier)); + // We never release identifier names, so this dictionary will grow. + identifier->isString = false; + identifier->value.number = intid; + + getIntIdentifierMap()->set(intid, identifier); + } + } + return (NPIdentifier)identifier; +} + +bool _NPN_IdentifierIsString(NPIdentifier identifier) +{ + PrivateIdentifier* i = (PrivateIdentifier*)identifier; + return i->isString; +} + +NPUTF8 *_NPN_UTF8FromIdentifier(NPIdentifier identifier) +{ + PrivateIdentifier* i = (PrivateIdentifier*)identifier; + if (!i->isString || !i->value.string) + return NULL; + + return (NPUTF8 *)strdup(i->value.string); +} + +int32_t _NPN_IntFromIdentifier(NPIdentifier identifier) +{ + PrivateIdentifier* i = (PrivateIdentifier*)identifier; + if (i->isString) + return 0; + return i->value.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); + 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)); + + 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/WebCore/bridge/npruntime.h b/WebCore/bridge/npruntime.h new file mode 100644 index 0000000..8a285be --- /dev/null +++ b/WebCore/bridge/npruntime.h @@ -0,0 +1,357 @@ +/* + * 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. + * + * Revision 1 (March 4, 2004): + * Initial proposal. + * + * Revision 2 (March 10, 2004): + * All calls into script were made asynchronous. Results are + * provided via the NPScriptResultFunctionPtr callback. + * + * Revision 3 (March 10, 2004): + * Corrected comments to not refer to class retain/release FunctionPtrs. + * + * Revision 4 (March 11, 2004): + * Added additional convenience NPN_SetExceptionWithUTF8(). + * Changed NPHasPropertyFunctionPtr and NPHasMethodFunctionPtr to take NPClass + * pointers instead of NPObject pointers. + * Added NPIsValidIdentifier(). + * + * Revision 5 (March 17, 2004): + * Added context parameter to result callbacks from ScriptObject functions. + * + * Revision 6 (March 29, 2004): + * Renamed functions implemented by user agent to NPN_*. Removed _ from + * type names. + * Renamed "JavaScript" types to "Script". + * + * Revision 7 (April 21, 2004): + * NPIdentifier becomes a void*, was int32_t + * Remove NP_IsValidIdentifier, renamed NP_IdentifierFromUTF8 to NP_GetIdentifier + * Added NPVariant and modified functions to use this new type. + * + * Revision 8 (July 9, 2004): + * Updated to joint Apple-Mozilla license. + * + * Revision 9 (August 12, 2004): + * Changed NPVariantType enum values to form PVariantType_XXX + * Added NPP arguments to NPObject functions. + * Replaced NPVariant functions with macros. + */ +#ifndef _NP_RUNTIME_H_ +#define _NP_RUNTIME_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#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. +*/ + + +/* + 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 called on variants that are resultant out parameters + in NPGetPropertyFunctionPtr and NPInvokeFunctionPtr. Resultant variants + from these two functions should be initialized using the + NPN_InitializeVariantXXX() functions. + + After calling NPReleaseVariantValue, the type of the variant will + be set to NPVariantUndefinedType. +*/ +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 NP_BEGIN_MACRO do { +#define NP_END_MACRO } while (0) + +#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, 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, _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 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 *obj); +typedef void (*NPInvalidateFunctionPtr)(NPObject *obj); +typedef bool (*NPHasMethodFunctionPtr)(NPObject *obj, NPIdentifier name); +typedef bool (*NPInvokeFunctionPtr)(NPObject *obj, 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 *obj, NPIdentifier name); +typedef bool (*NPGetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, NPVariant *result); +typedef bool (*NPSetPropertyFunctionPtr)(NPObject *obj, 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 have a reference count of one. It is the caller's responsibility + to release the returned object. + + NPInvokeFunctionPtr function may return false to indicate a the method could not be invoked. + + NPGetPropertyFunctionPtr and NPSetPropertyFunctionPtr may return false to indicate a property doesn't + exist. + + 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. If a class has an allocate + function it is the responsibility of that implementation to set the initial retain + count to 1. +*/ +NPObject *NPN_CreateObject(NPP npp, NPClass *aClass); + +/* + Increment the NPObject's reference count. +*/ +NPObject *NPN_RetainObject (NPObject *obj); + +/* + 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 *obj); + +/* + 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. +*/ +void NPN_SetException (NPObject *obj, const NPUTF8 *message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/WebCore/bridge/npruntime_impl.h b/WebCore/bridge/npruntime_impl.h new file mode 100644 index 0000000..559f340 --- /dev/null +++ b/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/WebCore/bridge/npruntime_internal.h b/WebCore/bridge/npruntime_internal.h new file mode 100644 index 0000000..5ccdecd --- /dev/null +++ b/WebCore/bridge/npruntime_internal.h @@ -0,0 +1,50 @@ +/* + * 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 +#endif diff --git a/WebCore/bridge/GlobalHistory.h b/WebCore/bridge/npruntime_priv.h index 4857f6f..301c163 100644 --- a/WebCore/bridge/GlobalHistory.h +++ b/WebCore/bridge/npruntime_priv.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * 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 @@ -23,15 +23,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GlobalHistory_h -#define GlobalHistory_h +#ifndef NP_RUNTIME_PRIV_H_ +#define NP_RUNTIME_PRIV_H_ -#include <wtf/unicode/Unicode.h> +#if ENABLE(NETSCAPE_PLUGIN_API) -namespace WebCore { +#include "npruntime_internal.h" - bool historyContains(const UChar* characters, unsigned length); +/* + NPN_InitializeVariantWithStringCopy() will copy string data. The string data + will be deallocated by calls to NPReleaseVariantValue(). +*/ +void NPN_InitializeVariantWithStringCopy(NPVariant*, const NPString*); -} // namespace WebCore +#endif // ENABLE(NETSCAPE_PLUGIN_API) +#endif -#endif // GlobalHistory_h diff --git a/WebCore/bridge/mac/GlobalHistoryMac.mm b/WebCore/bridge/objc/WebScriptObject.h index 466f60e..357d42a 100644 --- a/WebCore/bridge/mac/GlobalHistoryMac.mm +++ b/WebCore/bridge/objc/WebScriptObject.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * 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 @@ -23,17 +23,22 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "config.h" -#import "GlobalHistory.h" +#import <Foundation/Foundation.h> +#import "runtime_root.h" -#import "WebCoreHistory.h" +@class WebUndefined; -namespace WebCore { +@protocol WebScriptObject ++ (NSString *)webScriptNameForSelector:(SEL)aSelector; ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector; ++ (NSString *)webScriptNameForKey:(const char *)name; ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name; -bool historyContains(const UChar* characters, unsigned length) -{ - // the other side of the bridge is careful not to throw exceptions here - return [[WebCoreHistory historyProvider] containsURL:characters length:length]; -} ++ (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 -} // namespace WebCore +@protocol WebUndefined ++ (WebUndefined *)undefined; +@end diff --git a/WebCore/bridge/objc/objc_class.h b/WebCore/bridge/objc/objc_class.h new file mode 100644 index 0000000..d96c604 --- /dev/null +++ b/WebCore/bridge/objc/objc_class.h @@ -0,0 +1,61 @@ +/* + * 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 const char *name() const; + + 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/WebCore/bridge/objc/objc_class.mm b/WebCore/bridge/objc/objc_class.mm new file mode 100644 index 0000000..79991c2 --- /dev/null +++ b/WebCore/bridge/objc/objc_class.mm @@ -0,0 +1,258 @@ +/* + * 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; +} + +const char* ObjcClass::name() const +{ + return object_getClassName(_isa); +} + +MethodList ObjcClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList methodList; + char fixedSizeBuffer[1024]; + char* buffer = fixedSizeBuffer; + const char* JSName = identifier.ascii(); + if (!convertJSMethodNameToObjc(JSName, buffer, sizeof(fixedSizeBuffer))) { + int length = strlen(JSName) + 1; + buffer = new char[length]; + if (!buffer || !convertJSMethodNameToObjc(JSName, 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, objcMethodSelectorName); // 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; + + const char* name = identifier.ascii(); + RetainPtr<CFStringRef> fieldName(AdoptCF, CFStringCreateWithCString(NULL, name, 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, name) == 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, objcInstance, propertyName); +} + +} +} diff --git a/WebCore/bridge/win/FrameWin.h b/WebCore/bridge/objc/objc_header.h index 405c7b2..07954a1 100644 --- a/WebCore/bridge/win/FrameWin.h +++ b/WebCore/bridge/objc/objc_header.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * 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 @@ -23,19 +23,31 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameWin_H -#define FrameWin_H +#ifndef KJS_BINDINGS_OBJC_HEADER_H +#define KJS_BINDINGS_OBJC_HEADER_H -#include "Frame.h" +#ifdef __OBJC__ -// Forward declared so we don't need wingdi.h. -typedef struct HBITMAP__* HBITMAP; +#include <objc/objc.h> +#include <objc/objc-class.h> +#include <objc/objc-runtime.h> -namespace WebCore { +typedef Class ClassStructPtr; +typedef id ObjectStructPtr; +typedef Method MethodStructPtr; +typedef Ivar IvarStructPtr; - HBITMAP imageFromSelection(Frame* frame, bool forceWhiteText); - void computePageRectsForFrame(Frame*, const IntRect& printRect, float headerHeight, float footerHeight, float userScaleFactor,Vector<IntRect>& pages, int& pageHeight); +@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/WebCore/bridge/objc/objc_instance.h b/WebCore/bridge/objc/objc_instance.h new file mode 100644 index 0000000..dc7a362 --- /dev/null +++ b/WebCore/bridge/objc/objc_instance.h @@ -0,0 +1,90 @@ +/* + * 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_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 instance, PassRefPtr<RootObject> rootObject) + { + return adoptRef(new ObjcInstance(instance, rootObject)); + } + + static void setGlobalException(NSString*, JSGlobalObject* exceptionEnvironment = 0); // A null exceptionEnvironment means the exception should propogate to any execution environment. + + ~ObjcInstance(); + + virtual Class *getClass() const; + + virtual JSValue* valueOf(ExecState*) const; + virtual JSValue* defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual JSValue* invokeMethod(ExecState*, const MethodList&, const ArgList&); + virtual bool supportsInvokeDefaultMethod() const; + virtual JSValue* invokeDefaultMethod(ExecState*, const ArgList&); + + virtual bool supportsSetValueOfUndefinedField(); + virtual void setValueOfUndefinedField(ExecState*, const Identifier&, JSValue*); + + virtual JSValue* getValueOfUndefinedField(ExecState*, const Identifier& property) const; + + ObjectStructPtr getObject() const { return _instance.get(); } + + JSValue* stringValue(ExecState*) const; + JSValue* numberValue(ExecState*) const; + JSValue* booleanValue() const; + + virtual BindingLanguage getBindingLanguage() const { return ObjectiveCLanguage; } + +protected: + virtual void virtualBegin(); + virtual void virtualEnd(); + +private: + static void moveGlobalExceptionToExecState(ExecState*); + + ObjcInstance(ObjectStructPtr instance, PassRefPtr<RootObject>); + + RetainPtr<ObjectStructPtr> _instance; + mutable ObjcClass *_class; + ObjectStructPtr _pool; + int _beginCount; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // BINDINGS_OBJC_INSTANCE_H_ diff --git a/WebCore/bridge/objc/objc_instance.mm b/WebCore/bridge/objc/objc_instance.mm new file mode 100644 index 0000000..6a714b9 --- /dev/null +++ b/WebCore/bridge/objc/objc_instance.mm @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2004, 2008 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. + */ + +#import "config.h" +#import "objc_instance.h" + +#import "FoundationExtras.h" +#import "WebScriptObject.h" +#include <runtime/Error.h> +#include <runtime/JSLock.h> +#include <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. + +void ObjcInstance::setGlobalException(NSString* exception, JSGlobalObject* exceptionEnvironment) +{ + HardRelease(s_exception); + HardRetain(exception); + s_exception = exception; + + s_exceptionEnvironment = exceptionEnvironment; +} + +void ObjcInstance::moveGlobalExceptionToExecState(ExecState* exec) +{ + if (!s_exception) { + ASSERT(!s_exceptionEnvironment); + return; + } + + if (!s_exceptionEnvironment || s_exceptionEnvironment == exec->dynamicGlobalObject()) { + JSLock lock(false); + throwError(exec, GeneralError, s_exception); + } + + HardRelease(s_exception); + s_exception = 0; + + s_exceptionEnvironment = 0; +} + +ObjcInstance::ObjcInstance(ObjectStructPtr instance, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) + , _instance(instance) + , _class(0) + , _pool(0) + , _beginCount(0) +{ +} + +ObjcInstance::~ObjcInstance() +{ + // -finalizeForWebScript and -dealloc/-finalize may require autorelease pools. + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)]) + [_instance.get() performSelector:@selector(finalizeForWebScript)]; + _instance = 0; + [pool drain]; +} + +void ObjcInstance::virtualBegin() +{ + if (!_pool) + _pool = [[NSAutoreleasePool alloc] init]; + _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:)]; +} + +JSValue* ObjcInstance::invokeMethod(ExecState* exec, const MethodList &methodList, const ArgList &args) +{ + JSValue* result = jsUndefined(); + + JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly. + + setGlobalException(nil); + + // Overloading methods is not allowed in ObjectiveC. Should only be one + // name match for a particular method. + ASSERT(methodList.size() == 1); + +@try { + ObjcMethod* method = 0; + method = static_cast<ObjcMethod*>(methodList[0]); + NSMethodSignature* signature = method->getMethodSignature(); + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature]; +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + [invocation setSelector:sel_registerName(method->name())]; +#else + [invocation setSelector:(SEL)method->name()]; +#endif + [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 = args.size(); + for (int i = 0; i < count; i++) { + ObjcValue value = convertValueToObjcValue(exec, args.at(exec, 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, args.at(exec, 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, _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, const ArgList &args) +{ + JSValue* result = jsUndefined(); + + JSLock::DropAllLocks dropAllLocks(false); // 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 = args.size(); + for (unsigned i = 0; i < count; i++) { + ObjcValue value = convertValueToObjcValue(exec, args.at(exec, 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, _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::supportsSetValueOfUndefinedField() +{ + id targetObject = getObject(); + if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]) + return true; + return false; +} + +void ObjcInstance::setValueOfUndefinedField(ExecState* exec, const Identifier &property, JSValue* aValue) +{ + id targetObject = getObject(); + + JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly. + + // This check is not really necessary because NSObject implements + // setValue:forUndefinedKey:, and unfortnately 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() encoding:NSASCIIStringEncoding]]; + } @catch(NSException* localException) { + // Do nothing. Class did not override valueForUndefinedKey:. + } + + moveGlobalExceptionToExecState(exec); + } +} + +JSValue* ObjcInstance::getValueOfUndefinedField(ExecState* exec, const Identifier& property) const +{ + JSValue* result = jsUndefined(); + + id targetObject = getObject(); + + JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly. + + // This check is not really necessary because NSObject implements + // valueForUndefinedKey:, and unfortnately the default implementation + // throws an exception. + if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){ + setGlobalException(nil); + + @try { + id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]]; + result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, _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* exec) const +{ + // FIXME: Implement something sensible + return jsNumber(exec, 0); +} + +JSValue* ObjcInstance::booleanValue() const +{ + // FIXME: Implement something sensible + return jsBoolean(false); +} + +JSValue* ObjcInstance::valueOf(ExecState* exec) const +{ + return stringValue(exec); +} diff --git a/WebCore/bridge/objc/objc_runtime.h b/WebCore/bridge/objc/objc_runtime.h new file mode 100644 index 0000000..5f0d8e2 --- /dev/null +++ b/WebCore/bridge/objc/objc_runtime.h @@ -0,0 +1,130 @@ +/* + * 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 "objc_header.h" +#include "runtime.h" +#include <runtime/JSGlobalObject.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; + + virtual const char *name() const; + +private: + IvarStructPtr _ivar; + RetainPtr<CFStringRef> _name; +}; + +class ObjcMethod : public Method { +public: + ObjcMethod() : _objcClass(0), _selector(0), _javaScriptName(0) {} + ObjcMethod(ClassStructPtr aClass, const char *_selector); + + virtual const char *name() const; + + virtual int numParameters() const; + + NSMethodSignature *getMethodSignature() const; + + bool isFallbackMethod() const { return strcmp(_selector, "invokeUndefinedMethodFromWebScript:withArguments:") == 0; } + void setJavaScriptName(CFStringRef n) { _javaScriptName = n; } + CFStringRef javaScriptName() const { return _javaScriptName.get(); } + +private: + ClassStructPtr _objcClass; + const char *_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 JSObject { +public: + ObjcFallbackObjectImp(ExecState*, ObjcInstance*, const Identifier& propertyName); + + static const ClassInfo s_info; + + const Identifier& propertyName() const { return _item; } + + static ObjectPrototype* createPrototype(ExecState* exec) + { + return exec->lexicalGlobalObject()->objectPrototype(); + } + + static PassRefPtr<StructureID> createStructureID(JSValue* prototype) + { + return StructureID::create(prototype, TypeInfo(ObjectType)); + } + +private: + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + 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/WebCore/bridge/objc/objc_runtime.mm b/WebCore/bridge/objc/objc_runtime.mm new file mode 100644 index 0000000..e6729a8 --- /dev/null +++ b/WebCore/bridge/objc/objc_runtime.mm @@ -0,0 +1,293 @@ +/* + * 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 "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, const char* name) +{ + _objcClass = aClass; + _selector = name; // Assume ObjC runtime keeps these around forever. + _javaScriptName = 0; +} + +const char* ObjcMethod::name() const +{ + return _selector; +} + +int ObjcMethod::numParameters() const +{ + return [getMethodSignature() numberOfArguments] - 2; +} + +NSMethodSignature* ObjcMethod::getMethodSignature() const +{ +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + return [_objcClass instanceMethodSignatureForSelector:sel_registerName(_selector)]; +#else + return [_objcClass instanceMethodSignatureForSelector:(SEL)_selector]; +#endif +} + +// ---------------------- ObjcField ---------------------- + +ObjcField::ObjcField(Ivar ivar) +{ + _ivar = ivar; // Assume ObjectiveC runtime will keep this alive forever + _name = 0; +} + +ObjcField::ObjcField(CFStringRef name) +{ + _ivar = 0; + _name = (CFStringRef)CFRetain(name); +} + +const char* ObjcField::name() const +{ +#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 + if (_ivar) + return ivar_getName(_ivar); +#else + if (_ivar) + return _ivar->ivar_name; +#endif + return [(NSString*)_name.get() UTF8String]; +} + +JSValue* ObjcField::valueFromInstance(ExecState* exec, const Instance* instance) const +{ + JSValue* result = jsUndefined(); + + id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject(); + + JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly. + + @try { + NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding]; + if (id objcValue = [targetObject valueForKey:key]) + result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, instance->rootObject()); + } @catch(NSException* localException) { + JSLock::lock(false); + throwError(exec, GeneralError, [localException reason]); + JSLock::unlock(false); + } + + // 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(false); // Can't put this inside the @try scope because it unwinds incorrectly. + + @try { + NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding]; + [targetObject setValue:value forKey:key]; + } @catch(NSException* localException) { + JSLock::lock(false); + throwError(exec, GeneralError, [localException reason]); + JSLock::unlock(false); + } +} + +// ---------------------- 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, TypeError, "Array is not mutable."); + return; + } + + if (index > [_array.get() count]) { + throwError(exec, RangeError, "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, GeneralError, "Objective-C exception."); + } +} + +JSValue* ObjcArray::valueAt(ExecState* exec, unsigned int index) const +{ + if (index > [_array.get() count]) + return throwError(exec, RangeError, "Index exceeds array size."); + @try { + id obj = [_array.get() objectAtIndex:index]; + if (obj) + return convertObjcValueToValue (exec, &obj, ObjcObjectType, _rootObject.get()); + } @catch(NSException* localException) { + return throwError(exec, GeneralError, "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, ObjcInstance* i, const Identifier& propertyName) + : JSObject(getDOMStructure<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; +} + +void ObjcFallbackObjectImp::put(ExecState*, const Identifier&, JSValue*, PutPropertySlot&) +{ +} + +static JSValue* callObjCFallbackObject(ExecState* exec, JSObject* function, JSValue* thisValue, const ArgList& args) +{ + if (!thisValue->isObject(&RuntimeObjectImp::s_info)) + return throwError(exec, TypeError); + + JSValue* result = jsUndefined(); + + RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(asObject(thisValue)); + Instance* instance = imp->getInternalInstance(); + + if (!instance) + return RuntimeObjectImp::throwInvalidAccessError(exec); + + instance->begin(); + + ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance); + id targetObject = objcInstance->getObject(); + + if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){ + ObjcClass* objcClass = static_cast<ObjcClass*>(instance->getClass()); + OwnPtr<ObjcMethod> fallbackMethod(new ObjcMethod(objcClass->isa(), sel_getName(@selector(invokeUndefinedMethodFromWebScript:withArguments:)))); + const Identifier& nameIdentifier = static_cast<ObjcFallbackObjectImp*>(function)->propertyName(); + RetainPtr<CFStringRef> name(AdoptCF, CFStringCreateWithCharacters(0, nameIdentifier.data(), nameIdentifier.size())); + fallbackMethod->setJavaScriptName(name.get()); + MethodList methodList; + methodList.append(fallbackMethod.get()); + result = instance->invokeMethod(exec, methodList, args); + } + + instance->end(); + + return 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/WebCore/bridge/objc/objc_utility.h b/WebCore/bridge/objc/objc_utility.h new file mode 100644 index 0000000..304b0a2 --- /dev/null +++ b/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 *, ErrorType, NSString *message); + +} // namespace Bindings +} // namespace JSC + +#endif diff --git a/WebCore/bridge/objc/objc_utility.mm b/WebCore/bridge/objc/objc_utility.mm new file mode 100644 index 0000000..daab4fe --- /dev/null +++ b/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(false); + + 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(false); + + 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(false); + + 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(exec, [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 Instance::createRuntimeObject(exec, ObjcInstance::create(obj, rootObject)); + } + case ObjcCharType: + return jsNumber(exec, *(char*)buffer); + case ObjcUnsignedCharType: + return jsNumber(exec, *(unsigned char*)buffer); + case ObjcShortType: + return jsNumber(exec, *(short*)buffer); + case ObjcUnsignedShortType: + return jsNumber(exec, *(unsigned short*)buffer); + case ObjcIntType: + return jsNumber(exec, *(int*)buffer); + case ObjcUnsignedIntType: + return jsNumber(exec, *(unsigned int*)buffer); + case ObjcLongType: + return jsNumber(exec, *(long*)buffer); + case ObjcUnsignedLongType: + return jsNumber(exec, *(unsigned long*)buffer); + case ObjcLongLongType: + return jsNumber(exec, *(long long*)buffer); + case ObjcUnsignedLongLongType: + return jsNumber(exec, *(unsigned long long*)buffer); + case ObjcFloatType: + return jsNumber(exec, *(float*)buffer); + case ObjcDoubleType: + return jsNumber(exec, *(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, ErrorType type, NSString *message) +{ + ASSERT(message); + size_t length = [message length]; + unichar *buffer = new unichar[length]; + [message getCharacters:buffer]; + JSObject *error = throwError(exec, type, UString(buffer, length)); + delete [] buffer; + return error; +} + +} +} diff --git a/WebCore/bridge/qt/qt_class.cpp b/WebCore/bridge/qt/qt_class.cpp new file mode 100644 index 0000000..3fe3f89 --- /dev/null +++ b/WebCore/bridge/qt/qt_class.cpp @@ -0,0 +1,217 @@ +/* + * 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 "identifier.h" + +#include "qt_class.h" +#include "qt_instance.h" +#include "qt_runtime.h" + +#include <qmetaobject.h> +#include <qdebug.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); + + QByteArray name(identifier.ascii()); + + // First see if we have a cache hit + JSObject* val = qtinst->m_methods.value(name); + if (val) + return val; + + // Nope, create an entry + 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 + 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; + + QByteArray signature = m.signature(); + signature.truncate(signature.indexOf('(')); + + if (normal == signature) { + 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(); + UString ustring = identifier.ustring(); + QString objName(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size())); + QByteArray ba = objName.toAscii(); + + // First check for a cached field + QtField* f = qtinst->m_fields.value(objName); + + 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; + else if (f->fieldType() == QtField::DynamicProperty) { + if (obj->dynamicPropertyNames().indexOf(ba) >= 0) + return f; + else { + // Dynamic property that disappeared + qtinst->m_fields.remove(objName); + delete f; + } + } else { + QList<QObject*> children = obj->children(); + for (int index = 0; index < children.count(); ++index) { + QObject *child = children.at(index); + if (child->objectName() == objName) + return f; + } + + // Didn't find it, delete it from the cache + qtinst->m_fields.remove(objName); + delete f; + } + } + + int index = m_metaObject->indexOfProperty(identifier.ascii()); + if (index >= 0) { + QMetaProperty prop = m_metaObject->property(index); + + if (prop.isScriptable(obj)) { + f = new QtField(prop); + qtinst->m_fields.insert(objName, f); + return f; + } + } + + // Dynamic properties + index = obj->dynamicPropertyNames().indexOf(ba); + if (index >= 0) { + f = new QtField(ba); + qtinst->m_fields.insert(objName, f); + return f; + } + + // Child objects + + QList<QObject*> children = obj->children(); + for (index = 0; index < children.count(); ++index) { + QObject *child = children.at(index); + if (child->objectName() == objName) { + f = new QtField(child); + qtinst->m_fields.insert(objName, f); + return f; + } + } + + // Nothing named this + return 0; + } else { + QByteArray ba(identifier.ascii()); + // 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(ba)) + return 0; + + // 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(ba); + qtinst->m_fields.insert(objName, f); + } + return f; + } +} + +} +} + diff --git a/WebCore/bridge/qt/qt_class.h b/WebCore/bridge/qt/qt_class.h new file mode 100644 index 0000000..449648d --- /dev/null +++ b/WebCore/bridge/qt/qt_class.h @@ -0,0 +1,60 @@ +/* + * 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_CLASS_H_ +#define BINDINGS_QT_CLASS_H_ + +#include "runtime.h" + +#include "qglobal.h" + +QT_BEGIN_NAMESPACE +class QObject; +class 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/WebCore/bridge/qt/qt_instance.cpp b/WebCore/bridge/qt/qt_instance.cpp new file mode 100644 index 0000000..c15dbe3 --- /dev/null +++ b/WebCore/bridge/qt/qt_instance.cpp @@ -0,0 +1,357 @@ +/* + * 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 "ArgList.h" +#include "JSDOMBinding.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "qt_class.h" +#include "qt_runtime.h" +#include "PropertyNameArray.h" +#include "runtime_object.h" +#include "ObjectPrototype.h" +#include "Error.h" + +#include <qmetaobject.h> +#include <qdebug.h> +#include <qmetatype.h> +#include <qhash.h> + +namespace JSC { +namespace Bindings { + +// Cache QtInstances +typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap; +static QObjectInstanceMap cachedInstances; + +// Cache JSObjects +typedef QHash<QtInstance*, JSObject*> InstanceJSObjectMap; +static InstanceJSObjectMap cachedObjects; + +// Derived RuntimeObject +class QtRuntimeObjectImp : public RuntimeObjectImp { + public: + QtRuntimeObjectImp(ExecState*, PassRefPtr<Instance>); + ~QtRuntimeObjectImp(); + virtual void invalidate(); + + virtual void mark() { + QtInstance* instance = static_cast<QtInstance*>(getInternalInstance()); + if (instance) + instance->mark(); + RuntimeObjectImp::mark(); + } + + protected: + void removeFromCache(); +}; + +QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance) + : RuntimeObjectImp(exec, WebCore::getDOMStructure<QtRuntimeObjectImp>(exec), instance) +{ +} + +QtRuntimeObjectImp::~QtRuntimeObjectImp() +{ + removeFromCache(); +} + +void QtRuntimeObjectImp::invalidate() +{ + removeFromCache(); + RuntimeObjectImp::invalidate(); +} + +void QtRuntimeObjectImp::removeFromCache() +{ + JSLock lock(false); + QtInstance* key = cachedObjects.key(this); + if (key) + cachedObjects.remove(key); +} + +// QtInstance +QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) + , m_class(0) + , m_object(o) + , m_hashkey(o) + , m_defaultMethod(0) +{ +} + +QtInstance::~QtInstance() +{ + JSLock lock(false); + + cachedObjects.remove(this); + cachedInstances.remove(m_hashkey); + + // clean up (unprotect from gc) the JSValues we've created + m_methods.clear(); + + foreach(QtField* f, m_fields.values()) { + delete f; + } + m_fields.clear(); +} + +PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject) +{ + JSLock lock(false); + + foreach(QtInstance* instance, cachedInstances.values(o)) { + if (instance->rootObject() == rootObject) + return instance; + } + + RefPtr<QtInstance> ret = QtInstance::create(o, rootObject); + cachedInstances.insert(o, ret.get()); + + return ret.release(); +} + +RuntimeObjectImp* QtInstance::getRuntimeObject(ExecState* exec, PassRefPtr<QtInstance> instance) +{ + JSLock lock(false); + QtInstance* qtInstance = instance.get(); + RuntimeObjectImp* ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(qtInstance)); + if (!ret) { + ret = new (exec) QtRuntimeObjectImp(exec, instance); + cachedObjects.insert(qtInstance, ret); + ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(qtInstance)); + } + return ret; +} + +Class* QtInstance::getClass() const +{ + if (!m_class) + m_class = QtClass::classForObject(m_object); + return m_class; +} + +void QtInstance::mark() +{ + if (m_defaultMethod) + m_defaultMethod->mark(); + foreach(JSObject* val, m_methods.values()) { + if (val && !val->marked()) + val->mark(); + } + foreach(JSValue* val, m_children.values()) { + if (val && !val->marked()) + val->mark(); + } +} + +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())); + } + } + + QList<QByteArray> dynProps = obj->dynamicPropertyNames(); + foreach(QByteArray ba, dynProps) { + array.add(Identifier(exec, ba.constData())); + } + + for (i=0; i < meta->methodCount(); i++) { + QMetaMethod method = meta->method(i); + if (method.access() != QMetaMethod::Private) { + array.add(Identifier(exec, method.signature())); + } + } + } +} + +JSValue* QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&) +{ + // 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 +{ + // Hmm.. see if there is a toString defined + QByteArray buf; + bool useDefault = true; + getClass(); + QObject* obj = getObject(); + if (m_class && obj) { + // 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().count() == 0) { + const char* retsig = m.typeName(); + if (retsig && *retsig) { + QVariant ret(QMetaType::type(retsig), (void*)0); + void * qargs[1]; + qargs[0] = ret.data(); + + if (obj->qt_metacall(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* exec) const +{ + return jsNumber(exec, 0); +} + +JSValue* QtInstance::booleanValue() const +{ + // ECMA 9.2 + return jsBoolean(true); +} + +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); + +const char* QtField::name() const +{ + if (m_type == MetaProperty) + return m_property.name(); + else if (m_type == ChildObject && m_childObject) + return m_childObject->objectName().toLatin1(); + else if (m_type == DynamicProperty) + return m_dynamicProperty.constData(); + return ""; // 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); + else if (m_type == DynamicProperty) + val = obj->property(m_dynamicProperty); + + JSValue* ret = convertQVariantToValue(exec, inst->rootObject(), val); + + // Need to save children so we can mark them + if (m_type == ChildObject) + instance->m_children.insert(ret); + + return ret; + } else { + QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name())); + return throwError(exec, GeneralError, 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); + } else if (m_type == DynamicProperty) + obj->setProperty(m_dynamicProperty.constData(), val); + } else { + QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name())); + throwError(exec, GeneralError, msg.toLatin1().constData()); + } +} + + +} +} diff --git a/WebCore/bridge/qt/qt_instance.h b/WebCore/bridge/qt/qt_instance.h new file mode 100644 index 0000000..d70e362 --- /dev/null +++ b/WebCore/bridge/qt/qt_instance.h @@ -0,0 +1,88 @@ +/* + * 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_INSTANCE_H_ +#define BINDINGS_QT_INSTANCE_H_ + +#include "runtime.h" +#include "runtime_root.h" +#include <qpointer.h> +#include <qhash.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 void begin(); + virtual void end(); + + virtual JSValue* valueOf(ExecState*) const; + virtual JSValue* defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual void mark(); // This isn't inherited + + virtual JSValue* invokeMethod(ExecState*, const MethodList&, const ArgList&); + + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual BindingLanguage getBindingLanguage() const { return QtLanguage; } + + JSValue* stringValue(ExecState* exec) const; + JSValue* numberValue(ExecState* exec) const; + JSValue* booleanValue() const; + + QObject* getObject() const { return m_object; } + + static PassRefPtr<QtInstance> getQtInstance(QObject*, PassRefPtr<RootObject>); + static RuntimeObjectImp* getRuntimeObject(ExecState* exec, PassRefPtr<QtInstance>); + +private: + static PassRefPtr<QtInstance> create(QObject *instance, PassRefPtr<RootObject> rootObject) + { + return adoptRef(new QtInstance(instance, rootObject)); + } + + friend class QtClass; + friend class QtField; + QtInstance(QObject*, PassRefPtr<RootObject>); // 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 QSet<JSValue*> m_children; + mutable QtRuntimeMetaMethod* m_defaultMethod; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif diff --git a/WebCore/bridge/qt/qt_runtime.cpp b/WebCore/bridge/qt/qt_runtime.cpp new file mode 100644 index 0000000..d9a2c59 --- /dev/null +++ b/WebCore/bridge/qt/qt_runtime.cpp @@ -0,0 +1,1721 @@ +/* + * 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 "DateInstance.h" +#include "DateMath.h" +#include "DatePrototype.h" +#include "FunctionPrototype.h" +#include "JSArray.h" +#include "JSDOMBinding.h" +#include "JSGlobalObject.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 "qvarlengtharray.h" +#include <JSFunction.h> +#include <limits.h> +#include <runtime.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 +} 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 + +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 (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(&RuntimeObjectImp::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) +{ + 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(exec) || value == jsUndefined()) { + if (distance) + *distance = -1; + return QVariant(); + } + + JSLock lock(false); + 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: + hint = QMetaType::QVariantMap; + break; + case QObj: + hint = QMetaType::QObjectStar; + 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) { + if (distance) + *distance = -1; + return QVariant(); + } + + QVariant ret; + int dist = -1; + switch (hint) { + case QMetaType::Bool: + 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.size() ? *(const ushort*)str.rep()->data() : 0)); + if (type == String) + dist = 3; + else + dist = 10; + } + break; + + case QMetaType::QString: { + UString ustring = value->toString(exec); + ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size())); + 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); + if (objdist >= 0) { + UString ustring = (*it).ustring(); + QString id = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + 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)); + 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)); + 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); + 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::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + 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::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + result.append(qstring); + } + dist = 5; + ret = QVariant(result); + } else { + // Make a single length array + UString ustring = value->toString(exec); + QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + QStringList result; + result.append(qstring); + ret = QVariant(result); + dist = 10; + } + break; + } + + case QMetaType::QByteArray: { + UString ustring = value->toString(exec); + ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()).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; + date->getUTCTime(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(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; + } + } else if (type == String) { + UString ustring = value->toString(exec); + QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + 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; + } + } + } + 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::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + // 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::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()); + + QRegExp re(qstring); + if (re.isValid()) { + ret = qVariantFromValue(re); + dist = 10; + } + } + break; + + case QMetaType::QObjectStar: + if (type == QObj) { + QtInstance* qtinst = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage)); + 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 = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage)); + 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); + 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); + 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); + 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); + 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); + 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); + if (itemdist >= 0) { + result.append(item.value<int>()); + dist = 10; + ret = QVariant::fromValue(result); + } + } + break; + } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) { + // Well.. we can do anything... just recurse with the autodetect flag + ret = convertValueToQVariant(exec, value, QMetaType::Void, distance, visitedObjects); + 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) +{ + HashSet<JSObject*> visitedObjects; + return convertValueToQVariant(exec, value, hint, distance, &visitedObjects); +} + +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(false); + + 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(exec, 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()->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 = JSC::gregorianDateTimeToMS(dt, time.msec(), /*inputIsUTC*/ false); + + DateInstance* instance = new (exec) DateInstance(exec->lexicalGlobalObject()->dateStructure()); + instance->setInternalValue(jsNumber(exec, trunc(ms))); + return instance; + } + + if (type == QMetaType::QByteArray) { + QByteArray ba = variant.value<QByteArray>(); + UString ustring(ba.constData()); + return jsString(exec, ustring); + } + + if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) { + QObject* obj = variant.value<QObject*>(); + return Instance::createRuntimeObject(exec, QtInstance::getQtInstance(obj, root)); + } + + 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, i.value()); + if (val) { + PutPropertySlot slot; + ret->put(exec, Identifier(exec, (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(), getDOMStructure<QtRuntimeMethod>(exec), ident) + , d_ptr(dd) +{ + QW_D(QtRuntimeMethod); + d->m_instance = inst; +} + +QtRuntimeMethod::~QtRuntimeMethod() +{ + 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, + const ArgList& jsArgs, + 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 (jsArgs.size() < (types.count() - 1)) { + 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 (int i = 0; converted && i < types.count() - 1; ++i) { + JSValue* arg = i < jsArgs.size() ? jsArgs.at(exec, 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 ((jsArgs.size() == types.count() - 1) + && (matchDistance == 0)) { + // perfect match, use this one + chosenIndex = index; + break; + } else { + QtMethodMatchData metaArgs(matchDistance, index, types, args); + if (candidates.isEmpty()) { + candidates.append(metaArgs); + } else { + QtMethodMatchData otherArgs = candidates.at(0); + if ((args.count() > otherArgs.args.count()) + || ((args.count() == otherArgs.args.count()) + && (matchDistance <= otherArgs.matchDistance))) { + candidates.prepend(metaArgs); + } else { + candidates.append(metaArgs); + } + } + } + } 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, TypeError, 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, TypeError, 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, SyntaxError, message.toLatin1().constData()); + } + } + + if (chosenIndex == -1 && candidates.count() > 0) { + QtMethodMatchData metaArgs = candidates.at(0); + if ((candidates.size() > 1) + && (metaArgs.args.count() == candidates.at(1).args.count()) + && (metaArgs.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) { + 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, TypeError, message.toLatin1().constData()); + } else { + chosenIndex = metaArgs.index; + args = metaArgs.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::mark() +{ + QtRuntimeMethod::mark(); + QW_D(QtRuntimeMetaMethod); + if (d->m_connect) + d->m_connect->mark(); + if (d->m_disconnect) + d->m_disconnect->mark(); +} + +JSValue* QtRuntimeMetaMethod::call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args) +{ + QtRuntimeMetaMethodData* d = static_cast<QtRuntimeMetaMethod *>(functionObject)->d_func(); + + // We're limited to 10 args + if (args.size() > 10) + return jsUndefined(); + + // We have to pick a method that matches.. + JSLock lock(false); + + 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, args, vargs, (void **)qargs, &errorObj)) != -1) { + if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0) + return jsUndefined(); + + if (vargs[0].isValid()) + return convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0]); + } + + if (errorObj) + return errorObj; + } else { + return throwError(exec, GeneralError, "cannot call function of deleted QObject"); + } + + // void functions return undefined + return 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); +} + +JSValue* QtRuntimeMetaMethod::lengthGetter(ExecState* exec, const Identifier&, const PropertySlot&) +{ + // QtScript always returns 0 + return jsNumber(exec, 0); +} + +JSValue* QtRuntimeMetaMethod::connectGetter(ExecState* exec, const Identifier& ident, const PropertySlot& slot) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slot.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, const Identifier& ident, const PropertySlot& slot) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slot.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; +} + +JSValue* QtRuntimeConnectionMethod::call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args) +{ + QtRuntimeConnectionMethodData* d = static_cast<QtRuntimeConnectionMethod *>(functionObject)->d_func(); + + JSLock lock(false); + + 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 (args.size() == 1) { + funcObject = args.at(exec, 0)->toObject(exec); + CallData callData; + if (funcObject->getCallData(callData) == CallTypeNone) { + if (d->m_isConnect) + return throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function"); + else + return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function"); + } + } else if (args.size() >= 2) { + if (args.at(exec, 0)->isObject()) { + thisObject = args.at(exec, 0)->toObject(exec); + + // Get the actual function to call + JSObject *asObj = args.at(exec, 1)->toObject(exec); + CallData callData; + if (asObj->getCallData(callData) != CallTypeNone) { + // Function version + funcObject = asObj; + } else { + // Convert it to a string + UString funcName = args.at(exec, 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 throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function"); + else + return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function"); + } + } + } else { + if (d->m_isConnect) + return throwError(exec, TypeError, "QtMetaMethod.connect: thisObject is not an object"); + else + return throwError(exec, TypeError, "QtMetaMethod.disconnect: thisObject is not an object"); + } + } else { + if (d->m_isConnect) + return throwError(exec, GeneralError, "QtMetaMethod.connect: no arguments given"); + else + return throwError(exec, GeneralError, "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 throwError(exec, GeneralError, 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 throwError(exec, GeneralError, 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 throwError(exec, TypeError, msg.toLatin1().constData()); + } + } else { + return throwError(exec, GeneralError, "cannot call function of deleted QObject"); + } + + return 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); +} + +JSValue* QtRuntimeConnectionMethod::lengthGetter(ExecState* exec, const Identifier&, const PropertySlot&) +{ + // we have one formal argument, and one optional + return jsNumber(exec, 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(false); + + // ### 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) + ArgList 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()); + } + } + CallData callData; + CallType callType = m_funcObject->getCallData(callData); + // Stuff in the __qt_sender property, if we can + if (m_funcObject->inherits(&JSFunction::info)) { + JSFunction* fimp = static_cast<JSFunction*>(m_funcObject.get()); + + JSObject* qt_sender = Instance::createRuntimeObject(exec, QtInstance::getQtInstance(sender(), ro)); + JSObject* wrapper = new (exec) JSObject(JSObject::createStructureID(jsNull())); + PutPropertySlot slot; + wrapper->put(exec, Identifier(exec, "__qt_sender__"), qt_sender, slot); + ScopeChain oldsc = fimp->scope(); + ScopeChain sc = oldsc; + sc.push(wrapper); + fimp->setScope(sc); + + call(exec, fimp, callType, callData, m_thisObject, l); + fimp->setScope(oldsc); + } else { + call(exec, m_funcObject, callType, callData, m_thisObject, l); + } + } + } + } + } 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 _rootObject && _rootObject->isValid() ? _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/WebCore/bridge/qt/qt_runtime.h b/WebCore/bridge/qt/qt_runtime.h new file mode 100644 index 0000000..80248a2 --- /dev/null +++ b/WebCore/bridge/qt/qt_runtime.h @@ -0,0 +1,231 @@ +/* + * 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 "completion.h" +#include "runtime.h" +#include "runtime_method.h" +#include "protect.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, + DynamicProperty, + ChildObject + } QtFieldType; + + QtField(const QMetaProperty &p) + : m_type(MetaProperty), m_property(p) + {} + + QtField(const QByteArray &b) + : m_type(DynamicProperty), m_dynamicProperty(b) + {} + + QtField(QObject *child) + : m_type(ChildObject), m_childObject(child) + {} + + virtual JSValue* valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue*) const; + virtual const char* 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* exec) + { + return exec->lexicalGlobalObject()->functionPrototype(); + } + + static PassRefPtr<StructureID> createStructureID(JSValue* prototype) + { + return StructureID::create(prototype, TypeInfo(ObjectType)); + } + +protected: + 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 void mark(); + +protected: + QtRuntimeMetaMethodData* d_func() const {return reinterpret_cast<QtRuntimeMetaMethodData*>(d_ptr);} + +private: + virtual CallType getCallData(CallData&); + static JSValue* call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args); + static JSValue* lengthGetter(ExecState*, const Identifier&, const PropertySlot&); + static JSValue* connectGetter(ExecState*, const Identifier&, const PropertySlot&); + static JSValue* disconnectGetter(ExecState*, const Identifier&, const PropertySlot&); +}; + +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&); + +protected: + QtRuntimeConnectionMethodData* d_func() const {return reinterpret_cast<QtRuntimeConnectionMethodData*>(d_ptr);} + +private: + virtual CallType getCallData(CallData&); + static JSValue* call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args); + static JSValue* lengthGetter(ExecState*, const Identifier&, const PropertySlot&); + 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); + +} // namespace Bindings +} // namespace JSC + +#endif diff --git a/WebCore/bridge/runtime.cpp b/WebCore/bridge/runtime.cpp new file mode 100644 index 0000000..3ca53ab --- /dev/null +++ b/WebCore/bridge/runtime.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2003, 2006, 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.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) + : _rootObject(rootObject) +{ + ASSERT(_rootObject); +} + +Array::~Array() +{ +} + +Instance::Instance(PassRefPtr<RootObject> rootObject) + : _rootObject(rootObject) +{ + ASSERT(_rootObject); +} + +Instance::~Instance() +{ +} + +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(); +} + +JSValue* Instance::getValueOfField(ExecState* exec, const Field* aField) const +{ + return aField->valueFromInstance(exec, this); +} + +void Instance::setValueOfField(ExecState* exec, const Field* aField, JSValue* aValue) const +{ + aField->setValueToInstance(exec, this, aValue); +} + +RuntimeObjectImp* Instance::createRuntimeObject(ExecState* exec, PassRefPtr<Instance> instance) +{ +#if PLATFORM(QT) + if (instance->getBindingLanguage() == QtLanguage) + return QtInstance::getRuntimeObject(exec, static_cast<QtInstance*>(instance.get())); +#endif + JSLock lock(false); + + return new (exec) RuntimeObjectImp(exec, instance); +} + +Instance* Instance::getInstance(JSObject* object, BindingLanguage language) +{ + if (!object) + return 0; + if (!object->inherits(&RuntimeObjectImp::s_info)) + return 0; + Instance* instance = static_cast<RuntimeObjectImp*>(object)->getInternalInstance(); + if (!instance) + return 0; + if (instance->getBindingLanguage() != language) + return 0; + return instance; +} + +RootObject* Instance::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +} } // namespace JSC::Bindings diff --git a/WebCore/bridge/runtime.h b/WebCore/bridge/runtime.h new file mode 100644 index 0000000..3c39121 --- /dev/null +++ b/WebCore/bridge/runtime.h @@ -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. + */ + +#ifndef JAVASCRIPTCORE_BINDINGS_RUNTIME_H +#define JAVASCRIPTCORE_BINDINGS_RUNTIME_H + +#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 RuntimeObjectImp; + +namespace Bindings { + +class Instance; +class Method; +class RootObject; + +typedef Vector<Method*> MethodList; + +class Field { +public: + virtual const char* name() const = 0; + virtual JSValue* valueFromInstance(ExecState*, const Instance*) const = 0; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue*) const = 0; + + virtual ~Field() { } +}; + +class Method : Noncopyable { +public: + virtual const char *name() const = 0; + virtual int numParameters() const = 0; + + virtual ~Method() { } +}; + +class Class : Noncopyable { +public: + virtual const char* name() const = 0; + 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: + typedef enum { +#if ENABLE(MAC_JAVA_BRIDGE) + JavaLanguage, +#endif +#if PLATFORM(MAC) + ObjectiveCLanguage, +#endif + CLanguage +#if PLATFORM(QT) + , QtLanguage +#endif + } BindingLanguage; + + Instance(PassRefPtr<RootObject>); + + static void setDidExecuteFunction(KJSDidExecuteFunctionPtr func); + static KJSDidExecuteFunctionPtr didExecuteFunction(); + + static RuntimeObjectImp* createRuntimeObject(ExecState*, PassRefPtr<Instance>); + static Instance* getInstance(JSObject*, BindingLanguage); + + // 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; + + virtual JSValue* getValueOfField(ExecState*, const Field*) const; + virtual JSValue* getValueOfUndefinedField(ExecState*, const Identifier&) const { return jsUndefined(); } + virtual void setValueOfField(ExecState*, const Field*, JSValue*) const; + virtual bool supportsSetValueOfUndefinedField() { return false; } + virtual void setValueOfUndefinedField(ExecState*, const Identifier&, JSValue*) {} + + virtual JSValue* invokeMethod(ExecState*, const MethodList&, const ArgList& args) = 0; + + virtual bool supportsInvokeDefaultMethod() const { return false; } + virtual JSValue* invokeDefaultMethod(ExecState*, const ArgList&) { return jsUndefined(); } + + virtual bool supportsConstruct() const { return false; } + virtual JSValue* invokeConstruct(ExecState*, const ArgList&) { return 0; } + + virtual void getPropertyNames(ExecState*, PropertyNameArray&) { } + + virtual JSValue* defaultValue(ExecState*, PreferredPrimitiveType) const = 0; + + virtual JSValue* valueOf(ExecState* exec) const { return jsString(exec, getClass()->name()); } + + RootObject* rootObject() const; + + virtual ~Instance(); + + virtual BindingLanguage getBindingLanguage() const = 0; + +protected: + virtual void virtualBegin() { } + virtual void virtualEnd() { } + + RefPtr<RootObject> _rootObject; +}; + +class Array : 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> _rootObject; +}; + +const char *signatureForParameters(const ArgList&); + +typedef HashMap<RefPtr<UString::Rep>, MethodList*> MethodListMap; +typedef HashMap<RefPtr<UString::Rep>, Method*> MethodMap; +typedef HashMap<RefPtr<UString::Rep>, Field*> FieldMap; + +} // namespace Bindings + +} // namespace JSC + +#endif diff --git a/WebCore/bridge/runtime_array.cpp b/WebCore/bridge/runtime_array.cpp new file mode 100644 index 0000000..c3912a9 --- /dev/null +++ b/WebCore/bridge/runtime_array.cpp @@ -0,0 +1,123 @@ +/* + * 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 "JSDOMBinding.h" + +using namespace WebCore; + +namespace JSC { + +const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &JSArray::info, 0, 0 }; + +RuntimeArray::RuntimeArray(ExecState* exec, Bindings::Array* a) + : JSObject(getDOMStructure<RuntimeArray>(exec)) + , _array(a) +{ +} + +JSValue* RuntimeArray::lengthGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + RuntimeArray* thisObj = static_cast<RuntimeArray*>(asObject(slot.slotBase())); + return jsNumber(exec, thisObj->getLength()); +} + +JSValue* RuntimeArray::indexGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + RuntimeArray* thisObj = static_cast<RuntimeArray*>(asObject(slot.slotBase())); + return thisObj->getConcreteArray()->valueAt(exec, slot.index()); +} + +bool RuntimeArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(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::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, RangeError); + 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, RangeError); + return; + } + + getConcreteArray()->setValueAt(exec, index, value); +} + +bool RuntimeArray::deleteProperty(ExecState*, const Identifier&) +{ + return false; +} + +bool RuntimeArray::deleteProperty(ExecState*, unsigned) +{ + return false; +} + +} diff --git a/WebCore/bridge/runtime_array.h b/WebCore/bridge/runtime_array.h new file mode 100644 index 0000000..22ad5f3 --- /dev/null +++ b/WebCore/bridge/runtime_array.h @@ -0,0 +1,73 @@ +/* + * 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 "runtime.h" +#include <runtime/JSGlobalObject.h> + +namespace JSC { + +class RuntimeArray : public JSObject { +public: + RuntimeArray(ExecState*, Bindings::Array*); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual bool getOwnPropertySlot(ExecState *, unsigned, PropertySlot&); + 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 _array.get(); } + + static const ClassInfo s_info; + + static ArrayPrototype* createPrototype(ExecState* exec) + { + return exec->lexicalGlobalObject()->arrayPrototype(); + } + + static PassRefPtr<StructureID> createStructureID(JSValue* prototype) + { + return StructureID::create(prototype, TypeInfo(ObjectType)); + } + +private: + static JSValue* lengthGetter(ExecState*, const Identifier&, const PropertySlot&); + static JSValue* indexGetter(ExecState*, const Identifier&, const PropertySlot&); + + OwnPtr<Bindings::Array> _array; +}; + +} // namespace JSC + +#endif // RUNTIME_ARRAY_H_ diff --git a/WebCore/bridge/runtime_method.cpp b/WebCore/bridge/runtime_method.cpp new file mode 100644 index 0000000..b855081 --- /dev/null +++ b/WebCore/bridge/runtime_method.cpp @@ -0,0 +1,110 @@ +/* + * 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 "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", 0, 0, 0 }; + +RuntimeMethod::RuntimeMethod(ExecState* exec, const Identifier& ident, Bindings::MethodList& m) + : InternalFunction(&exec->globalData(), getDOMStructure<RuntimeMethod>(exec), ident) + , _methodList(new MethodList(m)) +{ +} + +JSValue* RuntimeMethod::lengthGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + RuntimeMethod* thisObj = static_cast<RuntimeMethod*>(asObject(slot.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(exec, thisObj->_methodList->at(0)->numParameters()); +} + +bool RuntimeMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + return InternalFunction::getOwnPropertySlot(exec, propertyName, slot); +} + +static JSValue* callRuntimeMethod(ExecState* exec, JSObject* function, JSValue* thisValue, const ArgList& args) +{ + RuntimeMethod* method = static_cast<RuntimeMethod*>(function); + + if (method->methods()->isEmpty()) + return jsUndefined(); + + RuntimeObjectImp* imp; + + if (thisValue->isObject(&RuntimeObjectImp::s_info)) { + imp = static_cast<RuntimeObjectImp*>(asObject(thisValue)); + } else { + // If thisObj is the DOM object for a plugin, get the corresponding + // runtime object from the DOM object. + JSValue* value = thisValue->get(exec, Identifier(exec, "__apple_runtime_object")); + if (value->isObject(&RuntimeObjectImp::s_info)) + imp = static_cast<RuntimeObjectImp*>(asObject(value)); + else + return throwError(exec, TypeError); + } + + RefPtr<Instance> instance = imp->getInternalInstance(); + if (!instance) + return RuntimeObjectImp::throwInvalidAccessError(exec); + + instance->begin(); + JSValue* result = instance->invokeMethod(exec, *method->methods(), args); + instance->end(); + return result; +} + +CallType RuntimeMethod::getCallData(CallData& callData) +{ + callData.native.function = callRuntimeMethod; + return CallTypeHost; +} + +} diff --git a/WebCore/bridge/runtime_method.h b/WebCore/bridge/runtime_method.h new file mode 100644 index 0000000..adc68f6 --- /dev/null +++ b/WebCore/bridge/runtime_method.h @@ -0,0 +1,63 @@ +/* + * 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 "runtime.h" +#include <runtime/InternalFunction.h> +#include <runtime/JSGlobalObject.h> +#include <wtf/OwnPtr.h> + +namespace JSC { + +class RuntimeMethod : public InternalFunction { +public: + RuntimeMethod(ExecState*, const Identifier& name, Bindings::MethodList&); + Bindings::MethodList* methods() const { return _methodList.get(); } + + static const ClassInfo s_info; + + static FunctionPrototype* createPrototype(ExecState* exec) + { + return exec->lexicalGlobalObject()->functionPrototype(); + } + + static PassRefPtr<StructureID> createStructureID(JSValue* prototype) + { + return StructureID::create(prototype, TypeInfo(ObjectType, ImplementsHasInstance)); + } + +private: + static JSValue* lengthGetter(ExecState*, const Identifier&, const PropertySlot&); + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual CallType getCallData(CallData&); + + OwnPtr<Bindings::MethodList> _methodList; +}; + +} // namespace JSC + +#endif diff --git a/WebCore/bridge/runtime_object.cpp b/WebCore/bridge/runtime_object.cpp new file mode 100644 index 0000000..5bde348 --- /dev/null +++ b/WebCore/bridge/runtime_object.cpp @@ -0,0 +1,270 @@ +/* + * 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_object.h" + +#include "JSDOMBinding.h" +#include "runtime_method.h" +#include "runtime_root.h" +#include <runtime/Error.h> +#include <runtime/ObjectPrototype.h> + +using namespace WebCore; + +namespace JSC { + +using namespace Bindings; + +const ClassInfo RuntimeObjectImp::s_info = { "RuntimeObject", 0, 0, 0 }; + +RuntimeObjectImp::RuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> i) + : JSObject(getDOMStructure<RuntimeObjectImp>(exec)) + , instance(i) +{ + instance->rootObject()->addRuntimeObject(this); +} + +RuntimeObjectImp::RuntimeObjectImp(ExecState* exec, PassRefPtr<StructureID> structure, PassRefPtr<Instance> i) + : JSObject(structure) + , instance(i) +{ + instance->rootObject()->addRuntimeObject(this); +} + +RuntimeObjectImp::~RuntimeObjectImp() +{ + if (instance) + instance->rootObject()->removeRuntimeObject(this); +} + +void RuntimeObjectImp::invalidate() +{ + ASSERT(instance); + instance = 0; +} + +JSValue* RuntimeObjectImp::fallbackObjectGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) +{ + RuntimeObjectImp* thisObj = static_cast<RuntimeObjectImp*>(asObject(slot.slotBase())); + RefPtr<Instance> instance = thisObj->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* RuntimeObjectImp::fieldGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) +{ + RuntimeObjectImp* thisObj = static_cast<RuntimeObjectImp*>(asObject(slot.slotBase())); + RefPtr<Instance> instance = thisObj->instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + Class *aClass = instance->getClass(); + Field* aField = aClass->fieldNamed(propertyName, instance.get()); + JSValue* result = instance->getValueOfField(exec, aField); + + instance->end(); + + return result; +} + +JSValue* RuntimeObjectImp::methodGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) +{ + RuntimeObjectImp* thisObj = static_cast<RuntimeObjectImp*>(asObject(slot.slotBase())); + RefPtr<Instance> instance = thisObj->instance; + + if (!instance) + return throwInvalidAccessError(exec); + + instance->begin(); + + Class *aClass = instance->getClass(); + MethodList methodList = aClass->methodsNamed(propertyName, instance.get()); + JSValue* result = new (exec) RuntimeMethod(exec, propertyName, methodList); + + instance->end(); + + return result; +} + +bool RuntimeObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (!instance) { + throwInvalidAccessError(exec); + return false; + } + + 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(); +#if PLATFORM(QT) + // For Qt instances we call the baseclass implementation to allow + // runtime objects to have custom properties or a prototype + if (instance->getBindingLanguage() == Instance::QtLanguage) + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +#endif + // For other platforms and/or binding languages we don't allow + // runtime properties/prototypes + return false; +} + +void RuntimeObjectImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot) +{ + if (!instance) { + throwInvalidAccessError(exec); + return; + } + + RefPtr<Instance> protector(instance); + instance->begin(); + + // Set the value of the property. + Field *aField = instance->getClass()->fieldNamed(propertyName, instance.get()); + if (aField) + instance->setValueOfField(exec, aField, value); + else if (instance->supportsSetValueOfUndefinedField()) + instance->setValueOfUndefinedField(exec, propertyName, value); +#if PLATFORM(QT) + else if (instance->getBindingLanguage() == Instance::QtLanguage) + JSObject::put(exec, propertyName, value, slot); +#endif + + instance->end(); +} + +bool RuntimeObjectImp::deleteProperty(ExecState*, const Identifier&) +{ + // Can never remove a property of a RuntimeObject. + return false; +} + +JSValue* RuntimeObjectImp::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (!instance) + return throwInvalidAccessError(exec); + + RefPtr<Instance> protector(instance); + instance->begin(); + JSValue* result = instance->defaultValue(exec, hint); + instance->end(); + return result; +} + +static JSValue* callRuntimeObject(ExecState* exec, JSObject* function, JSValue*, const ArgList& args) +{ + RefPtr<Instance> instance(static_cast<RuntimeObjectImp*>(function)->getInternalInstance()); + instance->begin(); + JSValue* result = instance->invokeDefaultMethod(exec, args); + instance->end(); + return result; +} + +CallType RuntimeObjectImp::getCallData(CallData& callData) +{ + if (!instance || !instance->supportsInvokeDefaultMethod()) + return CallTypeNone; + callData.native.function = callRuntimeObject; + return CallTypeHost; +} + +JSObject* callRuntimeConstructor(ExecState* exec, JSObject* constructor, const ArgList& args) +{ + RefPtr<Instance> instance(static_cast<RuntimeObjectImp*>(constructor)->getInternalInstance()); + instance->begin(); + JSValue* result = instance->invokeConstruct(exec, args); + instance->end(); + + ASSERT(result); + return result->isObject() ? static_cast<JSObject*>(result) : constructor; +} + +ConstructType RuntimeObjectImp::getConstructData(ConstructData& constructData) +{ + if (!instance || !instance->supportsConstruct()) + return ConstructTypeNone; + constructData.native.function = callRuntimeConstructor; + return ConstructTypeHost; +} + +void RuntimeObjectImp::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +{ + if (!instance) { + throwInvalidAccessError(exec); + return; + } + + instance->begin(); + instance->getPropertyNames(exec, propertyNames); + instance->end(); +} + +JSObject* RuntimeObjectImp::throwInvalidAccessError(ExecState* exec) +{ + return throwError(exec, ReferenceError, "Trying to access object from destroyed plug-in."); +} + +} diff --git a/WebCore/bridge/runtime_object.h b/WebCore/bridge/runtime_object.h new file mode 100644 index 0000000..7e11273 --- /dev/null +++ b/WebCore/bridge/runtime_object.h @@ -0,0 +1,82 @@ +/* + * 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 KJS_RUNTIME_OBJECT_H +#define KJS_RUNTIME_OBJECT_H + +#include "runtime.h" +#include <runtime/JSGlobalObject.h> + +namespace JSC { + +class RuntimeObjectImp : public JSObject { +public: + virtual ~RuntimeObjectImp(); + + virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + 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 getPropertyNames(ExecState*, PropertyNameArray&); + + virtual void invalidate(); + Bindings::Instance* getInternalInstance() const { return instance.get(); } + + static JSObject* throwInvalidAccessError(ExecState*); + + static const ClassInfo s_info; + + static ObjectPrototype* createPrototype(ExecState* exec) + { + return exec->lexicalGlobalObject()->objectPrototype(); + } + + static PassRefPtr<StructureID> createStructureID(JSValue* prototype) + { + return StructureID::create(prototype, TypeInfo(ObjectType)); + } + +protected: + RuntimeObjectImp(ExecState*, PassRefPtr<StructureID>, PassRefPtr<Bindings::Instance>); + +private: + friend class Bindings::Instance; + RuntimeObjectImp(ExecState*, PassRefPtr<Bindings::Instance>); + + virtual const ClassInfo* classInfo() const { return &s_info; } + + static JSValue* fallbackObjectGetter(ExecState*, const Identifier&, const PropertySlot&); + static JSValue* fieldGetter(ExecState*, const Identifier&, const PropertySlot&); + static JSValue* methodGetter(ExecState*, const Identifier&, const PropertySlot&); + + RefPtr<Bindings::Instance> instance; +}; + +} // namespace + +#endif diff --git a/WebCore/bridge/runtime_root.cpp b/WebCore/bridge/runtime_root.cpp new file mode 100644 index 0000000..027d0c9 --- /dev/null +++ b/WebCore/bridge/runtime_root.cpp @@ -0,0 +1,173 @@ +/* + * 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 "runtime.h" +#include "runtime_object.h" +#include <runtime/JSGlobalObject.h> +#include <wtf/HashCountedSet.h> +#include <wtf/HashSet.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() +{ + static 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; +} + +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<RuntimeObjectImp*>::iterator end = m_runtimeObjects.end(); + for (HashSet<RuntimeObjectImp*>::iterator it = m_runtimeObjects.begin(); it != end; ++it) + (*it)->invalidate(); + + m_runtimeObjects.clear(); + } + + m_isValid = false; + + m_nativeHandle = 0; + m_globalObject = 0; + + 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::addRuntimeObject(RuntimeObjectImp* object) +{ + ASSERT(m_isValid); + ASSERT(!m_runtimeObjects.contains(object)); + + m_runtimeObjects.add(object); +} + +void RootObject::removeRuntimeObject(RuntimeObjectImp* object) +{ + ASSERT(m_isValid); + ASSERT(m_runtimeObjects.contains(object)); + + m_runtimeObjects.remove(object); +} + +} } // namespace JSC::Bindings diff --git a/WebCore/bridge/runtime_root.h b/WebCore/bridge/runtime_root.h new file mode 100644 index 0000000..a86096d --- /dev/null +++ b/WebCore/bridge/runtime_root.h @@ -0,0 +1,89 @@ +/* + * 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 <kjs/protect.h> + +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/RefCounted.h> + +namespace JSC { + +class Interpreter; +class JSGlobalObject; +class RuntimeObjectImp; + +namespace Bindings { + +class RootObject; + +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 addRuntimeObject(RuntimeObjectImp*); + void removeRuntimeObject(RuntimeObjectImp*); +private: + RootObject(const void* nativeHandle, JSGlobalObject*); + + bool m_isValid; + + const void* m_nativeHandle; + ProtectedPtr<JSGlobalObject> m_globalObject; + ProtectCountSet m_protectCountSet; + + HashSet<RuntimeObjectImp*> m_runtimeObjects; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif diff --git a/WebCore/bridge/test.js b/WebCore/bridge/test.js new file mode 100644 index 0000000..5d4f79f --- /dev/null +++ b/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/WebCore/bridge/testC.js b/WebCore/bridge/testC.js new file mode 100644 index 0000000..44677c7 --- /dev/null +++ b/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/WebCore/bridge/testM.js b/WebCore/bridge/testM.js new file mode 100644 index 0000000..7985d21 --- /dev/null +++ b/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/WebCore/bridge/testbindings.cpp b/WebCore/bridge/testbindings.cpp new file mode 100644 index 0000000..fa61e2f --- /dev/null +++ b/WebCore/bridge/testbindings.cpp @@ -0,0 +1,421 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * 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 <assert.h> +#include <stdio.h> +#include <string.h> + +#include "JSValue.h" +#include "JSObject.h" +#include "types.h" +#include "interpreter.h" + +#include "npruntime_internal.h" + +#include "runtime.h" +#include "runtime_object.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/WebCore/bridge/testbindings.mm b/WebCore/bridge/testbindings.mm new file mode 100644 index 0000000..9215c48 --- /dev/null +++ b/WebCore/bridge/testbindings.mm @@ -0,0 +1,289 @@ +/* + * 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 <Foundation/Foundation.h> + +#import <WebKit/WebScriptObject.h> + +#include <stdio.h> +#include <string.h> + +#include "JSValue.h" +#include "JSObject.h" +#include "types.h" +#include "interpreter.h" + +#include "runtime.h" +#include "runtime_object.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/WebCore/bridge/testbindings.pro b/WebCore/bridge/testbindings.pro new file mode 100644 index 0000000..e6e0b9d --- /dev/null +++ b/WebCore/bridge/testbindings.pro @@ -0,0 +1,8 @@ +QT -= gui + +include(../../WebKit.pri) +INCLUDEPATH += .. ../kjs . +qt-port:INCLUDEPATH += bindings/qt + +SOURCES += testqtbindings.cpp + diff --git a/WebCore/bridge/testqtbindings.cpp b/WebCore/bridge/testqtbindings.cpp new file mode 100644 index 0000000..41a9a3a --- /dev/null +++ b/WebCore/bridge/testqtbindings.cpp @@ -0,0 +1,142 @@ +/* + * 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 <assert.h> +#include <stdio.h> +#include <string.h> + +#include "JSValue.h" +#include "JSObject.h" +#include "types.h" +#include "interpreter.h" + +#include "qobject.h" +#include "qdebug.h" + +#include "runtime.h" +#include "runtime_object.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" diff --git a/WebCore/bridge/win/FrameCGWin.cpp b/WebCore/bridge/win/FrameCGWin.cpp deleted file mode 100644 index eea6961..0000000 --- a/WebCore/bridge/win/FrameCGWin.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 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 "FrameWin.h" - -#include <windows.h> - -#include "FrameView.h" -#include "GraphicsContext.h" -#include "Settings.h" - -#include <CoreGraphics/CoreGraphics.h> - -using std::min; - -namespace WebCore { - -static void drawRectIntoContext(IntRect rect, FrameView* view, GraphicsContext* gc) -{ - IntSize offset = view->scrollOffset(); - rect.move(-offset.width(), -offset.height()); - rect = view->convertToContainingWindow(rect); - - gc->concatCTM(AffineTransform().translate(-rect.x(), -rect.y())); - - view->paint(gc, rect); -} - -HBITMAP imageFromSelection(Frame* frame, bool forceBlackText) -{ - frame->setPaintRestriction(forceBlackText ? PaintRestrictionSelectionOnlyBlackText : PaintRestrictionSelectionOnly); - FloatRect fr = frame->selectionRect(); - IntRect ir(static_cast<int>(fr.x()), static_cast<int>(fr.y()), - static_cast<int>(fr.width()), static_cast<int>(fr.height())); - - void* bits; - HDC hdc = CreateCompatibleDC(0); - int w = ir.width(); - int h = ir.height(); - BITMAPINFO bmp = { { sizeof(BITMAPINFOHEADER), w, h, 1, 32 } }; - - HBITMAP hbmp = CreateDIBSection(0, &bmp, DIB_RGB_COLORS, static_cast<void**>(&bits), 0, 0); - HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc, hbmp)); - CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); - CGContextRef context = CGBitmapContextCreate(static_cast<void*>(bits), w, h, - 8, w * sizeof(RGBQUAD), deviceRGB, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); - CGColorSpaceRelease(deviceRGB); - CGContextSaveGState(context); - - GraphicsContext gc(context); - - frame->document()->updateLayout(); - drawRectIntoContext(ir, frame->view(), &gc); - - CGContextRelease(context); - SelectObject(hdc, hbmpOld); - DeleteDC(hdc); - - frame->setPaintRestriction(PaintRestrictionNone); - - return hbmp; -} - -} // namespace WebCore diff --git a/WebCore/bridge/win/FrameWin.cpp b/WebCore/bridge/win/FrameWin.cpp deleted file mode 100644 index 6599a88..0000000 --- a/WebCore/bridge/win/FrameWin.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 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 "FrameWin.h" - -#include <winsock2.h> -#include <windows.h> - -#include "AffineTransform.h" -#include "FloatRect.h" -#include "Document.h" -#include "EditorClient.h" -#include "FrameLoader.h" -#include "FrameLoadRequest.h" -#include "FramePrivate.h" -#include "FrameView.h" -#include "HTMLIFrameElement.h" -#include "HTMLNames.h" -#include "HTMLTableCellElement.h" -#include "KeyboardEvent.h" -#include "NP_jsobject.h" -#include "NotImplemented.h" -#include "Page.h" -#include "Plugin.h" -#include "PluginDatabase.h" -#include "PluginView.h" -#include "RegularExpression.h" -#include "RenderFrame.h" -#include "RenderTableCell.h" -#include "RenderView.h" -#include "ResourceHandle.h" -#include "TextResourceDecoder.h" -#include "kjs_proxy.h" -#include "kjs_window.h" -#include "npruntime_impl.h" -#include "runtime_root.h" -#include "GraphicsContext.h" -#include "Settings.h" - -using std::min; -using namespace KJS::Bindings; - -namespace WebCore { - -using namespace HTMLNames; - -void Frame::clearPlatformScriptObjects() -{ -} - -KJS::Bindings::Instance* Frame::createScriptInstanceForWidget(Widget* widget) -{ - // FIXME: Ideally we'd have an isPluginView() here but we can't add that to the open source tree right now. - if (widget->isFrameView()) - return 0; - - return static_cast<PluginView*>(widget)->bindingInstance(); -} - -void computePageRectsForFrame(Frame* frame, const IntRect& printRect, float headerHeight, float footerHeight, float userScaleFactor,Vector<IntRect>& pages, int& outPageHeight) -{ - ASSERT(frame); - - pages.clear(); - outPageHeight = 0; - - if (!frame->document() || !frame->view() || !frame->document()->renderer()) - return; - - RenderView* root = static_cast<RenderView *>(frame->document()->renderer()); - - if (!root) { - LOG_ERROR("document to be printed has no renderer"); - return; - } - - if (userScaleFactor <= 0) { - LOG_ERROR("userScaleFactor has bad value %.2f", userScaleFactor); - return; - } - - float ratio = static_cast<float>(printRect.height()) / static_cast<float>(printRect.width()); - - float pageWidth = static_cast<float>(root->docWidth()); - float pageHeight = pageWidth * ratio; - outPageHeight = static_cast<int>(pageHeight); // this is the height of the page adjusted by margins - pageHeight -= (headerHeight + footerHeight); - - if (pageHeight <= 0) { - LOG_ERROR("pageHeight has bad value %.2f", pageHeight); - return; - } - - float currPageHeight = pageHeight / userScaleFactor; - float docHeight = root->layer()->height(); - float docWidth = root->layer()->width(); - float currPageWidth = pageWidth / userScaleFactor; - - - // always return at least one page, since empty files should print a blank page - float printedPagesHeight = 0.0f; - do { - float proposedBottom = min(docHeight, printedPagesHeight + pageHeight); - frame->adjustPageHeight(&proposedBottom, printedPagesHeight, proposedBottom, printedPagesHeight); - currPageHeight = max(1.0f, proposedBottom - printedPagesHeight); - - pages.append(IntRect(0, printedPagesHeight, currPageWidth, currPageHeight)); - printedPagesHeight += currPageHeight; - } while (printedPagesHeight < docHeight); -} - -DragImageRef Frame::dragImageForSelection() -{ - if (selectionController()->isRange()) - return imageFromSelection(this, false); - - return 0; -} - -void Frame::dashboardRegionsChanged() -{ -} - -} // namespace WebCore diff --git a/WebCore/bridge/win/PageWin.cpp b/WebCore/bridge/win/PageWin.cpp deleted file mode 100644 index f4c744a..0000000 --- a/WebCore/bridge/win/PageWin.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2006, 2007 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 "Page.h" - -#include "Frame.h" -#include "FrameView.h" -#include "FloatRect.h" -#include <windows.h> - -namespace WebCore { - -HINSTANCE Page::s_instanceHandle = 0; - -} // namespace WebCore |