diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/plugins | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/plugins')
61 files changed, 14384 insertions, 0 deletions
diff --git a/Source/WebCore/plugins/DOMMimeType.cpp b/Source/WebCore/plugins/DOMMimeType.cpp new file mode 100644 index 0000000..114d218 --- /dev/null +++ b/Source/WebCore/plugins/DOMMimeType.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DOMMimeType.h" + +#include "DOMPlugin.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "Page.h" +#include "PluginData.h" +#include "Settings.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +DOMMimeType::DOMMimeType(PassRefPtr<PluginData> pluginData, Frame* frame, unsigned index) + : m_pluginData(pluginData) + , m_frame(frame) + , m_index(index) +{ + if (m_frame) + m_frame->addDestructionObserver(this); +} + +DOMMimeType::~DOMMimeType() +{ + if (m_frame) + m_frame->removeDestructionObserver(this); +} + +const String &DOMMimeType::type() const +{ + return mimeClassInfo().type; +} + +String DOMMimeType::suffixes() const +{ + const Vector<String>& extensions = mimeClassInfo().extensions; + + StringBuilder builder; + for (size_t i = 0; i < extensions.size(); ++i) { + if (i) + builder.append(','); + builder.append(extensions[i]); + } + return builder.toString(); +} + +const String &DOMMimeType::description() const +{ + return mimeClassInfo().desc; +} + +PassRefPtr<DOMPlugin> DOMMimeType::enabledPlugin() const +{ + if (!m_frame || !m_frame->page() || !m_frame->page()->mainFrame()->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) + return 0; + + return DOMPlugin::create(m_pluginData.get(), m_frame, m_pluginData->mimePluginIndices()[m_index]); +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/DOMMimeType.h b/Source/WebCore/plugins/DOMMimeType.h new file mode 100644 index 0000000..74c62d0 --- /dev/null +++ b/Source/WebCore/plugins/DOMMimeType.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DOMMimeType_h +#define DOMMimeType_h + +#include "Frame.h" +#include "PluginData.h" + +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class DOMPlugin; + +class DOMMimeType : public RefCounted<DOMMimeType>, private FrameDestructionObserver { +public: + static PassRefPtr<DOMMimeType> create(PassRefPtr<PluginData> pluginData, Frame* frame, unsigned index) { return adoptRef(new DOMMimeType(pluginData, frame, index)); } + ~DOMMimeType(); + + const String &type() const; + String suffixes() const; + const String &description() const; + PassRefPtr<DOMPlugin> enabledPlugin() const; + + // FrameDestructionObserver + virtual void frameDestroyed() { m_frame = 0; } + +private: + const MimeClassInfo& mimeClassInfo() const { return m_pluginData->mimes()[m_index]; } + + DOMMimeType(PassRefPtr<PluginData>, Frame*, unsigned index); + RefPtr<PluginData> m_pluginData; + Frame* m_frame; + unsigned m_index; +}; + +} + +#endif diff --git a/Source/WebCore/plugins/DOMMimeType.idl b/Source/WebCore/plugins/DOMMimeType.idl new file mode 100644 index 0000000..1d97a6b --- /dev/null +++ b/Source/WebCore/plugins/DOMMimeType.idl @@ -0,0 +1,30 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +module window { + + interface DOMMimeType { + readonly attribute DOMString type; + readonly attribute DOMString suffixes; + readonly attribute DOMString description; + readonly attribute DOMPlugin enabledPlugin; + }; + +} diff --git a/Source/WebCore/plugins/DOMMimeTypeArray.cpp b/Source/WebCore/plugins/DOMMimeTypeArray.cpp new file mode 100644 index 0000000..82d4006 --- /dev/null +++ b/Source/WebCore/plugins/DOMMimeTypeArray.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DOMMimeTypeArray.h" + +#include "DOMPlugin.h" +#include "Frame.h" +#include "Page.h" +#include "PluginData.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +DOMMimeTypeArray::DOMMimeTypeArray(Frame* frame) + : m_frame(frame) +{ +} + +DOMMimeTypeArray::~DOMMimeTypeArray() +{ +} + +unsigned DOMMimeTypeArray::length() const +{ + PluginData* data = getPluginData(); + if (!data) + return 0; + return data->mimes().size(); +} + +PassRefPtr<DOMMimeType> DOMMimeTypeArray::item(unsigned index) +{ + PluginData* data = getPluginData(); + if (!data) + return 0; + const Vector<MimeClassInfo>& mimes = data->mimes(); + if (index >= mimes.size()) + return 0; + return DOMMimeType::create(data, m_frame, index).get(); +} + +bool DOMMimeTypeArray::canGetItemsForName(const AtomicString& propertyName) +{ + PluginData *data = getPluginData(); + if (!data) + return 0; + const Vector<MimeClassInfo>& mimes = data->mimes(); + for (unsigned i = 0; i < mimes.size(); ++i) { + if (mimes[i].type == propertyName) + return true; + } + return false; +} + +PassRefPtr<DOMMimeType> DOMMimeTypeArray::namedItem(const AtomicString& propertyName) +{ + PluginData *data = getPluginData(); + if (!data) + return 0; + const Vector<MimeClassInfo>& mimes = data->mimes(); + for (unsigned i = 0; i < mimes.size(); ++i) { + if (mimes[i].type == propertyName) + return DOMMimeType::create(data, m_frame, i).get(); + } + return 0; +} + +PluginData* DOMMimeTypeArray::getPluginData() const +{ + if (!m_frame) + return 0; + Page* p = m_frame->page(); + if (!p) + return 0; + return p->pluginData(); +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/DOMMimeTypeArray.h b/Source/WebCore/plugins/DOMMimeTypeArray.h new file mode 100644 index 0000000..ed078bb --- /dev/null +++ b/Source/WebCore/plugins/DOMMimeTypeArray.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DOMMimeTypeArray_h +#define DOMMimeTypeArray_h + +#include "DOMMimeType.h" +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Frame; +class PluginData; + +class DOMMimeTypeArray : public RefCounted<DOMMimeTypeArray> { +public: + static PassRefPtr<DOMMimeTypeArray> create(Frame* frame) { return adoptRef(new DOMMimeTypeArray(frame)); } + ~DOMMimeTypeArray(); + + void disconnectFrame() { m_frame = 0; } + + unsigned length() const; + PassRefPtr<DOMMimeType> item(unsigned index); + bool canGetItemsForName(const AtomicString& propertyName); + PassRefPtr<DOMMimeType> namedItem(const AtomicString& propertyName); + +private: + DOMMimeTypeArray(Frame*); + PluginData* getPluginData() const; + + Frame* m_frame; +}; + +} // namespace WebCore + +#endif // MimeTypeArray_h diff --git a/Source/WebCore/plugins/DOMMimeTypeArray.idl b/Source/WebCore/plugins/DOMMimeTypeArray.idl new file mode 100644 index 0000000..8b79a7e --- /dev/null +++ b/Source/WebCore/plugins/DOMMimeTypeArray.idl @@ -0,0 +1,32 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +module window { + + interface [ + HasNameGetter, + HasIndexGetter + ] DOMMimeTypeArray { + readonly attribute unsigned long length; + DOMMimeType item(in unsigned long index); + DOMMimeType namedItem(in DOMString name); + }; + +} diff --git a/Source/WebCore/plugins/DOMPlugin.cpp b/Source/WebCore/plugins/DOMPlugin.cpp new file mode 100644 index 0000000..8ddb507 --- /dev/null +++ b/Source/WebCore/plugins/DOMPlugin.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DOMPlugin.h" + +#include "PluginData.h" +#include "Frame.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +DOMPlugin::DOMPlugin(PluginData* pluginData, Frame* frame, unsigned index) + : m_pluginData(pluginData) + , m_frame(frame) + , m_index(index) +{ + if (m_frame) + m_frame->addDestructionObserver(this); +} + +DOMPlugin::~DOMPlugin() +{ + if (m_frame) + m_frame->removeDestructionObserver(this); +} + +String DOMPlugin::name() const +{ + return pluginInfo().name; +} + +String DOMPlugin::filename() const +{ + return pluginInfo().file; +} + +String DOMPlugin::description() const +{ + return pluginInfo().desc; +} + +unsigned DOMPlugin::length() const +{ + return pluginInfo().mimes.size(); +} + +PassRefPtr<DOMMimeType> DOMPlugin::item(unsigned index) +{ + if (index >= pluginInfo().mimes.size()) + return 0; + + const MimeClassInfo& mime = pluginInfo().mimes[index]; + + const Vector<MimeClassInfo>& mimes = m_pluginData->mimes(); + for (unsigned i = 0; i < mimes.size(); ++i) { + if (mimes[i] == mime && m_pluginData->mimePluginIndices()[i] == m_index) + return DOMMimeType::create(m_pluginData.get(), m_frame, i).get(); + } + return 0; +} + +bool DOMPlugin::canGetItemsForName(const AtomicString& propertyName) +{ + const Vector<MimeClassInfo>& mimes = m_pluginData->mimes(); + for (unsigned i = 0; i < mimes.size(); ++i) + if (mimes[i].type == propertyName) + return true; + return false; +} + +PassRefPtr<DOMMimeType> DOMPlugin::namedItem(const AtomicString& propertyName) +{ + const Vector<MimeClassInfo>& mimes = m_pluginData->mimes(); + for (unsigned i = 0; i < mimes.size(); ++i) + if (mimes[i].type == propertyName) + return DOMMimeType::create(m_pluginData.get(), m_frame, i).get(); + return 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/DOMPlugin.h b/Source/WebCore/plugins/DOMPlugin.h new file mode 100644 index 0000000..f305ec2 --- /dev/null +++ b/Source/WebCore/plugins/DOMPlugin.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DOMPlugin_h +#define DOMPlugin_h + +#include "DOMMimeType.h" +#include <wtf/Forward.h> +#include <wtf/RefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class Plugin; +class PluginData; + +class DOMPlugin : public RefCounted<DOMPlugin>, private FrameDestructionObserver { +public: + static PassRefPtr<DOMPlugin> create(PluginData* pluginData, Frame* frame, unsigned index) { return adoptRef(new DOMPlugin(pluginData, frame, index)); } + ~DOMPlugin(); + + String name() const; + String filename() const; + String description() const; + + unsigned length() const; + + PassRefPtr<DOMMimeType> item(unsigned index); + bool canGetItemsForName(const AtomicString& propertyName); + PassRefPtr<DOMMimeType> namedItem(const AtomicString& propertyName); + + // FrameDestructionObserver + virtual void frameDestroyed() { m_frame = 0; } + +private: + const PluginInfo& pluginInfo() const { return m_pluginData->plugins()[m_index]; } + + DOMPlugin(PluginData*, Frame*, unsigned index); + RefPtr<PluginData> m_pluginData; + Frame* m_frame; + unsigned m_index; +}; + +} // namespace WebCore + +#endif // Plugin_h diff --git a/Source/WebCore/plugins/DOMPlugin.idl b/Source/WebCore/plugins/DOMPlugin.idl new file mode 100644 index 0000000..dc2b141 --- /dev/null +++ b/Source/WebCore/plugins/DOMPlugin.idl @@ -0,0 +1,35 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +module window { + + interface [ + HasNameGetter, + HasIndexGetter + ] DOMPlugin { + readonly attribute DOMString name; + readonly attribute DOMString filename; + readonly attribute DOMString description; + readonly attribute unsigned long length; + DOMMimeType item(in unsigned long index); + DOMMimeType namedItem(in DOMString name); + }; + +} diff --git a/Source/WebCore/plugins/DOMPluginArray.cpp b/Source/WebCore/plugins/DOMPluginArray.cpp new file mode 100644 index 0000000..766c3eb --- /dev/null +++ b/Source/WebCore/plugins/DOMPluginArray.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DOMPluginArray.h" + +#include "DOMPlugin.h" +#include "Frame.h" +#include "Page.h" +#include "PluginData.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +DOMPluginArray::DOMPluginArray(Frame* frame) + : m_frame(frame) +{ +} + +DOMPluginArray::~DOMPluginArray() +{ +} + +unsigned DOMPluginArray::length() const +{ + PluginData* data = pluginData(); + if (!data) + return 0; + return data->plugins().size(); +} + +PassRefPtr<DOMPlugin> DOMPluginArray::item(unsigned index) +{ + PluginData* data = pluginData(); + if (!data) + return 0; + const Vector<PluginInfo>& plugins = data->plugins(); + if (index >= plugins.size()) + return 0; + return DOMPlugin::create(data, m_frame, index).get(); +} + +bool DOMPluginArray::canGetItemsForName(const AtomicString& propertyName) +{ + PluginData* data = pluginData(); + if (!data) + return 0; + const Vector<PluginInfo>& plugins = data->plugins(); + for (unsigned i = 0; i < plugins.size(); ++i) { + if (plugins[i].name == propertyName) + return true; + } + return false; +} + +PassRefPtr<DOMPlugin> DOMPluginArray::namedItem(const AtomicString& propertyName) +{ + PluginData* data = pluginData(); + if (!data) + return 0; + const Vector<PluginInfo>& plugins = data->plugins(); + for (unsigned i = 0; i < plugins.size(); ++i) { + if (plugins[i].name == propertyName) + return DOMPlugin::create(data, m_frame, i).get(); + } + return 0; +} + +void DOMPluginArray::refresh(bool reload) +{ + Page::refreshPlugins(reload); +} + +PluginData* DOMPluginArray::pluginData() const +{ + if (!m_frame) + return 0; + Page* page = m_frame->page(); + if (!page) + return 0; + return page->pluginData(); +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/DOMPluginArray.h b/Source/WebCore/plugins/DOMPluginArray.h new file mode 100644 index 0000000..cc70572 --- /dev/null +++ b/Source/WebCore/plugins/DOMPluginArray.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DOMPluginArray_h +#define DOMPluginArray_h + +#include "DOMPlugin.h" +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Frame; +class PluginData; + +class DOMPluginArray : public RefCounted<DOMPluginArray> { +public: + static PassRefPtr<DOMPluginArray> create(Frame* frame) { return adoptRef(new DOMPluginArray(frame)); } + ~DOMPluginArray(); + + void disconnectFrame() { m_frame = 0; } + + unsigned length() const; + PassRefPtr<DOMPlugin> item(unsigned index); + bool canGetItemsForName(const AtomicString& propertyName); + PassRefPtr<DOMPlugin> namedItem(const AtomicString& propertyName); + + void refresh(bool reload); + +private: + DOMPluginArray(Frame*); + PluginData* pluginData() const; + + Frame* m_frame; +}; + +} // namespace WebCore + +#endif // PluginArray_h diff --git a/Source/WebCore/plugins/DOMPluginArray.idl b/Source/WebCore/plugins/DOMPluginArray.idl new file mode 100644 index 0000000..4c0d64c --- /dev/null +++ b/Source/WebCore/plugins/DOMPluginArray.idl @@ -0,0 +1,33 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +module window { + + interface [ + HasNameGetter, + HasIndexGetter + ] DOMPluginArray { + readonly attribute unsigned long length; + DOMPlugin item(in unsigned long index); + DOMPlugin namedItem(in DOMString name); + void refresh(in boolean reload); + }; + +} diff --git a/Source/WebCore/plugins/PluginData.cpp b/Source/WebCore/plugins/PluginData.cpp new file mode 100644 index 0000000..b8e751d --- /dev/null +++ b/Source/WebCore/plugins/PluginData.cpp @@ -0,0 +1,81 @@ +/* + Copyright (C) 2000 Harri Porten (porten@kde.org) + Copyright (C) 2000 Daniel Molkentin (molkentin@kde.org) + Copyright (C) 2000 Stefan Schimanski (schimmi@kde.org) + Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All Rights Reserved. + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "PluginData.h" + +#if USE(PLATFORM_STRATEGIES) +#include "PlatformStrategies.h" +#include "PluginStrategy.h" +#endif + +namespace WebCore { + +PluginData::PluginData(const Page* page) +{ + initPlugins(page); + + for (unsigned i = 0; i < m_plugins.size(); ++i) { + const PluginInfo& plugin = m_plugins[i]; + for (unsigned j = 0; j < plugin.mimes.size(); ++j) { + m_mimes.append(plugin.mimes[j]); + m_mimePluginIndices.append(i); + } + } +} + +bool PluginData::supportsMimeType(const String& mimeType) const +{ + for (unsigned i = 0; i < m_mimes.size(); ++i) + if (m_mimes[i].type == mimeType) + return true; + return false; +} + +String PluginData::pluginNameForMimeType(const String& mimeType) const +{ + for (unsigned i = 0; i < m_mimes.size(); ++i) { + const MimeClassInfo& info = m_mimes[i]; + + if (info.type == mimeType) + return m_plugins[m_mimePluginIndices[i]].name; + } + + return String(); +} + +#if USE(PLATFORM_STRATEGIES) +void PluginData::refresh() +{ + platformStrategies()->pluginStrategy()->refreshPlugins(); +} + +void PluginData::initPlugins(const Page* page) +{ + ASSERT(m_plugins.isEmpty()); + + platformStrategies()->pluginStrategy()->getPluginInfo(page, m_plugins); +} +#endif + +} diff --git a/Source/WebCore/plugins/PluginData.h b/Source/WebCore/plugins/PluginData.h new file mode 100644 index 0000000..c6cde81 --- /dev/null +++ b/Source/WebCore/plugins/PluginData.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef PluginData_h +#define PluginData_h + +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include "PlatformString.h" + +namespace WebCore { + +class Page; +struct PluginInfo; + +struct MimeClassInfo { + String type; + String desc; + Vector<String> extensions; +}; + +inline bool operator==(const MimeClassInfo& a, const MimeClassInfo& b) +{ + return a.type == b.type && a.desc == b.desc && a.extensions == b.extensions; +} + +struct PluginInfo { + String name; + String file; + String desc; + Vector<MimeClassInfo> mimes; +}; + +// FIXME: merge with PluginDatabase in the future +class PluginData : public RefCounted<PluginData> { +public: + static PassRefPtr<PluginData> create(const Page* page) { return adoptRef(new PluginData(page)); } + + const Vector<PluginInfo>& plugins() const { return m_plugins; } + const Vector<MimeClassInfo>& mimes() const { return m_mimes; } + const Vector<size_t>& mimePluginIndices() const { return m_mimePluginIndices; } + + bool supportsMimeType(const String& mimeType) const; + String pluginNameForMimeType(const String& mimeType) const; + + static void refresh(); + +private: + PluginData(const Page*); + void initPlugins(const Page*); + + Vector<PluginInfo> m_plugins; + Vector<MimeClassInfo> m_mimes; + Vector<size_t> m_mimePluginIndices; +}; + +} + +#endif diff --git a/Source/WebCore/plugins/PluginDataNone.cpp b/Source/WebCore/plugins/PluginDataNone.cpp new file mode 100644 index 0000000..edfee5b --- /dev/null +++ b/Source/WebCore/plugins/PluginDataNone.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Kevin Ollivier <kevino@theolliviers.com> All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "PluginData.h" + +namespace WebCore { + +void PluginData::initPlugins(const Page*) +{ +} + +void PluginData::refresh() +{ +} + +}; diff --git a/Source/WebCore/plugins/PluginDatabase.cpp b/Source/WebCore/plugins/PluginDatabase.cpp new file mode 100644 index 0000000..b9e154a --- /dev/null +++ b/Source/WebCore/plugins/PluginDatabase.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginDatabase.h" + +#include "Frame.h" +#include "KURL.h" +#include "PluginPackage.h" +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) +#include "FileSystem.h" +#endif +#include <stdlib.h> +#include <wtf/text/CString.h> + +#if PLATFORM(ANDROID) +#include "JavaSharedClient.h" +#include "PluginClient.h" +#endif + +namespace WebCore { + +typedef HashMap<String, RefPtr<PluginPackage> > PluginPackageByNameMap; + +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) +static const size_t maximumPersistentPluginMetadataCacheSize = 32768; + +static bool gPersistentPluginMetadataCacheIsEnabled; + +String& persistentPluginMetadataCachePath() +{ + DEFINE_STATIC_LOCAL(String, cachePath, ()); + return cachePath; +} +#endif + +PluginDatabase::PluginDatabase() +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + : m_persistentMetadataCacheIsLoaded(false) +#endif +{ +} + +PluginDatabase* PluginDatabase::installedPlugins(bool populate) +{ + static PluginDatabase* plugins = 0; + + if (!plugins) { + plugins = new PluginDatabase; + + if (populate) { + plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories()); + plugins->refresh(); + } + } + + return plugins; +} + +bool PluginDatabase::isMIMETypeRegistered(const String& mimeType) +{ + if (mimeType.isNull()) + return false; + if (m_registeredMIMETypes.contains(mimeType)) + return true; + // No plugin was found, try refreshing the database and searching again + return (refresh() && m_registeredMIMETypes.contains(mimeType)); +} + +void PluginDatabase::addExtraPluginDirectory(const String& directory) +{ + m_pluginDirectories.append(directory); + refresh(); +} + +bool PluginDatabase::refresh() +{ +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + if (!m_persistentMetadataCacheIsLoaded) + loadPersistentMetadataCache(); +#endif + bool pluginSetChanged = false; + + if (!m_plugins.isEmpty()) { + PluginSet pluginsToUnload; + getDeletedPlugins(pluginsToUnload); + + // Unload plugins + PluginSet::const_iterator end = pluginsToUnload.end(); + for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it) + remove(it->get()); + + pluginSetChanged = !pluginsToUnload.isEmpty(); + } + + HashSet<String> paths; + getPluginPathsInDirectories(paths); + + HashMap<String, time_t> pathsWithTimes; + + // We should only skip unchanged files if we didn't remove any plugins above. If we did remove + // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions + // of RealPlayer installed and just removed the newer one, we'll pick up the older one. + bool shouldSkipUnchangedFiles = !pluginSetChanged; + + HashSet<String>::const_iterator pathsEnd = paths.end(); + for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) { + time_t lastModified; + if (!getFileModificationTime(*it, lastModified)) + continue; + + pathsWithTimes.add(*it, lastModified); + + // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything. + if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified) + continue; + + if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) { + ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified); + remove(oldPackage.get()); + } + + RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified); + if (package && add(package.release())) + pluginSetChanged = true; + } + + // Cache all the paths we found with their timestamps for next time. + pathsWithTimes.swap(m_pluginPathsWithTimes); + + if (!pluginSetChanged) + return false; + +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + updatePersistentMetadataCache(); +#endif + + m_registeredMIMETypes.clear(); + + // Register plug-in MIME types + PluginSet::const_iterator end = m_plugins.end(); + for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { + // Get MIME types + MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin(); + MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end(); + for (; map_it != map_end; ++map_it) + m_registeredMIMETypes.add(map_it->first); + } + + return true; +} + +Vector<PluginPackage*> PluginDatabase::plugins() const +{ + Vector<PluginPackage*> result; + + PluginSet::const_iterator end = m_plugins.end(); + for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) + result.append((*it).get()); + + return result; +} + +int PluginDatabase::preferredPluginCompare(const void* a, const void* b) +{ + PluginPackage* pluginA = *static_cast<PluginPackage* const*>(a); + PluginPackage* pluginB = *static_cast<PluginPackage* const*>(b); + + return pluginA->compare(*pluginB); +} + +PluginPackage* PluginDatabase::pluginForMIMEType(const String& mimeType) +{ + if (mimeType.isEmpty()) + return 0; + + String key = mimeType.lower(); + PluginSet::const_iterator end = m_plugins.end(); + PluginPackage* preferredPlugin = m_preferredPlugins.get(key).get(); + if (preferredPlugin + && preferredPlugin->isEnabled() + && preferredPlugin->mimeToDescriptions().contains(key)) { + return preferredPlugin; + } + + Vector<PluginPackage*, 2> pluginChoices; + + for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { + PluginPackage* plugin = (*it).get(); + + if (!plugin->isEnabled()) + continue; + + if (plugin->mimeToDescriptions().contains(key)) { +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + if (!plugin->ensurePluginLoaded()) + continue; +#endif + pluginChoices.append(plugin); + } + } + + if (pluginChoices.isEmpty()) + return 0; + + qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare); + + return pluginChoices[0]; +} + +String PluginDatabase::MIMETypeForExtension(const String& extension) const +{ + if (extension.isEmpty()) + return String(); + + PluginSet::const_iterator end = m_plugins.end(); + String mimeType; + Vector<PluginPackage*, 2> pluginChoices; + HashMap<PluginPackage*, String> mimeTypeForPlugin; + + for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { + if (!(*it)->isEnabled()) + continue; + + MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end(); + + for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) { + mimeType = mime_it->first; + PluginPackage* preferredPlugin = m_preferredPlugins.get(mimeType).get(); + const Vector<String>& extensions = mime_it->second; + bool foundMapping = false; + for (unsigned i = 0; i < extensions.size(); i++) { + if (equalIgnoringCase(extensions[i], extension)) { + PluginPackage* plugin = (*it).get(); + + if (preferredPlugin && PluginPackage::equal(*plugin, *preferredPlugin)) + return mimeType; + +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + if (!plugin->ensurePluginLoaded()) + continue; +#endif + pluginChoices.append(plugin); + mimeTypeForPlugin.add(plugin, mimeType); + foundMapping = true; + break; + } + } + if (foundMapping) + break; + } + } + + if (pluginChoices.isEmpty()) + return String(); + + qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare); + + return mimeTypeForPlugin.get(pluginChoices[0]); +} + +PluginPackage* PluginDatabase::findPlugin(const KURL& url, String& mimeType) +{ + if (!mimeType.isEmpty()) + return pluginForMIMEType(mimeType); + + String filename = url.lastPathComponent(); + if (filename.endsWith("/")) + return 0; + + int extensionPos = filename.reverseFind('.'); + if (extensionPos == -1) + return 0; + + String mimeTypeForExtension = MIMETypeForExtension(filename.substring(extensionPos + 1)); + PluginPackage* plugin = pluginForMIMEType(mimeTypeForExtension); + if (!plugin) { + // FIXME: if no plugin could be found, query Windows for the mime type + // corresponding to the extension. + return 0; + } + + mimeType = mimeTypeForExtension; + return plugin; +} + +void PluginDatabase::setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin) +{ + if (!plugin || plugin->mimeToExtensions().contains(mimeType)) + m_preferredPlugins.set(mimeType.lower(), plugin); +} + +void PluginDatabase::getDeletedPlugins(PluginSet& plugins) const +{ + PluginSet::const_iterator end = m_plugins.end(); + for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { + if (!fileExists((*it)->path())) + plugins.add(*it); + } +} + +bool PluginDatabase::add(PassRefPtr<PluginPackage> prpPackage) +{ + ASSERT_ARG(prpPackage, prpPackage); + + RefPtr<PluginPackage> package = prpPackage; + + if (!m_plugins.add(package).second) + return false; + + m_pluginsByPath.add(package->path(), package); + return true; +} + +void PluginDatabase::remove(PluginPackage* package) +{ + MIMEToExtensionsMap::const_iterator it = package->mimeToExtensions().begin(); + MIMEToExtensionsMap::const_iterator end = package->mimeToExtensions().end(); + for ( ; it != end; ++it) { + PluginPackageByNameMap::iterator packageInMap = m_preferredPlugins.find(it->first); + if (packageInMap != m_preferredPlugins.end() && packageInMap->second == package) + m_preferredPlugins.remove(packageInMap); + } + + m_plugins.remove(package); + m_pluginsByPath.remove(package->path()); +} + +void PluginDatabase::clear() +{ + m_plugins.clear(); + m_pluginsByPath.clear(); + m_pluginPathsWithTimes.clear(); + m_registeredMIMETypes.clear(); + m_preferredPlugins.clear(); +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + m_persistentMetadataCacheIsLoaded = false; +#endif +} + +#if (!OS(WINCE)) && (!OS(SYMBIAN)) && (!OS(WINDOWS) || !ENABLE(NETSCAPE_PLUGIN_API)) +// For Safari/Win the following three methods are implemented +// in PluginDatabaseWin.cpp, but if we can use WebCore constructs +// for the logic we should perhaps move it here under XP_WIN? + +Vector<String> PluginDatabase::defaultPluginDirectories() +{ + Vector<String> paths; + + // Add paths specific to each platform +#if defined(XP_UNIX) + String userPluginPath = homeDirectoryPath(); + userPluginPath.append(String("/.mozilla/plugins")); + paths.append(userPluginPath); + + userPluginPath = homeDirectoryPath(); + userPluginPath.append(String("/.netscape/plugins")); + paths.append(userPluginPath); + + paths.append("/usr/lib/browser/plugins"); + paths.append("/usr/local/lib/mozilla/plugins"); + paths.append("/usr/lib/firefox/plugins"); + paths.append("/usr/lib64/browser-plugins"); + paths.append("/usr/lib/browser-plugins"); + paths.append("/usr/lib/mozilla/plugins"); + paths.append("/usr/local/netscape/plugins"); + paths.append("/opt/mozilla/plugins"); + paths.append("/opt/mozilla/lib/plugins"); + paths.append("/opt/netscape/plugins"); + paths.append("/opt/netscape/communicator/plugins"); + paths.append("/usr/lib/netscape/plugins"); + paths.append("/usr/lib/netscape/plugins-libc5"); + paths.append("/usr/lib/netscape/plugins-libc6"); + paths.append("/usr/lib64/netscape/plugins"); + paths.append("/usr/lib64/mozilla/plugins"); + paths.append("/usr/lib/nsbrowser/plugins"); + paths.append("/usr/lib64/nsbrowser/plugins"); + + String mozHome(getenv("MOZILLA_HOME")); + mozHome.append("/plugins"); + paths.append(mozHome); + + Vector<String> mozPaths; + String mozPath(getenv("MOZ_PLUGIN_PATH")); + mozPath.split(UChar(':'), /* allowEmptyEntries */ false, mozPaths); + paths.append(mozPaths); +#elif defined(XP_MACOSX) + String userPluginPath = homeDirectoryPath(); + userPluginPath.append(String("/Library/Internet Plug-Ins")); + paths.append(userPluginPath); + paths.append("/Library/Internet Plug-Ins"); +#elif defined(XP_WIN) + String userPluginPath = homeDirectoryPath(); + userPluginPath.append(String("\\Application Data\\Mozilla\\plugins")); + paths.append(userPluginPath); +#endif + + // Add paths specific to each port +#if PLATFORM(QT) + Vector<String> qtPaths; + String qtPath(qgetenv("QTWEBKIT_PLUGIN_PATH").constData()); + qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths); + paths.append(qtPaths); +#endif + +#if PLATFORM(ANDROID) + if (android::JavaSharedClient::GetPluginClient()) + return android::JavaSharedClient::GetPluginClient()->getPluginDirectories(); +#endif + + return paths; +} + +bool PluginDatabase::isPreferredPluginDirectory(const String& path) +{ + String preferredPath = homeDirectoryPath(); + +#if defined(XP_UNIX) + preferredPath.append(String("/.mozilla/plugins")); +#elif defined(XP_MACOSX) + preferredPath.append(String("/Library/Internet Plug-Ins")); +#elif defined(XP_WIN) + preferredPath.append(String("\\Application Data\\Mozilla\\plugins")); +#endif + + // TODO: We should normalize the path before doing a comparison. + return path == preferredPath; +} + +void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const +{ + // FIXME: This should be a case insensitive set. + HashSet<String> uniqueFilenames; + +#if defined(XP_UNIX) || defined(ANDROID) + String fileNameFilter("*.so"); +#else + String fileNameFilter(""); +#endif + + Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end(); + for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) { + Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter); + Vector<String>::const_iterator pluginsEnd = pluginPaths.end(); + for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) { + if (!fileExists(*pIt)) + continue; + + paths.add(*pIt); + } + } +} + +#endif // !OS(SYMBIAN) && !OS(WINDOWS) + +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + +static void fillBufferWithContentsOfFile(PlatformFileHandle file, Vector<char>& buffer) +{ + size_t bufferSize = 0; + size_t bufferCapacity = 1024; + buffer.resize(bufferCapacity); + + do { + bufferSize += readFromFile(file, buffer.data() + bufferSize, bufferCapacity - bufferSize); + if (bufferSize == bufferCapacity) { + if (bufferCapacity < maximumPersistentPluginMetadataCacheSize) { + bufferCapacity *= 2; + buffer.resize(bufferCapacity); + } else { + buffer.clear(); + return; + } + } else + break; + } while (true); + + buffer.shrink(bufferSize); +} + +static bool readUTF8String(String& resultString, char*& start, const char* end) +{ + if (start >= end) + return false; + + int len = strlen(start); + resultString = String::fromUTF8(start, len); + start += len + 1; + + return true; +} + +static bool readTime(time_t& resultTime, char*& start, const char* end) +{ + if (start + sizeof(time_t) >= end) + return false; + + resultTime = *reinterpret_cast_ptr<time_t*>(start); + start += sizeof(time_t); + + return true; +} + +static const char schemaVersion = '1'; +static const char persistentPluginMetadataCacheFilename[] = "PluginMetadataCache.bin"; + +void PluginDatabase::loadPersistentMetadataCache() +{ + if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty()) + return; + + PlatformFileHandle file; + String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename); + file = openFile(absoluteCachePath, OpenForRead); + + if (!isHandleValid(file)) + return; + + // Mark cache as loaded regardless of success or failure. If + // there's error in the cache, we won't try to load it anymore. + m_persistentMetadataCacheIsLoaded = true; + + Vector<char> fileContents; + fillBufferWithContentsOfFile(file, fileContents); + closeFile(file); + + if (fileContents.size() < 2 || fileContents.first() != schemaVersion || fileContents.last() != '\0') { + LOG_ERROR("Unable to read plugin metadata cache: corrupt schema"); + deleteFile(absoluteCachePath); + return; + } + + char* bufferPos = fileContents.data() + 1; + char* end = fileContents.data() + fileContents.size(); + + PluginSet cachedPlugins; + HashMap<String, time_t> cachedPluginPathsWithTimes; + HashMap<String, RefPtr<PluginPackage> > cachedPluginsByPath; + + while (bufferPos < end) { + String path; + time_t lastModified; + String name; + String desc; + String mimeDesc; + if (!(readUTF8String(path, bufferPos, end) + && readTime(lastModified, bufferPos, end) + && readUTF8String(name, bufferPos, end) + && readUTF8String(desc, bufferPos, end) + && readUTF8String(mimeDesc, bufferPos, end))) { + LOG_ERROR("Unable to read plugin metadata cache: corrupt data"); + deleteFile(absoluteCachePath); + return; + } + + // Skip metadata that points to plugins from directories that + // are not part of plugin directory list anymore. + String pluginDirectoryName = directoryName(path); + if (m_pluginDirectories.find(pluginDirectoryName) == WTF::notFound) + continue; + + RefPtr<PluginPackage> package = PluginPackage::createPackageFromCache(path, lastModified, name, desc, mimeDesc); + + if (package && cachedPlugins.add(package).second) { + cachedPluginPathsWithTimes.add(package->path(), package->lastModified()); + cachedPluginsByPath.add(package->path(), package); + } + } + + m_plugins.swap(cachedPlugins); + m_pluginsByPath.swap(cachedPluginsByPath); + m_pluginPathsWithTimes.swap(cachedPluginPathsWithTimes); +} + +static bool writeUTF8String(PlatformFileHandle file, const String& string) +{ + CString utf8String = string.utf8(); + int length = utf8String.length() + 1; + return writeToFile(file, utf8String.data(), length) == length; +} + +static bool writeTime(PlatformFileHandle file, const time_t& time) +{ + return writeToFile(file, reinterpret_cast<const char*>(&time), sizeof(time_t)) == sizeof(time_t); +} + +void PluginDatabase::updatePersistentMetadataCache() +{ + if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty()) + return; + + makeAllDirectories(persistentMetadataCachePath()); + String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename); + deleteFile(absoluteCachePath); + + if (m_plugins.isEmpty()) + return; + + PlatformFileHandle file; + file = openFile(absoluteCachePath, OpenForWrite); + + if (!isHandleValid(file)) { + LOG_ERROR("Unable to open plugin metadata cache for saving"); + return; + } + + char localSchemaVersion = schemaVersion; + if (writeToFile(file, &localSchemaVersion, 1) != 1) { + LOG_ERROR("Unable to write plugin metadata cache schema"); + closeFile(file); + deleteFile(absoluteCachePath); + return; + } + + PluginSet::const_iterator end = m_plugins.end(); + for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { + if (!(writeUTF8String(file, (*it)->path()) + && writeTime(file, (*it)->lastModified()) + && writeUTF8String(file, (*it)->name()) + && writeUTF8String(file, (*it)->description()) + && writeUTF8String(file, (*it)->fullMIMEDescription()))) { + LOG_ERROR("Unable to write plugin metadata to cache"); + closeFile(file); + deleteFile(absoluteCachePath); + return; + } + } + + closeFile(file); +} + +bool PluginDatabase::isPersistentMetadataCacheEnabled() +{ + return gPersistentPluginMetadataCacheIsEnabled; +} + +void PluginDatabase::setPersistentMetadataCacheEnabled(bool isEnabled) +{ + gPersistentPluginMetadataCacheIsEnabled = isEnabled; +} + +String PluginDatabase::persistentMetadataCachePath() +{ + return WebCore::persistentPluginMetadataCachePath(); +} + +void PluginDatabase::setPersistentMetadataCachePath(const String& persistentMetadataCachePath) +{ + WebCore::persistentPluginMetadataCachePath() = persistentMetadataCachePath; +} +#endif +} diff --git a/Source/WebCore/plugins/PluginDatabase.h b/Source/WebCore/plugins/PluginDatabase.h new file mode 100644 index 0000000..b1e1525 --- /dev/null +++ b/Source/WebCore/plugins/PluginDatabase.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginDatabase_h +#define PluginDatabase_h + +#include "PlatformString.h" +#include "PluginPackage.h" +#include <wtf/HashSet.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +#if defined(ANDROID_PLUGINS) +namespace android { + class WebSettings; +} +#endif + +namespace WebCore { + class Element; + class Frame; + class IntSize; + class KURL; + class PluginDatabaseClient; + class PluginPackage; + + typedef HashSet<RefPtr<PluginPackage>, PluginPackageHash> PluginSet; + + class PluginDatabase : public Noncopyable { + public: + PluginDatabase(); + + // The first call to installedPlugins creates the plugin database + // and by default populates it with the plugins installed on the system. + // For testing purposes, it is possible to not populate the database + // automatically, as the plugins might affect the DRT results by + // writing to a.o. stderr. + static PluginDatabase* installedPlugins(bool populate = true); + + bool refresh(); + void clear(); + Vector<PluginPackage*> plugins() const; + bool isMIMETypeRegistered(const String& mimeType); + void addExtraPluginDirectory(const String&); + + static bool isPreferredPluginDirectory(const String& directory); + static int preferredPluginCompare(const void*, const void*); + + PluginPackage* findPlugin(const KURL&, String& mimeType); + PluginPackage* pluginForMIMEType(const String& mimeType); + void setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin); + + void setPluginDirectories(const Vector<String>& directories) + { + clear(); + m_pluginDirectories = directories; + } + + static Vector<String> defaultPluginDirectories(); + Vector<String> pluginDirectories() const { return m_pluginDirectories; } + + String MIMETypeForExtension(const String& extension) const; +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + static bool isPersistentMetadataCacheEnabled(); + static void setPersistentMetadataCacheEnabled(bool isEnabled); + static String persistentMetadataCachePath(); + static void setPersistentMetadataCachePath(const String& persistentMetadataCachePath); +#endif + + private: + void getPluginPathsInDirectories(HashSet<String>&) const; + void getDeletedPlugins(PluginSet&) const; + + // Returns whether the plugin was actually added or not (it won't be added if it's a duplicate of an existing plugin). + bool add(PassRefPtr<PluginPackage>); + void remove(PluginPackage*); +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + void loadPersistentMetadataCache(); + void updatePersistentMetadataCache(); +#endif + + Vector<String> m_pluginDirectories; + HashSet<String> m_registeredMIMETypes; + PluginSet m_plugins; + HashMap<String, RefPtr<PluginPackage> > m_pluginsByPath; + HashMap<String, time_t> m_pluginPathsWithTimes; + +#if defined(ANDROID_PLUGINS) + // Need access to setPluginDirectories() to change the default + // path after startup. + friend class ::android::WebSettings; +#endif + HashMap<String, RefPtr<PluginPackage> > m_preferredPlugins; +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + bool m_persistentMetadataCacheIsLoaded; +#endif + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/plugins/PluginDebug.cpp b/Source/WebCore/plugins/PluginDebug.cpp new file mode 100644 index 0000000..8c3efcb --- /dev/null +++ b/Source/WebCore/plugins/PluginDebug.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginDebug.h" +#include "PlatformString.h" + +#if !LOG_DISABLED + +namespace WebCore { + +static const char* const errorStrings[] = { + "No errors occurred.", /* NPERR_NO_ERROR */ + "Error with no specific error code occurred.", /* NPERR_GENERIC_ERROR */ + "Invalid instance passed to the plug-in.", /* NPERR_INVALID_INSTANCE_ERROR */ + "Function table invalid.", /* NPERR_INVALID_FUNCTABLE_ERROR */ + "Loading of plug-in failed.", /* NPERR_MODULE_LOAD_FAILED_ERROR */ + "Memory allocation failed.", /* NPERR_OUT_OF_MEMORY_ERROR */ + "Plug-in missing or invalid.", /* NPERR_INVALID_PLUGIN_ERROR */ + "Plug-in directory missing or invalid.", /* NPERR_INVALID_PLUGIN_DIR_ERROR */ + "Versions of plug-in and Communicator do not match.", /* NPERR_INCOMPATIBLE_VERSION_ERROR */ + "Parameter missing or invalid.", /* NPERR_INVALID_PARAM */ + "URL missing or invalid.", /* NPERR_INVALID_URL */ + "File missing or invalid.", /* NPERR_FILE_NOT_FOUND */ + "Stream contains no data.", /* NPERR_NO_DATA */ + "Seekable stream expected.", /* NPERR_STREAM_NOT_SEEKABLE */ + "Unknown error code" +}; + +#ifdef XP_MACOSX +static const char* const drawingModels[] = { + "NPDrawingModelQuickDraw", + "NPDrawingModelCoreGraphics", + "NPDrawingModelOpenGL", + "NPDrawingModelCoreAnimation" +}; + +static const char* const eventModels[] = { + "NPEventModelCarbon", + "NPEventModelCocoa" +}; +#endif //XP_MACOSX + +const char* prettyNameForNPError(NPError error) +{ + return errorStrings[error]; +} + +#ifdef XP_MACOSX +const char* prettyNameForDrawingModel(NPDrawingModel drawingModel) +{ + return drawingModels[drawingModel]; +} + +const char* prettyNameForEventModel(NPEventModel eventModel) +{ + return eventModels[eventModel]; +} +#endif //XP_MACOSX + +CString prettyNameForNPNVariable(NPNVariable variable) +{ + switch (variable) { + case NPNVxDisplay: return "NPNVxDisplay"; + case NPNVxtAppContext: return "NPNVxtAppContext"; + case NPNVnetscapeWindow: return "NPNVnetscapeWindow"; + case NPNVjavascriptEnabledBool: return "NPNVjavascriptEnabledBool"; + case NPNVasdEnabledBool: return "NPNVasdEnabledBool"; + case NPNVisOfflineBool: return "NPNVisOfflineBool"; + + case NPNVserviceManager: return "NPNVserviceManager (not supported)"; + case NPNVDOMElement: return "NPNVDOMElement (not supported)"; + case NPNVDOMWindow: return "NPNVDOMWindow (not supported)"; + case NPNVToolkit: return "NPNVToolkit (not supported)"; + case NPNVSupportsXEmbedBool: return "NPNVSupportsXEmbedBool (not supported)"; + + case NPNVWindowNPObject: return "NPNVWindowNPObject"; + case NPNVPluginElementNPObject: return "NPNVPluginElementNPObject"; + case NPNVSupportsWindowless: return "NPNVSupportsWindowless"; + case NPNVprivateModeBool: return "NPNVprivateModeBool"; + +#ifdef XP_MACOSX + case NPNVpluginDrawingModel: return "NPNVpluginDrawingModel"; +#ifndef NP_NO_QUICKDRAW + case NPNVsupportsQuickDrawBool: return "NPNVsupportsQuickDrawBool"; +#endif + case NPNVsupportsCoreGraphicsBool: return "NPNVsupportsCoreGraphicsBool"; + case NPNVsupportsOpenGLBool: return "NPNVsupportsOpenGLBool"; + case NPNVsupportsCoreAnimationBool: return "NPNVsupportsCoreAnimationBool"; +#ifndef NP_NO_CARBON + case NPNVsupportsCarbonBool: return "NPNVsupportsCarbonBool"; +#endif + case NPNVsupportsCocoaBool: return "NPNVsupportsCocoaBool"; +#endif + + default: return "Unknown variable"; + } +} + +CString prettyNameForNPPVariable(NPPVariable variable, void* value) +{ + switch (variable) { + case NPPVpluginNameString: return "NPPVpluginNameString"; + case NPPVpluginDescriptionString: return "NPPVpluginDescriptionString"; + case NPPVpluginWindowBool: return "NPPVpluginWindowBool"; + case NPPVpluginTransparentBool: return "NPPVpluginTransparentBool"; + + case NPPVjavaClass: return "NPPVjavaClass (not supported)"; + case NPPVpluginWindowSize: return "NPPVpluginWindowSize (not supported)"; + case NPPVpluginTimerInterval: return "NPPVpluginTimerInterval (not supported)"; + case NPPVpluginScriptableInstance: return "NPPVpluginScriptableInstance (not supported)"; + case NPPVpluginScriptableIID: return "NPPVpluginScriptableIID (not supported)"; + case NPPVjavascriptPushCallerBool: return "NPPVjavascriptPushCallerBool (not supported)"; + case NPPVpluginKeepLibraryInMemory: return "NPPVpluginKeepLibraryInMemory (not supported)"; + case NPPVpluginNeedsXEmbed: return "NPPVpluginNeedsXEmbed (not supported)"; + + case NPPVpluginScriptableNPObject: return "NPPVpluginScriptableNPObject"; + + case NPPVformValue: return "NPPVformValue (not supported)"; + case NPPVpluginUrlRequestsDisplayedBool: return "NPPVpluginUrlRequestsDisplayedBool (not supported)"; + + case NPPVpluginWantsAllNetworkStreams: return "NPPVpluginWantsAllNetworkStreams"; + case NPPVpluginCancelSrcStream: return "NPPVpluginCancelSrcStream"; + +#ifdef XP_MACOSX + case NPPVpluginDrawingModel: { + String result("NPPVpluginDrawingModel, "); + result.append(prettyNameForDrawingModel(NPDrawingModel(uintptr_t(value)))); + return result.latin1(); + } + case NPPVpluginEventModel: { + String result("NPPVpluginEventModel, "); + result.append(prettyNameForEventModel(NPEventModel(uintptr_t(value)))); + return result.latin1(); + } + case NPPVpluginCoreAnimationLayer: return "NPPVpluginCoreAnimationLayer"; +#endif + + default: return "Unknown variable"; + } +} + +CString prettyNameForNPNURLVariable(NPNURLVariable variable) +{ + switch (variable) { + case NPNURLVCookie: return "NPNURLVCookie"; + case NPNURLVProxy: return "NPNURLVProxy"; + default: return "Unknown variable"; + } +} +} // namespace WebCore + +#endif // !LOG_DISABLED diff --git a/Source/WebCore/plugins/PluginDebug.h b/Source/WebCore/plugins/PluginDebug.h new file mode 100644 index 0000000..017e686 --- /dev/null +++ b/Source/WebCore/plugins/PluginDebug.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginDebug_h +#define PluginDebug_h + +#include "Logging.h" +#include "npruntime_internal.h" +#include <wtf/text/CString.h> + +#define LOG_NPERROR(err) if (err != NPERR_NO_ERROR) LOG_VERBOSE(Plugins, "%s\n", prettyNameForNPError(err)) +#define LOG_PLUGIN_NET_ERROR() LOG_VERBOSE(Plugins, "Stream failed due to problems with network, disk I/O, lack of memory, or other problems.\n") + +#if !LOG_DISABLED + +namespace WebCore { + +const char* prettyNameForNPError(NPError error); + +CString prettyNameForNPNVariable(NPNVariable variable); +CString prettyNameForNPPVariable(NPPVariable variable, void* value); +CString prettyNameForNPNURLVariable(NPNURLVariable variable); + +#ifdef XP_MACOSX +const char* prettyNameForDrawingModel(NPDrawingModel drawingModel); +const char* prettyNameForEventModel(NPEventModel eventModel); +#endif + +} // namespace WebCore + +#endif // !LOG_DISABLED + +#endif // PluginDebug_h diff --git a/Source/WebCore/plugins/PluginMainThreadScheduler.cpp b/Source/WebCore/plugins/PluginMainThreadScheduler.cpp new file mode 100644 index 0000000..7528676 --- /dev/null +++ b/Source/WebCore/plugins/PluginMainThreadScheduler.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginMainThreadScheduler.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +PluginMainThreadScheduler& PluginMainThreadScheduler::scheduler() +{ + DEFINE_STATIC_LOCAL(PluginMainThreadScheduler, scheduler, ()); + + return scheduler; +} + +PluginMainThreadScheduler::PluginMainThreadScheduler() + : m_callPending(false) +{ +} + +void PluginMainThreadScheduler::scheduleCall(NPP npp, MainThreadFunction function, void* userData) +{ + MutexLocker lock(m_queueMutex); + + CallQueueMap::iterator it = m_callQueueMap.find(npp); + if (it == m_callQueueMap.end()) + return; + + it->second.append(Call(function, userData)); + + if (!m_callPending) { + callOnMainThread(mainThreadCallback, this); + m_callPending = true; + } +} + +void PluginMainThreadScheduler::registerPlugin(NPP npp) +{ + MutexLocker lock(m_queueMutex); + + ASSERT(!m_callQueueMap.contains(npp)); + m_callQueueMap.set(npp, Deque<Call>()); +} + +void PluginMainThreadScheduler::unregisterPlugin(NPP npp) +{ + MutexLocker lock(m_queueMutex); + + ASSERT(m_callQueueMap.contains(npp)); + m_callQueueMap.remove(npp); +} + +void PluginMainThreadScheduler::dispatchCallsForPlugin(NPP npp, const Deque<Call>& calls) +{ + Deque<Call>::const_iterator end = calls.end(); + for (Deque<Call>::const_iterator it = calls.begin(); it != end; ++it) { + // Check if the plug-in has been destroyed. + { + MutexLocker lock(m_queueMutex); + if (!m_callQueueMap.contains(npp)) + return; + } + + (*it).performCall(); + } +} + +void PluginMainThreadScheduler::dispatchCalls() +{ + m_queueMutex.lock(); + CallQueueMap copy(m_callQueueMap); + + { + // Empty all the queues in the original map + CallQueueMap::iterator end = m_callQueueMap.end(); + for (CallQueueMap::iterator it = m_callQueueMap.begin(); it != end; ++it) + it->second.clear(); + } + + m_callPending = false; + m_queueMutex.unlock(); + + CallQueueMap::iterator end = copy.end(); + for (CallQueueMap::iterator it = copy.begin(); it != end; ++it) + dispatchCallsForPlugin(it->first, it->second); +} + +void PluginMainThreadScheduler::mainThreadCallback(void* context) +{ + static_cast<PluginMainThreadScheduler*>(context)->dispatchCalls(); +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/PluginMainThreadScheduler.h b/Source/WebCore/plugins/PluginMainThreadScheduler.h new file mode 100644 index 0000000..610e89c --- /dev/null +++ b/Source/WebCore/plugins/PluginMainThreadScheduler.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginMainThreadScheduler_h +#define PluginMainThreadScheduler_h + +#include <wtf/Deque.h> +#include <wtf/HashMap.h> +#include <wtf/MainThread.h> +#include <wtf/Threading.h> + +typedef struct _NPP NPP_t; +typedef NPP_t* NPP; + +namespace WebCore { + +class PluginMainThreadScheduler : public Noncopyable { +public: + typedef void MainThreadFunction(void*); + + static PluginMainThreadScheduler& scheduler(); + + void scheduleCall(NPP, MainThreadFunction*, void* userData); + + void registerPlugin(NPP); + void unregisterPlugin(NPP); + +private: + PluginMainThreadScheduler(); + void dispatchCalls(); + + class Call; + + void dispatchCallsForPlugin(NPP, const Deque<Call>& calls); + typedef HashMap<NPP, Deque<Call> > CallQueueMap; + + static void mainThreadCallback(void* context); + + class Call { + public: + Call(MainThreadFunction* function, void* userData) + : m_function(function) + , m_userData(userData) + { + } + + void performCall() const + { + m_function(m_userData); + } + + private: + MainThreadFunction* m_function; + void* m_userData; + }; + + bool m_callPending; + CallQueueMap m_callQueueMap; + Mutex m_queueMutex; +}; + +} // namespace WebCore + +#endif // PluginMainThreadScheduler_h diff --git a/Source/WebCore/plugins/PluginPackage.cpp b/Source/WebCore/plugins/PluginPackage.cpp new file mode 100644 index 0000000..10149bf --- /dev/null +++ b/Source/WebCore/plugins/PluginPackage.cpp @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2009 Holger Hans Peter Freyther + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginPackage.h" + +#include "MIMETypeRegistry.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include "Timer.h" +#include "npruntime_impl.h" +#include <string.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +PluginPackage::~PluginPackage() +{ + // This destructor gets called during refresh() if PluginDatabase's + // PluginSet hash is already populated, as it removes items from + // the hash table. Calling the destructor on a loaded plug-in of + // course would cause a crash, so we check to call unload before we + // ASSERT. + // FIXME: There is probably a better way to fix this. + if (!m_loadCount) + unloadWithoutShutdown(); + else + unload(); + + ASSERT(!m_isLoaded); +} + +void PluginPackage::freeLibrarySoon() +{ + ASSERT(!m_freeLibraryTimer.isActive()); + ASSERT(m_module); + ASSERT(!m_loadCount); + + m_freeLibraryTimer.startOneShot(0); +} + +void PluginPackage::freeLibraryTimerFired(Timer<PluginPackage>*) +{ + ASSERT(m_module); + ASSERT(!m_loadCount); + + unloadModule(m_module); + m_module = 0; +} + + +int PluginPackage::compare(const PluginPackage& compareTo) const +{ + // Sort plug-ins that allow multiple instances first. + bool AallowsMultipleInstances = !quirks().contains(PluginQuirkDontAllowMultipleInstances); + bool BallowsMultipleInstances = !compareTo.quirks().contains(PluginQuirkDontAllowMultipleInstances); + if (AallowsMultipleInstances != BallowsMultipleInstances) + return AallowsMultipleInstances ? -1 : 1; + + // Sort plug-ins in a preferred path first. + bool AisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(parentDirectory()); + bool BisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(compareTo.parentDirectory()); + if (AisInPreferredDirectory != BisInPreferredDirectory) + return AisInPreferredDirectory ? -1 : 1; + + int diff = strcmp(name().utf8().data(), compareTo.name().utf8().data()); + if (diff) + return diff; + + diff = compareFileVersion(compareTo.version()); + if (diff) + return diff; + + return strcmp(parentDirectory().utf8().data(), compareTo.parentDirectory().utf8().data()); +} + +PluginPackage::PluginPackage(const String& path, const time_t& lastModified) + : m_isEnabled(true) + , m_isLoaded(false) + , m_loadCount(0) + , m_path(path) + , m_moduleVersion(0) + , m_module(0) + , m_lastModified(lastModified) + , m_freeLibraryTimer(this, &PluginPackage::freeLibraryTimerFired) +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + , m_infoIsFromCache(true) +#endif +{ + m_fileName = pathGetFileName(m_path); + m_parentDirectory = m_path.left(m_path.length() - m_fileName.length() - 1); +} + +#if !OS(SYMBIAN) +void PluginPackage::unload() +{ + if (!m_isLoaded) + return; + + if (--m_loadCount > 0) + return; + + m_NPP_Shutdown(); + + unloadWithoutShutdown(); +} +#endif // !OS(SYMBIAN) + +void PluginPackage::unloadWithoutShutdown() +{ + if (!m_isLoaded) + return; + + ASSERT(!m_loadCount); + ASSERT(m_module); + + // <rdar://5530519>: Crash when closing tab with pdf file (Reader 7 only) + // If the plugin has subclassed its parent window, as with Reader 7, we may have + // gotten here by way of the plugin's internal window proc forwarding a message to our + // original window proc. If we free the plugin library from here, we will jump back + // to code we just freed when we return, so delay calling FreeLibrary at least until + // the next message loop + freeLibrarySoon(); + + m_isLoaded = false; +} + +void PluginPackage::setEnabled(bool enabled) +{ + m_isEnabled = enabled; +} + +PassRefPtr<PluginPackage> PluginPackage::createPackage(const String& path, const time_t& lastModified) +{ + RefPtr<PluginPackage> package = adoptRef(new PluginPackage(path, lastModified)); + + if (!package->fetchInfo()) + return 0; + + return package.release(); +} + +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) +PassRefPtr<PluginPackage> PluginPackage::createPackageFromCache(const String& path, const time_t& lastModified, const String& name, const String& description, const String& mimeDescription) +{ + RefPtr<PluginPackage> package = adoptRef(new PluginPackage(path, lastModified)); + package->m_name = name; + package->m_description = description; + package->determineModuleVersionFromDescription(); + package->setMIMEDescription(mimeDescription); + package->m_infoIsFromCache = true; + return package.release(); +} +#endif + +#if defined(XP_UNIX) +void PluginPackage::determineQuirks(const String& mimeType) +{ + if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { + // Because a single process cannot create multiple VMs, and we cannot reliably unload a + // Java VM, we cannot unload the Java plugin, or we'll lose reference to our only VM + m_quirks.add(PluginQuirkDontUnloadPlugin); + + // Setting the window region to an empty region causes bad scrolling repaint problems + // with the Java plug-in. + m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling); + return; + } + + if (mimeType == "application/x-shockwave-flash") { + static const PlatformModuleVersion flashTenVersion(0x0a000000); + + if (compareFileVersion(flashTenVersion) >= 0) { + // Flash 10.0 b218 doesn't like having a NULL window handle + m_quirks.add(PluginQuirkDontSetNullWindowHandleOnDestroy); +#if PLATFORM(QT) + m_quirks.add(PluginQuirkRequiresGtkToolKit); +#endif + } else { + // Flash 9 and older requests windowless plugins if we return a mozilla user agent + m_quirks.add(PluginQuirkWantsMozillaUserAgent); + } + +#if PLATFORM(QT) + // Flash will crash on repeated calls to SetWindow in windowed mode + m_quirks.add(PluginQuirkDontCallSetWindowMoreThanOnce); + +#if CPU(X86_64) + // 64-bit Flash freezes if right-click is sent in windowless mode + m_quirks.add(PluginQuirkIgnoreRightClickInWindowlessMode); +#endif +#endif + + m_quirks.add(PluginQuirkRequiresDefaultScreenDepth); + m_quirks.add(PluginQuirkThrottleInvalidate); + m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages); + m_quirks.add(PluginQuirkFlashURLNotifyBug); + } + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + // Passing a 32-bit depth pixmap to NPAPI plugins is too inefficient. Instead, pass a X Pixmap + // that has same depth as the screen depth since graphics operations are optimized + // for this depth. + m_quirks.add(PluginQuirkRequiresDefaultScreenDepth); +#endif +} +#endif + +#if !OS(WINDOWS) +void PluginPackage::determineModuleVersionFromDescription() +{ + // It's a bit lame to detect the plugin version by parsing it + // from the plugin description string, but it doesn't seem that + // version information is available in any standardized way at + // the module level, like in Windows + + if (m_description.isEmpty()) + return; + + if (m_description.startsWith("Shockwave Flash") && m_description.length() >= 19) { + // The flash version as a PlatformModuleVersion differs on Unix from Windows + // since the revision can be larger than a 8 bits, so we allow it 16 here and + // push the major/minor up 8 bits. Thus on Unix, Flash's version may be + // 0x0a000000 instead of 0x000a0000. + + Vector<String> versionParts; + m_description.substring(16).split(' ', /*allowEmptyEntries =*/ false, versionParts); + if (versionParts.isEmpty()) + return; + + if (versionParts.size() >= 1) { + Vector<String> majorMinorParts; + versionParts[0].split('.', majorMinorParts); + if (majorMinorParts.size() >= 1) { + bool converted = false; + unsigned major = majorMinorParts[0].toUInt(&converted); + if (converted) + m_moduleVersion = (major & 0xff) << 24; + } + if (majorMinorParts.size() == 2) { + bool converted = false; + unsigned minor = majorMinorParts[1].toUInt(&converted); + if (converted) + m_moduleVersion |= (minor & 0xff) << 16; + } + } + + if (versionParts.size() >= 2) { + String revision = versionParts[1]; + if (revision.length() > 1 && (revision[0] == 'r' || revision[0] == 'b')) { + revision.remove(0, 1); + m_moduleVersion |= revision.toInt() & 0xffff; + } + } + } +} +#endif + +#if ENABLE(NETSCAPE_PLUGIN_API) +void PluginPackage::initializeBrowserFuncs() +{ + memset(&m_browserFuncs, 0, sizeof(m_browserFuncs)); + m_browserFuncs.size = sizeof(m_browserFuncs); + m_browserFuncs.version = NPVersion(); + + m_browserFuncs.geturl = NPN_GetURL; + m_browserFuncs.posturl = NPN_PostURL; + m_browserFuncs.requestread = NPN_RequestRead; + m_browserFuncs.newstream = NPN_NewStream; + m_browserFuncs.write = NPN_Write; + m_browserFuncs.destroystream = NPN_DestroyStream; + m_browserFuncs.status = NPN_Status; + m_browserFuncs.uagent = NPN_UserAgent; + m_browserFuncs.memalloc = NPN_MemAlloc; + m_browserFuncs.memfree = NPN_MemFree; + m_browserFuncs.memflush = NPN_MemFlush; + m_browserFuncs.reloadplugins = NPN_ReloadPlugins; + m_browserFuncs.geturlnotify = NPN_GetURLNotify; + m_browserFuncs.posturlnotify = NPN_PostURLNotify; + m_browserFuncs.getvalue = NPN_GetValue; + m_browserFuncs.setvalue = NPN_SetValue; + m_browserFuncs.invalidaterect = NPN_InvalidateRect; + m_browserFuncs.invalidateregion = NPN_InvalidateRegion; + m_browserFuncs.forceredraw = NPN_ForceRedraw; + m_browserFuncs.getJavaEnv = NPN_GetJavaEnv; + m_browserFuncs.getJavaPeer = NPN_GetJavaPeer; + m_browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState; + m_browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState; + m_browserFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall; + + m_browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue; + m_browserFuncs.getstringidentifier = _NPN_GetStringIdentifier; + m_browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers; + m_browserFuncs.getintidentifier = _NPN_GetIntIdentifier; + m_browserFuncs.identifierisstring = _NPN_IdentifierIsString; + m_browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier; + m_browserFuncs.intfromidentifier = _NPN_IntFromIdentifier; + m_browserFuncs.createobject = _NPN_CreateObject; + m_browserFuncs.retainobject = _NPN_RetainObject; + m_browserFuncs.releaseobject = _NPN_ReleaseObject; + m_browserFuncs.invoke = _NPN_Invoke; + m_browserFuncs.invokeDefault = _NPN_InvokeDefault; + m_browserFuncs.evaluate = _NPN_Evaluate; + m_browserFuncs.getproperty = _NPN_GetProperty; + m_browserFuncs.setproperty = _NPN_SetProperty; + m_browserFuncs.removeproperty = _NPN_RemoveProperty; + m_browserFuncs.hasproperty = _NPN_HasProperty; + m_browserFuncs.hasmethod = _NPN_HasMethod; + m_browserFuncs.setexception = _NPN_SetException; + m_browserFuncs.enumerate = _NPN_Enumerate; + m_browserFuncs.construct = _NPN_Construct; + m_browserFuncs.getvalueforurl = NPN_GetValueForURL; + m_browserFuncs.setvalueforurl = NPN_SetValueForURL; + m_browserFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo; +} +#endif + +#if ENABLE(PLUGIN_PACKAGE_SIMPLE_HASH) +unsigned PluginPackage::hash() const +{ + unsigned hashCodes[] = { + m_path.impl()->hash(), + m_lastModified + }; + + return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); +} + +bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b) +{ + return a.m_description == b.m_description; +} +#endif + +int PluginPackage::compareFileVersion(const PlatformModuleVersion& compareVersion) const +{ + // return -1, 0, or 1 if plug-in version is less than, equal to, or greater than + // the passed version + +#if OS(WINDOWS) + if (m_moduleVersion.mostSig != compareVersion.mostSig) + return m_moduleVersion.mostSig > compareVersion.mostSig ? 1 : -1; + if (m_moduleVersion.leastSig != compareVersion.leastSig) + return m_moduleVersion.leastSig > compareVersion.leastSig ? 1 : -1; +#else + if (m_moduleVersion != compareVersion) + return m_moduleVersion > compareVersion ? 1 : -1; +#endif + + return 0; +} + +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) +bool PluginPackage::ensurePluginLoaded() +{ + if (!m_infoIsFromCache) + return m_isLoaded; + + m_quirks = PluginQuirkSet(); + m_name = String(); + m_description = String(); + m_fullMIMEDescription = String(); + m_moduleVersion = 0; + + return fetchInfo(); +} +#endif + +} diff --git a/Source/WebCore/plugins/PluginPackage.h b/Source/WebCore/plugins/PluginPackage.h new file mode 100644 index 0000000..f4d1dac --- /dev/null +++ b/Source/WebCore/plugins/PluginPackage.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginPackage_h +#define PluginPackage_h + +#include "FileSystem.h" +#include "PlatformString.h" +#include "PluginQuirkSet.h" +#include "Timer.h" +#include "npruntime_internal.h" +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> +#include <wtf/text/StringHash.h> + +#if OS(SYMBIAN) +class QPluginLoader; +class NPInterface; +#endif + +namespace WebCore { + typedef HashMap<String, String> MIMEToDescriptionsMap; + typedef HashMap<String, Vector<String> > MIMEToExtensionsMap; + + class PluginPackage : public RefCounted<PluginPackage> { + public: + ~PluginPackage(); + static PassRefPtr<PluginPackage> createPackage(const String& path, const time_t& lastModified); +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + static PassRefPtr<PluginPackage> createPackageFromCache(const String& path, const time_t& lastModified, const String& name, const String& description, const String& mimeDescription); +#endif + + const String& name() const { return m_name; } + const String& description() const { return m_description; } + const String& path() const { return m_path; } + const String& fileName() const { return m_fileName; } + const String& parentDirectory() const { return m_parentDirectory; } + uint16_t NPVersion() const; + time_t lastModified() const { return m_lastModified; } + + const MIMEToDescriptionsMap& mimeToDescriptions() const { return m_mimeToDescriptions; } + const MIMEToExtensionsMap& mimeToExtensions() const { return m_mimeToExtensions; } + + unsigned hash() const; + static bool equal(const PluginPackage& a, const PluginPackage& b); + + bool load(); + void unload(); + void unloadWithoutShutdown(); + + bool isEnabled() const { return m_isEnabled; } + void setEnabled(bool); + + const NPPluginFuncs* pluginFuncs() const { return &m_pluginFuncs; } + int compareFileVersion(const PlatformModuleVersion&) const; + int compare(const PluginPackage&) const; + PluginQuirkSet quirks() const { return m_quirks; } + const PlatformModuleVersion& version() const { return m_moduleVersion; } +#if OS(SYMBIAN) + NPInterface* npInterface() const { return m_npInterface; } +#endif // OS(SYMBIAN) + +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + bool ensurePluginLoaded(); + void setMIMEDescription(const String& mimeDescription); + String fullMIMEDescription() const { return m_fullMIMEDescription;} +#endif + private: + PluginPackage(const String& path, const time_t& lastModified); + +#if OS(SYMBIAN) + NPInterface* m_npInterface; + QPluginLoader* m_pluginLoader; +#endif // OS(SYMBIAN) + bool fetchInfo(); + bool isPluginBlacklisted(); + void determineQuirks(const String& mimeType); + + void determineModuleVersionFromDescription(); + void initializeBrowserFuncs(); + + bool m_isEnabled; + bool m_isLoaded; + int m_loadCount; + + String m_description; + String m_path; + String m_fileName; + String m_name; + String m_parentDirectory; + + PlatformModuleVersion m_moduleVersion; + + MIMEToDescriptionsMap m_mimeToDescriptions; + MIMEToExtensionsMap m_mimeToExtensions; + + PlatformModule m_module; + time_t m_lastModified; + + NPP_ShutdownProcPtr m_NPP_Shutdown; + NPPluginFuncs m_pluginFuncs; + NPNetscapeFuncs m_browserFuncs; + + void freeLibrarySoon(); + void freeLibraryTimerFired(Timer<PluginPackage>*); + Timer<PluginPackage> m_freeLibraryTimer; + + PluginQuirkSet m_quirks; +#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) + String m_fullMIMEDescription; + bool m_infoIsFromCache; +#endif + }; + + struct PluginPackageHash { + static unsigned hash(const uintptr_t key) { return reinterpret_cast<PluginPackage*>(key)->hash(); } + static unsigned hash(const RefPtr<PluginPackage>& key) { return key->hash(); } + + static bool equal(const uintptr_t a, const uintptr_t b) { return equal(reinterpret_cast<PluginPackage*>(a), reinterpret_cast<PluginPackage*>(b)); } + static bool equal(const RefPtr<PluginPackage>& a, const RefPtr<PluginPackage>& b) { return PluginPackage::equal(*a.get(), *b.get()); } + static const bool safeToCompareToEmptyOrDeleted = false; + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/plugins/PluginPackageNone.cpp b/Source/WebCore/plugins/PluginPackageNone.cpp new file mode 100644 index 0000000..f7e2a6b --- /dev/null +++ b/Source/WebCore/plugins/PluginPackageNone.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Kevin Ollivier <kevino@theolliviers.com> All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginPackage.h" + +namespace WebCore { + +void PluginPackage::determineQuirks(const String&) +{ +} + +bool PluginPackage::fetchInfo() +{ + return false; +} + +bool PluginPackage::load() +{ + return false; +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +uint16_t PluginPackage::NPVersion() const +{ + return 0; +} +#endif + +} diff --git a/Source/WebCore/plugins/PluginQuirkSet.h b/Source/WebCore/plugins/PluginQuirkSet.h new file mode 100644 index 0000000..1a684fe --- /dev/null +++ b/Source/WebCore/plugins/PluginQuirkSet.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginQuirkSet_h +#define PluginQuirkSet_h + + +namespace WebCore { + + enum PluginQuirk { + PluginQuirkWantsMozillaUserAgent = 1 << 0, + PluginQuirkDeferFirstSetWindowCall = 1 << 1, + PluginQuirkThrottleInvalidate = 1 << 2, + PluginQuirkRemoveWindowlessVideoParam = 1 << 3, + PluginQuirkThrottleWMUserPlusOneMessages = 1 << 4, + PluginQuirkDontUnloadPlugin = 1 << 5, + PluginQuirkDontCallWndProcForSameMessageRecursively = 1 << 6, + PluginQuirkHasModalMessageLoop = 1 << 7, + PluginQuirkFlashURLNotifyBug = 1 << 8, + PluginQuirkDontClipToZeroRectWhenScrolling = 1 << 9, + PluginQuirkDontSetNullWindowHandleOnDestroy = 1 << 10, + PluginQuirkDontAllowMultipleInstances = 1 << 11, + PluginQuirkRequiresGtkToolKit = 1 << 12, + PluginQuirkRequiresDefaultScreenDepth = 1 << 13, + PluginQuirkDontCallSetWindowMoreThanOnce = 1 << 14, + PluginQuirkIgnoreRightClickInWindowlessMode = 1 << 15 + }; + + class PluginQuirkSet { + public: + PluginQuirkSet() : m_quirks(0) { } + void add(PluginQuirk quirk) { m_quirks |= quirk; } + bool contains(PluginQuirk quirk) const { return m_quirks & quirk; } + private: + unsigned m_quirks; + }; + +} // namespace WebCore + +#endif // PluginQuirkSet_h diff --git a/Source/WebCore/plugins/PluginStrategy.h b/Source/WebCore/plugins/PluginStrategy.h new file mode 100644 index 0000000..9d8a7b5 --- /dev/null +++ b/Source/WebCore/plugins/PluginStrategy.h @@ -0,0 +1,50 @@ +/* + * 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 PluginStrategy_h +#define PluginStrategy_h + +#if USE(PLATFORM_STRATEGIES) + +#include "PluginData.h" + +namespace WebCore { + +class Page; + +class PluginStrategy { +public: + virtual void refreshPlugins() = 0; + virtual void getPluginInfo(const Page*, Vector<PluginInfo>&) = 0; + +protected: + virtual ~PluginStrategy() { } +}; + +} // namespace WebCore + +#endif // USE(PLATFORM_STRATEGIES) + +#endif // PluginStrategy_h diff --git a/Source/WebCore/plugins/PluginStream.cpp b/Source/WebCore/plugins/PluginStream.cpp new file mode 100644 index 0000000..f9021a8 --- /dev/null +++ b/Source/WebCore/plugins/PluginStream.cpp @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginStream.h" + +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "PluginDebug.h" +#include "ResourceLoadScheduler.h" +#include "SharedBuffer.h" +#include "SubresourceLoader.h" +#include <StringExtras.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> + +// We use -2 here because some plugins like to return -1 to indicate error +// and this way we won't clash with them. +static const int WebReasonNone = -2; + +using std::max; +using std::min; + +namespace WebCore { + +typedef HashMap<NPStream*, NPP> StreamMap; +static StreamMap& streams() +{ + static StreamMap staticStreams; + return staticStreams; +} + +PluginStream::PluginStream(PluginStreamClient* client, Frame* frame, const ResourceRequest& resourceRequest, bool sendNotification, void* notifyData, const NPPluginFuncs* pluginFuncs, NPP instance, const PluginQuirkSet& quirks) + : m_resourceRequest(resourceRequest) + , m_client(client) + , m_frame(frame) + , m_notifyData(notifyData) + , m_sendNotification(sendNotification) + , m_streamState(StreamBeforeStarted) + , m_loadManually(false) + , m_delayDeliveryTimer(this, &PluginStream::delayDeliveryTimerFired) + , m_deliveryData(0) + , m_tempFileHandle(invalidPlatformFileHandle) + , m_pluginFuncs(pluginFuncs) + , m_instance(instance) + , m_quirks(quirks) +{ + ASSERT(m_instance); + + m_stream.url = 0; + m_stream.ndata = 0; + m_stream.pdata = 0; + m_stream.end = 0; + m_stream.notifyData = 0; + m_stream.lastmodified = 0; + + streams().add(&m_stream, m_instance); +} + +PluginStream::~PluginStream() +{ + ASSERT(m_streamState != StreamStarted); + ASSERT(!m_loader); + + fastFree((char*)m_stream.url); + + streams().remove(&m_stream); +} + +void PluginStream::start() +{ + ASSERT(!m_loadManually); + m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(m_frame, this, m_resourceRequest); +} + +void PluginStream::stop() +{ + m_streamState = StreamStopped; + + if (m_loadManually) { + ASSERT(!m_loader); + + DocumentLoader* documentLoader = m_frame->loader()->activeDocumentLoader(); + ASSERT(documentLoader); + + if (documentLoader->isLoadingMainResource()) + documentLoader->cancelMainResourceLoad(m_frame->loader()->cancelledError(m_resourceRequest)); + + return; + } + + if (m_loader) { + m_loader->cancel(); + m_loader = 0; + } + + m_client = 0; +} + +void PluginStream::startStream() +{ + ASSERT(m_streamState == StreamBeforeStarted); + + const KURL& responseURL = m_resourceResponse.url(); + + // Some plugins (Flash) expect that javascript URLs are passed back decoded as this is the + // format used when requesting the URL. + if (protocolIsJavaScript(responseURL)) + m_stream.url = fastStrDup(decodeURLEscapeSequences(responseURL.string()).utf8().data()); + else + m_stream.url = fastStrDup(responseURL.string().utf8().data()); + + CString mimeTypeStr = m_resourceResponse.mimeType().utf8(); + + long long expectedContentLength = m_resourceResponse.expectedContentLength(); + + if (m_resourceResponse.isHTTP()) { + Vector<UChar> stringBuilder; + String separator(": "); + + String statusLine = makeString("HTTP ", String::number(m_resourceResponse.httpStatusCode()), " OK\n"); + stringBuilder.append(statusLine.characters(), statusLine.length()); + + HTTPHeaderMap::const_iterator end = m_resourceResponse.httpHeaderFields().end(); + for (HTTPHeaderMap::const_iterator it = m_resourceResponse.httpHeaderFields().begin(); it != end; ++it) { + stringBuilder.append(it->first.characters(), it->first.length()); + stringBuilder.append(separator.characters(), separator.length()); + stringBuilder.append(it->second.characters(), it->second.length()); + stringBuilder.append('\n'); + } + + m_headers = String::adopt(stringBuilder).utf8(); + + // If the content is encoded (most likely compressed), then don't send its length to the plugin, + // which is only interested in the decoded length, not yet known at the moment. + // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic. + String contentEncoding = m_resourceResponse.httpHeaderField("Content-Encoding"); + if (!contentEncoding.isNull() && contentEncoding != "identity") + expectedContentLength = -1; + } + + m_stream.headers = m_headers.data(); + m_stream.pdata = 0; + m_stream.ndata = this; + m_stream.end = max(expectedContentLength, 0LL); + m_stream.lastmodified = m_resourceResponse.lastModifiedDate(); + m_stream.notifyData = m_notifyData; + + m_transferMode = NP_NORMAL; + m_offset = 0; + m_reason = WebReasonNone; + + // Protect the stream if destroystream is called from within the newstream handler + RefPtr<PluginStream> protect(this); + + // calling into a plug-in could result in re-entrance if the plug-in yields + // control to the system (rdar://5744899). prevent this by deferring further + // loading while calling into the plug-in. + if (m_loader) + m_loader->setDefersLoading(true); + NPError npErr = m_pluginFuncs->newstream(m_instance, (NPMIMEType)mimeTypeStr.data(), &m_stream, false, &m_transferMode); + if (m_loader) + m_loader->setDefersLoading(false); + + // If the stream was destroyed in the call to newstream we return + if (m_reason != WebReasonNone) + return; + + if (npErr != NPERR_NO_ERROR) { + cancelAndDestroyStream(npErr); + return; + } + + m_streamState = StreamStarted; + + if (m_transferMode == NP_NORMAL) + return; + + m_path = openTemporaryFile("WKP", m_tempFileHandle); + + // Something went wrong, cancel loading the stream + if (!isHandleValid(m_tempFileHandle)) + cancelAndDestroyStream(NPRES_NETWORK_ERR); +} + +NPP PluginStream::ownerForStream(NPStream* stream) +{ + return streams().get(stream); +} + +void PluginStream::cancelAndDestroyStream(NPReason reason) +{ + RefPtr<PluginStream> protect(this); + + destroyStream(reason); + stop(); +} + +void PluginStream::destroyStream(NPReason reason) +{ + m_reason = reason; + if (m_reason != NPRES_DONE) { + // Stop any pending data from being streamed + if (m_deliveryData) + m_deliveryData->resize(0); + } else if (m_deliveryData && m_deliveryData->size() > 0) { + // There is more data to be streamed, don't destroy the stream now. + return; + } + destroyStream(); +} + +void PluginStream::destroyStream() +{ + if (m_streamState == StreamStopped) + return; + + ASSERT(m_reason != WebReasonNone); + ASSERT(!m_deliveryData || m_deliveryData->size() == 0); + + closeFile(m_tempFileHandle); + + bool newStreamCalled = m_stream.ndata; + + // Protect from destruction if: + // NPN_DestroyStream is called from NPP_NewStream or + // PluginStreamClient::streamDidFinishLoading() removes the last reference + RefPtr<PluginStream> protect(this); + + if (newStreamCalled) { + if (m_reason == NPRES_DONE && (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)) { + ASSERT(!m_path.isNull()); + + if (m_loader) + m_loader->setDefersLoading(true); + m_pluginFuncs->asfile(m_instance, &m_stream, m_path.data()); + if (m_loader) + m_loader->setDefersLoading(false); + } + + if (m_streamState != StreamBeforeStarted) { + if (m_loader) + m_loader->setDefersLoading(true); + + NPError npErr = m_pluginFuncs->destroystream(m_instance, &m_stream, m_reason); + + if (m_loader) + m_loader->setDefersLoading(false); + + LOG_NPERROR(npErr); + } + + m_stream.ndata = 0; + } + + if (m_sendNotification) { + // Flash 9 can dereference null if we call NPP_URLNotify without first calling NPP_NewStream + // for requests made with NPN_PostURLNotify; see <rdar://5588807> + if (m_loader) + m_loader->setDefersLoading(true); + if (!newStreamCalled && m_quirks.contains(PluginQuirkFlashURLNotifyBug) && + equalIgnoringCase(m_resourceRequest.httpMethod(), "POST")) { + m_transferMode = NP_NORMAL; + m_stream.url = ""; + m_stream.notifyData = m_notifyData; + + static char emptyMimeType[] = ""; + m_pluginFuncs->newstream(m_instance, emptyMimeType, &m_stream, false, &m_transferMode); + m_pluginFuncs->destroystream(m_instance, &m_stream, m_reason); + + // in successful requests, the URL is dynamically allocated and freed in our + // destructor, so reset it to 0 + m_stream.url = 0; + } + m_pluginFuncs->urlnotify(m_instance, m_resourceRequest.url().string().utf8().data(), m_reason, m_notifyData); + if (m_loader) + m_loader->setDefersLoading(false); + } + + m_streamState = StreamStopped; + + if (!m_loadManually && m_client) + m_client->streamDidFinishLoading(this); + + if (!m_path.isNull()) { + String tempFilePath = String::fromUTF8(m_path.data()); + deleteFile(tempFilePath); + } +} + +void PluginStream::delayDeliveryTimerFired(Timer<PluginStream>* timer) +{ + ASSERT(timer == &m_delayDeliveryTimer); + + deliverData(); +} + +void PluginStream::deliverData() +{ + ASSERT(m_deliveryData); + + if (m_streamState == StreamStopped) + // FIXME: We should cancel our job in the SubresourceLoader on error so we don't reach this case + return; + + ASSERT(m_streamState != StreamBeforeStarted); + + if (!m_stream.ndata || m_deliveryData->size() == 0) + return; + + int32_t totalBytes = m_deliveryData->size(); + int32_t totalBytesDelivered = 0; + + if (m_loader) + m_loader->setDefersLoading(true); + while (totalBytesDelivered < totalBytes) { + int32_t deliveryBytes = m_pluginFuncs->writeready(m_instance, &m_stream); + + if (deliveryBytes <= 0) { +#if PLATFORM(ANDROID) +// TODO: This needs to be upstreamed. + if (m_loader) + m_loader->pauseLoad(true); + + // ask the plugin for a delay value. + int delay = deliveryDelay(); + m_delayDeliveryTimer.startOneShot(delay * 0.001); +#else + m_delayDeliveryTimer.startOneShot(0); +#endif + break; + } else { + deliveryBytes = min(deliveryBytes, totalBytes - totalBytesDelivered); + int32_t dataLength = deliveryBytes; + char* data = m_deliveryData->data() + totalBytesDelivered; + + // Write the data + deliveryBytes = m_pluginFuncs->write(m_instance, &m_stream, m_offset, dataLength, (void*)data); + if (deliveryBytes < 0) { + LOG_PLUGIN_NET_ERROR(); + if (m_loader) + m_loader->setDefersLoading(false); + cancelAndDestroyStream(NPRES_NETWORK_ERR); + return; + } + deliveryBytes = min(deliveryBytes, dataLength); + m_offset += deliveryBytes; + totalBytesDelivered += deliveryBytes; + } + } + if (m_loader) + m_loader->setDefersLoading(false); + + if (totalBytesDelivered > 0) { + if (totalBytesDelivered < totalBytes) { + int remainingBytes = totalBytes - totalBytesDelivered; + memmove(m_deliveryData->data(), m_deliveryData->data() + totalBytesDelivered, remainingBytes); + m_deliveryData->resize(remainingBytes); + } else { +#if PLATFORM(ANDROID) +//TODO: This needs to be upstreamed to WebKit. + if (m_loader) + m_loader->pauseLoad(false); +#endif + m_deliveryData->resize(0); + if (m_reason != WebReasonNone) + destroyStream(); + } + } +} + +void PluginStream::sendJavaScriptStream(const KURL& requestURL, const CString& resultString) +{ + didReceiveResponse(0, ResourceResponse(requestURL, "text/plain", resultString.length(), "", "")); + + if (m_streamState == StreamStopped) + return; + + if (!resultString.isNull()) { + didReceiveData(0, resultString.data(), resultString.length()); + if (m_streamState == StreamStopped) + return; + } + + m_loader = 0; + + destroyStream(resultString.isNull() ? NPRES_NETWORK_ERR : NPRES_DONE); +} + +void PluginStream::didReceiveResponse(NetscapePlugInStreamLoader* loader, const ResourceResponse& response) +{ + ASSERT(loader == m_loader); + ASSERT(m_streamState == StreamBeforeStarted); + + m_resourceResponse = response; + + startStream(); +} + +void PluginStream::didReceiveData(NetscapePlugInStreamLoader* loader, const char* data, int length) +{ + ASSERT(loader == m_loader); + ASSERT(m_streamState == StreamStarted); + + // If the plug-in cancels the stream in deliverData it could be deleted, + // so protect it here. + RefPtr<PluginStream> protect(this); + + if (m_transferMode != NP_ASFILEONLY) { + if (!m_deliveryData) + m_deliveryData.set(new Vector<char>); + + int oldSize = m_deliveryData->size(); + m_deliveryData->resize(oldSize + length); + memcpy(m_deliveryData->data() + oldSize, data, length); + +#if PLATFORM(ANDROID) +//TODO: This needs to be upstreamed to WebKit. + if (!m_delayDeliveryTimer.isActive()) +#endif + deliverData(); + } + + if (m_streamState != StreamStopped && isHandleValid(m_tempFileHandle)) { + int bytesWritten = writeToFile(m_tempFileHandle, data, length); + if (bytesWritten != length) + cancelAndDestroyStream(NPRES_NETWORK_ERR); + } +} + +void PluginStream::didFail(NetscapePlugInStreamLoader* loader, const ResourceError&) +{ + ASSERT(loader == m_loader); + + LOG_PLUGIN_NET_ERROR(); + + // destroyStream can result in our being deleted + RefPtr<PluginStream> protect(this); + + destroyStream(NPRES_NETWORK_ERR); + + m_loader = 0; +} + +void PluginStream::didFinishLoading(NetscapePlugInStreamLoader* loader) +{ + ASSERT(loader == m_loader); + ASSERT(m_streamState == StreamStarted); + + // destroyStream can result in our being deleted + RefPtr<PluginStream> protect(this); + + destroyStream(NPRES_DONE); + + m_loader = 0; +} + +bool PluginStream::wantsAllStreams() const +{ + if (!m_pluginFuncs->getvalue) + return false; + + void* result = 0; + if (m_pluginFuncs->getvalue(m_instance, NPPVpluginWantsAllNetworkStreams, &result) != NPERR_NO_ERROR) + return false; + + return result != 0; +} + +#if PLATFORM(ANDROID) +int PluginStream::deliveryDelay() const +{ + if (!m_pluginFuncs->getvalue) + return 0; + + int delay = 0; + if (m_pluginFuncs->getvalue(m_instance, NPPDataDeliveryDelayMs, &delay) != NPERR_NO_ERROR) + return 0; + + return delay; +} +#endif + +} diff --git a/Source/WebCore/plugins/PluginStream.h b/Source/WebCore/plugins/PluginStream.h new file mode 100644 index 0000000..ae69539 --- /dev/null +++ b/Source/WebCore/plugins/PluginStream.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginStream_h +#define PluginStream_h + +#include "FileSystem.h" +#include "KURL.h" +#include "NetscapePlugInStreamLoader.h" +#include "PlatformString.h" +#include "PluginQuirkSet.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "Timer.h" +#include "npruntime_internal.h" +#include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + class Frame; + class PluginStream; + + enum PluginStreamState { StreamBeforeStarted, StreamStarted, StreamStopped }; + + class PluginStreamClient { + public: + virtual ~PluginStreamClient() {} + virtual void streamDidFinishLoading(PluginStream*) {} + }; + + class PluginStream : public RefCounted<PluginStream>, private NetscapePlugInStreamLoaderClient { + public: + static PassRefPtr<PluginStream> create(PluginStreamClient* client, Frame* frame, const ResourceRequest& request, bool sendNotification, void* notifyData, const NPPluginFuncs* functions, NPP instance, const PluginQuirkSet& quirks) + { + return adoptRef(new PluginStream(client, frame, request, sendNotification, notifyData, functions, instance, quirks)); + } + virtual ~PluginStream(); + + void start(); + void stop(); + + void startStream(); + + void setLoadManually(bool loadManually) { m_loadManually = loadManually; } + + void sendJavaScriptStream(const KURL& requestURL, const WTF::CString& resultString); + void cancelAndDestroyStream(NPReason); + + static NPP ownerForStream(NPStream*); + + // NetscapePlugInStreamLoaderClient + virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&); + virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int); + virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&); + virtual void didFinishLoading(NetscapePlugInStreamLoader*); + virtual bool wantsAllStreams() const; + + private: + PluginStream(PluginStreamClient*, Frame*, const ResourceRequest&, bool sendNotification, void* notifyData, const NPPluginFuncs*, NPP instance, const PluginQuirkSet&); + + void deliverData(); + void destroyStream(NPReason); + void destroyStream(); +#if PLATFORM(ANDROID) + int deliveryDelay() const; +#endif + ResourceRequest m_resourceRequest; + ResourceResponse m_resourceResponse; + + PluginStreamClient* m_client; + Frame* m_frame; + RefPtr<NetscapePlugInStreamLoader> m_loader; + void* m_notifyData; + bool m_sendNotification; + PluginStreamState m_streamState; + bool m_loadManually; + + Timer<PluginStream> m_delayDeliveryTimer; + void delayDeliveryTimerFired(Timer<PluginStream>*); + + OwnPtr< Vector<char> > m_deliveryData; + + PlatformFileHandle m_tempFileHandle; + + const NPPluginFuncs* m_pluginFuncs; + NPP m_instance; + uint16_t m_transferMode; + int32_t m_offset; + WTF::CString m_headers; + WTF::CString m_path; + NPReason m_reason; + NPStream m_stream; + PluginQuirkSet m_quirks; + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/plugins/PluginView.cpp b/Source/WebCore/plugins/PluginView.cpp new file mode 100644 index 0000000..f06daf4 --- /dev/null +++ b/Source/WebCore/plugins/PluginView.cpp @@ -0,0 +1,1620 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2010 Girish Ramakrishnan <girish@forwardbias.in> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginView.h" + +#if USE(JSC) +#include "Bridge.h" +#endif +#include "Chrome.h" +#include "CookieJar.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "Image.h" +#include "KeyboardEvent.h" +#include "MIMETypeRegistry.h" +#include "MouseEvent.h" +#include "NotImplemented.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include "PluginMainThreadScheduler.h" +#include "PluginPackage.h" +#include "ProxyServer.h" +#include "RenderBox.h" +#include "RenderObject.h" +#include "ScriptController.h" +#include "ScriptValue.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "npruntime_impl.h" +#include <wtf/ASCIICType.h> + +#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) +#include "PluginMessageThrottlerWin.h" +#endif + +#if defined(ANDROID_PLUGINS) +#include "TouchEvent.h" +#endif + +#if USE(JSC) +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include "c_instance.h" +#include "runtime_root.h" +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> + +using JSC::ExecState; +using JSC::JSLock; +using JSC::JSObject; +using JSC::JSValue; +using JSC::UString; +#endif + +#if ENABLE(NETSCAPE_PLUGIN_API) + +using std::min; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +static int s_callingPlugin; + +typedef HashMap<NPP, PluginView*> InstanceMap; + +static InstanceMap& instanceMap() +{ + static InstanceMap& map = *new InstanceMap; + return map; +} + +static String scriptStringIfJavaScriptURL(const KURL& url) +{ + if (!protocolIsJavaScript(url)) + return String(); + + // This returns an unescaped string + return decodeURLEscapeSequences(url.string().substring(11)); +} + +PluginView* PluginView::s_currentPluginView = 0; + +void PluginView::popPopupsStateTimerFired(Timer<PluginView>*) +{ + popPopupsEnabledState(); +} + +IntRect PluginView::windowClipRect() const +{ + // Start by clipping to our bounds. + IntRect clipRect(m_windowRect); + + // Take our element and get the clip rect from the enclosing layer and frame view. + RenderLayer* layer = m_element->renderer()->enclosingLayer(); + FrameView* parentView = m_element->document()->view(); + clipRect.intersect(parentView->windowClipRectForLayer(layer, true)); + + return clipRect; +} + +void PluginView::setFrameRect(const IntRect& rect) +{ + if (m_element->document()->printing()) + return; + + if (rect != frameRect()) + Widget::setFrameRect(rect); + + updatePluginWidget(); + +#if OS(WINDOWS) || OS(SYMBIAN) + // On Windows and Symbian, always call plugin to change geometry. + setNPWindowRect(rect); +#elif defined(XP_UNIX) + // On Unix, multiple calls to setNPWindow() in windowed mode causes Flash to crash + if (m_mode == NP_FULL || !m_isWindowed) + setNPWindowRect(rect); +#endif +} + +void PluginView::frameRectsChanged() +{ + updatePluginWidget(); +} + +void PluginView::handleEvent(Event* event) +{ + if (!m_plugin || m_isWindowed) + return; + + // Protect the plug-in from deletion while dispatching the event. + RefPtr<PluginView> protect(this); + + if (event->isMouseEvent()) + handleMouseEvent(static_cast<MouseEvent*>(event)); + else if (event->isKeyboardEvent()) + handleKeyboardEvent(static_cast<KeyboardEvent*>(event)); +#if defined(ANDROID_PLUGINS) +#if ENABLE(TOUCH_EVENTS) + else if (event->isTouchEvent()) + handleTouchEvent(static_cast<TouchEvent*>(event)); +#endif + else if (event->type() == eventNames().DOMFocusOutEvent) + handleFocusEvent(false); + else if (event->type() == eventNames().DOMFocusInEvent) + handleFocusEvent(true); +#endif +#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) + else if (event->type() == eventNames().focusoutEvent) + handleFocusOutEvent(); + else if (event->type() == eventNames().focusinEvent) + handleFocusInEvent(); +#endif +} + +void PluginView::init() +{ + if (m_haveInitialized) + return; + + m_haveInitialized = true; + + if (!m_plugin) { + ASSERT(m_status == PluginStatusCanNotFindPlugin); + return; + } + + LOG(Plugins, "PluginView::init(): Initializing plug-in '%s'", m_plugin->name().utf8().data()); + + if (!m_plugin->load()) { + m_plugin = 0; + m_status = PluginStatusCanNotLoadPlugin; + return; + } + + if (!startOrAddToUnstartedList()) { + m_status = PluginStatusCanNotLoadPlugin; + return; + } + + m_status = PluginStatusLoadedSuccessfully; +} + +bool PluginView::startOrAddToUnstartedList() +{ + if (!m_parentFrame->page()) + return false; + + // We only delay starting the plug-in if we're going to kick off the load + // ourselves. Otherwise, the loader will try to deliver data before we've + // started the plug-in. + if (!m_loadManually && !m_parentFrame->page()->canStartMedia()) { + m_parentFrame->document()->addMediaCanStartListener(this); + m_isWaitingToStart = true; + return true; + } + + return start(); +} + +bool PluginView::start() +{ + if (m_isStarted) + return false; + + m_isWaitingToStart = false; + + PluginMainThreadScheduler::scheduler().registerPlugin(m_instance); + + ASSERT(m_plugin); + ASSERT(m_plugin->pluginFuncs()->newp); + + NPError npErr; + { + PluginView::setCurrentPluginView(this); +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + setCallingPlugin(true); + npErr = m_plugin->pluginFuncs()->newp((NPMIMEType)m_mimeType.utf8().data(), m_instance, m_mode, m_paramCount, m_paramNames, m_paramValues, NULL); + setCallingPlugin(false); + LOG_NPERROR(npErr); + PluginView::setCurrentPluginView(0); + } + + if (npErr != NPERR_NO_ERROR) { + m_status = PluginStatusCanNotLoadPlugin; + PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance); + return false; + } + + m_isStarted = true; + + if (!m_url.isEmpty() && !m_loadManually) { + FrameLoadRequest frameLoadRequest(m_parentFrame->document()->securityOrigin()); + frameLoadRequest.resourceRequest().setHTTPMethod("GET"); + frameLoadRequest.resourceRequest().setURL(m_url); +#ifdef ANDROID_PLUGINS + if (!SecurityOrigin::shouldHideReferrer( + m_url, m_parentFrame->loader()->outgoingReferrer())) + frameLoadRequest.resourceRequest().setHTTPReferrer( + m_parentFrame->loader()->outgoingReferrer()); +#endif + load(frameLoadRequest, false, 0); + } + + m_status = PluginStatusLoadedSuccessfully; + + if (!platformStart()) + m_status = PluginStatusCanNotLoadPlugin; + + if (m_status != PluginStatusLoadedSuccessfully) + return false; + + if (parentFrame()->page()) + parentFrame()->page()->didStartPlugin(this); + + return true; +} + +void PluginView::mediaCanStart() +{ + ASSERT(!m_isStarted); + if (!start()) + parentFrame()->loader()->client()->dispatchDidFailToStartPlugin(this); +} + +PluginView::~PluginView() +{ + LOG(Plugins, "PluginView::~PluginView()"); + + ASSERT(!m_lifeSupportTimer.isActive()); + + // If we failed to find the plug-in, we'll return early in our constructor, and + // m_instance will be 0. + if (m_instance) + instanceMap().remove(m_instance); + + if (m_isWaitingToStart) + m_parentFrame->document()->removeMediaCanStartListener(this); + + stop(); + + deleteAllValues(m_requests); + + freeStringArray(m_paramNames, m_paramCount); + freeStringArray(m_paramValues, m_paramCount); + + platformDestroy(); + + m_parentFrame->script()->cleanupScriptObjectsForPlugin(this); + +#if PLATFORM(ANDROID) + // Since we have no legacy plugins to check, we ignore the quirks check. + if (m_plugin) +#else + if (m_plugin && !(m_plugin->quirks().contains(PluginQuirkDontUnloadPlugin))) +#endif + m_plugin->unload(); +} + +void PluginView::stop() +{ + if (!m_isStarted) + return; + + if (parentFrame()->page()) + parentFrame()->page()->didStopPlugin(this); + + LOG(Plugins, "PluginView::stop(): Stopping plug-in '%s'", m_plugin->name().utf8().data()); + + HashSet<RefPtr<PluginStream> > streams = m_streams; + HashSet<RefPtr<PluginStream> >::iterator end = streams.end(); + for (HashSet<RefPtr<PluginStream> >::iterator it = streams.begin(); it != end; ++it) { + (*it)->stop(); + disconnectStream((*it).get()); + } + + ASSERT(m_streams.isEmpty()); + + m_isStarted = false; + +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + +#if ENABLE(NETSCAPE_PLUGIN_API) +#ifdef XP_WIN + // Unsubclass the window + if (m_isWindowed) { +#if OS(WINCE) + WNDPROC currentWndProc = (WNDPROC)GetWindowLong(platformPluginWidget(), GWL_WNDPROC); + + if (currentWndProc == PluginViewWndProc) + SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)m_pluginWndProc); +#else + WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC); + + if (currentWndProc == PluginViewWndProc) + SetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)m_pluginWndProc); +#endif + } +#endif // XP_WIN +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#if !defined(XP_MACOSX) + // Clear the window + m_npWindow.window = 0; + + if (m_plugin->pluginFuncs()->setwindow && !m_plugin->quirks().contains(PluginQuirkDontSetNullWindowHandleOnDestroy)) { + PluginView::setCurrentPluginView(this); + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + } + +#ifdef XP_UNIX + if (m_isWindowed && m_npWindow.ws_info) + delete (NPSetWindowCallbackStruct *)m_npWindow.ws_info; + m_npWindow.ws_info = 0; +#endif + +#endif // !defined(XP_MACOSX) + + PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance); + + NPSavedData* savedData = 0; + PluginView::setCurrentPluginView(this); + setCallingPlugin(true); + NPError npErr = m_plugin->pluginFuncs()->destroy(m_instance, &savedData); + setCallingPlugin(false); + LOG_NPERROR(npErr); + PluginView::setCurrentPluginView(0); + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (savedData) { + // TODO: Actually save this data instead of just discarding it + if (savedData->buf) + NPN_MemFree(savedData->buf); + NPN_MemFree(savedData); + } +#endif + + m_instance->pdata = 0; +} + +void PluginView::setCurrentPluginView(PluginView* pluginView) +{ + s_currentPluginView = pluginView; +} + +PluginView* PluginView::currentPluginView() +{ + return s_currentPluginView; +} + +static char* createUTF8String(const String& str) +{ + CString cstr = str.utf8(); + char* result = reinterpret_cast<char*>(fastMalloc(cstr.length() + 1)); + + strncpy(result, cstr.data(), cstr.length() + 1); + + return result; +} + +void PluginView::performRequest(PluginRequest* request) +{ + if (!m_isStarted) + return; + + // don't let a plugin start any loads if it is no longer part of a document that is being + // displayed unless the loads are in the same frame as the plugin. + const String& targetFrameName = request->frameLoadRequest().frameName(); + if (m_parentFrame->loader()->documentLoader() != m_parentFrame->loader()->activeDocumentLoader() && + (targetFrameName.isNull() || m_parentFrame->tree()->find(targetFrameName) != m_parentFrame)) + return; + + KURL requestURL = request->frameLoadRequest().resourceRequest().url(); + String jsString = scriptStringIfJavaScriptURL(requestURL); + + if (jsString.isNull()) { + // if this is not a targeted request, create a stream for it. otherwise, + // just pass it off to the loader + if (targetFrameName.isEmpty()) { + RefPtr<PluginStream> stream = PluginStream::create(this, m_parentFrame.get(), request->frameLoadRequest().resourceRequest(), request->sendNotification(), request->notifyData(), plugin()->pluginFuncs(), instance(), m_plugin->quirks()); + m_streams.add(stream); + stream->start(); + } else { + // If the target frame is our frame, we could destroy the + // PluginView, so we protect it. <rdar://problem/6991251> + RefPtr<PluginView> protect(this); + + m_parentFrame->loader()->load(request->frameLoadRequest().resourceRequest(), targetFrameName, false); + + // FIXME: <rdar://problem/4807469> This should be sent when the document has finished loading + if (request->sendNotification()) { + PluginView::setCurrentPluginView(this); +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + setCallingPlugin(true); + m_plugin->pluginFuncs()->urlnotify(m_instance, requestURL.string().utf8().data(), NPRES_DONE, request->notifyData()); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + } + } + return; + } + + // Targeted JavaScript requests are only allowed on the frame that contains the JavaScript plugin + // and this has been made sure in ::load. + ASSERT(targetFrameName.isEmpty() || m_parentFrame->tree()->find(targetFrameName) == m_parentFrame); + + // Executing a script can cause the plugin view to be destroyed, so we keep a reference to the parent frame. + RefPtr<Frame> parentFrame = m_parentFrame; + ScriptValue result = m_parentFrame->script()->executeScript(jsString, request->shouldAllowPopups()); + + if (targetFrameName.isNull()) { + String resultString; + +#if USE(JSC) + ScriptState* scriptState = parentFrame->script()->globalObject(pluginWorld())->globalExec(); +#elif USE(V8) + ScriptState* scriptState = 0; // Not used with V8 +#endif + CString cstr; + if (result.getString(scriptState, resultString)) + cstr = resultString.utf8(); + + RefPtr<PluginStream> stream = PluginStream::create(this, m_parentFrame.get(), request->frameLoadRequest().resourceRequest(), request->sendNotification(), request->notifyData(), plugin()->pluginFuncs(), instance(), m_plugin->quirks()); + m_streams.add(stream); + stream->sendJavaScriptStream(requestURL, cstr); + } +} + +void PluginView::requestTimerFired(Timer<PluginView>* timer) +{ + ASSERT(timer == &m_requestTimer); + ASSERT(m_requests.size() > 0); + ASSERT(!m_isJavaScriptPaused); + + PluginRequest* request = m_requests[0]; + m_requests.remove(0); + + // Schedule a new request before calling performRequest since the call to + // performRequest can cause the plugin view to be deleted. + if (m_requests.size() > 0) + m_requestTimer.startOneShot(0); + + performRequest(request); + delete request; +} + +void PluginView::scheduleRequest(PluginRequest* request) +{ + m_requests.append(request); + + if (!m_isJavaScriptPaused) + m_requestTimer.startOneShot(0); +} + +NPError PluginView::load(const FrameLoadRequest& frameLoadRequest, bool sendNotification, void* notifyData) +{ + ASSERT(frameLoadRequest.resourceRequest().httpMethod() == "GET" || frameLoadRequest.resourceRequest().httpMethod() == "POST"); + + KURL url = frameLoadRequest.resourceRequest().url(); + + if (url.isEmpty()) + return NPERR_INVALID_URL; + + // Don't allow requests to be made when the document loader is stopping all loaders. + DocumentLoader* loader = m_parentFrame->loader()->documentLoader(); + if (!loader || loader->isStopping()) + return NPERR_GENERIC_ERROR; + + const String& targetFrameName = frameLoadRequest.frameName(); + String jsString = scriptStringIfJavaScriptURL(url); + + if (!jsString.isNull()) { + // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. + if (!m_parentFrame->script()->canExecuteScripts(NotAboutToExecuteScript)) + return NPERR_GENERIC_ERROR; + + // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. + if (!targetFrameName.isNull() && m_parentFrame->tree()->find(targetFrameName) != m_parentFrame) + return NPERR_INVALID_PARAM; + } else if (!m_parentFrame->document()->securityOrigin()->canDisplay(url)) + return NPERR_GENERIC_ERROR; + + PluginRequest* request = new PluginRequest(frameLoadRequest, sendNotification, notifyData, arePopupsAllowed()); + scheduleRequest(request); + + return NPERR_NO_ERROR; +} + +static KURL makeURL(const KURL& baseURL, const char* relativeURLString) +{ + String urlString = relativeURLString; + + // Strip return characters. + urlString.replace('\n', ""); + urlString.replace('\r', ""); + + return KURL(baseURL, urlString); +} + +NPError PluginView::getURLNotify(const char* url, const char* target, void* notifyData) +{ + FrameLoadRequest frameLoadRequest(m_parentFrame->document()->securityOrigin()); + + frameLoadRequest.setFrameName(target); + frameLoadRequest.resourceRequest().setHTTPMethod("GET"); + frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url)); +#ifdef ANDROID_PLUGINS + if (!SecurityOrigin::shouldHideReferrer( + frameLoadRequest.resourceRequest().url(), m_url)) + frameLoadRequest.resourceRequest().setHTTPReferrer(m_url); +#endif + + return load(frameLoadRequest, true, notifyData); +} + +NPError PluginView::getURL(const char* url, const char* target) +{ + FrameLoadRequest frameLoadRequest(m_parentFrame->document()->securityOrigin()); + + frameLoadRequest.setFrameName(target); + frameLoadRequest.resourceRequest().setHTTPMethod("GET"); + frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url)); +#ifdef ANDROID_PLUGINS + if (!SecurityOrigin::shouldHideReferrer( + frameLoadRequest.resourceRequest().url(), m_url)) + frameLoadRequest.resourceRequest().setHTTPReferrer(m_url); +#endif + + return load(frameLoadRequest, false, 0); +} + +NPError PluginView::postURLNotify(const char* url, const char* target, uint32_t len, const char* buf, NPBool file, void* notifyData) +{ + return handlePost(url, target, len, buf, file, notifyData, true, true); +} + +NPError PluginView::postURL(const char* url, const char* target, uint32_t len, const char* buf, NPBool file) +{ + // As documented, only allow headers to be specified via NPP_PostURL when using a file. + return handlePost(url, target, len, buf, file, 0, false, file); +} + +NPError PluginView::newStream(NPMIMEType type, const char* target, NPStream** stream) +{ + notImplemented(); + // Unsupported + return NPERR_GENERIC_ERROR; +} + +int32_t PluginView::write(NPStream* stream, int32_t len, void* buffer) +{ + notImplemented(); + // Unsupported + return -1; +} + +NPError PluginView::destroyStream(NPStream* stream, NPReason reason) +{ + if (!stream || PluginStream::ownerForStream(stream) != m_instance) + return NPERR_INVALID_INSTANCE_ERROR; + + PluginStream* browserStream = static_cast<PluginStream*>(stream->ndata); + browserStream->cancelAndDestroyStream(reason); + + return NPERR_NO_ERROR; +} + +void PluginView::status(const char* message) +{ + if (Page* page = m_parentFrame->page()) + page->chrome()->setStatusbarText(m_parentFrame.get(), String::fromUTF8(message)); +} + +NPError PluginView::setValue(NPPVariable variable, void* value) +{ + LOG(Plugins, "PluginView::setValue(%s): ", prettyNameForNPPVariable(variable, value).data()); + + switch (variable) { + case NPPVpluginWindowBool: + m_isWindowed = value; + return NPERR_NO_ERROR; + case NPPVpluginTransparentBool: + m_isTransparent = value; + return NPERR_NO_ERROR; +#if defined(XP_MACOSX) + case NPPVpluginDrawingModel: { + // Can only set drawing model inside NPP_New() + if (this != currentPluginView()) + return NPERR_GENERIC_ERROR; + + NPDrawingModel newDrawingModel = NPDrawingModel(uintptr_t(value)); + switch (newDrawingModel) { + case NPDrawingModelCoreGraphics: + m_drawingModel = newDrawingModel; + return NPERR_NO_ERROR; +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: +#endif + case NPDrawingModelCoreAnimation: + default: + LOG(Plugins, "Plugin asked for unsupported drawing model: %s", + prettyNameForDrawingModel(newDrawingModel)); + return NPERR_GENERIC_ERROR; + } + } + + case NPPVpluginEventModel: { + // Can only set event model inside NPP_New() + if (this != currentPluginView()) + return NPERR_GENERIC_ERROR; + + NPEventModel newEventModel = NPEventModel(uintptr_t(value)); + switch (newEventModel) { +#ifndef NP_NO_CARBON + case NPEventModelCarbon: +#endif + case NPEventModelCocoa: + m_eventModel = newEventModel; + return NPERR_NO_ERROR; + + default: + LOG(Plugins, "Plugin asked for unsupported event model: %s", + prettyNameForEventModel(newEventModel)); + return NPERR_GENERIC_ERROR; + } + } +#endif // defined(XP_MACOSX) + +#if PLATFORM(QT) && defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + case NPPVpluginWindowlessLocalBool: + m_renderToImage = true; + return NPERR_NO_ERROR; +#endif + + default: +#ifdef PLUGIN_PLATFORM_SETVALUE + return platformSetValue(variable, value); +#else + notImplemented(); + return NPERR_GENERIC_ERROR; +#endif + } +} + +void PluginView::invalidateTimerFired(Timer<PluginView>* timer) +{ + ASSERT(timer == &m_invalidateTimer); + + for (unsigned i = 0; i < m_invalidRects.size(); i++) + invalidateRect(m_invalidRects[i]); + m_invalidRects.clear(); +} + + +void PluginView::pushPopupsEnabledState(bool state) +{ + m_popupStateStack.append(state); +} + +void PluginView::popPopupsEnabledState() +{ + m_popupStateStack.removeLast(); +} + +bool PluginView::arePopupsAllowed() const +{ + if (!m_popupStateStack.isEmpty()) + return m_popupStateStack.last(); + + return false; +} + +void PluginView::setJavaScriptPaused(bool paused) +{ + if (m_isJavaScriptPaused == paused) + return; + m_isJavaScriptPaused = paused; + + if (m_isJavaScriptPaused) + m_requestTimer.stop(); + else if (!m_requests.isEmpty()) + m_requestTimer.startOneShot(0); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +NPObject* PluginView::npObject() +{ + NPObject* object = 0; + + if (!m_isStarted || !m_plugin || !m_plugin->pluginFuncs()->getvalue) + return 0; + + // On Windows, calling Java's NPN_GetValue can allow the message loop to + // run, allowing loading to take place or JavaScript to run. Protect the + // PluginView from destruction. <rdar://problem/6978804> + RefPtr<PluginView> protect(this); + + NPError npErr; + { + PluginView::setCurrentPluginView(this); +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + setCallingPlugin(true); + npErr = m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScriptableNPObject, &object); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + } + + if (npErr != NPERR_NO_ERROR) + return 0; + + return object; +} +#endif + +#if USE(JSC) +PassRefPtr<JSC::Bindings::Instance> PluginView::bindingInstance() +{ +#if ENABLE(NETSCAPE_PLUGIN_API) + NPObject* object = npObject(); + if (!object) + return 0; + + if (hasOneRef()) { + // The renderer for the PluginView was destroyed during the above call, and + // the PluginView will be destroyed when this function returns, so we + // return null. + return 0; + } + + RefPtr<JSC::Bindings::RootObject> root = m_parentFrame->script()->createRootObject(this); + RefPtr<JSC::Bindings::Instance> instance = JSC::Bindings::CInstance::create(object, root.release()); + + _NPN_ReleaseObject(object); + + return instance.release(); +#else + return 0; +#endif +} +#endif + +#if USE(V8) +// This is really JS engine independent +NPObject* PluginView::getNPObject() { +#if ENABLE(NETSCAPE_PLUGIN_API) + if (!m_plugin || !m_plugin->pluginFuncs()->getvalue) + return 0; + + NPObject* object = 0; + + NPError npErr; + { + PluginView::setCurrentPluginView(this); + setCallingPlugin(true); + npErr = m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScriptableNPObject, &object); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + } + + if (npErr != NPERR_NO_ERROR || !object) + return 0; + + // Bindings::CInstance (used in JSC version) retains the object, so in ~PluginView() it calls + // cleanupScriptObjectsForPlugin() to releases the object. To maintain the reference count, + // don't call _NPN_ReleaseObject(object) here. + return object; +#else + return 0; +#endif // NETSCAPE_PLUGIN_API +} +#endif // V8 + +void PluginView::disconnectStream(PluginStream* stream) +{ + ASSERT(m_streams.contains(stream)); + + m_streams.remove(stream); +} + +void PluginView::setParameters(const Vector<String>& paramNames, const Vector<String>& paramValues) +{ + ASSERT(paramNames.size() == paramValues.size()); + + unsigned size = paramNames.size(); + unsigned paramCount = 0; + + m_paramNames = reinterpret_cast<char**>(fastMalloc(sizeof(char*) * size)); + m_paramValues = reinterpret_cast<char**>(fastMalloc(sizeof(char*) * size)); + + for (unsigned i = 0; i < size; i++) { + if (m_plugin->quirks().contains(PluginQuirkRemoveWindowlessVideoParam) && equalIgnoringCase(paramNames[i], "windowlessvideo")) + continue; + + if (paramNames[i] == "pluginspage") + m_pluginsPage = paramValues[i]; + + m_paramNames[paramCount] = createUTF8String(paramNames[i]); + m_paramValues[paramCount] = createUTF8String(paramValues[i]); + + paramCount++; + } + + m_paramCount = paramCount; +} + +PluginView::PluginView(Frame* parentFrame, const IntSize& size, PluginPackage* plugin, Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually) + : m_parentFrame(parentFrame) + , m_plugin(plugin) + , m_element(element) + , m_isStarted(false) + , m_url(url) + , m_baseURL(m_parentFrame->loader()->completeURL(m_parentFrame->document()->baseURL().string())) + , m_status(PluginStatusLoadedSuccessfully) + , m_requestTimer(this, &PluginView::requestTimerFired) + , m_invalidateTimer(this, &PluginView::invalidateTimerFired) + , m_popPopupsStateTimer(this, &PluginView::popPopupsStateTimerFired) + , m_lifeSupportTimer(this, &PluginView::lifeSupportTimerFired) + , m_mode(loadManually ? NP_FULL : NP_EMBED) + , m_paramNames(0) + , m_paramValues(0) + , m_mimeType(mimeType) + , m_instance(0) +#if defined(XP_MACOSX) + , m_isWindowed(false) +#else + , m_isWindowed(true) +#endif + , m_isTransparent(false) + , m_haveInitialized(false) + , m_isWaitingToStart(false) +#if defined(XP_UNIX) + , m_needsXEmbed(false) +#endif +#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) + , m_pluginWndProc(0) + , m_lastMessage(0) + , m_isCallingPluginWndProc(false) + , m_wmPrintHDC(0) + , m_haveUpdatedPluginWidget(false) +#endif +#if (PLATFORM(QT) && OS(WINDOWS)) || defined(XP_MACOSX) + , m_window(0) +#endif +#if defined(XP_MACOSX) + , m_drawingModel(NPDrawingModel(-1)) + , m_eventModel(NPEventModel(-1)) + , m_contextRef(0) + , m_fakeWindow(0) +#endif +#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) + , m_hasPendingGeometryChange(true) + , m_drawable(0) + , m_visual(0) + , m_colormap(0) + , m_pluginDisplay(0) +#endif +#if PLATFORM(QT) && defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + , m_renderToImage(false) +#endif + , m_loadManually(loadManually) + , m_manualStream(0) + , m_isJavaScriptPaused(false) + , m_isHalted(false) + , m_hasBeenHalted(false) + , m_haveCalledSetWindow(false) +{ +#if defined(ANDROID_PLUGINS) + platformInit(); +#endif + + if (!m_plugin) { + m_status = PluginStatusCanNotFindPlugin; + return; + } + + m_instance = &m_instanceStruct; + m_instance->ndata = this; + m_instance->pdata = 0; + + instanceMap().add(m_instance, this); + + setParameters(paramNames, paramValues); + + memset(&m_npWindow, 0, sizeof(m_npWindow)); +#if defined(XP_MACOSX) + memset(&m_npCgContext, 0, sizeof(m_npCgContext)); +#endif + + resize(size); +} + +void PluginView::focusPluginElement() +{ + // Focus the plugin + if (Page* page = m_parentFrame->page()) + page->focusController()->setFocusedFrame(m_parentFrame); + m_parentFrame->document()->setFocusedNode(m_element); +} + +void PluginView::didReceiveResponse(const ResourceResponse& response) +{ + if (m_status != PluginStatusLoadedSuccessfully) + return; + + ASSERT(m_loadManually); + ASSERT(!m_manualStream); + + m_manualStream = PluginStream::create(this, m_parentFrame.get(), m_parentFrame->loader()->activeDocumentLoader()->request(), false, 0, plugin()->pluginFuncs(), instance(), m_plugin->quirks()); + m_manualStream->setLoadManually(true); + + m_manualStream->didReceiveResponse(0, response); +} + +void PluginView::didReceiveData(const char* data, int length) +{ + if (m_status != PluginStatusLoadedSuccessfully) + return; + + ASSERT(m_loadManually); + ASSERT(m_manualStream); + + m_manualStream->didReceiveData(0, data, length); +} + +void PluginView::didFinishLoading() +{ + if (m_status != PluginStatusLoadedSuccessfully) + return; + + ASSERT(m_loadManually); + ASSERT(m_manualStream); + + m_manualStream->didFinishLoading(0); +} + +void PluginView::didFail(const ResourceError& error) +{ + if (m_status != PluginStatusLoadedSuccessfully) + return; + + ASSERT(m_loadManually); + ASSERT(m_manualStream); + + m_manualStream->didFail(0, error); +} + +void PluginView::setCallingPlugin(bool b) const +{ + if (!m_plugin->quirks().contains(PluginQuirkHasModalMessageLoop)) + return; + + if (b) + ++s_callingPlugin; + else + --s_callingPlugin; + + ASSERT(s_callingPlugin >= 0); +} + +bool PluginView::isCallingPlugin() +{ + return s_callingPlugin > 0; +} + +PassRefPtr<PluginView> PluginView::create(Frame* parentFrame, const IntSize& size, Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually) +{ + // if we fail to find a plugin for this MIME type, findPlugin will search for + // a plugin by the file extension and update the MIME type, so pass a mutable String + String mimeTypeCopy = mimeType; + PluginPackage* plugin = PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); + + // No plugin was found, try refreshing the database and searching again + if (!plugin && PluginDatabase::installedPlugins()->refresh()) { + mimeTypeCopy = mimeType; + plugin = PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); + } + + return adoptRef(new PluginView(parentFrame, size, plugin, element, url, paramNames, paramValues, mimeTypeCopy, loadManually)); +} + +void PluginView::freeStringArray(char** stringArray, int length) +{ + if (!stringArray) + return; + + for (int i = 0; i < length; i++) + fastFree(stringArray[i]); + + fastFree(stringArray); +} + +static inline bool startsWithBlankLine(const Vector<char>& buffer) +{ + return buffer.size() > 0 && buffer[0] == '\n'; +} + +static inline int locationAfterFirstBlankLine(const Vector<char>& buffer) +{ + const char* bytes = buffer.data(); + unsigned length = buffer.size(); + + 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; + else if (bytes[i] == '\n') + // Support for Director. It sends "\r\n\n" (3880387). + return i + 1; + else if (bytes[i] == '\r' && bytes[i + 1] == '\n') + // Support for Flash. It sends "\r\n\r\n" (3758113). + return i + 2; + } + } + + return -1; +} + +static inline const char* findEOL(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 NULL 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 inline 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 inline HTTPHeaderMap parseRFC822HeaderFields(const Vector<char>& buffer, unsigned length) +{ + const char* bytes = buffer.data(); + const char* eol; + String lastKey; + HTTPHeaderMap headerFields; + + // Loop ove rlines until we're past the header, or we can't find any more end-of-lines + while ((eol = findEOL(bytes, length))) { + const char* line = bytes; + int lineLength = eol - bytes; + + // Move bytes to the character after the terminator as returned by findEOL. + bytes = eol + 1; + if ((*eol == '\r') && (*bytes == '\n')) + bytes++; // Safe since findEOL won't return a spanning CRLF. + + length -= (bytes - line); + if (lineLength == 0) + // Blank line; we're at the end of the header + break; + else if (*line == ' ' || *line == '\t') { + // Continuation of the previous header + if (lastKey.isNull()) { + // malformed header; ignore it and continue + continue; + } else { + // Merge the continuation of the previous header + String currentValue = headerFields.get(lastKey); + String newValue(line, lineLength); + + headerFields.set(lastKey, currentValue + newValue); + } + } else { + // Brand new header + const char* colon; + for (colon = line; *colon != ':' && colon != eol; colon++) { + // empty loop + } + if (colon == eol) + // malformed header; ignore it and continue + continue; + else { + lastKey = capitalizeRFC822HeaderFieldName(String(line, colon - line)); + String value; + + for (colon++; colon != eol; colon++) { + if (*colon != ' ' && *colon != '\t') + break; + } + if (colon == eol) + value = ""; + else + value = String(colon, eol - colon); + + String oldValue = headerFields.get(lastKey); + if (!oldValue.isNull()) { + String tmp = oldValue; + tmp += ", "; + tmp += value; + value = tmp; + } + + headerFields.set(lastKey, value); + } + } + } + + return headerFields; +} + +NPError PluginView::handlePost(const char* url, const char* target, uint32_t len, const char* buf, bool file, void* notifyData, bool sendNotification, bool allowHeaders) +{ + if (!url || !len || !buf) + return NPERR_INVALID_PARAM; + + FrameLoadRequest frameLoadRequest(m_parentFrame->document()->securityOrigin()); + + HTTPHeaderMap headerFields; + Vector<char> buffer; + + if (file) { + NPError readResult = handlePostReadFile(buffer, len, buf); + if(readResult != NPERR_NO_ERROR) + return readResult; + } else { + buffer.resize(len); + memcpy(buffer.data(), buf, len); + } + + const char* postData = buffer.data(); + int postDataLength = buffer.size(); + + if (allowHeaders) { + if (startsWithBlankLine(buffer)) { + postData++; + postDataLength--; + } else { + int location = locationAfterFirstBlankLine(buffer); + if (location != -1) { + // If the blank line is somewhere in the middle of the buffer, everything before is the header + headerFields = parseRFC822HeaderFields(buffer, location); + unsigned dataLength = buffer.size() - 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"); + + postData += location; + postDataLength = dataLength; + } + } + } + + frameLoadRequest.resourceRequest().setHTTPMethod("POST"); + frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url)); + frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields); + frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(postData, postDataLength)); + frameLoadRequest.setFrameName(target); + + return load(frameLoadRequest, sendNotification, notifyData); +} + +#ifdef PLUGIN_SCHEDULE_TIMER +uint32_t PluginView::scheduleTimer(NPP instance, uint32_t interval, bool repeat, + void (*timerFunc)(NPP, uint32_t timerID)) +{ + return m_timerList.schedule(instance, interval, repeat, timerFunc); +} + +void PluginView::unscheduleTimer(NPP instance, uint32_t timerID) +{ + m_timerList.unschedule(instance, timerID); +} +#endif + +void PluginView::invalidateWindowlessPluginRect(const IntRect& rect) +{ + if (!isVisible()) + return; + + if (!m_element->renderer()) + return; + RenderBox* renderer = toRenderBox(m_element->renderer()); + + IntRect dirtyRect = rect; + dirtyRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); + renderer->repaintRectangle(dirtyRect); +} + +void PluginView::paintMissingPluginIcon(GraphicsContext* context, const IntRect& rect) +{ + static RefPtr<Image> nullPluginImage; + if (!nullPluginImage) + nullPluginImage = Image::loadPlatformResource("nullPlugin"); + + IntRect imageRect(frameRect().x(), frameRect().y(), nullPluginImage->width(), nullPluginImage->height()); + + int xOffset = (frameRect().width() - imageRect.width()) / 2; + int yOffset = (frameRect().height() - imageRect.height()) / 2; + + imageRect.move(xOffset, yOffset); + + if (!rect.intersects(imageRect)) + return; + + context->save(); + context->clip(windowClipRect()); + context->drawImage(nullPluginImage.get(), ColorSpaceDeviceRGB, imageRect.location()); + context->restore(); +} + +static const char* MozillaUserAgent = "Mozilla/5.0 (" +#if defined(XP_MACOSX) + "Macintosh; U; Intel Mac OS X;" +#elif defined(XP_WIN) + "Windows; U; Windows NT 5.1;" +#elif defined(XP_UNIX) +// The Gtk port uses X11 plugins in Mac. +#if OS(DARWIN) && PLATFORM(GTK) + "X11; U; Intel Mac OS X;" +#else + "X11; U; Linux i686;" +#endif +#endif + " en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0"; + +const char* PluginView::userAgent() +{ +#if !PLATFORM(ANDROID) + if (m_plugin->quirks().contains(PluginQuirkWantsMozillaUserAgent)) + return MozillaUserAgent; +#endif + + if (m_userAgent.isNull()) + m_userAgent = m_parentFrame->loader()->userAgent(m_url).utf8(); + + return m_userAgent.data(); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +const char* PluginView::userAgentStatic() +{ + return MozillaUserAgent; +} +#endif + + +Node* PluginView::node() const +{ + return m_element; +} + +String PluginView::pluginName() const +{ + return m_plugin->name(); +} + +void PluginView::lifeSupportTimerFired(Timer<PluginView>*) +{ + deref(); +} + +void PluginView::keepAlive() +{ + if (m_lifeSupportTimer.isActive()) + return; + + ref(); + m_lifeSupportTimer.startOneShot(0); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +void PluginView::keepAlive(NPP instance) +{ + PluginView* view = instanceMap().get(instance); + if (!view) + return; + + view->keepAlive(); +} + +NPError PluginView::getValueStatic(NPNVariable variable, void* value) +{ + LOG(Plugins, "PluginView::getValueStatic(%s)", prettyNameForNPNVariable(variable).data()); + + NPError result; + if (platformGetValueStatic(variable, value, &result)) + return result; + + return NPERR_GENERIC_ERROR; +} + +NPError PluginView::getValue(NPNVariable variable, void* value) +{ + LOG(Plugins, "PluginView::getValue(%s)", prettyNameForNPNVariable(variable).data()); + + NPError result; + if (platformGetValue(variable, value, &result)) + return result; + + if (platformGetValueStatic(variable, value, &result)) + return result; + + switch (variable) { + case NPNVWindowNPObject: { + if (m_isJavaScriptPaused) + return NPERR_GENERIC_ERROR; + + NPObject* windowScriptObject = m_parentFrame->script()->windowScriptNPObject(); + + // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html> + if (windowScriptObject) + _NPN_RetainObject(windowScriptObject); + + void** v = (void**)value; + *v = windowScriptObject; + + return NPERR_NO_ERROR; + } + + case NPNVPluginElementNPObject: { + if (m_isJavaScriptPaused) + return NPERR_GENERIC_ERROR; + + NPObject* pluginScriptObject = 0; + + if (m_element->hasTagName(appletTag) || m_element->hasTagName(embedTag) || m_element->hasTagName(objectTag)) + pluginScriptObject = static_cast<HTMLPlugInElement*>(m_element)->getNPObject(); + + // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html> + if (pluginScriptObject) + _NPN_RetainObject(pluginScriptObject); + + void** v = (void**)value; + *v = pluginScriptObject; + + return NPERR_NO_ERROR; + } + + case NPNVprivateModeBool: { + Page* page = m_parentFrame->page(); + if (!page) + return NPERR_GENERIC_ERROR; + *((NPBool*)value) = !page->settings() || page->settings()->privateBrowsingEnabled(); + return NPERR_NO_ERROR; + } + + default: + return NPERR_GENERIC_ERROR; + } +} + +static Frame* getFrame(Frame* parentFrame, Element* element) +{ + if (parentFrame) + return parentFrame; + + Document* document = element->document(); + if (!document) + document = element->ownerDocument(); + if (document) + return document->frame(); + + return 0; +} + +NPError PluginView::getValueForURL(NPNURLVariable variable, const char* url, char** value, uint32_t* len) +{ + LOG(Plugins, "PluginView::getValueForURL(%s)", prettyNameForNPNURLVariable(variable).data()); + + NPError result = NPERR_NO_ERROR; + + switch (variable) { + case NPNURLVCookie: { + KURL u(m_baseURL, url); + if (u.isValid()) { + Frame* frame = getFrame(parentFrame(), m_element); + if (frame) { + const CString cookieStr = cookies(frame->document(), u).utf8(); + if (!cookieStr.isNull()) { + const int size = cookieStr.length(); + *value = static_cast<char*>(NPN_MemAlloc(size+1)); + if (*value) { + memset(*value, 0, size+1); + memcpy(*value, cookieStr.data(), size+1); + if (len) + *len = size; + } else + result = NPERR_OUT_OF_MEMORY_ERROR; + } + } + } else + result = NPERR_INVALID_URL; + break; + } + case NPNURLVProxy: { + KURL u(m_baseURL, url); + if (u.isValid()) { + Frame* frame = getFrame(parentFrame(), m_element); + const FrameLoader* frameLoader = frame ? frame->loader() : 0; + const NetworkingContext* context = frameLoader ? frameLoader->networkingContext() : 0; + const CString proxyStr = toString(proxyServersForURL(u, context)).utf8(); + if (!proxyStr.isNull()) { + const int size = proxyStr.length(); + *value = static_cast<char*>(NPN_MemAlloc(size+1)); + if (*value) { + memset(*value, 0, size+1); + memcpy(*value, proxyStr.data(), size+1); + if (len) + *len = size; + } else + result = NPERR_OUT_OF_MEMORY_ERROR; + } + } else + result = NPERR_INVALID_URL; + break; + } + default: + result = NPERR_GENERIC_ERROR; + LOG(Plugins, "PluginView::getValueForURL: %s", prettyNameForNPNURLVariable(variable).data()); + break; + } + + return result; +} + + +NPError PluginView::setValueForURL(NPNURLVariable variable, const char* url, const char* value, uint32_t len) +{ + LOG(Plugins, "PluginView::setValueForURL(%s)", prettyNameForNPNURLVariable(variable).data()); + + NPError result = NPERR_NO_ERROR; + + switch (variable) { + case NPNURLVCookie: { + KURL u(m_baseURL, url); + if (u.isValid()) { + const String cookieStr = String::fromUTF8(value, len); + Frame* frame = getFrame(parentFrame(), m_element); + if (frame && !cookieStr.isEmpty()) + setCookies(frame->document(), u, cookieStr); + } else + result = NPERR_INVALID_URL; + break; + } + case NPNURLVProxy: + LOG(Plugins, "PluginView::setValueForURL(%s): Plugins are NOT allowed to set proxy information.", prettyNameForNPNURLVariable(variable).data()); + result = NPERR_GENERIC_ERROR; + break; + default: + LOG(Plugins, "PluginView::setValueForURL: %s", prettyNameForNPNURLVariable(variable).data()); + result = NPERR_GENERIC_ERROR; + break; + } + + return result; +} + +NPError PluginView::getAuthenticationInfo(const char* protocol, const char* host, int32_t port, const char* scheme, const char* realm, char** username, uint32_t* ulen, char** password, uint32_t* plen) +{ + LOG(Plugins, "PluginView::getAuthenticationInfo: protocol=%s, host=%s, port=%d", protocol, host, port); + notImplemented(); + return NPERR_GENERIC_ERROR; +} +#endif + +void PluginView::privateBrowsingStateChanged(bool privateBrowsingEnabled) +{ + NPP_SetValueProcPtr setValue = m_plugin->pluginFuncs()->setvalue; + if (!setValue) + return; + + PluginView::setCurrentPluginView(this); +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + setCallingPlugin(true); + NPBool value = privateBrowsingEnabled; + setValue(m_instance, NPNVprivateModeBool, &value); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); +} + +} // namespace WebCore + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebCore/plugins/PluginView.h b/Source/WebCore/plugins/PluginView.h new file mode 100644 index 0000000..a58d217 --- /dev/null +++ b/Source/WebCore/plugins/PluginView.h @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginView_h +#define PluginView_h + +#include "FrameLoadRequest.h" +#include "HaltablePlugin.h" +#include "IntRect.h" +#include "MediaCanStartListener.h" +#include "PluginViewBase.h" +#include "ResourceRequest.h" +#include "Timer.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> + +#if ENABLE(NETSCAPE_PLUGIN_API) +#include "PluginStream.h" +#include "npruntime_internal.h" +#endif + +// ANDROID +// TODO: Upstream to webkit.org +#ifdef PLUGIN_SCHEDULE_TIMER +#include "PluginTimer.h" +#endif + +#if OS(WINDOWS) && (PLATFORM(QT) || PLATFORM(WX)) +typedef struct HWND__* HWND; +typedef HWND PlatformPluginWidget; +#elif defined(ANDROID_PLUGINS) +typedef struct PluginWidgetAndroid* PlatformPluginWidget; +#else +typedef PlatformWidget PlatformPluginWidget; +#if defined(XP_MACOSX) && PLATFORM(QT) +#include <QPixmap> +#endif +#endif +#if PLATFORM(QT) +#include <QGraphicsItem> +#include <QImage> +QT_BEGIN_NAMESPACE +class QPainter; +QT_END_NAMESPACE +#endif + +#if USE(JSC) +namespace JSC { + namespace Bindings { + class Instance; + } +} +#endif + +class NPObject; + +namespace WebCore { + class Element; + class Frame; + class Image; + class KeyboardEvent; + class MouseEvent; +#ifdef ANDROID_PLUGINS + class TouchEvent; +#endif + class KURL; +#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) + class PluginMessageThrottlerWin; +#endif + class PluginPackage; + class PluginRequest; + class PluginStream; + class ResourceError; + class ResourceResponse; + + enum PluginStatus { + PluginStatusCanNotFindPlugin, + PluginStatusCanNotLoadPlugin, + PluginStatusLoadedSuccessfully + }; + + class PluginRequest : public Noncopyable { + public: + PluginRequest(const FrameLoadRequest& frameLoadRequest, bool sendNotification, void* notifyData, bool shouldAllowPopups) + : m_frameLoadRequest(frameLoadRequest) + , m_notifyData(notifyData) + , m_sendNotification(sendNotification) + , m_shouldAllowPopups(shouldAllowPopups) { } + public: + const FrameLoadRequest& frameLoadRequest() const { return m_frameLoadRequest; } + void* notifyData() const { return m_notifyData; } + bool sendNotification() const { return m_sendNotification; } + bool shouldAllowPopups() const { return m_shouldAllowPopups; } + private: + FrameLoadRequest m_frameLoadRequest; + void* m_notifyData; + bool m_sendNotification; + bool m_shouldAllowPopups; + }; + + class PluginManualLoader { + public: + virtual ~PluginManualLoader() {} + virtual void didReceiveResponse(const ResourceResponse&) = 0; + virtual void didReceiveData(const char*, int) = 0; + virtual void didFinishLoading() = 0; + virtual void didFail(const ResourceError&) = 0; + }; + + class PluginView : public PluginViewBase +#if ENABLE(NETSCAPE_PLUGIN_API) + , private PluginStreamClient +#endif + , public PluginManualLoader + , private HaltablePlugin + , private MediaCanStartListener { + public: + static PassRefPtr<PluginView> create(Frame* parentFrame, const IntSize&, Element*, const KURL&, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually); + virtual ~PluginView(); + + PluginPackage* plugin() const { return m_plugin.get(); } +#if ENABLE(NETSCAPE_PLUGIN_API) + NPP instance() const { return m_instance; } +#endif + + void setNPWindowRect(const IntRect&); + static PluginView* currentPluginView(); + +#if ENABLE(NETSCAPE_PLUGIN_API) + NPObject* npObject(); +#endif +#if USE(JSC) + PassRefPtr<JSC::Bindings::Instance> bindingInstance(); +#endif + + PluginStatus status() const { return m_status; } + +#if ENABLE(NETSCAPE_PLUGIN_API) + // NPN functions + NPError getURLNotify(const char* url, const char* target, void* notifyData); + NPError getURL(const char* url, const char* target); + NPError postURLNotify(const char* url, const char* target, uint32_t len, const char* but, NPBool file, void* notifyData); + NPError postURL(const char* url, const char* target, uint32_t len, const char* but, NPBool file); + NPError newStream(NPMIMEType type, const char* target, NPStream** stream); + int32_t write(NPStream* stream, int32_t len, void* buffer); + NPError destroyStream(NPStream* stream, NPReason reason); +#endif + const char* userAgent(); +#if ENABLE(NETSCAPE_PLUGIN_API) + static const char* userAgentStatic(); +#endif + void status(const char* message); + +#if ENABLE(NETSCAPE_PLUGIN_API) + NPError getValue(NPNVariable variable, void* value); + static NPError getValueStatic(NPNVariable variable, void* value); + NPError setValue(NPPVariable variable, void* value); + NPError getValueForURL(NPNURLVariable variable, const char* url, char** value, uint32_t* len); + NPError setValueForURL(NPNURLVariable variable, const char* url, const char* value, uint32_t len); + NPError getAuthenticationInfo(const char* protocol, const char* host, int32_t port, const char* scheme, const char* realm, char** username, uint32_t* ulen, char** password, uint32_t* plen); + void invalidateRect(NPRect*); + void invalidateRegion(NPRegion); +#endif + void forceRedraw(); + void pushPopupsEnabledState(bool state); + void popPopupsEnabledState(); +#ifdef PLUGIN_SCHEDULE_TIMER + uint32_t scheduleTimer(NPP, uint32_t interval, bool repeat, + void (*timerFunc)(NPP, uint32_t timerID)); + void unscheduleTimer(NPP, uint32_t timerID); +#endif +#if USE(V8) + NPObject* getNPObject(); +#endif + + virtual void invalidateRect(const IntRect&); + + bool arePopupsAllowed() const; + + void setJavaScriptPaused(bool); + + void privateBrowsingStateChanged(bool); + + void disconnectStream(PluginStream*); + void streamDidFinishLoading(PluginStream* stream) { disconnectStream(stream); } + + // Widget functions + virtual void setFrameRect(const IntRect&); + virtual void frameRectsChanged(); + virtual void setFocus(bool); + virtual void show(); + virtual void hide(); + virtual void paint(GraphicsContext*, const IntRect&); + + // This method is used by plugins on all platforms to obtain a clip rect that includes clips set by WebCore, + // e.g., in overflow:auto sections. The clip rects coordinates are in the containing window's coordinate space. + // This clip includes any clips that the widget itself sets up for its children. + IntRect windowClipRect() const; + + virtual void handleEvent(Event*); + virtual void setParent(ScrollView*); + virtual void setParentVisible(bool); + + virtual bool isPluginView() const { return true; } + + Frame* parentFrame() const { return m_parentFrame.get(); } + + void focusPluginElement(); + + const String& pluginsPage() const { return m_pluginsPage; } + const String& mimeType() const { return m_mimeType; } + const KURL& url() const { return m_url; } + +#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) + static LRESULT CALLBACK PluginViewWndProc(HWND, UINT, WPARAM, LPARAM); + LRESULT wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + WNDPROC pluginWndProc() const { return m_pluginWndProc; } +#endif + + // Used for manual loading + void didReceiveResponse(const ResourceResponse&); + void didReceiveData(const char*, int); + void didFinishLoading(); + void didFail(const ResourceError&); + + // HaltablePlugin + virtual void halt(); + virtual void restart(); + virtual Node* node() const; + virtual bool isWindowed() const { return m_isWindowed; } + virtual String pluginName() const; + + bool isHalted() const { return m_isHalted; } + bool hasBeenHalted() const { return m_hasBeenHalted; } + + static bool isCallingPlugin(); + +#ifdef ANDROID_PLUGINS + Element* getElement() const { return m_element; } +#endif + + bool start(); + +#if ENABLE(NETSCAPE_PLUGIN_API) + static void keepAlive(NPP); +#endif + void keepAlive(); + +#if USE(ACCELERATED_COMPOSITING) +#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) && PLATFORM(QT) + virtual PlatformLayer* platformLayer() const; +#elif ENABLE(NETSCAPE_PLUGIN_API) && defined(ANDROID_PLUGINS) + virtual PlatformLayer* platformLayer() const; +#else + virtual PlatformLayer* platformLayer() const { return 0; } +#endif +#endif + + private: + PluginView(Frame* parentFrame, const IntSize&, PluginPackage*, Element*, const KURL&, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually); + + void setParameters(const Vector<String>& paramNames, const Vector<String>& paramValues); + bool startOrAddToUnstartedList(); + void init(); + bool platformStart(); + void stop(); + void platformDestroy(); + static void setCurrentPluginView(PluginView*); +#if ENABLE(NETSCAPE_PLUGIN_API) + NPError load(const FrameLoadRequest&, bool sendNotification, void* notifyData); + NPError handlePost(const char* url, const char* target, uint32_t len, const char* buf, bool file, void* notifyData, bool sendNotification, bool allowHeaders); + NPError handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf); +#endif + static void freeStringArray(char** stringArray, int length); + void setCallingPlugin(bool) const; + + void invalidateWindowlessPluginRect(const IntRect&); + + virtual void mediaCanStart(); + +#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) + void paintWindowedPluginIntoContext(GraphicsContext*, const IntRect&); + static HDC WINAPI hookedBeginPaint(HWND, PAINTSTRUCT*); + static BOOL WINAPI hookedEndPaint(HWND, const PAINTSTRUCT*); +#endif + +#if ENABLE(NETSCAPE_PLUGIN_API) + static bool platformGetValueStatic(NPNVariable variable, void* value, NPError* result); + bool platformGetValue(NPNVariable variable, void* value, NPError* result); +#endif + + RefPtr<Frame> m_parentFrame; + RefPtr<PluginPackage> m_plugin; + Element* m_element; + bool m_isStarted; + KURL m_url; + KURL m_baseURL; + PluginStatus m_status; + Vector<IntRect> m_invalidRects; + + void performRequest(PluginRequest*); + void scheduleRequest(PluginRequest*); + void requestTimerFired(Timer<PluginView>*); + void invalidateTimerFired(Timer<PluginView>*); + Timer<PluginView> m_requestTimer; + Timer<PluginView> m_invalidateTimer; + + void popPopupsStateTimerFired(Timer<PluginView>*); + Timer<PluginView> m_popPopupsStateTimer; + + void lifeSupportTimerFired(Timer<PluginView>*); + Timer<PluginView> m_lifeSupportTimer; + +#ifndef NP_NO_CARBON +#if ENABLE(NETSCAPE_PLUGIN_API) + bool dispatchNPEvent(NPEvent&); +#endif // ENABLE(NETSCAPE_PLUGIN_API) +#endif + void updatePluginWidget(); + void paintMissingPluginIcon(GraphicsContext*, const IntRect&); + + void handleKeyboardEvent(KeyboardEvent*); + void handleMouseEvent(MouseEvent*); +#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) + void handleFocusInEvent(); + void handleFocusOutEvent(); +#endif + +#if OS(WINDOWS) + void paintIntoTransformedContext(HDC); + PassRefPtr<Image> snapshot(); +#endif + +#ifdef ANDROID_PLUGINS + void handleFocusEvent(bool hasFocus); + void handleTouchEvent(TouchEvent*); + // called at the end of the base constructor + void platformInit(); +#endif +#ifdef PLUGIN_PLATFORM_SETVALUE + // called if the default setValue does not recognize the variable + NPError platformSetValue(NPPVariable variable, void* value); +#endif + + int m_mode; + int m_paramCount; + char** m_paramNames; + char** m_paramValues; + String m_pluginsPage; + + String m_mimeType; + WTF::CString m_userAgent; + +#if ENABLE(NETSCAPE_PLUGIN_API) + NPP m_instance; + NPP_t m_instanceStruct; + NPWindow m_npWindow; +#endif + + Vector<bool, 4> m_popupStateStack; + + HashSet<RefPtr<PluginStream> > m_streams; + Vector<PluginRequest*> m_requests; + + bool m_isWindowed; + bool m_isTransparent; + bool m_haveInitialized; + bool m_isWaitingToStart; + +#if defined(XP_UNIX) + bool m_needsXEmbed; +#endif + +#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) + OwnPtr<PluginMessageThrottlerWin> m_messageThrottler; + WNDPROC m_pluginWndProc; + unsigned m_lastMessage; + bool m_isCallingPluginWndProc; + HDC m_wmPrintHDC; + bool m_haveUpdatedPluginWidget; +#endif + +// ANDROID +// TODO: Upstream to webkit.org +#ifdef PLUGIN_SCHEDULE_TIMER + PluginTimerList m_timerList; +#endif + +#if ((PLATFORM(QT) || PLATFORM(WX)) && OS(WINDOWS)) || defined(XP_MACOSX) + // On Mac OSX and Qt/Windows the plugin does not have its own native widget, + // but is using the containing window as its reference for positioning/painting. + PlatformPluginWidget m_window; +public: + PlatformPluginWidget platformPluginWidget() const { return m_window; } + void setPlatformPluginWidget(PlatformPluginWidget widget) { m_window = widget; } +#elif defined(ANDROID_PLUGINS) +public: + PlatformPluginWidget m_window; + PlatformPluginWidget platformPluginWidget() const { return m_window; } // MANUAL MERGE FIXME +#else +public: + void setPlatformPluginWidget(PlatformPluginWidget widget) { setPlatformWidget(widget); } + PlatformPluginWidget platformPluginWidget() const { return platformWidget(); } +#endif + +private: + +#if defined(XP_UNIX) || OS(SYMBIAN) || defined(ANDROID_PLUGINS) + void setNPWindowIfNeeded(); +#elif defined(XP_MACOSX) + NP_CGContext m_npCgContext; + OwnPtr<Timer<PluginView> > m_nullEventTimer; + NPDrawingModel m_drawingModel; + NPEventModel m_eventModel; + CGContextRef m_contextRef; + WindowRef m_fakeWindow; +#if PLATFORM(QT) + QPixmap m_pixmap; +#endif + + Point m_lastMousePos; + void setNPWindowIfNeeded(); + void nullEventTimerFired(Timer<PluginView>*); + Point globalMousePosForPlugin() const; + Point mousePosForPlugin(MouseEvent* event = 0) const; +#endif + +#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) + bool m_hasPendingGeometryChange; + Pixmap m_drawable; + Visual* m_visual; + Colormap m_colormap; + Display* m_pluginDisplay; + + void initXEvent(XEvent* event); +#endif + +#if PLATFORM(QT) +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + QImage m_image; + bool m_renderToImage; + void paintUsingImageSurfaceExtension(QPainter* painter, const IntRect& exposedRect); +#endif +#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) + void paintUsingXPixmap(QPainter* painter, const QRect &exposedRect); +#if USE(ACCELERATED_COMPOSITING) + OwnPtr<PlatformLayer> m_platformLayer; + friend class PluginGraphicsLayerQt; +#endif // USE(ACCELERATED_COMPOSITING) +#endif +#endif // PLATFORM(QT) + + IntRect m_clipRect; // The clip rect to apply to a windowed plug-in + IntRect m_windowRect; // Our window rect. +#ifdef ANDROID_PLUGINS + IntRect m_pageRect; // The rect in page coordinate system. +#endif + + bool m_loadManually; + RefPtr<PluginStream> m_manualStream; + + bool m_isJavaScriptPaused; + + bool m_isHalted; + bool m_hasBeenHalted; + + bool m_haveCalledSetWindow; + + static PluginView* s_currentPluginView; + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/plugins/PluginViewBase.h b/Source/WebCore/plugins/PluginViewBase.h new file mode 100644 index 0000000..8dc667a --- /dev/null +++ b/Source/WebCore/plugins/PluginViewBase.h @@ -0,0 +1,59 @@ +/* + * 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 PluginWidget_h +#define PluginWidget_h + +#include "Widget.h" +#include "GraphicsLayer.h" + +namespace JSC { + class ExecState; + class JSGlobalObject; + class JSObject; +} + +namespace WebCore { + +// PluginViewBase is a widget that all plug-in views inherit from, both in Webkit and WebKit2. +// It's intended as a stopgap measure until we can merge all plug-in views into a single plug-in view. +class PluginViewBase : public Widget { +public: +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const { return 0; } +#endif + + virtual JSC::JSObject* scriptObject(JSC::JSGlobalObject*) { return 0; } + virtual void privateBrowsingStateChanged(bool) { } + +protected: + PluginViewBase(PlatformWidget widget = 0) : Widget(widget) { } + +private: + virtual bool isPluginViewBase() const { return true; } +}; + +} // namespace WebCore + +#endif // PluginWidget_h diff --git a/Source/WebCore/plugins/PluginViewNone.cpp b/Source/WebCore/plugins/PluginViewNone.cpp new file mode 100644 index 0000000..9aeaf3b --- /dev/null +++ b/Source/WebCore/plugins/PluginViewNone.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2008 Kevin Ollivier <kevino@theolliviers.com> All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginView.h" + +#if USE(JSC) +#include "Bridge.h" +#endif + +using namespace WTF; + +namespace WebCore { + +void PluginView::setFocus(bool) +{ +} + +void PluginView::show() +{ +} + +void PluginView::hide() +{ +} + +void PluginView::paint(GraphicsContext*, const IntRect&) +{ +} + +void PluginView::handleKeyboardEvent(KeyboardEvent*) +{ +} + +void PluginView::handleMouseEvent(MouseEvent*) +{ +} + +void PluginView::setParent(ScrollView*) +{ +} + +void PluginView::setNPWindowRect(const IntRect&) +{ +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +NPError PluginView::handlePostReadFile(Vector<char>&, uint32_t, const char*) +{ + return 0; +} + +bool PluginView::platformGetValue(NPNVariable, void*, NPError*) +{ + return false; +} + +bool PluginView::platformGetValueStatic(NPNVariable, void*, NPError*) +{ + return false; +} + +void PluginView::invalidateRect(NPRect*) +{ +} +#endif + +void PluginView::invalidateRect(const IntRect&) +{ +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +void PluginView::invalidateRegion(NPRegion) +{ +} +#endif + +void PluginView::forceRedraw() +{ +} + +bool PluginView::platformStart() +{ + return true; +} + +void PluginView::platformDestroy() +{ +} + +void PluginView::setParentVisible(bool) +{ +} + +void PluginView::updatePluginWidget() +{ +} + +void PluginView::halt() +{ +} + +void PluginView::restart() +{ +} + +#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) +void PluginView::handleFocusInEvent() +{ +} + +void PluginView::handleFocusOutEvent() +{ +} +#endif + +// The functions below are for platforms that do not use PluginView for plugins +// due to architectural differences. The plan is to eventually have all +// ports using PluginView, but until then, if new functions like this are +// added, please make sure they have the proper platform #ifs so that changes +// do not break ports who compile both this file and PluginView.cpp. +#if PLATFORM(MAC) || PLATFORM(CHROMIUM) || PLATFORM(EFL) || (OS(WINCE) && !PLATFORM(QT)) || (PLATFORM(QT) && !OS(WINCE)) || PLATFORM(BREWMP) +#if ENABLE(NETSCAPE_PLUGIN_API) +void PluginView::keepAlive(NPP) +{ +} +#endif + +#if USE(JSC) +PassRefPtr<JSC::Bindings::Instance> PluginView::bindingInstance() +{ + return 0; +} +#endif + +void PluginView::privateBrowsingStateChanged(bool) +{ +} + +void PluginView::setJavaScriptPaused(bool) +{ +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/plugins/chromium/PluginDataChromium.cpp b/Source/WebCore/plugins/chromium/PluginDataChromium.cpp new file mode 100644 index 0000000..c924063 --- /dev/null +++ b/Source/WebCore/plugins/chromium/PluginDataChromium.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginDataChromium.h" + +#include "ChromiumBridge.h" + +namespace WebCore { + +class PluginCache { +public: + PluginCache() : m_loaded(false), m_refresh(false) {} + ~PluginCache() { reset(false); } + + void reset(bool refresh) + { + m_plugins.clear(); + m_loaded = false; + m_refresh = refresh; + } + + const Vector<PluginInfo>& plugins() + { + if (!m_loaded) { + ChromiumBridge::plugins(m_refresh, &m_plugins); + m_loaded = true; + m_refresh = false; + } + return m_plugins; + } + +private: + Vector<PluginInfo> m_plugins; + bool m_loaded; + bool m_refresh; +}; + +static PluginCache pluginCache; + +void PluginData::initPlugins(const Page*) +{ + const Vector<PluginInfo>& plugins = pluginCache.plugins(); + for (size_t i = 0; i < plugins.size(); ++i) + m_plugins.append(plugins[i]); +} + +void PluginData::refresh() +{ + pluginCache.reset(true); + pluginCache.plugins(); // Force the plugins to be reloaded now. +} + +String getPluginMimeTypeFromExtension(const String& extension) +{ + const Vector<PluginInfo>& plugins = pluginCache.plugins(); + for (size_t i = 0; i < plugins.size(); ++i) { + for (size_t j = 0; j < plugins[i].mimes.size(); ++j) { + const MimeClassInfo& mime = plugins[i].mimes[j]; + const Vector<String>& extensions = mime.extensions; + for (size_t k = 0; k < extensions.size(); ++k) { + if (extension == extensions[k]) + return mime.type; + } + } + } + return String(); +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/chromium/PluginDataChromium.h b/Source/WebCore/plugins/chromium/PluginDataChromium.h new file mode 100644 index 0000000..78701b4 --- /dev/null +++ b/Source/WebCore/plugins/chromium/PluginDataChromium.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 Google, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginDataChromium_h +#define PluginDataChromium_h + +#include "PluginData.h" + +namespace WebCore { + +// Checks if any of the plugins handle this extension, and if so returns the +// plugin's mime type for this extension. Otherwise returns an empty string. +String getPluginMimeTypeFromExtension(const String& extension); + +} // namespace WebCore + +#endif // PluginDataChromium_h diff --git a/Source/WebCore/plugins/gtk/PluginDataGtk.cpp b/Source/WebCore/plugins/gtk/PluginDataGtk.cpp new file mode 100644 index 0000000..705bf9a --- /dev/null +++ b/Source/WebCore/plugins/gtk/PluginDataGtk.cpp @@ -0,0 +1,65 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + Copyright (C) 2008 Collabora Ltd. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "PluginData.h" + +#include "PluginDatabase.h" +#include "PluginPackage.h" +#include <stdio.h> +namespace WebCore { + +void PluginData::initPlugins(const Page*) +{ + PluginDatabase *db = PluginDatabase::installedPlugins(); + const Vector<PluginPackage*> &plugins = db->plugins(); + + for (unsigned int i = 0; i < plugins.size(); ++i) { + PluginInfo info; + PluginPackage* package = plugins[i]; + + info.name = package->name(); + info.file = package->fileName(); + info.desc = package->description(); + + const MIMEToDescriptionsMap& mimeToDescriptions = package->mimeToDescriptions(); + MIMEToDescriptionsMap::const_iterator end = mimeToDescriptions.end(); + for (MIMEToDescriptionsMap::const_iterator it = mimeToDescriptions.begin(); it != end; ++it) { + MimeClassInfo mime; + + mime.type = it->first; + mime.desc = it->second; + mime.extensions = package->mimeToExtensions().get(mime.type); + + info.mimes.append(mime); + } + + m_plugins.append(info); + } +} + +void PluginData::refresh() +{ + PluginDatabase *db = PluginDatabase::installedPlugins(); + db->refresh(); +} + +}; diff --git a/Source/WebCore/plugins/gtk/PluginPackageGtk.cpp b/Source/WebCore/plugins/gtk/PluginPackageGtk.cpp new file mode 100644 index 0000000..a702296 --- /dev/null +++ b/Source/WebCore/plugins/gtk/PluginPackageGtk.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2008 Novell Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginPackage.h" + +#include <gio/gio.h> +#include <stdio.h> + +#include "GOwnPtr.h" +#include "MIMETypeRegistry.h" +#include "NotImplemented.h" +#include "npruntime_impl.h" +#include "PluginDebug.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +bool PluginPackage::fetchInfo() +{ +#if defined(XP_UNIX) + if (!load()) + return false; + + NP_GetMIMEDescriptionFuncPtr NP_GetMIMEDescription = 0; + NPP_GetValueProcPtr NPP_GetValue = 0; + + g_module_symbol(m_module, "NP_GetMIMEDescription", (void**)&NP_GetMIMEDescription); + g_module_symbol(m_module, "NP_GetValue", (void**)&NPP_GetValue); + + if (!NP_GetMIMEDescription || !NPP_GetValue) + return false; + + char* buffer = 0; + NPError err = NPP_GetValue(0, NPPVpluginNameString, &buffer); + if (err == NPERR_NO_ERROR) + m_name = buffer; + + buffer = 0; + err = NPP_GetValue(0, NPPVpluginDescriptionString, &buffer); + if (err == NPERR_NO_ERROR) { + m_description = buffer; + determineModuleVersionFromDescription(); + } + + const gchar* types = NP_GetMIMEDescription(); + if (!types) + return true; + + gchar** mimeDescs = g_strsplit(types, ";", -1); + for (int i = 0; mimeDescs[i] && mimeDescs[i][0]; i++) { + GOwnPtr<char> mime(g_utf8_strdown(mimeDescs[i], -1)); + gchar** mimeData = g_strsplit(mime.get(), ":", 3); + if (g_strv_length(mimeData) < 3) { + g_strfreev(mimeData); + continue; + } + + String description = String::fromUTF8(mimeData[2]); + gchar** extensions = g_strsplit(mimeData[1], ",", -1); + + Vector<String> extVector; + for (int j = 0; extensions[j]; j++) + extVector.append(String::fromUTF8(extensions[j])); + + determineQuirks(mimeData[0]); + m_mimeToExtensions.add(mimeData[0], extVector); + m_mimeToDescriptions.add(mimeData[0], description); + + g_strfreev(extensions); + g_strfreev(mimeData); + } + g_strfreev(mimeDescs); + + return true; +#else + notImplemented(); + return false; +#endif +} + +bool PluginPackage::load() +{ + if (m_isLoaded) { + m_loadCount++; + return true; + } + + GOwnPtr<gchar> finalPath(g_strdup(m_path.utf8().data())); + while (g_file_test(finalPath.get(), G_FILE_TEST_IS_SYMLINK)) { + GOwnPtr<GFile> file(g_file_new_for_path(finalPath.get())); + GOwnPtr<GFile> dir(g_file_get_parent(file.get())); + GOwnPtr<gchar> linkPath(g_file_read_link(finalPath.get(), 0)); + GOwnPtr<GFile> resolvedFile(g_file_resolve_relative_path(dir.get(), linkPath.get())); + finalPath.set(g_file_get_path(resolvedFile.get())); + } + + // No joke. If there is a netscape component in the path, go back + // to the symlink, as flash breaks otherwise. + // See http://src.chromium.org/viewvc/chrome/trunk/src/webkit/glue/plugins/plugin_list_posix.cc + GOwnPtr<gchar> baseName(g_path_get_basename(finalPath.get())); + if (!g_strcmp0(baseName.get(), "libflashplayer.so") + && g_strstr_len(finalPath.get(), -1, "/netscape/")) + finalPath.set(g_strdup(m_path.utf8().data())); + + m_module = g_module_open(finalPath.get(), G_MODULE_BIND_LOCAL); + + if (!m_module) { + LOG(Plugins,"Module Load Failed :%s, Error:%s\n", (m_path.utf8()).data(), g_module_error()); + return false; + } + + m_isLoaded = true; + + NP_InitializeFuncPtr NP_Initialize = 0; + m_NPP_Shutdown = 0; + + NPError npErr; + + g_module_symbol(m_module, "NP_Initialize", (void**)&NP_Initialize); + g_module_symbol(m_module, "NP_Shutdown", (void**)&m_NPP_Shutdown); + + if (!NP_Initialize || !m_NPP_Shutdown) + goto abort; + + memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); + m_pluginFuncs.size = sizeof(m_pluginFuncs); + + initializeBrowserFuncs(); + +#if defined(XP_UNIX) + npErr = NP_Initialize(&m_browserFuncs, &m_pluginFuncs); +#else + npErr = NP_Initialize(&m_browserFuncs); +#endif + if (npErr != NPERR_NO_ERROR) + goto abort; + + m_loadCount++; + return true; + +abort: + unloadWithoutShutdown(); + return false; +} + +uint16_t PluginPackage::NPVersion() const +{ + return NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL; +} +} diff --git a/Source/WebCore/plugins/gtk/PluginViewGtk.cpp b/Source/WebCore/plugins/gtk/PluginViewGtk.cpp new file mode 100644 index 0000000..ec855a2 --- /dev/null +++ b/Source/WebCore/plugins/gtk/PluginViewGtk.cpp @@ -0,0 +1,901 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2009, 2010 Kakai, Inc. <brian@kakai.com> + * Copyright (C) 2010 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 COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginView.h" + +#include "Bridge.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "FocusController.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameTree.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "HostWindow.h" +#include "Image.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "Page.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformMouseEvent.h" +#include "PluginDebug.h" +#include "PluginMainThreadScheduler.h" +#include "PluginPackage.h" +#include "RenderLayer.h" +#include "Settings.h" +#include "JSDOMBinding.h" +#include "ScriptController.h" +#include "npruntime_impl.h" +#include "runtime_root.h" +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> + +#ifdef GTK_API_VERSION_2 +#include <gdkconfig.h> +#endif +#include <gtk/gtk.h> + +#if defined(XP_UNIX) +#include "RefPtrCairo.h" +#include "gtk2xtbin.h" +#define Bool int // this got undefined somewhere +#define Status int // ditto +#include <X11/extensions/Xrender.h> +#include <cairo/cairo-xlib.h> +#include <gdk/gdkx.h> +#elif defined(GDK_WINDOWING_WIN32) +#include "PluginMessageThrottlerWin.h" +#include <gdk/gdkwin32.h> +#endif + +using JSC::ExecState; +using JSC::Interpreter; +using JSC::JSLock; +using JSC::JSObject; +using JSC::UString; + +using std::min; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +bool PluginView::dispatchNPEvent(NPEvent& event) +{ + // sanity check + if (!m_plugin->pluginFuncs()->event) + return false; + + PluginView::setCurrentPluginView(this); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + + bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event); + + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + return accepted; +} + +#if defined(XP_UNIX) +static Window getRootWindow(Frame* parentFrame) +{ + GtkWidget* parentWidget = parentFrame->view()->hostWindow()->platformPageClient(); + GdkScreen* gscreen = gtk_widget_get_screen(parentWidget); + return GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(gscreen)); +} +#endif + +void PluginView::updatePluginWidget() +{ + if (!parent()) + return; + + ASSERT(parent()->isFrameView()); + FrameView* frameView = static_cast<FrameView*>(parent()); + + IntRect oldWindowRect = m_windowRect; + IntRect oldClipRect = m_clipRect; + + m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); + m_clipRect = windowClipRect(); + m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); + + if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect) + return; + +#if defined(XP_UNIX) + if (!m_isWindowed) { + Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + if (m_drawable) + XFreePixmap(display, m_drawable); + + m_drawable = XCreatePixmap(display, getRootWindow(m_parentFrame.get()), + m_windowRect.width(), m_windowRect.height(), + ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth); + XSync(display, false); // make sure that the server knows about the Drawable + } +#endif + + setNPWindowIfNeeded(); +} + +void PluginView::setFocus(bool focused) +{ + ASSERT(platformPluginWidget() == platformWidget()); + Widget::setFocus(focused); +} + +void PluginView::show() +{ + ASSERT(platformPluginWidget() == platformWidget()); + Widget::show(); +} + +void PluginView::hide() +{ + ASSERT(platformPluginWidget() == platformWidget()); + Widget::hide(); +} + +void PluginView::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_isStarted) { + paintMissingPluginIcon(context, rect); + return; + } + + if (context->paintingDisabled()) + return; + + setNPWindowIfNeeded(); + + if (m_isWindowed) + return; + +#if defined(XP_UNIX) + if (!m_drawable) + return; + + Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + const bool syncX = m_pluginDisplay && m_pluginDisplay != display; + + IntRect exposedRect(rect); + exposedRect.intersect(frameRect()); + exposedRect.move(-frameRect().x(), -frameRect().y()); + + RefPtr<cairo_surface_t> drawableSurface = adoptRef(cairo_xlib_surface_create(display, + m_drawable, + m_visual, + m_windowRect.width(), + m_windowRect.height())); + + if (m_isTransparent) { + // If we have a 32 bit drawable and the plugin wants transparency, + // we'll clear the exposed area to transparent first. Otherwise, + // we'd end up with junk in there from the last paint, or, worse, + // uninitialized data. + RefPtr<cairo_t> cr = adoptRef(cairo_create(drawableSurface.get())); + + if (!(cairo_surface_get_content(drawableSurface.get()) & CAIRO_CONTENT_ALPHA)) { + // Attempt to fake it when we don't have an alpha channel on our + // pixmap. If that's not possible, at least clear the window to + // avoid drawing artifacts. + + // This Would not work without double buffering, but we always use it. + cairo_set_source_surface(cr.get(), cairo_get_group_target(context->platformContext()), + -m_windowRect.x(), -m_windowRect.y()); + cairo_set_operator(cr.get(), CAIRO_OPERATOR_SOURCE); + } else + cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR); + + cairo_rectangle(cr.get(), exposedRect.x(), exposedRect.y(), + exposedRect.width(), exposedRect.height()); + cairo_fill(cr.get()); + } + + XEvent xevent; + memset(&xevent, 0, sizeof(XEvent)); + XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; + exposeEvent.type = GraphicsExpose; + exposeEvent.display = display; + exposeEvent.drawable = m_drawable; + exposeEvent.x = exposedRect.x(); + exposeEvent.y = exposedRect.y(); + exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode + exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode + + dispatchNPEvent(xevent); + + if (syncX) + XSync(m_pluginDisplay, false); // sync changes by plugin + + cairo_t* cr = context->platformContext(); + cairo_save(cr); + + cairo_set_source_surface(cr, drawableSurface.get(), frameRect().x(), frameRect().y()); + + cairo_rectangle(cr, + frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y(), + exposedRect.width(), exposedRect.height()); + cairo_clip(cr); + + if (m_isTransparent) + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + else + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + + cairo_restore(cr); +#endif // defined(XP_UNIX) +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + + if (m_isWindowed) + return; + + if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent) + return; + + NPEvent xEvent; +#if defined(XP_UNIX) + initXEvent(&xEvent); + GdkEventKey* gdkEvent = event->keyEvent()->gdkEventKey(); + + xEvent.type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // KeyPress/Release get unset somewhere + xEvent.xkey.root = getRootWindow(m_parentFrame.get()); + xEvent.xkey.subwindow = 0; // we have no child window + xEvent.xkey.time = event->timeStamp(); + xEvent.xkey.state = gdkEvent->state; // GdkModifierType mirrors xlib state masks + xEvent.xkey.keycode = gdkEvent->hardware_keycode; + xEvent.xkey.same_screen = true; + + // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window + // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not + // set to their normal Xserver values. e.g. Key events don't have a position. + // source: https://developer.mozilla.org/en/NPEvent + xEvent.xkey.x = 0; + xEvent.xkey.y = 0; + xEvent.xkey.x_root = 0; + xEvent.xkey.y_root = 0; +#endif + + if (!dispatchNPEvent(xEvent)) + event->setDefaultHandled(); +} + +#if defined(XP_UNIX) +static unsigned int inputEventState(MouseEvent* event) +{ + unsigned int state = 0; + if (event->ctrlKey()) + state |= ControlMask; + if (event->shiftKey()) + state |= ShiftMask; + if (event->altKey()) + state |= Mod1Mask; + if (event->metaKey()) + state |= Mod4Mask; + return state; +} + +void PluginView::initXEvent(XEvent* xEvent) +{ + memset(xEvent, 0, sizeof(XEvent)); + + xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server + xEvent->xany.send_event = false; + xEvent->xany.display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + // NOTE: event->xany.window doesn't always correspond to the .window property of other XEvent's + // but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify + // events; thus, this is right: + GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient(); + xEvent->xany.window = widget ? GDK_WINDOW_XWINDOW(gtk_widget_get_window(widget)) : 0; +} + +static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame) +{ + XButtonEvent& xbutton = xEvent->xbutton; + xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease; + xbutton.root = getRootWindow(parentFrame); + xbutton.subwindow = 0; + xbutton.time = event->timeStamp(); + xbutton.x = postZoomPos.x(); + xbutton.y = postZoomPos.y(); + xbutton.x_root = event->screenX(); + xbutton.y_root = event->screenY(); + xbutton.state = inputEventState(event); + switch (event->button()) { + case MiddleButton: + xbutton.button = Button2; + break; + case RightButton: + xbutton.button = Button3; + break; + case LeftButton: + default: + xbutton.button = Button1; + break; + } + xbutton.same_screen = true; +} + +static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame) +{ + XMotionEvent& xmotion = xEvent->xmotion; + xmotion.type = MotionNotify; + xmotion.root = getRootWindow(parentFrame); + xmotion.subwindow = 0; + xmotion.time = event->timeStamp(); + xmotion.x = postZoomPos.x(); + xmotion.y = postZoomPos.y(); + xmotion.x_root = event->screenX(); + xmotion.y_root = event->screenY(); + xmotion.state = inputEventState(event); + xmotion.is_hint = NotifyNormal; + xmotion.same_screen = true; +} + +static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame) +{ + XCrossingEvent& xcrossing = xEvent->xcrossing; + xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify; + xcrossing.root = getRootWindow(parentFrame); + xcrossing.subwindow = 0; + xcrossing.time = event->timeStamp(); + xcrossing.x = postZoomPos.y(); + xcrossing.y = postZoomPos.x(); + xcrossing.x_root = event->screenX(); + xcrossing.y_root = event->screenY(); + xcrossing.state = inputEventState(event); + xcrossing.mode = NotifyNormal; + xcrossing.detail = NotifyDetailNone; + xcrossing.same_screen = true; + xcrossing.focus = false; +} +#endif + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + + if (m_isWindowed) + return; + + if (event->type() == eventNames().mousedownEvent) { + if (Page* page = m_parentFrame->page()) + page->focusController()->setActive(true); + focusPluginElement(); + } + + NPEvent xEvent; +#if defined(XP_UNIX) + initXEvent(&xEvent); + + IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); + + if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent) + setXButtonEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get()); + else if (event->type() == eventNames().mousemoveEvent) + setXMotionEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get()); + else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent) + setXCrossingEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get()); + else + return; +#endif + + if (!dispatchNPEvent(xEvent)) + event->setDefaultHandled(); +} + +#if defined(XP_UNIX) +void PluginView::handleFocusInEvent() +{ + XEvent npEvent; + initXEvent(&npEvent); + + XFocusChangeEvent& event = npEvent.xfocus; + event.type = 9; // FocusIn gets unset somewhere + event.mode = NotifyNormal; + event.detail = NotifyDetailNone; + + dispatchNPEvent(npEvent); +} + +void PluginView::handleFocusOutEvent() +{ + XEvent npEvent; + initXEvent(&npEvent); + + XFocusChangeEvent& event = npEvent.xfocus; + event.type = 10; // FocusOut gets unset somewhere + event.mode = NotifyNormal; + event.detail = NotifyDetailNone; + + dispatchNPEvent(npEvent); +} +#endif + +void PluginView::setParent(ScrollView* parent) +{ + Widget::setParent(parent); + + if (parent) + init(); +} + +void PluginView::setNPWindowRect(const IntRect&) +{ + if (!m_isWindowed) + setNPWindowIfNeeded(); +} + +void PluginView::setNPWindowIfNeeded() +{ + if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) + return; + + // If the plugin didn't load sucessfully, no point in calling setwindow + if (m_status != PluginStatusLoadedSuccessfully) + return; + + // On Unix, only call plugin's setwindow if it's full-page or windowed + if (m_mode != NP_FULL && m_mode != NP_EMBED) + return; + + // Check if the platformPluginWidget still exists + if (m_isWindowed && !platformPluginWidget()) + return; + + if (m_isWindowed) { + m_npWindow.x = m_windowRect.x(); + m_npWindow.y = m_windowRect.y(); + m_npWindow.width = m_windowRect.width(); + m_npWindow.height = m_windowRect.height(); + + m_npWindow.clipRect.left = max(0, m_clipRect.x()); + m_npWindow.clipRect.top = max(0, m_clipRect.y()); + m_npWindow.clipRect.right = m_clipRect.x() + m_clipRect.width(); + m_npWindow.clipRect.bottom = m_clipRect.y() + m_clipRect.height(); + } else { + m_npWindow.x = 0; + m_npWindow.y = 0; + m_npWindow.width = m_windowRect.width(); + m_npWindow.height = m_windowRect.height(); + + m_npWindow.clipRect.left = 0; + m_npWindow.clipRect.top = 0; + m_npWindow.clipRect.right = 0; + m_npWindow.clipRect.bottom = 0; + } + + PluginView::setCurrentPluginView(this); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + + if (m_isWindowed) { + GtkAllocation allocation = { m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height() }; + gtk_widget_size_allocate(platformPluginWidget(), &allocation); +#if defined(XP_UNIX) + if (!m_needsXEmbed) { + gtk_xtbin_set_position(GTK_XTBIN(platformPluginWidget()), m_windowRect.x(), m_windowRect.y()); + gtk_xtbin_resize(platformPluginWidget(), m_windowRect.width(), m_windowRect.height()); + } +#endif + } +} + +void PluginView::setParentVisible(bool visible) +{ + if (isParentVisible() == visible) + return; + + Widget::setParentVisible(visible); + + if (isSelfVisible() && platformPluginWidget()) { + if (visible) + gtk_widget_show(platformPluginWidget()); + else + gtk_widget_hide(platformPluginWidget()); + } +} + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) +{ + WTF::String filename(buf, len); + + if (filename.startsWith("file:///")) + filename = filename.substring(8); + + // Get file info + if (!g_file_test ((filename.utf8()).data(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) + return NPERR_FILE_NOT_FOUND; + + //FIXME - read the file data into buffer + FILE* fileHandle = fopen((filename.utf8()).data(), "r"); + + if (fileHandle == 0) + return NPERR_FILE_NOT_FOUND; + + //buffer.resize(); + + int bytesRead = fread(buffer.data(), 1, 0, fileHandle); + + fclose(fileHandle); + + if (bytesRead <= 0) + return NPERR_FILE_NOT_FOUND; + + return NPERR_NO_ERROR; +} + +bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVToolkit: +#if defined(XP_UNIX) + *static_cast<uint32_t*>(value) = 2; +#else + *static_cast<uint32_t*>(value) = 0; +#endif + *result = NPERR_NO_ERROR; + return true; + + case NPNVSupportsXEmbedBool: +#if defined(XP_UNIX) + *static_cast<NPBool*>(value) = true; +#else + *static_cast<NPBool*>(value) = false; +#endif + *result = NPERR_NO_ERROR; + return true; + + case NPNVjavascriptEnabledBool: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + + case NPNVSupportsWindowless: +#if defined(XP_UNIX) + *static_cast<NPBool*>(value) = true; +#else + *static_cast<NPBool*>(value) = false; +#endif + *result = NPERR_NO_ERROR; + return true; + + default: + return false; + } +} + +bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVxDisplay: +#if defined(XP_UNIX) + if (m_needsXEmbed) + *(void **)value = (void *)GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + else + *(void **)value = (void *)GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay; + *result = NPERR_NO_ERROR; +#else + *result = NPERR_GENERIC_ERROR; +#endif + return true; + +#if defined(XP_UNIX) + case NPNVxtAppContext: + if (!m_needsXEmbed) { + *(void **)value = XtDisplayToApplicationContext (GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay); + + *result = NPERR_NO_ERROR; + } else + *result = NPERR_GENERIC_ERROR; + return true; +#endif + + case NPNVnetscapeWindow: { +#if defined(XP_UNIX) + void* w = reinterpret_cast<void*>(value); + GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient(); + *((XID *)w) = GDK_WINDOW_XWINDOW(gtk_widget_get_window(widget)); +#endif +#ifdef GDK_WINDOWING_WIN32 + HGDIOBJ* w = reinterpret_cast<HGDIOBJ*>(value); + GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient(); + *w = GDK_WINDOW_HWND(gtk_widget_get_window(widget)); +#endif + *result = NPERR_NO_ERROR; + return true; + } + + default: + return false; + } +} + +void PluginView::invalidateRect(const IntRect& rect) +{ + if (m_isWindowed) { + gtk_widget_queue_draw_area(GTK_WIDGET(platformPluginWidget()), rect.x(), rect.y(), rect.width(), rect.height()); + return; + } + + invalidateWindowlessPluginRect(rect); +} + +void PluginView::invalidateRect(NPRect* rect) +{ + if (!rect) { + invalidate(); + return; + } + + IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); + invalidateWindowlessPluginRect(r); +} + +void PluginView::invalidateRegion(NPRegion) +{ + // TODO: optimize + invalidate(); +} + +void PluginView::forceRedraw() +{ + if (m_isWindowed) + gtk_widget_queue_draw(platformPluginWidget()); + else + gtk_widget_queue_draw(m_parentFrame->view()->hostWindow()->platformPageClient()); +} + +static Display* getPluginDisplay() +{ + // The plugin toolkit might have a different X connection open. 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. + +#if defined(XP_UNIX) + return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); +#else + return 0; +#endif +} + +#if defined(XP_UNIX) +static void getVisualAndColormap(int depth, Visual** visual, Colormap* colormap) +{ + *visual = 0; + *colormap = 0; + + Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + int rmaj, rmin; + if (depth == 32 && (!XRenderQueryVersion(display, &rmaj, &rmin) || (rmaj == 0 && rmin < 5))) + return; + + XVisualInfo templ; + templ.screen = gdk_screen_get_number(gdk_screen_get_default()); + templ.depth = depth; + templ.c_class = TrueColor; + int nVisuals; + XVisualInfo* visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nVisuals); + + if (!nVisuals) + return; + + if (depth == 32) { + for (int idx = 0; idx < nVisuals; ++idx) { + XRenderPictFormat* format = XRenderFindVisualFormat(display, visualInfo[idx].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask) { + *visual = visualInfo[idx].visual; + break; + } + } + } else + *visual = visualInfo[0].visual; + + XFree(visualInfo); + + if (*visual) + *colormap = XCreateColormap(display, GDK_ROOT_WINDOW(), *visual, AllocNone); +} +#endif + +static gboolean plugRemovedCallback(GtkSocket* socket, gpointer) +{ + return TRUE; +} + +static void plugAddedCallback(GtkSocket* socket, PluginView* view) +{ + if (!socket || !view) + return; + + // FIXME: Java Plugins do not seem to draw themselves properly the + // first time unless we do a size-allocate after they have done + // the plug operation on their side, which in general does not + // happen since we do size-allocates before setting the + // NPWindow. Apply this workaround until we figure out a better + // solution, if any. + IntRect rect = view->frameRect(); + GtkAllocation allocation = { rect.x(), rect.y(), rect.width(), rect.height() }; + gtk_widget_size_allocate(GTK_WIDGET(socket), &allocation); +} + +bool PluginView::platformStart() +{ + ASSERT(m_isStarted); + ASSERT(m_status == PluginStatusLoadedSuccessfully); + + if (m_plugin->pluginFuncs()->getvalue) { + PluginView::setCurrentPluginView(this); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + } + + if (m_isWindowed) { +#if defined(XP_UNIX) + GtkWidget* pageClient = m_parentFrame->view()->hostWindow()->platformPageClient(); + + if (m_needsXEmbed) { + // If our parent is not anchored the startup process will + // fail miserably for XEmbed plugins a bit later on when + // we try to get the ID of our window (since realize will + // fail), so let's just abort here. + if (!gtk_widget_get_parent(pageClient)) + return false; + + setPlatformWidget(gtk_socket_new()); + gtk_container_add(GTK_CONTAINER(pageClient), platformPluginWidget()); + g_signal_connect(platformPluginWidget(), "plug-added", G_CALLBACK(plugAddedCallback), this); + g_signal_connect(platformPluginWidget(), "plug-removed", G_CALLBACK(plugRemovedCallback), NULL); + } else + setPlatformWidget(gtk_xtbin_new(pageClient, 0)); +#else + setPlatformWidget(gtk_socket_new()); + gtk_container_add(GTK_CONTAINER(pageClient), platformPluginWidget()); +#endif + } else { + setPlatformWidget(0); + m_pluginDisplay = getPluginDisplay(); + } + + show(); + +#if defined(XP_UNIX) + NPSetWindowCallbackStruct* ws = new NPSetWindowCallbackStruct(); + ws->type = 0; +#endif + + if (m_isWindowed) { + m_npWindow.type = NPWindowTypeWindow; +#if defined(XP_UNIX) + if (m_needsXEmbed) { + GtkWidget* widget = platformPluginWidget(); + gtk_widget_realize(widget); + m_npWindow.window = (void*)gtk_socket_get_id(GTK_SOCKET(platformPluginWidget())); + GdkWindow* window = gtk_widget_get_window(widget); + ws->display = GDK_WINDOW_XDISPLAY(window); + ws->visual = GDK_VISUAL_XVISUAL(gdk_window_get_visual(window)); + ws->depth = gdk_visual_get_depth(gdk_window_get_visual(window)); + ws->colormap = XCreateColormap(ws->display, GDK_ROOT_WINDOW(), ws->visual, AllocNone); + } else { + m_npWindow.window = (void*)GTK_XTBIN(platformPluginWidget())->xtwindow; + ws->display = GTK_XTBIN(platformPluginWidget())->xtdisplay; + ws->visual = GTK_XTBIN(platformPluginWidget())->xtclient.xtvisual; + ws->depth = GTK_XTBIN(platformPluginWidget())->xtclient.xtdepth; + ws->colormap = GTK_XTBIN(platformPluginWidget())->xtclient.xtcolormap; + } + XFlush (ws->display); +#elif defined(GDK_WINDOWING_WIN32) + m_npWindow.window = (void*)GDK_WINDOW_HWND(gtk_widget_get_window(platformPluginWidget())); +#endif + } else { + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = 0; // Not used? + +#if defined(XP_UNIX) + GdkScreen* gscreen = gdk_screen_get_default(); + GdkVisual* gvisual = gdk_screen_get_system_visual(gscreen); + + if (gdk_visual_get_depth(gvisual) == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) { + getVisualAndColormap(32, &m_visual, &m_colormap); + ws->depth = 32; + } + + if (!m_visual) { + getVisualAndColormap(gdk_visual_get_depth(gvisual), &m_visual, &m_colormap); + ws->depth = gdk_visual_get_depth(gvisual); + } + + ws->display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + ws->visual = m_visual; + ws->colormap = m_colormap; + + m_npWindow.x = 0; + m_npWindow.y = 0; + m_npWindow.width = -1; + m_npWindow.height = -1; +#else + notImplemented(); + m_status = PluginStatusCanNotLoadPlugin; + return false; +#endif + } + +#if defined(XP_UNIX) + m_npWindow.ws_info = ws; +#endif + + // TODO remove in favor of null events, like mac port? + if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) + updatePluginWidget(); // was: setNPWindowIfNeeded(), but this doesn't produce 0x0 rects at first go + + return true; +} + +void PluginView::platformDestroy() +{ +#if defined(XP_UNIX) + if (m_drawable) { + XFreePixmap(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), m_drawable); + m_drawable = 0; + } +#endif +} + +void PluginView::halt() +{ +} + +void PluginView::restart() +{ +} + +} // namespace WebCore + diff --git a/Source/WebCore/plugins/gtk/gtk2xtbin.c b/Source/WebCore/plugins/gtk/gtk2xtbin.c new file mode 100644 index 0000000..e03fad3 --- /dev/null +++ b/Source/WebCore/plugins/gtk/gtk2xtbin.c @@ -0,0 +1,966 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:expandtab:shiftwidth=2:tabstop=2: */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Gtk2XtBin Widget Implementation. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * The GtkXtBin widget allows for Xt toolkit code to be used + * inside a GTK application. + */ + +#include "GtkVersioning.h" +#include "xembed.h" +#include "gtk2xtbin.h" +#include <gtk/gtk.h> +#include <gdk/gdkx.h> +#include <glib.h> +#include <assert.h> +#include <sys/time.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/* Xlib/Xt stuff */ +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Shell.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> + +/* uncomment this if you want debugging information about widget + creation and destruction */ +#undef DEBUG_XTBIN + +#define XTBIN_MAX_EVENTS 30 + +static void gtk_xtbin_class_init (GtkXtBinClass *klass); +static void gtk_xtbin_init (GtkXtBin *xtbin); +static void gtk_xtbin_realize (GtkWidget *widget); +static void gtk_xtbin_unrealize (GtkWidget *widget); +static void gtk_xtbin_dispose (GObject *object); + +/* Xt aware XEmbed */ +static void xt_client_init (XtClient * xtclient, + Visual *xtvisual, + Colormap xtcolormap, + int xtdepth); +static void xt_client_create (XtClient * xtclient, + Window embeder, + int height, + int width ); +static void xt_client_unrealize (XtClient* xtclient); +static void xt_client_destroy (XtClient* xtclient); +static void xt_client_set_info (Widget xtplug, + unsigned long flags); +static void xt_client_event_handler (Widget w, + XtPointer client_data, + XEvent *event); +static void xt_client_handle_xembed_message (Widget w, + XtPointer client_data, + XEvent *event); +static void xt_client_focus_listener (Widget w, + XtPointer user_data, + XEvent *event); +static void xt_add_focus_listener( Widget w, XtPointer user_data ); +static void xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data); +static void xt_remove_focus_listener(Widget w, XtPointer user_data); +static void send_xembed_message (XtClient *xtclient, + long message, + long detail, + long data1, + long data2, + long time); +static int error_handler (Display *display, + XErrorEvent *error); +/* For error trap of XEmbed */ +static void trap_errors(void); +static int untrap_error(void); +static int (*old_error_handler) (Display *, XErrorEvent *); +static int trapped_error_code = 0; + +static GtkWidgetClass *parent_class = NULL; + +static Display *xtdisplay = NULL; +static String *fallback = NULL; +static gboolean xt_is_initialized = FALSE; +static gint num_widgets = 0; + +static GPollFD xt_event_poll_fd; +static gint xt_polling_timer_id = 0; +static guint tag = 0; + +static gboolean +xt_event_prepare (GSource* source_data, + gint *timeout) +{ + int mask; + + GDK_THREADS_ENTER(); + mask = XPending(xtdisplay); + GDK_THREADS_LEAVE(); + + return (gboolean)mask; +} + +static gboolean +xt_event_check (GSource* source_data) +{ + GDK_THREADS_ENTER (); + + if (xt_event_poll_fd.revents & G_IO_IN) { + int mask; + mask = XPending(xtdisplay); + GDK_THREADS_LEAVE (); + return (gboolean)mask; + } + + GDK_THREADS_LEAVE (); + return FALSE; +} + +static gboolean +xt_event_dispatch (GSource* source_data, + GSourceFunc call_back, + gpointer user_data) +{ + XtAppContext ac; + int i = 0; + + ac = XtDisplayToApplicationContext(xtdisplay); + + GDK_THREADS_ENTER (); + + /* Process only real X traffic here. We only look for data on the + * pipe, limit it to XTBIN_MAX_EVENTS and only call + * XtAppProcessEvent so that it will look for X events. There's no + * timer processing here since we already have a timer callback that + * does it. */ + for (i=0; i < XTBIN_MAX_EVENTS && XPending(xtdisplay); i++) { + XtAppProcessEvent(ac, XtIMXEvent); + } + + GDK_THREADS_LEAVE (); + + return TRUE; +} + +typedef void (*GSourceFuncsFinalize) (GSource* source); + +static GSourceFuncs xt_event_funcs = { + xt_event_prepare, + xt_event_check, + xt_event_dispatch, + (GSourceFuncsFinalize)g_free, + (GSourceFunc)NULL, + (GSourceDummyMarshal)NULL +}; + +static gboolean +xt_event_polling_timer_callback(gpointer user_data) +{ + Display * display; + XtAppContext ac; + int eventsToProcess = 20; + + display = (Display *)user_data; + ac = XtDisplayToApplicationContext(display); + + /* We need to process many Xt events here. If we just process + one event we might starve one or more Xt consumers. On the other hand + this could hang the whole app if Xt events come pouring in. So process + up to 20 Xt events right now and save the rest for later. This is a hack, + but it oughta work. We *really* should have out of process plugins. + */ + while (eventsToProcess-- && XtAppPending(ac)) + XtAppProcessEvent(ac, XtIMAll); + return TRUE; +} + +GType +gtk_xtbin_get_type (void) +{ + static GType xtbin_type = 0; + + if (!xtbin_type) { + static const GTypeInfo xtbin_info = + { + sizeof (GtkXtBinClass), + NULL, + NULL, + + (GClassInitFunc)gtk_xtbin_class_init, + NULL, + NULL, + + sizeof (GtkXtBin), + 0, + (GInstanceInitFunc)gtk_xtbin_init, + NULL + }; + xtbin_type = g_type_register_static (GTK_TYPE_SOCKET, + "GtkXtBin", + &xtbin_info, + 0); + } + return xtbin_type; +} + +static void +gtk_xtbin_class_init (GtkXtBinClass *klass) +{ + GtkWidgetClass *widget_class; + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (klass); + + widget_class = GTK_WIDGET_CLASS (klass); + widget_class->realize = gtk_xtbin_realize; + widget_class->unrealize = gtk_xtbin_unrealize; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = gtk_xtbin_dispose; +} + +static void +gtk_xtbin_init (GtkXtBin *xtbin) +{ + xtbin->xtdisplay = NULL; + xtbin->parent_window = NULL; + xtbin->xtwindow = 0; + xtbin->x = 0; + xtbin->y = 0; +} + +static void +gtk_xtbin_realize (GtkWidget *widget) +{ + GtkXtBin *xtbin; + GtkAllocation allocation = { 0, 0, 200, 200 }; +#if GTK_CHECK_VERSION(2, 18, 0) + GtkAllocation widget_allocation; +#endif + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_realize()\n"); +#endif + + g_return_if_fail (GTK_IS_XTBIN (widget)); + + xtbin = GTK_XTBIN (widget); + + /* caculate the allocation before realize */ +#if GTK_CHECK_VERSION(2, 24, 0) + allocation.width = gdk_window_get_width(xtbin->parent_window); + allocation.height = gdk_window_get_height(xtbin->parent_window); +#else + gint x, y, w, h, d; /* geometry of window */ + gdk_window_get_geometry(xtbin->parent_window, &x, &y, &w, &h, &d); + allocation.width = w; + allocation.height = h; +#endif + gtk_widget_size_allocate (widget, &allocation); + +#ifdef DEBUG_XTBIN + printf("initial allocation %d %d %d %d\n", x, y, w, h); +#endif + +#if GTK_CHECK_VERSION(2, 18, 0) + gtk_widget_get_allocation(widget, &widget_allocation); + xtbin->width = widget_allocation.width; + xtbin->height = widget_allocation.height; +#else + xtbin->width = widget->allocation.width; + xtbin->height = widget->allocation.height; +#endif + + /* use GtkSocket's realize */ + (*GTK_WIDGET_CLASS(parent_class)->realize)(widget); + + /* create the Xt client widget */ + xt_client_create(&(xtbin->xtclient), + gtk_socket_get_id(GTK_SOCKET(xtbin)), + xtbin->height, + xtbin->width); + xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget); + + gdk_flush(); + + /* now that we have created the xt client, add it to the socket. */ + gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow); +} + + + +GtkWidget* +gtk_xtbin_new (GtkWidget *parent_widget, String *f) +{ + GtkXtBin *xtbin; + gpointer user_data; + GdkScreen *screen; + GdkVisual* visual; + Colormap colormap; + GdkWindow* parent_window = gtk_widget_get_window(parent_widget); + + assert(parent_window != NULL); + xtbin = g_object_new (GTK_TYPE_XTBIN, NULL); + + if (!xtbin) + return (GtkWidget*)NULL; + + if (f) + fallback = f; + + /* Initialize the Xt toolkit */ + xtbin->parent_window = parent_window; + + screen = gtk_widget_get_screen(parent_widget); + visual = gdk_screen_get_system_visual(screen); + colormap = XCreateColormap(GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(screen)), + GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(screen)), + GDK_VISUAL_XVISUAL(visual), AllocNone); + + xt_client_init(&(xtbin->xtclient), + GDK_VISUAL_XVISUAL(visual), + colormap, + gdk_visual_get_depth(visual)); + + if (!xtbin->xtclient.xtdisplay) { + /* If XtOpenDisplay failed, we can't go any further. + * Bail out. + */ +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n"); +#endif + g_free (xtbin); + return (GtkWidget *)NULL; + } + + /* If this is the first running widget, hook this display into the + mainloop */ + if (0 == num_widgets) { + int cnumber; + /* + * hook Xt event loop into the glib event loop. + */ + + /* the assumption is that gtk_init has already been called */ + GSource* gs = g_source_new(&xt_event_funcs, sizeof(GSource)); + if (!gs) { + return NULL; + } + + g_source_set_priority(gs, GDK_PRIORITY_EVENTS); + g_source_set_can_recurse(gs, TRUE); + tag = g_source_attach(gs, (GMainContext*)NULL); +#ifdef VMS + cnumber = XConnectionNumber(xtdisplay); +#else + cnumber = ConnectionNumber(xtdisplay); +#endif + xt_event_poll_fd.fd = cnumber; + xt_event_poll_fd.events = G_IO_IN; + xt_event_poll_fd.revents = 0; /* hmm... is this correct? */ + + g_main_context_add_poll ((GMainContext*)NULL, + &xt_event_poll_fd, + G_PRIORITY_LOW); + /* add a timer so that we can poll and process Xt timers */ + xt_polling_timer_id = + g_timeout_add(25, + (GSourceFunc)xt_event_polling_timer_callback, + xtdisplay); + } + + /* Bump up our usage count */ + num_widgets++; + + /* Build the hierachy */ + xtbin->xtdisplay = xtbin->xtclient.xtdisplay; + gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window); + gdk_window_get_user_data(xtbin->parent_window, &user_data); + if (user_data) + gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin)); + + return GTK_WIDGET (xtbin); +} + +void +gtk_xtbin_set_position (GtkXtBin *xtbin, + gint x, + gint y) +{ + xtbin->x = x; + xtbin->y = y; + + if (gtk_widget_get_realized (GTK_WIDGET(xtbin))) + gdk_window_move (gtk_widget_get_window(GTK_WIDGET (xtbin)), x, y); +} + +void +gtk_xtbin_resize (GtkWidget *widget, + gint width, + gint height) +{ + Arg args[2]; + GtkXtBin *xtbin = GTK_XTBIN (widget); + GtkAllocation allocation; + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_resize %p %d %d\n", (void *)widget, width, height); +#endif + + xtbin->height = height; + xtbin->width = width; + + // Avoid BadValue errors in XtSetValues + if (height <= 0 || width <=0) { + height = 1; + width = 1; + } + XtSetArg(args[0], XtNheight, height); + XtSetArg(args[1], XtNwidth, width); + XtSetValues(xtbin->xtclient.top_widget, args, 2); + + /* we need to send a size allocate so the socket knows about the + size changes */ + allocation.x = xtbin->x; + allocation.y = xtbin->y; + allocation.width = xtbin->width; + allocation.height = xtbin->height; + + gtk_widget_size_allocate(widget, &allocation); +} + +static void +gtk_xtbin_unrealize (GtkWidget *object) +{ + GtkXtBin *xtbin; + GtkWidget *widget; + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_unrealize()\n"); +#endif + + /* gtk_object_destroy() will already hold a refcount on object + */ + xtbin = GTK_XTBIN(object); + widget = GTK_WIDGET(object); + + gtk_widget_set_visible(widget, FALSE); + if (gtk_widget_get_realized (widget)) { + xt_client_unrealize(&(xtbin->xtclient)); + } + + (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget); +} + +static void +gtk_xtbin_dispose (GObject *object) +{ + GtkXtBin *xtbin; + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_destroy()\n"); +#endif + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_XTBIN (object)); + + xtbin = GTK_XTBIN (object); + + if(xtbin->xtwindow) { + /* remove the event handler */ + xt_client_destroy(&(xtbin->xtclient)); + xtbin->xtwindow = 0; + + num_widgets--; /* reduce our usage count */ + + /* If this is the last running widget, remove the Xt display + connection from the mainloop */ + if (0 == num_widgets) { +#ifdef DEBUG_XTBIN + printf("removing the Xt connection from the main loop\n"); +#endif + g_main_context_remove_poll((GMainContext*)NULL, &xt_event_poll_fd); + g_source_remove(tag); + + g_source_remove(xt_polling_timer_id); + xt_polling_timer_id = 0; + } + } + + G_OBJECT_CLASS(parent_class)->dispose(object); +} + +/* +* Following is the implementation of Xt XEmbedded for client side +*/ + +/* Initial Xt plugin */ +static void +xt_client_init( XtClient * xtclient, + Visual *xtvisual, + Colormap xtcolormap, + int xtdepth) +{ + XtAppContext app_context; + char *mArgv[1]; + int mArgc = 0; + + /* + * Initialize Xt stuff + */ + xtclient->top_widget = NULL; + xtclient->child_widget = NULL; + xtclient->xtdisplay = NULL; + xtclient->xtvisual = NULL; + xtclient->xtcolormap = 0; + xtclient->xtdepth = 0; + + if (!xt_is_initialized) { +#ifdef DEBUG_XTBIN + printf("starting up Xt stuff\n"); +#endif + XtToolkitInitialize(); + app_context = XtCreateApplicationContext(); + if (fallback) + XtAppSetFallbackResources(app_context, fallback); + + xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL, + "Wrapper", NULL, 0, &mArgc, mArgv); + if (xtdisplay) + xt_is_initialized = TRUE; + } + xtclient->xtdisplay = xtdisplay; + xtclient->xtvisual = xtvisual; + xtclient->xtcolormap = xtcolormap; + xtclient->xtdepth = xtdepth; +} + +/* Create the Xt client widgets +* */ +static void +xt_client_create ( XtClient* xtclient , + Window embedderid, + int height, + int width ) +{ + int n; + Arg args[6]; + Widget child_widget; + Widget top_widget; + +#ifdef DEBUG_XTBIN + printf("xt_client_create() \n"); +#endif + top_widget = XtAppCreateShell("drawingArea", "Wrapper", + applicationShellWidgetClass, + xtclient->xtdisplay, + NULL, 0); + xtclient->top_widget = top_widget; + + /* set size of Xt window */ + n = 0; + XtSetArg(args[n], XtNheight, height);n++; + XtSetArg(args[n], XtNwidth, width);n++; + XtSetValues(top_widget, args, n); + + child_widget = XtVaCreateWidget("form", + compositeWidgetClass, + top_widget, NULL); + + n = 0; + XtSetArg(args[n], XtNheight, height);n++; + XtSetArg(args[n], XtNwidth, width);n++; + XtSetArg(args[n], XtNvisual, xtclient->xtvisual ); n++; + XtSetArg(args[n], XtNdepth, xtclient->xtdepth ); n++; + XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap ); n++; + XtSetArg(args[n], XtNborderWidth, 0); n++; + XtSetValues(child_widget, args, n); + + XSync(xtclient->xtdisplay, FALSE); + xtclient->oldwindow = top_widget->core.window; + top_widget->core.window = embedderid; + + /* this little trick seems to finish initializing the widget */ +#if XlibSpecificationRelease >= 6 + XtRegisterDrawable(xtclient->xtdisplay, + embedderid, + top_widget); +#else + _XtRegisterWindow( embedderid, + top_widget); +#endif + XtRealizeWidget(child_widget); + + /* listen to all Xt events */ + XSelectInput(xtclient->xtdisplay, + XtWindow(top_widget), + 0x0FFFFF); + xt_client_set_info (child_widget, 0); + + XtManageChild(child_widget); + xtclient->child_widget = child_widget; + + /* set the event handler */ + XtAddEventHandler(child_widget, + 0x0FFFFF & ~ResizeRedirectMask, + TRUE, + (XtEventHandler)xt_client_event_handler, xtclient); + XtAddEventHandler(child_widget, + SubstructureNotifyMask | ButtonReleaseMask, + TRUE, + (XtEventHandler)xt_client_focus_listener, + xtclient); + XSync(xtclient->xtdisplay, FALSE); +} + +static void +xt_client_unrealize ( XtClient* xtclient ) +{ +#if XlibSpecificationRelease >= 6 + XtUnregisterDrawable(xtclient->xtdisplay, + xtclient->top_widget->core.window); +#else + _XtUnregisterWindow(xtclient->top_widget->core.window, + xtclient->top_widget); +#endif + + /* flush the queue before we returning origin top_widget->core.window + or we can get X error since the window is gone */ + XSync(xtclient->xtdisplay, False); + + xtclient->top_widget->core.window = xtclient->oldwindow; + XtUnrealizeWidget(xtclient->top_widget); +} + +static void +xt_client_destroy (XtClient* xtclient) +{ + if(xtclient->top_widget) { + XtRemoveEventHandler(xtclient->child_widget, 0x0FFFFF, TRUE, + (XtEventHandler)xt_client_event_handler, xtclient); + XtDestroyWidget(xtclient->top_widget); + xtclient->top_widget = NULL; + } +} + +static void +xt_client_set_info (Widget xtplug, unsigned long flags) +{ + unsigned long buffer[2]; + + Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False); + + buffer[1] = 0; /* Protocol version */ + buffer[1] = flags; + + XChangeProperty (XtDisplay(xtplug), XtWindow(xtplug), + infoAtom, infoAtom, 32, + PropModeReplace, + (unsigned char *)buffer, 2); +} + +static void +xt_client_handle_xembed_message(Widget w, XtPointer client_data, XEvent *event) +{ + XtClient *xtplug = (XtClient*)client_data; + switch (event->xclient.data.l[1]) + { + case XEMBED_EMBEDDED_NOTIFY: + break; + case XEMBED_WINDOW_ACTIVATE: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_WINDOW_ACTIVATE\n"); +#endif + break; + case XEMBED_WINDOW_DEACTIVATE: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n"); +#endif + break; + case XEMBED_MODALITY_ON: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_MODALITY_ON\n"); +#endif + break; + case XEMBED_MODALITY_OFF: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_MODALITY_OFF\n"); +#endif + break; + case XEMBED_FOCUS_IN: + case XEMBED_FOCUS_OUT: + { + XEvent xevent; + memset(&xevent, 0, sizeof(xevent)); + + if(event->xclient.data.l[1] == XEMBED_FOCUS_IN) { +#ifdef DEBUG_XTBIN + printf("XTEMBED got focus in\n"); +#endif + xevent.xfocus.type = FocusIn; + } + else { +#ifdef DEBUG_XTBIN + printf("XTEMBED got focus out\n"); +#endif + xevent.xfocus.type = FocusOut; + } + + xevent.xfocus.window = XtWindow(xtplug->child_widget); + xevent.xfocus.display = XtDisplay(xtplug->child_widget); + XSendEvent(XtDisplay(xtplug->child_widget), + xevent.xfocus.window, + False, NoEventMask, + &xevent ); + XSync( XtDisplay(xtplug->child_widget), False); + } + break; + default: + break; + } /* End of XEmbed Message */ +} + +static void +xt_client_event_handler( Widget w, XtPointer client_data, XEvent *event) +{ + XtClient *xtplug = (XtClient*)client_data; + + switch(event->type) + { + case ClientMessage: + /* Handle xembed message */ + if (event->xclient.message_type== + XInternAtom (XtDisplay(xtplug->child_widget), + "_XEMBED", False)) { + xt_client_handle_xembed_message(w, client_data, event); + } + break; + case ReparentNotify: + break; + case MappingNotify: + xt_client_set_info (w, XEMBED_MAPPED); + break; + case UnmapNotify: + xt_client_set_info (w, 0); + break; + case FocusIn: + send_xembed_message ( xtplug, + XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); + break; + case FocusOut: + break; + case KeyPress: +#ifdef DEBUG_XTBIN + printf("Key Press Got!\n"); +#endif + break; + default: + break; + } /* End of switch(event->type) */ +} + +static void +send_xembed_message (XtClient *xtclient, + long message, + long detail, + long data1, + long data2, + long time) +{ + XEvent xevent; + Window w=XtWindow(xtclient->top_widget); + Display* dpy=xtclient->xtdisplay; + int errorcode; + + memset(&xevent,0,sizeof(xevent)); + xevent.xclient.window = w; + xevent.xclient.type = ClientMessage; + xevent.xclient.message_type = XInternAtom(dpy,"_XEMBED",False); + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = time; + xevent.xclient.data.l[1] = message; + xevent.xclient.data.l[2] = detail; + xevent.xclient.data.l[3] = data1; + xevent.xclient.data.l[4] = data2; + + trap_errors (); + XSendEvent (dpy, w, False, NoEventMask, &xevent); + XSync (dpy,False); + + if((errorcode = untrap_error())) { +#ifdef DEBUG_XTBIN + printf("send_xembed_message error(%d)!!!\n",errorcode); +#endif + } +} + +static int +error_handler(Display *display, XErrorEvent *error) +{ + trapped_error_code = error->error_code; + return 0; +} + +static void +trap_errors(void) +{ + trapped_error_code =0; + old_error_handler = XSetErrorHandler(error_handler); +} + +static int +untrap_error(void) +{ + XSetErrorHandler(old_error_handler); + if(trapped_error_code) { +#ifdef DEBUG_XTBIN + printf("Get X Window Error = %d\n", trapped_error_code); +#endif + } + return trapped_error_code; +} + +static void +xt_client_focus_listener( Widget w, XtPointer user_data, XEvent *event) +{ + Display *dpy = XtDisplay(w); + XtClient *xtclient = user_data; + Window win = XtWindow(w); + + switch(event->type) + { + case CreateNotify: + if(event->xcreatewindow.parent == win) { + Widget child=XtWindowToWidget( dpy, event->xcreatewindow.window); + if (child) + xt_add_focus_listener_tree(child, user_data); + } + break; + case DestroyNotify: + xt_remove_focus_listener( w, user_data); + break; + case ReparentNotify: + if(event->xreparent.parent == win) { + /* I am the new parent */ + Widget child=XtWindowToWidget(dpy, event->xreparent.window); + if (child) + xt_add_focus_listener_tree( child, user_data); + } + else if(event->xreparent.window == win) { + /* I am the new child */ + } + else { + /* I am the old parent */ + } + break; + case ButtonRelease: +#if 0 + XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time); +#endif + send_xembed_message ( xtclient, + XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); + break; + default: + break; + } /* End of switch(event->type) */ +} + +static void +xt_add_focus_listener( Widget w, XtPointer user_data) +{ + XWindowAttributes attr; + long eventmask; + XtClient *xtclient = user_data; + + trap_errors (); + XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attr); + eventmask = attr.your_event_mask | SubstructureNotifyMask | ButtonReleaseMask; + XSelectInput(XtDisplay(w), + XtWindow(w), + eventmask); + + XtAddEventHandler(w, + SubstructureNotifyMask | ButtonReleaseMask, + TRUE, + (XtEventHandler)xt_client_focus_listener, + xtclient); + untrap_error(); +} + +static void +xt_remove_focus_listener(Widget w, XtPointer user_data) +{ + trap_errors (); + XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE, + (XtEventHandler)xt_client_focus_listener, user_data); + + untrap_error(); +} + +static void +xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data) +{ + Window win = XtWindow(treeroot); + Window *children; + Window root, parent; + Display *dpy = XtDisplay(treeroot); + unsigned int i, nchildren; + + /* ensure we don't add more than once */ + xt_remove_focus_listener( treeroot, user_data); + xt_add_focus_listener( treeroot, user_data); + trap_errors(); + if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) { + untrap_error(); + return; + } + + if(untrap_error()) + return; + + for(i=0; i<nchildren; ++i) { + Widget child = XtWindowToWidget(dpy, children[i]); + if (child) + xt_add_focus_listener_tree( child, user_data); + } + XFree((void*)children); + + return; +} diff --git a/Source/WebCore/plugins/gtk/gtk2xtbin.h b/Source/WebCore/plugins/gtk/gtk2xtbin.h new file mode 100644 index 0000000..11f6e06 --- /dev/null +++ b/Source/WebCore/plugins/gtk/gtk2xtbin.h @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set expandtab shiftwidth=2 tabstop=2: */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Gtk2XtBin Widget Implementation. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __GTK_XTBIN_H__ +#define __GTK_XTBIN_H__ + +#include <gtk/gtk.h> +#include <X11/Intrinsic.h> +#include <X11/Xutil.h> +#include <X11/Xlib.h> +#ifdef MOZILLA_CLIENT +#include "nscore.h" +#ifdef _IMPL_GTKXTBIN_API +#define GTKXTBIN_API(type) NS_EXPORT_(type) +#else +#define GTKXTBIN_API(type) NS_IMPORT_(type) +#endif +#else +#define GTKXTBIN_API(type) type +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct _GtkXtBin GtkXtBin; +typedef struct _GtkXtBinClass GtkXtBinClass; + +#define GTK_TYPE_XTBIN (gtk_xtbin_get_type ()) +#define GTK_XTBIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GTK_TYPE_XTBIN, GtkXtBin)) +#define GTK_XTBIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GTK_TYPE_XTBIN, GtkXtBinClass)) +#define GTK_IS_XTBIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GTK_TYPE_XTBIN)) +#define GTK_IS_XTBIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GTK_TYPE_XTBIN)) +typedef struct _XtClient XtClient; + +struct _XtClient { + Display *xtdisplay; + Widget top_widget; /* The toplevel widget */ + Widget child_widget; /* The embedded widget */ + Visual *xtvisual; + int xtdepth; + Colormap xtcolormap; + Window oldwindow; +}; + +struct _GtkXtBin +{ + GtkSocket gsocket; + GdkWindow *parent_window; + Display *xtdisplay; /* Xt Toolkit Display */ + + Window xtwindow; /* Xt Toolkit XWindow */ + gint x, y; + gint width, height; + XtClient xtclient; /* Xt Client for XEmbed */ +}; + +struct _GtkXtBinClass +{ + GtkSocketClass widget_class; +}; + +GTKXTBIN_API(GType) gtk_xtbin_get_type (void); +GTKXTBIN_API(GtkWidget *) gtk_xtbin_new (GtkWidget *parent_widget, String *f); +GTKXTBIN_API(void) gtk_xtbin_set_position (GtkXtBin *xtbin, + gint x, + gint y); +GTKXTBIN_API(void) gtk_xtbin_resize (GtkWidget *widget, + gint width, + gint height); + +typedef struct _XtTMRec { + XtTranslations translations; /* private to Translation Manager */ + XtBoundActions proc_table; /* procedure bindings for actions */ + struct _XtStateRec *current_state; /* Translation Manager state ptr */ + unsigned long lastEventTime; +} XtTMRec, *XtTM; + +typedef struct _CorePart { + Widget self; /* pointer to widget itself */ + WidgetClass widget_class; /* pointer to Widget's ClassRec */ + Widget parent; /* parent widget */ + XrmName xrm_name; /* widget resource name quarkified */ + Boolean being_destroyed; /* marked for destroy */ + XtCallbackList destroy_callbacks; /* who to call when widget destroyed */ + XtPointer constraints; /* constraint record */ + Position x, y; /* window position */ + Dimension width, height; /* window dimensions */ + Dimension border_width; /* window border width */ + Boolean managed; /* is widget geometry managed? */ + Boolean sensitive; /* is widget sensitive to user events*/ + Boolean ancestor_sensitive; /* are all ancestors sensitive? */ + XtEventTable event_table; /* private to event dispatcher */ + XtTMRec tm; /* translation management */ + XtTranslations accelerators; /* accelerator translations */ + Pixel border_pixel; /* window border pixel */ + Pixmap border_pixmap; /* window border pixmap or NULL */ + WidgetList popup_list; /* list of popups */ + Cardinal num_popups; /* how many popups */ + String name; /* widget resource name */ + Screen *screen; /* window's screen */ + Colormap colormap; /* colormap */ + Window window; /* window ID */ + Cardinal depth; /* number of planes in window */ + Pixel background_pixel; /* window background pixel */ + Pixmap background_pixmap; /* window background pixmap or NULL */ + Boolean visible; /* is window mapped and not occluded?*/ + Boolean mapped_when_managed;/* map window if it's managed? */ +} CorePart; + +typedef struct _WidgetRec { + CorePart core; + } WidgetRec, CoreRec; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* __GTK_XTBIN_H__ */ diff --git a/Source/WebCore/plugins/gtk/xembed.h b/Source/WebCore/plugins/gtk/xembed.h new file mode 100644 index 0000000..dff7be9 --- /dev/null +++ b/Source/WebCore/plugins/gtk/xembed.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:expandtab:shiftwidth=2:tabstop=2: */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the XEMBED Declaration. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 +#define XEMBED_REQUEST_FOCUS 3 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 +#define XEMBED_FOCUS_NEXT 6 +#define XEMBED_FOCUS_PREV 7 +#define XEMBED_GRAB_KEY 8 +#define XEMBED_UNGRAB_KEY 9 +#define XEMBED_MODALITY_ON 10 +#define XEMBED_MODALITY_OFF 11 + +/* Non standard messages*/ +#define XEMBED_GTK_GRAB_KEY 108 +#define XEMBED_GTK_UNGRAB_KEY 109 + +/* Details for XEMBED_FOCUS_IN: */ +#define XEMBED_FOCUS_CURRENT 0 +#define XEMBED_FOCUS_FIRST 1 +#define XEMBED_FOCUS_LAST 2 + +/* Flags for _XEMBED_INFO */ +#define XEMBED_MAPPED (1 << 0) diff --git a/Source/WebCore/plugins/mac/PluginPackageMac.cpp b/Source/WebCore/plugins/mac/PluginPackageMac.cpp new file mode 100644 index 0000000..865ea32 --- /dev/null +++ b/Source/WebCore/plugins/mac/PluginPackageMac.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginPackage.h" + +#include <wtf/RetainPtr.h> +#include "MIMETypeRegistry.h" +#include "npruntime_impl.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include "WebCoreNSStringExtras.h" +#include <wtf/text/CString.h> + +#include <CoreFoundation/CoreFoundation.h> + +#define PluginNameOrDescriptionStringNumber 126 +#define MIMEDescriptionStringNumber 127 +#define MIMEListStringStringNumber 128 + +namespace WebCore { + +void PluginPackage::determineQuirks(const String& mimeType) +{ + if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { + // Because a single process cannot create multiple VMs, and we cannot reliably unload a + // Java VM, we cannot unload the Java Plugin, or we'll lose reference to our only VM + m_quirks.add(PluginQuirkDontUnloadPlugin); + + // Setting the window region to an empty region causes bad scrolling repaint problems + // with the Java plug-in. + m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling); + } + + if (mimeType == "application/x-shockwave-flash") { + // The flash plugin only requests windowless plugins if we return a mozilla user agent + m_quirks.add(PluginQuirkWantsMozillaUserAgent); + m_quirks.add(PluginQuirkThrottleInvalidate); + m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages); + m_quirks.add(PluginQuirkFlashURLNotifyBug); + } + +} + +typedef void (*BP_CreatePluginMIMETypesPreferencesFuncPtr)(void); + +static WTF::RetainPtr<CFDictionaryRef> readPListFile(CFStringRef fileName, bool createFile, CFBundleRef bundle) +{ + if (createFile) { + BP_CreatePluginMIMETypesPreferencesFuncPtr funcPtr = + (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("BP_CreatePluginMIMETypesPreferences")); + if (funcPtr) + funcPtr(); + } + + WTF::RetainPtr<CFDictionaryRef> map; + WTF::RetainPtr<CFURLRef> url = + CFURLCreateWithFileSystemPath(kCFAllocatorDefault, fileName, kCFURLPOSIXPathStyle, false); + + CFDataRef resource = 0; + SInt32 code; + if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url.get(), &resource, 0, 0, &code)) + return map; + + WTF::RetainPtr<CFPropertyListRef> propertyList = + CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, kCFPropertyListImmutable, 0); + + CFRelease(resource); + + if (!propertyList) + return map; + + if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID()) + return map; + + map = static_cast<CFDictionaryRef>(static_cast<CFPropertyListRef>(propertyList.get())); + return map; +} + +static Vector<String> stringListFromResourceId(SInt16 id) +{ + Vector<String> list; + + Handle handle = Get1Resource('STR#', id); + if (!handle) + return list; + + CFStringEncoding encoding = stringEncodingForResource(handle); + + unsigned char* p = (unsigned char*)*handle; + if (!p) + return list; + + SInt16 count = *(SInt16*)p; + p += sizeof(SInt16); + + for (SInt16 i = 0; i < count; ++i) { + unsigned char length = *p; + WTF::RetainPtr<CFStringRef> str = CFStringCreateWithPascalString(0, p, encoding); + list.append(str.get()); + p += 1 + length; + } + + return list; +} + +bool PluginPackage::fetchInfo() +{ + if (!load()) + return false; + + WTF::RetainPtr<CFDictionaryRef> mimeDict; + + WTF::RetainPtr<CFTypeRef> mimeTypesFileName = CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginMIMETypesFilename")); + if (mimeTypesFileName && CFGetTypeID(mimeTypesFileName.get()) == CFStringGetTypeID()) { + + WTF::RetainPtr<CFStringRef> fileName = (CFStringRef)mimeTypesFileName.get(); + WTF::RetainPtr<CFStringRef> homeDir = homeDirectoryPath().createCFString(); + WTF::RetainPtr<CFStringRef> path = CFStringCreateWithFormat(0, 0, CFSTR("%@/Library/Preferences/%@"), homeDir.get(), fileName.get()); + + WTF::RetainPtr<CFDictionaryRef> plist = readPListFile(path.get(), /*createFile*/ false, m_module); + if (plist) { + // If the plist isn't localized, have the plug-in recreate it in the preferred language. + WTF::RetainPtr<CFStringRef> localizationName = + (CFStringRef)CFDictionaryGetValue(plist.get(), CFSTR("WebPluginLocalizationName")); + CFLocaleRef locale = CFLocaleCopyCurrent(); + if (localizationName != CFLocaleGetIdentifier(locale)) + plist = readPListFile(path.get(), /*createFile*/ true, m_module); + + CFRelease(locale); + } else { + // Plist doesn't exist, ask the plug-in to create it. + plist = readPListFile(path.get(), /*createFile*/ true, m_module); + } + + if (plist) + mimeDict = (CFDictionaryRef)CFDictionaryGetValue(plist.get(), CFSTR("WebPluginMIMETypes")); + } + + if (!mimeDict) + mimeDict = (CFDictionaryRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginMIMETypes")); + + if (mimeDict) { + CFIndex propCount = CFDictionaryGetCount(mimeDict.get()); + Vector<const void*, 128> keys(propCount); + Vector<const void*, 128> values(propCount); + CFDictionaryGetKeysAndValues(mimeDict.get(), keys.data(), values.data()); + for (int i = 0; i < propCount; ++i) { + String mimeType = (CFStringRef)keys[i]; + mimeType = mimeType.lower(); + + WTF::RetainPtr<CFDictionaryRef> extensionsDict = (CFDictionaryRef)values[i]; + + WTF::RetainPtr<CFNumberRef> enabled = (CFNumberRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginTypeEnabled")); + if (enabled) { + int enabledValue = 0; + if (CFNumberGetValue(enabled.get(), kCFNumberIntType, &enabledValue) && enabledValue == 0) + continue; + } + + Vector<String> mimeExtensions; + WTF::RetainPtr<CFArrayRef> extensions = (CFArrayRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginExtensions")); + if (extensions) { + CFIndex extensionCount = CFArrayGetCount(extensions.get()); + for (CFIndex i = 0; i < extensionCount; ++i) { + String extension =(CFStringRef)CFArrayGetValueAtIndex(extensions.get(), i); + extension = extension.lower(); + mimeExtensions.append(extension); + } + } + m_mimeToExtensions.set(mimeType, mimeExtensions); + + String description = (CFStringRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginTypeDescription")); + m_mimeToDescriptions.set(mimeType, description); + } + + m_name = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginName")); + m_description = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginDescription")); + + } else { + int resFile = CFBundleOpenBundleResourceMap(m_module); + + UseResFile(resFile); + + Vector<String> mimes = stringListFromResourceId(MIMEListStringStringNumber); + + if (mimes.size() % 2 != 0) + return false; + + Vector<String> descriptions = stringListFromResourceId(MIMEDescriptionStringNumber); + if (descriptions.size() != mimes.size() / 2) + return false; + + for (size_t i = 0; i < mimes.size(); i += 2) { + String mime = mimes[i].lower(); + Vector<String> extensions; + mimes[i + 1].lower().split(UChar(','), extensions); + + m_mimeToExtensions.set(mime, extensions); + + m_mimeToDescriptions.set(mime, descriptions[i / 2]); + } + + Vector<String> names = stringListFromResourceId(PluginNameOrDescriptionStringNumber); + if (names.size() == 2) { + m_description = names[0]; + m_name = names[1]; + } + + CFBundleCloseBundleResourceMap(m_module, resFile); + } + + LOG(Plugins, "PluginPackage::fetchInfo(): Found plug-in '%s'", m_name.utf8().data()); + if (isPluginBlacklisted()) { + LOG(Plugins, "\tPlug-in is blacklisted!"); + return false; + } + + return true; +} + +bool PluginPackage::isPluginBlacklisted() +{ + return false; +} + +bool PluginPackage::load() +{ + if (m_isLoaded) { + m_loadCount++; + return true; + } + + WTF::RetainPtr<CFStringRef> path(AdoptCF, m_path.createCFString()); + WTF::RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), + kCFURLPOSIXPathStyle, false)); + m_module = CFBundleCreate(NULL, url.get()); + if (!m_module || !CFBundleLoadExecutable(m_module)) { + LOG(Plugins, "%s not loaded", m_path.utf8().data()); + return false; + } + + m_isLoaded = true; + + NP_GetEntryPointsFuncPtr NP_GetEntryPoints = 0; + NP_InitializeFuncPtr NP_Initialize; + NPError npErr; + + NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_Initialize")); + NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_GetEntryPoints")); + m_NPP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_Shutdown")); + + if (!NP_Initialize || !NP_GetEntryPoints || !m_NPP_Shutdown) + goto abort; + + memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); + m_pluginFuncs.size = sizeof(m_pluginFuncs); + + initializeBrowserFuncs(); + + npErr = NP_Initialize(&m_browserFuncs); + LOG_NPERROR(npErr); + if (npErr != NPERR_NO_ERROR) + goto abort; + + npErr = NP_GetEntryPoints(&m_pluginFuncs); + LOG_NPERROR(npErr); + if (npErr != NPERR_NO_ERROR) + goto abort; + + m_loadCount++; + return true; + +abort: + unloadWithoutShutdown(); + return false; +} + +uint16_t PluginPackage::NPVersion() const +{ + return NP_VERSION_MINOR; +} +} // namespace WebCore diff --git a/Source/WebCore/plugins/mac/PluginViewMac.mm b/Source/WebCore/plugins/mac/PluginViewMac.mm new file mode 100644 index 0000000..7119f0d --- /dev/null +++ b/Source/WebCore/plugins/mac/PluginViewMac.mm @@ -0,0 +1,821 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginView.h" + +#include "Bridge.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "EventNames.h" +#include "FocusController.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameTree.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HostWindow.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "Image.h" +#include "JSDOMBinding.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "NotImplemented.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PlatformKeyboardEvent.h" +#include "PluginDebug.h" +#include "PluginPackage.h" +#include "PluginMainThreadScheduler.h" +#include "RenderLayer.h" +#include "ScriptController.h" +#include "Settings.h" +#include "npruntime_impl.h" +#include "runtime_root.h" +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> +#include <wtf/RetainPtr.h> + + +using JSC::ExecState; +using JSC::Interpreter; +using JSC::JSLock; +using JSC::JSObject; +using JSC::JSValue; +using JSC::UString; + +#if PLATFORM(QT) +#include <QWidget> +#include <QKeyEvent> +#include <QPainter> +#include "QWebPageClient.h" +QT_BEGIN_NAMESPACE +extern Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget* w); +QT_END_NAMESPACE +#endif + +#if PLATFORM(WX) +#include <wx/defs.h> +#include <wx/wx.h> +#endif + +using std::min; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +#ifndef NP_NO_CARBON +static int modifiersForEvent(UIEventWithKeyState *event); +#endif + +static inline WindowRef nativeWindowFor(PlatformWidget widget) +{ +#if PLATFORM(QT) + if (widget) +#if QT_MAC_USE_COCOA + return static_cast<WindowRef>([qt_mac_window_for(widget) windowRef]); +#else + return static_cast<WindowRef>(qt_mac_window_for(widget)); +#endif +#elif PLATFORM(WX) + if (widget) + return (WindowRef)widget->MacGetTopLevelWindowRef(); +#endif + return 0; +} + +static inline CGContextRef cgHandleFor(PlatformWidget widget) +{ +#if PLATFORM(QT) + if (widget) + return (CGContextRef)widget->macCGHandle(); +#endif +#if PLATFORM(WX) + if (widget) + return (CGContextRef)widget->MacGetCGContextRef(); +#endif + return 0; +} + +static inline IntPoint topLevelOffsetFor(PlatformWidget widget) +{ +#if PLATFORM(QT) + if (widget) { + PlatformWidget topLevel = widget->window(); + return widget->mapTo(topLevel, QPoint(0, 0)) + topLevel->geometry().topLeft() - topLevel->pos(); + } +#endif +#if PLATFORM(WX) + if (widget) { + PlatformWidget toplevel = wxGetTopLevelParent(widget); + return toplevel->ScreenToClient(widget->GetScreenPosition()); + } +#endif + return IntPoint(); +} + +// --------------- Lifetime management ----------------- + +bool PluginView::platformStart() +{ + ASSERT(m_isStarted); + ASSERT(m_status == PluginStatusLoadedSuccessfully); + + if (m_drawingModel == NPDrawingModel(-1)) { + // We default to QuickDraw, even though we don't support it, + // since that's what Safari does, and some plugins expect this + // behavior and never set the drawing model explicitly. +#ifndef NP_NO_QUICKDRAW + m_drawingModel = NPDrawingModelQuickDraw; +#else + // QuickDraw not available, so we have to default to CoreGraphics + m_drawingModel = NPDrawingModelCoreGraphics; +#endif + } + + if (m_eventModel == NPEventModel(-1)) { + // If the plug-in did not specify an event model + // we default to Carbon, when it is available. +#ifndef NP_NO_CARBON + m_eventModel = NPEventModelCarbon; +#else + m_eventModel = NPEventModelCocoa; +#endif + } + + // Gracefully handle unsupported drawing or event models. We can do this + // now since the drawing and event model can only be set during NPP_New. +#ifndef NP_NO_CARBON + NPBool eventModelSupported; + if (getValueStatic(NPNVariable(NPNVsupportsCarbonBool + m_eventModel), &eventModelSupported) != NPERR_NO_ERROR + || !eventModelSupported) { +#endif + m_status = PluginStatusCanNotLoadPlugin; + LOG(Plugins, "Plug-in '%s' uses unsupported event model %s", + m_plugin->name().utf8().data(), prettyNameForEventModel(m_eventModel)); + return false; +#ifndef NP_NO_CARBON + } +#endif + +#ifndef NP_NO_QUICKDRAW + NPBool drawingModelSupported; + if (getValueStatic(NPNVariable(NPNVsupportsQuickDrawBool + m_drawingModel), &drawingModelSupported) != NPERR_NO_ERROR + || !drawingModelSupported) { +#endif + m_status = PluginStatusCanNotLoadPlugin; + LOG(Plugins, "Plug-in '%s' uses unsupported drawing model %s", + m_plugin->name().utf8().data(), prettyNameForDrawingModel(m_drawingModel)); + return false; +#ifndef NP_NO_QUICKDRAW + } +#endif + +#if PLATFORM(QT) + // Set the platformPluginWidget only in the case of QWebView so that the context menu appears in the right place. + // In all other cases, we use off-screen rendering + if (QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient()) { + if (QWidget* widget = qobject_cast<QWidget*>(client->pluginParent())) + setPlatformPluginWidget(widget); + } +#endif +#if PLATFORM(WX) + if (wxWindow* widget = m_parentFrame->view()->hostWindow()->platformPageClient()) + setPlatformPluginWidget(widget); +#endif + + // Create a fake window relative to which all events will be sent when using offscreen rendering + if (!platformPluginWidget()) { +#ifndef NP_NO_CARBON + // Make the default size really big. It is unclear why this is required but with a smaller size, mouse move + // events don't get processed. Resizing the fake window to flash's size doesn't help. + ::Rect windowBounds = { 0, 0, 1000, 1000 }; + CreateNewWindow(kDocumentWindowClass, kWindowStandardDocumentAttributes, &windowBounds, &m_fakeWindow); + // Flash requires the window to be hilited to process mouse move events. + HiliteWindow(m_fakeWindow, true); +#endif + } + + updatePluginWidget(); + + if (!m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall)) + setNPWindowIfNeeded(); + + // TODO: Implement null timer throttling depending on plugin activation + m_nullEventTimer.set(new Timer<PluginView>(this, &PluginView::nullEventTimerFired)); + m_nullEventTimer->startRepeating(0.02); + + m_lastMousePos.h = m_lastMousePos.v = 0; + + return true; +} + +void PluginView::platformDestroy() +{ + if (platformPluginWidget()) + setPlatformPluginWidget(0); + else { + CGContextRelease(m_contextRef); +#ifndef NP_NO_CARBON + if (m_fakeWindow) + DisposeWindow(m_fakeWindow); +#endif + } +} + +// Used before the plugin view has been initialized properly, and as a +// fallback for variables that do not require a view to resolve. +bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVToolkit: + *static_cast<uint32_t*>(value) = 0; + *result = NPERR_NO_ERROR; + return true; + + case NPNVjavascriptEnabledBool: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + +#ifndef NP_NO_CARBON + case NPNVsupportsCarbonBool: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + +#endif + case NPNVsupportsCocoaBool: + *static_cast<NPBool*>(value) = false; + *result = NPERR_NO_ERROR; + return true; + + // CoreGraphics is the only drawing model we support + case NPNVsupportsCoreGraphicsBool: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + +#ifndef NP_NO_QUICKDRAW + // QuickDraw is deprecated in 10.5 and not supported on 64-bit + case NPNVsupportsQuickDrawBool: +#endif + case NPNVsupportsOpenGLBool: + case NPNVsupportsCoreAnimationBool: + *static_cast<NPBool*>(value) = false; + *result = NPERR_NO_ERROR; + return true; + + default: + return false; + } +} + +// Used only for variables that need a view to resolve +bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* error) +{ + return false; +} + +void PluginView::setParent(ScrollView* parent) +{ + LOG(Plugins, "PluginView::setParent(%p)", parent); + + Widget::setParent(parent); + + if (parent) + init(); +} + +// -------------- Geometry and painting ---------------- + +void PluginView::show() +{ + LOG(Plugins, "PluginView::show()"); + + setSelfVisible(true); + + Widget::show(); +} + +void PluginView::hide() +{ + LOG(Plugins, "PluginView::hide()"); + + setSelfVisible(false); + + Widget::hide(); +} + +void PluginView::setFocus(bool focused) +{ + LOG(Plugins, "PluginView::setFocus(%d)", focused); + if (!focused) { + Widget::setFocus(focused); + return; + } + + if (platformPluginWidget()) +#if PLATFORM(QT) + platformPluginWidget()->setFocus(Qt::OtherFocusReason); +#else + platformPluginWidget()->SetFocus(); +#endif + else + Widget::setFocus(focused); + + // TODO: Also handle and pass on blur events (focus lost) + +#ifndef NP_NO_CARBON + EventRecord record; + record.what = NPEventType_GetFocusEvent; + record.message = 0; + record.when = TickCount(); + record.where = globalMousePosForPlugin(); + record.modifiers = GetCurrentKeyModifiers(); + + if (!dispatchNPEvent(record)) + LOG(Events, "PluginView::setFocus(%d): Focus event not accepted", focused); +#endif +} + +void PluginView::setParentVisible(bool visible) +{ + if (isParentVisible() == visible) + return; + + Widget::setParentVisible(visible); +} + +void PluginView::setNPWindowRect(const IntRect&) +{ + setNPWindowIfNeeded(); +} + +void PluginView::setNPWindowIfNeeded() +{ + if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) + return; + + CGContextRef newContextRef = 0; + WindowRef newWindowRef = 0; + if (platformPluginWidget()) { + newContextRef = cgHandleFor(platformPluginWidget()); + newWindowRef = nativeWindowFor(platformPluginWidget()); + m_npWindow.type = NPWindowTypeWindow; + } else { + newContextRef = m_contextRef; + newWindowRef = m_fakeWindow; + m_npWindow.type = NPWindowTypeDrawable; + } + + if (!newContextRef || !newWindowRef) + return; + + m_npWindow.window = (void*)&m_npCgContext; +#ifndef NP_NO_CARBON + m_npCgContext.window = newWindowRef; +#endif + m_npCgContext.context = newContextRef; + + m_npWindow.x = m_windowRect.x(); + m_npWindow.y = m_windowRect.y(); + m_npWindow.width = m_windowRect.width(); + m_npWindow.height = m_windowRect.height(); + + // TODO: (also clip against scrollbars, etc.) + m_npWindow.clipRect.left = max(0, m_windowRect.x()); + m_npWindow.clipRect.top = max(0, m_windowRect.y()); + m_npWindow.clipRect.right = m_windowRect.x() + m_windowRect.width(); + m_npWindow.clipRect.bottom = m_windowRect.y() + m_windowRect.height(); + + LOG(Plugins, "PluginView::setNPWindowIfNeeded(): window=%p, context=%p," + " window.x:%d window.y:%d window.width:%d window.height:%d window.clipRect size:%dx%d", + newWindowRef, newContextRef, m_npWindow.x, m_npWindow.y, m_npWindow.width, m_npWindow.height, + m_npWindow.clipRect.right - m_npWindow.clipRect.left, m_npWindow.clipRect.bottom - m_npWindow.clipRect.top); + + PluginView::setCurrentPluginView(this); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); +} + +void PluginView::updatePluginWidget() +{ + if (!parent()) + return; + + ASSERT(parent()->isFrameView()); + FrameView* frameView = static_cast<FrameView*>(parent()); + + IntRect oldWindowRect = m_windowRect; + IntRect oldClipRect = m_clipRect; + + m_windowRect = frameView->contentsToWindow(frameRect()); + IntPoint offset = topLevelOffsetFor(platformPluginWidget()); + m_windowRect.move(offset.x(), offset.y()); + + if (!platformPluginWidget()) { + if (m_windowRect.size() != oldWindowRect.size()) { + CGContextRelease(m_contextRef); +#if PLATFORM(QT) + m_pixmap = QPixmap(m_windowRect.size()); + m_pixmap.fill(Qt::transparent); + m_contextRef = m_pixmap.isNull() ? 0 : qt_mac_cg_context(&m_pixmap); +#endif + } + } + + m_clipRect = windowClipRect(); + m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); + + if (platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect)) + setNPWindowIfNeeded(); +} + +void PluginView::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_isStarted || m_status != PluginStatusLoadedSuccessfully) { + paintMissingPluginIcon(context, rect); + return; + } + + if (context->paintingDisabled()) + return; + + setNPWindowIfNeeded(); + + CGContextRef cgContext = m_npCgContext.context; + if (!cgContext) + return; + + CGContextSaveGState(cgContext); + if (platformPluginWidget()) { + IntPoint offset = frameRect().location(); + CGContextTranslateCTM(cgContext, offset.x(), offset.y()); + } + + IntRect targetRect(frameRect()); + targetRect.intersects(rect); + + // clip the context so that plugin only updates the interested area. + CGRect r; + r.origin.x = targetRect.x() - frameRect().x(); + r.origin.y = targetRect.y() - frameRect().y(); + r.size.width = targetRect.width(); + r.size.height = targetRect.height(); + CGContextClipToRect(cgContext, r); + + if (!platformPluginWidget() && m_isTransparent) { // clean the pixmap in transparent mode +#if PLATFORM(QT) + QPainter painter(&m_pixmap); + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(QRectF(r.origin.x, r.origin.y, r.size.width, r.size.height), Qt::transparent); +#endif + } + +#ifndef NP_NO_CARBON + EventRecord event; + event.what = updateEvt; + event.message = (long unsigned int)m_npCgContext.window; + event.when = TickCount(); + event.where.h = 0; + event.where.v = 0; + event.modifiers = GetCurrentKeyModifiers(); + + if (!dispatchNPEvent(event)) + LOG(Events, "PluginView::paint(): Paint event not accepted"); +#endif + + CGContextRestoreGState(cgContext); + + if (!platformPluginWidget()) { +#if PLATFORM(QT) + QPainter* painter = context->platformContext(); + painter->drawPixmap(targetRect.x(), targetRect.y(), m_pixmap, + targetRect.x() - frameRect().x(), targetRect.y() - frameRect().y(), targetRect.width(), targetRect.height()); +#endif + } +} + +void PluginView::invalidateRect(const IntRect& rect) +{ + if (platformPluginWidget()) +#if PLATFORM(QT) + platformPluginWidget()->update(convertToContainingWindow(rect)); +#else + platformPluginWidget()->RefreshRect(convertToContainingWindow(rect)); +#endif + else + invalidateWindowlessPluginRect(rect); +} + +void PluginView::invalidateRect(NPRect* rect) +{ + IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); + invalidateRect(r); +} + +void PluginView::invalidateRegion(NPRegion region) +{ + // TODO: optimize + invalidate(); +} + +void PluginView::forceRedraw() +{ + notImplemented(); +} + + +// ----------------- Event handling -------------------- + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + if (!m_isStarted) + return; + +#ifndef NP_NO_CARBON + EventRecord record; + + if (event->type() == eventNames().mousemoveEvent) { + // Mouse movement is handled by null timer events + m_lastMousePos = mousePosForPlugin(event); + return; + } else if (event->type() == eventNames().mouseoverEvent) { + record.what = NPEventType_AdjustCursorEvent; + } else if (event->type() == eventNames().mouseoutEvent) { + record.what = NPEventType_AdjustCursorEvent; + } else if (event->type() == eventNames().mousedownEvent) { + record.what = mouseDown; + // The plugin needs focus to receive keyboard events + if (Page* page = m_parentFrame->page()) + page->focusController()->setFocusedFrame(m_parentFrame); + m_parentFrame->document()->setFocusedNode(m_element); + } else if (event->type() == eventNames().mouseupEvent) { + record.what = mouseUp; + } else { + return; + } + record.where = mousePosForPlugin(event); + record.modifiers = modifiersForEvent(event); + + if (!event->buttonDown()) + record.modifiers |= btnState; + + if (event->button() == 2) + record.modifiers |= controlKey; + + if (!dispatchNPEvent(record)) { + if (record.what == NPEventType_AdjustCursorEvent) + return; // Signals that the plugin wants a normal cursor + + LOG(Events, "PluginView::handleMouseEvent(): Mouse event type %d at %d,%d not accepted", + record.what, record.where.h, record.where.v); + } else { + event->setDefaultHandled(); + } +#endif +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + if (!m_isStarted) + return; + + LOG(Plugins, "PluginView::handleKeyboardEvent() ----------------- "); + + LOG(Plugins, "PV::hKE(): KE.keyCode: 0x%02X, KE.charCode: %d", + event->keyCode(), event->charCode()); + +#ifndef NP_NO_CARBON + EventRecord record; + + if (event->type() == eventNames().keydownEvent) { + // This event is the result of a PlatformKeyboardEvent::KeyDown which + // was disambiguated into a PlatformKeyboardEvent::RawKeyDown. Since + // we don't have access to the text here, we return, and wait for the + // corresponding event based on PlatformKeyboardEvent::Char. + return; + } else if (event->type() == eventNames().keypressEvent) { + // Which would be this one. This event was disambiguated from the same + // PlatformKeyboardEvent::KeyDown, but to a PlatformKeyboardEvent::Char, + // which retains the text from the original event. So, we can safely pass + // on the event as a key-down event to the plugin. + record.what = keyDown; + } else if (event->type() == eventNames().keyupEvent) { + // PlatformKeyboardEvent::KeyUp events always have the text, so nothing + // fancy here. + record.what = keyUp; + } else { + return; + } + + const PlatformKeyboardEvent* platformEvent = event->keyEvent(); + int keyCode = platformEvent->nativeVirtualKeyCode(); + + const String text = platformEvent->text(); + if (text.length() < 1) { + event->setDefaultHandled(); + return; + } + + WTF::RetainPtr<CFStringRef> cfText(WTF::AdoptCF, text.createCFString()); + + LOG(Plugins, "PV::hKE(): PKE.text: %s, PKE.unmodifiedText: %s, PKE.keyIdentifier: %s", + text.ascii().data(), platformEvent->unmodifiedText().ascii().data(), + platformEvent->keyIdentifier().ascii().data()); + + char charCodes[2] = { 0, 0 }; + if (!CFStringGetCString(cfText.get(), charCodes, 2, CFStringGetSystemEncoding())) { + LOG_ERROR("Could not resolve character code using system encoding."); + event->setDefaultHandled(); + return; + } + + record.where = globalMousePosForPlugin(); + record.modifiers = modifiersForEvent(event); + record.message = ((keyCode & 0xFF) << 8) | (charCodes[0] & 0xFF); + record.when = TickCount(); + + LOG(Plugins, "PV::hKE(): record.modifiers: %d", record.modifiers); + +#if PLATFORM(QT) + LOG(Plugins, "PV::hKE(): PKE.qtEvent()->nativeVirtualKey: 0x%02X, charCode: %d", + keyCode, int(uchar(charCodes[0]))); +#endif + + if (!dispatchNPEvent(record)) + LOG(Events, "PluginView::handleKeyboardEvent(): Keyboard event type %d not accepted", record.what); + else + event->setDefaultHandled(); +#endif +} + +#ifndef NP_NO_CARBON +void PluginView::nullEventTimerFired(Timer<PluginView>*) +{ + EventRecord record; + + record.what = nullEvent; + record.message = 0; + record.when = TickCount(); + record.where = m_lastMousePos; + record.modifiers = GetCurrentKeyModifiers(); + if (!Button()) + record.modifiers |= btnState; + + if (!dispatchNPEvent(record)) + LOG(Events, "PluginView::nullEventTimerFired(): Null event not accepted"); +} +#endif + +#ifndef NP_NO_CARBON +static int modifiersForEvent(UIEventWithKeyState* event) +{ + int modifiers = 0; + + if (event->ctrlKey()) + modifiers |= controlKey; + + if (event->altKey()) + modifiers |= optionKey; + + if (event->metaKey()) + modifiers |= cmdKey; + + if (event->shiftKey()) + modifiers |= shiftKey; + + return modifiers; +} +#endif + +#ifndef NP_NO_CARBON +Point PluginView::globalMousePosForPlugin() const +{ + Point pos; + GetGlobalMouse(&pos); + +#if PLATFORM(WX) + // make sure the titlebar/toolbar size is included + WindowRef windowRef = nativeWindowFor(platformPluginWidget()); + ::Rect content, structure; + + GetWindowBounds(windowRef, kWindowStructureRgn, &structure); + GetWindowBounds(windowRef, kWindowContentRgn, &content); + + int top = content.top - structure.top; + pos.v -= top; +#endif + + return pos; +} +#endif + +#ifndef NP_NO_CARBON +Point PluginView::mousePosForPlugin(MouseEvent* event) const +{ + ASSERT(event); + if (platformPluginWidget()) + return globalMousePosForPlugin(); + + if (event->button() == 2) { + // always pass the global position for right-click since Flash uses it to position the context menu + return globalMousePosForPlugin(); + } + + Point pos; + IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); + pos.h = postZoomPos.x() + m_windowRect.x(); + // The number 22 is the height of the title bar. As to why it figures in the calculation below + // is left as an exercise to the reader :-) + pos.v = postZoomPos.y() + m_windowRect.y() - 22; + return pos; +} +#endif + +#ifndef NP_NO_CARBON +bool PluginView::dispatchNPEvent(NPEvent& event) +{ + PluginView::setCurrentPluginView(this); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + + bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event); + + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + return accepted; +} +#endif + +// ------------------- Miscellaneous ------------------ + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) +{ + String filename(buf, len); + + if (filename.startsWith("file:///")) + filename = filename.substring(8); + + if (!fileExists(filename)) + return NPERR_FILE_NOT_FOUND; + + FILE* fileHandle = fopen((filename.utf8()).data(), "r"); + + if (fileHandle == 0) + return NPERR_FILE_NOT_FOUND; + + int bytesRead = fread(buffer.data(), 1, 0, fileHandle); + + fclose(fileHandle); + + if (bytesRead <= 0) + return NPERR_FILE_NOT_FOUND; + + return NPERR_NO_ERROR; +} + +void PluginView::halt() +{ +} + +void PluginView::restart() +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/npapi.cpp b/Source/WebCore/plugins/npapi.cpp new file mode 100644 index 0000000..bc64901 --- /dev/null +++ b/Source/WebCore/plugins/npapi.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "Page.h" +#include "PluginMainThreadScheduler.h" +#include "PluginView.h" +#include "npruntime_internal.h" + +using namespace WebCore; + +// The plugin view is always the ndata of the instance,. Sometimes, plug-ins will call an instance-specific function +// with a NULL instance. To workaround this, call the last plug-in view that made a call to a plug-in. +// Currently, the current plug-in view is only set before NPP_New in PluginView::start. +// This specifically works around Flash and Shockwave. When we call NPP_New, they call NPN_Useragent with a NULL instance. +static PluginView* pluginViewForInstance(NPP instance) +{ + if (instance && instance->ndata) + return static_cast<PluginView*>(instance->ndata); + return PluginView::currentPluginView(); +} + +void* NPN_MemAlloc(uint32_t size) +{ + return malloc(size); +} + +void NPN_MemFree(void* ptr) +{ + free(ptr); +} + +uint32_t NPN_MemFlush(uint32_t size) +{ + // Do nothing + return 0; +} + +void NPN_ReloadPlugins(NPBool reloadPages) +{ + Page::refreshPlugins(reloadPages); +} + +NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) +{ + return NPERR_STREAM_NOT_SEEKABLE; +} + +NPError NPN_GetURLNotify(NPP instance, const char* url, const char* target, void* notifyData) +{ + return pluginViewForInstance(instance)->getURLNotify(url, target, notifyData); +} + +NPError NPN_GetURL(NPP instance, const char* url, const char* target) +{ + return pluginViewForInstance(instance)->getURL(url, target); +} + +NPError NPN_PostURLNotify(NPP instance, const char* url, const char* target, uint32_t len, const char* buf, NPBool file, void* notifyData) +{ + return pluginViewForInstance(instance)->postURLNotify(url, target, len, buf, file, notifyData); +} + +NPError NPN_PostURL(NPP instance, const char* url, const char* target, uint32_t len, const char* buf, NPBool file) +{ + return pluginViewForInstance(instance)->postURL(url, target, len, buf, file); +} + +NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream) +{ + return pluginViewForInstance(instance)->newStream(type, target, stream); +} + +int32_t NPN_Write(NPP instance, NPStream* stream, int32_t len, void* buffer) +{ + return pluginViewForInstance(instance)->write(stream, len, buffer); +} + +NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPReason reason) +{ + return pluginViewForInstance(instance)->destroyStream(stream, reason); +} + +const char* NPN_UserAgent(NPP instance) +{ + PluginView* view = pluginViewForInstance(instance); + + if (!view) + return PluginView::userAgentStatic(); + + return view->userAgent(); +} + +void NPN_Status(NPP instance, const char* message) +{ + pluginViewForInstance(instance)->status(message); +} + +void NPN_InvalidateRect(NPP instance, NPRect* invalidRect) +{ + pluginViewForInstance(instance)->invalidateRect(invalidRect); +} + +void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion) +{ + pluginViewForInstance(instance)->invalidateRegion(invalidRegion); +} + +void NPN_ForceRedraw(NPP instance) +{ + pluginViewForInstance(instance)->forceRedraw(); +} + +NPError NPN_GetValue(NPP instance, NPNVariable variable, void* value) +{ + PluginView* view = pluginViewForInstance(instance); + + if (!view) + return PluginView::getValueStatic(variable, value); + + return pluginViewForInstance(instance)->getValue(variable, value); +} + +NPError NPN_SetValue(NPP instance, NPPVariable variable, void* value) +{ + return pluginViewForInstance(instance)->setValue(variable, value); +} + +void* NPN_GetJavaEnv() +{ + // Unsupported + return 0; +} + +void* NPN_GetJavaPeer(NPP instance) +{ + // Unsupported + return 0; +} + +void NPN_PushPopupsEnabledState(NPP instance, NPBool enabled) +{ + pluginViewForInstance(instance)->pushPopupsEnabledState(enabled); +} + +void NPN_PopPopupsEnabledState(NPP instance) +{ + pluginViewForInstance(instance)->popPopupsEnabledState(); +} + +void NPN_PluginThreadAsyncCall(NPP instance, void (*func) (void *), void *userData) +{ + PluginMainThreadScheduler::scheduler().scheduleCall(instance, func, userData); +} + +NPError NPN_GetValueForURL(NPP instance, NPNURLVariable variable, const char* url, char** value, uint32_t* len) +{ + return pluginViewForInstance(instance)->getValueForURL(variable, url, value, len); +} + +NPError NPN_SetValueForURL(NPP instance, NPNURLVariable variable, const char* url, const char* value, uint32_t len) +{ + return pluginViewForInstance(instance)->setValueForURL(variable, url, value, len); +} + +NPError NPN_GetAuthenticationInfo(NPP instance, const char* protocol, const char* host, int32_t port, const char* scheme, const char* realm, char** username, uint32_t* ulen, char** password, uint32_t* plen) +{ + return pluginViewForInstance(instance)->getAuthenticationInfo(protocol, host, port, scheme, realm, username, ulen, password, plen); +} + +#ifdef PLUGIN_SCHEDULE_TIMER +uint32_t NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, + void (*timerFunc)(NPP npp, uint32_t timerID)) +{ + return pluginViewForInstance(instance)->scheduleTimer(instance, interval, + repeat != 0, timerFunc); +} + +void NPN_UnscheduleTimer(NPP instance, uint32_t timerID) +{ + pluginViewForInstance(instance)->unscheduleTimer(instance, timerID); +} +#endif diff --git a/Source/WebCore/plugins/npfunctions.h b/Source/WebCore/plugins/npfunctions.h new file mode 100644 index 0000000..675ccb9 --- /dev/null +++ b/Source/WebCore/plugins/npfunctions.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef NPFUNCTIONS_H +#define NPFUNCTIONS_H + + +#include "npruntime.h" +#include "npapi.h" +#if defined(ANDROID_PLUGINS) +#include "nativehelper/jni.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(XP_WIN) +#define EXPORTED_CALLBACK(_type, _name) _type (__stdcall * _name) +#else +#define EXPORTED_CALLBACK(_type, _name) _type (* _name) +#endif + +typedef NPError (*NPN_GetURLNotifyProcPtr)(NPP instance, const char* URL, const char* window, void* notifyData); +typedef NPError (*NPN_PostURLNotifyProcPtr)(NPP instance, const char* URL, const char* window, uint32_t len, const char* buf, NPBool file, void* notifyData); +typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream, NPByteRange* rangeList); +typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, NPMIMEType type, const char* window, NPStream** stream); +typedef int32_t (*NPN_WriteProcPtr)(NPP instance, NPStream* stream, int32_t len, void* buffer); +typedef NPError (*NPN_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason); +typedef void (*NPN_StatusProcPtr)(NPP instance, const char* message); +typedef const char*(*NPN_UserAgentProcPtr)(NPP instance); +typedef void* (*NPN_MemAllocProcPtr)(uint32_t size); +typedef void (*NPN_MemFreeProcPtr)(void* ptr); +typedef uint32_t (*NPN_MemFlushProcPtr)(uint32_t size); +typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages); +typedef NPError (*NPN_GetValueProcPtr)(NPP instance, NPNVariable variable, void *ret_value); +typedef NPError (*NPN_SetValueProcPtr)(NPP instance, NPPVariable variable, void *value); +typedef void (*NPN_InvalidateRectProcPtr)(NPP instance, NPRect *rect); +typedef void (*NPN_InvalidateRegionProcPtr)(NPP instance, NPRegion region); +typedef void (*NPN_ForceRedrawProcPtr)(NPP instance); +typedef NPError (*NPN_GetURLProcPtr)(NPP instance, const char* URL, const char* window); +typedef NPError (*NPN_PostURLProcPtr)(NPP instance, const char* URL, const char* window, uint32_t len, const char* buf, NPBool file); +typedef void* (*NPN_GetJavaEnvProcPtr)(void); +typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance); +typedef void (*NPN_PushPopupsEnabledStateProcPtr)(NPP instance, NPBool enabled); +typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP instance); +typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP npp, void (*func)(void *), void *userData); +typedef NPError (*NPN_GetValueForURLProcPtr)(NPP npp, NPNURLVariable variable, const char* url, char** value, uint32_t* len); +typedef NPError (*NPN_SetValueForURLProcPtr)(NPP npp, NPNURLVariable variable, const char* url, const char* value, uint32_t len); +typedef NPError (*NPN_GetAuthenticationInfoProcPtr)(NPP npp, const char* protocol, const char* host, int32_t port, const char* scheme, const char *realm, char** username, uint32_t* ulen, char** password, uint32_t* plen); + +typedef uint32_t (*NPN_ScheduleTimerProcPtr)(NPP npp, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)); +typedef void (*NPN_UnscheduleTimerProcPtr)(NPP npp, uint32_t timerID); +typedef NPError (*NPN_PopUpContextMenuProcPtr)(NPP instance, NPMenu* menu); +typedef NPBool (*NPN_ConvertPointProcPtr)(NPP npp, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); + +typedef void (*NPN_ReleaseVariantValueProcPtr) (NPVariant *variant); + +typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr) (const NPUTF8 *name); +typedef void (*NPN_GetStringIdentifiersProcPtr) (const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers); +typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr) (int32_t intid); +typedef int32_t (*NPN_IntFromIdentifierProcPtr) (NPIdentifier identifier); +typedef bool (*NPN_IdentifierIsStringProcPtr) (NPIdentifier identifier); +typedef NPUTF8 *(*NPN_UTF8FromIdentifierProcPtr) (NPIdentifier identifier); + +typedef NPObject* (*NPN_CreateObjectProcPtr) (NPP, NPClass *aClass); +typedef NPObject* (*NPN_RetainObjectProcPtr) (NPObject *obj); +typedef void (*NPN_ReleaseObjectProcPtr) (NPObject *obj); +typedef bool (*NPN_InvokeProcPtr) (NPP npp, NPObject *obj, NPIdentifier methodName, const NPVariant *args, unsigned argCount, NPVariant *result); +typedef bool (*NPN_InvokeDefaultProcPtr) (NPP npp, NPObject *obj, const NPVariant *args, unsigned argCount, NPVariant *result); +typedef bool (*NPN_EvaluateProcPtr) (NPP npp, NPObject *obj, NPString *script, NPVariant *result); +typedef bool (*NPN_GetPropertyProcPtr) (NPP npp, NPObject *obj, NPIdentifier propertyName, NPVariant *result); +typedef bool (*NPN_SetPropertyProcPtr) (NPP npp, NPObject *obj, NPIdentifier propertyName, const NPVariant *value); +typedef bool (*NPN_HasPropertyProcPtr) (NPP, NPObject *npobj, NPIdentifier propertyName); +typedef bool (*NPN_HasMethodProcPtr) (NPP npp, NPObject *npobj, NPIdentifier methodName); +typedef bool (*NPN_RemovePropertyProcPtr) (NPP npp, NPObject *obj, NPIdentifier propertyName); +typedef void (*NPN_SetExceptionProcPtr) (NPObject *obj, const NPUTF8 *message); +typedef bool (*NPN_EnumerateProcPtr) (NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count); +typedef bool (*NPN_ConstructProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result); + +typedef NPError (*NPP_NewProcPtr)(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved); +typedef NPError (*NPP_DestroyProcPtr)(NPP instance, NPSavedData** save); +typedef NPError (*NPP_SetWindowProcPtr)(NPP instance, NPWindow* window); +typedef NPError (*NPP_NewStreamProcPtr)(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype); +typedef NPError (*NPP_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason); +typedef void (*NPP_StreamAsFileProcPtr)(NPP instance, NPStream* stream, const char* fname); +typedef int32_t (*NPP_WriteReadyProcPtr)(NPP instance, NPStream* stream); +typedef int32_t (*NPP_WriteProcPtr)(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer); +typedef void (*NPP_PrintProcPtr)(NPP instance, NPPrint* platformPrint); +typedef int16_t (*NPP_HandleEventProcPtr)(NPP instance, void* event); +typedef void (*NPP_URLNotifyProcPtr)(NPP instance, const char* URL, NPReason reason, void* notifyData); +typedef NPError (*NPP_GetValueProcPtr)(NPP instance, NPPVariable variable, void *ret_value); +typedef NPError (*NPP_SetValueProcPtr)(NPP instance, NPNVariable variable, void *value); + +typedef void *(*NPP_GetJavaClassProcPtr)(void); +typedef void* JRIGlobalRef; //not using this right now + +typedef struct _NPNetscapeFuncs { + uint16_t size; + uint16_t version; + + NPN_GetURLProcPtr geturl; + NPN_PostURLProcPtr posturl; + NPN_RequestReadProcPtr requestread; + NPN_NewStreamProcPtr newstream; + NPN_WriteProcPtr write; + NPN_DestroyStreamProcPtr destroystream; + NPN_StatusProcPtr status; + NPN_UserAgentProcPtr uagent; + NPN_MemAllocProcPtr memalloc; + NPN_MemFreeProcPtr memfree; + NPN_MemFlushProcPtr memflush; + NPN_ReloadPluginsProcPtr reloadplugins; + NPN_GetJavaEnvProcPtr getJavaEnv; + NPN_GetJavaPeerProcPtr getJavaPeer; + NPN_GetURLNotifyProcPtr geturlnotify; + NPN_PostURLNotifyProcPtr posturlnotify; + NPN_GetValueProcPtr getvalue; + NPN_SetValueProcPtr setvalue; + NPN_InvalidateRectProcPtr invalidaterect; + NPN_InvalidateRegionProcPtr invalidateregion; + NPN_ForceRedrawProcPtr forceredraw; + + NPN_GetStringIdentifierProcPtr getstringidentifier; + NPN_GetStringIdentifiersProcPtr getstringidentifiers; + NPN_GetIntIdentifierProcPtr getintidentifier; + NPN_IdentifierIsStringProcPtr identifierisstring; + NPN_UTF8FromIdentifierProcPtr utf8fromidentifier; + NPN_IntFromIdentifierProcPtr intfromidentifier; + NPN_CreateObjectProcPtr createobject; + NPN_RetainObjectProcPtr retainobject; + NPN_ReleaseObjectProcPtr releaseobject; + NPN_InvokeProcPtr invoke; + NPN_InvokeDefaultProcPtr invokeDefault; + NPN_EvaluateProcPtr evaluate; + NPN_GetPropertyProcPtr getproperty; + NPN_SetPropertyProcPtr setproperty; + NPN_RemovePropertyProcPtr removeproperty; + NPN_HasPropertyProcPtr hasproperty; + NPN_HasMethodProcPtr hasmethod; + NPN_ReleaseVariantValueProcPtr releasevariantvalue; + NPN_SetExceptionProcPtr setexception; + NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate; + NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate; + NPN_EnumerateProcPtr enumerate; + NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall; + NPN_ConstructProcPtr construct; + NPN_GetValueForURLProcPtr getvalueforurl; + NPN_SetValueForURLProcPtr setvalueforurl; + NPN_GetAuthenticationInfoProcPtr getauthenticationinfo; + NPN_ScheduleTimerProcPtr scheduletimer; + NPN_UnscheduleTimerProcPtr unscheduletimer; + NPN_PopUpContextMenuProcPtr popupcontextmenu; + NPN_ConvertPointProcPtr convertpoint; +} NPNetscapeFuncs; + +typedef struct _NPPluginFuncs { + uint16_t size; + uint16_t version; + NPP_NewProcPtr newp; + NPP_DestroyProcPtr destroy; + NPP_SetWindowProcPtr setwindow; + NPP_NewStreamProcPtr newstream; + NPP_DestroyStreamProcPtr destroystream; + NPP_StreamAsFileProcPtr asfile; + NPP_WriteReadyProcPtr writeready; + NPP_WriteProcPtr write; + NPP_PrintProcPtr print; + NPP_HandleEventProcPtr event; + NPP_URLNotifyProcPtr urlnotify; + JRIGlobalRef javaClass; + NPP_GetValueProcPtr getvalue; + NPP_SetValueProcPtr setvalue; +} NPPluginFuncs; + +typedef EXPORTED_CALLBACK(NPError, NP_GetEntryPointsFuncPtr)(NPPluginFuncs*); +typedef EXPORTED_CALLBACK(void, NPP_ShutdownProcPtr)(void); + +#if defined(XP_MACOSX) +typedef void (*BP_CreatePluginMIMETypesPreferencesFuncPtr)(void); +typedef NPError (*MainFuncPtr)(NPNetscapeFuncs*, NPPluginFuncs*, NPP_ShutdownProcPtr*); +#endif + +#if defined(XP_UNIX) +typedef EXPORTED_CALLBACK(NPError, NP_InitializeFuncPtr)(NPNetscapeFuncs*, NPPluginFuncs*); +typedef EXPORTED_CALLBACK(char*, NP_GetMIMEDescriptionFuncPtr)(void); +#elif defined(ANDROID_PLUGINS) +typedef EXPORTED_CALLBACK(NPError, NP_InitializeFuncPtr)(NPNetscapeFuncs*, NPPluginFuncs*, JNIEnv *java_environment); +typedef EXPORTED_CALLBACK(char*, NP_GetMIMEDescriptionFuncPtr)(void); +#else +typedef EXPORTED_CALLBACK(NPError, NP_InitializeFuncPtr)(NPNetscapeFuncs*); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Source/WebCore/plugins/qt/PluginContainerQt.cpp b/Source/WebCore/plugins/qt/PluginContainerQt.cpp new file mode 100644 index 0000000..8486180 --- /dev/null +++ b/Source/WebCore/plugins/qt/PluginContainerQt.cpp @@ -0,0 +1,150 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "PluginContainerQt.h" + +#include "FocusController.h" +#include "Frame.h" +#include "FrameView.h" +#include "Page.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformWheelEvent.h" +#include "PluginView.h" +#include <QApplication> +#include <QX11Info> + +using namespace WebCore; + +PluginClientWrapper::PluginClientWrapper(QWidget* parent, WId client) + : QWidget(0, Qt::Popup) +{ + // create a QWidget that adopts the plugin window id, do not give it + // a parent so that we don't end up handling events supposed to be + // handled by the QX11EmbedContainer. + + // without the parent this will be considered a toplevel widget, + // and thus make Qt not quit the event loop after the last window + // has been closed. In order to work around this, we set the window + // type to Qt::Popup. + + create(client, false, true); + m_parent = parent; +} + +PluginClientWrapper::~PluginClientWrapper() +{ + destroy(false, false); +} + +bool PluginClientWrapper::x11Event(XEvent* event) +{ + // modify the event window id and insert it into the Qt event system. + event->xany.window = m_parent->effectiveWinId(); + static_cast<QApplication*>(QApplication::instance())->x11ProcessEvent(event); + return true; +} + +PluginContainerQt::PluginContainerQt(PluginView* view, QWidget* parent) + : QX11EmbedContainer(parent) + , m_pluginView(view) + , m_clientWrapper(0) +{ + connect(this, SIGNAL(clientClosed()), this, SLOT(on_clientClosed())); + connect(this, SIGNAL(clientIsEmbedded()), this, SLOT(on_clientIsEmbedded())); +} + +PluginContainerQt::~PluginContainerQt() +{ + delete m_clientWrapper; + m_pluginView->setPlatformPluginWidget(0); +} + +void PluginContainerQt::on_clientClosed() +{ + delete m_clientWrapper; + m_clientWrapper = 0; +} + +void PluginContainerQt::on_clientIsEmbedded() +{ + delete m_clientWrapper; + m_clientWrapper = 0; + + // Only create a QWidget wrapper for the plugin in the case it isn't in the + // Qt window mapper, and thus receiving events from the Qt event system. + // This way the PluginClientWrapper receives the scroll events and passes + // them to the parent. NOTICE: Native Qt based plugins running in process, + // will already be in the window mapper, and thus creating a wrapper, stops + // them from getting events from Qt, as they are redirected to the wrapper. + if (!QWidget::find(clientWinId())) + m_clientWrapper = new PluginClientWrapper(this, clientWinId()); +} + +void PluginContainerQt::redirectWheelEventsToParent(bool enable) +{ + // steal wheel events from the plugin as we want to handle it. When doing this + // all button 4, 5, 6, and 7, ButtonPress and ButtonRelease events are passed + // to the x11Event handler of the PluginClientWrapper, which then changes the + // window id of the event to the parent of PluginContainer and puts the event + // back into the Qt event loop, so that we will actually scroll the parent + // frame. + for (int buttonNo = 4; buttonNo < 8; buttonNo++) { + if (enable) + XGrabButton(x11Info().display(), buttonNo, AnyModifier, clientWinId(), + false, ButtonPressMask, GrabModeAsync, GrabModeAsync, 0L, 0L); + else + XUngrabButton(x11Info().display(), buttonNo, AnyModifier, clientWinId()); + } +} + +bool PluginContainerQt::x11Event(XEvent* event) +{ + switch (event->type) { + case EnterNotify: + // if the plugin window doesn't have focus we do not want to send wheel + // events to it, but to the parent frame, so let's redirect here. + redirectWheelEventsToParent(!hasFocus()); + break; + case LeaveNotify: + // it is always safe to ungrab wheel events when the mouse leaves the + // plugin window. + redirectWheelEventsToParent(false); + break; + } + + return QX11EmbedContainer::x11Event(event); +} + +void PluginContainerQt::focusInEvent(QFocusEvent* event) +{ + // we got focus, stop redirecting the wheel events + redirectWheelEventsToParent(false); + + if (Page* page = m_pluginView->parentFrame()->page()) + page->focusController()->setActive(true); + + m_pluginView->focusPluginElement(); +} + +void PluginContainerQt::focusOutEvent(QFocusEvent*) +{ + if (Page* page = m_pluginView->parentFrame()->page()) + page->focusController()->setActive(false); +} diff --git a/Source/WebCore/plugins/qt/PluginContainerQt.h b/Source/WebCore/plugins/qt/PluginContainerQt.h new file mode 100644 index 0000000..3a2896d --- /dev/null +++ b/Source/WebCore/plugins/qt/PluginContainerQt.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef PluginContainerQt_h +#define PluginContainerQt_h + +#include <QX11EmbedContainer> + +namespace WebCore { + + class PluginView; + + class PluginContainerQt : public QX11EmbedContainer + { + Q_OBJECT + public: + PluginContainerQt(PluginView*, QWidget* parent); + ~PluginContainerQt(); + + void redirectWheelEventsToParent(bool enable = true); + + protected: + virtual bool x11Event(XEvent*); + virtual void focusInEvent(QFocusEvent*); + virtual void focusOutEvent(QFocusEvent*); + + public slots: + void on_clientClosed(); + void on_clientIsEmbedded(); + + private: + PluginView* m_pluginView; + QWidget* m_clientWrapper; + }; + + class PluginClientWrapper : public QWidget + { + public: + PluginClientWrapper(QWidget* parent, WId client); + ~PluginClientWrapper(); + bool x11Event(XEvent*); + + private: + QWidget* m_parent; + }; +} + +#endif // PluginContainerQt_h diff --git a/Source/WebCore/plugins/qt/PluginPackageQt.cpp b/Source/WebCore/plugins/qt/PluginPackageQt.cpp new file mode 100644 index 0000000..ce07faf --- /dev/null +++ b/Source/WebCore/plugins/qt/PluginPackageQt.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginPackage.h" + +#include "MIMETypeRegistry.h" +#include "npruntime_impl.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +bool PluginPackage::fetchInfo() +{ + if (!load()) + return false; + + NPP_GetValueProcPtr gv = (NPP_GetValueProcPtr)m_module->resolve("NP_GetValue"); + typedef char *(*NPP_GetMIMEDescriptionProcPtr)(); + NPP_GetMIMEDescriptionProcPtr gm = + (NPP_GetMIMEDescriptionProcPtr)m_module->resolve("NP_GetMIMEDescription"); + if (!gm || !gv) + return false; + + char *buf = 0; + NPError err = gv(0, NPPVpluginNameString, (void*) &buf); + if (err != NPERR_NO_ERROR) + return false; + + m_name = buf; + err = gv(0, NPPVpluginDescriptionString, (void*) &buf); + if (err != NPERR_NO_ERROR) + return false; + + m_description = buf; + determineModuleVersionFromDescription(); + + String mimeDescription = gm(); + setMIMEDescription(mimeDescription); + m_infoIsFromCache = false; + + return true; +} + +void PluginPackage::setMIMEDescription(const String& mimeDescription) +{ + m_fullMIMEDescription = mimeDescription.lower(); + + Vector<String> types; + mimeDescription.lower().split(UChar(';'), false, types); + for (unsigned i = 0; i < types.size(); ++i) { + Vector<String> mime; + types[i].split(UChar(':'), true, mime); + if (mime.size() > 0) { + Vector<String> exts; + if (mime.size() > 1) + mime[1].split(UChar(','), false, exts); + determineQuirks(mime[0]); + m_mimeToExtensions.add(mime[0], exts); + if (mime.size() > 2) + m_mimeToDescriptions.add(mime[0], mime[2]); + } + } +} + +static NPError staticPluginQuirkRequiresGtkToolKit_NPN_GetValue(NPP instance, NPNVariable variable, void* value) +{ + if (variable == NPNVToolkit) { + *static_cast<uint32_t*>(value) = 2; + return NPERR_NO_ERROR; + } + + return NPN_GetValue(instance, variable, value); +} + +static void initializeGtk(QLibrary* module = 0) +{ + // Ensures missing Gtk initialization in some versions of Adobe's flash player + // plugin do not cause crashes. See BR# 40567, 44324, and 44405 for details. + if (module) { + typedef void *(*gtk_init_ptr)(int*, char***); + gtk_init_ptr gtkInit = (gtk_init_ptr)module->resolve("gtk_init"); + if (gtkInit) { + // Prevent gtk_init() from replacing the X error handlers, since the Gtk + // handlers abort when they receive an X error, thus killing the viewer. +#ifdef Q_WS_X11 + int (*old_error_handler)(Display*, XErrorEvent*) = XSetErrorHandler(0); + int (*old_io_error_handler)(Display*) = XSetIOErrorHandler(0); +#endif + gtkInit(0, 0); +#ifdef Q_WS_X11 + XSetErrorHandler(old_error_handler); + XSetIOErrorHandler(old_io_error_handler); +#endif + return; + } + } + + QLibrary library("libgtk-x11-2.0.so.0"); + if (library.load()) { + typedef void *(*gtk_init_check_ptr)(int*, char***); + gtk_init_check_ptr gtkInitCheck = (gtk_init_check_ptr)library.resolve("gtk_init_check"); + // NOTE: We're using gtk_init_check() since gtk_init() calls exit() on failure. + if (gtkInitCheck) + (void) gtkInitCheck(0, 0); + } +} + +bool PluginPackage::load() +{ + if (m_isLoaded) { + m_loadCount++; + return true; + } + + m_module = new QLibrary((QString)m_path); + m_module->setLoadHints(QLibrary::ResolveAllSymbolsHint); + if (!m_module->load()) { + LOG(Plugins, "%s not loaded (%s)", m_path.utf8().data(), + m_module->errorString().toLatin1().constData()); + return false; + } + + m_isLoaded = true; + + NP_InitializeFuncPtr NP_Initialize; + NPError npErr; + + NP_Initialize = (NP_InitializeFuncPtr)m_module->resolve("NP_Initialize"); + m_NPP_Shutdown = (NPP_ShutdownProcPtr)m_module->resolve("NP_Shutdown"); + + if (!NP_Initialize || !m_NPP_Shutdown) + goto abort; + + memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); + m_pluginFuncs.size = sizeof(m_pluginFuncs); + + initializeBrowserFuncs(); + + if (m_path.contains("npwrapper.")) { + // nspluginwrapper relies on the toolkit value to know if glib is available + // It does so in NP_Initialize with a null instance, therefore it is done this way: + m_browserFuncs.getvalue = staticPluginQuirkRequiresGtkToolKit_NPN_GetValue; + // Workaround Adobe's failure to properly initialize Gtk in some versions + // of their flash player plugin. + initializeGtk(); + } else if (m_path.contains("flashplayer")) { + // Workaround Adobe's failure to properly initialize Gtk in some versions + // of their flash player plugin. + initializeGtk(m_module); + } + +#if defined(XP_UNIX) + npErr = NP_Initialize(&m_browserFuncs, &m_pluginFuncs); +#else + npErr = NP_Initialize(&m_browserFuncs); +#endif + if (npErr != NPERR_NO_ERROR) + goto abort; + + m_loadCount++; + return true; + +abort: + unloadWithoutShutdown(); + return false; +} + +uint16_t PluginPackage::NPVersion() const +{ + return NP_VERSION_MINOR; +} + +} diff --git a/Source/WebCore/plugins/qt/PluginViewQt.cpp b/Source/WebCore/plugins/qt/PluginViewQt.cpp new file mode 100644 index 0000000..fdbe552 --- /dev/null +++ b/Source/WebCore/plugins/qt/PluginViewQt.cpp @@ -0,0 +1,1011 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginView.h" + +#if USE(JSC) +#include "Bridge.h" +#endif +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "FloatPoint.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "HostWindow.h" +#include "Image.h" +#if USE(JSC) +#include "JSDOMBinding.h" +#endif +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "NotImplemented.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PlatformKeyboardEvent.h" +#include "PluginContainerQt.h" +#include "PluginDebug.h" +#include "PluginPackage.h" +#include "PluginMainThreadScheduler.h" +#include "QWebPageClient.h" +#include "RenderLayer.h" +#include "ScriptController.h" +#include "Settings.h" +#include "npruntime_impl.h" +#include "qwebpage_p.h" +#include "runtime_root.h" + +#include <QApplication> +#include <QDesktopWidget> +#include <QGraphicsWidget> +#include <QKeyEvent> +#include <QPainter> +#include <QStyleOptionGraphicsItem> +#include <QWidget> +#include <QX11Info> +#include <X11/X.h> +#ifndef QT_NO_XRENDER +#define Bool int +#define Status int +#include <X11/extensions/Xrender.h> +#endif +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> + +using JSC::ExecState; +using JSC::Interpreter; +using JSC::JSLock; +using JSC::JSObject; +using JSC::UString; + +using std::min; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +#if USE(ACCELERATED_COMPOSITING) +// Qt's GraphicsLayer (GraphicsLayerQt) requires layers to be QGraphicsWidgets +class PluginGraphicsLayerQt : public QGraphicsWidget { +public: + PluginGraphicsLayerQt(PluginView* view) : m_view(view) { } + ~PluginGraphicsLayerQt() { } + + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0) + { + Q_UNUSED(widget); + m_view->paintUsingXPixmap(painter, option->exposedRect.toRect()); + } + +private: + PluginView* m_view; +}; +#endif + +void PluginView::updatePluginWidget() +{ + if (!parent()) + return; + + ASSERT(parent()->isFrameView()); + FrameView* frameView = static_cast<FrameView*>(parent()); + + IntRect oldWindowRect = m_windowRect; + IntRect oldClipRect = m_clipRect; + + m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); + m_clipRect = windowClipRect(); + m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); + + if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect) + return; + + if (!m_isWindowed && m_windowRect.size() != oldWindowRect.size()) { +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + // On Maemo5, Flash always renders to 16-bit buffer + if (m_renderToImage) + m_image = QImage(m_windowRect.width(), m_windowRect.height(), QImage::Format_RGB16); + else +#endif + { + if (m_drawable) + XFreePixmap(QX11Info::display(), m_drawable); + + m_drawable = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), m_windowRect.width(), m_windowRect.height(), + ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth); + QApplication::syncX(); // make sure that the server knows about the Drawable + } + } + + // do not call setNPWindowIfNeeded immediately, will be called on paint() + m_hasPendingGeometryChange = true; + + // (i) in order to move/resize the plugin window at the same time as the + // rest of frame during e.g. scrolling, we set the window geometry + // in the paint() function, but as paint() isn't called when the + // plugin window is outside the frame which can be caused by a + // scroll, we need to move/resize immediately. + // (ii) if we are running layout tests from DRT, paint() won't ever get called + // so we need to call setNPWindowIfNeeded() if window geometry has changed + if (!m_windowRect.intersects(frameView->frameRect()) + || (QWebPagePrivate::drtRun && platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect))) + setNPWindowIfNeeded(); + + if (!m_platformLayer) { + // Make sure we get repainted afterwards. This is necessary for downward + // scrolling to move the plugin widget properly. + invalidate(); + } +} + +void PluginView::setFocus(bool focused) +{ + if (platformPluginWidget()) { + if (focused) + platformPluginWidget()->setFocus(Qt::OtherFocusReason); + } else { + Widget::setFocus(focused); + } +} + +void PluginView::show() +{ + Q_ASSERT(platformPluginWidget() == platformWidget()); + Widget::show(); +} + +void PluginView::hide() +{ + Q_ASSERT(platformPluginWidget() == platformWidget()); + Widget::hide(); +} + +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) +void PluginView::paintUsingImageSurfaceExtension(QPainter* painter, const IntRect& exposedRect) +{ + NPImageExpose imageExpose; + QPoint offset; + QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); + const bool surfaceHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent()); + + QPaintDevice* surface = QPainter::redirected(painter->device(), &offset); + + // If the surface is a QImage, we can render directly into it + if (surfaceHasUntransformedContents && surface && surface->devType() == QInternal::Image) { + QImage* image = static_cast<QImage*>(surface); + offset = -offset; // negating the offset gives us the offset of the view within the surface + imageExpose.data = reinterpret_cast<char*>(image->bits()); + imageExpose.dataSize.width = image->width(); + imageExpose.dataSize.height = image->height(); + imageExpose.stride = image->bytesPerLine(); + imageExpose.depth = image->depth(); // this is guaranteed to be 16 on Maemo5 + imageExpose.translateX = offset.x() + m_windowRect.x(); + imageExpose.translateY = offset.y() + m_windowRect.y(); + imageExpose.scaleX = 1; + imageExpose.scaleY = 1; + } else { + if (m_isTransparent) { + // On Maemo5, Flash expects the buffer to contain the contents that are below it. + // We don't support transparency for non-raster graphicssystem, so clean the image + // before giving to Flash. + QPainter imagePainter(&m_image); + imagePainter.fillRect(exposedRect, Qt::white); + } + + imageExpose.data = reinterpret_cast<char*>(m_image.bits()); + imageExpose.dataSize.width = m_image.width(); + imageExpose.dataSize.height = m_image.height(); + imageExpose.stride = m_image.bytesPerLine(); + imageExpose.depth = m_image.depth(); + imageExpose.translateX = 0; + imageExpose.translateY = 0; + imageExpose.scaleX = 1; + imageExpose.scaleY = 1; + } + imageExpose.x = exposedRect.x(); + imageExpose.y = exposedRect.y(); + imageExpose.width = exposedRect.width(); + imageExpose.height = exposedRect.height(); + + XEvent xevent; + memset(&xevent, 0, sizeof(XEvent)); + XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; + exposeEvent.type = GraphicsExpose; + exposeEvent.display = 0; + exposeEvent.drawable = reinterpret_cast<XID>(&imageExpose); + exposeEvent.x = exposedRect.x(); + exposeEvent.y = exposedRect.y(); + exposeEvent.width = exposedRect.width(); + exposeEvent.height = exposedRect.height(); + + dispatchNPEvent(xevent); + + if (!surfaceHasUntransformedContents || !surface || surface->devType() != QInternal::Image) + painter->drawImage(QPoint(frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y()), m_image, exposedRect); +} +#endif + +void PluginView::paintUsingXPixmap(QPainter* painter, const QRect &exposedRect) +{ + QPixmap qtDrawable = QPixmap::fromX11Pixmap(m_drawable, QPixmap::ExplicitlyShared); + const int drawableDepth = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth; + ASSERT(drawableDepth == qtDrawable.depth()); + const bool syncX = m_pluginDisplay && m_pluginDisplay != QX11Info::display(); + + // When printing, Qt uses a QPicture to capture the output in preview mode. The + // QPicture holds a reference to the X Pixmap. As a result, the print preview would + // update itself when the X Pixmap changes. To prevent this, we create a copy. + if (m_element->document()->printing()) + qtDrawable = qtDrawable.copy(); + + if (m_isTransparent && drawableDepth != 32) { + // Attempt content propagation for drawable with no alpha by copying over from the backing store + QPoint offset; + QPaintDevice* backingStoreDevice = QPainter::redirected(painter->device(), &offset); + offset = -offset; // negating the offset gives us the offset of the view within the backing store pixmap + + const bool hasValidBackingStore = backingStoreDevice && backingStoreDevice->devType() == QInternal::Pixmap; + QPixmap* backingStorePixmap = static_cast<QPixmap*>(backingStoreDevice); + + // We cannot grab contents from the backing store when painting on QGraphicsView items + // (because backing store contents are already transformed). What we really mean to do + // here is to check if we are painting on QWebView, but let's be a little permissive :) + QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); + const bool backingStoreHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent()); + + if (hasValidBackingStore && backingStorePixmap->depth() == drawableDepth + && backingStoreHasUntransformedContents) { + GC gc = XDefaultGC(QX11Info::display(), QX11Info::appScreen()); + XCopyArea(QX11Info::display(), backingStorePixmap->handle(), m_drawable, gc, + offset.x() + m_windowRect.x() + exposedRect.x(), offset.y() + m_windowRect.y() + exposedRect.y(), + exposedRect.width(), exposedRect.height(), exposedRect.x(), exposedRect.y()); + } else { // no backing store, clean the pixmap because the plugin thinks its transparent + QPainter painter(&qtDrawable); + painter.fillRect(exposedRect, Qt::white); + } + + if (syncX) + QApplication::syncX(); + } + + XEvent xevent; + memset(&xevent, 0, sizeof(XEvent)); + XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; + exposeEvent.type = GraphicsExpose; + exposeEvent.display = QX11Info::display(); + exposeEvent.drawable = qtDrawable.handle(); + exposeEvent.x = exposedRect.x(); + exposeEvent.y = exposedRect.y(); + exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode + exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode + + dispatchNPEvent(xevent); + + if (syncX) + XSync(m_pluginDisplay, false); // sync changes by plugin + + painter->drawPixmap(QPoint(exposedRect.x(), exposedRect.y()), qtDrawable, exposedRect); +} + +void PluginView::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_isStarted) { + paintMissingPluginIcon(context, rect); + return; + } + + if (context->paintingDisabled()) + return; + + setNPWindowIfNeeded(); + + if (m_isWindowed) + return; + +#if USE(ACCELERATED_COMPOSITING) + if (m_platformLayer) + return; +#endif + + if (!m_drawable +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + && m_image.isNull() +#endif + ) + return; + + QPainter* painter = context->platformContext(); + IntRect exposedRect(rect); + exposedRect.intersect(frameRect()); + exposedRect.move(-frameRect().x(), -frameRect().y()); + +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + if (!m_image.isNull()) { + paintUsingImageSurfaceExtension(painter, exposedRect); + return; + } +#endif + + painter->translate(frameRect().x(), frameRect().y()); + paintUsingXPixmap(painter, exposedRect); + painter->translate(-frameRect().x(), -frameRect().y()); +} + +// TODO: Unify across ports. +bool PluginView::dispatchNPEvent(NPEvent& event) +{ + if (!m_plugin->pluginFuncs()->event) + return false; + + PluginView::setCurrentPluginView(this); +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + setCallingPlugin(true); + bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + + return accepted; +} + +void setSharedXEventFields(XEvent* xEvent, QWidget* ownerWidget) +{ + xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server + xEvent->xany.send_event = false; + xEvent->xany.display = QX11Info::display(); + // NOTE: event->xany.window doesn't always respond to the .window property of other XEvent's + // but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify + // events; thus, this is right: + xEvent->xany.window = ownerWidget ? ownerWidget->window()->handle() : 0; +} + +void PluginView::initXEvent(XEvent* xEvent) +{ + memset(xEvent, 0, sizeof(XEvent)); + + QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); + QWidget* ownerWidget = client ? client->ownerWidget() : 0; + setSharedXEventFields(xEvent, ownerWidget); +} + +void setXKeyEventSpecificFields(XEvent* xEvent, KeyboardEvent* event) +{ + const PlatformKeyboardEvent* keyEvent = event->keyEvent(); + + xEvent->type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // ints as Qt unsets KeyPress and KeyRelease + xEvent->xkey.root = QX11Info::appRootWindow(); + xEvent->xkey.subwindow = 0; // we have no child window + xEvent->xkey.time = event->timeStamp(); + xEvent->xkey.state = keyEvent->nativeModifiers(); + xEvent->xkey.keycode = keyEvent->nativeScanCode(); + + // We may not have a nativeScanCode() if the key event is from DRT's eventsender. In that + // case just populate the XEvent's keycode with the Qt platform-independent keycode. The only + // place this keycode will be used is in webkit_test_plugin_handle_event(). + if (QWebPagePrivate::drtRun && !xEvent->xkey.keycode) { + QKeyEvent* qKeyEvent = keyEvent->qtEvent(); + ASSERT(qKeyEvent); + if (!qKeyEvent->text().isEmpty()) + xEvent->xkey.keycode = int(qKeyEvent->text().at(0).unicode() + qKeyEvent->modifiers()); + else if (qKeyEvent->key() && (qKeyEvent->key() != Qt::Key_unknown)) + xEvent->xkey.keycode = int(qKeyEvent->key() + qKeyEvent->modifiers()); + } + + xEvent->xkey.same_screen = true; + + // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window + // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not + // set to their normal Xserver values. e.g. Key events don't have a position. + // source: https://developer.mozilla.org/en/NPEvent + xEvent->xkey.x = 0; + xEvent->xkey.y = 0; + xEvent->xkey.x_root = 0; + xEvent->xkey.y_root = 0; +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + if (m_isWindowed) + return; + + if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent) + return; + + XEvent npEvent; + initXEvent(&npEvent); + setXKeyEventSpecificFields(&npEvent, event); + + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); +} + +static unsigned int inputEventState(MouseEvent* event) +{ + unsigned int state = 0; + if (event->ctrlKey()) + state |= ControlMask; + if (event->shiftKey()) + state |= ShiftMask; + if (event->altKey()) + state |= Mod1Mask; + if (event->metaKey()) + state |= Mod4Mask; + return state; +} + +static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) +{ + XButtonEvent& xbutton = xEvent->xbutton; + xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease; + xbutton.root = QX11Info::appRootWindow(); + xbutton.subwindow = 0; + xbutton.time = event->timeStamp(); + xbutton.x = postZoomPos.x(); + xbutton.y = postZoomPos.y(); + xbutton.x_root = event->screenX(); + xbutton.y_root = event->screenY(); + xbutton.state = inputEventState(event); + switch (event->button()) { + case MiddleButton: + xbutton.button = Button2; + break; + case RightButton: + xbutton.button = Button3; + break; + case LeftButton: + default: + xbutton.button = Button1; + break; + } + xbutton.same_screen = true; +} + +static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) +{ + XMotionEvent& xmotion = xEvent->xmotion; + xmotion.type = MotionNotify; + xmotion.root = QX11Info::appRootWindow(); + xmotion.subwindow = 0; + xmotion.time = event->timeStamp(); + xmotion.x = postZoomPos.x(); + xmotion.y = postZoomPos.y(); + xmotion.x_root = event->screenX(); + xmotion.y_root = event->screenY(); + xmotion.state = inputEventState(event); + xmotion.is_hint = NotifyNormal; + xmotion.same_screen = true; +} + +static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) +{ + XCrossingEvent& xcrossing = xEvent->xcrossing; + xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify; + xcrossing.root = QX11Info::appRootWindow(); + xcrossing.subwindow = 0; + xcrossing.time = event->timeStamp(); + xcrossing.x = postZoomPos.y(); + xcrossing.y = postZoomPos.x(); + xcrossing.x_root = event->screenX(); + xcrossing.y_root = event->screenY(); + xcrossing.state = inputEventState(event); + xcrossing.mode = NotifyNormal; + xcrossing.detail = NotifyDetailNone; + xcrossing.same_screen = true; + xcrossing.focus = false; +} + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + if (m_isWindowed) + return; + + if (event->button() == RightButton && m_plugin->quirks().contains(PluginQuirkIgnoreRightClickInWindowlessMode)) + return; + + if (event->type() == eventNames().mousedownEvent) { + // Give focus to the plugin on click + if (Page* page = m_parentFrame->page()) + page->focusController()->setActive(true); + + focusPluginElement(); + } + + XEvent npEvent; + initXEvent(&npEvent); + + IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); + + if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent) + setXButtonEventSpecificFields(&npEvent, event, postZoomPos); + else if (event->type() == eventNames().mousemoveEvent) + setXMotionEventSpecificFields(&npEvent, event, postZoomPos); + else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent) + setXCrossingEventSpecificFields(&npEvent, event, postZoomPos); + else + return; + + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); +} + +void PluginView::handleFocusInEvent() +{ + XEvent npEvent; + initXEvent(&npEvent); + + XFocusChangeEvent& event = npEvent.xfocus; + event.type = 9; /* int as Qt unsets FocusIn */ + event.mode = NotifyNormal; + event.detail = NotifyDetailNone; + + dispatchNPEvent(npEvent); +} + +void PluginView::handleFocusOutEvent() +{ + XEvent npEvent; + initXEvent(&npEvent); + + XFocusChangeEvent& event = npEvent.xfocus; + event.type = 10; /* int as Qt unsets FocusOut */ + event.mode = NotifyNormal; + event.detail = NotifyDetailNone; + + dispatchNPEvent(npEvent); +} + +void PluginView::setParent(ScrollView* parent) +{ + Widget::setParent(parent); + + if (parent) + init(); +} + +void PluginView::setNPWindowRect(const IntRect&) +{ + if (!m_isWindowed) + setNPWindowIfNeeded(); +} + +void PluginView::setNPWindowIfNeeded() +{ + if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) + return; + + // If the plugin didn't load sucessfully, no point in calling setwindow + if (m_status != PluginStatusLoadedSuccessfully) + return; + + // On Unix, only call plugin if it's full-page or windowed + if (m_mode != NP_FULL && m_mode != NP_EMBED) + return; + + // Check if the platformPluginWidget still exists + if (m_isWindowed && !platformPluginWidget()) + return; + + if (!m_hasPendingGeometryChange) + return; + m_hasPendingGeometryChange = false; + + if (m_isWindowed) { + platformPluginWidget()->setGeometry(m_windowRect); + // if setMask is set with an empty QRegion, no clipping will + // be performed, so in that case we hide the plugin view + platformPluginWidget()->setVisible(!m_clipRect.isEmpty()); + platformPluginWidget()->setMask(QRegion(m_clipRect)); + + m_npWindow.x = m_windowRect.x(); + m_npWindow.y = m_windowRect.y(); + } else { + m_npWindow.x = 0; + m_npWindow.y = 0; + } + + // If the width or height are null, set the clipRect to null, indicating that + // the plugin is not visible/scrolled out. + if (!m_clipRect.width() || !m_clipRect.height()) { + m_npWindow.clipRect.left = 0; + m_npWindow.clipRect.right = 0; + m_npWindow.clipRect.top = 0; + m_npWindow.clipRect.bottom = 0; + } else { + // Clipping rectangle of the plug-in; the origin is the top left corner of the drawable or window. + m_npWindow.clipRect.left = m_npWindow.x + m_clipRect.x(); + m_npWindow.clipRect.top = m_npWindow.y + m_clipRect.y(); + m_npWindow.clipRect.right = m_npWindow.x + m_clipRect.x() + m_clipRect.width(); + m_npWindow.clipRect.bottom = m_npWindow.y + m_clipRect.y() + m_clipRect.height(); + } + + if (m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce)) { + // FLASH WORKAROUND: Only set initially. Multiple calls to + // setNPWindow() cause the plugin to crash in windowed mode. + if (!m_isWindowed || m_npWindow.width == -1 || m_npWindow.height == -1) { + m_npWindow.width = m_windowRect.width(); + m_npWindow.height = m_windowRect.height(); + } + } else { + m_npWindow.width = m_windowRect.width(); + m_npWindow.height = m_windowRect.height(); + } + + PluginView::setCurrentPluginView(this); +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); +} + +void PluginView::setParentVisible(bool visible) +{ + if (isParentVisible() == visible) + return; + + Widget::setParentVisible(visible); + + if (isSelfVisible() && platformPluginWidget()) + platformPluginWidget()->setVisible(visible); +} + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) +{ + String filename(buf, len); + + if (filename.startsWith("file:///")) + filename = filename.substring(8); + + long long size; + if (!getFileSize(filename, size)) + return NPERR_FILE_NOT_FOUND; + + FILE* fileHandle = fopen((filename.utf8()).data(), "r"); + if (!fileHandle) + return NPERR_FILE_NOT_FOUND; + + buffer.resize(size); + int bytesRead = fread(buffer.data(), 1, size, fileHandle); + + fclose(fileHandle); + + if (bytesRead <= 0) + return NPERR_FILE_NOT_FOUND; + + return NPERR_NO_ERROR; +} + +bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVToolkit: + *static_cast<uint32_t*>(value) = 0; + *result = NPERR_NO_ERROR; + return true; + + case NPNVSupportsXEmbedBool: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + + case NPNVjavascriptEnabledBool: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + + case NPNVSupportsWindowless: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + case NPNVSupportsWindowlessLocal: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; +#endif + + default: + return false; + } +} + +bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVxDisplay: + *(void **)value = QX11Info::display(); + *result = NPERR_NO_ERROR; + return true; + + case NPNVxtAppContext: + *result = NPERR_GENERIC_ERROR; + return true; + + case NPNVnetscapeWindow: { + void* w = reinterpret_cast<void*>(value); + QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); + *((XID *)w) = client ? client->ownerWidget()->window()->winId() : 0; + *result = NPERR_NO_ERROR; + return true; + } + + case NPNVToolkit: + if (m_plugin->quirks().contains(PluginQuirkRequiresGtkToolKit)) { + *((uint32_t *)value) = 2; + *result = NPERR_NO_ERROR; + return true; + } + return false; + + default: + return false; + } +} + +void PluginView::invalidateRect(const IntRect& rect) +{ +#if USE(ACCELERATED_COMPOSITING) && !USE(TEXTURE_MAPPER) + if (m_platformLayer) { + m_platformLayer->update(QRectF(rect)); + return; + } +#endif + + if (m_isWindowed) { + if (platformWidget()) + platformWidget()->update(rect); + return; + } + + invalidateWindowlessPluginRect(rect); +} + +void PluginView::invalidateRect(NPRect* rect) +{ + if (!rect) { + invalidate(); + return; + } + IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); + invalidateRect(r); +} + +void PluginView::invalidateRegion(NPRegion region) +{ + Q_UNUSED(region); + invalidate(); +} + +void PluginView::forceRedraw() +{ + invalidate(); +} + +static Display *getPluginDisplay() +{ + // The plugin toolkit might run using a different X connection. 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("libgdk-x11-2.0", 0); + if (!library.load()) + 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; + + return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default()); +} + +static void getVisualAndColormap(int depth, Visual **visual, Colormap *colormap) +{ + *visual = 0; + *colormap = 0; + +#ifndef QT_NO_XRENDER + static const bool useXRender = qgetenv("QT_X11_NO_XRENDER").isNull(); // Should also check for XRender >= 0.5 +#else + static const bool useXRender = false; +#endif + + if (!useXRender && depth == 32) + return; + + int nvi; + XVisualInfo templ; + templ.screen = QX11Info::appScreen(); + templ.depth = depth; + templ.c_class = TrueColor; + XVisualInfo* xvi = XGetVisualInfo(QX11Info::display(), VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi); + + if (!xvi) + return; + +#ifndef QT_NO_XRENDER + if (depth == 32) { + for (int idx = 0; idx < nvi; ++idx) { + XRenderPictFormat* format = XRenderFindVisualFormat(QX11Info::display(), xvi[idx].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask) { + *visual = xvi[idx].visual; + break; + } + } + } else +#endif // QT_NO_XRENDER + *visual = xvi[0].visual; + + XFree(xvi); + + if (*visual) + *colormap = XCreateColormap(QX11Info::display(), QX11Info::appRootWindow(), *visual, AllocNone); +} + +bool PluginView::platformStart() +{ + ASSERT(m_isStarted); + ASSERT(m_status == PluginStatusLoadedSuccessfully); + + if (m_plugin->pluginFuncs()->getvalue) { + PluginView::setCurrentPluginView(this); +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); +#endif + setCallingPlugin(true); + m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + } + + if (m_isWindowed) { + QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); + if (m_needsXEmbed && client) { + setPlatformWidget(new PluginContainerQt(this, client->ownerWidget())); + // sync our XEmbed container window creation before sending the xid to plugins. + QApplication::syncX(); + } else { + notImplemented(); + m_status = PluginStatusCanNotLoadPlugin; + return false; + } + } else { + setPlatformWidget(0); + m_pluginDisplay = getPluginDisplay(); + +#if USE(ACCELERATED_COMPOSITING) && !USE(TEXTURE_MAPPER) + if (m_parentFrame->page()->chrome()->client()->allowsAcceleratedCompositing() + && m_parentFrame->page()->settings() + && m_parentFrame->page()->settings()->acceleratedCompositingEnabled()) { + m_platformLayer = new PluginGraphicsLayerQt(this); + // Trigger layer computation in RenderLayerCompositor + m_element->setNeedsStyleRecalc(SyntheticStyleChange); + } +#endif + } + + show(); + + NPSetWindowCallbackStruct* wsi = new NPSetWindowCallbackStruct(); + wsi->type = 0; + + if (m_isWindowed) { + const QX11Info* x11Info = &platformPluginWidget()->x11Info(); + + wsi->display = x11Info->display(); + wsi->visual = (Visual*)x11Info->visual(); + wsi->depth = x11Info->depth(); + wsi->colormap = x11Info->colormap(); + + m_npWindow.type = NPWindowTypeWindow; + m_npWindow.window = (void*)platformPluginWidget()->winId(); + m_npWindow.width = -1; + m_npWindow.height = -1; + } else { + const QX11Info* x11Info = &QApplication::desktop()->x11Info(); + + if (x11Info->depth() == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) { + getVisualAndColormap(32, &m_visual, &m_colormap); + wsi->depth = 32; + } + + if (!m_visual) { + getVisualAndColormap(x11Info->depth(), &m_visual, &m_colormap); + wsi->depth = x11Info->depth(); + } + + wsi->display = x11Info->display(); + wsi->visual = m_visual; + wsi->colormap = m_colormap; + + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = 0; // Not used? + m_npWindow.x = 0; + m_npWindow.y = 0; + m_npWindow.width = -1; + m_npWindow.height = -1; + } + + m_npWindow.ws_info = wsi; + + if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) { + updatePluginWidget(); + setNPWindowIfNeeded(); + } + + return true; +} + +void PluginView::platformDestroy() +{ + if (platformPluginWidget()) + delete platformPluginWidget(); + + if (m_drawable) + XFreePixmap(QX11Info::display(), m_drawable); + + if (m_colormap) + XFreeColormap(QX11Info::display(), m_colormap); +} + +void PluginView::halt() +{ +} + +void PluginView::restart() +{ +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* PluginView::platformLayer() const +{ + return m_platformLayer.get(); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/plugins/symbian/PluginContainerSymbian.cpp b/Source/WebCore/plugins/symbian/PluginContainerSymbian.cpp new file mode 100644 index 0000000..b839870 --- /dev/null +++ b/Source/WebCore/plugins/symbian/PluginContainerSymbian.cpp @@ -0,0 +1,77 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "PluginContainerSymbian.h" + +#include "FocusController.h" +#include "Frame.h" +#include "FrameView.h" +#include "Page.h" +#include "PlatformKeyboardEvent.h" +#include "PluginView.h" + +#include <QApplication> +#include <QWidget> + +using namespace WebCore; + +PluginContainerSymbian::PluginContainerSymbian(PluginView* view, QWidget* parent, QGraphicsProxyWidget* proxy) + : QWidget(parent) + , m_pluginView(view) + , m_proxy(proxy) + , m_hasPendingGeometryChange(false) +{ +} + +PluginContainerSymbian::~PluginContainerSymbian() +{ +} + +void PluginContainerSymbian::requestGeometry(const QRect& rect, const QRegion& clip) +{ + if (m_windowRect != rect || m_clipRegion != clip) { + m_windowRect = rect; + m_clipRegion = clip; + m_hasPendingGeometryChange = true; + } +} + +void PluginContainerSymbian::adjustGeometry() +{ + if (m_hasPendingGeometryChange) { + setGeometry(m_windowRect); + setMask(m_clipRegion); + m_hasPendingGeometryChange = false; + } +} + +void PluginContainerSymbian::focusInEvent(QFocusEvent*) +{ + if (Page* page = m_pluginView->parentFrame()->page()) + page->focusController()->setActive(true); + + m_pluginView->focusPluginElement(); +} + +void PluginContainerSymbian::focusOutEvent(QFocusEvent*) +{ + if (Page* page = m_pluginView->parentFrame()->page()) + page->focusController()->setActive(false); +} diff --git a/Source/WebCore/plugins/symbian/PluginContainerSymbian.h b/Source/WebCore/plugins/symbian/PluginContainerSymbian.h new file mode 100644 index 0000000..fead872 --- /dev/null +++ b/Source/WebCore/plugins/symbian/PluginContainerSymbian.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef PluginContainerSymbian_h +#define PluginContainerSymbian_h + +#include <QWidget> + +class QGraphicsProxyWidget; + +namespace WebCore { + + class PluginView; + + class PluginContainerSymbian : public QWidget { + Q_OBJECT + public: + PluginContainerSymbian(PluginView*, QWidget* parent, QGraphicsProxyWidget* proxy = 0); + ~PluginContainerSymbian(); + + void requestGeometry(const QRect&, const QRegion& clip = QRegion()); + void adjustGeometry(); + QGraphicsProxyWidget* proxy() { return m_proxy; } + + protected: + virtual void focusInEvent(QFocusEvent*); + virtual void focusOutEvent(QFocusEvent*); + private: + PluginView* m_pluginView; + QGraphicsProxyWidget* m_proxy; + QRect m_windowRect; + QRegion m_clipRegion; + bool m_hasPendingGeometryChange; + }; +} + +#endif // PluginContainerSymbian_h diff --git a/Source/WebCore/plugins/symbian/PluginDatabaseSymbian.cpp b/Source/WebCore/plugins/symbian/PluginDatabaseSymbian.cpp new file mode 100644 index 0000000..2e09296 --- /dev/null +++ b/Source/WebCore/plugins/symbian/PluginDatabaseSymbian.cpp @@ -0,0 +1,79 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "config.h" +#include "PluginDatabase.h" + +#include <QFileInfo> +#include <f32file.h> + +static const char QTPLUGIN_FILTER[] = "*.qtplugin"; +static const char QT_PLUGIN_FOLDER[] = ":\\resource\\qt\\plugins\\npqtplugins\\"; + +namespace WebCore { + +Vector<String> PluginDatabase::defaultPluginDirectories() +{ + Vector<String> directories; + //find the installation drive + TDriveList drivelist; + TChar driveLetter; + RFs fsSession; + + if (fsSession.Connect() == KErrNone && fsSession.DriveList(drivelist) == KErrNone) { + for (TInt driveNumber = EDriveA; driveNumber <= EDriveZ; driveNumber++) { + if (drivelist[driveNumber] && fsSession.DriveToChar(driveNumber, driveLetter) == KErrNone) { + QString driveStringValue(QChar((uint)driveLetter.GetUpperCase())); + QString stubDirPath; + stubDirPath.append(driveStringValue); + stubDirPath.append(QT_PLUGIN_FOLDER); + if (QFileInfo(stubDirPath).exists()) + directories.append(stubDirPath); + } + } + } + + fsSession.Close(); + return directories; +} + +bool PluginDatabase::isPreferredPluginDirectory(const String& path) +{ + return true; +} + +void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const +{ + // FIXME: This should be a case insensitive set. + HashSet<String> uniqueFilenames; + + String fileNameFilter(QTPLUGIN_FILTER); + + Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end(); + for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) { + Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter); + Vector<String>::const_iterator pluginsEnd = pluginPaths.end(); + for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) { + if (!fileExists(*pIt)) + continue; + paths.add(*pIt); + } + } +} + +} diff --git a/Source/WebCore/plugins/symbian/PluginPackageSymbian.cpp b/Source/WebCore/plugins/symbian/PluginPackageSymbian.cpp new file mode 100644 index 0000000..7fca625 --- /dev/null +++ b/Source/WebCore/plugins/symbian/PluginPackageSymbian.cpp @@ -0,0 +1,182 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "config.h" +#include "PluginPackage.h" + +#include "MIMETypeRegistry.h" +#include "npinterface.h" +#include "npruntime_impl.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include <QPluginLoader> +#include <wtf/text/CString.h> + +namespace WebCore { + +bool PluginPackage::fetchInfo() +{ + if (!load()) + return false; + + char* buf = 0; + NPError err = m_pluginFuncs.getvalue(0, NPPVpluginNameString, (void *)&buf); + m_name = buf; + err = m_pluginFuncs.getvalue(0, NPPVpluginDescriptionString, (void *)&buf); + m_description = buf; + + determineModuleVersionFromDescription(); + + String s = m_npInterface->NP_GetMIMEDescription(); + Vector<String> types; + s.split(UChar('|'), false, types); // <MIME1>;<ext1,ext2,ext3,...>;<Description>|<MIME2>|<MIME3>|... + + for (int i = 0; i < types.size(); ++i) { + Vector<String> mime; + types[i].split(UChar(';'), true, mime); // <MIME1>;<ext1,ext2,ext3,...>;<Description> + if (mime.size() > 0) { + Vector<String> exts; + if (mime.size() > 1) + mime[1].split(UChar(','), false, exts); // <ext1,ext2,ext3,...> + + m_mimeToExtensions.add(mime[0], exts); // <MIME>,<ext1,ext2,ext3> + if (mime.size() > 2) + m_mimeToDescriptions.add(mime[0], mime[2]); // <MIME>,<Description> + } + } + unload(); + return true; +} + +bool PluginPackage::load() +{ + if (m_isLoaded) { + m_loadCount++; + return true; + } + + m_pluginLoader = new QPluginLoader(m_path); + if (!m_pluginLoader->load()) { + delete m_pluginLoader; + m_pluginLoader = 0; + return false; + } + + QObject* plugin = m_pluginLoader->instance(); + if (!plugin) { + m_pluginLoader->unload(); + delete m_pluginLoader; + m_pluginLoader = 0; + return false; + } + + // Plugin instance created + // Cast plugin to NPInterface, + m_npInterface = qobject_cast<NPInterface*>(plugin); + if (!m_npInterface) { + m_pluginLoader->unload(); + delete m_pluginLoader; + m_pluginLoader = 0; + return false; + } + + m_isLoaded = true; + + NPError npErr; + memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); + m_pluginFuncs.size = sizeof(m_pluginFuncs); + m_browserFuncs.size = sizeof(m_browserFuncs); + m_browserFuncs.version = NP_VERSION_MINOR; + m_browserFuncs.geturl = NPN_GetURL; + m_browserFuncs.posturl = NPN_PostURL; + m_browserFuncs.requestread = NPN_RequestRead; + m_browserFuncs.newstream = NPN_NewStream; + m_browserFuncs.write = NPN_Write; + m_browserFuncs.destroystream = NPN_DestroyStream; + m_browserFuncs.status = NPN_Status; + m_browserFuncs.uagent = NPN_UserAgent; + m_browserFuncs.memalloc = NPN_MemAlloc; + m_browserFuncs.memfree = NPN_MemFree; + m_browserFuncs.memflush = NPN_MemFlush; + m_browserFuncs.reloadplugins = NPN_ReloadPlugins; + m_browserFuncs.geturlnotify = NPN_GetURLNotify; + m_browserFuncs.posturlnotify = NPN_PostURLNotify; + m_browserFuncs.getvalue = NPN_GetValue; + m_browserFuncs.setvalue = NPN_SetValue; + m_browserFuncs.invalidaterect = NPN_InvalidateRect; + m_browserFuncs.invalidateregion = NPN_InvalidateRegion; + m_browserFuncs.forceredraw = NPN_ForceRedraw; + m_browserFuncs.getJavaEnv = NPN_GetJavaEnv; + m_browserFuncs.getJavaPeer = NPN_GetJavaPeer; + m_browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState; + m_browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState; + m_browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue; + m_browserFuncs.getstringidentifier = _NPN_GetStringIdentifier; + m_browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers; + m_browserFuncs.getintidentifier = _NPN_GetIntIdentifier; + m_browserFuncs.identifierisstring = _NPN_IdentifierIsString; + m_browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier; + m_browserFuncs.createobject = _NPN_CreateObject; + m_browserFuncs.retainobject = _NPN_RetainObject; + m_browserFuncs.releaseobject = _NPN_ReleaseObject; + m_browserFuncs.invoke = _NPN_Invoke; + m_browserFuncs.invokeDefault = _NPN_InvokeDefault; + m_browserFuncs.evaluate = _NPN_Evaluate; + m_browserFuncs.getproperty = _NPN_GetProperty; + m_browserFuncs.setproperty = _NPN_SetProperty; + m_browserFuncs.removeproperty = _NPN_RemoveProperty; + m_browserFuncs.hasproperty = _NPN_HasMethod; + m_browserFuncs.hasmethod = _NPN_HasProperty; + m_browserFuncs.setexception = _NPN_SetException; + m_browserFuncs.enumerate = _NPN_Enumerate; + m_browserFuncs.construct = _NPN_Construct; + + npErr = m_npInterface->NP_Initialize(&m_browserFuncs, &m_pluginFuncs); + if (npErr != NPERR_NO_ERROR) { + m_pluginLoader->unload(); + delete m_pluginLoader; + m_pluginLoader = 0; + return false; + } + + m_loadCount++; + return true; +} + +void PluginPackage::unload() +{ + if (!m_isLoaded) + return; + + if (--m_loadCount > 0) + return; + + m_isLoaded = false; + m_npInterface->NP_Shutdown(); + + m_pluginLoader->unload(); + delete m_pluginLoader; + m_pluginLoader = 0; +} + +uint16_t PluginPackage::NPVersion() const +{ + return NP_VERSION_MINOR; +} +} + diff --git a/Source/WebCore/plugins/symbian/PluginViewSymbian.cpp b/Source/WebCore/plugins/symbian/PluginViewSymbian.cpp new file mode 100644 index 0000000..b8a72b1 --- /dev/null +++ b/Source/WebCore/plugins/symbian/PluginViewSymbian.cpp @@ -0,0 +1,441 @@ +/* + Copyright (C) 2009, 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "config.h" +#include "PluginView.h" + +#include "Bridge.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "HostWindow.h" +#include "Image.h" +#include "JSDOMBinding.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "NotImplemented.h" +#include "Page.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformMouseEvent.h" +#include "PluginContainerSymbian.h" +#include "PluginDebug.h" +#include "PluginMainThreadScheduler.h" +#include "PluginPackage.h" +#include "QWebPageClient.h" +#include "RenderLayer.h" +#include "ScriptController.h" +#include "Settings.h" +#include "npfunctions.h" +#include "npinterface.h" +#include "npruntime_impl.h" +#include "qgraphicswebview.h" +#include "runtime_root.h" +#include <QGraphicsProxyWidget> +#include <QKeyEvent> +#include <QPixmap> +#include <QRegion> +#include <QVector> +#include <QWidget> +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> + +using JSC::ExecState; +using JSC::Interpreter; +using JSC::JSLock; +using JSC::JSObject; +using JSC::UString; + +using namespace std; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +void PluginView::updatePluginWidget() +{ + if (!parent()) + return; + ASSERT(parent()->isFrameView()); + FrameView* frameView = static_cast<FrameView*>(parent()); + IntRect oldWindowRect = m_windowRect; + IntRect oldClipRect = m_clipRect; + + m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); + + m_clipRect = windowClipRect(); + m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); + if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect) + return; + + // in order to move/resize the plugin window at the same time as the rest of frame + // during e.g. scrolling, we set the mask and geometry in the paint() function, but + // as paint() isn't called when the plugin window is outside the frame which can + // be caused by a scroll, we need to move/resize immediately. + if (!m_windowRect.intersects(frameView->frameRect())) + setNPWindowIfNeeded(); +} + +void PluginView::setFocus(bool focused) +{ + if (platformPluginWidget()) { + if (focused) + platformPluginWidget()->setFocus(Qt::OtherFocusReason); + } else { + Widget::setFocus(focused); + } +} + +void PluginView::show() +{ + setSelfVisible(true); + + if (isParentVisible() && platformPluginWidget()) + platformPluginWidget()->setVisible(true); +} + +void PluginView::hide() +{ + setSelfVisible(false); + + if (isParentVisible() && platformPluginWidget()) + platformPluginWidget()->setVisible(false); +} + +void PluginView::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_isStarted) { + paintMissingPluginIcon(context, rect); + return; + } + + if (context->paintingDisabled()) + return; + m_npWindow.ws_info = (void*)(context->platformContext()); + setNPWindowIfNeeded(); + + if (m_isWindowed && platformPluginWidget()) + static_cast<PluginContainerSymbian*>(platformPluginWidget())->adjustGeometry(); + + if (m_isWindowed) + return; + + context->save(); + IntRect clipRect(rect); + clipRect.intersect(frameRect()); + context->clip(clipRect); + context->translate(frameRect().location().x(), frameRect().location().y()); + + QPaintEvent ev(rect); + QEvent& npEvent = ev; + dispatchNPEvent(npEvent); + + context->restore(); +} + +// TODO: Unify across ports. +bool PluginView::dispatchNPEvent(NPEvent& event) +{ + if (!m_plugin->pluginFuncs()->event) + return false; + + PluginView::setCurrentPluginView(this); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + + setCallingPlugin(true); + bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); + + return accepted; +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + if (m_isWindowed) + return; + + ASSERT(event->keyEvent()->qtEvent()); + QEvent& npEvent = *(event->keyEvent()->qtEvent()); + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); +} + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + if (m_isWindowed) + return; + + if (event->type() == eventNames().mousedownEvent) { + // Give focus to the plugin on click + if (Page* page = m_parentFrame->page()) + page->focusController()->setActive(true); + + focusPluginElement(); + } + + QEvent::Type type; + if (event->type() == eventNames().mousedownEvent) + type = QEvent::MouseButtonPress; + else if (event->type() == eventNames().mousemoveEvent) + type = QEvent::MouseMove; + else if (event->type() == eventNames().mouseupEvent) + type = QEvent::MouseButtonRelease; + else + return; + + QPoint position(event->offsetX(), event->offsetY()); + Qt::MouseButton button; + switch (event->which()) { + case 1: + button = Qt::LeftButton; + break; + case 2: + button = Qt::MidButton; + break; + case 3: + button = Qt::RightButton; + break; + default: + button = Qt::NoButton; + } + Qt::KeyboardModifiers modifiers = 0; + if (event->ctrlKey()) + modifiers |= Qt::ControlModifier; + if (event->altKey()) + modifiers |= Qt::AltModifier; + if (event->shiftKey()) + modifiers |= Qt::ShiftModifier; + if (event->metaKey()) + modifiers |= Qt::MetaModifier; + QMouseEvent mouseEvent(type, position, button, button, modifiers); + QEvent& npEvent = mouseEvent; + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); +} + +void PluginView::setParent(ScrollView* parent) +{ + Widget::setParent(parent); + + if (parent) { + init(); + if (m_status == PluginStatusLoadedSuccessfully) + updatePluginWidget(); + } +} + +void PluginView::setNPWindowRect(const IntRect&) +{ + if (!m_isWindowed) + setNPWindowIfNeeded(); +} + +void PluginView::setNPWindowIfNeeded() +{ + if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) + return; + if (m_isWindowed) { + ASSERT(platformPluginWidget()); + platformPluginWidget()->setGeometry(m_windowRect); + // if setMask is set with an empty QRegion, no clipping will + // be performed, so in that case we hide the plugin view + platformPluginWidget()->setVisible(!m_clipRect.isEmpty()); + platformPluginWidget()->setMask(QRegion(m_clipRect)); + + m_npWindow.x = m_windowRect.x(); + m_npWindow.y = m_windowRect.y(); + + m_npWindow.clipRect.left = max(0, m_clipRect.x()); + m_npWindow.clipRect.top = max(0, m_clipRect.y()); + m_npWindow.clipRect.right = m_clipRect.x() + m_clipRect.width(); + m_npWindow.clipRect.bottom = m_clipRect.y() + m_clipRect.height(); + + } else { + // always call this method before painting. + m_npWindow.x = 0; + m_npWindow.y = 0; + + m_npWindow.clipRect.left = 0; + m_npWindow.clipRect.top = 0; + m_npWindow.clipRect.right = m_windowRect.width(); + m_npWindow.clipRect.bottom = m_windowRect.height(); + m_npWindow.window = 0; + } + + m_npWindow.width = m_windowRect.width(); + m_npWindow.height = m_windowRect.height(); + + PluginView::setCurrentPluginView(this); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + PluginView::setCurrentPluginView(0); +} + +void PluginView::setParentVisible(bool visible) +{ + if (isParentVisible() == visible) + return; + + Widget::setParentVisible(visible); + + if (isSelfVisible() && platformPluginWidget()) + platformPluginWidget()->setVisible(visible); +} + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) +{ + notImplemented(); + return NPERR_NO_ERROR; +} + +bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVjavascriptEnabledBool: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + + case NPNVSupportsWindowless: + *static_cast<NPBool*>(value) = true; + *result = NPERR_NO_ERROR; + return true; + + default: + return false; + } +} + +bool PluginView::platformGetValue(NPNVariable, void*, NPError*) +{ + return false; +} + +void PluginView::invalidateRect(const IntRect& rect) +{ + if (m_isWindowed) { + platformWidget()->update(rect); + return; + } + + invalidateWindowlessPluginRect(rect); +} + +void PluginView::invalidateRect(NPRect* rect) +{ + if (m_isWindowed) + return; + if (!rect) { + invalidate(); + return; + } + IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); + m_invalidRects.append(r); + if (!m_invalidateTimer.isActive()) + m_invalidateTimer.startOneShot(0.001); +} + +void PluginView::invalidateRegion(NPRegion region) +{ + if (m_isWindowed) + return; + + if (!region) + return; + + QVector<QRect> rects = region->rects(); + for (int i = 0; i < rects.size(); ++i) { + const QRect& qRect = rects.at(i); + m_invalidRects.append(qRect); + if (!m_invalidateTimer.isActive()) + m_invalidateTimer.startOneShot(0.001); + } +} + +void PluginView::forceRedraw() +{ + if (m_isWindowed) + return; + invalidate(); +} + +bool PluginView::platformStart() +{ + ASSERT(m_isStarted); + ASSERT(m_status == PluginStatusLoadedSuccessfully); + + show(); + + if (m_isWindowed) { + QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); + QGraphicsProxyWidget* proxy = 0; + if (QGraphicsWebView *webView = qobject_cast<QGraphicsWebView*>(client->pluginParent())) + proxy = new QGraphicsProxyWidget(webView); + + PluginContainerSymbian* container = new PluginContainerSymbian(this, proxy ? 0 : client->ownerWidget(), proxy); + setPlatformWidget(container); + if (proxy) + proxy->setWidget(container); + + m_npWindow.type = NPWindowTypeWindow; + m_npWindow.window = (void*)platformPluginWidget(); + + } else { + setPlatformWidget(0); + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = 0; // Not used? + } + updatePluginWidget(); + setNPWindowIfNeeded(); + + return true; +} + +void PluginView::platformDestroy() +{ + if (platformPluginWidget()) { + PluginContainerSymbian* container = static_cast<PluginContainerSymbian*>(platformPluginWidget()); + if (container && container->proxy()) + delete container->proxy(); + else + delete container; + } +} + +void PluginView::halt() +{ +} + +void PluginView::restart() +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/symbian/npinterface.h b/Source/WebCore/plugins/symbian/npinterface.h new file mode 100644 index 0000000..e296127 --- /dev/null +++ b/Source/WebCore/plugins/symbian/npinterface.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef npinterface_h +#define npinterface_h + +#include "npfunctions.h" +#include <QtPlugin> + +class NPInterface { +public: + virtual NPError NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs) = 0; + virtual void NP_Shutdown() = 0; + virtual char* NP_GetMIMEDescription() = 0; +}; + + +QT_BEGIN_NAMESPACE +Q_DECLARE_INTERFACE(NPInterface, "com.nokia.qts60.webplugin/1.0"); +QT_END_NAMESPACE + +#endif // npinterface_h diff --git a/Source/WebCore/plugins/win/PaintHooks.asm b/Source/WebCore/plugins/win/PaintHooks.asm new file mode 100644 index 0000000..1508813 --- /dev/null +++ b/Source/WebCore/plugins/win/PaintHooks.asm @@ -0,0 +1,50 @@ +;/* +; Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) +; +; This library is free software; you can redistribute it and/or +; modify it under the terms of the GNU Library General Public +; License as published by the Free Software Foundation; either +; version 2 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Library General Public License for more details. +; +; You should have received a copy of the GNU Library General Public License +; along with this library; see the file COPYING.LIB. If not, write to +; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +; Boston, MA 02110-1301, USA. +;*/ + +;HDC __stdcall _HBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint); + +PUBLIC _HBeginPaint + +_TEXT SEGMENT + +_HBeginPaint PROC + mov r10,rcx + mov eax,1017h + syscall + ret +_HBeginPaint ENDP + +_TEXT ENDS + +;BOOL __stdcall _HEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint); + +PUBLIC _HEndPaint + +_TEXT SEGMENT + +_HEndPaint PROC + mov r10,rcx + mov eax,1019h + syscall + ret +_HEndPaint ENDP + +_TEXT ENDS + +END diff --git a/Source/WebCore/plugins/win/PluginDatabaseWin.cpp b/Source/WebCore/plugins/win/PluginDatabaseWin.cpp new file mode 100644 index 0000000..27121c6 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginDatabaseWin.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * Copyright (C) 2008-2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginDatabase.h" + +#include "Frame.h" +#include "KURL.h" +#include "PluginPackage.h" +#include <windows.h> +#include <shlwapi.h> + +#if OS(WINCE) +// WINCE doesn't support Registry Key Access Rights. The parameter should always be 0 +#ifndef KEY_ENUMERATE_SUB_KEYS +#define KEY_ENUMERATE_SUB_KEYS 0 +#endif + +DWORD SHGetValue(HKEY hkey, LPCWSTR pszSubKey, LPCWSTR pszValue, LPDWORD pdwType, LPVOID pvData, LPDWORD pcbData) +{ + HKEY key; + if (RegOpenKeyEx(hkey, pszSubKey, 0, 0, &key) == ERROR_SUCCESS) { + DWORD result = RegQueryValueEx(key, pszValue, 0, pdwType, (LPBYTE)pvData, pcbData); + RegCloseKey(key); + return result; + } + return ERROR_INVALID_NAME; +} + +BOOL PathRemoveFileSpec(LPWSTR moduleFileNameStr) +{ + if (!*moduleFileNameStr) + return FALSE; + + LPWSTR lastPos = 0; + LPWSTR curPos = moduleFileNameStr; + do { + if (*curPos == L'/' || *curPos == L'\\') + lastPos = curPos; + } while (*++curPos); + + if (lastPos == curPos - 1) + return FALSE; + + if (lastPos) + *lastPos = 0; + else { + moduleFileNameStr[0] = L'\\'; + moduleFileNameStr[1] = 0; + } + + return TRUE; +} +#endif + +namespace WebCore { + +static inline void addPluginPathsFromRegistry(HKEY rootKey, HashSet<String>& paths) +{ + HKEY key; + HRESULT result = RegOpenKeyExW(rootKey, L"Software\\MozillaPlugins", 0, KEY_ENUMERATE_SUB_KEYS, &key); + + if (result != ERROR_SUCCESS) + return; + + wchar_t name[128]; + FILETIME lastModified; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = WTF_ARRAY_LENGTH(name); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + WCHAR pathStr[_MAX_PATH]; + DWORD pathStrSize = sizeof(pathStr); + DWORD type; + + result = SHGetValue(key, name, TEXT("Path"), &type, (LPBYTE)pathStr, &pathStrSize); + if (result != ERROR_SUCCESS || type != REG_SZ) + continue; + + paths.add(String(pathStr, pathStrSize / sizeof(WCHAR) - 1)); + } + + RegCloseKey(key); +} + +void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const +{ + // FIXME: This should be a case insensitive set. + HashSet<String> uniqueFilenames; + + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAW findFileData; + + String oldWMPPluginPath; + String newWMPPluginPath; + + Vector<String>::const_iterator end = m_pluginDirectories.end(); + for (Vector<String>::const_iterator it = m_pluginDirectories.begin(); it != end; ++it) { + String pattern = *it + "\\*"; + + hFind = FindFirstFileW(pattern.charactersWithNullTermination(), &findFileData); + + if (hFind == INVALID_HANDLE_VALUE) + continue; + + do { + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; + + String filename = String(findFileData.cFileName, wcslen(findFileData.cFileName)); + if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) && + (!equalIgnoringCase(filename, "Plugin.dll") || !it->endsWith("Shockwave 10", false))) + continue; + + String fullPath = *it + "\\" + filename; + if (!uniqueFilenames.add(fullPath).second) + continue; + + paths.add(fullPath); + + if (equalIgnoringCase(filename, "npdsplay.dll")) + oldWMPPluginPath = fullPath; + else if (equalIgnoringCase(filename, "np-mswmp.dll")) + newWMPPluginPath = fullPath; + + } while (FindNextFileW(hFind, &findFileData) != 0); + + FindClose(hFind); + } + + addPluginPathsFromRegistry(HKEY_LOCAL_MACHINE, paths); + addPluginPathsFromRegistry(HKEY_CURRENT_USER, paths); + + // If both the old and new WMP plugin are present in the plugins set, + // we remove the old one so we don't end up choosing the old one. + if (!oldWMPPluginPath.isEmpty() && !newWMPPluginPath.isEmpty()) + paths.remove(oldWMPPluginPath); +} + +static inline Vector<int> parseVersionString(const String& versionString) +{ + Vector<int> version; + + unsigned startPos = 0; + unsigned endPos; + + while (startPos < versionString.length()) { + for (endPos = startPos; endPos < versionString.length(); ++endPos) + if (versionString[endPos] == '.' || versionString[endPos] == '_') + break; + + int versionComponent = versionString.substring(startPos, endPos - startPos).toInt(); + version.append(versionComponent); + + startPos = endPos + 1; + } + + return version; +} + +// This returns whether versionA is higher than versionB +static inline bool compareVersions(const Vector<int>& versionA, const Vector<int>& versionB) +{ + for (unsigned i = 0; i < versionA.size(); i++) { + if (i >= versionB.size()) + return true; + + if (versionA[i] > versionB[i]) + return true; + else if (versionA[i] < versionB[i]) + return false; + } + + // If we come here, the versions are either the same or versionB has an extra component, just return false + return false; +} + +static inline void addMozillaPluginDirectories(Vector<String>& directories) +{ + // Enumerate all Mozilla plugin directories in the registry + HKEY key; + LONG result; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Mozilla"), 0, KEY_READ, &key); + if (result == ERROR_SUCCESS) { + WCHAR name[128]; + FILETIME lastModified; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = sizeof(name) / sizeof(WCHAR); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + String extensionsPath = String(name, nameLen) + "\\Extensions"; + HKEY extensionsKey; + + // Try opening the key + result = RegOpenKeyEx(key, extensionsPath.charactersWithNullTermination(), 0, KEY_READ, &extensionsKey); + + if (result == ERROR_SUCCESS) { + // Now get the plugins directory + WCHAR pluginsDirectoryStr[_MAX_PATH]; + DWORD pluginsDirectorySize = sizeof(pluginsDirectoryStr); + DWORD type; + + result = RegQueryValueEx(extensionsKey, TEXT("Plugins"), 0, &type, (LPBYTE)&pluginsDirectoryStr, &pluginsDirectorySize); + + if (result == ERROR_SUCCESS && type == REG_SZ) + directories.append(String(pluginsDirectoryStr, pluginsDirectorySize / sizeof(WCHAR) - 1)); + + RegCloseKey(extensionsKey); + } + } + + RegCloseKey(key); + } +} + +static inline void addWindowsMediaPlayerPluginDirectory(Vector<String>& directories) +{ +#if !OS(WINCE) + // The new WMP Firefox plugin is installed in \PFiles\Plugins if it can't find any Firefox installs + WCHAR pluginDirectoryStr[_MAX_PATH + 1]; + DWORD pluginDirectorySize = ::ExpandEnvironmentStringsW(TEXT("%SYSTEMDRIVE%\\PFiles\\Plugins"), pluginDirectoryStr, WTF_ARRAY_LENGTH(pluginDirectoryStr)); + + if (pluginDirectorySize > 0 && pluginDirectorySize <= WTF_ARRAY_LENGTH(pluginDirectoryStr)) + directories.append(String(pluginDirectoryStr, pluginDirectorySize - 1)); +#endif + + DWORD type; + WCHAR installationDirectoryStr[_MAX_PATH]; + DWORD installationDirectorySize = sizeof(installationDirectoryStr); + + HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\MediaPlayer"), TEXT("Installation Directory"), &type, (LPBYTE)&installationDirectoryStr, &installationDirectorySize); + + if (result == ERROR_SUCCESS && type == REG_SZ) + directories.append(String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1)); +} + +static inline void addQuickTimePluginDirectory(Vector<String>& directories) +{ + DWORD type; + WCHAR installationDirectoryStr[_MAX_PATH]; + DWORD installationDirectorySize = sizeof(installationDirectoryStr); + + HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Apple Computer, Inc.\\QuickTime"), TEXT("InstallDir"), &type, (LPBYTE)&installationDirectoryStr, &installationDirectorySize); + + if (result == ERROR_SUCCESS && type == REG_SZ) { + String pluginDir = String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1) + "\\plugins"; + directories.append(pluginDir); + } +} + +static inline void addAdobeAcrobatPluginDirectory(Vector<String>& directories) +{ + HKEY key; + HRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Adobe\\Acrobat Reader"), 0, KEY_READ, &key); + if (result != ERROR_SUCCESS) + return; + + WCHAR name[128]; + FILETIME lastModified; + + Vector<int> latestAcrobatVersion; + String latestAcrobatVersionString; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = sizeof(name) / sizeof(WCHAR); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + Vector<int> acrobatVersion = parseVersionString(String(name, nameLen)); + if (compareVersions(acrobatVersion, latestAcrobatVersion)) { + latestAcrobatVersion = acrobatVersion; + latestAcrobatVersionString = String(name, nameLen); + } + } + + if (!latestAcrobatVersionString.isNull()) { + DWORD type; + WCHAR acrobatInstallPathStr[_MAX_PATH]; + DWORD acrobatInstallPathSize = sizeof(acrobatInstallPathStr); + + String acrobatPluginKeyPath = "Software\\Adobe\\Acrobat Reader\\" + latestAcrobatVersionString + "\\InstallPath"; + result = SHGetValue(HKEY_LOCAL_MACHINE, acrobatPluginKeyPath.charactersWithNullTermination(), 0, &type, (LPBYTE)acrobatInstallPathStr, &acrobatInstallPathSize); + + if (result == ERROR_SUCCESS) { + String acrobatPluginDirectory = String(acrobatInstallPathStr, acrobatInstallPathSize / sizeof(WCHAR) - 1) + "\\browser"; + directories.append(acrobatPluginDirectory); + } + } + + RegCloseKey(key); +} + +static inline void addJavaPluginDirectory(Vector<String>& directories) +{ + HKEY key; + HRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\JavaSoft\\Java Plug-in"), 0, KEY_READ, &key); + if (result != ERROR_SUCCESS) + return; + + WCHAR name[128]; + FILETIME lastModified; + + Vector<int> latestJavaVersion; + String latestJavaVersionString; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = sizeof(name) / sizeof(WCHAR); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + Vector<int> javaVersion = parseVersionString(String(name, nameLen)); + if (compareVersions(javaVersion, latestJavaVersion)) { + latestJavaVersion = javaVersion; + latestJavaVersionString = String(name, nameLen); + } + } + + if (!latestJavaVersionString.isEmpty()) { + DWORD type; + WCHAR javaInstallPathStr[_MAX_PATH]; + DWORD javaInstallPathSize = sizeof(javaInstallPathStr); + DWORD useNewPluginValue; + DWORD useNewPluginSize; + + String javaPluginKeyPath = "Software\\JavaSoft\\Java Plug-in\\" + latestJavaVersionString; + result = SHGetValue(HKEY_LOCAL_MACHINE, javaPluginKeyPath.charactersWithNullTermination(), TEXT("UseNewJavaPlugin"), &type, (LPVOID)&useNewPluginValue, &useNewPluginSize); + + if (result == ERROR_SUCCESS && useNewPluginValue == 1) { + result = SHGetValue(HKEY_LOCAL_MACHINE, javaPluginKeyPath.charactersWithNullTermination(), TEXT("JavaHome"), &type, (LPBYTE)javaInstallPathStr, &javaInstallPathSize); + if (result == ERROR_SUCCESS) { + String javaPluginDirectory = String(javaInstallPathStr, javaInstallPathSize / sizeof(WCHAR) - 1) + "\\bin\\new_plugin"; + directories.append(javaPluginDirectory); + } + } + } + + RegCloseKey(key); +} + +static inline String safariPluginsDirectory() +{ + WCHAR moduleFileNameStr[_MAX_PATH]; + static String pluginsDirectory; + static bool cachedPluginDirectory = false; + + if (!cachedPluginDirectory) { + cachedPluginDirectory = true; + + int moduleFileNameLen = GetModuleFileName(0, moduleFileNameStr, _MAX_PATH); + + if (!moduleFileNameLen || moduleFileNameLen == _MAX_PATH) + goto exit; + + if (!PathRemoveFileSpec(moduleFileNameStr)) + goto exit; + + pluginsDirectory = String(moduleFileNameStr) + "\\Plugins"; + } +exit: + return pluginsDirectory; +} + +static inline void addMacromediaPluginDirectories(Vector<String>& directories) +{ +#if !OS(WINCE) + WCHAR systemDirectoryStr[MAX_PATH]; + + if (!GetSystemDirectory(systemDirectoryStr, WTF_ARRAY_LENGTH(systemDirectoryStr))) + return; + + WCHAR macromediaDirectoryStr[MAX_PATH]; + + PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Flash")); + directories.append(macromediaDirectoryStr); + + PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Shockwave 10")); + directories.append(macromediaDirectoryStr); +#endif +} + +Vector<String> PluginDatabase::defaultPluginDirectories() +{ + Vector<String> directories; + String ourDirectory = safariPluginsDirectory(); + + if (!ourDirectory.isNull()) + directories.append(ourDirectory); + addQuickTimePluginDirectory(directories); + addAdobeAcrobatPluginDirectory(directories); + addMozillaPluginDirectories(directories); + addWindowsMediaPlayerPluginDirectory(directories); + addMacromediaPluginDirectories(directories); +#if PLATFORM(QT) + addJavaPluginDirectory(directories); +#endif + + return directories; +} + +bool PluginDatabase::isPreferredPluginDirectory(const String& directory) +{ + String ourDirectory = safariPluginsDirectory(); + + if (!ourDirectory.isNull() && !directory.isNull()) + return ourDirectory == directory; + + return false; +} + +} diff --git a/Source/WebCore/plugins/win/PluginMessageThrottlerWin.cpp b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.cpp new file mode 100644 index 0000000..c5f3081 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginMessageThrottlerWin.h" + +#include "PluginView.h" +#include <wtf/ASCIICType.h> +#include <wtf/CurrentTime.h> + +using namespace WTF; + +namespace WebCore { + +// Set a timer to make sure we process any queued messages at least every 16ms. +// This value allows Flash 60 messages/second, which should be enough for video +// playback, and also gets us over the limit for kicking into high-resolution +// timer mode (see SharedTimerWin.cpp). +static const double MessageThrottleTimeInterval = 0.016; + +// During a continuous stream of messages, process one every 5ms. +static const double MessageDirectProcessingInterval = 0.005; + +PluginMessageThrottlerWin::PluginMessageThrottlerWin(PluginView* pluginView) + : m_pluginView(pluginView) + , m_back(0) + , m_front(0) + , m_messageThrottleTimer(this, &PluginMessageThrottlerWin::messageThrottleTimerFired) + , m_lastMessageTime(0) +{ + // Initialize the free list with our inline messages + for (unsigned i = 0; i < NumInlineMessages - 1; i++) + m_inlineMessages[i].next = &m_inlineMessages[i + 1]; + m_inlineMessages[NumInlineMessages - 1].next = 0; + m_freeInlineMessages = &m_inlineMessages[0]; +} + +PluginMessageThrottlerWin::~PluginMessageThrottlerWin() +{ + PluginMessage* next; + + for (PluginMessage* message = m_front; message; message = next) { + next = message->next; + freeMessage(message); + } +} + +void PluginMessageThrottlerWin::appendMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + PluginMessage* message = allocateMessage(); + + message->hWnd = hWnd; + message->msg = msg; + message->wParam = wParam; + message->lParam = lParam; + message->next = 0; + + if (m_back) + m_back->next = message; + m_back = message; + if (!m_front) + m_front = message; + + // If it has been more than MessageDirectProcessingInterval between throttled messages, + // go ahead and process a message directly. + double currentTime = WTF::currentTime(); + if (currentTime - m_lastMessageTime > MessageDirectProcessingInterval) { + processQueuedMessage(); + m_lastMessageTime = currentTime; + if (!m_front) + return; + } + + if (!m_messageThrottleTimer.isActive()) + m_messageThrottleTimer.startOneShot(MessageThrottleTimeInterval); +} + +void PluginMessageThrottlerWin::processQueuedMessage() +{ + PluginMessage* message = m_front; + m_front = m_front->next; + if (message == m_back) + m_back = 0; + + // Protect the PluginView from destruction while calling its window proc. + // <rdar://problem/6930280> + RefPtr<PluginView> protect(m_pluginView); + ::CallWindowProc(m_pluginView->pluginWndProc(), message->hWnd, message->msg, message->wParam, message->lParam); + + freeMessage(message); +} + +void PluginMessageThrottlerWin::messageThrottleTimerFired(Timer<PluginMessageThrottlerWin>*) +{ + processQueuedMessage(); + + if (m_front) + m_messageThrottleTimer.startOneShot(MessageThrottleTimeInterval); +} + +PluginMessage* PluginMessageThrottlerWin::allocateMessage() +{ + PluginMessage *message; + + if (m_freeInlineMessages) { + message = m_freeInlineMessages; + m_freeInlineMessages = message->next; + } else + message = new PluginMessage; + + return message; +} + +bool PluginMessageThrottlerWin::isInlineMessage(PluginMessage* message) +{ + return message >= &m_inlineMessages[0] && message <= &m_inlineMessages[NumInlineMessages - 1]; +} + +void PluginMessageThrottlerWin::freeMessage(PluginMessage* message) +{ + if (isInlineMessage(message)) { + message->next = m_freeInlineMessages; + m_freeInlineMessages = message; + } else + delete message; +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/win/PluginMessageThrottlerWin.h b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.h new file mode 100644 index 0000000..0a7be70 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginMessageThrottlerWin_h +#define PluginMessageThrottlerWin_h + +#include "Timer.h" + +#include <windows.h> + +namespace WebCore { + class PluginView; + + struct PluginMessage { + HWND hWnd; + UINT msg; + WPARAM wParam; + LPARAM lParam; + + struct PluginMessage* next; + }; + + class PluginMessageThrottlerWin { + public: + PluginMessageThrottlerWin(PluginView*); + ~PluginMessageThrottlerWin(); + + void appendMessage(HWND, UINT msg, WPARAM, LPARAM); + + private: + void processQueuedMessage(); + void messageThrottleTimerFired(Timer<PluginMessageThrottlerWin>*); + PluginMessage* allocateMessage(); + bool isInlineMessage(PluginMessage* message); + void freeMessage(PluginMessage* message); + + PluginView* m_pluginView; + PluginMessage* m_back; + PluginMessage* m_front; + + static const int NumInlineMessages = 4; + PluginMessage m_inlineMessages[NumInlineMessages]; + PluginMessage* m_freeInlineMessages; + + Timer<PluginMessageThrottlerWin> m_messageThrottleTimer; + double m_lastMessageTime; + }; + +} // namespace WebCore + +#endif // PluginMessageThrottlerWin_h diff --git a/Source/WebCore/plugins/win/PluginPackageWin.cpp b/Source/WebCore/plugins/win/PluginPackageWin.cpp new file mode 100644 index 0000000..74bd2a9 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginPackageWin.cpp @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "PluginPackage.h" + +#include "MIMETypeRegistry.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include "Timer.h" +#include "npruntime_impl.h" +#include <string.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/text/CString.h> +#include <shlwapi.h> + +namespace WebCore { + +static String getVersionInfo(const LPVOID versionInfoData, const String& info) +{ + LPVOID buffer; + UINT bufferLength; + String subInfo = "\\StringfileInfo\\040904E4\\" + info; + bool retval = VerQueryValueW(versionInfoData, + const_cast<UChar*>(subInfo.charactersWithNullTermination()), + &buffer, &bufferLength); + if (!retval || bufferLength == 0) + return String(); + + // Subtract 1 from the length; we don't want the trailing null character. + return String(reinterpret_cast<UChar*>(buffer), bufferLength - 1); +} + +bool PluginPackage::isPluginBlacklisted() +{ + if (name() == "Citrix ICA Client") { + // The Citrix ICA Client plug-in requires a Mozilla-based browser; see <rdar://6418681>. + return true; + } + + if (name() == "Silverlight Plug-In") { + // workaround for <rdar://5557379> Crash in Silverlight when opening microsoft.com. + // the latest 1.0 version of Silverlight does not reproduce this crash, so allow it + // and any newer versions + static const PlatformModuleVersion slPluginMinRequired(0x51BE0000, 0x00010000); + + if (compareFileVersion(slPluginMinRequired) < 0) + return true; + } else if (fileName() == "npmozax.dll") { + // Bug 15217: Mozilla ActiveX control complains about missing xpcom_core.dll + return true; + } else if (name() == "Yahoo Application State Plugin") { + // https://bugs.webkit.org/show_bug.cgi?id=26860 + // Bug in Yahoo Application State plug-in earlier than 1.0.0.6 leads to heap corruption. + static const PlatformModuleVersion yahooAppStatePluginMinRequired(0x00000006, 0x00010000); + if (compareFileVersion(yahooAppStatePluginMinRequired) < 0) + return true; + } + + return false; +} + +void PluginPackage::determineQuirks(const String& mimeType) +{ + if (mimeType == "application/x-shockwave-flash") { + static const PlatformModuleVersion flashTenVersion(0x00000000, 0x000a0000); + + // Pre 10 Flash only requests windowless plugins if we return a mozilla user agent + if (compareFileVersion(flashTenVersion) < 0) + m_quirks.add(PluginQuirkWantsMozillaUserAgent); + + m_quirks.add(PluginQuirkThrottleInvalidate); + m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages); + m_quirks.add(PluginQuirkFlashURLNotifyBug); + } + + if (name().contains("Microsoft") && name().contains("Windows Media")) { + // The WMP plugin sets its size on the first NPP_SetWindow call and never updates its size, so + // call SetWindow when the plugin view has a correct size + m_quirks.add(PluginQuirkDeferFirstSetWindowCall); + + // Windowless mode does not work at all with the WMP plugin so just remove that parameter + // and don't pass it to the plug-in. + m_quirks.add(PluginQuirkRemoveWindowlessVideoParam); + + // WMP has a modal message loop that it enters whenever we call it or + // ask it to paint. This modal loop can deliver messages to other + // windows in WebKit at times when they are not expecting them (for + // example, delivering a WM_PAINT message during a layout), and these + // can cause crashes. + m_quirks.add(PluginQuirkHasModalMessageLoop); + } + + if (name() == "VLC Multimedia Plugin" || name() == "VLC Multimedia Plug-in") { + // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window handle + m_quirks.add(PluginQuirkDontSetNullWindowHandleOnDestroy); + + // VLC 0.8.6d and 0.8.6e crash if multiple instances are created. + // <rdar://problem/5773070> tracks allowing multiple instances when this + // bug is fixed. + m_quirks.add(PluginQuirkDontAllowMultipleInstances); + } + + // The DivX plugin sets its size on the first NPP_SetWindow call and never updates its size, so + // call SetWindow when the plugin view has a correct size + if (mimeType == "video/divx") + m_quirks.add(PluginQuirkDeferFirstSetWindowCall); + + // FIXME: This is a workaround for a problem in our NPRuntime bindings; if a plug-in creates an + // NPObject and passes it to a function it's not possible to see what root object that NPObject belongs to. + // Thus, we don't know that the object should be invalidated when the plug-in instance goes away. + // See <rdar://problem/5487742>. + if (mimeType == "application/x-silverlight") + m_quirks.add(PluginQuirkDontUnloadPlugin); + + if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { + // Because a single process cannot create multiple VMs, and we cannot reliably unload a + // Java VM, we cannot unload the Java plugin, or we'll lose reference to our only VM + m_quirks.add(PluginQuirkDontUnloadPlugin); + + // Setting the window region to an empty region causes bad scrolling repaint problems + // with the Java plug-in. + m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling); + } + + if (mimeType == "audio/x-pn-realaudio-plugin") { + // Prevent the Real plugin from calling the Window Proc recursively, causing the stack to overflow. + m_quirks.add(PluginQuirkDontCallWndProcForSameMessageRecursively); + + static const PlatformModuleVersion lastKnownUnloadableRealPlayerVersion(0x000B0B24, 0x00060000); + + // Unloading RealPlayer versions newer than 10.5 can cause a hang; see rdar://5669317. + // FIXME: Resume unloading when this bug in the RealPlayer Plug-In is fixed (rdar://5713147) + if (compareFileVersion(lastKnownUnloadableRealPlayerVersion) > 0) + m_quirks.add(PluginQuirkDontUnloadPlugin); + } +} + +bool PluginPackage::fetchInfo() +{ + DWORD versionInfoSize, zeroHandle; + versionInfoSize = GetFileVersionInfoSizeW(const_cast<UChar*>(m_path.charactersWithNullTermination()), &zeroHandle); + if (versionInfoSize == 0) + return false; + + OwnArrayPtr<char> versionInfoData(new char[versionInfoSize]); + + if (!GetFileVersionInfoW(const_cast<UChar*>(m_path.charactersWithNullTermination()), + 0, versionInfoSize, versionInfoData.get())) + return false; + + m_name = getVersionInfo(versionInfoData.get(), "ProductName"); + m_description = getVersionInfo(versionInfoData.get(), "FileDescription"); + if (m_name.isNull() || m_description.isNull()) + return false; + + VS_FIXEDFILEINFO* info; + UINT infoSize; + if (!VerQueryValueW(versionInfoData.get(), L"\\", (LPVOID*) &info, &infoSize) || infoSize < sizeof(VS_FIXEDFILEINFO)) + return false; + m_moduleVersion.leastSig = info->dwFileVersionLS; + m_moduleVersion.mostSig = info->dwFileVersionMS; + + if (isPluginBlacklisted()) + return false; + + Vector<String> types; + getVersionInfo(versionInfoData.get(), "MIMEType").split('|', types); + Vector<String> extensionLists; + getVersionInfo(versionInfoData.get(), "FileExtents").split('|', extensionLists); + Vector<String> descriptions; + getVersionInfo(versionInfoData.get(), "FileOpenName").split('|', descriptions); + + for (unsigned i = 0; i < types.size(); i++) { + String type = types[i].lower(); + String description = i < descriptions.size() ? descriptions[i] : ""; + String extensionList = i < extensionLists.size() ? extensionLists[i] : ""; + + Vector<String> extensionsVector; + extensionList.split(',', extensionsVector); + + // Get rid of the extension list that may be at the end of the description string. + int pos = description.find("(*"); + if (pos != -1) { + // There might be a space that we need to get rid of. + if (pos > 1 && description[pos - 1] == ' ') + pos--; + description = description.left(pos); + } + + // Determine the quirks for the MIME types this plug-in supports + determineQuirks(type); + + m_mimeToExtensions.add(type, extensionsVector); + m_mimeToDescriptions.add(type, description); + } + + return true; +} + +bool PluginPackage::load() +{ + if (m_freeLibraryTimer.isActive()) { + ASSERT(m_module); + m_freeLibraryTimer.stop(); + } else if (m_isLoaded) { + if (m_quirks.contains(PluginQuirkDontAllowMultipleInstances)) + return false; + m_loadCount++; + return true; + } else { +#if OS(WINCE) + m_module = ::LoadLibraryW(m_path.charactersWithNullTermination()); +#else + WCHAR currentPath[MAX_PATH]; + + if (!::GetCurrentDirectoryW(MAX_PATH, currentPath)) + return false; + + String path = m_path.substring(0, m_path.reverseFind('\\')); + + if (!::SetCurrentDirectoryW(path.charactersWithNullTermination())) + return false; + + // Load the library + m_module = ::LoadLibraryExW(m_path.charactersWithNullTermination(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); + + if (!::SetCurrentDirectoryW(currentPath)) { + if (m_module) + ::FreeLibrary(m_module); + return false; + } +#endif + } + + if (!m_module) + return false; + + m_isLoaded = true; + + NP_GetEntryPointsFuncPtr NP_GetEntryPoints = 0; + NP_InitializeFuncPtr NP_Initialize = 0; + NPError npErr; + +#if OS(WINCE) + NP_Initialize = (NP_InitializeFuncPtr)GetProcAddress(m_module, L"NP_Initialize"); + NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)GetProcAddress(m_module, L"NP_GetEntryPoints"); + m_NPP_Shutdown = (NPP_ShutdownProcPtr)GetProcAddress(m_module, L"NP_Shutdown"); +#else + NP_Initialize = (NP_InitializeFuncPtr)GetProcAddress(m_module, "NP_Initialize"); + NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)GetProcAddress(m_module, "NP_GetEntryPoints"); + m_NPP_Shutdown = (NPP_ShutdownProcPtr)GetProcAddress(m_module, "NP_Shutdown"); +#endif + + if (!NP_Initialize || !NP_GetEntryPoints || !m_NPP_Shutdown) + goto abort; + + memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); + m_pluginFuncs.size = sizeof(m_pluginFuncs); + + npErr = NP_GetEntryPoints(&m_pluginFuncs); + LOG_NPERROR(npErr); + if (npErr != NPERR_NO_ERROR) + goto abort; + + initializeBrowserFuncs(); + + npErr = NP_Initialize(&m_browserFuncs); + LOG_NPERROR(npErr); + + if (npErr != NPERR_NO_ERROR) + goto abort; + + m_loadCount++; + return true; + +abort: + unloadWithoutShutdown(); + return false; +} + +unsigned PluginPackage::hash() const +{ + const unsigned hashCodes[] = { + m_name.impl()->hash(), + m_description.impl()->hash(), + m_mimeToExtensions.size() + }; + + return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); +} + +bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b) +{ + if (a.m_name != b.m_name) + return false; + + if (a.m_description != b.m_description) + return false; + + if (a.m_mimeToExtensions.size() != b.m_mimeToExtensions.size()) + return false; + + MIMEToExtensionsMap::const_iterator::Keys end = a.m_mimeToExtensions.end().keys(); + for (MIMEToExtensionsMap::const_iterator::Keys it = a.m_mimeToExtensions.begin().keys(); it != end; ++it) { + if (!b.m_mimeToExtensions.contains(*it)) + return false; + } + + return true; +} + +uint16_t PluginPackage::NPVersion() const +{ + return NP_VERSION_MINOR; +} +} diff --git a/Source/WebCore/plugins/win/PluginViewWin.cpp b/Source/WebCore/plugins/win/PluginViewWin.cpp new file mode 100644 index 0000000..758d90a --- /dev/null +++ b/Source/WebCore/plugins/win/PluginViewWin.cpp @@ -0,0 +1,1080 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2008-2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginView.h" + +#include "BitmapImage.h" +#include "Bridge.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "EventNames.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "HostWindow.h" +#include "Image.h" +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include "KeyboardEvent.h" +#include "LocalWindowsContext.h" +#include "MIMETypeRegistry.h" +#include "MouseEvent.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include "PluginMainThreadScheduler.h" +#include "PluginMessageThrottlerWin.h" +#include "PluginPackage.h" +#include "RenderWidget.h" +#include "ScriptController.h" +#include "Settings.h" +#include "WebCoreInstanceHandle.h" +#include "c_instance.h" +#include "npruntime_impl.h" +#include "runtime_root.h" +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> +#include <wtf/ASCIICType.h> + +#if !PLATFORM(WX) +#include "BitmapInfo.h" +#endif + +#if OS(WINCE) +#undef LOG_NPERROR +#define LOG_NPERROR(x) +#undef LOG_PLUGIN_NET_ERROR +#define LOG_PLUGIN_NET_ERROR() +#endif + +#if PLATFORM(CAIRO) +#include <cairo-win32.h> +#endif + +#if PLATFORM(QT) +#include "QWebPageClient.h" +#include <QWidget> +#endif + +#if PLATFORM(WX) +#include <wx/defs.h> +#include <wx/window.h> +#endif + +static inline HWND windowHandleForPageClient(PlatformPageClient client) +{ +#if PLATFORM(QT) + if (!client) + return 0; + if (QWidget* pluginParent = qobject_cast<QWidget*>(client->pluginParent())) + return pluginParent->winId(); + return 0; +#elif PLATFORM(WX) + if (!client) + return 0; + return (HWND)client->GetHandle(); +#else + return client; +#endif +} + +using JSC::ExecState; +using JSC::JSLock; +using JSC::JSObject; +using JSC::UString; + +using std::min; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +const LPCWSTR kWebPluginViewdowClassName = L"WebPluginView"; +const LPCWSTR kWebPluginViewProperty = L"WebPluginViewProperty"; + +#if !OS(WINCE) +// The code used to hook BeginPaint/EndPaint originally came from +// <http://www.fengyuan.com/article/wmprint.html>. +// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com). + +static unsigned beginPaintSysCall; +static BYTE* beginPaint; + +static unsigned endPaintSysCall; +static BYTE* endPaint; + +typedef HDC (WINAPI *PtrBeginPaint)(HWND, PAINTSTRUCT*); +typedef BOOL (WINAPI *PtrEndPaint)(HWND, const PAINTSTRUCT*); + +#if OS(WINDOWS) && CPU(X86_64) && COMPILER(MSVC) +extern "C" HDC __stdcall _HBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint); +extern "C" BOOL __stdcall _HEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint); +#endif + +HDC WINAPI PluginView::hookedBeginPaint(HWND hWnd, PAINTSTRUCT* lpPaint) +{ + PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty)); + if (pluginView && pluginView->m_wmPrintHDC) { + // We're secretly handling WM_PRINTCLIENT, so set up the PAINTSTRUCT so + // that the plugin will paint into the HDC we provide. + memset(lpPaint, 0, sizeof(PAINTSTRUCT)); + lpPaint->hdc = pluginView->m_wmPrintHDC; + GetClientRect(hWnd, &lpPaint->rcPaint); + return pluginView->m_wmPrintHDC; + } + +#if COMPILER(GCC) + HDC result; + asm ("push %2\n" + "push %3\n" + "call *%4\n" + : "=a" (result) + : "a" (beginPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (beginPaint) + : "memory" + ); + return result; +#elif defined(_M_IX86) + // Call through to the original BeginPaint. + __asm mov eax, beginPaintSysCall + __asm push lpPaint + __asm push hWnd + __asm call beginPaint +#else + return _HBeginPaint(hWnd, lpPaint); +#endif +} + +BOOL WINAPI PluginView::hookedEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint) +{ + PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty)); + if (pluginView && pluginView->m_wmPrintHDC) { + // We're secretly handling WM_PRINTCLIENT, so we don't have to do any + // cleanup. + return TRUE; + } + +#if COMPILER(GCC) + BOOL result; + asm ("push %2\n" + "push %3\n" + "call *%4\n" + : "=a" (result) + : "a" (endPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (endPaint) + ); + return result; +#elif defined (_M_IX86) + // Call through to the original EndPaint. + __asm mov eax, endPaintSysCall + __asm push lpPaint + __asm push hWnd + __asm call endPaint +#else + return _HEndPaint(hWnd, lpPaint); +#endif +} + +static void hook(const char* module, const char* proc, unsigned& sysCallID, BYTE*& pProc, const void* pNewProc) +{ + // See <http://www.fengyuan.com/article/wmprint.html> for an explanation of + // how this function works. + + HINSTANCE hMod = GetModuleHandleA(module); + + pProc = reinterpret_cast<BYTE*>(reinterpret_cast<ptrdiff_t>(GetProcAddress(hMod, proc))); + +#if COMPILER(GCC) || defined(_M_IX86) + if (pProc[0] != 0xB8) + return; + + // FIXME: Should we be reading the bytes one-by-one instead of doing an + // unaligned read? + sysCallID = *reinterpret_cast<unsigned*>(pProc + 1); + + DWORD flOldProtect; + if (!VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, &flOldProtect)) + return; + + pProc[0] = 0xE9; + *reinterpret_cast<unsigned*>(pProc + 1) = reinterpret_cast<intptr_t>(pNewProc) - reinterpret_cast<intptr_t>(pProc + 5); + + pProc += 5; +#else + /* Disassembly of BeginPaint() + 00000000779FC5B0 4C 8B D1 mov r10,rcx + 00000000779FC5B3 B8 17 10 00 00 mov eax,1017h + 00000000779FC5B8 0F 05 syscall + 00000000779FC5BA C3 ret + 00000000779FC5BB 90 nop + 00000000779FC5BC 90 nop + 00000000779FC5BD 90 nop + 00000000779FC5BE 90 nop + 00000000779FC5BF 90 nop + 00000000779FC5C0 90 nop + 00000000779FC5C1 90 nop + 00000000779FC5C2 90 nop + 00000000779FC5C3 90 nop + */ + // Check for the signature as in the above disassembly + DWORD guard = 0xB8D18B4C; + if (*reinterpret_cast<DWORD*>(pProc) != guard) + return; + + DWORD flOldProtect; + VirtualProtect(pProc, 12, PAGE_EXECUTE_READWRITE, & flOldProtect); + pProc[0] = 0x48; // mov rax, this + pProc[1] = 0xb8; + *(__int64*)(pProc+2) = (__int64)pNewProc; + pProc[10] = 0xff; // jmp rax + pProc[11] = 0xe0; +#endif +} + +static void setUpOffscreenPaintingHooks(HDC (WINAPI*hookedBeginPaint)(HWND, PAINTSTRUCT*), BOOL (WINAPI*hookedEndPaint)(HWND, const PAINTSTRUCT*)) +{ + static bool haveHooked = false; + if (haveHooked) + return; + haveHooked = true; + + // Most (all?) windowed plugins don't seem to respond to WM_PRINTCLIENT, so + // we hook into BeginPaint/EndPaint to allow their normal WM_PAINT handling + // to draw into a given HDC. Note that this hooking affects the entire + // process. + hook("user32.dll", "BeginPaint", beginPaintSysCall, beginPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedBeginPaint))); + hook("user32.dll", "EndPaint", endPaintSysCall, endPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedEndPaint))); + +} +#endif + +static bool registerPluginView() +{ + static bool haveRegisteredWindowClass = false; + if (haveRegisteredWindowClass) + return true; + + haveRegisteredWindowClass = true; + +#if PLATFORM(QT) + WebCore::setInstanceHandle((HINSTANCE)(qWinAppInst())); +#endif + + ASSERT(WebCore::instanceHandle()); + +#if OS(WINCE) + WNDCLASS wcex = { 0 }; +#else + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.hIconSm = 0; +#endif + + wcex.style = CS_DBLCLKS; +#if OS(WINCE) + wcex.style |= CS_PARENTDC; +#endif + wcex.lpfnWndProc = DefWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = WebCore::instanceHandle(); + wcex.hIcon = 0; + wcex.hCursor = LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)COLOR_WINDOW; + wcex.lpszMenuName = 0; + wcex.lpszClassName = kWebPluginViewdowClassName; + +#if OS(WINCE) + return !!RegisterClass(&wcex); +#else + return !!RegisterClassEx(&wcex); +#endif +} + +LRESULT CALLBACK PluginView::PluginViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty)); + + return pluginView->wndProc(hWnd, message, wParam, lParam); +} + +static bool isWindowsMessageUserGesture(UINT message) +{ + switch (message) { + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_KEYUP: + return true; + default: + return false; + } +} + +LRESULT +PluginView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // <rdar://5711136> Sometimes Flash will call SetCapture before creating + // a full-screen window and will not release it, which causes the + // full-screen window to never receive mouse events. We set/release capture + // on mouse down/up before sending the event to the plug-in to prevent that. + switch (message) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + ::SetCapture(hWnd); + break; + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + ::ReleaseCapture(); + break; + } + + if (message == m_lastMessage && + m_plugin->quirks().contains(PluginQuirkDontCallWndProcForSameMessageRecursively) && + m_isCallingPluginWndProc) + return 1; + + if (message == WM_USER + 1 && + m_plugin->quirks().contains(PluginQuirkThrottleWMUserPlusOneMessages)) { + if (!m_messageThrottler) + m_messageThrottler.set(new PluginMessageThrottlerWin(this)); + + m_messageThrottler->appendMessage(hWnd, message, wParam, lParam); + return 0; + } + + m_lastMessage = message; + m_isCallingPluginWndProc = true; + + // If the plug-in doesn't explicitly support changing the pop-up state, we enable + // popups for all user gestures. + // Note that we need to pop the state in a timer, because the Flash plug-in + // pops up windows in response to a posted message. + if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE && + isWindowsMessageUserGesture(message) && !m_popPopupsStateTimer.isActive()) { + + pushPopupsEnabledState(true); + + m_popPopupsStateTimer.startOneShot(0); + } + +#if !OS(WINCE) + if (message == WM_PRINTCLIENT) { + // Most (all?) windowed plugins don't respond to WM_PRINTCLIENT, so we + // change the message to WM_PAINT and rely on our hooked versions of + // BeginPaint/EndPaint to make the plugin draw into the given HDC. + message = WM_PAINT; + m_wmPrintHDC = reinterpret_cast<HDC>(wParam); + } +#endif + + // Call the plug-in's window proc. + LRESULT result = ::CallWindowProc(m_pluginWndProc, hWnd, message, wParam, lParam); + + m_wmPrintHDC = 0; + + m_isCallingPluginWndProc = false; + + return result; +} + +void PluginView::updatePluginWidget() +{ + if (!parent()) + return; + + ASSERT(parent()->isFrameView()); + FrameView* frameView = static_cast<FrameView*>(parent()); + + IntRect oldWindowRect = m_windowRect; + IntRect oldClipRect = m_clipRect; + +#if OS(WINCE) + m_windowRect = frameView->contentsToWindow(frameRect()); +#else + m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); +#endif + m_clipRect = windowClipRect(); + m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); + + if (platformPluginWidget() && (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect || m_clipRect != oldClipRect)) { + HRGN rgn; + + setCallingPlugin(true); + + // To prevent flashes while scrolling, we disable drawing during the window + // update process by clipping the window to the zero rect. + + bool clipToZeroRect = !m_plugin->quirks().contains(PluginQuirkDontClipToZeroRectWhenScrolling); + + if (clipToZeroRect) { + rgn = ::CreateRectRgn(0, 0, 0, 0); + ::SetWindowRgn(platformPluginWidget(), rgn, FALSE); + } else { + rgn = ::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.right(), m_clipRect.bottom()); + ::SetWindowRgn(platformPluginWidget(), rgn, TRUE); + } + + if (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect) + ::MoveWindow(platformPluginWidget(), m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), TRUE); + + if (clipToZeroRect) { + rgn = ::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.right(), m_clipRect.bottom()); + ::SetWindowRgn(platformPluginWidget(), rgn, TRUE); + } + + setCallingPlugin(false); + + m_haveUpdatedPluginWidget = true; + } +} + +void PluginView::setFocus(bool focused) +{ + if (focused && platformPluginWidget()) + SetFocus(platformPluginWidget()); + + Widget::setFocus(focused); +} + +void PluginView::show() +{ + setSelfVisible(true); + + if (isParentVisible() && platformPluginWidget()) + ShowWindow(platformPluginWidget(), SW_SHOWNA); + + Widget::show(); +} + +void PluginView::hide() +{ + setSelfVisible(false); + + if (isParentVisible() && platformPluginWidget()) + ShowWindow(platformPluginWidget(), SW_HIDE); + + Widget::hide(); +} + +bool PluginView::dispatchNPEvent(NPEvent& npEvent) +{ + if (!m_plugin->pluginFuncs()->event) + return true; + + bool shouldPop = false; + + if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE && isWindowsMessageUserGesture(npEvent.event)) { + pushPopupsEnabledState(true); + shouldPop = true; + } + + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + bool result = m_plugin->pluginFuncs()->event(m_instance, &npEvent); + setCallingPlugin(false); + + if (shouldPop) + popPopupsEnabledState(); + + return result; +} + +void PluginView::paintIntoTransformedContext(HDC hdc) +{ + if (m_isWindowed) { +#if !OS(WINCE) + SendMessage(platformPluginWidget(), WM_PRINTCLIENT, reinterpret_cast<WPARAM>(hdc), PRF_CLIENT | PRF_CHILDREN | PRF_OWNED); +#endif + return; + } + + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = hdc; + + WINDOWPOS windowpos = { 0, 0, 0, 0, 0, 0, 0 }; + + IntRect r = static_cast<FrameView*>(parent())->contentsToWindow(frameRect()); + + windowpos.x = r.x(); + windowpos.y = r.y(); + windowpos.cx = r.width(); + windowpos.cy = r.height(); + + NPEvent npEvent; + npEvent.event = WM_WINDOWPOSCHANGED; + npEvent.lParam = reinterpret_cast<uintptr_t>(&windowpos); + npEvent.wParam = 0; + + dispatchNPEvent(npEvent); + + setNPWindowRect(frameRect()); + + npEvent.event = WM_PAINT; + npEvent.wParam = reinterpret_cast<uintptr_t>(hdc); + + // This is supposed to be a pointer to the dirty rect, but it seems that the Flash plugin + // ignores it so we just pass null. + npEvent.lParam = 0; + + dispatchNPEvent(npEvent); +} + +void PluginView::paintWindowedPluginIntoContext(GraphicsContext* context, const IntRect& rect) +{ +#if !OS(WINCE) + ASSERT(m_isWindowed); + ASSERT(context->shouldIncludeChildWindows()); + + ASSERT(parent()->isFrameView()); + IntPoint locationInWindow = static_cast<FrameView*>(parent())->contentsToWindow(frameRect().location()); + + LocalWindowsContext windowsContext(context, frameRect(), false); + +#if PLATFORM(CAIRO) + // Must flush drawings up to this point to the backing metafile, otherwise the + // plugin region will be overwritten with any clear regions specified in the + // cairo-controlled portions of the rendering. + PlatformGraphicsContext* ctx = context->platformContext(); + cairo_show_page(ctx); +#endif + + HDC hdc = windowsContext.hdc(); + XFORM originalTransform; + GetWorldTransform(hdc, &originalTransform); + + // The plugin expects the DC to be in client coordinates, so we translate + // the DC to make that so. + AffineTransform ctm = context->getCTM(); + ctm.translate(locationInWindow.x(), locationInWindow.y()); + XFORM transform = static_cast<XFORM>(ctm.toTransformationMatrix()); + + SetWorldTransform(hdc, &transform); + + paintIntoTransformedContext(hdc); + + SetWorldTransform(hdc, &originalTransform); +#endif +} + +void PluginView::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_isStarted) { + // Draw the "missing plugin" image + paintMissingPluginIcon(context, rect); + return; + } + + if (context->paintingDisabled()) + return; + + // Ensure that we have called SetWindow before we try to paint. + if (!m_haveCalledSetWindow) + setNPWindowRect(frameRect()); + + if (m_isWindowed) { +#if !OS(WINCE) + if (context->shouldIncludeChildWindows()) + paintWindowedPluginIntoContext(context, rect); +#endif + return; + } + + ASSERT(parent()->isFrameView()); + IntRect rectInWindow = static_cast<FrameView*>(parent())->contentsToWindow(frameRect()); + LocalWindowsContext windowsContext(context, rectInWindow, m_isTransparent); + + // On Safari/Windows without transparency layers the GraphicsContext returns the HDC + // of the window and the plugin expects that the passed in DC has window coordinates. + // In the Qt port we always draw in an offscreen buffer and therefore need to preserve + // the translation set in getWindowsContext. +#if !PLATFORM(QT) && !OS(WINCE) + if (!context->inTransparencyLayer()) { + XFORM transform; + GetWorldTransform(windowsContext.hdc(), &transform); + transform.eDx = 0; + transform.eDy = 0; + SetWorldTransform(windowsContext.hdc(), &transform); + } +#endif + + paintIntoTransformedContext(windowsContext.hdc()); +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + NPEvent npEvent; + + npEvent.wParam = event->keyCode(); + + if (event->type() == eventNames().keydownEvent) { + npEvent.event = WM_KEYDOWN; + npEvent.lParam = 0; + } else if (event->type() == eventNames().keyupEvent) { + npEvent.event = WM_KEYUP; + npEvent.lParam = 0x8000; + } + + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); +} + +#if !OS(WINCE) +extern bool ignoreNextSetCursor; +#endif + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + NPEvent npEvent; + + IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(IntPoint(event->pageX(), event->pageY())); + + npEvent.lParam = MAKELPARAM(p.x(), p.y()); + npEvent.wParam = 0; + + if (event->ctrlKey()) + npEvent.wParam |= MK_CONTROL; + if (event->shiftKey()) + npEvent.wParam |= MK_SHIFT; + + if (event->type() == eventNames().mousemoveEvent || + event->type() == eventNames().mouseoutEvent || + event->type() == eventNames().mouseoverEvent) { + npEvent.event = WM_MOUSEMOVE; + if (event->buttonDown()) + switch (event->button()) { + case LeftButton: + npEvent.wParam |= MK_LBUTTON; + break; + case MiddleButton: + npEvent.wParam |= MK_MBUTTON; + break; + case RightButton: + npEvent.wParam |= MK_RBUTTON; + break; + } + } + else if (event->type() == eventNames().mousedownEvent) { + focusPluginElement(); + switch (event->button()) { + case 0: + npEvent.event = WM_LBUTTONDOWN; + break; + case 1: + npEvent.event = WM_MBUTTONDOWN; + break; + case 2: + npEvent.event = WM_RBUTTONDOWN; + break; + } + } else if (event->type() == eventNames().mouseupEvent) { + switch (event->button()) { + case 0: + npEvent.event = WM_LBUTTONUP; + break; + case 1: + npEvent.event = WM_MBUTTONUP; + break; + case 2: + npEvent.event = WM_RBUTTONUP; + break; + } + } else + return; + + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); + +#if !PLATFORM(QT) && !PLATFORM(WX) && !OS(WINCE) + // Currently, Widget::setCursor is always called after this function in EventHandler.cpp + // and since we don't want that we set ignoreNextSetCursor to true here to prevent that. + ignoreNextSetCursor = true; + if (Page* page = m_parentFrame->page()) + page->chrome()->client()->setLastSetCursorToCurrentCursor(); +#endif +} + +void PluginView::setParent(ScrollView* parent) +{ + Widget::setParent(parent); + +#if OS(WINCE) + if (parent) { + init(); + if (parent->isVisible()) + show(); + else + hide(); + } +#else + if (parent) + init(); + else { + if (!platformPluginWidget()) + return; + + // If the plug-in window or one of its children have the focus, we need to + // clear it to prevent the web view window from being focused because that can + // trigger a layout while the plugin element is being detached. + HWND focusedWindow = ::GetFocus(); + if (platformPluginWidget() == focusedWindow || ::IsChild(platformPluginWidget(), focusedWindow)) + ::SetFocus(0); + } +#endif +} + +void PluginView::setParentVisible(bool visible) +{ + if (isParentVisible() == visible) + return; + + Widget::setParentVisible(visible); + + if (isSelfVisible() && platformPluginWidget()) { + if (visible) + ShowWindow(platformPluginWidget(), SW_SHOWNA); + else + ShowWindow(platformPluginWidget(), SW_HIDE); + } +} + +void PluginView::setNPWindowRect(const IntRect& rect) +{ + if (!m_isStarted) + return; + +#if OS(WINCE) + IntRect r = static_cast<FrameView*>(parent())->contentsToWindow(rect); + m_npWindow.x = r.x(); + m_npWindow.y = r.y(); + + m_npWindow.width = r.width(); + m_npWindow.height = r.height(); + + m_npWindow.clipRect.right = r.width(); + m_npWindow.clipRect.bottom = r.height(); +#else + IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(rect.location()); + m_npWindow.x = p.x(); + m_npWindow.y = p.y(); + + m_npWindow.width = rect.width(); + m_npWindow.height = rect.height(); + + m_npWindow.clipRect.right = rect.width(); + m_npWindow.clipRect.bottom = rect.height(); +#endif + m_npWindow.clipRect.left = 0; + m_npWindow.clipRect.top = 0; + + if (m_plugin->pluginFuncs()->setwindow) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + + m_haveCalledSetWindow = true; + + if (!m_isWindowed) + return; + + ASSERT(platformPluginWidget()); + +#if OS(WINCE) + if (!m_pluginWndProc) { + WNDPROC currentWndProc = (WNDPROC)GetWindowLong(platformPluginWidget(), GWL_WNDPROC); + if (currentWndProc != PluginViewWndProc) + m_pluginWndProc = (WNDPROC)SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)PluginViewWndProc); + } +#else + WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC); + if (currentWndProc != PluginViewWndProc) + m_pluginWndProc = (WNDPROC)SetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)PluginViewWndProc); +#endif + } +} + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) +{ + String filename(buf, len); + + if (filename.startsWith("file:///")) + filename = filename.substring(8); + + // Get file info + WIN32_FILE_ATTRIBUTE_DATA attrs; + if (GetFileAttributesExW(filename.charactersWithNullTermination(), GetFileExInfoStandard, &attrs) == 0) + return NPERR_FILE_NOT_FOUND; + + if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return NPERR_FILE_NOT_FOUND; + + HANDLE fileHandle = CreateFileW(filename.charactersWithNullTermination(), FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (fileHandle == INVALID_HANDLE_VALUE) + return NPERR_FILE_NOT_FOUND; + + buffer.resize(attrs.nFileSizeLow); + + DWORD bytesRead; + int retval = ReadFile(fileHandle, buffer.data(), attrs.nFileSizeLow, &bytesRead, 0); + + CloseHandle(fileHandle); + + if (retval == 0 || bytesRead != attrs.nFileSizeLow) + return NPERR_FILE_NOT_FOUND; + + return NPERR_NO_ERROR; +} + +bool PluginView::platformGetValueStatic(NPNVariable, void*, NPError*) +{ + return false; +} + +bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVnetscapeWindow: { + HWND* w = reinterpret_cast<HWND*>(value); + *w = windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0); + *result = NPERR_NO_ERROR; + return true; + } + + case NPNVSupportsWindowless: { + NPBool* flag = reinterpret_cast<NPBool*>(value); + *flag = TRUE; + *result = NPERR_NO_ERROR; + return true; + } + + default: + return false; + } +} + +void PluginView::invalidateRect(const IntRect& rect) +{ + if (m_isWindowed) { + RECT invalidRect = { rect.x(), rect.y(), rect.right(), rect.bottom() }; + ::InvalidateRect(platformPluginWidget(), &invalidRect, false); + return; + } + + invalidateWindowlessPluginRect(rect); +} + +void PluginView::invalidateRect(NPRect* rect) +{ + if (!rect) { + invalidate(); + return; + } + + IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); + + if (m_isWindowed) { + RECT invalidRect = { r.x(), r.y(), r.right(), r.bottom() }; + InvalidateRect(platformPluginWidget(), &invalidRect, FALSE); + } else { + if (m_plugin->quirks().contains(PluginQuirkThrottleInvalidate)) { + m_invalidRects.append(r); + if (!m_invalidateTimer.isActive()) + m_invalidateTimer.startOneShot(0.001); + } else + invalidateRect(r); + } +} + +void PluginView::invalidateRegion(NPRegion region) +{ + if (m_isWindowed) + return; + + RECT r; + + if (GetRgnBox(region, &r) == 0) { + invalidate(); + return; + } + + IntRect rect(IntPoint(r.left, r.top), IntSize(r.right-r.left, r.bottom-r.top)); + invalidateRect(rect); +} + +void PluginView::forceRedraw() +{ + if (m_isWindowed) + ::UpdateWindow(platformPluginWidget()); + else + ::UpdateWindow(windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0)); +} + +bool PluginView::platformStart() +{ + ASSERT(m_isStarted); + ASSERT(m_status == PluginStatusLoadedSuccessfully); + + if (m_isWindowed) { + registerPluginView(); +#if !OS(WINCE) + setUpOffscreenPaintingHooks(hookedBeginPaint, hookedEndPaint); +#endif + + DWORD flags = WS_CHILD; + if (isSelfVisible()) + flags |= WS_VISIBLE; + + HWND parentWindowHandle = windowHandleForPageClient(m_parentFrame->view()->hostWindow()->platformPageClient()); + HWND window = ::CreateWindowEx(0, kWebPluginViewdowClassName, 0, flags, + 0, 0, 0, 0, parentWindowHandle, 0, WebCore::instanceHandle(), 0); + +#if OS(WINDOWS) && (PLATFORM(QT) || PLATFORM(WX)) + m_window = window; +#else + setPlatformWidget(window); +#endif + + // Calling SetWindowLongPtrA here makes the window proc ASCII, which is required by at least + // the Shockwave Director plug-in. +#if OS(WINDOWS) && CPU(X86_64) + ::SetWindowLongPtrA(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)DefWindowProcA); +#elif OS(WINCE) + ::SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProc); +#else + ::SetWindowLongPtrA(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProcA); +#endif + SetProp(platformPluginWidget(), kWebPluginViewProperty, this); + + m_npWindow.type = NPWindowTypeWindow; + m_npWindow.window = platformPluginWidget(); + } else { + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = 0; + } + + updatePluginWidget(); + + if (!m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall)) + setNPWindowRect(frameRect()); + + return true; +} + +void PluginView::platformDestroy() +{ + if (!platformPluginWidget()) + return; + + DestroyWindow(platformPluginWidget()); + setPlatformPluginWidget(0); +} + +PassRefPtr<Image> PluginView::snapshot() +{ +#if !PLATFORM(WX) && !OS(WINCE) + OwnPtr<HDC> hdc(CreateCompatibleDC(0)); + + if (!m_isWindowed) { + // Enable world transforms. + SetGraphicsMode(hdc.get(), GM_ADVANCED); + + XFORM transform; + GetWorldTransform(hdc.get(), &transform); + + // Windowless plug-ins assume that they're drawing onto the view's DC. + // Translate the context so that the plug-in draws at (0, 0). + ASSERT(parent()->isFrameView()); + IntPoint position = static_cast<FrameView*>(parent())->contentsToWindow(frameRect()).location(); + transform.eDx = -position.x(); + transform.eDy = -position.y(); + SetWorldTransform(hdc.get(), &transform); + } + + void* bits; + BitmapInfo bmp = BitmapInfo::createBottomUp(frameRect().size()); + OwnPtr<HBITMAP> hbmp(CreateDIBSection(0, &bmp, DIB_RGB_COLORS, &bits, 0, 0)); + + HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc.get(), hbmp.get())); + + paintIntoTransformedContext(hdc.get()); + + SelectObject(hdc.get(), hbmpOld); + + return BitmapImage::create(hbmp.get()); +#else + return 0; +#endif +} + +void PluginView::halt() +{ + ASSERT(!m_isHalted); + ASSERT(m_isStarted); + +#if !PLATFORM(QT) + // Show a screenshot of the plug-in. + toRenderWidget(m_element->renderer())->showSubstituteImage(snapshot()); +#endif + + m_isHalted = true; + m_hasBeenHalted = true; + + stop(); + platformDestroy(); +} + +void PluginView::restart() +{ + ASSERT(!m_isStarted); + ASSERT(m_isHalted); + + // Clear any substitute image. + toRenderWidget(m_element->renderer())->showSubstituteImage(0); + + m_isHalted = false; + m_haveUpdatedPluginWidget = false; + start(); +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/wx/PluginDataWx.cpp b/Source/WebCore/plugins/wx/PluginDataWx.cpp new file mode 100644 index 0000000..0eb890f --- /dev/null +++ b/Source/WebCore/plugins/wx/PluginDataWx.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008 Kevin Ollivier <kevino@theolliviers.com> All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "PluginData.h" + +#include "NotImplemented.h" +#include "PluginDatabase.h" +#include "PluginPackage.h" + +namespace WebCore { + +void PluginData::initPlugins(const Page*) +{ + PluginDatabase* db = PluginDatabase::installedPlugins(); + const Vector<PluginPackage*> &plugins = db->plugins(); + + for (unsigned int i = 0; i < plugins.size(); ++i) { + PluginInfo info; + PluginPackage* package = plugins[i]; + + info.name = package->name(); + info.file = package->fileName(); + info.desc = package->description(); + + const MIMEToDescriptionsMap& mimeToDescriptions = package->mimeToDescriptions(); + MIMEToDescriptionsMap::const_iterator end = mimeToDescriptions.end(); + for (MIMEToDescriptionsMap::const_iterator it = mimeToDescriptions.begin(); it != end; ++it) { + MimeClassInfo mime; + + mime.type = it->first; + mime.desc = it->second; + mime.extensions = package->mimeToExtensions().get(mime.type); + + info.mimes.append(mime); + } + + m_plugins.append(info); + } +} + +void PluginData::refresh() +{ + notImplemented(); +} + +}; |