diff options
Diffstat (limited to 'Source/WebKit2/WebProcess/Plugins/Netscape')
23 files changed, 7033 insertions, 0 deletions
diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPMethod.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPMethod.cpp new file mode 100644 index 000000000..898147135 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPMethod.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "JSNPMethod.h" + +#include "JSNPObject.h" +#include <JavaScriptCore/Error.h> +#include <JavaScriptCore/FunctionPrototype.h> +#include <JavaScriptCore/JSGlobalObject.h> +#include <JavaScriptCore/JSObject.h> +#include <WebCore/JSHTMLElement.h> +#include <WebCore/JSPluginElementFunctions.h> + +using namespace JSC; +using namespace WebCore; + +namespace WebKit { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSNPMethod); + +const ClassInfo JSNPMethod::s_info = { "NPMethod", &InternalFunction::s_info, 0, 0, CREATE_METHOD_TABLE(JSNPMethod) }; + +JSNPMethod::JSNPMethod(JSGlobalObject* globalObject, Structure* structure, NPIdentifier npIdentifier) + : InternalFunction(globalObject, structure) + , m_npIdentifier(npIdentifier) +{ +} + +void JSNPMethod::finishCreation(JSGlobalData& globalData, const Identifier& name) +{ + Base::finishCreation(globalData, name); + ASSERT(inherits(&s_info)); +} + +static EncodedJSValue JSC_HOST_CALL callMethod(ExecState* exec) +{ + JSNPMethod* jsNPMethod = static_cast<JSNPMethod*>(exec->callee()); + + JSValue thisValue = exec->hostThisValue(); + + // Check if we're calling a method on the plug-in script object. + if (thisValue.inherits(&JSHTMLElement::s_info)) { + JSHTMLElement* element = static_cast<JSHTMLElement*>(asObject(thisValue)); + + // Try to get the script object from the element + if (JSObject* scriptObject = pluginScriptObject(exec, element)) + thisValue = scriptObject; + } + + if (thisValue.inherits(&JSNPObject::s_info)) { + JSNPObject* jsNPObject = static_cast<JSNPObject*>(asObject(thisValue)); + + return JSValue::encode(jsNPObject->callMethod(exec, jsNPMethod->npIdentifier())); + } + + return throwVMTypeError(exec); +} + +CallType JSNPMethod::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callMethod; + return CallTypeHost; +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPMethod.h b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPMethod.h new file mode 100644 index 000000000..ff0961c38 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPMethod.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef JSNPMethod_h +#define JSNPMethod_h + +#include <JavaScriptCore/FunctionPrototype.h> +#include <JavaScriptCore/InternalFunction.h> +#include <JavaScriptCore/JSGlobalObject.h> + +typedef void* NPIdentifier; + +namespace WebKit { + +// A JSObject that wraps an NPMethod. +class JSNPMethod : public JSC::InternalFunction { +public: + typedef JSC::InternalFunction Base; + + static JSNPMethod* create(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, const JSC::Identifier& name, NPIdentifier npIdent) + { + JSC::Structure* structure = createStructure(exec->globalData(), globalObject, globalObject->functionPrototype()); + JSNPMethod* method = new (JSC::allocateCell<JSNPMethod>(*exec->heap())) JSNPMethod(globalObject, structure, npIdent); + method->finishCreation(exec->globalData(), name); + return method; + } + + static const JSC::ClassInfo s_info; + + NPIdentifier npIdentifier() const { return m_npIdentifier; } + +protected: + void finishCreation(JSC::JSGlobalData&, const JSC::Identifier& name); + +private: + JSNPMethod(JSC::JSGlobalObject*, JSC::Structure*, NPIdentifier); + + static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(globalData, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), &s_info); + } + + static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&); + + NPIdentifier m_npIdentifier; +}; + + +} // namespace WebKit + +#endif // JSNPMethod_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPObject.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPObject.cpp new file mode 100644 index 000000000..aa56a3752 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPObject.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "JSNPObject.h" + +#include "JSNPMethod.h" +#include "NPJSObject.h" +#include "NPRuntimeObjectMap.h" +#include "NPRuntimeUtilities.h" +#include <JavaScriptCore/Error.h> +#include <JavaScriptCore/JSGlobalObject.h> +#include <JavaScriptCore/JSLock.h> +#include <JavaScriptCore/ObjectPrototype.h> +#include <WebCore/IdentifierRep.h> +#include <wtf/Assertions.h> +#include <wtf/text/WTFString.h> + +using namespace JSC; +using namespace WebCore; + +namespace WebKit { + +static NPIdentifier npIdentifierFromIdentifier(const Identifier& identifier) +{ + return static_cast<NPIdentifier>(IdentifierRep::get(identifier.ustring().utf8().data())); +} + +const ClassInfo JSNPObject::s_info = { "NPObject", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSNPObject) }; + +JSNPObject::JSNPObject(JSGlobalObject* globalObject, Structure* structure, NPRuntimeObjectMap* objectMap, NPObject* npObject) + : JSNonFinalObject(globalObject->globalData(), structure) + , m_objectMap(objectMap) + , m_npObject(npObject) +{ + ASSERT(globalObject == structure->globalObject()); +} + +void JSNPObject::finishCreation(JSGlobalObject* globalObject) +{ + Base::finishCreation(globalObject->globalData()); + ASSERT(inherits(&s_info)); + + // We should never have an NPJSObject inside a JSNPObject. + ASSERT(!NPJSObject::isNPJSObject(m_npObject)); + + retainNPObject(m_npObject); +} + +JSNPObject::~JSNPObject() +{ + ASSERT(!m_npObject); +} + +void JSNPObject::destroy(JSCell* cell) +{ + jsCast<JSNPObject*>(cell)->JSNPObject::~JSNPObject(); +} + +void JSNPObject::invalidate() +{ + ASSERT(m_npObject); + ASSERT_GC_OBJECT_INHERITS(this, &s_info); + + releaseNPObject(m_npObject); + m_npObject = 0; +} + +NPObject* JSNPObject::leakNPObject() +{ + ASSERT(m_npObject); + ASSERT_GC_OBJECT_INHERITS(this, &s_info); + + NPObject* object = m_npObject; + m_npObject = 0; + return object; +} + +JSValue JSNPObject::callMethod(ExecState* exec, NPIdentifier methodName) +{ + ASSERT_GC_OBJECT_INHERITS(this, &s_info); + if (!m_npObject) + return throwInvalidAccessError(exec); + + size_t argumentCount = exec->argumentCount(); + Vector<NPVariant, 8> arguments(argumentCount); + + // Convert all arguments to NPVariants. + for (size_t i = 0; i < argumentCount; ++i) + m_objectMap->convertJSValueToNPVariant(exec, exec->argument(i), arguments[i]); + + // Calling NPClass::invoke will call into plug-in code, and there's no telling what the plug-in can do. + // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until + // the call has finished. + NPRuntimeObjectMap::PluginProtector protector(m_objectMap); + + bool returnValue; + NPVariant result; + VOID_TO_NPVARIANT(result); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + returnValue = m_npObject->_class->invoke(m_npObject, methodName, arguments.data(), argumentCount, &result); + NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); + } + + // Release all arguments; + for (size_t i = 0; i < argumentCount; ++i) + releaseNPVariantValue(&arguments[i]); + + if (!returnValue) + throwError(exec, createError(exec, "Error calling method on NPObject.")); + + JSValue propertyValue = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result); + releaseNPVariantValue(&result); + return propertyValue; +} + +JSC::JSValue JSNPObject::callObject(JSC::ExecState* exec) +{ + ASSERT_GC_OBJECT_INHERITS(this, &s_info); + if (!m_npObject) + return throwInvalidAccessError(exec); + + size_t argumentCount = exec->argumentCount(); + Vector<NPVariant, 8> arguments(argumentCount); + + // Convert all arguments to NPVariants. + for (size_t i = 0; i < argumentCount; ++i) + m_objectMap->convertJSValueToNPVariant(exec, exec->argument(i), arguments[i]); + + // Calling NPClass::invokeDefault will call into plug-in code, and there's no telling what the plug-in can do. + // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until + // the call has finished. + NPRuntimeObjectMap::PluginProtector protector(m_objectMap); + + bool returnValue; + NPVariant result; + VOID_TO_NPVARIANT(result); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + returnValue = m_npObject->_class->invokeDefault(m_npObject, arguments.data(), argumentCount, &result); + NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); + } + + // Release all arguments; + for (size_t i = 0; i < argumentCount; ++i) + releaseNPVariantValue(&arguments[i]); + + if (!returnValue) + throwError(exec, createError(exec, "Error calling method on NPObject.")); + + JSValue propertyValue = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result); + releaseNPVariantValue(&result); + return propertyValue; +} + +JSValue JSNPObject::callConstructor(ExecState* exec) +{ + ASSERT_GC_OBJECT_INHERITS(this, &s_info); + if (!m_npObject) + return throwInvalidAccessError(exec); + + size_t argumentCount = exec->argumentCount(); + Vector<NPVariant, 8> arguments(argumentCount); + + // Convert all arguments to NPVariants. + for (size_t i = 0; i < argumentCount; ++i) + m_objectMap->convertJSValueToNPVariant(exec, exec->argument(i), arguments[i]); + + // Calling NPClass::construct will call into plug-in code, and there's no telling what the plug-in can do. + // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until + // the call has finished. + NPRuntimeObjectMap::PluginProtector protector(m_objectMap); + + bool returnValue; + NPVariant result; + VOID_TO_NPVARIANT(result); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + returnValue = m_npObject->_class->construct(m_npObject, arguments.data(), argumentCount, &result); + NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); + } + + if (!returnValue) + throwError(exec, createError(exec, "Error calling method on NPObject.")); + + JSValue value = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result); + releaseNPVariantValue(&result); + return value; +} + +static EncodedJSValue JSC_HOST_CALL callNPJSObject(ExecState* exec) +{ + JSObject* object = exec->callee(); + ASSERT(object->inherits(&JSNPObject::s_info)); + + return JSValue::encode(static_cast<JSNPObject*>(object)->callObject(exec)); +} + +JSC::CallType JSNPObject::getCallData(JSC::JSCell* cell, JSC::CallData& callData) +{ + JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + if (!thisObject->m_npObject || !thisObject->m_npObject->_class->invokeDefault) + return CallTypeNone; + + callData.native.function = callNPJSObject; + return CallTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL constructWithConstructor(ExecState* exec) +{ + JSObject* constructor = exec->callee(); + ASSERT(constructor->inherits(&JSNPObject::s_info)); + + return JSValue::encode(static_cast<JSNPObject*>(constructor)->callConstructor(exec)); +} + +ConstructType JSNPObject::getConstructData(JSCell* cell, ConstructData& constructData) +{ + JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + if (!thisObject->m_npObject || !thisObject->m_npObject->_class->construct) + return ConstructTypeNone; + + constructData.native.function = constructWithConstructor; + return ConstructTypeHost; +} + +bool JSNPObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + if (!thisObject->m_npObject) { + throwInvalidAccessError(exec); + return false; + } + + NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); + + // First, check if the NPObject has a property with this name. + if (thisObject->m_npObject->_class->hasProperty && thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) { + slot.setCustom(thisObject, thisObject->propertyGetter); + return true; + } + + // Second, check if the NPObject has a method with this name. + if (thisObject->m_npObject->_class->hasMethod && thisObject->m_npObject->_class->hasMethod(thisObject->m_npObject, npIdentifier)) { + slot.setCustom(thisObject, thisObject->methodGetter); + return true; + } + + return false; +} + +bool JSNPObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + JSNPObject* thisObject = jsCast<JSNPObject*>(object); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + if (!thisObject->m_npObject) { + throwInvalidAccessError(exec); + return false; + } + + NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); + + // First, check if the NPObject has a property with this name. + if (thisObject->m_npObject->_class->hasProperty && thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) { + PropertySlot slot; + slot.setCustom(thisObject, propertyGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete); + return true; + } + + // Second, check if the NPObject has a method with this name. + if (thisObject->m_npObject->_class->hasMethod && thisObject->m_npObject->_class->hasMethod(thisObject->m_npObject, npIdentifier)) { + PropertySlot slot; + slot.setCustom(thisObject, methodGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly); + return true; + } + + return false; +} + +void JSNPObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot&) +{ + JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + if (!thisObject->m_npObject) { + throwInvalidAccessError(exec); + return; + } + + NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); + + if (!thisObject->m_npObject->_class->hasProperty || !thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) { + // FIXME: Should we throw an exception here? + return; + } + + if (!thisObject->m_npObject->_class->setProperty) + return; + + NPVariant variant; + thisObject->m_objectMap->convertJSValueToNPVariant(exec, value, variant); + + // Calling NPClass::setProperty will call into plug-in code, and there's no telling what the plug-in can do. + // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until + // the call has finished. + NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + thisObject->m_npObject->_class->setProperty(thisObject->m_npObject, npIdentifier, &variant); + + NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); + + // FIXME: Should we throw an exception if setProperty returns false? + } + + releaseNPVariantValue(&variant); +} + +bool JSNPObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + return jsCast<JSNPObject*>(cell)->deleteProperty(exec, npIdentifierFromIdentifier(propertyName)); +} + +bool JSNPObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) +{ + return jsCast<JSNPObject*>(cell)->deleteProperty(exec, static_cast<NPIdentifier>(IdentifierRep::get(propertyName))); +} + +bool JSNPObject::deleteProperty(ExecState* exec, NPIdentifier propertyName) +{ + ASSERT_GC_OBJECT_INHERITS(this, &s_info); + if (!m_npObject) { + throwInvalidAccessError(exec); + return false; + } + + if (!m_npObject->_class->removeProperty) { + // FIXME: Should we throw an exception here? + return false; + } + + // Calling NPClass::setProperty will call into plug-in code, and there's no telling what the plug-in can do. + // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until + // the call has finished. + NPRuntimeObjectMap::PluginProtector protector(m_objectMap); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + + // FIXME: Should we throw an exception if removeProperty returns false? + if (!m_npObject->_class->removeProperty(m_npObject, propertyName)) + return false; + + NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); + } + + return true; +} + +void JSNPObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNameArray, EnumerationMode mode) +{ + JSNPObject* thisObject = jsCast<JSNPObject*>(object); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + if (!thisObject->m_npObject) { + throwInvalidAccessError(exec); + return; + } + + if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(thisObject->m_npObject->_class) || !thisObject->m_npObject->_class->enumerate) + return; + + NPIdentifier* identifiers = 0; + uint32_t identifierCount = 0; + + // Calling NPClass::enumerate will call into plug-in code, and there's no telling what the plug-in can do. + // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until + // the call has finished. + NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap); + + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + + // FIXME: Should we throw an exception if enumerate returns false? + if (!thisObject->m_npObject->_class->enumerate(thisObject->m_npObject, &identifiers, &identifierCount)) + return; + + NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); + } + + for (uint32_t i = 0; i < identifierCount; ++i) { + IdentifierRep* identifierRep = static_cast<IdentifierRep*>(identifiers[i]); + + Identifier identifier; + if (identifierRep->isString()) { + const char* string = identifierRep->string(); + int length = strlen(string); + + identifier = Identifier(exec, String::fromUTF8WithLatin1Fallback(string, length).impl()); + } else + identifier = Identifier::from(exec, identifierRep->number()); + + propertyNameArray.add(identifier); + } + + npnMemFree(identifiers); +} + +JSValue JSNPObject::propertyGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName) +{ + JSNPObject* thisObj = static_cast<JSNPObject*>(asObject(slotBase)); + ASSERT_GC_OBJECT_INHERITS(thisObj, &s_info); + + if (!thisObj->m_npObject) + return throwInvalidAccessError(exec); + + if (!thisObj->m_npObject->_class->getProperty) + return jsUndefined(); + + NPVariant result; + VOID_TO_NPVARIANT(result); + + // Calling NPClass::getProperty will call into plug-in code, and there's no telling what the plug-in can do. + // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until + // the call has finished. + NPRuntimeObjectMap::PluginProtector protector(thisObj->m_objectMap); + + bool returnValue; + { + JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); + NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); + returnValue = thisObj->m_npObject->_class->getProperty(thisObj->m_npObject, npIdentifier, &result); + + NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); + } + + if (!returnValue) + return jsUndefined(); + + JSValue propertyValue = thisObj->m_objectMap->convertNPVariantToJSValue(exec, thisObj->globalObject(), result); + releaseNPVariantValue(&result); + return propertyValue; +} + +JSValue JSNPObject::methodGetter(ExecState* exec, JSValue slotBase, const Identifier& methodName) +{ + JSNPObject* thisObj = static_cast<JSNPObject*>(asObject(slotBase)); + ASSERT_GC_OBJECT_INHERITS(thisObj, &s_info); + + if (!thisObj->m_npObject) + return throwInvalidAccessError(exec); + + NPIdentifier npIdentifier = npIdentifierFromIdentifier(methodName); + return JSNPMethod::create(exec, thisObj->globalObject(), methodName, npIdentifier); +} + +JSObject* JSNPObject::throwInvalidAccessError(ExecState* exec) +{ + return throwError(exec, createReferenceError(exec, "Trying to access object from destroyed plug-in.")); +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPObject.h b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPObject.h new file mode 100644 index 000000000..7f26f0d67 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/JSNPObject.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef JSNPObject_h +#define JSNPObject_h + +#include <JavaScriptCore/JSGlobalObject.h> +#include <JavaScriptCore/JSObject.h> +#include <JavaScriptCore/ObjectPrototype.h> + +typedef void* NPIdentifier; +struct NPObject; + +namespace WebKit { + +class NPRuntimeObjectMap; + +// JSNPObject is a JSObject that wraps an NPObject. + +class JSNPObject : public JSC::JSNonFinalObject { +public: + typedef JSC::JSNonFinalObject Base; + + static JSNPObject* create(JSC::JSGlobalObject* globalObject, NPRuntimeObjectMap* objectMap, NPObject* npObject) + { + JSC::Structure* structure = createStructure(globalObject->globalData(), globalObject, globalObject->objectPrototype()); + JSNPObject* object = new (JSC::allocateCell<JSNPObject>(globalObject->globalData().heap)) JSNPObject(globalObject, structure, objectMap, npObject); + object->finishCreation(globalObject); + return object; + } + + ~JSNPObject(); + static void destroy(JSCell*); + + void invalidate(); + + // Used to invalidate an NPObject asynchronously. + NPObject* leakNPObject(); + + JSC::JSValue callMethod(JSC::ExecState*, NPIdentifier methodName); + JSC::JSValue callObject(JSC::ExecState*); + JSC::JSValue callConstructor(JSC::ExecState*); + + static const JSC::ClassInfo s_info; + + NPObject* npObject() const { return m_npObject; } + +protected: + void finishCreation(JSC::JSGlobalObject*); + +private: + JSNPObject(JSC::JSGlobalObject*, JSC::Structure*, NPRuntimeObjectMap*, NPObject*); + + static const unsigned StructureFlags = JSC::OverridesGetOwnPropertySlot | JSC::OverridesGetPropertyNames | JSObject::StructureFlags; + + static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(globalData, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), &s_info); + } + + static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&); + static JSC::ConstructType getConstructData(JSC::JSCell*, JSC::ConstructData&); + + static bool getOwnPropertySlot(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&); + static bool getOwnPropertyDescriptor(JSC::JSObject*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor&); + static void put(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, JSC::PutPropertySlot&); + + static bool deleteProperty(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName); + static bool deletePropertyByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName); + + bool deleteProperty(JSC::ExecState*, NPIdentifier propertyName); + + static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode); + + static JSC::JSValue propertyGetter(JSC::ExecState*, JSC::JSValue, const JSC::Identifier&); + static JSC::JSValue methodGetter(JSC::ExecState*, JSC::JSValue, const JSC::Identifier&); + static JSC::JSObject* throwInvalidAccessError(JSC::ExecState*); + + NPRuntimeObjectMap* m_objectMap; + NPObject* m_npObject; +}; + +} // namespace WebKit + +#endif // JSNPObject_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NPJSObject.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/NPJSObject.cpp new file mode 100644 index 000000000..f0ed9f74c --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NPJSObject.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "NPJSObject.h" + +#include "JSNPObject.h" +#include "NPRuntimeObjectMap.h" +#include "NPRuntimeUtilities.h" +#include <JavaScriptCore/JSLock.h> +#include <JavaScriptCore/JSObject.h> +#include <JavaScriptCore/StrongInlines.h> +#include <WebCore/Frame.h> +#include <WebCore/IdentifierRep.h> +#include <wtf/text/WTFString.h> + +using namespace JSC; +using namespace WebCore; + +namespace WebKit { + +NPJSObject* NPJSObject::create(JSGlobalData& globalData, NPRuntimeObjectMap* objectMap, JSObject* jsObject) +{ + // We should never have a JSNPObject inside an NPJSObject. + ASSERT(!jsObject->inherits(&JSNPObject::s_info)); + + NPJSObject* npJSObject = toNPJSObject(createNPObject(0, npClass())); + npJSObject->initialize(globalData, objectMap, jsObject); + + return npJSObject; +} + +NPJSObject::NPJSObject() + : m_objectMap(0) +{ +} + +NPJSObject::~NPJSObject() +{ + m_objectMap->npJSObjectDestroyed(this); +} + +bool NPJSObject::isNPJSObject(NPObject* npObject) +{ + return npObject->_class == npClass(); +} + +void NPJSObject::initialize(JSGlobalData& globalData, NPRuntimeObjectMap* objectMap, JSObject* jsObject) +{ + ASSERT(!m_objectMap); + ASSERT(!m_jsObject); + + m_objectMap = objectMap; + m_jsObject.set(globalData, jsObject); +} + +static Identifier identifierFromIdentifierRep(ExecState* exec, IdentifierRep* identifierRep) +{ + ASSERT(identifierRep->isString()); + + const char* string = identifierRep->string(); + int length = strlen(string); + + return Identifier(exec, String::fromUTF8WithLatin1Fallback(string, length).impl()); +} + +bool NPJSObject::hasMethod(NPIdentifier methodName) +{ + IdentifierRep* identifierRep = static_cast<IdentifierRep*>(methodName); + + if (!identifierRep->isString()) + return false; + + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + + JSValue value = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep)); + exec->clearException(); + + CallData callData; + return getCallData(value, callData) != CallTypeNone; +} + +bool NPJSObject::invoke(NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + IdentifierRep* identifierRep = static_cast<IdentifierRep*>(methodName); + + if (!identifierRep->isString()) + return false; + + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + + JSValue function = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep)); + return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result); +} + +bool NPJSObject::invokeDefault(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + + JSValue function = m_jsObject.get(); + return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result); +} + +bool NPJSObject::hasProperty(NPIdentifier identifier) +{ + IdentifierRep* identifierRep = static_cast<IdentifierRep*>(identifier); + + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + + bool result; + if (identifierRep->isString()) + result = m_jsObject->hasProperty(exec, identifierFromIdentifierRep(exec, identifierRep)); + else + result = m_jsObject->hasProperty(exec, identifierRep->number()); + + exec->clearException(); + return result; +} + +bool NPJSObject::getProperty(NPIdentifier propertyName, NPVariant* result) +{ + IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName); + + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + JSValue jsResult; + if (identifierRep->isString()) + jsResult = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep)); + else + jsResult = m_jsObject->get(exec, identifierRep->number()); + + m_objectMap->convertJSValueToNPVariant(exec, jsResult, *result); + exec->clearException(); + return true; +} + +bool NPJSObject::setProperty(NPIdentifier propertyName, const NPVariant* value) +{ + IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName); + + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + + JSValue jsValue = m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), *value); + if (identifierRep->isString()) { + PutPropertySlot slot; + m_jsObject->methodTable()->put(m_jsObject.get(), exec, identifierFromIdentifierRep(exec, identifierRep), jsValue, slot); + } else + m_jsObject->methodTable()->putByIndex(m_jsObject.get(), exec, identifierRep->number(), jsValue); + exec->clearException(); + + return true; +} + +bool NPJSObject::removeProperty(NPIdentifier propertyName) +{ + IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName); + + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + if (identifierRep->isString()) { + Identifier identifier = identifierFromIdentifierRep(exec, identifierRep); + + if (!m_jsObject->hasProperty(exec, identifier)) { + exec->clearException(); + return false; + } + + m_jsObject->methodTable()->deleteProperty(m_jsObject.get(), exec, identifier); + } else { + if (!m_jsObject->hasProperty(exec, identifierRep->number())) { + exec->clearException(); + return false; + } + + m_jsObject->methodTable()->deletePropertyByIndex(m_jsObject.get(), exec, identifierRep->number()); + } + + exec->clearException(); + return true; +} + +bool NPJSObject::enumerate(NPIdentifier** identifiers, uint32_t* identifierCount) +{ + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + + PropertyNameArray propertyNames(exec); + m_jsObject->methodTable()->getPropertyNames(m_jsObject.get(), exec, propertyNames, ExcludeDontEnumProperties); + + NPIdentifier* nameIdentifiers = npnMemNewArray<NPIdentifier>(propertyNames.size()); + + for (size_t i = 0; i < propertyNames.size(); ++i) + nameIdentifiers[i] = static_cast<NPIdentifier>(IdentifierRep::get(propertyNames[i].ustring().utf8().data())); + + *identifiers = nameIdentifiers; + *identifierCount = propertyNames.size(); + + return true; +} + +bool NPJSObject::construct(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + ExecState* exec = m_objectMap->globalExec(); + if (!exec) + return false; + + JSLock lock(SilenceAssertionsOnly); + + ConstructData constructData; + ConstructType constructType = getConstructData(m_jsObject.get(), constructData); + if (constructType == ConstructTypeNone) + return false; + + // Convert the passed in arguments. + MarkedArgumentBuffer argumentList; + for (uint32_t i = 0; i < argumentCount; ++i) + argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), arguments[i])); + + exec->globalData().timeoutChecker.start(); + JSValue value = JSC::construct(exec, m_jsObject.get(), constructType, constructData, argumentList); + exec->globalData().timeoutChecker.stop(); + + // Convert and return the new object. + m_objectMap->convertJSValueToNPVariant(exec, value, *result); + exec->clearException(); + + return true; +} + +bool NPJSObject::invoke(ExecState* exec, JSGlobalObject* globalObject, JSValue function, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return false; + + // Convert the passed in arguments. + MarkedArgumentBuffer argumentList; + for (uint32_t i = 0; i < argumentCount; ++i) + argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, globalObject, arguments[i])); + + exec->globalData().timeoutChecker.start(); + JSValue value = JSC::call(exec, function, callType, callData, m_jsObject->methodTable()->toThisObject(m_jsObject.get(), exec), argumentList); + exec->globalData().timeoutChecker.stop(); + + // Convert and return the result of the function call. + m_objectMap->convertJSValueToNPVariant(exec, value, *result); + exec->clearException(); + + return true; +} + +NPClass* NPJSObject::npClass() +{ + static NPClass npClass = { + NP_CLASS_STRUCT_VERSION, + NP_Allocate, + NP_Deallocate, + 0, + NP_HasMethod, + NP_Invoke, + NP_InvokeDefault, + NP_HasProperty, + NP_GetProperty, + NP_SetProperty, + NP_RemoveProperty, + NP_Enumerate, + NP_Construct + }; + + return &npClass; +} + +NPObject* NPJSObject::NP_Allocate(NPP npp, NPClass*) +{ + ASSERT_UNUSED(npp, !npp); + + return new NPJSObject; +} + +void NPJSObject::NP_Deallocate(NPObject* npObject) +{ + NPJSObject* npJSObject = toNPJSObject(npObject); + delete npJSObject; +} + +bool NPJSObject::NP_HasMethod(NPObject* npObject, NPIdentifier methodName) +{ + return toNPJSObject(npObject)->hasMethod(methodName); +} + +bool NPJSObject::NP_Invoke(NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + return toNPJSObject(npObject)->invoke(methodName, arguments, argumentCount, result); +} + +bool NPJSObject::NP_InvokeDefault(NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + return toNPJSObject(npObject)->invokeDefault(arguments, argumentCount, result); +} + +bool NPJSObject::NP_HasProperty(NPObject* npObject, NPIdentifier propertyName) +{ + return toNPJSObject(npObject)->hasProperty(propertyName); +} + +bool NPJSObject::NP_GetProperty(NPObject* npObject, NPIdentifier propertyName, NPVariant* result) +{ + return toNPJSObject(npObject)->getProperty(propertyName, result); +} + +bool NPJSObject::NP_SetProperty(NPObject* npObject, NPIdentifier propertyName, const NPVariant* value) +{ + return toNPJSObject(npObject)->setProperty(propertyName, value); +} + +bool NPJSObject::NP_RemoveProperty(NPObject* npObject, NPIdentifier propertyName) +{ + return toNPJSObject(npObject)->removeProperty(propertyName); +} + +bool NPJSObject::NP_Enumerate(NPObject* npObject, NPIdentifier** identifiers, uint32_t* identifierCount) +{ + return toNPJSObject(npObject)->enumerate(identifiers, identifierCount); +} + +bool NPJSObject::NP_Construct(NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + return toNPJSObject(npObject)->construct(arguments, argumentCount, result); +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NPJSObject.h b/Source/WebKit2/WebProcess/Plugins/Netscape/NPJSObject.h new file mode 100644 index 000000000..f0ee07a5c --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NPJSObject.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef NPJSObject_h +#define NPJSObject_h + +#include <JavaScriptCore/Strong.h> +#include <WebCore/npruntime_internal.h> +#include <wtf/Noncopyable.h> + +namespace JSC { + +class JSGlobalData; +class JSGlobalObject; +class JSObject; + +} + +namespace WebKit { + +class NPRuntimeObjectMap; + +// NPJSObject is an NPObject that wraps a JSObject. +class NPJSObject : public NPObject { + WTF_MAKE_NONCOPYABLE(NPJSObject); +public: + static NPJSObject* create(JSC::JSGlobalData&, NPRuntimeObjectMap*, JSC::JSObject*); + + JSC::JSObject* jsObject() const { return m_jsObject.get(); } + + static bool isNPJSObject(NPObject*); + + static NPJSObject* toNPJSObject(NPObject* npObject) + { + ASSERT(isNPJSObject(npObject)); + return static_cast<NPJSObject*>(npObject); + } + +private: + NPJSObject(); + ~NPJSObject(); + + void initialize(JSC::JSGlobalData&, NPRuntimeObjectMap*, JSC::JSObject*); + + bool hasMethod(NPIdentifier methodName); + bool invoke(NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result); + bool invokeDefault(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result); + bool hasProperty(NPIdentifier propertyName); + bool getProperty(NPIdentifier propertyName, NPVariant* result); + bool setProperty(NPIdentifier propertyName, const NPVariant* value); + bool removeProperty(NPIdentifier propertyName); + bool enumerate(NPIdentifier** identifiers, uint32_t* identifierCount); + bool construct(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result); + + bool invoke(JSC::ExecState*, JSC::JSGlobalObject*, JSC::JSValue function, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result); + + static NPClass* npClass(); + static NPObject* NP_Allocate(NPP, NPClass*); + static void NP_Deallocate(NPObject*); + static bool NP_HasMethod(NPObject*, NPIdentifier methodName); + static bool NP_Invoke(NPObject*, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result); + static bool NP_InvokeDefault(NPObject*, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result); + static bool NP_HasProperty(NPObject*, NPIdentifier propertyName); + static bool NP_GetProperty(NPObject*, NPIdentifier propertyName, NPVariant* result); + static bool NP_SetProperty(NPObject*, NPIdentifier propertyName, const NPVariant* value); + static bool NP_RemoveProperty(NPObject*, NPIdentifier propertyName); + static bool NP_Enumerate(NPObject*, NPIdentifier** identifiers, uint32_t* identifierCount); + static bool NP_Construct(NPObject*, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result); + + NPRuntimeObjectMap* m_objectMap; + JSC::Strong<JSC::JSObject> m_jsObject; +}; + +} // namespace WebKit + +#endif // NPJSObject_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.cpp new file mode 100644 index 000000000..5aadd13f4 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "NPRuntimeObjectMap.h" + +#include "JSNPObject.h" +#include "NPJSObject.h" +#include "NPRuntimeUtilities.h" +#include "PluginView.h" +#include "WebProcess.h" +#include <JavaScriptCore/Completion.h> +#include <JavaScriptCore/Error.h> +#include <JavaScriptCore/JSLock.h> +#include <JavaScriptCore/SourceCode.h> +#include <JavaScriptCore/Strong.h> +#include <JavaScriptCore/StrongInlines.h> +#include <WebCore/Frame.h> + +using namespace JSC; +using namespace WebCore; + +namespace WebKit { + + +NPRuntimeObjectMap::NPRuntimeObjectMap(PluginView* pluginView) + : m_pluginView(pluginView) + , m_finalizationTimer(WebProcess::shared().runLoop(), this, &NPRuntimeObjectMap::invalidateQueuedObjects) +{ +} + +NPRuntimeObjectMap::PluginProtector::PluginProtector(NPRuntimeObjectMap* npRuntimeObjectMap) +{ + // If we're already in the plug-in view destructor, we shouldn't try to keep it alive. + if (!npRuntimeObjectMap->m_pluginView->isBeingDestroyed()) + m_pluginView = npRuntimeObjectMap->m_pluginView; +} + +NPRuntimeObjectMap::PluginProtector::~PluginProtector() +{ +} + +NPObject* NPRuntimeObjectMap::getOrCreateNPObject(JSGlobalData& globalData, JSObject* jsObject) +{ + // If this is a JSNPObject, we can just get its underlying NPObject. + if (jsObject->classInfo() == &JSNPObject::s_info) { + JSNPObject* jsNPObject = static_cast<JSNPObject*>(jsObject); + NPObject* npObject = jsNPObject->npObject(); + + retainNPObject(npObject); + return npObject; + } + + // First, check if we already know about this object. + if (NPJSObject* npJSObject = m_npJSObjects.get(jsObject)) { + retainNPObject(npJSObject); + return npJSObject; + } + + NPJSObject* npJSObject = NPJSObject::create(globalData, this, jsObject); + m_npJSObjects.set(jsObject, npJSObject); + + return npJSObject; +} + +void NPRuntimeObjectMap::npJSObjectDestroyed(NPJSObject* npJSObject) +{ + // Remove the object from the map. + ASSERT(m_npJSObjects.contains(npJSObject->jsObject())); + m_npJSObjects.remove(npJSObject->jsObject()); +} + +JSObject* NPRuntimeObjectMap::getOrCreateJSObject(JSGlobalObject* globalObject, NPObject* npObject) +{ + // If this is an NPJSObject, we can just get the JSObject that it's wrapping. + if (NPJSObject::isNPJSObject(npObject)) + return NPJSObject::toNPJSObject(npObject)->jsObject(); + + if (JSC::Weak<JSNPObject> jsNPObject = m_jsNPObjects.get(npObject)) + return jsNPObject.get(); + + JSNPObject* jsNPObject = JSNPObject::create(globalObject, this, npObject); + m_jsNPObjects.set(npObject, JSC::Weak<JSNPObject>(globalObject->globalData(), jsNPObject, this, npObject)); + + return jsNPObject; +} + +JSValue NPRuntimeObjectMap::convertNPVariantToJSValue(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, const NPVariant& variant) +{ + switch (variant.type) { + case NPVariantType_Void: + return jsUndefined(); + + case NPVariantType_Null: + return jsNull(); + + case NPVariantType_Bool: + return jsBoolean(variant.value.boolValue); + + case NPVariantType_Int32: + return jsNumber(variant.value.intValue); + + case NPVariantType_Double: + return jsNumber(variant.value.doubleValue); + + case NPVariantType_String: + return jsString(exec, String::fromUTF8WithLatin1Fallback(variant.value.stringValue.UTF8Characters, + variant.value.stringValue.UTF8Length)); + case NPVariantType_Object: + return getOrCreateJSObject(globalObject, variant.value.objectValue); + } + + ASSERT_NOT_REACHED(); + return jsUndefined(); +} + +void NPRuntimeObjectMap::convertJSValueToNPVariant(ExecState* exec, JSValue value, NPVariant& variant) +{ + JSLock lock(SilenceAssertionsOnly); + + VOID_TO_NPVARIANT(variant); + + if (value.isNull()) { + NULL_TO_NPVARIANT(variant); + return; + } + + if (value.isUndefined()) { + VOID_TO_NPVARIANT(variant); + return; + } + + if (value.isBoolean()) { + BOOLEAN_TO_NPVARIANT(value.toBoolean(exec), variant); + return; + } + + if (value.isNumber()) { + DOUBLE_TO_NPVARIANT(value.toNumber(exec), variant); + return; + } + + if (value.isString()) { + NPString npString = createNPString(value.toString(exec).utf8()); + STRINGN_TO_NPVARIANT(npString.UTF8Characters, npString.UTF8Length, variant); + return; + } + + if (value.isObject()) { + NPObject* npObject = getOrCreateNPObject(exec->globalData(), asObject(value)); + OBJECT_TO_NPVARIANT(npObject, variant); + return; + } + + ASSERT_NOT_REACHED(); +} + +bool NPRuntimeObjectMap::evaluate(NPObject* npObject, const String&scriptString, NPVariant* result) +{ + Strong<JSGlobalObject> globalObject(this->globalObject()->globalData(), this->globalObject()); + if (!globalObject) + return false; + + ExecState* exec = globalObject->globalExec(); + + JSLock lock(SilenceAssertionsOnly); + JSValue thisValue = getOrCreateJSObject(globalObject.get(), npObject); + + globalObject->globalData().timeoutChecker.start(); + JSValue resultValue = JSC::evaluate(exec, globalObject->globalScopeChain(), makeSource(UString(scriptString.impl())), thisValue); + globalObject->globalData().timeoutChecker.stop(); + + convertJSValueToNPVariant(exec, resultValue, *result); + return true; +} + +void NPRuntimeObjectMap::invalidate() +{ + Vector<NPJSObject*> npJSObjects; + copyValuesToVector(m_npJSObjects, npJSObjects); + + // Deallocate all the object wrappers so we won't leak any JavaScript objects. + for (size_t i = 0; i < npJSObjects.size(); ++i) + deallocateNPObject(npJSObjects[i]); + + // We shouldn't have any NPJSObjects left now. + ASSERT(m_npJSObjects.isEmpty()); + + Vector<NPObject*> objects; + + for (HashMap<NPObject*, JSC::Weak<JSNPObject> >::iterator ptr = m_jsNPObjects.begin(), end = m_jsNPObjects.end(); ptr != end; ++ptr) + objects.append(ptr->second->leakNPObject()); + + m_jsNPObjects.clear(); + + for (size_t i = 0; i < objects.size(); ++i) + releaseNPObject(objects[i]); + + // Deal with any objects that were scheduled for delayed destruction + if (m_npObjectsToFinalize.isEmpty()) + return; + ASSERT(m_finalizationTimer.isActive()); + m_finalizationTimer.stop(); + invalidateQueuedObjects(); +} + +JSGlobalObject* NPRuntimeObjectMap::globalObject() const +{ + Frame* frame = m_pluginView->frame(); + if (!frame) + return 0; + + return frame->script()->globalObject(pluginWorld()); +} + +ExecState* NPRuntimeObjectMap::globalExec() const +{ + JSGlobalObject* globalObject = this->globalObject(); + if (!globalObject) + return 0; + + return globalObject->globalExec(); +} + +static String& globalExceptionString() +{ + DEFINE_STATIC_LOCAL(String, exceptionString, ()); + return exceptionString; +} + +void NPRuntimeObjectMap::setGlobalException(const String& exceptionString) +{ + globalExceptionString() = exceptionString; +} + +void NPRuntimeObjectMap::moveGlobalExceptionToExecState(ExecState* exec) +{ + if (globalExceptionString().isNull()) + return; + + { + JSLock lock(SilenceAssertionsOnly); + throwError(exec, createError(exec, stringToUString(globalExceptionString()))); + } + + globalExceptionString() = String(); +} + +void NPRuntimeObjectMap::invalidateQueuedObjects() +{ + ASSERT(m_npObjectsToFinalize.size()); + // We deliberately re-request m_npObjectsToFinalize.size() as custom dealloc + // functions may execute JS and so get more objects added to the dealloc queue + for (size_t i = 0; i < m_npObjectsToFinalize.size(); ++i) + deallocateNPObject(m_npObjectsToFinalize[i]); + m_npObjectsToFinalize.clear(); +} + +void NPRuntimeObjectMap::addToInvalidationQueue(NPObject* npObject) +{ + if (trySafeReleaseNPObject(npObject)) + return; + if (m_npObjectsToFinalize.isEmpty()) + m_finalizationTimer.startOneShot(0); + ASSERT(m_finalizationTimer.isActive()); + m_npObjectsToFinalize.append(npObject); +} + +void NPRuntimeObjectMap::finalize(JSC::Handle<JSC::Unknown> handle, void* context) +{ + HashMap<NPObject*, JSC::Weak<JSNPObject> >::iterator found = m_jsNPObjects.find(static_cast<NPObject*>(context)); + ASSERT(found != m_jsNPObjects.end()); + ASSERT_UNUSED(handle, asObject(handle.get()) == found->second); + JSNPObject* object = found->second.get(); + m_jsNPObjects.remove(found); + addToInvalidationQueue(object->leakNPObject()); +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.h b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.h new file mode 100644 index 000000000..bef701bee --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeObjectMap.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef NPJSObjectWrapperMap_h +#define NPJSObjectWrapperMap_h + + +#include "RunLoop.h" + +#include <heap/Weak.h> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> + +struct NPObject; +typedef struct _NPVariant NPVariant; + +namespace JSC { + class ExecState; + class JSGlobalData; + class JSGlobalObject; + class JSObject; + class JSValue; +} + +namespace WebKit { + +class JSNPObject; +class NPJSObject; +class PluginView; + +// A per plug-in map of NPObjects that wrap JavaScript objects. +class NPRuntimeObjectMap : private JSC::WeakHandleOwner { +public: + explicit NPRuntimeObjectMap(PluginView*); + + class PluginProtector { + public: + explicit PluginProtector(NPRuntimeObjectMap* npRuntimeObjectMap); + ~PluginProtector(); + + private: + RefPtr<PluginView> m_pluginView; + }; + + // Returns an NPObject that wraps the given JSObject object. If there is already an NPObject that wraps this JSObject, it will + // retain it and return it. + NPObject* getOrCreateNPObject(JSC::JSGlobalData&, JSC::JSObject*); + void npJSObjectDestroyed(NPJSObject*); + + // Returns a JSObject object that wraps the given NPObject. + JSC::JSObject* getOrCreateJSObject(JSC::JSGlobalObject*, NPObject*); + void jsNPObjectDestroyed(JSNPObject*); + + void convertJSValueToNPVariant(JSC::ExecState*, JSC::JSValue, NPVariant&); + JSC::JSValue convertNPVariantToJSValue(JSC::ExecState*, JSC::JSGlobalObject*, const NPVariant&); + + bool evaluate(NPObject*, const String& scriptString, NPVariant* result); + + // Called when the plug-in is destroyed. Will invalidate all the NPObjects. + void invalidate(); + + JSC::JSGlobalObject* globalObject() const; + JSC::ExecState* globalExec() const; + + static void setGlobalException(const String& exceptionString); + static void moveGlobalExceptionToExecState(JSC::ExecState*); + +private: + // WeakHandleOwner + virtual void finalize(JSC::Handle<JSC::Unknown>, void* context); + void addToInvalidationQueue(NPObject*); + void invalidateQueuedObjects(); + + PluginView* m_pluginView; + + HashMap<JSC::JSObject*, NPJSObject*> m_npJSObjects; + HashMap<NPObject*, JSC::Weak<JSNPObject> > m_jsNPObjects; + Vector<NPObject*> m_npObjectsToFinalize; + RunLoop::Timer<NPRuntimeObjectMap> m_finalizationTimer; +}; + +} // namespace WebKit + +#endif // NPJSObjectWrapperMap_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeUtilities.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeUtilities.cpp new file mode 100644 index 000000000..71bf90b4d --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeUtilities.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "NPRuntimeUtilities.h" + +#include <wtf/text/CString.h> + +namespace WebKit { + +void* npnMemAlloc(uint32_t size) +{ + // We could use fastMalloc here, but there might be plug-ins that mix NPN_MemAlloc/NPN_MemFree with malloc and free, + // so having them be equivalent seems like a good idea. + return malloc(size); +} + +void npnMemFree(void* ptr) +{ + // We could use fastFree here, but there might be plug-ins that mix NPN_MemAlloc/NPN_MemFree with malloc and free, + // so having them be equivalent seems like a good idea. + free(ptr); +} + +NPString createNPString(const CString& string) +{ + char* utf8Characters = npnMemNewArray<char>(string.length()); + memcpy(utf8Characters, string.data(), string.length()); + + NPString npString; + npString.UTF8Characters = utf8Characters; + npString.UTF8Length = string.length(); + + return npString; +} + +NPObject* createNPObject(NPP npp, NPClass* npClass) +{ + ASSERT(npClass); + + NPObject* npObject; + if (npClass->allocate) + npObject = npClass->allocate(npp, npClass); + else + npObject = npnMemNew<NPObject>(); + + npObject->_class = npClass; + npObject->referenceCount = 1; + + return npObject; +} + +void deallocateNPObject(NPObject* npObject) +{ + ASSERT(npObject); + if (!npObject) + return; + + if (npObject->_class->deallocate) + npObject->_class->deallocate(npObject); + else + npnMemFree(npObject); +} + +void retainNPObject(NPObject* npObject) +{ + ASSERT(npObject); + if (!npObject) + return; + + npObject->referenceCount++; +} + +bool trySafeReleaseNPObject(NPObject* npObject) +{ + ASSERT(npObject); + if (!npObject) + return true; + + ASSERT(npObject->referenceCount >= 1); + + npObject->referenceCount--; + if (npObject->referenceCount) + return true; + if (npObject->_class->deallocate) + return false; + deallocateNPObject(npObject); + return true; +} + +void releaseNPObject(NPObject* npObject) +{ + ASSERT(npObject); + if (!npObject) + return; + + ASSERT(npObject->referenceCount >= 1); + npObject->referenceCount--; + if (!npObject->referenceCount) + deallocateNPObject(npObject); +} + +void releaseNPVariantValue(NPVariant* variant) +{ + ASSERT(variant); + + switch (variant->type) { + case NPVariantType_Void: + case NPVariantType_Null: + case NPVariantType_Bool: + case NPVariantType_Int32: + case NPVariantType_Double: + // Nothing to do. + break; + + case NPVariantType_String: + npnMemFree(const_cast<NPUTF8*>(variant->value.stringValue.UTF8Characters)); + variant->value.stringValue.UTF8Characters = 0; + variant->value.stringValue.UTF8Length = 0; + break; + case NPVariantType_Object: + releaseNPObject(variant->value.objectValue); + variant->value.objectValue = 0; + break; + } + + variant->type = NPVariantType_Void; +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeUtilities.h b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeUtilities.h new file mode 100644 index 000000000..2f135b250 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NPRuntimeUtilities.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef NPRuntimeUtilities_h +#define NPRuntimeUtilities_h + +#include <WebCore/npruntime_internal.h> +#include <wtf/Forward.h> + +struct NPClass; +struct NPObject; + +namespace WebKit { + +void* npnMemAlloc(uint32_t); +void npnMemFree(void*); + +template<typename T> T* npnMemNew() +{ + return static_cast<T*>(npnMemAlloc(sizeof(T))); +} + +template<typename T> T* npnMemNewArray(size_t count) +{ + return static_cast<T*>(npnMemAlloc(sizeof(T) * count)); +} + +NPString createNPString(const CString&); + +NPObject* createNPObject(NPP, NPClass*); +void deallocateNPObject(NPObject*); + +void retainNPObject(NPObject*); +void releaseNPObject(NPObject*); + +// This function decrements the refcount of the specified object. If the +// refcount reaches 0 it will attempt to destroy the object. If the object has +// a custom deallocate function it will fail and return false, so it will be +// up to the caller to call deallocateNPObject. +// This function is used to implement the delayed finalization of NPObjects +// released during GC. +bool trySafeReleaseNPObject(NPObject*); + +void releaseNPVariantValue(NPVariant*); + +} + +#endif // NPRuntimeUtilities_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapeBrowserFuncs.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapeBrowserFuncs.cpp new file mode 100644 index 000000000..a8a806e01 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapeBrowserFuncs.cpp @@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "NetscapeBrowserFuncs.h" + +#include "NPRuntimeUtilities.h" +#include "NetscapePlugin.h" +#include "PluginController.h" +#include <WebCore/HTTPHeaderMap.h> +#include <WebCore/IdentifierRep.h> +#include <WebCore/NotImplemented.h> +#include <WebCore/ProtectionSpace.h> +#include <WebCore/SharedBuffer.h> +#include <utility> + +using namespace WebCore; +using namespace std; + +namespace WebKit { + +// Helper class for delaying destruction of a plug-in. +class PluginDestructionProtector { +public: + explicit PluginDestructionProtector(NetscapePlugin* plugin) + : m_protector(static_cast<Plugin*>(plugin)->controller()) + { + } + +private: + PluginController::PluginDestructionProtector m_protector; +}; + +static bool startsWithBlankLine(const char* bytes, unsigned length) +{ + return length > 0 && bytes[0] == '\n'; +} + +static int locationAfterFirstBlankLine(const char* bytes, unsigned length) +{ + for (unsigned i = 0; i < length - 4; i++) { + // Support for Acrobat. It sends "\n\n". + if (bytes[i] == '\n' && bytes[i + 1] == '\n') + return i + 2; + + // Returns the position after 2 CRLF's or 1 CRLF if it is the first line. + if (bytes[i] == '\r' && bytes[i + 1] == '\n') { + i += 2; + if (i == 2) + return i; + + if (bytes[i] == '\n') { + // Support for Director. It sends "\r\n\n" (3880387). + return i + 1; + } + + if (bytes[i] == '\r' && bytes[i + 1] == '\n') { + // Support for Flash. It sends "\r\n\r\n" (3758113). + return i + 2; + } + } + } + + return -1; +} + +static const char* findEndOfLine(const char* bytes, unsigned length) +{ + // According to the HTTP specification EOL is defined as + // a CRLF pair. Unfortunately, some servers will use LF + // instead. Worse yet, some servers will use a combination + // of both (e.g. <header>CRLFLF<body>), so findEOL needs + // to be more forgiving. It will now accept CRLF, LF or + // CR. + // + // It returns 0 if EOLF is not found or it will return + // a pointer to the first terminating character. + for (unsigned i = 0; i < length; i++) { + if (bytes[i] == '\n') + return bytes + i; + if (bytes[i] == '\r') { + // Check to see if spanning buffer bounds + // (CRLF is across reads). If so, wait for + // next read. + if (i + 1 == length) + break; + + return bytes + i; + } + } + + return 0; +} + +static String capitalizeRFC822HeaderFieldName(const String& name) +{ + bool capitalizeCharacter = true; + String result; + + for (unsigned i = 0; i < name.length(); i++) { + UChar c; + + if (capitalizeCharacter && name[i] >= 'a' && name[i] <= 'z') + c = toASCIIUpper(name[i]); + else if (!capitalizeCharacter && name[i] >= 'A' && name[i] <= 'Z') + c = toASCIILower(name[i]); + else + c = name[i]; + + if (name[i] == '-') + capitalizeCharacter = true; + else + capitalizeCharacter = false; + + result.append(c); + } + + return result; +} + +static HTTPHeaderMap parseRFC822HeaderFields(const char* bytes, unsigned length) +{ + String lastHeaderKey; + HTTPHeaderMap headerFields; + + // Loop over lines until we're past the header, or we can't find any more end-of-lines + while (const char* endOfLine = findEndOfLine(bytes, length)) { + const char* line = bytes; + int lineLength = endOfLine - bytes; + + // Move bytes to the character after the terminator as returned by findEndOfLine. + bytes = endOfLine + 1; + if ((*endOfLine == '\r') && (*bytes == '\n')) + bytes++; // Safe since findEndOfLine won't return a spanning CRLF. + + length -= (bytes - line); + if (!lineLength) { + // Blank line; we're at the end of the header + break; + } + + if (*line == ' ' || *line == '\t') { + // Continuation of the previous header + if (lastHeaderKey.isNull()) { + // malformed header; ignore it and continue + continue; + } + + // Merge the continuation of the previous header + String currentValue = headerFields.get(lastHeaderKey); + String newValue(line, lineLength); + + headerFields.set(lastHeaderKey, currentValue + newValue); + } else { + // Brand new header + const char* colon = line; + while (*colon != ':' && colon != endOfLine) + colon++; + + if (colon == endOfLine) { + // malformed header; ignore it and continue + continue; + } + + lastHeaderKey = capitalizeRFC822HeaderFieldName(String(line, colon - line)); + String value; + + for (colon++; colon != endOfLine; colon++) { + if (*colon != ' ' && *colon != '\t') + break; + } + if (colon == endOfLine) + value = ""; + else + value = String(colon, endOfLine - colon); + + String oldValue = headerFields.get(lastHeaderKey); + if (!oldValue.isNull()) { + String tmp = oldValue; + tmp += ", "; + tmp += value; + value = tmp; + } + + headerFields.set(lastHeaderKey, value); + } + } + + return headerFields; +} + +static NPError parsePostBuffer(bool isFile, const char *buffer, uint32_t length, bool parseHeaders, HTTPHeaderMap& headerFields, Vector<uint8_t>& bodyData) +{ + RefPtr<SharedBuffer> fileContents; + const char* postBuffer = 0; + uint32_t postBufferSize = 0; + + if (isFile) { + fileContents = SharedBuffer::createWithContentsOfFile(String::fromUTF8(buffer)); + if (!fileContents) + return NPERR_FILE_NOT_FOUND; + + postBuffer = fileContents->data(); + postBufferSize = fileContents->size(); + + // FIXME: The NPAPI spec states that the file should be deleted here. + } else { + postBuffer = buffer; + postBufferSize = length; + } + + if (parseHeaders) { + if (startsWithBlankLine(postBuffer, postBufferSize)) { + postBuffer++; + postBufferSize--; + } else { + int location = locationAfterFirstBlankLine(postBuffer, postBufferSize); + if (location != -1) { + // If the blank line is somewhere in the middle of the buffer, everything before is the header + headerFields = parseRFC822HeaderFields(postBuffer, location); + unsigned dataLength = postBufferSize - location; + + // Sometimes plugins like to set Content-Length themselves when they post, + // but WebFoundation does not like that. So we will remove the header + // and instead truncate the data to the requested length. + String contentLength = headerFields.get("Content-Length"); + + if (!contentLength.isNull()) + dataLength = min(contentLength.toInt(), (int)dataLength); + headerFields.remove("Content-Length"); + + postBuffer += location; + postBufferSize = dataLength; + + } + } + } + + ASSERT(bodyData.isEmpty()); + bodyData.append(postBuffer, postBufferSize); + + return NPERR_NO_ERROR; +} + +static String makeURLString(const char* url) +{ + String urlString(url); + + // Strip return characters. + urlString.replace('\r', ""); + urlString.replace('\n', ""); + + return urlString; +} + +static NPError NPN_GetURL(NPP npp, const char* url, const char* target) +{ + if (!url) + return NPERR_GENERIC_ERROR; + + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->loadURL("GET", makeURLString(url), target, HTTPHeaderMap(), Vector<uint8_t>(), false, 0); + + return NPERR_GENERIC_ERROR; +} + +static NPError NPN_PostURL(NPP npp, const char* url, const char* target, uint32_t len, const char* buf, NPBool file) +{ + HTTPHeaderMap headerFields; + Vector<uint8_t> postData; + + // NPN_PostURL only allows headers if the post buffer points to a file. + bool parseHeaders = file; + + NPError error = parsePostBuffer(file, buf, len, parseHeaders, headerFields, postData); + if (error != NPERR_NO_ERROR) + return error; + + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->loadURL("POST", makeURLString(url), target, headerFields, postData, false, 0); + return NPERR_NO_ERROR; +} + +static NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) +{ + notImplemented(); + return NPERR_GENERIC_ERROR; +} + +static NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream) +{ + notImplemented(); + return NPERR_GENERIC_ERROR; +} + +static int32_t NPN_Write(NPP instance, NPStream* stream, int32_t len, void* buffer) +{ + notImplemented(); + return -1; +} + +static NPError NPN_DestroyStream(NPP npp, NPStream* stream, NPReason reason) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + return plugin->destroyStream(stream, reason); +} + +static void NPN_Status(NPP npp, const char* message) +{ + String statusbarText; + if (!message) + statusbarText = ""; + else + statusbarText = String::fromUTF8WithLatin1Fallback(message, strlen(message)); + + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->setStatusbarText(statusbarText); +} + +static const char* NPN_UserAgent(NPP npp) +{ + return NetscapePlugin::userAgent(npp); +} + +static void* NPN_MemAlloc(uint32_t size) +{ + return npnMemAlloc(size); +} + +static void NPN_MemFree(void* ptr) +{ + npnMemFree(ptr); +} + +static uint32_t NPN_MemFlush(uint32_t size) +{ + return 0; +} + +static void NPN_ReloadPlugins(NPBool reloadPages) +{ + notImplemented(); +} + +static JRIEnv* NPN_GetJavaEnv(void) +{ + notImplemented(); + return 0; +} + +static jref NPN_GetJavaPeer(NPP instance) +{ + notImplemented(); + return 0; +} + +static NPError NPN_GetURLNotify(NPP npp, const char* url, const char* target, void* notifyData) +{ + if (!url) + return NPERR_GENERIC_ERROR; + + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->loadURL("GET", makeURLString(url), target, HTTPHeaderMap(), Vector<uint8_t>(), true, notifyData); + + return NPERR_NO_ERROR; +} + +static NPError NPN_PostURLNotify(NPP npp, const char* url, const char* target, uint32_t len, const char* buf, NPBool file, void* notifyData) +{ + HTTPHeaderMap headerFields; + Vector<uint8_t> postData; + NPError error = parsePostBuffer(file, buf, len, true, headerFields, postData); + if (error != NPERR_NO_ERROR) + return error; + + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->loadURL("POST", makeURLString(url), target, headerFields, postData, true, notifyData); + return NPERR_NO_ERROR; +} + +#if PLATFORM(MAC) +// Whether the browser supports compositing of Core Animation plug-ins. +static const unsigned WKNVSupportsCompositingCoreAnimationPluginsBool = 74656; + +// Whether the browser expects a non-retained Core Animation layer. +static const unsigned WKNVExpectsNonretainedLayer = 74657; + +// The Core Animation render server port. +static const unsigned WKNVCALayerRenderServerPort = 71879; + +#endif + +static NPError NPN_GetValue(NPP npp, NPNVariable variable, void *value) +{ + switch (variable) { + case NPNVWindowNPObject: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + NPObject* windowNPObject = plugin->windowScriptNPObject(); + if (!windowNPObject) + return NPERR_GENERIC_ERROR; + + *(NPObject**)value = windowNPObject; + break; + } + case NPNVPluginElementNPObject: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + NPObject* pluginElementNPObject = plugin->pluginElementNPObject(); + *(NPObject**)value = pluginElementNPObject; + break; + } + case NPNVprivateModeBool: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + *(NPBool*)value = plugin->isPrivateBrowsingEnabled(); + break; + } +#if PLATFORM(MAC) + case NPNVsupportsCoreGraphicsBool: + // Always claim to support the Core Graphics drawing model. + *(NPBool*)value = true; + break; + + case WKNVSupportsCompositingCoreAnimationPluginsBool: + case NPNVsupportsCoreAnimationBool: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + *(NPBool*)value = plugin->isAcceleratedCompositingEnabled(); + break; + } + case NPNVcontentsScaleFactor: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + *(double*)value = plugin->contentsScaleFactor(); + break; + } + case NPNVsupportsCocoaBool: + // Always claim to support the Cocoa event model. + *(NPBool*)value = true; + break; + + case NPNVsupportsUpdatedCocoaTextInputBool: { + // The plug-in is asking whether we support the updated Cocoa text input model. + // If we haven't yet delivered a key down event to the plug-in, we can opt into the updated + // model and say that we support it. Otherwise, we'll just fall back and say that we don't support it. + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + bool supportsUpdatedTextInput = !plugin->hasHandledAKeyDownEvent(); + if (supportsUpdatedTextInput) + plugin->setPluginWantsLegacyCocoaTextInput(false); + + *reinterpret_cast<NPBool*>(value) = supportsUpdatedTextInput; + break; + } + + case WKNVCALayerRenderServerPort: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + *(mach_port_t*)value = plugin->compositingRenderServerPort(); + break; + } + + case WKNVExpectsNonretainedLayer: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + // Asking for this will make us expect a non-retained layer from the plug-in. + plugin->setPluginReturnsNonretainedLayer(true); + *(NPBool*)value = true; + break; + } + +#ifndef NP_NO_QUICKDRAW + case NPNVsupportsQuickDrawBool: + // We don't support the QuickDraw drawing model. + *(NPBool*)value = false; + break; +#endif +#ifndef NP_NO_CARBON + case NPNVsupportsCarbonBool: + *(NPBool*)value = true; + break; +#endif +#elif PLATFORM(WIN) + case NPNVnetscapeWindow: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + *reinterpret_cast<HWND*>(value) = plugin->containingWindow(); + break; + } + case NPNVSupportsWindowless: + *(NPBool*)value = true; + break; +#elif PLUGIN_ARCHITECTURE(X11) + case NPNVxDisplay: { + if (!npp) + return NPERR_GENERIC_ERROR; + *reinterpret_cast<Display**>(value) = NetscapePlugin::x11HostDisplay(); + break; + } + case NPNVSupportsXEmbedBool: + *static_cast<NPBool*>(value) = true; + break; + case NPNVSupportsWindowless: + *static_cast<NPBool*>(value) = true; + break; + + case NPNVToolkit: { +#if PLATFORM(GTK) + *reinterpret_cast<uint32_t*>(value) = 2; +#else + const uint32_t expectedGTKToolKitVersion = 2; + + // Set the expected GTK version if we know that this plugin needs it or if the plugin call us + // with a null instance. The latter is the case with NSPluginWrapper plugins. + bool requiresGTKToolKitVersion; + if (!npp) + requiresGTKToolKitVersion = true; + else { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + requiresGTKToolKitVersion = plugin->quirks().contains(PluginQuirks::RequiresGTKToolKit); + } + + *reinterpret_cast<uint32_t*>(value) = requiresGTKToolKitVersion ? expectedGTKToolKitVersion : 0; +#endif + break; + } + + // TODO: implement NPNVnetscapeWindow once we want to support windowed plugins. +#endif + default: + notImplemented(); + return NPERR_GENERIC_ERROR; + } + + return NPERR_NO_ERROR; +} + +static NPError NPN_SetValue(NPP npp, NPPVariable variable, void *value) +{ + switch (variable) { +#if PLATFORM(MAC) + case NPPVpluginDrawingModel: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + NPDrawingModel drawingModel = static_cast<NPDrawingModel>(reinterpret_cast<uintptr_t>(value)); + return plugin->setDrawingModel(drawingModel); + } + + case NPPVpluginEventModel: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + NPEventModel eventModel = static_cast<NPEventModel>(reinterpret_cast<uintptr_t>(value)); + return plugin->setEventModel(eventModel); + } +#endif + + case NPPVpluginWindowBool: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->setIsWindowed(value); + return NPERR_NO_ERROR; + } + + case NPPVpluginTransparentBool: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->setIsTransparent(value); + return NPERR_NO_ERROR; + } + + default: + notImplemented(); + return NPERR_GENERIC_ERROR; + } +} + +static void NPN_InvalidateRect(NPP npp, NPRect* invalidRect) +{ +#if PLUGIN_ARCHITECTURE(X11) + // NSPluginWrapper, a plugin wrapper binary that allows running 32-bit plugins + // on 64-bit architectures typically used in X11, will sometimes give us a null NPP here. + if (!npp) + return; +#endif + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->invalidate(invalidRect); +} + +static void NPN_InvalidateRegion(NPP npp, NPRegion invalidRegion) +{ + // FIXME: We could at least figure out the bounding rectangle of the invalid region. + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->invalidate(0); +} + +static void NPN_ForceRedraw(NPP instance) +{ + notImplemented(); +} + +static NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name) +{ + return static_cast<NPIdentifier>(IdentifierRep::get(name)); +} + +static void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers) +{ + ASSERT(names); + ASSERT(identifiers); + + if (!names || !identifiers) + return; + + for (int32_t i = 0; i < nameCount; ++i) + identifiers[i] = NPN_GetStringIdentifier(names[i]); +} + +static NPIdentifier NPN_GetIntIdentifier(int32_t intid) +{ + return static_cast<NPIdentifier>(IdentifierRep::get(intid)); +} + +static bool NPN_IdentifierIsString(NPIdentifier identifier) +{ + return static_cast<IdentifierRep*>(identifier)->isString(); +} + +static NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier) +{ + const char* string = static_cast<IdentifierRep*>(identifier)->string(); + if (!string) + return 0; + + uint32_t stringLength = strlen(string); + char* utf8String = npnMemNewArray<char>(stringLength + 1); + memcpy(utf8String, string, stringLength); + utf8String[stringLength] = '\0'; + + return utf8String; +} + +static int32_t NPN_IntFromIdentifier(NPIdentifier identifier) +{ + return static_cast<IdentifierRep*>(identifier)->number(); +} + +static NPObject* NPN_CreateObject(NPP npp, NPClass *npClass) +{ + return createNPObject(npp, npClass); +} + +static NPObject *NPN_RetainObject(NPObject *npObject) +{ + retainNPObject(npObject); + return npObject; +} + +static void NPN_ReleaseObject(NPObject *npObject) +{ + releaseNPObject(npObject); +} + +static bool NPN_Invoke(NPP npp, NPObject *npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + if (RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp)) { + bool returnValue; + if (plugin->tryToShortCircuitInvoke(npObject, methodName, arguments, argumentCount, returnValue, *result)) + return returnValue; + } + + if (npObject->_class->invoke) + return npObject->_class->invoke(npObject, methodName, arguments, argumentCount, result); + + return false; +} + +static bool NPN_InvokeDefault(NPP, NPObject *npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + if (npObject->_class->invokeDefault) + return npObject->_class->invokeDefault(npObject, arguments, argumentCount, result); + + return false; +} + +static bool NPN_Evaluate(NPP npp, NPObject *npObject, NPString *script, NPVariant* result) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + String scriptString = String::fromUTF8WithLatin1Fallback(script->UTF8Characters, script->UTF8Length); + + return plugin->evaluate(npObject, scriptString, result); +} + +static bool NPN_GetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, NPVariant* result) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + if (npObject->_class->getProperty) + return npObject->_class->getProperty(npObject, propertyName, result); + + return false; +} + +static bool NPN_SetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, const NPVariant* value) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + if (npObject->_class->setProperty) + return npObject->_class->setProperty(npObject, propertyName, value); + + return false; +} + +static bool NPN_RemoveProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + if (npObject->_class->removeProperty) + return npObject->_class->removeProperty(npObject, propertyName); + + return false; +} + +static bool NPN_HasProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + if (npObject->_class->hasProperty) + return npObject->_class->hasProperty(npObject, propertyName); + + return false; +} + +static bool NPN_HasMethod(NPP npp, NPObject* npObject, NPIdentifier methodName) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + if (npObject->_class->hasMethod) + return npObject->_class->hasMethod(npObject, methodName); + + return false; +} + +static void NPN_ReleaseVariantValue(NPVariant* variant) +{ + releaseNPVariantValue(variant); +} + +static void NPN_SetException(NPObject*, const NPUTF8* message) +{ + NetscapePlugin::setException(message); +} + +static void NPN_PushPopupsEnabledState(NPP npp, NPBool enabled) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->pushPopupsEnabledState(enabled); +} + +static void NPN_PopPopupsEnabledState(NPP npp) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + plugin->popPopupsEnabledState(); +} + +static bool NPN_Enumerate(NPP npp, NPObject* npObject, NPIdentifier** identifiers, uint32_t* identifierCount) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate) + return npObject->_class->enumerate(npObject, identifiers, identifierCount); + + return false; +} + +static void NPN_PluginThreadAsyncCall(NPP npp, void (*function)(void*), void* userData) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + plugin->pluginThreadAsyncCall(function, userData); +} + +static bool NPN_Construct(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npObject->_class) && npObject->_class->construct) + return npObject->_class->construct(npObject, arguments, argumentCount, result); + + return false; +} + +static NPError copyCString(const CString& string, char** value, uint32_t* len) +{ + ASSERT(!string.isNull()); + ASSERT(value); + ASSERT(len); + + *value = npnMemNewArray<char>(string.length()); + if (!*value) + return NPERR_GENERIC_ERROR; + + memcpy(*value, string.data(), string.length()); + *len = string.length(); + return NPERR_NO_ERROR; +} + +static NPError NPN_GetValueForURL(NPP npp, NPNURLVariable variable, const char* url, char** value, uint32_t* len) +{ + if (!value || !len) + return NPERR_GENERIC_ERROR; + + switch (variable) { + case NPNURLVCookie: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + String cookies = plugin->cookiesForURL(makeURLString(url)); + if (cookies.isNull()) + return NPERR_GENERIC_ERROR; + + return copyCString(cookies.utf8(), value, len); + } + + case NPNURLVProxy: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + String proxies = plugin->proxiesForURL(makeURLString(url)); + if (proxies.isNull()) + return NPERR_GENERIC_ERROR; + + return copyCString(proxies.utf8(), value, len); + } + default: + notImplemented(); + return NPERR_GENERIC_ERROR; + } +} + +static NPError NPN_SetValueForURL(NPP npp, NPNURLVariable variable, const char* url, const char* value, uint32_t len) +{ + switch (variable) { + case NPNURLVCookie: { + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + PluginDestructionProtector protector(plugin.get()); + + plugin->setCookiesForURL(makeURLString(url), String(value, len)); + return NPERR_NO_ERROR; + } + + case NPNURLVProxy: + // Can't set the proxy for a URL. + return NPERR_GENERIC_ERROR; + + default: + notImplemented(); + return NPERR_GENERIC_ERROR; + } +} + +static bool initializeProtectionSpace(const char* protocol, const char* host, int port, const char* scheme, const char* realm, ProtectionSpace& protectionSpace) +{ + ProtectionSpaceServerType serverType; + if (!strcasecmp(protocol, "http")) + serverType = ProtectionSpaceServerHTTP; + else if (!strcasecmp(protocol, "https")) + serverType = ProtectionSpaceServerHTTPS; + else { + // We only care about http and https. + return false; + } + + ProtectionSpaceAuthenticationScheme authenticationScheme = ProtectionSpaceAuthenticationSchemeDefault; + if (serverType == ProtectionSpaceServerHTTP) { + if (!strcasecmp(scheme, "basic")) + authenticationScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic; + else if (!strcmp(scheme, "digest")) + authenticationScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest; + } + + protectionSpace = ProtectionSpace(host, port, serverType, realm, authenticationScheme); + return true; +} + +static NPError NPN_GetAuthenticationInfo(NPP npp, const char* protocol, const char* host, int32_t port, const char* scheme, + const char* realm, char** username, uint32_t* usernameLength, char** password, uint32_t* passwordLength) +{ + if (!protocol || !host || !scheme || !realm || !username || !usernameLength || !password || !passwordLength) + return NPERR_GENERIC_ERROR; + + ProtectionSpace protectionSpace; + if (!initializeProtectionSpace(protocol, host, port, scheme, realm, protectionSpace)) + return NPERR_GENERIC_ERROR; + + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + String usernameString; + String passwordString; + if (!plugin->getAuthenticationInfo(protectionSpace, usernameString, passwordString)) + return NPERR_GENERIC_ERROR; + + NPError result = copyCString(usernameString.utf8(), username, usernameLength); + if (result != NPERR_NO_ERROR) + return result; + + result = copyCString(passwordString.utf8(), password, passwordLength); + if (result != NPERR_NO_ERROR) { + npnMemFree(*username); + return result; + } + + return NPERR_NO_ERROR; +} + +static uint32_t NPN_ScheduleTimer(NPP npp, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + return plugin->scheduleTimer(interval, repeat, timerFunc); +} + +static void NPN_UnscheduleTimer(NPP npp, uint32_t timerID) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + plugin->unscheduleTimer(timerID); +} + +#if PLATFORM(MAC) +static NPError NPN_PopUpContextMenu(NPP npp, NPMenu* menu) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + return plugin->popUpContextMenu(menu); +} + +static NPBool NPN_ConvertPoint(NPP npp, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double* destX, double* destY, NPCoordinateSpace destSpace) +{ + RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); + + double destinationX; + double destinationY; + + bool returnValue = plugin->convertPoint(sourceX, sourceY, sourceSpace, destinationX, destinationY, destSpace); + + if (destX) + *destX = destinationX; + if (destY) + *destY = destinationY; + + return returnValue; +} +#endif + +static void initializeBrowserFuncs(NPNetscapeFuncs &netscapeFuncs) +{ + netscapeFuncs.size = sizeof(NPNetscapeFuncs); + netscapeFuncs.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + + netscapeFuncs.geturl = NPN_GetURL; + netscapeFuncs.posturl = NPN_PostURL; + netscapeFuncs.requestread = NPN_RequestRead; + netscapeFuncs.newstream = NPN_NewStream; + netscapeFuncs.write = NPN_Write; + netscapeFuncs.destroystream = NPN_DestroyStream; + netscapeFuncs.status = NPN_Status; + netscapeFuncs.uagent = NPN_UserAgent; + netscapeFuncs.memalloc = NPN_MemAlloc; + netscapeFuncs.memfree = NPN_MemFree; + netscapeFuncs.memflush = NPN_MemFlush; + netscapeFuncs.reloadplugins = NPN_ReloadPlugins; + netscapeFuncs.getJavaEnv = NPN_GetJavaEnv; + netscapeFuncs.getJavaPeer = NPN_GetJavaPeer; + netscapeFuncs.geturlnotify = NPN_GetURLNotify; + netscapeFuncs.posturlnotify = NPN_PostURLNotify; + netscapeFuncs.getvalue = NPN_GetValue; + netscapeFuncs.setvalue = NPN_SetValue; + netscapeFuncs.invalidaterect = NPN_InvalidateRect; + netscapeFuncs.invalidateregion = NPN_InvalidateRegion; + netscapeFuncs.forceredraw = NPN_ForceRedraw; + + netscapeFuncs.getstringidentifier = NPN_GetStringIdentifier; + netscapeFuncs.getstringidentifiers = NPN_GetStringIdentifiers; + netscapeFuncs.getintidentifier = NPN_GetIntIdentifier; + netscapeFuncs.identifierisstring = NPN_IdentifierIsString; + netscapeFuncs.utf8fromidentifier = NPN_UTF8FromIdentifier; + netscapeFuncs.intfromidentifier = NPN_IntFromIdentifier; + netscapeFuncs.createobject = NPN_CreateObject; + netscapeFuncs.retainobject = NPN_RetainObject; + netscapeFuncs.releaseobject = NPN_ReleaseObject; + netscapeFuncs.invoke = NPN_Invoke; + netscapeFuncs.invokeDefault = NPN_InvokeDefault; + netscapeFuncs.evaluate = NPN_Evaluate; + netscapeFuncs.getproperty = NPN_GetProperty; + netscapeFuncs.setproperty = NPN_SetProperty; + netscapeFuncs.removeproperty = NPN_RemoveProperty; + netscapeFuncs.hasproperty = NPN_HasProperty; + netscapeFuncs.hasmethod = NPN_HasMethod; + netscapeFuncs.releasevariantvalue = NPN_ReleaseVariantValue; + netscapeFuncs.setexception = NPN_SetException; + netscapeFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState; + netscapeFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState; + netscapeFuncs.enumerate = NPN_Enumerate; + netscapeFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall; + netscapeFuncs.construct = NPN_Construct; + netscapeFuncs.getvalueforurl = NPN_GetValueForURL; + netscapeFuncs.setvalueforurl = NPN_SetValueForURL; + netscapeFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo; + netscapeFuncs.scheduletimer = NPN_ScheduleTimer; + netscapeFuncs.unscheduletimer = NPN_UnscheduleTimer; +#if PLATFORM(MAC) + netscapeFuncs.popupcontextmenu = NPN_PopUpContextMenu; + netscapeFuncs.convertpoint = NPN_ConvertPoint; +#else + netscapeFuncs.popupcontextmenu = 0; + netscapeFuncs.convertpoint = 0; +#endif +} + +NPNetscapeFuncs* netscapeBrowserFuncs() +{ + static NPNetscapeFuncs netscapeFuncs; + static bool initialized = false; + + if (!initialized) { + initializeBrowserFuncs(netscapeFuncs); + initialized = true; + } + + return &netscapeFuncs; +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapeBrowserFuncs.h b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapeBrowserFuncs.h new file mode 100644 index 000000000..49a7f3a9d --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapeBrowserFuncs.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef NetscapeBrowserFuncs_h +#define NetscapeBrowserFuncs_h + +#include <WebCore/npfunctions.h> + +namespace WebKit { + +NPNetscapeFuncs* netscapeBrowserFuncs(); + +} // namespace WebKit + + +#endif // NetscapeBrowserFuncs_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.cpp new file mode 100644 index 000000000..1f09dc408 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.cpp @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "NetscapePlugin.h" + +#include "NPRuntimeObjectMap.h" +#include "NPRuntimeUtilities.h" +#include "NetscapePluginStream.h" +#include "PluginController.h" +#include "ShareableBitmap.h" +#include <WebCore/GraphicsContext.h> +#include <WebCore/HTTPHeaderMap.h> +#include <WebCore/IntRect.h> +#include <WebCore/KURL.h> +#include <utility> +#include <wtf/text/CString.h> + +using namespace WebCore; +using namespace std; + +namespace WebKit { + +// The plug-in that we're currently calling NPP_New for. +static NetscapePlugin* currentNPPNewPlugin; + +PassRefPtr<NetscapePlugin> NetscapePlugin::create(PassRefPtr<NetscapePluginModule> pluginModule) +{ + if (!pluginModule) + return 0; + + return adoptRef(new NetscapePlugin(pluginModule)); +} + +NetscapePlugin::NetscapePlugin(PassRefPtr<NetscapePluginModule> pluginModule) + : m_nextRequestID(0) + , m_pluginModule(pluginModule) + , m_npWindow() + , m_isStarted(false) +#if PLATFORM(MAC) + , m_isWindowed(false) +#else + , m_isWindowed(true) +#endif + , m_isTransparent(false) + , m_inNPPNew(false) + , m_loadManually(false) + , m_nextTimerID(0) +#if PLATFORM(MAC) + , m_drawingModel(static_cast<NPDrawingModel>(-1)) + , m_eventModel(static_cast<NPEventModel>(-1)) + , m_pluginReturnsNonretainedLayer(!m_pluginModule->pluginQuirks().contains(PluginQuirks::ReturnsRetainedCoreAnimationLayer)) + , m_currentMouseEvent(0) + , m_pluginHasFocus(false) + , m_windowHasFocus(false) + , m_pluginWantsLegacyCocoaTextInput(true) + , m_isComplexTextInputEnabled(false) + , m_hasHandledAKeyDownEvent(false) + , m_ignoreNextKeyUpEventCounter(0) +#ifndef NP_NO_CARBON + , m_nullEventTimer(RunLoop::main(), this, &NetscapePlugin::nullEventTimerFired) + , m_npCGContext() +#endif +#elif PLUGIN_ARCHITECTURE(X11) + , m_drawable(0) + , m_pluginDisplay(0) +#endif +{ + m_npp.ndata = this; + m_npp.pdata = 0; + + m_pluginModule->incrementLoadCount(); +} + +NetscapePlugin::~NetscapePlugin() +{ + ASSERT(!m_isStarted); + ASSERT(m_timers.isEmpty()); + + m_pluginModule->decrementLoadCount(); +} + +PassRefPtr<NetscapePlugin> NetscapePlugin::fromNPP(NPP npp) +{ + if (npp) + return static_cast<NetscapePlugin*>(npp->ndata); + + // FIXME: Return the current NetscapePlugin here. + ASSERT_NOT_REACHED(); + return 0; +} + +void NetscapePlugin::invalidate(const NPRect* invalidRect) +{ + IntRect rect; + + if (!invalidRect) + rect = IntRect(0, 0, m_pluginSize.width(), m_pluginSize.height()); + else + rect = IntRect(invalidRect->left, invalidRect->top, invalidRect->right - invalidRect->left, invalidRect->bottom - invalidRect->top); + + if (platformInvalidate(rect)) + return; + + controller()->invalidate(rect); +} + +const char* NetscapePlugin::userAgent(NPP npp) +{ + if (npp) + return fromNPP(npp)->userAgent(); + + if (currentNPPNewPlugin) + return currentNPPNewPlugin->userAgent(); + + return 0; +} + +const char* NetscapePlugin::userAgent() +{ +#if PLUGIN_ARCHITECTURE(WIN) + static const char* MozillaUserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0"; + + if (quirks().contains(PluginQuirks::WantsMozillaUserAgent)) + return MozillaUserAgent; +#endif + + if (m_userAgent.isNull()) { + String userAgent = controller()->userAgent(); + ASSERT(!userAgent.isNull()); + +#if PLUGIN_ARCHITECTURE(MAC) + if (quirks().contains(PluginQuirks::AppendVersion3UserAgent)) + userAgent += " Version/3.2.1"; +#endif + + m_userAgent = userAgent.utf8(); + } + return m_userAgent.data(); +} + +void NetscapePlugin::loadURL(const String& method, const String& urlString, const String& target, const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, + bool sendNotification, void* notificationData) +{ + uint64_t requestID = ++m_nextRequestID; + + controller()->loadURL(requestID, method, urlString, target, headerFields, httpBody, allowPopups()); + + if (target.isNull()) { + // The browser is going to send the data in a stream, create a plug-in stream. + RefPtr<NetscapePluginStream> pluginStream = NetscapePluginStream::create(this, requestID, urlString, sendNotification, notificationData); + ASSERT(!m_streams.contains(requestID)); + + m_streams.set(requestID, pluginStream.release()); + return; + } + + if (sendNotification) { + // Eventually we are going to get a frameDidFinishLoading or frameDidFail call for this request. + // Keep track of the notification data so we can call NPP_URLNotify. + ASSERT(!m_pendingURLNotifications.contains(requestID)); + m_pendingURLNotifications.set(requestID, make_pair(urlString, notificationData)); + } +} + +NPError NetscapePlugin::destroyStream(NPStream* stream, NPReason reason) +{ + NetscapePluginStream* pluginStream = 0; + + for (StreamsMap::const_iterator it = m_streams.begin(), end = m_streams.end(); it != end; ++it) { + if (it->second->npStream() == stream) { + pluginStream = it->second.get(); + break; + } + } + + if (!pluginStream) + return NPERR_INVALID_INSTANCE_ERROR; + + return pluginStream->destroy(reason); +} + +void NetscapePlugin::setIsWindowed(bool isWindowed) +{ + // Once the plugin has started, it's too late to change whether the plugin is windowed or not. + // (This is true in Firefox and Chrome, too.) Disallow setting m_isWindowed in that case to + // keep our internal state consistent. + if (m_isStarted) + return; + + m_isWindowed = isWindowed; +} + +void NetscapePlugin::setIsTransparent(bool isTransparent) +{ + m_isTransparent = isTransparent; +} + +void NetscapePlugin::setStatusbarText(const String& statusbarText) +{ + controller()->setStatusbarText(statusbarText); +} + +static void (*setExceptionFunction)(const String&); + +void NetscapePlugin::setSetExceptionFunction(void (*function)(const String&)) +{ + ASSERT(!setExceptionFunction || setExceptionFunction == function); + setExceptionFunction = function; +} + +void NetscapePlugin::setException(const String& exceptionString) +{ + ASSERT(setExceptionFunction); + setExceptionFunction(exceptionString); +} + +bool NetscapePlugin::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result) +{ + return controller()->evaluate(npObject, scriptString, result, allowPopups()); +} + +bool NetscapePlugin::isPrivateBrowsingEnabled() +{ + return controller()->isPrivateBrowsingEnabled(); +} + +NPObject* NetscapePlugin::windowScriptNPObject() +{ + return controller()->windowScriptNPObject(); +} + +NPObject* NetscapePlugin::pluginElementNPObject() +{ + return controller()->pluginElementNPObject(); +} + +bool NetscapePlugin::tryToShortCircuitInvoke(NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, bool& returnValue, NPVariant& result) +{ + return controller()->tryToShortCircuitInvoke(npObject, methodName, arguments, argumentCount, returnValue, result); +} + +void NetscapePlugin::cancelStreamLoad(NetscapePluginStream* pluginStream) +{ + if (pluginStream == m_manualStream) { + controller()->cancelManualStreamLoad(); + return; + } + + // Ask the plug-in controller to cancel this stream load. + controller()->cancelStreamLoad(pluginStream->streamID()); +} + +void NetscapePlugin::removePluginStream(NetscapePluginStream* pluginStream) +{ + if (pluginStream == m_manualStream) { + m_manualStream = 0; + return; + } + + ASSERT(m_streams.get(pluginStream->streamID()) == pluginStream); + m_streams.remove(pluginStream->streamID()); +} + +bool NetscapePlugin::isAcceleratedCompositingEnabled() +{ +#if USE(ACCELERATED_COMPOSITING) + return controller()->isAcceleratedCompositingEnabled(); +#else + return false; +#endif +} + +void NetscapePlugin::pushPopupsEnabledState(bool state) +{ + m_popupEnabledStates.append(state); +} + +void NetscapePlugin::popPopupsEnabledState() +{ + ASSERT(!m_popupEnabledStates.isEmpty()); + + m_popupEnabledStates.removeLast(); +} + +void NetscapePlugin::pluginThreadAsyncCall(void (*function)(void*), void* userData) +{ + RunLoop::main()->dispatch(bind(&NetscapePlugin::handlePluginThreadAsyncCall, this, function, userData)); +} + +void NetscapePlugin::handlePluginThreadAsyncCall(void (*function)(void*), void* userData) +{ + if (!m_isStarted) + return; + + function(userData); +} + +PassOwnPtr<NetscapePlugin::Timer> NetscapePlugin::Timer::create(NetscapePlugin* netscapePlugin, unsigned timerID, unsigned interval, bool repeat, TimerFunc timerFunc) +{ + return adoptPtr(new Timer(netscapePlugin, timerID, interval, repeat, timerFunc)); +} + +NetscapePlugin::Timer::Timer(NetscapePlugin* netscapePlugin, unsigned timerID, unsigned interval, bool repeat, TimerFunc timerFunc) + : m_netscapePlugin(netscapePlugin) + , m_timerID(timerID) + , m_interval(interval) + , m_repeat(repeat) + , m_timerFunc(timerFunc) + , m_timer(RunLoop::main(), this, &Timer::timerFired) +{ +} + +NetscapePlugin::Timer::~Timer() +{ +} + +void NetscapePlugin::Timer::start() +{ + double timeInterval = m_interval / 1000.0; + + if (m_repeat) + m_timer.startRepeating(timeInterval); + else + m_timer.startOneShot(timeInterval); +} + +void NetscapePlugin::Timer::stop() +{ + m_timer.stop(); +} + +void NetscapePlugin::Timer::timerFired() +{ + m_timerFunc(&m_netscapePlugin->m_npp, m_timerID); + + if (!m_repeat) + m_netscapePlugin->unscheduleTimer(m_timerID); +} + +uint32_t NetscapePlugin::scheduleTimer(unsigned interval, bool repeat, void (*timerFunc)(NPP, unsigned timerID)) +{ + if (!timerFunc) + return 0; + + // FIXME: Handle wrapping around. + unsigned timerID = ++m_nextTimerID; + + OwnPtr<Timer> timer = Timer::create(this, timerID, interval, repeat, timerFunc); + + // FIXME: Based on the plug-in visibility, figure out if we should throttle the timer, or if we should start it at all. + timer->start(); + m_timers.set(timerID, timer.leakPtr()); + + return timerID; +} + +void NetscapePlugin::unscheduleTimer(unsigned timerID) +{ + TimerMap::iterator it = m_timers.find(timerID); + if (it == m_timers.end()) + return; + + OwnPtr<Timer> timer = adoptPtr(it->second); + m_timers.remove(it); + + timer->stop(); +} + +double NetscapePlugin::contentsScaleFactor() +{ + return controller()->contentsScaleFactor(); +} + +String NetscapePlugin::proxiesForURL(const String& urlString) +{ + return controller()->proxiesForURL(urlString); +} + +String NetscapePlugin::cookiesForURL(const String& urlString) +{ + return controller()->cookiesForURL(urlString); +} + +void NetscapePlugin::setCookiesForURL(const String& urlString, const String& cookieString) +{ + controller()->setCookiesForURL(urlString, cookieString); +} + +bool NetscapePlugin::getAuthenticationInfo(const ProtectionSpace& protectionSpace, String& username, String& password) +{ + return controller()->getAuthenticationInfo(protectionSpace, username, password); +} + +NPError NetscapePlugin::NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* savedData) +{ + return m_pluginModule->pluginFuncs().newp(pluginType, &m_npp, mode, argc, argn, argv, savedData); +} + +NPError NetscapePlugin::NPP_Destroy(NPSavedData** savedData) +{ + return m_pluginModule->pluginFuncs().destroy(&m_npp, savedData); +} + +NPError NetscapePlugin::NPP_SetWindow(NPWindow* npWindow) +{ + return m_pluginModule->pluginFuncs().setwindow(&m_npp, npWindow); +} + +NPError NetscapePlugin::NPP_NewStream(NPMIMEType mimeType, NPStream* stream, NPBool seekable, uint16_t* streamType) +{ + return m_pluginModule->pluginFuncs().newstream(&m_npp, mimeType, stream, seekable, streamType); +} + +NPError NetscapePlugin::NPP_DestroyStream(NPStream* stream, NPReason reason) +{ + return m_pluginModule->pluginFuncs().destroystream(&m_npp, stream, reason); +} + +void NetscapePlugin::NPP_StreamAsFile(NPStream* stream, const char* filename) +{ + return m_pluginModule->pluginFuncs().asfile(&m_npp, stream, filename); +} + +int32_t NetscapePlugin::NPP_WriteReady(NPStream* stream) +{ + return m_pluginModule->pluginFuncs().writeready(&m_npp, stream); +} + +int32_t NetscapePlugin::NPP_Write(NPStream* stream, int32_t offset, int32_t len, void* buffer) +{ + return m_pluginModule->pluginFuncs().write(&m_npp, stream, offset, len, buffer); +} + +int16_t NetscapePlugin::NPP_HandleEvent(void* event) +{ + return m_pluginModule->pluginFuncs().event(&m_npp, event); +} + +void NetscapePlugin::NPP_URLNotify(const char* url, NPReason reason, void* notifyData) +{ + m_pluginModule->pluginFuncs().urlnotify(&m_npp, url, reason, notifyData); +} + +NPError NetscapePlugin::NPP_GetValue(NPPVariable variable, void *value) +{ + if (!m_pluginModule->pluginFuncs().getvalue) + return NPERR_GENERIC_ERROR; + + return m_pluginModule->pluginFuncs().getvalue(&m_npp, variable, value); +} + +NPError NetscapePlugin::NPP_SetValue(NPNVariable variable, void *value) +{ + if (!m_pluginModule->pluginFuncs().setvalue) + return NPERR_GENERIC_ERROR; + + return m_pluginModule->pluginFuncs().setvalue(&m_npp, variable, value); +} + +void NetscapePlugin::callSetWindow() +{ + if (wantsPluginRelativeNPWindowCoordinates()) { + m_npWindow.x = 0; + m_npWindow.y = 0; + m_npWindow.clipRect.top = m_clipRect.y(); + m_npWindow.clipRect.left = m_clipRect.x(); + } else { + IntPoint pluginLocationInRootViewCoordinates = convertToRootView(IntPoint()); + IntPoint clipRectInRootViewCoordinates = convertToRootView(m_clipRect.location()); + + m_npWindow.x = pluginLocationInRootViewCoordinates.x(); + m_npWindow.y = pluginLocationInRootViewCoordinates.y(); + m_npWindow.clipRect.top = clipRectInRootViewCoordinates.y(); + m_npWindow.clipRect.left = clipRectInRootViewCoordinates.x(); + } + + m_npWindow.width = m_pluginSize.width(); + m_npWindow.height = m_pluginSize.height(); + m_npWindow.clipRect.right = m_npWindow.clipRect.left + m_clipRect.width(); + m_npWindow.clipRect.bottom = m_npWindow.clipRect.top + m_clipRect.height(); + + NPP_SetWindow(&m_npWindow); +} + +bool NetscapePlugin::shouldLoadSrcURL() +{ + // Check if we should cancel the load + NPBool cancelSrcStream = false; + + if (NPP_GetValue(NPPVpluginCancelSrcStream, &cancelSrcStream) != NPERR_NO_ERROR) + return true; + + return !cancelSrcStream; +} + +NetscapePluginStream* NetscapePlugin::streamFromID(uint64_t streamID) +{ + return m_streams.get(streamID).get(); +} + +void NetscapePlugin::stopAllStreams() +{ + Vector<RefPtr<NetscapePluginStream> > streams; + copyValuesToVector(m_streams, streams); + + for (size_t i = 0; i < streams.size(); ++i) + streams[i]->stop(NPRES_USER_BREAK); +} + +bool NetscapePlugin::allowPopups() const +{ + if (m_pluginModule->pluginFuncs().version >= NPVERS_HAS_POPUPS_ENABLED_STATE) { + if (!m_popupEnabledStates.isEmpty()) + return m_popupEnabledStates.last(); + } + + // FIXME: Check if the current event is a user gesture. + // Really old versions of Flash required this for popups to work, but all newer versions + // support NPN_PushPopupEnabledState/NPN_PopPopupEnabledState. + return false; +} + +bool NetscapePlugin::initialize(const Parameters& parameters) +{ + uint16_t mode = parameters.loadManually ? NP_FULL : NP_EMBED; + + m_loadManually = parameters.loadManually; + + CString mimeTypeCString = parameters.mimeType.utf8(); + + ASSERT(parameters.names.size() == parameters.values.size()); + + Vector<CString> paramNames; + Vector<CString> paramValues; + for (size_t i = 0; i < parameters.names.size(); ++i) { + String parameterName = parameters.names[i]; + +#if PLUGIN_ARCHITECTURE(MAC) + if (m_pluginModule->pluginQuirks().contains(PluginQuirks::WantsLowercaseParameterNames)) + parameterName = parameterName.lower(); +#endif + + paramNames.append(parameterName.utf8()); + paramValues.append(parameters.values[i].utf8()); + } + + // The strings that these pointers point to are kept alive by paramNames and paramValues. + Vector<const char*> names; + Vector<const char*> values; + for (size_t i = 0; i < paramNames.size(); ++i) { + names.append(paramNames[i].data()); + values.append(paramValues[i].data()); + } + +#if PLUGIN_ARCHITECTURE(MAC) + if (m_pluginModule->pluginQuirks().contains(PluginQuirks::MakeTransparentIfBackgroundAttributeExists)) { + for (size_t i = 0; i < parameters.names.size(); ++i) { + if (equalIgnoringCase(parameters.names[i], "background")) { + setIsTransparent(true); + break; + } + } + } +#endif + + NetscapePlugin* previousNPPNewPlugin = currentNPPNewPlugin; + + m_inNPPNew = true; + currentNPPNewPlugin = this; + + NPError error = NPP_New(const_cast<char*>(mimeTypeCString.data()), mode, names.size(), + const_cast<char**>(names.data()), const_cast<char**>(values.data()), 0); + + m_inNPPNew = false; + currentNPPNewPlugin = previousNPPNewPlugin; + + if (error != NPERR_NO_ERROR) + return false; + + m_isStarted = true; + + // FIXME: This is not correct in all cases. + m_npWindow.type = NPWindowTypeDrawable; + + if (!platformPostInitialize()) { + destroy(); + return false; + } + + // Load the src URL if needed. + if (!parameters.loadManually && !parameters.url.isEmpty() && shouldLoadSrcURL()) + loadURL("GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false, 0); + + return true; +} + +void NetscapePlugin::destroy() +{ + ASSERT(m_isStarted); + + // Stop all streams. + stopAllStreams(); + +#if !PLUGIN_ARCHITECTURE(MAC) && !PLUGIN_ARCHITECTURE(X11) + m_npWindow.window = 0; + callSetWindow(); +#endif + + NPP_Destroy(0); + + m_isStarted = false; + + platformDestroy(); + + deleteAllValues(m_timers); + m_timers.clear(); +} + +void NetscapePlugin::paint(GraphicsContext* context, const IntRect& dirtyRect) +{ + ASSERT(m_isStarted); + + platformPaint(context, dirtyRect); +} + +PassRefPtr<ShareableBitmap> NetscapePlugin::snapshot() +{ + if (!supportsSnapshotting() || m_pluginSize.isEmpty()) + return 0; + + ASSERT(m_isStarted); + + IntSize backingStoreSize = m_pluginSize; + backingStoreSize.scale(contentsScaleFactor()); + + RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha); + OwnPtr<GraphicsContext> context = bitmap->createGraphicsContext(); + + // FIXME: We should really call applyDeviceScaleFactor instead of scale, but that ends up calling into WKSI + // which we currently don't have initiated in the plug-in process. + context->scale(FloatSize(contentsScaleFactor(), contentsScaleFactor())); + + platformPaint(context.get(), IntRect(IntPoint(), m_pluginSize), true); + + return bitmap.release(); +} + +bool NetscapePlugin::isTransparent() +{ + return m_isTransparent; +} + +void NetscapePlugin::geometryDidChange(const IntSize& pluginSize, const IntRect& clipRect, const AffineTransform& pluginToRootViewTransform) +{ + ASSERT(m_isStarted); + + if (pluginSize == m_pluginSize && m_clipRect == clipRect && m_pluginToRootViewTransform == pluginToRootViewTransform) { + // Nothing to do. + return; + } + + bool shouldCallWindow = true; + + // If the plug-in doesn't want window relative coordinates, we don't need to call setWindow unless its size or clip rect changes. + if (wantsPluginRelativeNPWindowCoordinates() && m_pluginSize == pluginSize && m_clipRect == clipRect) + shouldCallWindow = false; + + m_pluginSize = pluginSize; + m_clipRect = clipRect; + m_pluginToRootViewTransform = pluginToRootViewTransform; + + IntPoint frameRectLocationInWindowCoordinates = m_pluginToRootViewTransform.mapPoint(IntPoint()); + m_frameRectInWindowCoordinates = IntRect(frameRectLocationInWindowCoordinates, m_pluginSize); + + platformGeometryDidChange(); + + if (!shouldCallWindow) + return; + + callSetWindow(); +} + +void NetscapePlugin::visibilityDidChange() +{ + ASSERT(m_isStarted); + + platformVisibilityDidChange(); +} + +void NetscapePlugin::frameDidFinishLoading(uint64_t requestID) +{ + ASSERT(m_isStarted); + + PendingURLNotifyMap::iterator it = m_pendingURLNotifications.find(requestID); + if (it == m_pendingURLNotifications.end()) + return; + + String url = it->second.first; + void* notificationData = it->second.second; + + m_pendingURLNotifications.remove(it); + + NPP_URLNotify(url.utf8().data(), NPRES_DONE, notificationData); +} + +void NetscapePlugin::frameDidFail(uint64_t requestID, bool wasCancelled) +{ + ASSERT(m_isStarted); + + PendingURLNotifyMap::iterator it = m_pendingURLNotifications.find(requestID); + if (it == m_pendingURLNotifications.end()) + return; + + String url = it->second.first; + void* notificationData = it->second.second; + + m_pendingURLNotifications.remove(it); + + NPP_URLNotify(url.utf8().data(), wasCancelled ? NPRES_USER_BREAK : NPRES_NETWORK_ERR, notificationData); +} + +void NetscapePlugin::didEvaluateJavaScript(uint64_t requestID, const String& result) +{ + ASSERT(m_isStarted); + + if (NetscapePluginStream* pluginStream = streamFromID(requestID)) + pluginStream->sendJavaScriptStream(result); +} + +void NetscapePlugin::streamDidReceiveResponse(uint64_t streamID, const KURL& responseURL, uint32_t streamLength, + uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& /* suggestedFileName */) +{ + ASSERT(m_isStarted); + + if (NetscapePluginStream* pluginStream = streamFromID(streamID)) + pluginStream->didReceiveResponse(responseURL, streamLength, lastModifiedTime, mimeType, headers); +} + +void NetscapePlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length) +{ + ASSERT(m_isStarted); + + if (NetscapePluginStream* pluginStream = streamFromID(streamID)) + pluginStream->didReceiveData(bytes, length); +} + +void NetscapePlugin::streamDidFinishLoading(uint64_t streamID) +{ + ASSERT(m_isStarted); + + if (NetscapePluginStream* pluginStream = streamFromID(streamID)) + pluginStream->didFinishLoading(); +} + +void NetscapePlugin::streamDidFail(uint64_t streamID, bool wasCancelled) +{ + ASSERT(m_isStarted); + + if (NetscapePluginStream* pluginStream = streamFromID(streamID)) + pluginStream->didFail(wasCancelled); +} + +void NetscapePlugin::manualStreamDidReceiveResponse(const KURL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, + const String& mimeType, const String& headers, const String& /* suggestedFileName */) +{ + ASSERT(m_isStarted); + ASSERT(m_loadManually); + ASSERT(!m_manualStream); + + m_manualStream = NetscapePluginStream::create(this, 0, responseURL.string(), false, 0); + m_manualStream->didReceiveResponse(responseURL, streamLength, lastModifiedTime, mimeType, headers); +} + +void NetscapePlugin::manualStreamDidReceiveData(const char* bytes, int length) +{ + ASSERT(m_isStarted); + ASSERT(m_loadManually); + ASSERT(m_manualStream); + + m_manualStream->didReceiveData(bytes, length); +} + +void NetscapePlugin::manualStreamDidFinishLoading() +{ + ASSERT(m_isStarted); + ASSERT(m_loadManually); + ASSERT(m_manualStream); + + m_manualStream->didFinishLoading(); +} + +void NetscapePlugin::manualStreamDidFail(bool wasCancelled) +{ + ASSERT(m_isStarted); + ASSERT(m_loadManually); + + if (!m_manualStream) + return; + m_manualStream->didFail(wasCancelled); +} + +bool NetscapePlugin::handleMouseEvent(const WebMouseEvent& mouseEvent) +{ + ASSERT(m_isStarted); + + return platformHandleMouseEvent(mouseEvent); +} + +bool NetscapePlugin::handleWheelEvent(const WebWheelEvent& wheelEvent) +{ + ASSERT(m_isStarted); + + return platformHandleWheelEvent(wheelEvent); +} + +bool NetscapePlugin::handleMouseEnterEvent(const WebMouseEvent& mouseEvent) +{ + ASSERT(m_isStarted); + + return platformHandleMouseEnterEvent(mouseEvent); +} + +bool NetscapePlugin::handleMouseLeaveEvent(const WebMouseEvent& mouseEvent) +{ + ASSERT(m_isStarted); + + return platformHandleMouseLeaveEvent(mouseEvent); +} + +bool NetscapePlugin::handleContextMenuEvent(const WebMouseEvent&) +{ + // We don't know if the plug-in has handled mousedown event by displaying a context menu, so we never want WebKit to show a default one. + return true; +} + +bool NetscapePlugin::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent) +{ + ASSERT(m_isStarted); + + return platformHandleKeyboardEvent(keyboardEvent); +} + +void NetscapePlugin::setFocus(bool hasFocus) +{ + ASSERT(m_isStarted); + + platformSetFocus(hasFocus); +} + +NPObject* NetscapePlugin::pluginScriptableNPObject() +{ + ASSERT(m_isStarted); + NPObject* scriptableNPObject = 0; + + if (NPP_GetValue(NPPVpluginScriptableNPObject, &scriptableNPObject) != NPERR_NO_ERROR) + return 0; + +#if PLUGIN_ARCHITECTURE(MAC) + if (m_pluginModule->pluginQuirks().contains(PluginQuirks::ReturnsNonRetainedScriptableNPObject)) + retainNPObject(scriptableNPObject); +#endif + + return scriptableNPObject; +} + +void NetscapePlugin::contentsScaleFactorChanged(float scaleFactor) +{ + ASSERT(m_isStarted); + +#if PLUGIN_ARCHITECTURE(MAC) + double contentsScaleFactor = scaleFactor; + NPP_SetValue(NPNVcontentsScaleFactor, &contentsScaleFactor); +#endif +} + +void NetscapePlugin::privateBrowsingStateChanged(bool privateBrowsingEnabled) +{ + ASSERT(m_isStarted); + + // From https://wiki.mozilla.org/Plugins:PrivateMode + // When the browser turns private mode on or off it will call NPP_SetValue for "NPNVprivateModeBool" + // (assigned enum value 18) with a pointer to an NPBool value on all applicable instances. + // Plugins should check the boolean value pointed to, not the pointer itself. + // The value will be true when private mode is on. + NPBool value = privateBrowsingEnabled; + NPP_SetValue(NPNVprivateModeBool, &value); +} + +bool NetscapePlugin::getFormValue(String& formValue) +{ + ASSERT(m_isStarted); + + char* formValueString = 0; + if (NPP_GetValue(NPPVformValue, &formValueString) != NPERR_NO_ERROR) + return false; + + formValue = String::fromUTF8(formValueString); + + // The plug-in allocates the form value string with NPN_MemAlloc so it needs to be freed with NPN_MemFree. + npnMemFree(formValueString); + return true; +} + +bool NetscapePlugin::handleScroll(ScrollDirection, ScrollGranularity) +{ + return false; +} + +Scrollbar* NetscapePlugin::horizontalScrollbar() +{ + return 0; +} + +Scrollbar* NetscapePlugin::verticalScrollbar() +{ + return 0; +} + +bool NetscapePlugin::supportsSnapshotting() const +{ +#if PLATFORM(MAC) + return m_pluginModule && m_pluginModule->pluginQuirks().contains(PluginQuirks::SupportsSnapshotting); +#endif + return false; +} + +IntPoint NetscapePlugin::convertToRootView(const IntPoint& pointInPluginCoordinates) const +{ + return m_pluginToRootViewTransform.mapPoint(pointInPluginCoordinates); +} + +bool NetscapePlugin::convertFromRootView(const IntPoint& pointInRootViewCoordinates, IntPoint& pointInPluginCoordinates) +{ + if (!m_pluginToRootViewTransform.isInvertible()) + return false; + + pointInPluginCoordinates = m_pluginToRootViewTransform.inverse().mapPoint(pointInRootViewCoordinates); + return true; +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.h b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.h new file mode 100644 index 000000000..84541820c --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.h @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef NetscapePlugin_h +#define NetscapePlugin_h + +#include "NetscapePluginModule.h" +#include "Plugin.h" +#include "RunLoop.h" +#include <WebCore/AffineTransform.h> +#include <WebCore/GraphicsLayer.h> +#include <WebCore/IntRect.h> +#include <wtf/HashMap.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + class HTTPHeaderMap; + class ProtectionSpace; +} + +namespace WebKit { + +class NetscapePluginStream; + +class NetscapePlugin : public Plugin { +public: + static PassRefPtr<NetscapePlugin> create(PassRefPtr<NetscapePluginModule> pluginModule); + virtual ~NetscapePlugin(); + + static PassRefPtr<NetscapePlugin> fromNPP(NPP); + +#if PLATFORM(MAC) + NPError setDrawingModel(NPDrawingModel); + NPError setEventModel(NPEventModel); + NPBool convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double& destX, double& destY, NPCoordinateSpace destSpace); + NPError popUpContextMenu(NPMenu*); + + void setPluginReturnsNonretainedLayer(bool pluginReturnsNonretainedLayer) { m_pluginReturnsNonretainedLayer = pluginReturnsNonretainedLayer; } + void setPluginWantsLegacyCocoaTextInput(bool pluginWantsLegacyCocoaTextInput) { m_pluginWantsLegacyCocoaTextInput = pluginWantsLegacyCocoaTextInput; } + + bool hasHandledAKeyDownEvent() const { return m_hasHandledAKeyDownEvent; } + + mach_port_t compositingRenderServerPort(); + + // Computes an affine transform from the given coordinate space to the screen coordinate space. + bool getScreenTransform(NPCoordinateSpace sourceSpace, WebCore::AffineTransform&); + +#ifndef NP_NO_CARBON + WindowRef windowRef() const; + bool isWindowActive() const { return m_windowHasFocus; } + void updateFakeWindowBounds(); + + static NetscapePlugin* netscapePluginFromWindow(WindowRef); + static unsigned buttonState(); +#endif + +#elif PLATFORM(WIN) + HWND containingWindow(); +#endif + + PluginQuirks quirks() const { return m_pluginModule->pluginQuirks(); } + + void invalidate(const NPRect*); + static const char* userAgent(NPP); + void loadURL(const String& method, const String& urlString, const String& target, const WebCore::HTTPHeaderMap& headerFields, + const Vector<uint8_t>& httpBody, bool sendNotification, void* notificationData); + NPError destroyStream(NPStream*, NPReason); + void setIsWindowed(bool); + void setIsTransparent(bool); + void setStatusbarText(const String&); + static void setException(const String&); + bool evaluate(NPObject*, const String&scriptString, NPVariant* result); + bool isPrivateBrowsingEnabled(); + + static void setSetExceptionFunction(void (*)(const String&)); + + // These return retained objects. + NPObject* windowScriptNPObject(); + NPObject* pluginElementNPObject(); + + bool tryToShortCircuitInvoke(NPObject*, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, bool& returnValue, NPVariant& result); + + void cancelStreamLoad(NetscapePluginStream*); + void removePluginStream(NetscapePluginStream*); + + bool isAcceleratedCompositingEnabled(); + + void pushPopupsEnabledState(bool enabled); + void popPopupsEnabledState(); + + void pluginThreadAsyncCall(void (*function)(void*), void* userData); + + // Called on the plug-in run loop (which is currently the main thread run loop). + void handlePluginThreadAsyncCall(void (*function)(void*), void* userData); + + unsigned scheduleTimer(unsigned interval, bool repeat, void (*timerFunc)(NPP, unsigned timerID)); + void unscheduleTimer(unsigned timerID); + + double contentsScaleFactor(); + String proxiesForURL(const String& urlString); + String cookiesForURL(const String& urlString); + void setCookiesForURL(const String& urlString, const String& cookieString); + bool getAuthenticationInfo(const WebCore::ProtectionSpace&, String& username, String& password); + + // Member functions for calling into the plug-in. + NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData*); + NPError NPP_Destroy(NPSavedData**); + NPError NPP_SetWindow(NPWindow*); + NPError NPP_NewStream(NPMIMEType, NPStream*, NPBool seekable, uint16_t* stype); + NPError NPP_DestroyStream(NPStream*, NPReason); + void NPP_StreamAsFile(NPStream*, const char* filename); + int32_t NPP_WriteReady(NPStream*); + int32_t NPP_Write(NPStream*, int32_t offset, int32_t len, void* buffer); + int16_t NPP_HandleEvent(void* event); + void NPP_URLNotify(const char* url, NPReason, void* notifyData); + NPError NPP_GetValue(NPPVariable, void *value); + NPError NPP_SetValue(NPNVariable, void *value); + +private: + NetscapePlugin(PassRefPtr<NetscapePluginModule> pluginModule); + + void callSetWindow(); + bool shouldLoadSrcURL(); + NetscapePluginStream* streamFromID(uint64_t streamID); + void stopAllStreams(); + bool allowPopups() const; + + const char* userAgent(); + + bool platformPostInitialize(); + void platformDestroy(); + bool platformInvalidate(const WebCore::IntRect&); + void platformGeometryDidChange(); + void platformVisibilityDidChange(); + void platformPaint(WebCore::GraphicsContext*, const WebCore::IntRect& dirtyRect, bool isSnapshot = false); + + bool platformHandleMouseEvent(const WebMouseEvent&); + bool platformHandleWheelEvent(const WebWheelEvent&); + bool platformHandleMouseEnterEvent(const WebMouseEvent&); + bool platformHandleMouseLeaveEvent(const WebMouseEvent&); + bool platformHandleKeyboardEvent(const WebKeyboardEvent&); + void platformSetFocus(bool); + + static bool wantsPluginRelativeNPWindowCoordinates(); + + // Plugin + virtual bool initialize(const Parameters&); + virtual void destroy(); + virtual void paint(WebCore::GraphicsContext*, const WebCore::IntRect& dirtyRect); + virtual PassRefPtr<ShareableBitmap> snapshot(); +#if PLATFORM(MAC) + virtual PlatformLayer* pluginLayer(); +#endif + virtual bool isTransparent(); + virtual void geometryDidChange(const WebCore::IntSize& pluginSize, const WebCore::IntRect& clipRect, const WebCore::AffineTransform& pluginToRootViewTransform); + virtual void visibilityDidChange(); + virtual void frameDidFinishLoading(uint64_t requestID); + virtual void frameDidFail(uint64_t requestID, bool wasCancelled); + virtual void didEvaluateJavaScript(uint64_t requestID, const String& result); + virtual void streamDidReceiveResponse(uint64_t streamID, const WebCore::KURL& responseURL, uint32_t streamLength, + uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFileName); + virtual void streamDidReceiveData(uint64_t streamID, const char* bytes, int length); + virtual void streamDidFinishLoading(uint64_t streamID); + virtual void streamDidFail(uint64_t streamID, bool wasCancelled); + virtual void manualStreamDidReceiveResponse(const WebCore::KURL& responseURL, uint32_t streamLength, + uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFileName); + virtual void manualStreamDidReceiveData(const char* bytes, int length); + virtual void manualStreamDidFinishLoading(); + virtual void manualStreamDidFail(bool wasCancelled); + + virtual bool handleMouseEvent(const WebMouseEvent&); + virtual bool handleWheelEvent(const WebWheelEvent&); + virtual bool handleMouseEnterEvent(const WebMouseEvent&); + virtual bool handleMouseLeaveEvent(const WebMouseEvent&); + virtual bool handleContextMenuEvent(const WebMouseEvent&); + virtual bool handleKeyboardEvent(const WebKeyboardEvent&); + virtual void setFocus(bool); + virtual NPObject* pluginScriptableNPObject(); + +#if PLATFORM(MAC) + virtual void windowFocusChanged(bool); + virtual void windowAndViewFramesChanged(const WebCore::IntRect& windowFrameInScreenCoordinates, const WebCore::IntRect& viewFrameInWindowCoordinates); + virtual void windowVisibilityChanged(bool); + + virtual uint64_t pluginComplexTextInputIdentifier() const; + virtual void sendComplexTextInput(const String& textInput); + + void pluginFocusOrWindowFocusChanged(); + void setComplexTextInputEnabled(bool); +#endif + + virtual void contentsScaleFactorChanged(float); + virtual void privateBrowsingStateChanged(bool); + virtual bool getFormValue(String& formValue); + virtual bool handleScroll(WebCore::ScrollDirection, WebCore::ScrollGranularity); + virtual WebCore::Scrollbar* horizontalScrollbar(); + virtual WebCore::Scrollbar* verticalScrollbar(); + + bool supportsSnapshotting() const; + + // Convert the given point from plug-in coordinates to root view coordinates. + WebCore::IntPoint convertToRootView(const WebCore::IntPoint&) const; + + // Convert the given point from root view coordinates to plug-in coordinates. Returns false if the point can't be + // converted (if the transformation matrix isn't invertible). + bool convertFromRootView(const WebCore::IntPoint& pointInRootViewCoordinates, WebCore::IntPoint& pointInPluginCoordinates); + +#if PLUGIN_ARCHITECTURE(WIN) + static BOOL WINAPI hookedTrackPopupMenu(HMENU, UINT uFlags, int x, int y, int nReserved, HWND, const RECT*); + void scheduleWindowedGeometryUpdate(); +#endif + + uint64_t m_nextRequestID; + + typedef HashMap<uint64_t, std::pair<String, void*> > PendingURLNotifyMap; + PendingURLNotifyMap m_pendingURLNotifications; + + typedef HashMap<uint64_t, RefPtr<NetscapePluginStream> > StreamsMap; + StreamsMap m_streams; + + RefPtr<NetscapePluginModule> m_pluginModule; + NPP_t m_npp; + NPWindow m_npWindow; + + WebCore::IntSize m_pluginSize; + + // The clip rect in plug-in coordinates. + WebCore::IntRect m_clipRect; + + // A transform that can be used to convert from root view coordinates to plug-in coordinates. + WebCore::AffineTransform m_pluginToRootViewTransform; + + // FIXME: Get rid of these. + WebCore::IntRect m_frameRectInWindowCoordinates; + + CString m_userAgent; + + bool m_isStarted; + bool m_isWindowed; + bool m_isTransparent; + bool m_inNPPNew; + bool m_loadManually; + RefPtr<NetscapePluginStream> m_manualStream; + Vector<bool, 8> m_popupEnabledStates; + + class Timer { + WTF_MAKE_NONCOPYABLE(Timer); + + public: + typedef void (*TimerFunc)(NPP, uint32_t timerID); + + static PassOwnPtr<Timer> create(NetscapePlugin*, unsigned timerID, unsigned interval, bool repeat, TimerFunc); + ~Timer(); + + void start(); + void stop(); + + private: + Timer(NetscapePlugin*, unsigned timerID, unsigned interval, bool repeat, TimerFunc); + + void timerFired(); + + // This is a weak pointer since Timer objects are destroyed before the NetscapePlugin object itself is destroyed. + NetscapePlugin* m_netscapePlugin; + + unsigned m_timerID; + unsigned m_interval; + bool m_repeat; + TimerFunc m_timerFunc; + + RunLoop::Timer<Timer> m_timer; + }; + typedef HashMap<unsigned, Timer*> TimerMap; + TimerMap m_timers; + unsigned m_nextTimerID; + +#if PLUGIN_ARCHITECTURE(MAC) + NPDrawingModel m_drawingModel; + NPEventModel m_eventModel; + + RetainPtr<PlatformLayer> m_pluginLayer; + bool m_pluginReturnsNonretainedLayer; + + NPCocoaEvent* m_currentMouseEvent; + + bool m_pluginHasFocus; + bool m_windowHasFocus; + + // Whether the plug-in wants to use the legacy Cocoa text input handling that + // existed in WebKit1, or the updated Cocoa text input handling specified on + // https://wiki.mozilla.org/NPAPI:CocoaEventModel#Text_Input + bool m_pluginWantsLegacyCocoaTextInput; + + // Whether complex text input is enabled. + bool m_isComplexTextInputEnabled; + + // Whether the plug-in has handled a keydown event. This is used to determine + // if we can tell the plug-in that we support the updated Cocoa text input specification. + bool m_hasHandledAKeyDownEvent; + + // The number of NPCocoaEventKeyUp events that should be ignored. + unsigned m_ignoreNextKeyUpEventCounter; + + WebCore::IntRect m_windowFrameInScreenCoordinates; + WebCore::IntRect m_viewFrameInWindowCoordinates; + +#ifndef NP_NO_CARBON + void nullEventTimerFired(); + + // FIXME: It's a bit wasteful to have one null event timer per plug-in. + // We should investigate having one per window. + RunLoop::Timer<NetscapePlugin> m_nullEventTimer; + NP_CGContext m_npCGContext; +#endif +#elif PLUGIN_ARCHITECTURE(WIN) + HWND m_window; + HWND m_contextMenuOwnerWindow; +#elif PLUGIN_ARCHITECTURE(X11) + Pixmap m_drawable; + Display* m_pluginDisplay; + +public: // Need to call it in the NPN_GetValue browser callback. + static Display* x11HostDisplay(); +#endif +}; + +} // namespace WebKit + +#endif // NetscapePlugin_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginNone.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginNone.cpp new file mode 100644 index 000000000..b5cf547b4 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginNone.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 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 INC. 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 INC. 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. + */ + +#include "config.h" +#if PLUGIN_ARCHITECTURE(UNSUPPORTED) + +#include "NetscapePlugin.h" + +using namespace WebCore; + +namespace WebKit { + +bool NetscapePlugin::platformPostInitialize() +{ + return false; +} + +void NetscapePlugin::platformDestroy() +{ +} + +bool NetscapePlugin::platformInvalidate(const IntRect&) +{ + return false; +} + +void NetscapePlugin::platformGeometryDidChange() +{ +} + +void NetscapePlugin::platformVisibilityDidChange() +{ +} + +void NetscapePlugin::platformPaint(GraphicsContext*, const IntRect&, bool) +{ +} + +bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent&) +{ + return false; +} + +bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent&) +{ + return false; +} + +void NetscapePlugin::platformSetFocus(bool) +{ +} + +bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent&) +{ + return false; +} + +bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent&) +{ + return false; +} + +bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& event) +{ + return false; +} + +bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates() +{ + return true; +} + +} // namespace WebKit + +#endif // PLUGIN_ARCHITECTURE(UNSUPPORTED) diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginStream.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginStream.cpp new file mode 100644 index 000000000..23a83fe6d --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginStream.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "NetscapePluginStream.h" + +#include "NetscapePlugin.h" +#include <utility> + +using namespace WebCore; +using namespace std; + +namespace WebKit { + +NetscapePluginStream::NetscapePluginStream(PassRefPtr<NetscapePlugin> plugin, uint64_t streamID, const String& requestURLString, bool sendNotification, void* notificationData) + : m_plugin(plugin) + , m_streamID(streamID) + , m_requestURLString(requestURLString) + , m_sendNotification(sendNotification) + , m_notificationData(notificationData) + , m_npStream() + , m_transferMode(NP_NORMAL) + , m_offset(0) + , m_fileHandle(invalidPlatformFileHandle) + , m_isStarted(false) +#if !ASSERT_DISABLED + , m_urlNotifyHasBeenCalled(false) +#endif + , m_deliveryDataTimer(RunLoop::main(), this, &NetscapePluginStream::deliverDataToPlugin) + , m_stopStreamWhenDoneDelivering(false) +{ +} + +NetscapePluginStream::~NetscapePluginStream() +{ + ASSERT(!m_isStarted); + ASSERT(!m_sendNotification || m_urlNotifyHasBeenCalled); + ASSERT(m_fileHandle == invalidPlatformFileHandle); +} + +void NetscapePluginStream::didReceiveResponse(const KURL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) +{ + // Starting the stream could cause the plug-in stream to go away so we keep a reference to it here. + RefPtr<NetscapePluginStream> protect(this); + + start(responseURL, streamLength, lastModifiedTime, mimeType, headers); +} + +void NetscapePluginStream::didReceiveData(const char* bytes, int length) +{ + // Delivering the data could cause the plug-in stream to go away so we keep a reference to it here. + RefPtr<NetscapePluginStream> protect(this); + + deliverData(bytes, length); +} + +void NetscapePluginStream::didFinishLoading() +{ + // Stopping the stream could cause the plug-in stream to go away so we keep a reference to it here. + RefPtr<NetscapePluginStream> protect(this); + + stop(NPRES_DONE); +} + +void NetscapePluginStream::didFail(bool wasCancelled) +{ + // Stopping the stream could cause the plug-in stream to go away so we keep a reference to it here. + RefPtr<NetscapePluginStream> protect(this); + + stop(wasCancelled ? NPRES_USER_BREAK : NPRES_NETWORK_ERR); +} + +void NetscapePluginStream::sendJavaScriptStream(const String& result) +{ + // starting the stream or delivering the data to it might cause the plug-in stream to go away, so we keep + // a reference to it here. + RefPtr<NetscapePluginStream> protect(this); + + CString resultCString = result.utf8(); + if (resultCString.isNull()) { + // There was an error evaluating the JavaScript, call NPP_URLNotify if needed and then destroy the stream. + notifyAndDestroyStream(NPRES_NETWORK_ERR); + return; + } + + if (!start(m_requestURLString, resultCString.length(), 0, "text/plain", "")) + return; + + deliverData(resultCString.data(), resultCString.length()); + stop(NPRES_DONE); +} + +NPError NetscapePluginStream::destroy(NPReason reason) +{ + // It doesn't make sense to call NPN_DestroyStream on a stream that hasn't been started yet. + if (!m_isStarted) + return NPERR_GENERIC_ERROR; + + // It isn't really valid for a plug-in to call NPN_DestroyStream with NPRES_DONE. + // (At least not for browser initiated streams, and we don't support plug-in initiated streams). + if (reason == NPRES_DONE) + return NPERR_INVALID_PARAM; + + cancel(); + stop(reason); + return NPERR_NO_ERROR; +} + +static bool isSupportedTransferMode(uint16_t transferMode) +{ + switch (transferMode) { + case NP_ASFILEONLY: + case NP_ASFILE: + case NP_NORMAL: + return true; + // FIXME: We don't support seekable streams. + case NP_SEEK: + return false; + } + + ASSERT_NOT_REACHED(); + return false; +} + +bool NetscapePluginStream::start(const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) +{ + m_responseURL = responseURLString.utf8(); + m_mimeType = mimeType.utf8(); + m_headers = headers.utf8(); + + m_npStream.ndata = this; + m_npStream.url = m_responseURL.data(); + m_npStream.end = streamLength; + m_npStream.lastmodified = lastModifiedTime; + m_npStream.notifyData = m_notificationData; + m_npStream.headers = m_headers.length() == 0 ? 0 : m_headers.data(); + + NPError error = m_plugin->NPP_NewStream(const_cast<char*>(m_mimeType.data()), &m_npStream, false, &m_transferMode); + if (error != NPERR_NO_ERROR) { + // We failed to start the stream, cancel the load and destroy it. + cancel(); + notifyAndDestroyStream(NPRES_NETWORK_ERR); + return false; + } + + // We successfully started the stream. + m_isStarted = true; + + if (!isSupportedTransferMode(m_transferMode)) { + // Cancel the load and stop the stream. + cancel(); + stop(NPRES_NETWORK_ERR); + return false; + } + + return true; +} + +void NetscapePluginStream::deliverData(const char* bytes, int length) +{ + ASSERT(m_isStarted); + + if (m_transferMode != NP_ASFILEONLY) { + if (!m_deliveryData) + m_deliveryData = adoptPtr(new Vector<uint8_t>); + + m_deliveryData->reserveCapacity(m_deliveryData->size() + length); + m_deliveryData->append(bytes, length); + + deliverDataToPlugin(); + } + + if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) + deliverDataToFile(bytes, length); +} + +void NetscapePluginStream::deliverDataToPlugin() +{ + ASSERT(m_isStarted); + + int32_t numBytesToDeliver = m_deliveryData->size(); + int32_t numBytesDelivered = 0; + + while (numBytesDelivered < numBytesToDeliver) { + int32_t numBytesPluginCanHandle = m_plugin->NPP_WriteReady(&m_npStream); + + // NPP_WriteReady could call NPN_DestroyStream and destroy the stream. + if (!m_isStarted) + return; + + if (numBytesPluginCanHandle <= 0) { + // The plug-in can't handle more data, we'll send the rest later + m_deliveryDataTimer.startOneShot(0); + break; + } + + // Figure out how much data to send to the plug-in. + int32_t dataLength = min(numBytesPluginCanHandle, numBytesToDeliver - numBytesDelivered); + uint8_t* data = m_deliveryData->data() + numBytesDelivered; + + int32_t numBytesWritten = m_plugin->NPP_Write(&m_npStream, m_offset, dataLength, data); + if (numBytesWritten < 0) { + cancel(); + stop(NPRES_NETWORK_ERR); + return; + } + + // NPP_Write could call NPN_DestroyStream and destroy the stream. + if (!m_isStarted) + return; + + numBytesWritten = min(numBytesWritten, dataLength); + m_offset += numBytesWritten; + numBytesDelivered += numBytesWritten; + } + + // We didn't write anything. + if (!numBytesDelivered) + return; + + if (numBytesDelivered < numBytesToDeliver) { + // Remove the bytes that we actually delivered. + m_deliveryData->remove(0, numBytesDelivered); + } else { + m_deliveryData->clear(); + + if (m_stopStreamWhenDoneDelivering) + stop(NPRES_DONE); + } +} + +void NetscapePluginStream::deliverDataToFile(const char* bytes, int length) +{ + if (m_fileHandle == invalidPlatformFileHandle && m_filePath.isNull()) { + // Create a temporary file. + m_filePath = openTemporaryFile("WebKitPluginStream", m_fileHandle); + + // We failed to open the file, stop the stream. + if (m_fileHandle == invalidPlatformFileHandle) { + stop(NPRES_NETWORK_ERR); + return; + } + } + + if (!length) + return; + + int byteCount = writeToFile(m_fileHandle, bytes, length); + if (byteCount != length) { + // This happens only rarely, when we are out of disk space or have a disk I/O error. + closeFile(m_fileHandle); + + stop(NPRES_NETWORK_ERR); + } +} + +void NetscapePluginStream::stop(NPReason reason) +{ + // The stream was stopped before it got a chance to start. This can happen if a stream is cancelled by + // WebKit before it received a response. + if (!m_isStarted) { + ASSERT(reason != NPRES_DONE); + notifyAndDestroyStream(reason); + return; + } + + if (reason == NPRES_DONE && m_deliveryData && !m_deliveryData->isEmpty()) { + // There is still data left that the plug-in hasn't been able to consume yet. + ASSERT(m_deliveryDataTimer.isActive()); + + // Set m_stopStreamWhenDoneDelivering to true so that the next time the delivery timer fires + // and calls deliverDataToPlugin the stream will be closed if all the remaining data was + // successfully delivered. + m_stopStreamWhenDoneDelivering = true; + return; + } + + m_deliveryData = nullptr; + m_deliveryDataTimer.stop(); + + if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) { + if (reason == NPRES_DONE) { + // Ensure that the file is created. + deliverDataToFile(0, 0); + if (m_fileHandle != invalidPlatformFileHandle) + closeFile(m_fileHandle); + + ASSERT(!m_filePath.isNull()); + + m_plugin->NPP_StreamAsFile(&m_npStream, m_filePath.utf8().data()); + } else { + // Just close the file. + if (m_fileHandle != invalidPlatformFileHandle) + closeFile(m_fileHandle); + } + + // Delete the file after calling NPP_StreamAsFile(), instead of in the destructor. It should be OK + // to delete the file here -- NPP_StreamAsFile() is always called immediately before NPP_DestroyStream() + // (the stream destruction function), so there can be no expectation that a plugin will read the stream + // file asynchronously after NPP_StreamAsFile() is called. + deleteFile(m_filePath); + m_filePath = String(); + + // NPP_StreamAsFile could call NPN_DestroyStream and destroy the stream. + if (!m_isStarted) + return; + } + + // Set m_isStarted to false before calling NPP_DestroyStream in case NPP_DestroyStream calls NPN_DestroyStream. + m_isStarted = false; + + m_plugin->NPP_DestroyStream(&m_npStream, reason); + + notifyAndDestroyStream(reason); +} + +void NetscapePluginStream::cancel() +{ + m_plugin->cancelStreamLoad(this); +} + +void NetscapePluginStream::notifyAndDestroyStream(NPReason reason) +{ + ASSERT(!m_isStarted); + ASSERT(!m_deliveryDataTimer.isActive()); + ASSERT(!m_urlNotifyHasBeenCalled); + + if (m_sendNotification) { + m_plugin->NPP_URLNotify(m_requestURLString.utf8().data(), reason, m_notificationData); + +#if !ASSERT_DISABLED + m_urlNotifyHasBeenCalled = true; +#endif + } + + m_plugin->removePluginStream(this); +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginStream.h b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginStream.h new file mode 100644 index 000000000..de26827fc --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePluginStream.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#ifndef NetscapePluginStream_h +#define NetscapePluginStream_h + +#include "RunLoop.h" +#include <WebCore/FileSystem.h> +#include <WebCore/npruntime_internal.h> +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + class KURL; +} + +namespace WebKit { + +class NetscapePlugin; + +class NetscapePluginStream : public RefCounted<NetscapePluginStream> { +public: + static PassRefPtr<NetscapePluginStream> create(PassRefPtr<NetscapePlugin> plugin, uint64_t streamID, const String& requestURLString, bool sendNotification, void* notificationData) + { + return adoptRef(new NetscapePluginStream(plugin, streamID, requestURLString, sendNotification, notificationData)); + } + ~NetscapePluginStream(); + + uint64_t streamID() const { return m_streamID; } + const NPStream* npStream() const { return &m_npStream; } + + void didReceiveResponse(const WebCore::KURL& responseURL, uint32_t streamLength, + uint32_t lastModifiedTime, const String& mimeType, const String& headers); + void didReceiveData(const char* bytes, int length); + void didFinishLoading(); + void didFail(bool wasCancelled); + + void sendJavaScriptStream(const String& result); + + void stop(NPReason); + NPError destroy(NPReason); + +private: + NetscapePluginStream(PassRefPtr<NetscapePlugin>, uint64_t streamID, const String& requestURLString, bool sendNotification, void* notificationData); + + bool start(const String& responseURLString, uint32_t streamLength, + uint32_t lastModifiedTime, const String& mimeType, const String& headers); + + void cancel(); + void notifyAndDestroyStream(NPReason); + + void deliverData(const char* bytes, int length); + void deliverDataToPlugin(); + void deliverDataToFile(const char* bytes, int length); + + RefPtr<NetscapePlugin> m_plugin; + uint64_t m_streamID; + + String m_requestURLString; + bool m_sendNotification; + void* m_notificationData; + + NPStream m_npStream; + uint16_t m_transferMode; + int32_t m_offset; + + String m_filePath; + WebCore::PlatformFileHandle m_fileHandle; + + // Whether NPP_NewStream has successfully been called. + bool m_isStarted; + +#if !ASSERT_DISABLED + bool m_urlNotifyHasBeenCalled; +#endif + + CString m_responseURL; + CString m_mimeType; + CString m_headers; + + RunLoop::Timer<NetscapePluginStream> m_deliveryDataTimer; + OwnPtr< Vector<uint8_t> > m_deliveryData; + bool m_stopStreamWhenDoneDelivering; +}; + +} // namespace WebKit + +#endif // NetscapePluginStream_h diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/gtk/PluginProxyGtk.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/gtk/PluginProxyGtk.cpp new file mode 100644 index 000000000..996af20ca --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/gtk/PluginProxyGtk.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "PluginProxy.h" + +#if ENABLE(PLUGIN_PROCESS) + +#include <WebCore/NotImplemented.h> + +namespace WebKit { + +bool PluginProxy::needsBackingStore() const +{ + notImplemented(); + return true; +} + +} // namespace WebKit + +#endif // ENABLE(PLUGIN_PROCESS) diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/mac/NetscapePluginMac.mm b/Source/WebKit2/WebProcess/Plugins/Netscape/mac/NetscapePluginMac.mm new file mode 100644 index 000000000..7e436958c --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/mac/NetscapePluginMac.mm @@ -0,0 +1,1072 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "NetscapePlugin.h" + +#import "PluginController.h" +#import "WebEvent.h" +#import <Carbon/Carbon.h> +#import <WebCore/GraphicsContext.h> +#import <WebCore/NotImplemented.h> +#import <WebKitSystemInterface.h> + +using namespace WebCore; + +namespace WebKit { + +#ifndef NP_NO_CARBON +static const double nullEventIntervalActive = 0.02; +static const double nullEventIntervalNotActive = 0.25; + +static unsigned buttonStateFromLastMouseEvent; + +#endif + +NPError NetscapePlugin::setDrawingModel(NPDrawingModel drawingModel) +{ + // The drawing model can only be set from NPP_New. + if (!m_inNPPNew) + return NPERR_GENERIC_ERROR; + + switch (drawingModel) { +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: +#endif + case NPDrawingModelCoreGraphics: + case NPDrawingModelCoreAnimation: + m_drawingModel = drawingModel; + break; + + default: + return NPERR_GENERIC_ERROR; + } + + return NPERR_NO_ERROR; +} + +NPError NetscapePlugin::setEventModel(NPEventModel eventModel) +{ + // The event model can only be set from NPP_New. + if (!m_inNPPNew) + return NPERR_GENERIC_ERROR; + + switch (eventModel) { +#ifndef NP_NO_CARBON + case NPEventModelCarbon: +#endif + case NPEventModelCocoa: + m_eventModel = eventModel; + break; + + default: + return NPERR_GENERIC_ERROR; + } + + return NPERR_NO_ERROR; +} + +bool NetscapePlugin::getScreenTransform(NPCoordinateSpace sourceSpace, AffineTransform& transform) +{ + ASSERT(transform.isIdentity()); + + switch (sourceSpace) { + case NPCoordinateSpacePlugin: { + transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y()); + transform.translate(m_viewFrameInWindowCoordinates.x(), m_viewFrameInWindowCoordinates.height() + m_viewFrameInWindowCoordinates.y()); + transform.flipY(); + transform *= m_pluginToRootViewTransform; + return true; + } + + case NPCoordinateSpaceWindow: { + transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y()); + return true; + } + + case NPCoordinateSpaceFlippedWindow: { + transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.height() + m_windowFrameInScreenCoordinates.y()); + transform.flipY(); + return true; + } + + case NPCoordinateSpaceScreen: { + // Nothing to do. + return true; + } + + case NPCoordinateSpaceFlippedScreen: { + double screenHeight = [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height; + transform.translate(0, screenHeight); + transform.flipY(); + return true; + } + + default: + return false; + } +} + +NPBool NetscapePlugin::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double& destX, double& destY, NPCoordinateSpace destSpace) +{ + AffineTransform sourceTransform; + if (!getScreenTransform(sourceSpace, sourceTransform)) + return false; + + AffineTransform destTransform; + if (!getScreenTransform(destSpace, destTransform)) + return false; + + if (!destTransform.isInvertible()) + return false; + + AffineTransform transform = destTransform.inverse() * sourceTransform; + + FloatPoint destinationPoint = transform.mapPoint(FloatPoint(sourceX, sourceY)); + + destX = destinationPoint.x(); + destY = destinationPoint.y(); + return true; +} + + +NPError NetscapePlugin::popUpContextMenu(NPMenu* npMenu) +{ + if (!m_currentMouseEvent) + return NPERR_GENERIC_ERROR; + + double screenX, screenY; + if (!convertPoint(m_currentMouseEvent->data.mouse.pluginX, m_currentMouseEvent->data.mouse.pluginY, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceScreen)) + ASSERT_NOT_REACHED(); + + WKPopupContextMenu(reinterpret_cast<NSMenu *>(npMenu), NSMakePoint(screenX, screenY)); + return NPERR_NO_ERROR; +} + +mach_port_t NetscapePlugin::compositingRenderServerPort() +{ + return controller()->compositingRenderServerPort(); +} + +#ifndef NP_NO_CARBON +typedef HashMap<WindowRef, NetscapePlugin*> WindowMap; + +static WindowMap& windowMap() +{ + DEFINE_STATIC_LOCAL(WindowMap, windowMap, ()); + + return windowMap; +} +#endif + +bool NetscapePlugin::platformPostInitialize() +{ + if (m_drawingModel == static_cast<NPDrawingModel>(-1)) { +#ifndef NP_NO_QUICKDRAW + // Default to QuickDraw if the plugin did not specify a drawing model. + m_drawingModel = NPDrawingModelQuickDraw; +#else + // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics. + m_drawingModel = NPDrawingModelCoreGraphics; +#endif + } + + if (m_eventModel == static_cast<NPEventModel>(-1)) { + // If the plug-in did not specify a drawing model we default to Carbon when it is available. +#ifndef NP_NO_CARBON + m_eventModel = NPEventModelCarbon; +#else + m_eventModel = NPEventModelCocoa; +#endif // NP_NO_CARBON + } + +#if !defined(NP_NO_CARBON) && !defined(NP_NO_QUICKDRAW) + // The CA drawing model does not work with the Carbon event model. + if (m_drawingModel == NPDrawingModelCoreAnimation && m_eventModel == NPEventModelCarbon) + return false; + + // The Cocoa event model does not work with the QuickDraw drawing model. + if (m_eventModel == NPEventModelCocoa && m_drawingModel == NPDrawingModelQuickDraw) + return false; +#endif + +#ifndef NP_NO_QUICKDRAW + // Right now we don't support the QuickDraw drawing model at all + if (m_drawingModel == NPDrawingModelQuickDraw && + !m_pluginModule->pluginQuirks().contains(PluginQuirks::AllowHalfBakedQuickDrawSupport)) + return false; +#endif + + if (m_drawingModel == NPDrawingModelCoreAnimation) { + void* value = 0; + // Get the Core Animation layer. + if (NPP_GetValue(NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) { + ASSERT(!m_pluginLayer); + + // The original Core Animation drawing model required that plug-ins pass a retained layer + // to the browser, which the browser would then adopt. However, the final spec changed this + // (See https://wiki.mozilla.org/NPAPI:CoreAnimationDrawingModel for more information) + // after a version of WebKit1 with the original implementation had shipped, but that now means + // that any plug-ins that expect the WebKit1 behavior would leak the CALayer. + // For plug-ins that we know return retained layers, we have the ReturnsRetainedCoreAnimationLayer + // plug-in quirk. Plug-ins can also check for whether the browser expects a non-retained layer to + // be returned by using NPN_GetValue and pass the WKNVExpectsNonretainedLayer parameter. + // https://bugs.webkit.org/show_bug.cgi?id=58282 describes the bug where WebKit expects retained layers. + if (m_pluginReturnsNonretainedLayer) + m_pluginLayer = reinterpret_cast<CALayer *>(value); + else + m_pluginLayer.adoptNS(reinterpret_cast<CALayer *>(value)); + } + } + +#ifndef NP_NO_CARBON + if (m_eventModel == NPEventModelCarbon) { + // Initialize the fake Carbon window. + ::Rect bounds = { 0, 0, 0, 0 }; + CreateNewWindow(kDocumentWindowClass, kWindowNoTitleBarAttribute, &bounds, reinterpret_cast<WindowRef*>(&m_npCGContext.window)); + ASSERT(m_npCGContext.window); + + // FIXME: Disable the backing store. + + m_npWindow.window = &m_npCGContext; + + ASSERT(!windowMap().contains(windowRef())); + windowMap().set(windowRef(), this); + + // Start the null event timer. + // FIXME: Throttle null events when the plug-in isn't visible on screen. + m_nullEventTimer.startRepeating(nullEventIntervalActive); + } +#endif + + return true; +} + +void NetscapePlugin::platformDestroy() +{ +#ifndef NP_NO_CARBON + if (m_eventModel == NPEventModelCarbon) { + if (WindowRef window = windowRef()) { + // Destroy the fake Carbon window. + DisposeWindow(window); + + ASSERT(windowMap().contains(window)); + windowMap().remove(window); + } + + // Stop the null event timer. + m_nullEventTimer.stop(); + } +#endif +} + +bool NetscapePlugin::platformInvalidate(const IntRect&) +{ + // NPN_InvalidateRect is just a no-op in the Core Animation drawing model. + if (m_drawingModel == NPDrawingModelCoreAnimation) + return true; + + return false; +} + +void NetscapePlugin::platformGeometryDidChange() +{ + switch (m_eventModel) { + case NPEventModelCocoa: + // Nothing to do + break; +#ifndef NP_NO_CARBON + case NPEventModelCarbon: + updateFakeWindowBounds(); + break; +#endif + default: + ASSERT_NOT_REACHED(); + } +} + +void NetscapePlugin::platformVisibilityDidChange() +{ + // FIXME: Implement this. <http://webkit.org/b/44368>. + notImplemented(); +} + +static inline NPCocoaEvent initializeEvent(NPCocoaEventType type) +{ + NPCocoaEvent event; + + event.type = type; + event.version = 0; + + return event; +} + +#ifndef NP_NO_CARBON +NetscapePlugin* NetscapePlugin::netscapePluginFromWindow(WindowRef windowRef) +{ + return windowMap().get(windowRef); +} + +WindowRef NetscapePlugin::windowRef() const +{ + ASSERT(m_eventModel == NPEventModelCarbon); + + return reinterpret_cast<WindowRef>(m_npCGContext.window); +} + +void NetscapePlugin::updateFakeWindowBounds() +{ + double screenX, screenY; + bool didConvert = convertPoint(0, 0, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceFlippedScreen); + ASSERT_UNUSED(didConvert, didConvert); + + Rect bounds; + bounds.top = screenY; + bounds.left = screenX; + bounds.bottom = screenY + m_pluginSize.height(); + bounds.right = screenX + m_pluginSize.width(); + + ::SetWindowBounds(windowRef(), kWindowStructureRgn, &bounds); +} + +unsigned NetscapePlugin::buttonState() +{ + return buttonStateFromLastMouseEvent; +} + +static inline EventRecord initializeEventRecord(EventKind eventKind) +{ + EventRecord eventRecord; + + eventRecord.what = eventKind; + eventRecord.message = 0; + eventRecord.when = TickCount(); + eventRecord.where = Point(); + eventRecord.modifiers = 0; + + return eventRecord; +} + +static bool anyMouseButtonIsDown(const WebEvent& event) +{ + if (event.type() == WebEvent::MouseDown) + return true; + + if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() != WebMouseEvent::NoButton) + return true; + + return false; +} + +static bool rightMouseButtonIsDown(const WebEvent& event) +{ + if (event.type() == WebEvent::MouseDown && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton) + return true; + + if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton) + return true; + + return false; +} + +static EventModifiers modifiersForEvent(const WebEvent& event) +{ + EventModifiers modifiers = 0; + + // We only want to set the btnState if a mouse button is _not_ down. + if (!anyMouseButtonIsDown(event)) + modifiers |= btnState; + + if (event.metaKey()) + modifiers |= cmdKey; + + if (event.shiftKey()) + modifiers |= shiftKey; + + if (event.altKey()) + modifiers |= optionKey; + + // Set controlKey if the control key is down or the right mouse button is down. + if (event.controlKey() || rightMouseButtonIsDown(event)) + modifiers |= controlKey; + + return modifiers; +} + +#endif + +void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool isSnapshot) +{ + CGContextRef platformContext = context->platformContext(); + + switch (m_eventModel) { + case NPEventModelCocoa: { + // Don't send draw events when we're using the Core Animation drawing model. + if (!isSnapshot && m_drawingModel == NPDrawingModelCoreAnimation) + return; + + NPCocoaEvent event = initializeEvent(NPCocoaEventDrawRect); + + event.data.draw.context = platformContext; + event.data.draw.x = dirtyRect.x(); + event.data.draw.y = dirtyRect.y(); + event.data.draw.width = dirtyRect.width(); + event.data.draw.height = dirtyRect.height(); + + NPP_HandleEvent(&event); + break; + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + if (platformContext != m_npCGContext.context) { + m_npCGContext.context = platformContext; + callSetWindow(); + } + + EventRecord event = initializeEventRecord(updateEvt); + event.message = reinterpret_cast<unsigned long>(windowRef()); + + NPP_HandleEvent(&event); + break; + } +#endif + + default: + ASSERT_NOT_REACHED(); + } +} + +static uint32_t modifierFlags(const WebEvent& event) +{ + uint32_t modifiers = 0; + + if (event.shiftKey()) + modifiers |= NSShiftKeyMask; + if (event.controlKey()) + modifiers |= NSControlKeyMask; + if (event.altKey()) + modifiers |= NSAlternateKeyMask; + if (event.metaKey()) + modifiers |= NSCommandKeyMask; + + return modifiers; +} + +static int32_t buttonNumber(WebMouseEvent::Button button) +{ + switch (button) { + case WebMouseEvent::NoButton: + case WebMouseEvent::LeftButton: + return 0; + case WebMouseEvent::RightButton: + return 1; + case WebMouseEvent::MiddleButton: + return 2; + } + + ASSERT_NOT_REACHED(); + return -1; +} + +static void fillInCocoaEventFromMouseEvent(NPCocoaEvent& event, const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates) +{ + event.data.mouse.modifierFlags = modifierFlags(mouseEvent); + event.data.mouse.pluginX = eventPositionInPluginCoordinates.x(); + event.data.mouse.pluginY = eventPositionInPluginCoordinates.y(); + event.data.mouse.buttonNumber = buttonNumber(mouseEvent.button()); + event.data.mouse.clickCount = mouseEvent.clickCount(); + event.data.mouse.deltaX = mouseEvent.deltaX(); + event.data.mouse.deltaY = mouseEvent.deltaY(); + event.data.mouse.deltaZ = mouseEvent.deltaZ(); +} + +static NPCocoaEvent initializeMouseEvent(const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates) +{ + NPCocoaEventType eventType; + + switch (mouseEvent.type()) { + case WebEvent::MouseDown: + eventType = NPCocoaEventMouseDown; + break; + case WebEvent::MouseUp: + eventType = NPCocoaEventMouseUp; + break; + case WebEvent::MouseMove: + if (mouseEvent.button() == WebMouseEvent::NoButton) + eventType = NPCocoaEventMouseMoved; + else + eventType = NPCocoaEventMouseDragged; + break; + default: + ASSERT_NOT_REACHED(); + return NPCocoaEvent(); + } + + NPCocoaEvent event = initializeEvent(eventType); + fillInCocoaEventFromMouseEvent(event, mouseEvent, eventPositionInPluginCoordinates); + return event; +} + +bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& mouseEvent) +{ + switch (m_eventModel) { + case NPEventModelCocoa: { + IntPoint eventPositionInPluginCoordinates; + if (!convertFromRootView(mouseEvent.position(), eventPositionInPluginCoordinates)) + return true; + + NPCocoaEvent event = initializeMouseEvent(mouseEvent, eventPositionInPluginCoordinates); + + NPCocoaEvent* previousMouseEvent = m_currentMouseEvent; + m_currentMouseEvent = &event; + + // Protect against NPP_HandleEvent causing the plug-in to be destroyed, since we + // access m_currentMouseEvent afterwards. + RefPtr<NetscapePlugin> protect(this); + + NPP_HandleEvent(&event); + + m_currentMouseEvent = previousMouseEvent; + + // Some plug-ins return false even if the mouse event has been handled. + // This leads to bugs such as <rdar://problem/9167611>. Work around this + // by always returning true. + return true; + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + EventKind eventKind = nullEvent; + + switch (mouseEvent.type()) { + case WebEvent::MouseDown: + eventKind = mouseDown; + buttonStateFromLastMouseEvent |= (1 << buttonNumber(mouseEvent.button())); + break; + case WebEvent::MouseUp: + eventKind = mouseUp; + buttonStateFromLastMouseEvent &= ~(1 << buttonNumber(mouseEvent.button())); + break; + case WebEvent::MouseMove: + eventKind = nullEvent; + break; + default: + ASSERT_NOT_REACHED(); + } + + EventRecord event = initializeEventRecord(eventKind); + event.modifiers = modifiersForEvent(mouseEvent); + event.where.h = mouseEvent.globalPosition().x(); + event.where.v = mouseEvent.globalPosition().y(); + + NPP_HandleEvent(&event); + + // Some plug-ins return false even if the mouse event has been handled. + // This leads to bugs such as <rdar://problem/9167611>. Work around this + // by always returning true. + return true; + } +#endif + + default: + ASSERT_NOT_REACHED(); + } + + return false; +} + +bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& wheelEvent) +{ + switch (m_eventModel) { + case NPEventModelCocoa: { + IntPoint eventPositionInPluginCoordinates; + if (!convertFromRootView(wheelEvent.position(), eventPositionInPluginCoordinates)) + return true; + + NPCocoaEvent event = initializeEvent(NPCocoaEventScrollWheel); + + event.data.mouse.modifierFlags = modifierFlags(wheelEvent); + event.data.mouse.pluginX = eventPositionInPluginCoordinates.x(); + event.data.mouse.pluginY = eventPositionInPluginCoordinates.y(); + event.data.mouse.buttonNumber = 0; + event.data.mouse.clickCount = 0; + event.data.mouse.deltaX = wheelEvent.delta().width(); + event.data.mouse.deltaY = wheelEvent.delta().height(); + event.data.mouse.deltaZ = 0; + return NPP_HandleEvent(&event); + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: + // Carbon doesn't have wheel events. + break; +#endif + + default: + ASSERT_NOT_REACHED(); + } + + return false; +} + +bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& mouseEvent) +{ + switch (m_eventModel) { + case NPEventModelCocoa: { + NPCocoaEvent event = initializeEvent(NPCocoaEventMouseEntered); + + fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint()); + return NPP_HandleEvent(&event); + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent); + eventRecord.modifiers = modifiersForEvent(mouseEvent); + + return NPP_HandleEvent(&eventRecord); + } +#endif + + default: + ASSERT_NOT_REACHED(); + } + + return false; +} + +bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& mouseEvent) +{ + switch (m_eventModel) { + case NPEventModelCocoa: { + NPCocoaEvent event = initializeEvent(NPCocoaEventMouseExited); + + fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint()); + return NPP_HandleEvent(&event); + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent); + eventRecord.modifiers = modifiersForEvent(mouseEvent); + + return NPP_HandleEvent(&eventRecord); + } +#endif + + default: + ASSERT_NOT_REACHED(); + } + + return false; +} + +static unsigned modifierFlags(const WebKeyboardEvent& keyboardEvent) +{ + unsigned modifierFlags = 0; + + if (keyboardEvent.capsLockKey()) + modifierFlags |= NSAlphaShiftKeyMask; + if (keyboardEvent.shiftKey()) + modifierFlags |= NSShiftKeyMask; + if (keyboardEvent.controlKey()) + modifierFlags |= NSControlKeyMask; + if (keyboardEvent.altKey()) + modifierFlags |= NSAlternateKeyMask; + if (keyboardEvent.metaKey()) + modifierFlags |= NSCommandKeyMask; + + return modifierFlags; +} + +static bool isFlagsChangedEvent(const WebKeyboardEvent& keyboardEvent) +{ + switch (keyboardEvent.nativeVirtualKeyCode()) { + case 54: // Right Command + case 55: // Left Command + + case 57: // Capslock + + case 56: // Left Shift + case 60: // Right Shift + + case 58: // Left Alt + case 61: // Right Alt + + case 59: // Left Ctrl + case 62: // Right Ctrl + return true; + } + + return false; +} + +static NPCocoaEvent initializeKeyboardEvent(const WebKeyboardEvent& keyboardEvent) +{ + NPCocoaEventType eventType; + + if (isFlagsChangedEvent(keyboardEvent)) + eventType = NPCocoaEventFlagsChanged; + else { + switch (keyboardEvent.type()) { + case WebEvent::KeyDown: + eventType = NPCocoaEventKeyDown; + break; + case WebEvent::KeyUp: + eventType = NPCocoaEventKeyUp; + break; + default: + ASSERT_NOT_REACHED(); + return NPCocoaEvent(); + } + } + + NPCocoaEvent event = initializeEvent(eventType); + event.data.key.modifierFlags = modifierFlags(keyboardEvent); + event.data.key.characters = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.text())); + event.data.key.charactersIgnoringModifiers = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.unmodifiedText())); + event.data.key.isARepeat = keyboardEvent.isAutoRepeat(); + event.data.key.keyCode = keyboardEvent.nativeVirtualKeyCode(); + + return event; +} + +bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& keyboardEvent) +{ + bool handled = false; + + switch (m_eventModel) { + case NPEventModelCocoa: { + if (keyboardEvent.type() == WebEvent::KeyDown) { + m_hasHandledAKeyDownEvent = true; + + if (!m_pluginWantsLegacyCocoaTextInput && m_isComplexTextInputEnabled && !keyboardEvent.isAutoRepeat()) { + // When complex text is enabled in the new model, the plug-in should never + // receive any key down or key up events until the composition is complete. + m_ignoreNextKeyUpEventCounter++; + return true; + } + } else if (keyboardEvent.type() == WebEvent::KeyUp && m_ignoreNextKeyUpEventCounter) { + m_ignoreNextKeyUpEventCounter--; + return true; + } + + NPCocoaEvent event = initializeKeyboardEvent(keyboardEvent); + int16_t returnValue = NPP_HandleEvent(&event); + handled = returnValue; + + if (!m_pluginWantsLegacyCocoaTextInput) { + if (event.type == NPCocoaEventKeyDown && returnValue == kNPEventStartIME) { + if (!keyboardEvent.isAutoRepeat()) + m_ignoreNextKeyUpEventCounter++; + setComplexTextInputEnabled(true); + } + } + + break; + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + EventKind eventKind = nullEvent; + + switch (keyboardEvent.type()) { + case WebEvent::KeyDown: + eventKind = keyboardEvent.isAutoRepeat() ? autoKey : keyDown; + break; + case WebEvent::KeyUp: + eventKind = keyUp; + break; + default: + ASSERT_NOT_REACHED(); + } + + EventRecord event = initializeEventRecord(eventKind); + event.modifiers = modifiersForEvent(keyboardEvent); + event.message = keyboardEvent.nativeVirtualKeyCode() << 8 | keyboardEvent.macCharCode(); + handled = NPP_HandleEvent(&event); + break; + } +#endif + + default: + ASSERT_NOT_REACHED(); + } + + // Most plug-ins simply return true for all keyboard events, even those that aren't handled. + // This leads to bugs such as <rdar://problem/8740926>. We work around this by returning false + // if the keyboard event has the command modifier pressed. + // However, for command-A (the shortcurt for 'Select All' we will always return true, since we don't + // want the entire page to be selected if the focus is in a plug-in text field (see <rdar://problem/9309903>). + if (keyboardEvent.metaKey()) { + if (keyboardEvent.text() == "a") + return true; + + return false; + } + + return handled; +} + +void NetscapePlugin::platformSetFocus(bool hasFocus) +{ + m_pluginHasFocus = hasFocus; + pluginFocusOrWindowFocusChanged(); + + switch (m_eventModel) { + case NPEventModelCocoa: { + NPCocoaEvent event = initializeEvent(NPCocoaEventFocusChanged); + + event.data.focus.hasFocus = hasFocus; + NPP_HandleEvent(&event); + break; + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + EventRecord event = initializeEventRecord(hasFocus ? NPEventType_GetFocusEvent : NPEventType_LoseFocusEvent); + + NPP_HandleEvent(&event); + break; + } +#endif + + default: + ASSERT_NOT_REACHED(); + } +} + +bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates() +{ + return true; +} + +void NetscapePlugin::windowFocusChanged(bool hasFocus) +{ + m_windowHasFocus = hasFocus; + pluginFocusOrWindowFocusChanged(); + + switch (m_eventModel) { + case NPEventModelCocoa: { + NPCocoaEvent event = initializeEvent(NPCocoaEventWindowFocusChanged); + + event.data.focus.hasFocus = hasFocus; + NPP_HandleEvent(&event); + break; + } + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + HiliteWindow(windowRef(), hasFocus); + if (hasFocus) + SetUserFocusWindow(windowRef()); + + EventRecord event = initializeEventRecord(activateEvt); + event.message = reinterpret_cast<unsigned long>(windowRef()); + if (hasFocus) + event.modifiers |= activeFlag; + + NPP_HandleEvent(&event); + break; + } +#endif + + default: + ASSERT_NOT_REACHED(); + } +} + +void NetscapePlugin::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates) +{ + m_windowFrameInScreenCoordinates = windowFrameInScreenCoordinates; + m_viewFrameInWindowCoordinates = viewFrameInWindowCoordinates; + + switch (m_eventModel) { + case NPEventModelCocoa: + // Nothing to do. + break; + +#ifndef NP_NO_CARBON + case NPEventModelCarbon: + updateFakeWindowBounds(); + break; +#endif + + default: + ASSERT_NOT_REACHED(); + } +} + +void NetscapePlugin::windowVisibilityChanged(bool) +{ + // FIXME: Implement. +} + +uint64_t NetscapePlugin::pluginComplexTextInputIdentifier() const +{ + // Just return a dummy value; this is only called for in-process plug-ins, which we don't support on Mac. + return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)); +} + + +#ifndef NP_NO_CARBON +static bool convertStringToKeyCodes(const String& string, ScriptCode scriptCode, Vector<UInt8>& keyCodes) +{ + // Create the mapping. + UnicodeMapping mapping; + + if (GetTextEncodingFromScriptInfo(scriptCode, kTextLanguageDontCare, kTextRegionDontCare, &mapping.otherEncoding) != noErr) + return false; + + mapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kTextEncodingDefaultFormat); + mapping.mappingVersion = kUnicodeUseLatestMapping; + + // Create the converter + UnicodeToTextInfo textInfo; + + if (CreateUnicodeToTextInfo(&mapping, &textInfo) != noErr) + return false; + + ByteCount inputLength = string.length() * sizeof(UniChar); + ByteCount inputRead; + ByteCount outputLength; + ByteCount maxOutputLength = string.length() * sizeof(UniChar); + + Vector<UInt8> outputData(maxOutputLength); + OSStatus status = ConvertFromUnicodeToText(textInfo, inputLength, string.characters(), kNilOptions, 0, 0, 0, 0, maxOutputLength, &inputRead, &outputLength, outputData.data()); + + DisposeUnicodeToTextInfo(&textInfo); + + if (status != noErr) + return false; + + outputData.swap(keyCodes); + return true; +} +#endif + +void NetscapePlugin::sendComplexTextInput(const String& textInput) +{ + if (!m_pluginWantsLegacyCocoaTextInput) { + // In the updated Cocoa text input spec, text input is disabled when the text input string has been sent + // by the UI process. Since the UI process has also updated its state, we can just reset the variable here + // instead of calling setComplexTextInputEnabled. + m_isComplexTextInputEnabled = false; + + // The UI process can also disable text input by sending an empty input string. In this case, we don't want + // to send it to the plug-in. + if (textInput.isNull()) + return; + } + + switch (m_eventModel) { + case NPEventModelCocoa: { + NPCocoaEvent event = initializeEvent(NPCocoaEventTextInput); + event.data.text.text = reinterpret_cast<NPNSString*>(static_cast<NSString*>(textInput)); + NPP_HandleEvent(&event); + break; + } +#ifndef NP_NO_CARBON + case NPEventModelCarbon: { + ScriptCode scriptCode = WKGetScriptCodeFromCurrentKeyboardInputSource(); + Vector<UInt8> keyCodes; + + if (!convertStringToKeyCodes(textInput, scriptCode, keyCodes)) + return; + + // Set the script code as the keyboard script. Normally Carbon does this whenever the input source changes. + // However, this is only done for the process that has the keyboard focus. We cheat and do it here instead. + SetScriptManagerVariable(smKeyScript, scriptCode); + + EventRecord event = initializeEventRecord(keyDown); + event.modifiers = 0; + + for (size_t i = 0; i < keyCodes.size(); i++) { + event.message = keyCodes[i]; + NPP_HandleEvent(&event); + } + break; + } +#endif + default: + ASSERT_NOT_REACHED(); + } +} + +void NetscapePlugin::pluginFocusOrWindowFocusChanged() +{ + bool pluginHasFocusAndWindowHasFocus = m_pluginHasFocus && m_windowHasFocus; + + controller()->pluginFocusOrWindowFocusChanged(pluginHasFocusAndWindowHasFocus); + + // In the updated Cocoa text input spec, the plug-in will enable complex text input + // by returning kNPEventStartIME from it's NPCocoaEventKeyDown handler. + if (!m_pluginWantsLegacyCocoaTextInput) + return; + + // In the old model, if the plug-in is focused, enable complex text input. + setComplexTextInputEnabled(pluginHasFocusAndWindowHasFocus); +} + +void NetscapePlugin::setComplexTextInputEnabled(bool complexTextInputEnabled) +{ + if (m_isComplexTextInputEnabled == complexTextInputEnabled) + return; + + m_isComplexTextInputEnabled = complexTextInputEnabled; + + PluginComplexTextInputState complexTextInputState = PluginComplexTextInputDisabled; + if (m_isComplexTextInputEnabled) + complexTextInputState = m_pluginWantsLegacyCocoaTextInput ? PluginComplexTextInputEnabledLegacy : PluginComplexTextInputEnabled; + + controller()->setComplexTextInputState(complexTextInputState); +} + +PlatformLayer* NetscapePlugin::pluginLayer() +{ + return static_cast<PlatformLayer*>(m_pluginLayer.get()); +} + +#ifndef NP_NO_CARBON +void NetscapePlugin::nullEventTimerFired() +{ + EventRecord event = initializeEventRecord(nullEvent); + + event.message = 0; + CGPoint mousePosition; + HIGetMousePosition(kHICoordSpaceScreenPixel, 0, &mousePosition); + event.where.h = mousePosition.x; + event.where.v = mousePosition.y; + + event.modifiers = GetCurrentKeyModifiers(); + if (!Button()) + event.modifiers |= btnState; + + NPP_HandleEvent(&event); +} +#endif + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/mac/PluginProxyMac.mm b/Source/WebKit2/WebProcess/Plugins/Netscape/mac/PluginProxyMac.mm new file mode 100644 index 000000000..49385d9d9 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/mac/PluginProxyMac.mm @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "PluginProxy.h" + +#if ENABLE(PLUGIN_PROCESS) + +#import "PluginController.h" +#import <WebKitSystemInterface.h> + +namespace WebKit { + +PlatformLayer* PluginProxy::pluginLayer() +{ + if (!m_pluginLayer && m_remoteLayerClientID) { + CALayer *renderLayer = WKMakeRenderLayer(m_remoteLayerClientID); + + // Create a layer with flipped geometry and add the real plug-in layer as a sublayer + // so the coordinate system will match the event coordinate system. + m_pluginLayer.adoptNS([[CALayer alloc] init]); + [m_pluginLayer.get() setGeometryFlipped:YES]; + + [renderLayer setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable]; + [m_pluginLayer.get() addSublayer:renderLayer]; + } + + return m_pluginLayer.get(); +} + +bool PluginProxy::needsBackingStore() const +{ + return !m_remoteLayerClientID; +} + +void PluginProxy::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus) +{ + controller()->pluginFocusOrWindowFocusChanged(pluginHasFocusAndWindowHasFocus); +} + +void PluginProxy::setComplexTextInputState(uint64_t complexTextInputState) +{ + controller()->setComplexTextInputState(static_cast<PluginComplexTextInputState>(complexTextInputState)); +} + +} // namespace WebKit + +#endif // ENABLE(PLUGIN_PROCESS) diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/qt/PluginProxyQt.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/qt/PluginProxyQt.cpp new file mode 100644 index 000000000..7af178429 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/qt/PluginProxyQt.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Nokia Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "PluginProxy.h" + +#if ENABLE(PLUGIN_PROCESS) + +#include <WebCore/NotImplemented.h> + +namespace WebKit { + +bool PluginProxy::needsBackingStore() const +{ + notImplemented(); + return true; +} + +} // namespace WebKit + +#endif // ENABLE(PLUGIN_PROCESS) diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/win/NetscapePluginWin.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/win/NetscapePluginWin.cpp new file mode 100644 index 000000000..b14f31d25 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/win/NetscapePluginWin.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#include "NetscapePlugin.h" + +#include "PluginController.h" +#include "WebEvent.h" +#include "WindowGeometry.h" +#include <WebCore/DefWndProcWindowClass.h> +#include <WebCore/GraphicsContext.h> +#include <WebCore/LocalWindowsContext.h> +#include <WebCore/NotImplemented.h> +#include <WebCore/WebCoreInstanceHandle.h> + +using namespace WebCore; + +namespace WebKit { + +static NetscapePlugin* currentPlugin; + +class CurrentPluginSetter { + WTF_MAKE_NONCOPYABLE(CurrentPluginSetter); +public: + explicit CurrentPluginSetter(NetscapePlugin* plugin) + : m_plugin(plugin) + , m_formerCurrentPlugin(currentPlugin) + { + currentPlugin = m_plugin; + } + + ~CurrentPluginSetter() + { + ASSERT(currentPlugin == m_plugin); + currentPlugin = m_formerCurrentPlugin; + } + +private: + NetscapePlugin* m_plugin; + NetscapePlugin* m_formerCurrentPlugin; +}; + +static LPCWSTR windowClassName = L"org.webkit.NetscapePluginWindow"; + +static void registerPluginView() +{ + static bool didRegister; + if (didRegister) + return; + didRegister = true; + + WNDCLASSW windowClass = {0}; + windowClass.style = CS_DBLCLKS; + windowClass.lpfnWndProc = ::DefWindowProcW; + windowClass.hInstance = instanceHandle(); + windowClass.hCursor = ::LoadCursorW(0, IDC_ARROW); + windowClass.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1); + windowClass.lpszClassName = windowClassName; + + ::RegisterClassW(&windowClass); +} + +HWND NetscapePlugin::containingWindow() +{ + return controller()->nativeParentWindow(); +} + +bool NetscapePlugin::platformPostInitialize() +{ + if (!m_isWindowed) { + m_window = 0; + + // Windowless plugins need a little help showing context menus since our containingWindow() + // is in a different process. See <http://webkit.org/b/51063>. + m_pluginModule->module()->installIATHook("user32.dll", "TrackPopupMenu", hookedTrackPopupMenu); + m_contextMenuOwnerWindow = ::CreateWindowExW(0, defWndProcWindowClassName(), 0, WS_CHILD, 0, 0, 0, 0, containingWindow(), 0, instanceHandle(), 0); + + return true; + } + + registerPluginView(); + + // Start out with the window hidden. The UI process will take care of showing it once it's correctly positioned the window. + m_window = ::CreateWindowExW(0, windowClassName, 0, WS_CHILD, 0, 0, 0, 0, containingWindow(), 0, instanceHandle(), 0); + if (!m_window) + return false; + + // FIXME: Do we need to pass our window to setPlatformWidget? + // FIXME: WebCore::PluginView sets the window proc to DefWindowProcA here for Shockwave Director. + + m_npWindow.type = NPWindowTypeWindow; + m_npWindow.window = m_window; + + return true; +} + +void NetscapePlugin::platformDestroy() +{ + if (!m_isWindowed) { + ASSERT(m_contextMenuOwnerWindow); + ::DestroyWindow(m_contextMenuOwnerWindow); + ASSERT(!m_window); + return; + } + + if (!::IsWindow(m_window)) + return; + ::DestroyWindow(m_window); +} + +bool NetscapePlugin::platformInvalidate(const IntRect& invalidRect) +{ + if (!m_isWindowed) + return false; + + RECT rect = invalidRect; + ::InvalidateRect(m_window, &rect, FALSE); + return true; +} + +void NetscapePlugin::platformGeometryDidChange() +{ + if (!m_isWindowed) + return; + + scheduleWindowedGeometryUpdate(); +} + +void NetscapePlugin::platformVisibilityDidChange() +{ + if (!m_isWindowed) + return; + + scheduleWindowedGeometryUpdate(); +} + +void NetscapePlugin::scheduleWindowedGeometryUpdate() +{ + // We only update the size here and let the UI process update our position and clip rect so + // that we can keep our position in sync when scrolling, etc. See <http://webkit.org/b/60210>. + ::SetWindowPos(m_window, 0, 0, 0, m_pluginSize.width(), m_pluginSize.height(), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); + + WindowGeometry geometry; + geometry.window = m_window; + geometry.visible = controller()->isPluginVisible(); + geometry.frame = IntRect(convertToRootView(IntPoint()), m_pluginSize); + geometry.clipRect = m_clipRect; + + controller()->scheduleWindowedPluginGeometryUpdate(geometry); +} + +void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool) +{ + CurrentPluginSetter setCurrentPlugin(this); + + // FIXME: Call SetWindow here if we haven't called it yet (see r59904). + + if (m_isWindowed) { + // FIXME: Paint windowed plugins into context if context->shouldIncludeChildWindows() is true. + return; + } + + controller()->willSendEventToPlugin(); + + LocalWindowsContext windowsContext(context, dirtyRect, m_isTransparent); + + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = windowsContext.hdc(); + + WINDOWPOS windowpos = { 0, 0, 0, 0, 0, 0, 0 }; + + IntPoint pluginLocationInRootViewCoordinates = convertToRootView(IntPoint()); + + windowpos.x = pluginLocationInRootViewCoordinates.x(); + windowpos.y = pluginLocationInRootViewCoordinates.y(); + windowpos.cx = m_pluginSize.width(); + windowpos.cy = m_pluginSize.height(); + + NPEvent npEvent; + npEvent.event = WM_WINDOWPOSCHANGED; + npEvent.wParam = 0; + npEvent.lParam = reinterpret_cast<uintptr_t>(&windowpos); + + NPP_HandleEvent(&npEvent); + + callSetWindow(); + + RECT dirtyWinRect = dirtyRect; + + npEvent.event = WM_PAINT; + npEvent.wParam = reinterpret_cast<uintptr_t>(windowsContext.hdc()); + npEvent.lParam = reinterpret_cast<uintptr_t>(&dirtyWinRect); + + NPP_HandleEvent(&npEvent); +} + +NPEvent toNP(const WebMouseEvent& event) +{ + NPEvent npEvent; + + npEvent.wParam = 0; + if (event.controlKey()) + npEvent.wParam |= MK_CONTROL; + if (event.shiftKey()) + npEvent.wParam |= MK_SHIFT; + + npEvent.lParam = MAKELPARAM(event.position().x(), event.position().y()); + + switch (event.type()) { + case WebEvent::MouseMove: + npEvent.event = WM_MOUSEMOVE; + switch (event.button()) { + case WebMouseEvent::LeftButton: + npEvent.wParam |= MK_LBUTTON; + break; + case WebMouseEvent::MiddleButton: + npEvent.wParam |= MK_MBUTTON; + break; + case WebMouseEvent::RightButton: + npEvent.wParam |= MK_RBUTTON; + break; + case WebMouseEvent::NoButton: + break; + } + break; + case WebEvent::MouseDown: + switch (event.button()) { + case WebMouseEvent::LeftButton: + npEvent.event = WM_LBUTTONDOWN; + break; + case WebMouseEvent::MiddleButton: + npEvent.event = WM_MBUTTONDOWN; + break; + case WebMouseEvent::RightButton: + npEvent.event = WM_RBUTTONDOWN; + break; + case WebMouseEvent::NoButton: + ASSERT_NOT_REACHED(); + break; + } + break; + case WebEvent::MouseUp: + switch (event.button()) { + case WebMouseEvent::LeftButton: + npEvent.event = WM_LBUTTONUP; + break; + case WebMouseEvent::MiddleButton: + npEvent.event = WM_MBUTTONUP; + break; + case WebMouseEvent::RightButton: + npEvent.event = WM_RBUTTONUP; + break; + case WebMouseEvent::NoButton: + ASSERT_NOT_REACHED(); + break; + } + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + return npEvent; +} + +bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& event) +{ + CurrentPluginSetter setCurrentPlugin(this); + + if (m_isWindowed) + return false; + + controller()->willSendEventToPlugin(); + + NPEvent npEvent = toNP(event); + NPP_HandleEvent(&npEvent); + return true; +} + +bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent&) +{ + CurrentPluginSetter setCurrentPlugin(this); + + notImplemented(); + return false; +} + +void NetscapePlugin::platformSetFocus(bool hasFocus) +{ + CurrentPluginSetter setCurrentPlugin(this); + + if (m_isWindowed) + return; + + controller()->willSendEventToPlugin(); + + NPEvent npEvent; + npEvent.event = hasFocus ? WM_SETFOCUS : WM_KILLFOCUS; + npEvent.wParam = 0; + npEvent.lParam = 0; + + NPP_HandleEvent(&npEvent); +} + +bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates() +{ + return false; +} + +bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& event) +{ + CurrentPluginSetter setCurrentPlugin(this); + + if (m_isWindowed) + return false; + + controller()->willSendEventToPlugin(); + + NPEvent npEvent = toNP(event); + NPP_HandleEvent(&npEvent); + return true; +} + +bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& event) +{ + CurrentPluginSetter setCurrentPlugin(this); + + if (m_isWindowed) + return false; + + controller()->willSendEventToPlugin(); + + NPEvent npEvent = toNP(event); + NPP_HandleEvent(&npEvent); + return true; +} + +bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent&) +{ + CurrentPluginSetter setCurrentPlugin(this); + + notImplemented(); + return false; +} + +BOOL NetscapePlugin::hookedTrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, int nReserved, HWND hWnd, const RECT* prcRect) +{ + // ::TrackPopupMenu fails when it is passed a window that is owned by another thread. If this + // happens, we substitute a dummy window that is owned by this thread. + + if (::GetWindowThreadProcessId(hWnd, 0) == ::GetCurrentThreadId()) + return ::TrackPopupMenu(hMenu, uFlags, x, y, nReserved, hWnd, prcRect); + + HWND originalFocusWindow = 0; + + ASSERT(currentPlugin); + if (currentPlugin) { + ASSERT(!currentPlugin->m_isWindowed); + ASSERT(currentPlugin->m_contextMenuOwnerWindow); + ASSERT(::GetWindowThreadProcessId(currentPlugin->m_contextMenuOwnerWindow, 0) == ::GetCurrentThreadId()); + hWnd = currentPlugin->m_contextMenuOwnerWindow; + + // If we don't focus the dummy window, the user will be able to scroll the page while the + // context menu is up, e.g. + originalFocusWindow = ::SetFocus(hWnd); + } + + BOOL result = ::TrackPopupMenu(hMenu, uFlags, x, y, nReserved, hWnd, prcRect); + + if (originalFocusWindow) + ::SetFocus(originalFocusWindow); + + return result; +} + +} // namespace WebKit diff --git a/Source/WebKit2/WebProcess/Plugins/Netscape/x11/NetscapePluginX11.cpp b/Source/WebKit2/WebProcess/Plugins/Netscape/x11/NetscapePluginX11.cpp new file mode 100644 index 000000000..a1c2a31a2 --- /dev/null +++ b/Source/WebKit2/WebProcess/Plugins/Netscape/x11/NetscapePluginX11.cpp @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#include "config.h" +#if PLUGIN_ARCHITECTURE(X11) + +#include "NetscapePlugin.h" + +#include "WebEvent.h" +#include <WebCore/GraphicsContext.h> +#include <WebCore/NotImplemented.h> + +#if PLATFORM(QT) +#include <WebCore/QtX11ImageConversion.h> +#elif PLATFORM(GTK) +#include "PlatformContextCairo.h" +#include "RefPtrCairo.h" +#include <cairo/cairo-xlib.h> +#include <gdk/gdkx.h> +#include <WebCore/GtkVersioning.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +static Display* getPluginDisplay() +{ +#if PLATFORM(QT) + // At the moment, we only support gdk based plugins (like Flash) that use a different X connection. + // The code below has the same effect as this one: + // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + + QLibrary library(QLatin1String("libgdk-x11-2.0"), 0); + if (!library.load()) + return 0; + + typedef void *(*gdk_init_check_ptr)(void*, void*); + gdk_init_check_ptr gdk_init_check = (gdk_init_check_ptr)library.resolve("gdk_init_check"); + if (!gdk_init_check) + return 0; + + typedef void *(*gdk_display_get_default_ptr)(); + gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default"); + if (!gdk_display_get_default) + return 0; + + typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *); + gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay"); + if (!gdk_x11_display_get_xdisplay) + return 0; + + gdk_init_check(0, 0); + return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default()); +#elif PLATFORM(GTK) + // Since we're a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based + // plugins, so we can return that. We might want to add other implementations here later. + return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); +#else + return 0; +#endif +} + +static inline int x11Screen() +{ +#if PLATFORM(QT) + return XDefaultScreen(NetscapePlugin::x11HostDisplay()); +#elif PLATFORM(GTK) + return gdk_screen_get_number(gdk_screen_get_default()); +#else + return 0; +#endif +} + +static inline int displayDepth() +{ +#if PLATFORM(QT) + return XDefaultDepth(NetscapePlugin::x11HostDisplay(), x11Screen()); +#elif PLATFORM(GTK) + return gdk_visual_get_depth(gdk_screen_get_system_visual(gdk_screen_get_default())); +#else + return 0; +#endif +} + +static inline unsigned long rootWindowID() +{ +#if PLATFORM(QT) + return XDefaultRootWindow(NetscapePlugin::x11HostDisplay()); +#elif PLATFORM(GTK) + return GDK_ROOT_WINDOW(); +#else + return 0; +#endif +} + +#if PLATFORM(GTK) +static bool moduleMixesGtkSymbols(Module* module) +{ +#ifdef GTK_API_VERSION_2 + return module->functionPointer<gpointer>("gtk_application_get_type"); +#else + return module->functionPointer<gpointer>("gtk_object_get_type"); +#endif +} +#endif + +Display* NetscapePlugin::x11HostDisplay() +{ +#if PLATFORM(QT) + static Display* dedicatedDisplay = 0; + if (!dedicatedDisplay) + dedicatedDisplay = XOpenDisplay(0); + + ASSERT(dedicatedDisplay); + return dedicatedDisplay; +#elif PLATFORM(GTK) + return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); +#else + return 0; +#endif +} + +bool NetscapePlugin::platformPostInitialize() +{ +#if PLATFORM(GTK) + if (moduleMixesGtkSymbols(m_pluginModule->module())) + return false; +#endif + + if (m_isWindowed) + return false; + + if (!(m_pluginDisplay = getPluginDisplay())) + return false; + + NPSetWindowCallbackStruct* callbackStruct = new NPSetWindowCallbackStruct; + callbackStruct->type = 0; + Display* display = x11HostDisplay(); + int depth = displayDepth(); +#if PLATFORM(QT) + ASSERT(depth == 16 || depth == 24 || depth == 32); +#endif + callbackStruct->display = display; + callbackStruct->depth = depth; + + XVisualInfo visualTemplate; + visualTemplate.screen = x11Screen(); + visualTemplate.depth = depth; + visualTemplate.c_class = TrueColor; + int numMatching; + XVisualInfo* visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask | VisualClassMask, + &visualTemplate, &numMatching); + ASSERT(visualInfo); + Visual* visual = visualInfo[0].visual; + ASSERT(visual); + XFree(visualInfo); + + callbackStruct->visual = visual; + callbackStruct->colormap = XCreateColormap(display, rootWindowID(), visual, AllocNone); + + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = 0; + m_npWindow.ws_info = callbackStruct; + + callSetWindow(); + + return true; +} + +void NetscapePlugin::platformDestroy() +{ + NPSetWindowCallbackStruct* callbackStruct = static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info); + Display* hostDisplay = x11HostDisplay(); + XFreeColormap(hostDisplay, callbackStruct->colormap); + delete callbackStruct; + + if (m_drawable) { + XFreePixmap(hostDisplay, m_drawable); + m_drawable = 0; + } +} + +bool NetscapePlugin::platformInvalidate(const IntRect&) +{ + notImplemented(); + return false; +} + +void NetscapePlugin::platformGeometryDidChange() +{ + if (m_isWindowed) { + notImplemented(); + return; + } + + Display* display = x11HostDisplay(); + if (m_drawable) + XFreePixmap(display, m_drawable); + + if (m_pluginSize.isEmpty()) { + m_drawable = 0; + return; + } + + m_drawable = XCreatePixmap(display, rootWindowID(), m_pluginSize.width(), m_pluginSize.height(), displayDepth()); + + XSync(display, false); // Make sure that the server knows about the Drawable. +} + +void NetscapePlugin::platformVisibilityDidChange() +{ + notImplemented(); +} + +void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool /*isSnapshot*/) +{ + if (m_isWindowed) { + notImplemented(); + return; + } + + if (!m_isStarted) { + // FIXME: we should paint a missing plugin icon. + return; + } + + if (context->paintingDisabled() || !m_drawable) + return; + + XEvent xevent; + memset(&xevent, 0, sizeof(XEvent)); + XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; + exposeEvent.type = GraphicsExpose; + exposeEvent.display = x11HostDisplay(); + exposeEvent.drawable = m_drawable; + + IntRect exposedRect(dirtyRect); + exposeEvent.x = exposedRect.x(); + exposeEvent.y = exposedRect.y(); + + // Note: in transparent mode Flash thinks width is the right and height is the bottom. + // We should take it into account if we want to support transparency. + exposeEvent.width = exposedRect.width(); + exposeEvent.height = exposedRect.height(); + + NPP_HandleEvent(&xevent); + + if (m_pluginDisplay != x11HostDisplay()) + XSync(m_pluginDisplay, false); + +#if PLATFORM(QT) + XImage* xImage = XGetImage(NetscapePlugin::x11HostDisplay(), m_drawable, exposedRect.x(), exposedRect.y(), + exposedRect.width(), exposedRect.height(), ULONG_MAX, ZPixmap); + QPainter* painter = context->platformContext(); + painter->drawImage(QPoint(exposedRect.x(), exposedRect.y()), qimageFromXImage(xImage), exposedRect); + + XDestroyImage(xImage); +#elif PLATFORM(GTK) + RefPtr<cairo_surface_t> drawableSurface = adoptRef(cairo_xlib_surface_create(m_pluginDisplay, + m_drawable, + static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info)->visual, + m_pluginSize.width(), + m_pluginSize.height())); + cairo_t* cr = context->platformContext()->cr(); + cairo_save(cr); + + cairo_set_source_surface(cr, drawableSurface.get(), 0, 0); + + cairo_rectangle(cr, exposedRect.x(), exposedRect.y(), exposedRect.width(), exposedRect.height()); + cairo_clip(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + + cairo_restore(cr); +#else + notImplemented(); +#endif +} + +static inline void initializeXEvent(XEvent& event) +{ + memset(&event, 0, sizeof(XEvent)); + event.xany.serial = 0; + event.xany.send_event = false; + event.xany.display = NetscapePlugin::x11HostDisplay(); + event.xany.window = 0; +} + +static inline uint64_t xTimeStamp(double timestampInSeconds) +{ + return timestampInSeconds * 1000; +} + +static inline unsigned xKeyModifiers(const WebEvent& event) +{ + unsigned xModifiers = 0; + if (event.controlKey()) + xModifiers |= ControlMask; + if (event.shiftKey()) + xModifiers |= ShiftMask; + if (event.altKey()) + xModifiers |= Mod1Mask; + if (event.metaKey()) + xModifiers |= Mod4Mask; + + return xModifiers; +} + +template <typename XEventType, typename WebEventType> +static inline void setCommonMouseEventFields(XEventType& xEvent, const WebEventType& webEvent, const WebCore::IntPoint& pluginLocation) +{ + xEvent.root = rootWindowID(); + xEvent.subwindow = 0; + xEvent.time = xTimeStamp(webEvent.timestamp()); + xEvent.x = webEvent.position().x() - pluginLocation.x(); + xEvent.y = webEvent.position().y() - pluginLocation.y(); + xEvent.x_root = webEvent.globalPosition().x(); + xEvent.y_root = webEvent.globalPosition().y(); + xEvent.state = xKeyModifiers(webEvent); + xEvent.same_screen = true; +} + +static inline void setXMotionEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) +{ + XMotionEvent& xMotion = xEvent.xmotion; + setCommonMouseEventFields(xMotion, webEvent, pluginLocation); + xMotion.type = MotionNotify; +} + +static inline void setXButtonEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) +{ + XButtonEvent& xButton = xEvent.xbutton; + setCommonMouseEventFields(xButton, webEvent, pluginLocation); + + xButton.type = (webEvent.type() == WebEvent::MouseDown) ? ButtonPress : ButtonRelease; + switch (webEvent.button()) { + case WebMouseEvent::LeftButton: + xButton.button = Button1; + break; + case WebMouseEvent::MiddleButton: + xButton.button = Button2; + break; + case WebMouseEvent::RightButton: + xButton.button = Button3; + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +static inline void setXButtonEventFieldsByWebWheelEvent(XEvent& xEvent, const WebWheelEvent& webEvent, const WebCore::IntPoint& pluginLocation) +{ + XButtonEvent& xButton = xEvent.xbutton; + setCommonMouseEventFields(xButton, webEvent, pluginLocation); + + xButton.type = ButtonPress; + FloatSize ticks = webEvent.wheelTicks(); + if (ticks.height()) { + if (ticks.height() > 0) + xButton.button = 4; // up + else + xButton.button = 5; // down + } else { + if (ticks.width() > 0) + xButton.button = 6; // left + else + xButton.button = 7; // right + } +} + +static inline void setXCrossingEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation, int type) +{ + XCrossingEvent& xCrossing = xEvent.xcrossing; + setCommonMouseEventFields(xCrossing, webEvent, pluginLocation); + + xCrossing.type = type; + xCrossing.mode = NotifyNormal; + xCrossing.detail = NotifyDetailNone; + xCrossing.focus = false; +} + +bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& event) +{ + if (m_isWindowed) + return false; + + if ((event.type() == WebEvent::MouseDown || event.type() == WebEvent::MouseUp) + && event.button() == WebMouseEvent::RightButton + && quirks().contains(PluginQuirks::IgnoreRightClickInWindowlessMode)) + return false; + + XEvent xEvent; + initializeXEvent(xEvent); + + switch (event.type()) { + case WebEvent::MouseDown: + case WebEvent::MouseUp: + setXButtonEventFields(xEvent, event, convertToRootView(IntPoint())); + break; + case WebEvent::MouseMove: + setXMotionEventFields(xEvent, event, convertToRootView(IntPoint())); + break; + case WebEvent::NoType: + case WebEvent::Wheel: + case WebEvent::KeyDown: + case WebEvent::KeyUp: + case WebEvent::RawKeyDown: + case WebEvent::Char: +#if ENABLE(GESTURE_EVENTS) + case WebEvent::GestureScrollBegin: + case WebEvent::GestureScrollEnd: +#endif +#if ENABLE(TOUCH_EVENTS) + case WebEvent::TouchStart: + case WebEvent::TouchMove: + case WebEvent::TouchEnd: + case WebEvent::TouchCancel: +#endif + return false; + } + + return !NPP_HandleEvent(&xEvent); +} + +// We undefine these constants in npruntime_internal.h to avoid collision +// with WebKit and platform headers. Values are defined in X.h. +const int kKeyPressType = 2; +const int kKeyReleaseType = 3; +const int kFocusInType = 9; +const int kFocusOutType = 10; + +bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& event) +{ + if (m_isWindowed) + return false; + + XEvent xEvent; + initializeXEvent(xEvent); + setXButtonEventFieldsByWebWheelEvent(xEvent, event, convertToRootView(IntPoint())); + + return !NPP_HandleEvent(&xEvent); +} + +void NetscapePlugin::platformSetFocus(bool focusIn) +{ + if (m_isWindowed) + return; + + XEvent xEvent; + initializeXEvent(xEvent); + XFocusChangeEvent& focusEvent = xEvent.xfocus; + focusEvent.type = focusIn ? kFocusInType : kFocusOutType; + focusEvent.mode = NotifyNormal; + focusEvent.detail = NotifyDetailNone; + + NPP_HandleEvent(&xEvent); +} + +bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates() +{ + return true; +} + +bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& event) +{ + if (m_isWindowed) + return false; + + XEvent xEvent; + initializeXEvent(xEvent); + setXCrossingEventFields(xEvent, event, convertToRootView(IntPoint()), EnterNotify); + + return !NPP_HandleEvent(&xEvent); +} + +bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& event) +{ + if (m_isWindowed) + return false; + + XEvent xEvent; + initializeXEvent(xEvent); + setXCrossingEventFields(xEvent, event, convertToRootView(IntPoint()), LeaveNotify); + + return !NPP_HandleEvent(&xEvent); +} + +static inline void setXKeyEventFields(XEvent& xEvent, const WebKeyboardEvent& webEvent) +{ + xEvent.xany.type = (webEvent.type() == WebEvent::KeyDown) ? kKeyPressType : kKeyReleaseType; + XKeyEvent& xKey = xEvent.xkey; + xKey.root = rootWindowID(); + xKey.subwindow = 0; + xKey.time = xTimeStamp(webEvent.timestamp()); + xKey.state = xKeyModifiers(webEvent); + xKey.keycode = webEvent.nativeVirtualKeyCode(); + + xKey.same_screen = true; + + // Key events propagated to the plugin does not need to have position. + // source: https://developer.mozilla.org/en/NPEvent + xKey.x = 0; + xKey.y = 0; + xKey.x_root = 0; + xKey.y_root = 0; +} + +bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& event) +{ + // We don't generate other types of keyboard events via WebEventFactory. + ASSERT(event.type() == WebEvent::KeyDown || event.type() == WebEvent::KeyUp); + + XEvent xEvent; + initializeXEvent(xEvent); + setXKeyEventFields(xEvent, event); + + return !NPP_HandleEvent(&xEvent); +} + +} // namespace WebKit + +#endif // PLUGIN_ARCHITECTURE(X11) |