diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
commit | f5b16fed647e941aa66933178da85db2860d639b (patch) | |
tree | f00e9856c04aad3b558a140955e7674add33f051 /webkit/glue/plugins | |
parent | 920c091ac3ee15079194c82ae8a7a18215f3f23c (diff) | |
download | chromium_src-f5b16fed647e941aa66933178da85db2860d639b.zip chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.gz chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.bz2 |
Add webkit to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/glue/plugins')
51 files changed, 8978 insertions, 0 deletions
diff --git a/webkit/glue/plugins/mozilla_extensions.cc b/webkit/glue/plugins/mozilla_extensions.cc new file mode 100644 index 0000000..efda1df --- /dev/null +++ b/webkit/glue/plugins/mozilla_extensions.cc @@ -0,0 +1,395 @@ +// Copyright 2008, 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 <windows.h> +#include <Winhttp.h> + +#include <algorithm> + +#include "webkit/glue/plugins/mozilla_extensions.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_errors.h" +#include "net/http/http_proxy_service.h" +#include "net/http/http_proxy_resolver_winhttp.h" +#include "third_party/npapi/bindings/npapi.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_instance.h" + +#define QI_SUPPORTS_IID(iid, iface) \ + QI_SUPPORTS_IID_(iid, iface::GetIID(), iface) + +#define QI_SUPPORTS_IID_(src_iid, iface_iid, iface) \ + if (iid.Equals(iface_iid)) { \ + AddRef(); \ + *result = static_cast<iface*>(this); \ + return NS_OK; \ + } + +namespace NPAPI +{ + +void MozillaExtensionApi::DetachFromInstance() { + plugin_instance_ = NULL; +} + +bool MozillaExtensionApi::FindProxyForUrl(const char* url, + std::string* proxy) { + bool result = false; + + if ((!url) || (!proxy)) { + NOTREACHED(); + return result; + } + + net::HttpProxyResolverWinHttp proxy_resolver; + net::HttpProxyService proxy_service(&proxy_resolver); + net::HttpProxyInfo proxy_info; + + if (proxy_service.ResolveProxy(GURL(std::string(url)), + &proxy_info, + NULL, + NULL) == net::OK) { + if (!proxy_info.is_direct()) { + std::wstring winhttp_proxy = proxy_info.proxy_server(); + + // Winhttp returns proxy in the the following format: + // - HTTP proxy: "111.111.111.111:11" + // -.SOCKS proxy: "socks=111.111.111.111:11" + // - Mixed proxy: "http=111.111.111.111:11; socks=222.222.222.222:22" + // + // We need to translate this into the following format: + // i) "DIRECT" -- no proxy + // ii) "PROXY xxx.xxx.xxx.xxx" -- use proxy + // iii) "SOCKS xxx.xxx.xxx.xxx" -- use SOCKS + // iv) Mixed. e.g. "PROXY 111.111.111.111;PROXY 112.112.112.112", + // "PROXY 111.111.111.111;SOCKS 112.112.112.112".... + StringToLowerASCII(winhttp_proxy); + if (std::wstring::npos == winhttp_proxy.find(L'=')) { + // Proxy is in the form: "111.111.111.111:11" + winhttp_proxy.insert(0, L"http "); + } else { + // Proxy is in the following form. + // -.SOCKS proxy: "socks=111.111.111.111:11" + // - Mixed proxy: "http=111.111.111.111:11; socks=222.222.222.222:22" + // in this case just replace the '=' with a space + std::replace_if(winhttp_proxy.begin(), + winhttp_proxy.end(), + std::bind2nd(std::equal_to<wchar_t>(), L'='), L' '); + } + + *proxy = WideToASCII(std::wstring(winhttp_proxy)); + result = true; + } + } + + return result; +} + +// nsISupports implementation +NS_IMETHODIMP MozillaExtensionApi::QueryInterface(REFNSIID iid, + void** result) { + static const nsIID knsISupportsIID = NS_ISUPPORTS_IID; + QI_SUPPORTS_IID_(iid, knsISupportsIID, nsIServiceManager) + QI_SUPPORTS_IID(iid, nsIServiceManager) + QI_SUPPORTS_IID(iid, nsIPluginManager) + QI_SUPPORTS_IID(iid, nsIPluginManager2) + QI_SUPPORTS_IID(iid, nsICookieStorage) + + NOTREACHED(); + return NS_ERROR_NO_INTERFACE; +} + +NS_IMETHODIMP_(nsrefcnt) MozillaExtensionApi::AddRef(void) { + return InterlockedIncrement(reinterpret_cast<LONG*>(&ref_count_)); +} + +NS_IMETHODIMP_(nsrefcnt) MozillaExtensionApi::Release(void) { + DCHECK(static_cast<int>(ref_count_) > 0); + if (InterlockedDecrement(reinterpret_cast<LONG*>(&ref_count_)) == 0) { + delete this; + return 0; + } + + return ref_count_; +} + +NS_IMETHODIMP MozillaExtensionApi::GetService(REFNSIID class_guid, + REFNSIID iid, + void** result) { + + static const nsIID kPluginManagerCID = NS_PLUGINMANAGER_CID; + static const nsIID kCookieStorageCID = NS_COOKIESTORAGE_CID; + + nsresult rv = NS_ERROR_FAILURE; + + if ((class_guid.Equals(kPluginManagerCID)) || + (class_guid.Equals(kCookieStorageCID))) { + rv = QueryInterface(iid, result); + } + + DCHECK(rv == NS_OK); + return rv; +} + +NS_IMETHODIMP MozillaExtensionApi::GetServiceByContractID( + const char* contract_id, + REFNSIID iid, + void** result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::IsServiceInstantiated(REFNSIID class_guid, + REFNSIID iid, + PRBool* result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::IsServiceInstantiatedByContractID( + const char* contract_id, + REFNSIID iid, + PRBool* result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + + +NS_IMETHODIMP MozillaExtensionApi::GetValue(nsPluginManagerVariable variable, + void * value) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::ReloadPlugins(PRBool reloadPages) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::UserAgent( + const char** resultingAgentString) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::GetURL( + nsISupports* pluginInst, + const char* url, + const char* target, + nsIPluginStreamListener* streamListener, + const char* altHost, + const char* referrer, + PRBool forceJSEnabled) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::PostURL( + nsISupports* pluginInst, + const char* url, + unsigned int postDataLen, + const char* postData, + PRBool isFile, + const char* target, + nsIPluginStreamListener* streamListener, + const char* altHost, + const char* referrer, + PRBool forceJSEnabled , + unsigned int postHeadersLength, + const char* postHeaders) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::RegisterPlugin( + REFNSIID aCID, + const char *aPluginName, + const char *aDescription, + const char * * aMimeTypes, + const char * * aMimeDescriptions, + const char * * aFileExtensions, + PRInt32 aCount) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::UnregisterPlugin(REFNSIID aCID) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::GetURLWithHeaders( + nsISupports* pluginInst, + const char* url, + const char* target /* = NULL */, + nsIPluginStreamListener* streamListener /* = NULL */, + const char* altHost /* = NULL */, + const char* referrer /* = NULL */, + PRBool forceJSEnabled /* = PR_FALSE */, + PRUint32 getHeadersLength /* = 0 */, + const char* getHeaders /* = NULL */){ + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +// nsIPluginManager2 +NS_IMETHODIMP MozillaExtensionApi::BeginWaitCursor() { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::EndWaitCursor() { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::SupportsURLProtocol(const char* aProtocol, + PRBool* aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::NotifyStatusChange(nsIPlugin* aPlugin, + nsresult aStatus) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::FindProxyForURL( + const char* aURL, + char** aResult) { + std::string proxy = "DIRECT"; + FindProxyForUrl(aURL, &proxy); + + // Allocate this using the NPAPI allocator. The plugin will call + // NPN_Free to free this. + char* result = static_cast<char*>(NPN_MemAlloc(proxy.length() + 1)); + strncpy(result, proxy.c_str(), proxy.length() + 1); + + *aResult = result; + return NS_OK; +} + +NS_IMETHODIMP MozillaExtensionApi::RegisterWindow( + nsIEventHandler* handler, + nsPluginPlatformWindowRef window) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::UnregisterWindow( + nsIEventHandler* handler, + nsPluginPlatformWindowRef win) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::AllocateMenuID(nsIEventHandler* aHandler, + PRBool aIsSubmenu, + PRInt16 *aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::DeallocateMenuID(nsIEventHandler* aHandler, + PRInt16 aMenuID) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::HasAllocatedMenuID(nsIEventHandler* aHandler, + PRInt16 aMenuID, + PRBool* aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +// nsICookieStorage +NS_IMETHODIMP MozillaExtensionApi::GetCookie( + const char* url, + void* cookie_buffer, + PRUint32& buffer_size) { + if ((!url) || (!cookie_buffer)) { + return NS_ERROR_INVALID_ARG; + } + + if (!plugin_instance_) + return NS_ERROR_FAILURE; + + WebPlugin* webplugin = plugin_instance_->webplugin(); + if (!webplugin) + return NS_ERROR_FAILURE; + + // Bypass third-party cookie blocking by using the url as the policy_url. + GURL cookies_url((std::string(url))); + std::string cookies = webplugin->GetCookies(cookies_url, cookies_url); + + if (cookies.empty()) + return NS_ERROR_FAILURE; + + if(cookies.length() >= buffer_size) + return NS_ERROR_FAILURE; + + strncpy(static_cast<char*>(cookie_buffer), + cookies.c_str(), + cookies.length() + 1); + + buffer_size = cookies.length(); + return NS_OK; +} + +NS_IMETHODIMP MozillaExtensionApi::SetCookie( + const char* url, + const void* cookie_buffer, + PRUint32 buffer_size){ + if ((!url) || (!cookie_buffer) || (!buffer_size)) { + return NS_ERROR_INVALID_ARG; + } + + if (!plugin_instance_) + return NS_ERROR_FAILURE; + + WebPlugin* webplugin = plugin_instance_->webplugin(); + if (!webplugin) + return NS_ERROR_FAILURE; + + std::string cookie(static_cast<const char*>(cookie_buffer), + buffer_size); + GURL cookies_url((std::string(url))); + webplugin->SetCookie(cookies_url, + cookies_url, + cookie); + return NS_OK; +} + + +} // namespace NPAPI diff --git a/webkit/glue/plugins/mozilla_extensions.h b/webkit/glue/plugins/mozilla_extensions.h new file mode 100644 index 0000000..737f9fe9 --- /dev/null +++ b/webkit/glue/plugins/mozilla_extensions.h @@ -0,0 +1,124 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_MOZILLA_EXTENSIONS_H__ +#define WEBKIT_GLUE_PLUGINS_MOZILLA_EXTENSIONS_H__ + +#include <windows.h> + +// Include npapi first to avoid definition clashes due to +// XP_WIN +#include "third_party/npapi/bindings/npapi.h" + +#include "third_party/mozilla/include/nsIServiceManager.h" +#include "third_party/mozilla/include/nsIPluginManager2.h" +#include "third_party/mozilla/include/nsICookieStorage.h" +#include "third_party/mozilla/include/nsError.h" + +#include "base/ref_counted.h" + +// NS_DECL_NSIPLUGINMANAGER doesn't include methods described as "C++" in the +// nsIPluginManager.idl. +#define NS_DECL_NSIPLUGINMANAGER_FIXED \ + NS_DECL_NSIPLUGINMANAGER \ + NS_IMETHOD \ + GetURL(nsISupports* pluginInst, \ + const char* url, \ + const char* target = NULL, \ + nsIPluginStreamListener* streamListener = NULL, \ + const char* altHost = NULL, \ + const char* referrer = NULL, \ + PRBool forceJSEnabled = PR_FALSE); \ + NS_IMETHOD \ + PostURL(nsISupports* pluginInst, \ + const char* url, \ + PRUint32 postDataLen, \ + const char* postData, \ + PRBool isFile = PR_FALSE, \ + const char* target = NULL, \ + nsIPluginStreamListener* streamListener = NULL, \ + const char* altHost = NULL, \ + const char* referrer = NULL, \ + PRBool forceJSEnabled = PR_FALSE, \ + PRUint32 postHeadersLength = 0, \ + const char* postHeaders = NULL); \ + NS_IMETHOD \ + GetURLWithHeaders(nsISupports* pluginInst, \ + const char* url, \ + const char* target = NULL, \ + nsIPluginStreamListener* streamListener = NULL, \ + const char* altHost = NULL, \ + const char* referrer = NULL, \ + PRBool forceJSEnabled = PR_FALSE, \ + PRUint32 getHeadersLength = 0, \ + const char* getHeaders = NULL); + +// Avoid dependence on the nsIsupportsImpl.h and so on. +#ifndef NS_DECL_ISUPPORTS +#define NS_DECL_ISUPPORTS \ + NS_IMETHOD QueryInterface(REFNSIID aIID, \ + void** aInstancePtr); \ + NS_IMETHOD_(nsrefcnt) AddRef(void); \ + NS_IMETHOD_(nsrefcnt) Release(void); +#endif // NS_DECL_ISUPPORTS + +namespace NPAPI +{ + +class PluginInstance; + +// Implementation of extended Mozilla interfaces needed to support +// Sun's new Java plugin. +class MozillaExtensionApi : public nsIServiceManager, + public nsIPluginManager2, + public nsICookieStorage { + public: + MozillaExtensionApi(PluginInstance* plugin_instance) : + plugin_instance_(plugin_instance), ref_count_(0) { + } + + void DetachFromInstance(); + + NS_DECL_ISUPPORTS + NS_DECL_NSISERVICEMANAGER + NS_DECL_NSIPLUGINMANAGER_FIXED + NS_DECL_NSIPLUGINMANAGER2 + NS_DECL_NSICOOKIESTORAGE + + protected: + bool FindProxyForUrl(const char* url, std::string* proxy); + + protected: + scoped_refptr<NPAPI::PluginInstance> plugin_instance_; + unsigned long ref_count_; +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGINS_MOZILLA_EXTENSIONS_H__ diff --git a/webkit/glue/plugins/nphostapi.h b/webkit/glue/plugins/nphostapi.h new file mode 100644 index 0000000..a248fd6 --- /dev/null +++ b/webkit/glue/plugins/nphostapi.h @@ -0,0 +1,302 @@ +// Copyright 2008, 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. + +// TODO: Did not implement JRIGlobalRef function yet. Not sure if this is used? + +#ifndef WEBKIT_GLUE_PLUGIN_NPHOSTAPI_H__ +#define WEBKIT_GLUE_PLUGIN_NPHOSTAPI_H__ + +#include "bindings/npapi.h" +#include "bindings/npruntime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// +// NPAPI NPP Function Pointers +// +typedef NPError (*NPP_NewProcPtr)(NPMIMEType pluginType, + NPP instance, + uint16 mode, + int16 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* stype); +typedef NPError (*NPP_DestroyStreamProcPtr)(NPP instance, + NPStream* stream, + NPReason reason); +typedef int32 (*NPP_WriteReadyProcPtr)(NPP instance, + NPStream* stream); +typedef int32 (*NPP_WriteProcPtr)(NPP instance, + NPStream* stream, + int32 offset, + int32 len, + void* buffer); +typedef void (*NPP_StreamAsFileProcPtr)(NPP instance, + NPStream* stream, + const char* fname); +typedef void (*NPP_PrintProcPtr)(NPP instance, + NPPrint* platformPrint); +typedef int16 (*NPP_HandleEventProcPtr)(NPP instance, + void* event); +typedef void (*NPP_URLNotifyProcPtr)(NPP instance, + const char* url, + NPReason reason, + void* notifyData); +typedef void* JRIGlobalRef; //not using this right now +typedef NPError (*NPP_GetValueProcPtr)(NPP instance, + NPPVariable variable, + void *ret_alue); +typedef NPError (*NPP_SetValueProcPtr)(NPP instance, + NPNVariable variable, + void *ret_alue); + +// +// NPAPI NPN Function Pointers +// +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 len, + const char* buf, + NPBool file); +typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream, + NPByteRange* rangeList); +typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, + NPMIMEType type, + const char* window, + NPStream** stream); +typedef int32 (*NPN_WriteProcPtr)(NPP instance, + NPStream* stream, + int32 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 size); +typedef void (*NPN_MemFreeProcPtr)(void* ptr); +typedef uint32 (*NPN_MemFlushProcPtr)(uint32 size); +typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages); + +typedef void* (*NPN_GetJavaEnvProcPtr)(void); +typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance); + +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 len, + const char* buf, + NPBool file, + void* notifyData); +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 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 void (*NPN_PushPopupsEnabledStateProcPtr)(NPP npp, + NPBool enabled); +typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP npp); +typedef bool (*NPN_EnumerateProcPtr)(NPP npp, + NPObject *obj, + NPIdentifier **identifier, + uint32_t *count); +typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP instance, + void (*func)(void *), + void *userData); +typedef bool (*NPN_ConstructProcPtr)(NPP npp, + NPObject* obj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result); + +// +// NPAPI Function table of NPP functions (functions provided by plugin to host) +// +typedef struct _NPPluginFuncs { + unsigned short size; + unsigned short 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; + +// +// NPAPI Function table NPN functions (functions provided by host to plugin) +// +typedef struct _NPNetscapeFuncs { + uint16 size; + uint16 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; +} NPNetscapeFuncs; + +// +// NPAPI DLL entry points +// +typedef NPError (__stdcall * NP_InitializeFunc)(NPNetscapeFuncs* pFuncs); +typedef NPError (__stdcall * NP_GetEntryPointsFunc)(NPPluginFuncs* pFuncs); +typedef NPError (__stdcall * NP_ShutdownFunc)(void); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBKIT_GLUE_PLUGIN_NPHOSTAPI_H__ diff --git a/webkit/glue/plugins/plugin_data_stream.cc b/webkit/glue/plugins/plugin_data_stream.cc new file mode 100644 index 0000000..bb7ee0b --- /dev/null +++ b/webkit/glue/plugins/plugin_data_stream.cc @@ -0,0 +1,68 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_data_stream.h" + +namespace NPAPI { + +PluginDataStream::PluginDataStream(PluginInstance *instance, + const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified) + : PluginStream(instance, url.c_str(), false, 0), + mime_type_(mime_type), + headers_(headers), + expected_length_(expected_length), + last_modified_(last_modified), + stream_open_failed_(false) { +} + +PluginDataStream::~PluginDataStream() { +} + +void PluginDataStream::SendToPlugin(const char* buffer, int length) { + if (stream_open_failed_) + return; + + if (!open()) { + if (!Open(mime_type_, headers_, expected_length_, last_modified_)) { + stream_open_failed_ = true; + return; + } + } + + // TODO(iyengar) - check if it was not fully sent, and figure out a + // backup plan. + int written = Write(buffer, length); + DCHECK(written == length); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_data_stream.h b/webkit/glue/plugins/plugin_data_stream.h new file mode 100644 index 0000000..64cc3b1 --- /dev/null +++ b/webkit/glue/plugins/plugin_data_stream.h @@ -0,0 +1,66 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_DATA_STREAM_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_DATA_STREAM_H__ + +#include "webkit/glue/plugins/plugin_stream.h" + +namespace NPAPI { + +class PluginInstance; + +// A NPAPI stream based on data received from the renderer. +class PluginDataStream : public PluginStream { + public: + // Create a new stream for sending to the plugin. + PluginDataStream(PluginInstance *instance, const std::string& url, + const std::string& mime_type, const std::string& headers, + uint32 expected_length, uint32 last_modified); + virtual ~PluginDataStream(); + + // Initiates the sending of data to the plugin. + void SendToPlugin(const char* buffer, int length); + + private: + std::string mime_type_; + std::string headers_; + uint32 expected_length_; + uint32 last_modified_; + // This flag when set serves as an indicator that subsequent + // data coming from the renderer should not be handed off to the plugin. + bool stream_open_failed_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginDataStream); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_DATA_STREAM_H__ + diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc new file mode 100644 index 0000000..63cd6ca --- /dev/null +++ b/webkit/glue/plugins/plugin_host.cc @@ -0,0 +1,877 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_host.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "webkit/default_plugin/default_plugin_shared.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/plugins/plugin_stream_url.h" +#include "third_party/npapi/bindings/npruntime.h" + +extern "C" { + +// FindInstance() +// Finds a PluginInstance from an NPP. +// The caller must take a reference if needed. +NPAPI::PluginInstance* FindInstance(NPP id) { + if (id == NULL) { + NOTREACHED(); + return NULL; + } + + return (NPAPI::PluginInstance *)id->ndata; +} + +namespace NPAPI +{ +scoped_refptr<PluginHost> PluginHost::singleton_; + +static const int kFlashMessageThrottleDelayMs = 10; + +PluginHost::PluginHost() +#pragma warning(suppress: 4355) // can use this + : throttle_factory_(this) { + InitializeHostFuncs(); +} + +PluginHost::~PluginHost() { +} + +PluginHost *PluginHost::Singleton() { + if (singleton_.get() == NULL) { + singleton_ = new PluginHost(); + } + + DCHECK(singleton_.get() != NULL); + return singleton_; +} + +void PluginHost::InitializeHostFuncs() { + memset(&host_funcs_, 0, sizeof(host_funcs_)); + host_funcs_.size = sizeof(host_funcs_); + host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR); + + // The "basic" functions + host_funcs_.geturl = &NPN_GetURL; + host_funcs_.posturl = &NPN_PostURL; + host_funcs_.requestread = &NPN_RequestRead; + host_funcs_.newstream = &NPN_NewStream; + host_funcs_.write = &NPN_Write; + host_funcs_.destroystream = &NPN_DestroyStream; + host_funcs_.status = &NPN_Status; + host_funcs_.uagent = &NPN_UserAgent; + host_funcs_.memalloc = &NPN_MemAlloc; + host_funcs_.memfree = &NPN_MemFree; + host_funcs_.memflush = &NPN_MemFlush; + host_funcs_.reloadplugins = &NPN_ReloadPlugins; + + // We don't implement java yet + host_funcs_.getJavaEnv = &NPN_GetJavaEnv; + host_funcs_.getJavaPeer = &NPN_GetJavaPeer; + + // Advanced functions we implement + host_funcs_.geturlnotify = &NPN_GetURLNotify; + host_funcs_.posturlnotify = &NPN_PostURLNotify; + host_funcs_.getvalue = &NPN_GetValue; + host_funcs_.setvalue = &NPN_SetValue; + host_funcs_.invalidaterect = &NPN_InvalidateRect; + host_funcs_.invalidateregion = &NPN_InvalidateRegion; + host_funcs_.forceredraw = &NPN_ForceRedraw; + + // These come from the Javascript Engine + host_funcs_.getstringidentifier = NPN_GetStringIdentifier; + host_funcs_.getstringidentifiers = NPN_GetStringIdentifiers; + host_funcs_.getintidentifier = NPN_GetIntIdentifier; + host_funcs_.identifierisstring = NPN_IdentifierIsString; + host_funcs_.utf8fromidentifier = NPN_UTF8FromIdentifier; + host_funcs_.intfromidentifier = NPN_IntFromIdentifier; + host_funcs_.createobject = NPN_CreateObject; + host_funcs_.retainobject = NPN_RetainObject; + host_funcs_.releaseobject = NPN_ReleaseObject; + host_funcs_.invoke = NPN_Invoke; + host_funcs_.invokeDefault = NPN_InvokeDefault; + host_funcs_.evaluate = NPN_Evaluate; + host_funcs_.getproperty = NPN_GetProperty; + host_funcs_.setproperty = NPN_SetProperty; + host_funcs_.removeproperty = NPN_RemoveProperty; + host_funcs_.hasproperty = NPN_HasProperty; + host_funcs_.hasmethod = NPN_HasMethod; + host_funcs_.releasevariantvalue = NPN_ReleaseVariantValue; + host_funcs_.setexception = NPN_SetException; + host_funcs_.pushpopupsenabledstate = &NPN_PushPopupsEnabledState; + host_funcs_.poppopupsenabledstate = &NPN_PopPopupsEnabledState; + host_funcs_.enumerate = &NPN_Enumerate; + host_funcs_.pluginthreadasynccall = &NPN_PluginThreadAsyncCall; + host_funcs_.construct = &NPN_Construct; + +} + +void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) { + // When running in the plugin process, we need to patch the NPN functions + // that the plugin calls to interact with NPObjects that we give. Otherwise + // the plugin will call the v8 NPN functions, which won't work since we have + // an NPObjectProxy and not a real v8 implementation. + if (overrides->invoke) + host_funcs_.invoke = overrides->invoke; + + if (overrides->invokeDefault) + host_funcs_.invokeDefault = overrides->invokeDefault; + + if (overrides->evaluate) + host_funcs_.evaluate = overrides->evaluate; + + if (overrides->getproperty) + host_funcs_.getproperty = overrides->getproperty; + + if (overrides->setproperty) + host_funcs_.setproperty = overrides->setproperty; + + if (overrides->removeproperty) + host_funcs_.removeproperty = overrides->removeproperty; + + if (overrides->hasproperty) + host_funcs_.hasproperty = overrides->hasproperty; + + if (overrides->hasmethod) + host_funcs_.hasmethod = overrides->hasmethod; + + if (overrides->setexception) + host_funcs_.setexception = overrides->setexception; + + if (overrides->enumerate) + host_funcs_.enumerate = overrides->enumerate; +} + +void PluginHost::InvalidateRect(NPP id, NPRect* invalidRect) { + if (!invalidRect) { + NOTREACHED(); + return; + } + + // Invalidates specified drawing area prior to repainting or refreshing a + // windowless plugin + + // Before a windowless plugin can refresh part of its drawing area, it must + // first invalidate it. This function causes the NPP_HandleEvent method to + // pass an update event or a paint message to the plug-in. After calling + // this method, the plug-in recieves a paint message asynchronously. + + // The browser redraws invalid areas of the document and any windowless + // plug-ins at regularly timed intervals. To force a paint message, the + // plug-in can call NPN_ForceRedraw after calling this method. + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + DCHECK(plugin.get() != NULL); + + if (plugin.get() && plugin->webplugin()) { + if (plugin->throttle_invalidate()) { + // We need to track plugin invalidates on a per instance basis. + ThrottledInvalidates plugin_instance_invalidates; + InstanceThrottledInvalidatesMap::iterator invalidate_index = + instance_throttled_invalidates_.find(id); + if (invalidate_index != instance_throttled_invalidates_.end()) { + plugin_instance_invalidates = (*invalidate_index).second; + } + + bool throttle_active = + (plugin_instance_invalidates.throttled_invalidates.size() > 0); + + gfx::Rect rect(invalidRect->left, + invalidRect->top, + invalidRect->right - invalidRect->left, + invalidRect->bottom - invalidRect->top); + + plugin_instance_invalidates.throttled_invalidates.push_back(rect); + + if (!throttle_active) { + // We hold a reference to the plugin instance to avoid race conditions + // due to the instance being released before the OnInvalidateRect + // function is invoked. + plugin->AddRef(); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + throttle_factory_.NewRunnableMethod(&PluginHost::OnInvalidateRect, + id, plugin.get()), + kFlashMessageThrottleDelayMs); + } + instance_throttled_invalidates_[id] = plugin_instance_invalidates; + } else { + gfx::Rect rect(invalidRect->left, + invalidRect->top, + invalidRect->right - invalidRect->left, + invalidRect->bottom - invalidRect->top); + plugin->webplugin()->InvalidateRect(rect); + } + } +} + +bool PluginHost::SetPostData(const char *buf, + uint32 length, + std::vector<std::string>* names, + std::vector<std::string>* values, + std::vector<char>* body) { + // Use a state table to do the parsing. Whitespace must be + // trimmed after the fact if desired. In our case, we actually + // don't care about the whitespace, because we're just going to + // pass this back into another POST. This function strips out the + // "Content-length" header and does not append it to the request. + + // + // This parser takes action only on state changes. + // + // Transition table: + // : \n NULL Other + // 0 GetHeader 1 2 4 0 + // 1 GetValue 1 0 3 1 + // 2 GetData 2 2 3 2 + // 3 DONE + // 4 ERR + // + enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER }; + enum { GETNAME, GETVALUE, GETDATA, DONE, ERR }; + int statemachine[3][4] = { { GETVALUE, GETDATA, ERR, GETNAME }, + { GETVALUE, GETNAME, DONE, GETVALUE }, + { GETDATA, GETDATA, DONE, GETDATA } }; + std::string name, value; + char *ptr = (char*)buf; + char *start = ptr; + int state = GETNAME; // initial state + bool done = false; + bool err = false; + do { + int input; + + // Translate the current character into an input + // for the state table. + switch (*ptr) { + case ':' : + input = INPUT_COLON; + break; + case '\n': + input = INPUT_NEWLINE; + break; + case 0 : + input = INPUT_NULL; + break; + default : + input = INPUT_OTHER; + break; + } + + int newstate = statemachine[state][input]; + + // Take action based on the new state. + if (state != newstate) { + switch (newstate) { + case GETNAME: + // Got a value. + value = std::string(start, ptr - start); + TrimWhitespace(value, TRIM_ALL, &value); + // If the name field is empty, we'll skip this header + // but we won't error out. + if (!name.empty() && name != "content-length") { + names->push_back(name); + values->push_back(value); + } + start = ptr + 1; + break; + case GETVALUE: + // Got a header. + name = StringToLowerASCII(std::string(start, ptr - start)); + TrimWhitespace(name, TRIM_ALL, &name); + start = ptr + 1; + break; + case GETDATA: + { + // Finished headers, now get body. + start = ptr + 1; + size_t previous_size = body->size(); + size_t new_body_size = length - static_cast<int>(start - buf); + body->resize(previous_size + new_body_size); + if (!body->empty()) + memcpy(&body->front() + previous_size, start, new_body_size); + done = true; + break; + } + case ERR: + // error + err = true; + done = true; + break; + } + } + state = newstate; + ptr++; + } while (!done); + + return !err; +} + +void PluginHost::OnInvalidateRect(NPP id, PluginInstance* instance) { + if (!instance) { + NOTREACHED(); + return; + } + + InstanceThrottledInvalidatesMap::iterator invalidate_index = + instance_throttled_invalidates_.find(id); + if (invalidate_index == instance_throttled_invalidates_.end()) { + NOTREACHED(); + instance->Release(); + return; + } + + ThrottledInvalidates plugin_instance_invalidates = + (*invalidate_index).second; + + if (instance->webplugin()) { + for (unsigned int throttle_index = 0; + throttle_index < + plugin_instance_invalidates.throttled_invalidates.size(); + throttle_index++) { + instance->webplugin()->InvalidateRect( + plugin_instance_invalidates.throttled_invalidates[throttle_index]); + } + } + + instance->Release(); + instance_throttled_invalidates_.erase(invalidate_index); +} + +} // namespace NPAPI + +// Allocates memory from the host's memory space. +void* NPN_MemAlloc(uint32 size) { + scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton(); + if (host != NULL) { + // Note: We must use the same allocator/deallocator + // that is used by the javascript library, as some of the + // JS APIs will pass memory to the plugin which the plugin + // will attempt to free. + return malloc(size); + } + return NULL; +} + +// Deallocates memory from the host's memory space +void NPN_MemFree(void* ptr) { + scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton(); + if (host != NULL) { + if (ptr != NULL && ptr != (void*)-1) { + free(ptr); + } + } +} + +// Requests that the host free a specified amount of memory. +uint32 NPN_MemFlush(uint32 size) { + // This is not relevant on Windows; MAC specific + return size; +} + +// This is for dynamic discovery of new plugins. +// Should force a re-scan of the plugins directory to load new ones. +void NPN_ReloadPlugins(NPBool reloadPages) { + // TODO: implement me +} + +// Requests a range of bytes for a seekable stream. +NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) { + // TODO: implement me + return NPERR_GENERIC_ERROR; +} + +static bool IsJavaScriptUrl(const std::string& url) { + return StartsWithASCII(url, "javascript:", false); +} + +// Generic form of GetURL for common code between +// GetURL() and GetURLNotify(). +static NPError GetURLNotify(NPP id, + const char* url, + const char* target, + bool notify, + void* notify_data) { + if (!url) + return NPERR_INVALID_URL; + + bool is_javascript_url = IsJavaScriptUrl(url); + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get()) { + plugin->webplugin()->HandleURLRequest("GET", + is_javascript_url, + target, + 0, + 0, + false, + notify, + url, + notify_data, + plugin->popups_allowed()); + } else { + NOTREACHED(); + return NPERR_GENERIC_ERROR; + } + return NPERR_NO_ERROR; +} + +// Requests creation of a new stream with the contents of the +// specified URL; gets notification of the result. +NPError NPN_GetURLNotify(NPP id, + const char* url, + const char* target, + void* notify_data) { + // This is identical to NPN_GetURL, but after finishing, the + // browser will call NPP_URLNotify to inform the plugin that + // it has completed. + + // According to the NPAPI documentation, if target == _self + // or a parent to _self, the browser should return NPERR_INVALID_PARAM, + // because it can't notify the plugin once deleted. This is + // absolutely false; firefox doesn't do this, and Flash relies on + // being able to use this. + + // Also according to the NPAPI documentation, we should return + // NPERR_INVALID_URL if the url requested is not valid. However, + // this would require that we synchronously start fetching the + // URL. That just isn't practical. As such, there really is + // no way to return this error. From looking at the Firefox + // implementation, it doesn't look like Firefox does this either. + + return GetURLNotify(id, url, target, true, notify_data); +} + +NPError NPN_GetURL(NPP id, const char* url, const char* target) { + // Notes: + // Request from the Plugin to fetch content either for the plugin + // or to be placed into a browser window. + // + // If target == null, the browser fetches content and streams to plugin. + // otherwise, the browser loads content into an existing browser frame. + // If the target is the window/frame containing the plugin, the plugin + // may be destroyed. + // If the target is _blank, a mailto: or news: url open content in a new + // browser window + // If the target is _self, no other instance of the plugin is created. The + // plugin continues to operate in its own window + + return GetURLNotify(id, url, target, false, 0); +} + +// Generic form of PostURL for common code between +// PostURL() and PostURLNotify(). +static NPError PostURLNotify(NPP id, + const char* url, + const char* target, + uint32 len, + const char* buf, + NPBool file, + bool notify, + void* notify_data) { + if (!url) + return NPERR_INVALID_URL; + + if (file) { + // Unfortunately, our WebKit requests can support files which + // contain *only* data. But the files from NPAPI contain + // headers + data! So, we need to read the file, extract + // the headers, write the data back to a new file, and then + // finally we can post the file. TODO: Implement me! + // TODO: implement me + NOTREACHED(); + return NPERR_GENERIC_ERROR; + } + + bool is_javascript_url = IsJavaScriptUrl(url); + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get()) { + // The post data sent by a plugin contains both headers + // and post data. Example: + // Content-type: text/html + // Content-length: 200 + // + // <200 bytes of content here> + // + // Unfortunately, our stream needs these broken apart, + // so we need to parse the data and set headers and data + // separately. + plugin->webplugin()->HandleURLRequest("POST", + is_javascript_url, + target, + len, + buf, + file ? true : false, + notify, + url, + notify_data, + plugin->popups_allowed()); + return NPERR_NO_ERROR; + } else { + NOTREACHED(); + } + return NPERR_GENERIC_ERROR; +} + +NPError NPN_PostURLNotify(NPP id, + const char* url, + const char* target, + uint32 len, + const char* buf, + NPBool file, + void* notify_data) { + return PostURLNotify(id, url, target, len, buf, file, true, notify_data); +} + +NPError NPN_PostURL(NPP id, + const char* url, + const char* target, + uint32 len, + const char* buf, + NPBool file) { + // POSTs data to an URL, either from a temp file or a buffer. + // If file is true, buf contains a temp file (which host will delete after + // completing), and len contains the length of the filename. + // If file is false, buf contains the data to send, and len contains the + // length of the buffer + // + // If target is null, + // server response is returned to the plugin + // If target is _current, _self, or _top, + // server response is written to the plugin window and plugin is unloaded. + // If target is _new or _blank, + // server response is written to a new browser window + // If target is an existing frame, + // server response goes to that frame. + // + // For protocols other than FTP + // file uploads must be line-end converted from \r\n to \n + // + // Note: you cannot specify headers (even a blank line) in a memory buffer, + // use NPN_PostURLNotify + + return PostURLNotify(id, url, target, len, buf, file, false, 0); +} + +NPError NPN_NewStream(NPP id, + NPMIMEType type, + const char* target, + NPStream** stream) { + // Requests creation of a new data stream produced by the plugin, + // consumed by the browser. + // + // Browser should put this stream into a window target. + // + + // TODO: implement me + return NPERR_GENERIC_ERROR; +} + +int32 NPN_Write(NPP id, NPStream* stream, int32 len, void* buffer) { + // Writes data to an existing Plugin-created stream. + + // TODO: implement me + return NPERR_GENERIC_ERROR; +} + +NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) { + // Destroys a stream (could be created by plugin or browser). + // + // Reasons: + // NPRES_DONE - normal completion + // NPRES_USER_BREAK - user terminated + // NPRES_NETWORK_ERROR - network error (all errors fit here?) + // + // + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get() == NULL) { + NOTREACHED(); + return NPERR_GENERIC_ERROR; + } + + return plugin->NPP_DestroyStream(stream, reason); +} + +const char* NPN_UserAgent(NPP id) { + // Flash passes in a null id during the NP_initialize call. We need to + // default to the Mozilla user agent if we don't have an NPP instance or + // else Flash won't request windowless mode. + if (id) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get() && !plugin->use_mozilla_user_agent()) + return webkit_glue::GetDefaultUserAgent().c_str(); + } + + static const char *UA = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20061103 Firefox/2.0a1"; + return UA; +} + +void NPN_Status(NPP id, const char* message) { + // Displays a message on the status line of the browser window. + + // TODO: implement me +} + +void NPN_InvalidateRect(NPP id, NPRect *invalidRect) { + scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton(); + if (host != NULL) { + host->InvalidateRect(id, invalidRect); + } +} + +void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) { + // Invalidates a specified drawing region prior to repainting + // or refreshing a window-less plugin. + // + // Similar to NPN_InvalidateRect. + + // TODO: implement me +} + +void NPN_ForceRedraw(NPP id) { + // Forces repaint for a windowless plug-in. + // + // Once a value has been invalidated with NPN_InvalidateRect/ + // NPN_InvalidateRegion, ForceRedraw can be used to force a paint message. + // + // The plugin will receive a WM_PAINT message, the lParam of the WM_PAINT + // message holds a pointer to an NPRect that is the bounding box of the + // update area. + // Since the plugin and browser share the same HDC, before drawing, the + // plugin is responsible fro saving the current HDC settings, setting up + // its own environment, drawing, and restoring the HDC to the previous + // settings. The HDC settings must be restored whenever control returns + // back to the browser, either before returning from NPP_HandleEvent or + // before calling a drawing-related netscape method. + // + + // TODO: implement me +} + +NPError NPN_GetValue(NPP id, NPNVariable variable, void *value) { + // Allows the plugin to query the browser for information + // + // Variables: + // NPNVxDisplay (unix only) + // NPNVxtAppContext (unix only) + // NPNVnetscapeWindow (win only) - Gets the native window on which the + // plug-in drawing occurs, returns HWND + // NPNVjavascriptEnabledBool: tells whether Javascript is enabled + // NPNVasdEnabledBool: tells whether SmartUpdate is enabled + // NPNVOfflineBool: tells whether offline-mode is enabled + + NPError rv = NPERR_GENERIC_ERROR; + + switch (variable) { + case NPNVWindowNPObject: + { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject(); + // Return value is expected to be retained, as + // described here: + // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> + if (np_object) { + NPN_RetainObject(np_object); + void **v = (void **)value; + *v = np_object; + rv = NPERR_NO_ERROR; + } else { + NOTREACHED(); + } + break; + } + case NPNVPluginElementNPObject: + { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + NPObject *np_object = plugin->webplugin()->GetPluginElement(); + // Return value is expected to be retained, as + // described here: + // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> + if (np_object) { + NPN_RetainObject(np_object); + void **v = (void **)value; + *v = np_object; + rv = NPERR_NO_ERROR; + } else { + NOTREACHED(); + } + break; + } + case NPNVnetscapeWindow: + { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + HWND handle = plugin->window_handle(); + *((void**)value) = (void*)handle; + rv = NPERR_NO_ERROR; + break; + } + case NPNVjavascriptEnabledBool: + { + // yes, JS is enabled. + *((void**)value) = (void*)1; + rv = NPERR_NO_ERROR; + break; + } + case NPNVserviceManager: + { + NPAPI::PluginInstance* instance = + NPAPI::PluginInstance::GetInitializingInstance(); + if (instance) { + instance->GetServiceManager(reinterpret_cast<void**>(value)); + } else { + NOTREACHED(); + } + + rv = NPERR_NO_ERROR; + break; + } + case default_plugin::kMissingPluginStatusStart + + default_plugin::MISSING_PLUGIN_AVAILABLE: + // fall through + case default_plugin::kMissingPluginStatusStart + + default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD: + { + // This is a hack for the default plugin to send notification to renderer. + // Because we will check if the plugin is default plugin, we don't need + // to worry about future standard change that may conflict with the + // variable definition. + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin->plugin_lib()->plugin_info().file == kDefaultPluginDllName) + plugin->webplugin()->OnMissingPluginStatus( + variable - default_plugin::kMissingPluginStatusStart); + break; + } + default: + { + // TODO: implement me + break; + } + } + return rv; +} + +NPError NPN_SetValue(NPP id, NPPVariable variable, void *value) { + // Allows the plugin to set various modes + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + switch(variable) + { + case NPPVpluginWindowBool: + { + // Sets windowless mode for display of the plugin + // Note: the documentation at http://developer.mozilla.org/en/docs/NPN_SetValue + // is wrong. When value is NULL, the mode is set to true. This is the same + // way Mozilla works. + bool mode = (value == 0); + plugin->set_windowless(mode); + return NPERR_NO_ERROR; + } + case NPPVpluginTransparentBool: + { + // Sets transparent mode for display of the plugin + // + // Transparent plugins require the browser to paint the background + // before having the plugin paint. By default, windowless plugins + // are transparent. Making a windowless plugin opaque means that + // the plugin does not require the browser to paint the background. + // + bool mode = (value != 0); + plugin->set_transparent(mode); + return NPERR_NO_ERROR; + } + case NPPVjavascriptPushCallerBool: + // Specifies whether you are pushing or popping the JSContext off + // the stack + // TODO: implement me + return NPERR_GENERIC_ERROR; + case NPPVpluginKeepLibraryInMemory: + // Tells browser that plugin dll should live longer than usual. + // TODO: implement me + return NPERR_GENERIC_ERROR; + default: + // TODO: implement me + break; + } + + NOTREACHED(); + return NPERR_GENERIC_ERROR; +} + +void *NPN_GetJavaEnv() { + // TODO: implement me + return NULL; +} + +void *NPN_GetJavaPeer(NPP) { + // TODO: implement me + return NULL; +} + +void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin) { + plugin->PushPopupsEnabledState(enabled); + } +} + +void NPN_PopPopupsEnabledState(NPP id) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin) { + plugin->PopPopupsEnabledState(); + } +} + +void NPN_PluginThreadAsyncCall(NPP id, + void (*func)(void *), + void *userData) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin) { + plugin->PluginThreadAsyncCall(func, userData); + } +} + +bool NPN_Construct(NPP npp, + NPObject* obj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result) { + NOTREACHED(); + return false; +} + +} // extern "C" diff --git a/webkit/glue/plugins/plugin_host.h b/webkit/glue/plugins/plugin_host.h new file mode 100644 index 0000000..61f03c5 --- /dev/null +++ b/webkit/glue/plugins/plugin_host.h @@ -0,0 +1,107 @@ +// Copyright 2008, 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. + +// TODO: Need mechanism to cleanup the static instance + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__ + +#include <string> +#include <vector> +#include <map> + +#include "base/ref_counted.h" +#include "base/gfx/rect.h" +#include "base/task.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +namespace NPAPI +{ +class PluginInstance; + +// The Plugin Host implements the NPN_xxx functions for NPAPI plugins. +// These are the functions exposed from the Plugin Host for use +// by the Plugin. +// +// The PluginHost is managed as a singleton. This isn't strictly +// necessary, but since the callback functions are all global C +// functions, there is really no point in having per-instance PluginHosts. +class PluginHost : public base::RefCounted<PluginHost> { + public: + // Access the single PluginHost instance. Callers + // must call deref() when finished with the object. + static PluginHost *Singleton(); + virtual ~PluginHost(); + + // The table of functions provided to the plugin. + NPNetscapeFuncs *host_functions() { return &host_funcs_; } + + // Helper function for parsing post headers, and applying attributes + // to the stream. NPAPI post data include headers + data combined. + // This function parses it out and adds it to the stream in a WebKit + // style. + static bool SetPostData(const char *buf, + uint32 length, + std::vector<std::string>* names, + std::vector<std::string>* values, + std::vector<char>* body); + + void PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides); + + // Handles invalidateRect requests for windowless plugins. + void InvalidateRect(NPP id, NPRect* invalidRect); + + private: + PluginHost(); + void InitializeHostFuncs(); + // For certain plugins like flash we need to throttle invalidateRect + // requests as they are made at a high frequency. + void OnInvalidateRect(NPP id, PluginInstance* instance); + + static scoped_refptr<PluginHost> singleton_; + NPNetscapeFuncs host_funcs_; + DISALLOW_EVIL_CONSTRUCTORS(PluginHost); + + // This structure keeps track of individual plugin instance invalidates. + struct ThrottledInvalidates { + std::vector<gfx::Rect> throttled_invalidates; + }; + + // We need to track throttled invalidate rects on a per plugin instance + // basis. + typedef std::map<NPP, ThrottledInvalidates> InstanceThrottledInvalidatesMap; + InstanceThrottledInvalidatesMap instance_throttled_invalidates_; + + ScopedRunnableMethodFactory<PluginHost> throttle_factory_; +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__ diff --git a/webkit/glue/plugins/plugin_instance.cc b/webkit/glue/plugins/plugin_instance.cc new file mode 100644 index 0000000..62e1357 --- /dev/null +++ b/webkit/glue/plugins/plugin_instance.cc @@ -0,0 +1,463 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_instance.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/thread_local_storage.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_data_stream.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/plugins/plugin_stream_url.h" +#include "webkit/glue/plugins/plugin_string_stream.h" +#include "webkit/glue/plugins/mozilla_extensions.h" +#include "net/base/escape.h" + +namespace NPAPI +{ +int PluginInstance::plugin_instance_tls_index_ = ThreadLocalStorage::Alloc(); + +PluginInstance::PluginInstance(PluginLib *plugin, const std::string &mime_type) + : plugin_(plugin), + npp_(0), + host_(PluginHost::Singleton()), + npp_functions_(plugin->functions()), + hwnd_(0), + windowless_(false), + transparent_(true), + mime_type_(mime_type), + webplugin_(0), + use_mozilla_user_agent_(false), + message_loop_(MessageLoop::current()), + load_manually_(false), + throttle_invalidate_(false), + get_notify_data_(NULL), + in_close_streams_(false) { + npp_ = new NPP_t(); + npp_->ndata = 0; + npp_->pdata = 0; + + memset(&zero_padding_, 0, sizeof(zero_padding_)); + DCHECK(message_loop_); +} + +PluginInstance::~PluginInstance() { + CloseStreams(); + + if (npp_ != 0) { + delete npp_; + npp_ = 0; + } + + if (plugin_) + plugin_->CloseInstance(); +} + +PluginStreamUrl *PluginInstance::CreateStream(int resource_id, + const std::string &url, + const std::string &mime_type, + bool notify_needed, + void *notify_data) { + PluginStreamUrl *stream = new PluginStreamUrl( + resource_id, GURL(url), this, notify_needed, notify_data); + + AddStream(stream); + return stream; +} + +void PluginInstance::SendStream(const std::string &url, + bool notify_needed, + void *notify_data) { + if (notify_needed) { + host_->host_functions()->geturlnotify(npp(), url.c_str(), NULL, + notify_data); + } else { + host_->host_functions()->geturl(npp(), url.c_str(), NULL); + } +} + +void PluginInstance::AddStream(PluginStream* stream) { + open_streams_.push_back(stream); +} + +void PluginInstance::RemoveStream(PluginStream* stream) { + if (in_close_streams_) + return; + + std::vector<scoped_refptr<PluginStream> >::iterator stream_index; + for (stream_index = open_streams_.begin(); + stream_index != open_streams_.end(); ++stream_index) { + if (*stream_index == stream) { + open_streams_.erase(stream_index); + break; + } + } +} + +bool PluginInstance::IsValidStream(const NPStream* stream) { + std::vector<scoped_refptr<PluginStream> >::iterator stream_index; + for (stream_index = open_streams_.begin(); + stream_index != open_streams_.end(); ++stream_index) { + if ((*stream_index)->stream() == stream) + return true; + } + + return false; +} + +void PluginInstance::CloseStreams() { + in_close_streams_ = true; + for (unsigned int index = 0; index < open_streams_.size(); ++index) { + // Close all streams on the way down. + open_streams_[index]->Close(NPRES_USER_BREAK); + } + open_streams_.clear(); + in_close_streams_ = false; +} + +bool PluginInstance::HandleEvent(UINT message, WPARAM wParam, LPARAM lParam) { + if (!windowless_) + return false; + + NPEvent windowEvent; + windowEvent.event = message; + windowEvent.lParam = static_cast<uint32>(lParam); + windowEvent.wParam = static_cast<uint32>(wParam); + return NPP_HandleEvent(&windowEvent) != 0; +} + +bool PluginInstance::Start(const GURL& url, + char** const param_names, + char** const param_values, + int param_count, + bool load_manually) { + load_manually_ = load_manually; + instance_url_ = url; + unsigned short mode = load_manually_ ? NP_FULL : NP_EMBED; + npp_->ndata = this; + + NPError err = NPP_New(mode, param_count, + const_cast<char **>(param_names), const_cast<char **>(param_values)); + return err == NPERR_NO_ERROR; +} + +NPObject *PluginInstance::GetPluginScriptableObject() { + NPObject *value; + NPError error = NPP_GetValue(NPPVpluginScriptableNPObject, &value); + if (error != NPERR_NO_ERROR) + return NULL; + return value; +} + +void PluginInstance::SetURLLoadData(const GURL& url, + void* notify_data) { + get_url_ = url; + get_notify_data_ = notify_data; +} + +// WebPluginLoadDelegate methods +void PluginInstance::DidFinishLoadWithReason(NPReason reason) { + if (!get_url_.is_empty()) { + NPP_URLNotify(get_url_.spec().c_str(), reason, get_notify_data_); + } + + get_url_ = GURL(); + get_notify_data_ = NULL; +} + +// NPAPI methods +NPError PluginInstance::NPP_New(unsigned short mode, + short argc, + char *argn[], + char *argv[]) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->newp != 0); + DCHECK(argc >= 0); + + if (npp_functions_->newp != 0) { + return npp_functions_->newp( + (NPMIMEType)mime_type_.c_str(), npp_, mode, argc, argn, argv, NULL); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +void PluginInstance::NPP_Destroy() { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->newp != 0); + + if (npp_functions_->destroy != 0) { + NPSavedData *savedData = 0; + npp_functions_->destroy(npp_, &savedData); + + // TODO: Support savedData. Technically, these need to be + // saved on a per-URL basis, and then only passed + // to new instances of the plugin at the same URL. + // Sounds like a huge security risk. When we do support + // these, we should pass them back to the PluginLib + // to be stored there. + DCHECK(savedData == 0); + } + + // Clean up back references to this instance if any + if (mozilla_extenstions_) { + mozilla_extenstions_->DetachFromInstance(); + mozilla_extenstions_ = NULL; + } +} + +NPError PluginInstance::NPP_SetWindow(NPWindow *window) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->setwindow != 0); + + if (npp_functions_->setwindow != 0) { + return npp_functions_->setwindow(npp_, window); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +NPError PluginInstance::NPP_NewStream(NPMIMEType type, + NPStream *stream, + NPBool seekable, + unsigned short *stype) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->newstream != 0); + if (npp_functions_->newstream != 0) { + return npp_functions_->newstream(npp_, type, stream, seekable, stype); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +NPError PluginInstance::NPP_DestroyStream(NPStream *stream, NPReason reason) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->destroystream != 0); + + if (stream == NULL || (stream->ndata == NULL) || + !IsValidStream(stream)) + return NPERR_INVALID_INSTANCE_ERROR; + + if (npp_functions_->destroystream != 0) { + NPError result = npp_functions_->destroystream(npp_, stream, reason); + stream->ndata = NULL; + return result; + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +int PluginInstance::NPP_WriteReady(NPStream *stream) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->writeready != 0); + if (npp_functions_->writeready != 0) { + return npp_functions_->writeready(npp_, stream); + } + return NULL; +} + +int PluginInstance::NPP_Write(NPStream *stream, + int offset, + int len, + void *buffer) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->write != 0); + if (npp_functions_->write != 0) { + return npp_functions_->write(npp_, stream, offset, len, buffer); + } + return NULL; +} + +void PluginInstance::NPP_StreamAsFile(NPStream *stream, const char *fname) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->asfile != 0); + if (npp_functions_->asfile != 0) { + npp_functions_->asfile(npp_, stream, fname); + } +} + +void PluginInstance::NPP_URLNotify(const char *url, + NPReason reason, + void *notifyData) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->urlnotify != 0); + if (npp_functions_->urlnotify != 0) { + npp_functions_->urlnotify(npp_, url, reason, notifyData); + } +} + +NPError PluginInstance::NPP_GetValue(NPPVariable variable, void *value) { + DCHECK(npp_functions_ != 0); + // getvalue is NULL for Shockwave + if (npp_functions_->getvalue != 0) { + return npp_functions_->getvalue(npp_, variable, value); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +NPError PluginInstance::NPP_SetValue(NPNVariable variable, void *value) { + DCHECK(npp_functions_ != 0); + if (npp_functions_->setvalue != 0) { + return npp_functions_->setvalue(npp_, variable, value); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +short PluginInstance::NPP_HandleEvent(NPEvent *event) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->event != 0); + if (npp_functions_->event != 0) { + return npp_functions_->event(npp_, (void*)event); + } + return false; +} + +bool PluginInstance::NPP_Print(NPPrint* platform_print) { + DCHECK(npp_functions_ != 0); + if (npp_functions_->print != 0) { + npp_functions_->print(npp_, platform_print); + return true; + } + return false; +} + +void PluginInstance::SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + if (success) { + PluginStringStream *stream = + new PluginStringStream(this, url, notify_needed, + reinterpret_cast<void*>(notify_data)); + AddStream(stream); + stream->SendToPlugin(WideToUTF8(result), "text/html"); + } else { + // NOTE: Sending an empty stream here will crash MacroMedia + // Flash 9. Just send the URL Notify. + if (notify_needed) { + this->NPP_URLNotify(url.c_str(), NPRES_DONE, + reinterpret_cast<void*>(notify_data)); + } + } +} + +void PluginInstance::DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified) { + DCHECK(load_manually_); + std::string response_url = url; + if (response_url.empty()) { + response_url = instance_url_.spec(); + } + + plugin_data_stream_ = new PluginDataStream(this, response_url, mime_type, + headers, expected_length, + last_modified); + AddStream(plugin_data_stream_.get()); +} + +void PluginInstance::DidReceiveManualData(const char* buffer, int length) { + DCHECK(load_manually_); + DCHECK(plugin_data_stream_.get() != NULL); + plugin_data_stream_->SendToPlugin(buffer, length); +} + +void PluginInstance::DidFinishManualLoading() { + DCHECK(load_manually_); + DCHECK(plugin_data_stream_); + plugin_data_stream_->Close(NPRES_DONE); + RemoveStream(plugin_data_stream_.get()); + plugin_data_stream_ = NULL; +} + +void PluginInstance::DidManualLoadFail() { + DCHECK(load_manually_); + DCHECK(plugin_data_stream_); + plugin_data_stream_->Close(NPRES_NETWORK_ERR); + RemoveStream(plugin_data_stream_.get()); + plugin_data_stream_ = NULL; +} + +void PluginInstance::PluginThreadAsyncCall(void (*func)(void *), + void *userData) { + message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &PluginInstance::OnPluginThreadAsyncCall, func, userData)); +} + +void PluginInstance::OnPluginThreadAsyncCall(void (*func)(void *), + void *userData) { + // We are invoking an arbitrary callback provided by a third + // party plugin. It's better to wrap this into an exception + // block to protect us from crashes. + __try { + func(userData); + } __except(EXCEPTION_EXECUTE_HANDLER) { + // Maybe we can disable a crashing plugin. + // But for now, just continue. + } +} + +PluginInstance* PluginInstance::SetInitializingInstance( + PluginInstance* instance) { + PluginInstance* old_instance = + static_cast<PluginInstance*>( + ThreadLocalStorage::Get(plugin_instance_tls_index_)); + ThreadLocalStorage::Set(plugin_instance_tls_index_, instance); + return old_instance; +} + +PluginInstance* PluginInstance::GetInitializingInstance() { + PluginInstance* instance = + static_cast<PluginInstance*>( + ThreadLocalStorage::Get(plugin_instance_tls_index_)); + return instance;} + +NPError PluginInstance::GetServiceManager(void** service_manager) { + if (!mozilla_extenstions_) { + mozilla_extenstions_ = new MozillaExtensionApi(this); + } + + DCHECK(mozilla_extenstions_); + mozilla_extenstions_->QueryInterface(nsIServiceManager::GetIID(), + service_manager); + return NPERR_NO_ERROR; +} + +void PluginInstance::PushPopupsEnabledState(bool enabled) { + popups_enabled_stack_.push(enabled); +} + +void PluginInstance::PopPopupsEnabledState() { + popups_enabled_stack_.pop(); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_instance.h b/webkit/glue/plugins/plugin_instance.h new file mode 100644 index 0000000..5743c24 --- /dev/null +++ b/webkit/glue/plugins/plugin_instance.h @@ -0,0 +1,288 @@ +// Copyright 2008, 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. + +// TODO: Need to deal with NPAPI's NPSavedData. +// I haven't seen plugins use it yet. + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__ + +#include <string> +#include <vector> +#include <stack> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "googleurl/src/gurl.h" +#include "third_party/npapi/bindings/npapi.h" + +class WebPlugin; +class MessageLoop; + +namespace NPAPI +{ +class PluginLib; +class PluginHost; +class PluginStream; +class PluginStreamUrl; +class PluginDataStream; +class MozillaExtensionApi; + +// A PluginInstance is an active, running instance of a Plugin. +// A single plugin may have many PluginInstances. +class PluginInstance : public base::RefCounted<PluginInstance> { + public: + // Create a new instance of a plugin. The PluginInstance + // will hold a reference to the plugin. + PluginInstance(PluginLib *plugin, const std::string &mime_type); + virtual ~PluginInstance(); + + // Activates the instance by calling NPP_New. + // This should be called after our instance is all + // setup from the host side and we are ready to receive + // requests from the plugin. We must not call any + // functions on the plugin instance until start has + // been called. + // + // url: The instance URL. + // param_names: the list of names of attributes passed via the + // element. + // param_values: the list of values corresponding to param_names + // param_count: number of attributes + // load_manually: if true indicates that the plugin data would be passed + // from webkit. if false indicates that the plugin should + // download the data. + // This also controls whether the plugin is instantiated as + // a full page plugin (NP_FULL) or embedded (NP_EMBED) + // + bool Start(const GURL& url, + char** const param_names, + char** const param_values, + int param_count, + bool load_manually); + + // NPAPI's instance identifier for this instance + NPP npp() { return npp_; } + + // Get/Set for the instance's HWND. + HWND window_handle() { return hwnd_; } + void set_window_handle(HWND value) { hwnd_ = value; } + + // Get/Set whether this instance is in Windowless mode. + // Default is false. + bool windowless() { return windowless_; } + void set_windowless(bool value) { windowless_ = value; } + + // Get/Set whether this instance is transparent. + // This only applies to windowless plugins. Transparent + // plugins require that webkit paint the background. + // Default is true. + bool transparent() { return transparent_; } + void set_transparent(bool value) { transparent_ = value; } + + // Get/Set the WebPlugin associated with this instance + WebPlugin* webplugin() { return webplugin_; } + void set_web_plugin(WebPlugin* webplugin) { webplugin_ = webplugin; } + + // Get the mimeType for this plugin stream + const std::string &mime_type() { return mime_type_; } + + NPAPI::PluginLib* plugin_lib() { return plugin_; } + + // Handles a windows native message which this PluginInstance should deal + // with. Returns true if the event is handled, false otherwise. + bool HandleEvent(UINT message, WPARAM wParam, LPARAM lParam); + + // Creates a stream for sending an URL. If notify_needed + // is true, it will send a notification to the plugin + // when the stream is complete; otherwise it will not. + // Set object_url to true if the load is for the object tag's + // url, or false if it's for a url that the plugin + // fetched through NPN_GetUrl[Notify]. + PluginStreamUrl *CreateStream(int resource_id, + const std::string &url, + const std::string &mime_type, + bool notify_needed, + void *notify_data); + + // Convenience function for sending a stream from a URL to this instance. + // URL can be a relative or a fully qualified url. + void SendStream(const std::string& url, bool notify_needed, + void* notify_data); + // For each instance, we track all streams. When the + // instance closes, all remaining streams are also + // closed. All streams associated with this instance + // should call AddStream so that they can be cleaned + // up when the instance shuts down. + void AddStream(PluginStream* stream); + + // This is called when a stream is closed. We remove the stream from the + // list, which releases the reference maintained to the stream. + void RemoveStream(PluginStream* stream); + + // Closes all open streams on this instance. + void CloseStreams(); + + // Have the plugin create it's script object. + NPObject *GetPluginScriptableObject(); + + // WebViewDelegate methods that we implement. This is for handling + // callbacks during getURLNotify. + virtual void DidFinishLoadWithReason(NPReason reason); + + // Helper method to set some persistent data for getURLNotify since + // resource fetches happen async. + void SetURLLoadData(const GURL& url, void* notify_data); + + // If true, send the Mozilla user agent instead of Chrome's to the plugin. + bool use_mozilla_user_agent() { return use_mozilla_user_agent_; } + void set_use_mozilla_user_agent() { use_mozilla_user_agent_ = true; } + + bool throttle_invalidate() const { return throttle_invalidate_; } + void set_throttle_invalidate(bool throttle_invalidate) { + throttle_invalidate_ = throttle_invalidate; + } + + // Helper that implements NPN_PluginThreadAsyncCall semantics + void PluginThreadAsyncCall(void (*func)(void *), + void *userData); + + // + // NPAPI methods for calling the Plugin Instance + // + NPError NPP_New(unsigned short, short, char *[], char *[]); + NPError NPP_SetWindow(NPWindow *); + NPError NPP_NewStream(NPMIMEType, NPStream *, NPBool, unsigned short *); + NPError NPP_DestroyStream(NPStream *, NPReason); + int NPP_WriteReady(NPStream *); + int NPP_Write(NPStream *, int, int, void *); + void NPP_StreamAsFile(NPStream *, const char *); + void NPP_URLNotify(const char *, NPReason, void *); + NPError NPP_GetValue(NPPVariable, void *); + NPError NPP_SetValue(NPNVariable, void *); + short NPP_HandleEvent(NPEvent *); + void NPP_Destroy(); + bool NPP_Print(NPPrint* platform_print); + + void SendJavaScriptStream(const std::string& url, const std::wstring& result, + bool success, bool notify_needed, int notify_data); + + void DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified); + void DidReceiveManualData(const char* buffer, int length); + void DidFinishManualLoading(); + void DidManualLoadFail(); + + NPError GetServiceManager(void** service_manager); + + static PluginInstance* SetInitializingInstance(PluginInstance* instance); + static PluginInstance* GetInitializingInstance(); + + void PushPopupsEnabledState(bool enabled); + void PopPopupsEnabledState(); + + bool popups_allowed() const { + return popups_enabled_stack_.empty() ? false : popups_enabled_stack_.top(); + } + + private: + void OnPluginThreadAsyncCall(void (*func)(void *), + void *userData); + bool IsValidStream(const NPStream* stream); + + // This is a hack to get the real player plugin to work with chrome + // The real player plugin dll(nppl3260) when loaded by firefox is loaded via + // the NS COM API which is analogous to win32 COM. So the NPAPI functions in + // the plugin are invoked via an interface by firefox. The plugin instance + // handle which is passed to every NPAPI method is owned by the real player + // plugin, i.e. it expects the ndata member to point to a structure which + // it knows about. Eventually it dereferences this structure and compares + // a member variable at offset 0x24(Version 6.0.11.2888) /2D (Version + // 6.0.11.3088) with 0 and on failing this check, takes a different code + // path which causes a crash. Safari and Opera work with version 6.0.11.2888 + // by chance as their ndata structure contains a 0 at the location which real + // player checks:(. They crash with version 6.0.11.3088 as well. The + // following member just adds a 96 byte padding to our PluginInstance class + // which is passed in the ndata member. This magic number works correctly on + // Vista with UAC on or off :(. + // NOTE: Please dont change the ordering of the member variables + // New members should be added after this padding array. + // TODO(iyengar) : Disassemble the Realplayer ndata structure and look into + // the possiblity of conforming to it (http://b/issue?id=936667). We + // could also log a bug with Real, which would save the effort. + uint8 zero_padding_[96]; + scoped_refptr<NPAPI::PluginLib> plugin_; + NPP npp_; + scoped_refptr<PluginHost> host_; + NPPluginFuncs* npp_functions_; + std::vector<scoped_refptr<PluginStream> > open_streams_; + HWND hwnd_; + bool windowless_; + bool transparent_; + WebPlugin* webplugin_; + std::string mime_type_; + GURL get_url_; + void* get_notify_data_; + bool use_mozilla_user_agent_; + scoped_refptr<MozillaExtensionApi> mozilla_extenstions_; + MessageLoop* message_loop_; + // Using TLS to store PluginInstance object during its creation. + // We need to pass this instance to the service manager + // (MozillaExtensionApi) created as a result of NPN_GetValue + // in the context of NP_Initialize. + static int plugin_instance_tls_index_; + scoped_refptr<PluginDataStream> plugin_data_stream_; + GURL instance_url_; + + // This flag if true indicates that the plugin data would be passed from + // webkit. if false indicates that the plugin should download the data. + bool load_manually_; + + // This flag indicates if the NPN_InvalidateRect calls made by the + // plugin need to be throttled. + bool throttle_invalidate_; + + // Stack indicating if popups are to be enabled for the outgoing + // NPN_GetURL/NPN_GetURLNotify calls. + std::stack<bool> popups_enabled_stack_; + + // True if in CloseStreams(). + bool in_close_streams_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginInstance); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
\ No newline at end of file diff --git a/webkit/glue/plugins/plugin_lib.cc b/webkit/glue/plugins/plugin_lib.cc new file mode 100644 index 0000000..bf36152 --- /dev/null +++ b/webkit/glue/plugins/plugin_lib.cc @@ -0,0 +1,433 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_lib.h" + +#include "base/file_util.h" +#include "base/file_version_info.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/stats_counters.h" +#include "base/string_util.h" +#include "base/task.h" +#include "webkit/activex_shim/npp_impl.h" +#include "webkit/default_plugin/plugin_main.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "net/base/mime_util.h" + + +namespace NPAPI +{ + +const wchar_t kPluginLibrariesLoadedCounter[] = L"PluginLibrariesLoaded"; +const wchar_t kPluginInstancesActiveCounter[] = L"PluginInstancesActive"; + +PluginLib::PluginMap* PluginLib::loaded_libs_; + +PluginLib* PluginLib::CreatePluginLib(const std::wstring& filename) { + // We can only have one PluginLib object per plugin as it controls the per + // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep + // a (non-ref counted) map of PluginLib objects. + if (!loaded_libs_) + loaded_libs_ = new PluginMap(); + + PluginMap::const_iterator iter = loaded_libs_->find(filename); + if (iter != loaded_libs_->end()) + return iter->second; + + static const InternalPluginInfo activex_shim_info = { + {kActiveXShimFileName, + L"ActiveX Plug-in", + L"ActiveX Plug-in provides a shim to support ActiveX controls", + L"1, 0, 0, 1", + L"application/x-oleobject|application/oleobject|" + L"application/x-ms-wmp|application/asx|video/x-ms-asf-plugin|" + L"application/x-mplayer2|video/x-ms-asf|video/x-ms-wm|audio/x-ms-wma|" + L"audio/x-ms-wax|video/x-ms-wmv|video/x-ms-wvx", + L"*|*|*|*|*|*|asf,asx,*|wm,*|wma,*|wax,*|wmv,*|wvx,*", + L"" + }, + activex_shim::ActiveX_Shim_NP_GetEntryPoints, + activex_shim::ActiveX_Shim_NP_Initialize, + activex_shim::ActiveX_Shim_NP_Shutdown + }; + + static const InternalPluginInfo default_null_plugin_info = { + {kDefaultPluginDllName, + L"Default Plug-in", + L"Provides functionality for installing third-party plug-ins", + L"1, 0, 0, 1", + L"*", + L"", + L"" + }, + default_plugin::NP_GetEntryPoints, + default_plugin::NP_Initialize, + default_plugin::NP_Shutdown + }; + + WebPluginInfo* info = NULL; + const InternalPluginInfo* internal_plugin_info = NULL; + if (filename == activex_shim_info.version_info.file_name) { + info = CreateWebPluginInfo(activex_shim_info.version_info); + internal_plugin_info = &activex_shim_info; + } else if (filename == default_null_plugin_info.version_info.file_name) { + info = CreateWebPluginInfo(default_null_plugin_info.version_info); + internal_plugin_info = &default_null_plugin_info; + } else { + info = ReadWebPluginInfo(filename); + if (!info) { + DLOG(INFO) << "This file isn't a valid NPAPI plugin: " << filename; + return NULL; + } + } + + return new PluginLib(info, internal_plugin_info); +} + +void PluginLib::UnloadAllPlugins() { + if (loaded_libs_) { + PluginMap::iterator lib_index; + for (lib_index = loaded_libs_->begin(); lib_index != loaded_libs_->end(); + ++lib_index) { + lib_index->second->Unload(); + } + delete loaded_libs_; + loaded_libs_ = NULL; + } +} + +void PluginLib::ShutdownAllPlugins() { + if (loaded_libs_) { + PluginMap::iterator lib_index; + for (lib_index = loaded_libs_->begin(); lib_index != loaded_libs_->end(); + ++lib_index) { + lib_index->second->Shutdown(); + } + } +} + +PluginLib::PluginLib(WebPluginInfo* info, + const InternalPluginInfo* internal_plugin_info) + : web_plugin_info_(info), + module_(0), + initialized_(false), + saved_data_(0), + instance_count_(0) { + StatsCounter(kPluginLibrariesLoadedCounter).Increment(); + memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); + + (*loaded_libs_)[info->file] = this; + if (internal_plugin_info) { + internal_ = true; + NP_Initialize_ = internal_plugin_info->np_initialize; + NP_GetEntryPoints_ = internal_plugin_info->np_getentrypoints; + NP_Shutdown_ = internal_plugin_info->np_shutdown; + } else { + internal_ = false; + } +} + +PluginLib::~PluginLib() { + StatsCounter(kPluginLibrariesLoadedCounter).Decrement(); + if (saved_data_ != 0) { + // TODO - delete the savedData object here + } +} + +NPPluginFuncs *PluginLib::functions() { + return &plugin_funcs_; +} + +bool PluginLib::SupportsType(const std::string &mime_type, + bool allow_wildcard) { + // Webkit will ask for a plugin to handle empty mime types. + if (mime_type.empty()) + return false; + + for (size_t i = 0; i < web_plugin_info_->mime_types.size(); ++i) { + const WebPluginMimeType& mime_info = web_plugin_info_->mime_types[i]; + if (mime_util::MatchesMimeType(mime_info.mime_type, mime_type)) { + if (!allow_wildcard && (mime_info.mime_type == "*")) { + continue; + } + return true; + } + } + return false; +} + +NPError PluginLib::NP_Initialize() { + if (initialized_) + return NPERR_NO_ERROR; + + if (!Load()) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + PluginHost *host = PluginHost::Singleton(); + if (host == 0) + return NPERR_GENERIC_ERROR; + + NPError rv = NP_Initialize_(host->host_functions()); + initialized_ = (rv == NPERR_NO_ERROR); + return rv; +} + +void PluginLib::NP_Shutdown(void) { + DCHECK(initialized_); + NP_Shutdown_(); +} + +PluginInstance *PluginLib::CreateInstance(const std::string &mime_type) { + PluginInstance *new_instance = new PluginInstance(this, mime_type); + instance_count_++; + StatsCounter(kPluginInstancesActiveCounter).Increment(); + DCHECK(new_instance != 0); + return new_instance; +} + +void PluginLib::CloseInstance() { + StatsCounter(kPluginInstancesActiveCounter).Decrement(); + instance_count_--; + // If a plugin is running in its own process it will get unloaded on process + // shutdown. + if ((instance_count_ == 0) && + webkit_glue::IsPluginRunningInRendererProcess()) { + Unload(); + loaded_libs_->erase(web_plugin_info_->file); + if (loaded_libs_->empty()) { + delete loaded_libs_; + loaded_libs_ = NULL; + } + } +} + +bool PluginLib::Load() { + bool rv = false; + HMODULE module = 0; + + if (!internal_) { + if (module_ != 0) + return rv; + + module = LoadPluginHelper(web_plugin_info_->file); + if (module == 0) + return rv; + + rv = true; // assume success now + + NP_Initialize_ = (NP_InitializeFunc)GetProcAddress( + module, "NP_Initialize"); + if (NP_Initialize_ == 0) + rv = false; + + NP_GetEntryPoints_ = (NP_GetEntryPointsFunc)GetProcAddress( + module, "NP_GetEntryPoints"); + if (NP_GetEntryPoints_ == 0) + rv = false; + + NP_Shutdown_ = (NP_ShutdownFunc)GetProcAddress( + module, "NP_Shutdown"); + if (NP_Shutdown_ == 0) + rv = false; + } else { + rv = true; + } + + if (rv) { + plugin_funcs_.size = sizeof(plugin_funcs_); + plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + if (NP_GetEntryPoints_(&plugin_funcs_) != NPERR_NO_ERROR) + rv = false; + } + + if (!internal_) { + if (rv) + module_ = module; + else + FreeLibrary(module); + } + + return rv; +} + +HMODULE PluginLib::LoadPluginHelper(const std::wstring plugin_file) { + // Switch the current directory to the plugin directory as the plugin + // may have dependencies on dlls in this directory. + bool restore_directory = false; + std::wstring current_directory; + if (PathService::Get(base::DIR_CURRENT, ¤t_directory)) { + std::wstring plugin_path = file_util::GetDirectoryFromPath( + plugin_file); + if (!plugin_path.empty()) { + PathService::SetCurrentDirectory(plugin_path); + restore_directory = true; + } + } + + HMODULE module = LoadLibrary(plugin_file.c_str()); + if (restore_directory) + PathService::SetCurrentDirectory(current_directory); + + return module; +} + +// This class implements delayed NP_Shutdown and FreeLibrary on the plugin dll. +class FreePluginLibraryTask : public Task { + public: + FreePluginLibraryTask(HMODULE module, NP_ShutdownFunc shutdown_func) + : module_(module), + NP_Shutdown_(shutdown_func) { + } + + ~FreePluginLibraryTask() {} + + void Run() { + if (NP_Shutdown_) + NP_Shutdown_(); + + if (module_) { + FreeLibrary(module_); + module_ = NULL; + } + } + + private: + HMODULE module_; + NP_ShutdownFunc NP_Shutdown_; + DISALLOW_EVIL_CONSTRUCTORS(FreePluginLibraryTask); +}; + +void PluginLib::Unload() { + if (!internal_ && module_) { + // In case of single process mode, a plugin can delete itself + // by executing a script. So delay the unloading of the DLL + // so that the plugin will have a chance to unwind. + bool defer_unload = webkit_glue::IsPluginRunningInRendererProcess(); + +#if USE(JAVASCRIPTCORE_BINDINGS) + // The plugin NPAPI instances may still be around. Delay the + // NP_Shutdown and FreeLibrary calls at least till the next + // peek message. + defer_unload = true; +#endif + + if (defer_unload) { + FreePluginLibraryTask* free_library_task = + new FreePluginLibraryTask(module_, NP_Shutdown_); + MessageLoop::current()->PostTask(FROM_HERE, free_library_task); + } else { + Shutdown(); + FreeLibrary(module_); + } + + module_ = 0; + } +} + +void PluginLib::Shutdown() { + if (initialized_ && !internal_) { + NP_Shutdown(); + initialized_ = false; + } +} + +WebPluginInfo* PluginLib::CreateWebPluginInfo(const PluginVersionInfo& pvi) { + std::vector<std::string> mime_types, file_extensions; + std::vector<std::wstring> descriptions; + SplitString(WideToNativeMB(pvi.mime_types), '|', &mime_types); + SplitString(WideToNativeMB(pvi.file_extents), '|', &file_extensions); + SplitString(pvi.file_open_names, '|', &descriptions); + + if (mime_types.empty()) + return NULL; + + WebPluginInfo *info = new WebPluginInfo(); + info->name = pvi.product_name; + info->desc = pvi.file_description; + info->version = pvi.file_version; + info->file = StringToLowerASCII(pvi.file_name); + + for (size_t i = 0; i < mime_types.size(); ++i) { + WebPluginMimeType mime_type; + mime_type.mime_type = StringToLowerASCII(mime_types[i]); + if (file_extensions.size() > i) + SplitString(file_extensions[i], ',', &mime_type.file_extensions); + + if (descriptions.size() > i) { + mime_type.description = descriptions[i]; + + // Remove the extension list from the description. + size_t ext = mime_type.description.find(L"(*"); + if (ext != std::wstring::npos) { + if (ext > 1 && mime_type.description[ext -1] == ' ') + ext--; + + mime_type.description.erase(ext); + } + } + + info->mime_types.push_back(mime_type); + } + + return info; +} + +WebPluginInfo* PluginLib::ReadWebPluginInfo(const std::wstring &filename) { + // On windows, the way we get the mime types for the library is + // to check the version information in the DLL itself. This + // will be a string of the format: <type1>|<type2>|<type3>|... + // For example: + // video/quicktime|audio/aiff|image/jpeg + scoped_ptr<FileVersionInfo> version_info( + FileVersionInfo::CreateFileVersionInfo(filename)); + if (!version_info.get()) + return NULL; + + PluginVersionInfo pvi; + version_info->GetValue(L"MIMEType", &pvi.mime_types); + version_info->GetValue(L"FileExtents", &pvi.file_extents); + version_info->GetValue(L"FileOpenName", &pvi.file_open_names); + pvi.product_name = version_info->product_name(); + pvi.file_description = version_info->file_description(); + pvi.file_version = version_info->file_version(); + pvi.file_name = filename; + + return CreateWebPluginInfo(pvi); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_lib.h b/webkit/glue/plugins/plugin_lib.h new file mode 100644 index 0000000..798b2ea --- /dev/null +++ b/webkit/glue/plugins/plugin_lib.h @@ -0,0 +1,163 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_LIB_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_LIB_H__ + +#include <hash_map> +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +struct WebPluginInfo; + +namespace NPAPI +{ + +class PluginInstance; + +// This struct fully describes a plugin. For dll plugins, it's read in from +// the version info of the dll; For internal plugins, it's predefined. +struct PluginVersionInfo { + std::wstring file_name; + std::wstring product_name; + std::wstring file_description; + std::wstring file_version; + std::wstring mime_types; + std::wstring file_extents; + std::wstring file_open_names; +}; + +// This struct contains information of an internal plugin and addresses of +// entry functions. +struct InternalPluginInfo { + PluginVersionInfo version_info; + NP_GetEntryPointsFunc np_getentrypoints; + NP_InitializeFunc np_initialize; + NP_ShutdownFunc np_shutdown; +}; + +// A PluginLib is a single NPAPI Plugin Library, and is the lifecycle +// manager for new PluginInstances. +class PluginLib : public base::RefCounted<PluginLib> { + public: + virtual ~PluginLib(); + static PluginLib* CreatePluginLib(const std::wstring& filename); + + // Unloads all the loaded plugin dlls and cleans up the plugin map. + static void UnloadAllPlugins(); + + // Shuts down all loaded plugin instances. + static void ShutdownAllPlugins(); + + // Get the Plugin's function pointer table. + NPPluginFuncs *functions(); + + // Returns true if this Plugin supports a given mime-type. + // mime_type should be all lower case. + bool SupportsType(const std::string &mime_type, bool allow_wildcard); + + // Creates a new instance of this plugin. + PluginInstance *CreateInstance(const std::string &mime_type); + + // Called by the instance when the instance is tearing down. + void CloseInstance(); + + // Gets information about this plugin and the mime types that it + // supports. + const WebPluginInfo& plugin_info() { return *web_plugin_info_; } + + // + // NPAPI functions + // + + // NPAPI method to initialize a Plugin. + // Initialize can be safely called multiple times + NPError NP_Initialize(); + + // NPAPI method to shutdown a Plugin. + void NP_Shutdown(void); + + // Helper function to load a plugin. + // Returns the module handle on success. + static HMODULE LoadPluginHelper(const std::wstring plugin_file); + + int instance_count() const { return instance_count_; } + + private: + // Creates a new PluginLib. The WebPluginInfo object is owned by this + // object. If internal_plugin_info is not NULL, this Lib is an internal + // plugin thus doesn't need to load dll. + PluginLib(WebPluginInfo* info, + const InternalPluginInfo* internal_plugin_info); + + // Attempts to load the plugin from the DLL. + // Returns true if it is a legitimate plugin, false otherwise + bool Load(); + + // Unloading the plugin DLL. + void Unload(); + + // Shutdown the plugin DLL. + void Shutdown(); + + // Returns a WebPluginInfo structure given a plugin's path. Returns NULL if + // the dll couldn't be found, or if it's not a plugin. + static WebPluginInfo* ReadWebPluginInfo(const std::wstring &filename); + // Creates WebPluginInfo structure based on read in or built in + // PluginVersionInfo. + static WebPluginInfo* CreateWebPluginInfo(const PluginVersionInfo& info); + + bool internal_; // Whether this an internal plugin. + scoped_ptr<WebPluginInfo> web_plugin_info_; // supported mime types, description + HMODULE module_; // the opened DLL handle + NPPluginFuncs plugin_funcs_; // the struct of plugin side functions + bool initialized_; // is the plugin initialized + NPSavedData *saved_data_; // persisted plugin info for NPAPI + int instance_count_; // count of plugins in use + + // A map of all the insantiated plugins. + typedef stdext::hash_map<std::wstring, scoped_refptr<PluginLib> > PluginMap; + static PluginMap* loaded_libs_; + + // C-style function pointers + NP_InitializeFunc NP_Initialize_; + NP_GetEntryPointsFunc NP_GetEntryPoints_; + NP_ShutdownFunc NP_Shutdown_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginLib); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_LIB_H__ diff --git a/webkit/glue/plugins/plugin_list.cc b/webkit/glue/plugins/plugin_list.cc new file mode 100644 index 0000000..8c8deef --- /dev/null +++ b/webkit/glue/plugins/plugin_list.cc @@ -0,0 +1,500 @@ +// Copyright 2008, 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 <algorithm> +#include <tchar.h> + +#include "webkit/glue/plugins/plugin_list.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/time.h" +#include "webkit/activex_shim/activex_shared.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI +{ + +scoped_refptr<PluginList> PluginList::singleton_; + +static const TCHAR kRegistryApps[] = + _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"); +static const TCHAR kRegistryFirefox[] = _T("firefox.exe"); +static const TCHAR kRegistryAcrobat[] = _T("AcroRd32.exe"); +static const TCHAR kRegistryWindowsMedia[] = _T("wmplayer.exe"); +static const TCHAR kRegistryQuickTime[] = _T("QuickTimePlayer.exe"); +static const TCHAR kRegistryPath[] = _T("Path"); +static const TCHAR kRegistryMozillaPlugins[] = _T("SOFTWARE\\MozillaPlugins"); +static const TCHAR kRegistryFirefoxInstalled[] = + _T("SOFTWARE\\Mozilla\\Mozilla Firefox"); +static const TCHAR kMozillaActiveXPlugin[] = _T("npmozax.dll"); +static const TCHAR kNewWMPPlugin[] = _T("np-mswmp.dll"); +static const TCHAR kOldWMPPlugin[] = _T("npdsplay.dll"); +static const TCHAR kRegistryJava[] = + _T("Software\\JavaSoft\\Java Runtime Environment"); +static const TCHAR kRegistryBrowserJavaVersion[] = _T("BrowserJavaVersion"); +static const TCHAR kRegistryCurrentJavaVersion[] = _T("CurrentVersion"); +static const TCHAR kRegistryJavaHome[] = _T("JavaHome"); + +// Extra registry paths to search. +static std::vector<std::wstring>* extra_plugin_paths_ = NULL; + +PluginList* PluginList::Singleton() { + if (singleton_.get() == NULL) { + singleton_ = new PluginList(); + singleton_->LoadPlugins(false); + } + + return singleton_; +} + +void PluginList::AddExtraPluginPath(const std::wstring& plugin_path) { + DCHECK(!singleton_.get() || !singleton_->plugins_loaded_); + + if (!extra_plugin_paths_) + extra_plugin_paths_ = new std::vector<std::wstring>; + extra_plugin_paths_->push_back(plugin_path); +} + +PluginList::PluginList() : + plugins_loaded_(false) { + CommandLine command_line; + dont_load_new_wmp_ = command_line.HasSwitch(kUseOldWMPPluginSwitch); + use_internal_activex_shim_ = + !command_line.HasSwitch(kNoNativeActiveXShimSwitch); +} + +PluginList::~PluginList() { + plugins_.clear(); +} + +void PluginList::LoadPlugins(bool refresh) { + if (plugins_loaded_ && !refresh) + return; + + plugins_.clear(); + plugins_loaded_ = true; + + TimeTicks start_time = TimeTicks::Now(); + + LoadInternalPlugins(); + + // Load any plugins listed in the registry + if (extra_plugin_paths_) { + for (size_t i = 0; i < extra_plugin_paths_->size(); ++i) { + LoadPlugin((*extra_plugin_paths_)[i]); + } + } + + // Load from the application-specific area + LoadPlugins(GetPluginAppDirectory()); + + // Load from the executable area + LoadPlugins(GetPluginExeDirectory()); + + // Load Java + LoadJavaPlugin(); + + // Load firefox plugins too. This is mainly to try to locate + // a pre-installed Flash player. + LoadFirefoxPlugins(); + + // Firefox hard-codes the paths of some popular plugins to ensure that + // the plugins are found. We are going to copy this as well. + LoadAcrobatPlugins(); + LoadQuicktimePlugins(); + LoadWindowsMediaPlugins(); + + if (webkit_glue::IsDefaultPluginEnabled()) { + scoped_refptr<PluginLib> default_plugin = PluginLib::CreatePluginLib( + kDefaultPluginDllName); + plugins_.push_back(default_plugin); + } + + TimeTicks end_time = TimeTicks::Now(); + TimeDelta elapsed = end_time - start_time; + DLOG(INFO) << "Loaded plugin list in " << elapsed.InMilliseconds() << " ms."; +} + +void PluginList::LoadPlugins(const std::wstring &path) { + WIN32_FIND_DATA find_file_data; + HANDLE find_handle; + + std::wstring dir = path; + // FindFirstFile requires that you specify a wildcard for directories. + dir.append(L"\\NP*.DLL"); + + find_handle = FindFirstFile(dir.c_str(), &find_file_data); + if (find_handle == INVALID_HANDLE_VALUE) + return; + + do { + if (!(find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + std::wstring filename = path; + filename.append(L"\\"); + filename.append(find_file_data.cFileName); + + LoadPlugin(filename); + } + } while (FindNextFile(find_handle, &find_file_data) != 0); + + DCHECK(GetLastError() == ERROR_NO_MORE_FILES); + FindClose(find_handle); +} + +void PluginList::LoadPlugin(const std::wstring &path) { + std::wstring path_lc = StringToLowerASCII(path); + if (!ShouldLoadPlugin(file_util::GetFilenameFromPath(path_lc))) + return; + + scoped_refptr<PluginLib> new_plugin = PluginLib::CreatePluginLib(path_lc); + if (!new_plugin.get()) + return; + + const WebPluginInfo& plugin_info = new_plugin->plugin_info(); + for (size_t i = 0; i < plugin_info.mime_types.size(); ++i) { + // TODO: don't load global handlers for now. + // WebKit hands to the Plugin before it tries + // to handle mimeTypes on its own. + const std::string &mime_type = plugin_info.mime_types[i].mime_type; + if (mime_type == "*" ) { +#ifndef NDEBUG + // Make an exception for NPSPY. + if (plugin_info.file.find(L"npspy.dll") != std::wstring::npos) { + // Put it at the beginning so it's used before the real plugin. + plugins_.insert(plugins_.begin(), new_plugin.get()); + } +#endif + continue; + } + + if (!SupportsType(mime_type)) + plugins_.push_back(new_plugin); + } +} + +bool PluginList::ShouldLoadPlugin(const std::wstring& filename) { + // Depends on XPCOM. + if (filename == kMozillaActiveXPlugin) + return false; + + // We will use activex shim to handle embeded wmp media. + if (use_internal_activex_shim_) { + if (filename == kNewWMPPlugin || filename == kOldWMPPlugin) + return false; + } else { + // If both the new and old WMP plugins exist, only load the new one. + if (filename == kNewWMPPlugin) { + if (dont_load_new_wmp_) + return false; + + int old_plugin = FindPluginFile(kOldWMPPlugin); + if (old_plugin != -1) + plugins_.erase(plugins_.begin() + old_plugin); + } else if (filename == kOldWMPPlugin) { + if (FindPluginFile(kNewWMPPlugin) != -1) + return false; + } + } + + return true; +} + +void PluginList::LoadInternalPlugins() { + if (use_internal_activex_shim_) { + scoped_refptr<PluginLib> new_plugin = PluginLib::CreatePluginLib( + kActiveXShimFileName); + plugins_.push_back(new_plugin); + } +} + +int PluginList::FindPluginFile(const std::wstring& filename) { + for (size_t i = 0; i < plugins_.size(); ++i) { + if (file_util::GetFilenameFromPath(plugins_[i]->plugin_info().file) == + filename) { + return static_cast<int>(i); + } + } + + return -1; +} + +PluginLib* PluginList::FindPlugin(const std::string& mime_type, + const std::string& clsid, + bool allow_wildcard) { + DCHECK(mime_type == StringToLowerASCII(mime_type)); + + for (size_t idx = 0; idx < plugins_.size(); ++idx) { + if (plugins_[idx]->SupportsType(mime_type, allow_wildcard)) { + if (!clsid.empty() && + plugins_[idx]->plugin_info().file == kActiveXShimFileName) { + // Special handling for ActiveX shim. If ActiveX is not installed, we + // should use the default plugin to show the installation UI. + if (!activex_shim::IsActiveXInstalled(clsid)) + continue; + } + return plugins_[idx]; + } + } + + return NULL; +} + +PluginLib* PluginList::FindPlugin(const GURL &url, std::string* actual_mime_type) { + std::wstring path = NativeMBToWide(url.path()); + std::wstring extension_wide = file_util::GetFileExtensionFromPath(path); + if (extension_wide.empty()) + return NULL;; + + std::string extension = StringToLowerASCII(WideToNativeMB(extension_wide)); + + for (size_t idx = 0; idx < plugins_.size(); ++idx) { + if (SupportsExtension(plugins_[idx]->plugin_info(), extension, actual_mime_type)) { + return plugins_[idx]; + } + } + + return NULL; +} + +bool PluginList::SupportsType(const std::string &mime_type) { + DCHECK(mime_type == StringToLowerASCII(mime_type)); + bool allow_wildcard = true; + return (FindPlugin(mime_type, "", allow_wildcard ) != 0); +} + +bool PluginList::SupportsExtension(const WebPluginInfo& info, + const std::string &extension, + std::string* actual_mime_type) { + for (size_t i = 0; i < info.mime_types.size(); ++i) { + const WebPluginMimeType& mime_type = info.mime_types[i]; + for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) { + if (mime_type.file_extensions[j] == extension) { + if (actual_mime_type) + *actual_mime_type = mime_type.mime_type; + return true; + } + } + } + + return false; +} + + +bool PluginList::GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) { + if (refresh) + LoadPlugins(true); + + plugins->resize(plugins_.size()); + for (size_t i = 0; i < plugins->size(); ++i) + (*plugins)[i] = plugins_[i]->plugin_info(); + + return true; +} + +bool PluginList::GetPluginInfo(const GURL& url, + const std::string& mime_type, + const std::string& clsid, + bool allow_wildcard, + WebPluginInfo* info, + std::string* actual_mime_type) { + scoped_refptr<PluginLib> plugin = FindPlugin(mime_type, clsid, + allow_wildcard); + + if (plugin.get() == NULL || + (plugin->plugin_info().file == kDefaultPluginDllName && clsid.empty())) { + scoped_refptr<PluginLib> default_plugin = plugin; + plugin = FindPlugin(url, actual_mime_type); + // url matches may not return the default plugin if no match is found. + if (plugin.get() == NULL && default_plugin.get() != NULL) + plugin = default_plugin; + } + + if (plugin.get() == NULL) + return false; + + *info = plugin->plugin_info(); + return true; +} + +bool PluginList::GetPluginInfoByDllPath(const std::wstring& dll_path, + WebPluginInfo* info) { + for (size_t i = 0; i < plugins_.size(); ++i) { + if (wcsicmp(plugins_[i]->plugin_info().file.c_str(), + dll_path.c_str()) == 0) { + *info = plugins_[i]->plugin_info(); + return true; + } + } + + return false; +} + +void PluginList::Shutdown() { + // TODO +} + +std::wstring PluginList::GetPluginAppDirectory() { + std::wstring app_path; + if (webkit_glue::GetApplicationDirectory(&app_path)) + app_path.append(L"\\plugins"); + + return app_path; +} + +std::wstring PluginList::GetPluginExeDirectory() { + std::wstring exe_path; + if (webkit_glue::GetExeDirectory(&exe_path)) + exe_path.append(L"\\plugins"); + + return exe_path; +} + +// Gets the installed path for a registered app. +static bool GetInstalledPath(const TCHAR* app, std::wstring* out) { + std::wstring reg_path(kRegistryApps); + reg_path.append(L"\\"); + reg_path.append(app); + + RegKey key(HKEY_LOCAL_MACHINE, reg_path.c_str()); + return key.ReadValue(kRegistryPath, out); +} + +// Enumerate through the registry key to find all installed FireFox paths. +// FireFox 3 beta and version 2 can coexist. See bug: 1025003 +static void GetFirefoxInstalledPaths(std::vector<std::wstring>* out) { + RegistryKeyIterator it(HKEY_LOCAL_MACHINE, kRegistryFirefoxInstalled); + for (; it.Valid(); ++it) { + std::wstring full_path = std::wstring(kRegistryFirefoxInstalled) + L"\\" + + it.Name() + L"\\Main"; + RegKey key(HKEY_LOCAL_MACHINE, full_path.c_str(), KEY_READ); + std::wstring install_dir; + if (!key.ReadValue(L"Install Directory", &install_dir)) + continue; + out->push_back(install_dir); + } +} + +void PluginList::LoadFirefoxPlugins() { + std::vector<std::wstring> paths; + GetFirefoxInstalledPaths(&paths); + for (unsigned int i = 0; i < paths.size(); ++i) { + std::wstring path = paths[i] + L"\\plugins"; + LoadPlugins(path); + } + + LoadPluginsInRegistryFolder(HKEY_CURRENT_USER, kRegistryMozillaPlugins); + LoadPluginsInRegistryFolder(HKEY_LOCAL_MACHINE, kRegistryMozillaPlugins); + + std::wstring firefox_app_data_plugin_path; + if (PathService::Get(base::DIR_APP_DATA, &firefox_app_data_plugin_path)) { + firefox_app_data_plugin_path += L"\\Mozilla\\plugins"; + LoadPlugins(firefox_app_data_plugin_path); + } +} + +void PluginList::LoadAcrobatPlugins() { + std::wstring path; + if (GetInstalledPath(kRegistryAcrobat, &path)) { + path.append(L"\\Browser"); + LoadPlugins(path); + } +} + +void PluginList::LoadQuicktimePlugins() { + std::wstring path; + if (GetInstalledPath(kRegistryQuickTime, &path)) { + path.append(L"\\plugins"); + LoadPlugins(path); + } +} + +void PluginList::LoadWindowsMediaPlugins() { + std::wstring path; + if (GetInstalledPath(kRegistryWindowsMedia, &path)) { + LoadPlugins(path); + } +} + +void PluginList::LoadJavaPlugin() { + // Load the new NPAPI Java plugin + // 1. Open the main JRE key under HKLM + RegKey java_key(HKEY_LOCAL_MACHINE, kRegistryJava, KEY_QUERY_VALUE); + + // 2. Read the current Java version + std::wstring java_version; + if (!java_key.ReadValue(kRegistryBrowserJavaVersion, &java_version)) + java_key.ReadValue(kRegistryCurrentJavaVersion, &java_version); + + if (!java_version.empty()) { + java_key.OpenKey(java_version.c_str(), KEY_QUERY_VALUE); + + // 3. Install path of the JRE binaries is specified in "JavaHome" + // value under the Java version key. + std::wstring java_plugin_directory; + if (java_key.ReadValue(kRegistryJavaHome, &java_plugin_directory)) { + + // 4. The new plugin resides under the 'bin/new_plugin' + // subdirectory. + DCHECK(!java_plugin_directory.empty()); + java_plugin_directory.append(L"\\bin\\new_plugin"); + + // 5. We don't know the exact name of the DLL but it's in the form + // NP*.dll so just invoke LoadPlugins on this path. + LoadPlugins(java_plugin_directory); + } + } +} + +void PluginList::LoadPluginsInRegistryFolder( + HKEY root_key, + const std::wstring& registry_folder) { + for (RegistryKeyIterator iter(root_key, registry_folder.c_str()); + iter.Valid(); ++iter) { + // Use the registry to gather plugin across the file system. + std::wstring reg_path = registry_folder; + reg_path.append(L"\\"); + reg_path.append(iter.Name()); + RegKey key(root_key, reg_path.c_str()); + + std::wstring path; + if (key.ReadValue(kRegistryPath, &path)) + LoadPlugin(path); + } +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_list.h b/webkit/glue/plugins/plugin_list.h new file mode 100644 index 0000000..1176ad8 --- /dev/null +++ b/webkit/glue/plugins/plugin_list.h @@ -0,0 +1,197 @@ +// Copyright 2008, 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. + +// TODO: Need mechanism to cleanup the static instance + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H__ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "webkit/glue/webplugin.h" + +class GURL; + +namespace NPAPI +{ + +// Used by plugins_test when testing the older WMP plugin to force the new +// plugin to not get loaded. +#define kUseOldWMPPluginSwitch L"use-old-wmp" +// Used for testing ActiveX shim. By default it's off. If this flag is specified +// we will use the native ActiveX shim. +#define kNoNativeActiveXShimSwitch L"no-activex" +// Internal file name for activex shim, used as a unique identifier. +#define kActiveXShimFileName L"activex-shim" + +#define kDefaultPluginDllName L"default_plugin" + +class PluginLib; +class PluginInstance; + +// The PluginList is responsible for loading our NPAPI based plugins. +// It loads plugins from a known directory by looking for DLLs +// which start with "NP", and checking to see if they are valid +// NPAPI libraries. +class PluginList : public base::RefCounted<PluginList> { + public: + // Gets the one instance of the PluginList. + // + // Accessing the singleton causes the PluginList to look on + // disk for existing plugins. It does not actually load + // libraries, that will only happen when you initialize + // the plugin for the first time. + static PluginList* Singleton(); + + // Add an extra plugin to load when we actually do the loading. This is + // static because we want to be able to add to it without searching the disk + // for plugins. Must be called before the plugins have been loaded. + static void AddExtraPluginPath(const std::wstring& plugin_path); + + virtual ~PluginList(); + + // Find a plugin to by mime type, and clsid. + // If clsid is empty, we will just find the plugin that supports mime type. + // Otherwise, if mime_type is application/x-oleobject etc that supported by + // by our activex shim, we need to check if the specified ActiveX exists. + // If not we will not return the activex shim, instead we will let the + // default plugin handle activex installation. + // The allow_wildcard parameter controls whether this function returns + // plugins which support wildcard mime types (* as the mime type) + PluginLib* FindPlugin(const std::string &mime_type, const std::string& clsid, + bool allow_wildcard); + + // Find a plugin to by extension. Returns the corresponding mime type + PluginLib* FindPlugin(const GURL &url, std::string* actual_mime_type); + + // Check if we have any plugin for a given type. + // mime_type must be all lowercase. + bool SupportsType(const std::string &mime_type); + + // Returns true if the given WebPluginInfo supports a given file extension. + // extension should be all lower case. + // If mime_type is not NULL, it will be set to the mime type if found. + // The mime type which corresponds to the extension is optionally returned + // back. + static bool SupportsExtension(const WebPluginInfo& info, + const std::string &extension, + std::string* actual_mime_type); + + // Shutdown all plugins. Should be called at process teardown. + void Shutdown(); + + // Get all the plugins + bool GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins); + + // Returns true if a plugin is found for the given url and mime type. + // The mime type which corresponds to the URL is optionally returned + // back. + // The allow_wildcard parameter controls whether this function returns + // plugins which support wildcard mime types (* as the mime type) + bool GetPluginInfo(const GURL& url, + const std::string& mime_type, + const std::string& clsid, + bool allow_wildcard, + WebPluginInfo* info, + std::string* actual_mime_type); + + // Get plugin info by plugin dll path. Returns true if the plugin is found and + // WebPluginInfo has been filled in |info| + bool GetPluginInfoByDllPath(const std::wstring& dll_path, + WebPluginInfo* info); + private: + // Constructors are private for singletons + PluginList(); + + // Load all plugins from the default plugins directory + void LoadPlugins(bool refresh); + + // Load all plugins from a specific directory + void LoadPlugins(const std::wstring &path); + + // Load a specific plugin with full path. filename can be mixed case. + void LoadPlugin(const std::wstring &filename); + + // Returns true if we should load the given plugin, or false otherwise. + // filename must be lower case. + bool ShouldLoadPlugin(const std::wstring& filename); + + // Load internal plugins. Right now there is only one: activex_shim. + void LoadInternalPlugins(); + + // Find a plugin by filename. Returns -1 if it's not found, otherwise its + // index in plugins_. filename needs to be lower case. + int FindPluginFile(const std::wstring& filename); + + // The application path where we expect to find plugins. + static std::wstring GetPluginAppDirectory(); + + // The executable path where we expect to find plugins. + static std::wstring GetPluginExeDirectory(); + + // Load plugins from the Firefox install path. This is kind of + // a kludge, but it helps us locate the flash player for users that + // already have it for firefox. Not having to download yet-another-plugin + // is a good thing. + void LoadFirefoxPlugins(); + + // Hardcoded logic to detect and load acrobat plugins + void LoadAcrobatPlugins(); + + // Hardcoded logic to detect and load quicktime plugins + void LoadQuicktimePlugins(); + + // Hardcoded logic to detect and load Windows Media Player plugins + void LoadWindowsMediaPlugins(); + + // Hardcoded logic to detect and load Java plugins + void LoadJavaPlugin(); + + // Search the registry at the given path and load plugins listed there. + void LoadPluginsInRegistryFolder(HKEY root_key, + const std::wstring& registry_folder); + + // true if we shouldn't load the new WMP plugin. + bool dont_load_new_wmp_; + + bool use_internal_activex_shim_; + + static scoped_refptr<PluginList> singleton_; + bool plugins_loaded_; + std::vector<scoped_refptr<PluginLib> > plugins_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginList); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H__ diff --git a/webkit/glue/plugins/plugin_stream.cc b/webkit/glue/plugins/plugin_stream.cc new file mode 100644 index 0000000..9f41ac9 --- /dev/null +++ b/webkit/glue/plugins/plugin_stream.cc @@ -0,0 +1,323 @@ +// Copyright 2008, 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. + +// TODO : Support NP_ASFILEONLY mode +// TODO : Support NP_SEEK mode +// TODO : Support SEEKABLE=true in NewStream + +#include "webkit/glue/plugins/plugin_stream.h" + +#include "base/string_util.h" +#include "base/message_loop.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/webkit_glue.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI { + +PluginStream::PluginStream( + PluginInstance *instance, + const char *url, + bool need_notify, + void *notify_data) + : instance_(instance), + bytes_sent_(0), + notify_needed_(need_notify), + notify_data_(notify_data), + close_on_write_data_(false), + opened_(false), + requested_plugin_mode_(NP_NORMAL), + temp_file_handle_(INVALID_HANDLE_VALUE) { + memset(&stream_, 0, sizeof(stream_)); + stream_.url = _strdup(url); + temp_file_name_[0] = '\0'; +} + +PluginStream::~PluginStream() { + // always cleanup our temporary files. + CleanupTempFile(); + + free(const_cast<char*>(stream_.url)); +} + +void PluginStream::UpdateUrl(const char* url) { + DCHECK(!opened_); + free(const_cast<char*>(stream_.url)); + stream_.url = _strdup(url); +} + +bool PluginStream::Open(const std::string &mime_type, + const std::string &headers, + uint32 length, + uint32 last_modified) { + headers_ = headers; + NPP id = instance_->npp(); + stream_.end = length; + stream_.lastmodified = last_modified; + stream_.pdata = 0; + stream_.ndata = id->ndata; + stream_.notifyData = notify_data_; + if (!headers_.empty()) + stream_.headers = headers_.c_str(); + + const char *char_mime_type = "application/x-unknown-content-type"; + std::string temp_mime_type; + if (!mime_type.empty()) { + char_mime_type = mime_type.c_str(); + } else { + GURL gurl(stream_.url); + std::wstring path(UTF8ToWide(gurl.path())); + if (webkit_glue::GetMimeTypeFromFile(path, &temp_mime_type)) + char_mime_type = temp_mime_type.c_str(); + } + + // Silverlight expects a valid mime type + DCHECK(strlen(char_mime_type) != 0); + NPError err = instance_->NPP_NewStream((NPMIMEType)char_mime_type, + &stream_, false, + &requested_plugin_mode_); + if (err != NPERR_NO_ERROR) + return false; + + opened_ = true; + + // If the plugin has requested certain modes, then we need a copy + // of this file on disk. Open it and save it as we go. + if (requested_plugin_mode_ == NP_ASFILEONLY || + requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_SEEK) { + if (OpenTempFile() == false) + return false; + } + + return true; +} + +int PluginStream::Write(const char *buffer, const int length) { + // There may be two streams to write to - the plugin and the file. + // It is unclear what to do if we cannot write to both. The rules of + // this function are that the plugin must consume at least as many + // bytes as returned by the WriteReady call. So, we will attempt to + // write that many to both streams. If we can't write that many bytes + // to each stream, we'll return failure. + + DCHECK(opened_); + if (WriteToFile(buffer, length) && WriteToPlugin(buffer, length)) + return length; + + return -1; +} + +bool PluginStream::WriteToFile(const char *buf, const int length) { + // For ASFILEONLY, ASFILE, and SEEK modes, we need to write + // to the disk + if (temp_file_handle_ != INVALID_HANDLE_VALUE && + (requested_plugin_mode_ == NP_SEEK || + requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_ASFILEONLY) ) { + int totalBytesWritten = 0; + DWORD bytes; + do { + if (WriteFile(temp_file_handle_, buf, length, &bytes, 0) == FALSE) + break; + totalBytesWritten += bytes; + } while (bytes > 0 && totalBytesWritten < length); + + if (totalBytesWritten != length) + return false; + } + + return true; +} + +bool PluginStream::WriteToPlugin(const char *buf, const int length) { + // For NORMAL and ASFILE modes, we send the data to the plugin now + if (requested_plugin_mode_ != NP_NORMAL && + requested_plugin_mode_ != NP_ASFILE) + return true; + + int written = TryWriteToPlugin(buf, length); + if (written == -1) + return false; + + if (written < length) { + // Buffer the remaining data. + size_t remaining = length - written; + size_t previous_size = delivery_data_.size(); + delivery_data_.resize(previous_size + remaining); + memcpy(&delivery_data_[previous_size], buf + written, remaining); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &PluginStream::OnDelayDelivery)); + } + + return true; +} + +void PluginStream::OnDelayDelivery() { + // It is possible that the plugin stream may have closed before the task + // was hit. + if (!opened_) { + return; + } + + int size = static_cast<int>(delivery_data_.size()); + int written = TryWriteToPlugin(&delivery_data_.front(), size); + if (written > 0) { + // Remove the data that we already wrote. + delivery_data_.erase(delivery_data_.begin(), + delivery_data_.begin() + written); + } +} + +int PluginStream::TryWriteToPlugin(const char *buf, const int length) { + bool result = true; + int byte_offset = 0; + + while (byte_offset < length) { + int bytes_remaining = length - byte_offset; + int bytes_to_write = instance_->NPP_WriteReady(&stream_); + if (bytes_to_write > bytes_remaining) + bytes_to_write = bytes_remaining; + + if (bytes_to_write == 0) + return byte_offset; + + int bytesSent = instance_->NPP_Write(&stream_, + bytes_sent_, + bytes_to_write, + const_cast<char*>(buf + byte_offset)); + if (bytesSent < bytes_to_write) { + // We couldn't write all the bytes. This is an error. + return -1; + } + bytes_sent_ += bytesSent; + byte_offset += bytesSent; + } + + if (close_on_write_data_) + Close(NPRES_DONE); + + return length; +} + +void PluginStream::WriteAsFile() { + if (requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_ASFILEONLY) + instance_->NPP_StreamAsFile(&stream_, temp_file_name_); +} + +bool PluginStream::Close(NPReason reason) { + if (opened_ == true) { + opened_ = false; + + if (delivery_data_.size()) { + if (reason == NPRES_DONE) { + // There is more data to be streamed, don't destroy the stream now. + close_on_write_data_ = true; + return true; + } else { + // Stop any pending data from being streamed + delivery_data_.resize(0); + } + } + + // If we have a temp file, be sure to close it. + // Also, allow the plugin to access it now. + if (temp_file_handle_ != INVALID_HANDLE_VALUE) { + CloseTempFile(); + WriteAsFile(); + } + + if (stream_.ndata != NULL) { + // Stream hasn't been closed yet. + NPError err = instance_->NPP_DestroyStream(&stream_, reason); + DCHECK(err == NPERR_NO_ERROR); + } + } + + Notify(reason); + return true; +} + +bool PluginStream::OpenTempFile() { + DCHECK(temp_file_handle_ == INVALID_HANDLE_VALUE); + + // The reason for using all the Ascii versions of these filesystem + // calls is that the filename which we pass back to the plugin + // via NPAPI is an ascii filename. Otherwise, we'd use wide-chars. + // + // TODO: + // This is a bug in NPAPI itself, and it needs to be fixed. + // The case which will fail is if a user has a multibyte name, + // but has the system locale set to english. GetTempPathA will + // return junk in this case, causing us to be unable to open the + // file. + + char temp_directory[MAX_PATH]; + if (GetTempPathA(MAX_PATH, temp_directory) == 0) + return false; + if (GetTempFileNameA(temp_directory, "npstream", 0, temp_file_name_) == 0) + return false; + temp_file_handle_ = CreateFileA(temp_file_name_, + FILE_ALL_ACCESS, + FILE_SHARE_READ, + 0, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (temp_file_handle_ == INVALID_HANDLE_VALUE) { + temp_file_name_[0] = '\0'; + return false; + } + return true; +} + +void PluginStream::CloseTempFile() { + if (temp_file_handle_ != INVALID_HANDLE_VALUE) { + CloseHandle(temp_file_handle_); + temp_file_handle_ = INVALID_HANDLE_VALUE; + } +} + +void PluginStream::CleanupTempFile() { + CloseTempFile(); + if (temp_file_name_[0] != '\0') { + DeleteFileA(temp_file_name_); + temp_file_name_[0] = '\0'; + } +} + +void PluginStream::Notify(NPReason reason) { + if (notify_needed_) { + instance_->NPP_URLNotify(stream_.url, reason, notify_data_); + notify_needed_ = false; + } +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_stream.h b/webkit/glue/plugins/plugin_stream.h new file mode 100644 index 0000000..e4b99ba --- /dev/null +++ b/webkit/glue/plugins/plugin_stream.h @@ -0,0 +1,136 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__ + +#include <string> +#include <vector> + +#include "base/ref_counted.h" +#include "third_party/npapi/bindings/npapi.h" + + +namespace NPAPI { + +class PluginInstance; + +// Base class for a NPAPI stream. Tracks basic elements +// of a stream for NPAPI notifications and stream position. +class PluginStream : public base::RefCounted<PluginStream> { + public: + // Create a new PluginStream object. If needNotify is true, then the + // plugin will be notified when the stream has been fully sent. + PluginStream(PluginInstance *instance, + const char *url, + bool need_notify, + void *notify_data); + virtual ~PluginStream(); + + // In case of a redirect, this can be called to update the url. But it must + // be called before Open(). + void UpdateUrl(const char* url); + + // Opens the stream to the Plugin. + // If the mime-type is not specified, we'll try to find one based on the + // mime-types table and the extension (if any) in the URL. + // If the size of the stream is known, use length to set the size. If + // not known, set length to 0. + bool Open(const std::string &mime_type, + const std::string &headers, + uint32 length, + uint32 last_modified); + + // Writes to the stream. + int Write(const char *buf, const int len); + + // Write the result as a file. + void WriteAsFile(); + + // Notify the plugin that a stream is complete. + void Notify(NPReason reason); + + // Close the stream. + virtual bool Close(NPReason reason); + + const NPStream* stream() const { + return &stream_; + } + + protected: + PluginInstance* instance() { return instance_.get(); } + // Check if the stream is open. + bool open() { return opened_; } + + private: + // Open a temporary file for this stream. + // If successful, will set temp_file_name_, temp_file_handle_, and + // return true. + bool OpenTempFile(); + + // Closes the temporary file if it is open. + void CloseTempFile(); + + // Closes the temporary file if it is open and deletes the file. + void CleanupTempFile(); + + // Sends the data to the file if it's open. + bool WriteToFile(const char *buf, const int length); + + // Sends the data to the plugin. If it's not ready, handles buffering it + // and retrying later. + bool WriteToPlugin(const char *buf, const int length); + + // Send the data to the plugin, returning how many bytes it accepted, or -1 + // if an error occurred. + int TryWriteToPlugin(const char *buf, const int length); + + // The callback which calls TryWriteToPlugin. + void OnDelayDelivery(); + + private: + NPStream stream_; + std::string headers_; + scoped_refptr<PluginInstance> instance_; + int bytes_sent_; + bool notify_needed_; + void * notify_data_; + bool close_on_write_data_; + uint16 requested_plugin_mode_; + bool opened_; + char temp_file_name_[MAX_PATH]; + HANDLE temp_file_handle_; + std::vector<char> delivery_data_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginStream); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__ diff --git a/webkit/glue/plugins/plugin_stream_url.cc b/webkit/glue/plugins/plugin_stream_url.cc new file mode 100644 index 0000000..9400f2c --- /dev/null +++ b/webkit/glue/plugins/plugin_stream_url.cc @@ -0,0 +1,104 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_stream_url.h" + +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/plugins/plugin_instance.h" + +namespace NPAPI { + +PluginStreamUrl::PluginStreamUrl( + int resource_id, + const GURL &url, + PluginInstance *instance, + bool notify_needed, + void *notify_data) + : PluginStream(instance, url.spec().c_str(), notify_needed, notify_data), + url_(url), + id_(resource_id) { +} + +PluginStreamUrl::~PluginStreamUrl() { +} + +bool PluginStreamUrl::Close(NPReason reason) { + if (id_ != 0) { + if (instance()->webplugin()) { + instance()->webplugin()->CancelResource(id_); + } + + id_ = 0; + } + + bool result = PluginStream::Close(reason); + instance()->RemoveStream(this); + return result; +} + +void PluginStreamUrl::WillSendRequest(const GURL& url) { + url_ = url; + UpdateUrl(url.spec().c_str()); +} + +void PluginStreamUrl::DidReceiveResponse(const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified, + bool* cancel) { + + bool opened = Open(mime_type, + headers, + expected_length, + last_modified); + if (!opened) { + instance()->RemoveStream(this); + *cancel = true; + } +} + +void PluginStreamUrl::DidReceiveData(const char* buffer, int length) { + if (!open()) + return; + + if (length > 0) + Write(const_cast<char*>(buffer), length); +} + +void PluginStreamUrl::DidFinishLoading() { + Close(NPRES_DONE); +} + +void PluginStreamUrl::DidFail() { + Close(NPRES_NETWORK_ERR); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_stream_url.h b/webkit/glue/plugins/plugin_stream_url.h new file mode 100644 index 0000000..b6b5b3b --- /dev/null +++ b/webkit/glue/plugins/plugin_stream_url.h @@ -0,0 +1,84 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__ + + +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_stream.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI { + +class PluginInstance; + +// A NPAPI Stream based on a URL. +class PluginStreamUrl : public PluginStream, + public WebPluginResourceClient { + public: + // Create a new stream for sending to the plugin by fetching + // a URL. If notifyNeeded is set, then the plugin will be notified + // when the stream has been fully sent to the plugin. Initialize + // must be called before the object is used. + PluginStreamUrl(int resource_id, + const GURL &url, + PluginInstance *instance, + bool notify_needed, + void *notify_data); + virtual ~PluginStreamUrl(); + + // Stop sending the stream to the client. + // Overrides the base Close so we can cancel our fetching the URL if + // it is still loading. + bool Close(NPReason reason); + + // + // WebPluginResourceClient methods + // + void WillSendRequest(const GURL& url); + void DidReceiveResponse(const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified, + bool* cancel); + void DidReceiveData(const char* buffer, int length); + void DidFinishLoading(); + void DidFail(); + + private: + GURL url_; + int id_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginStreamUrl); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
\ No newline at end of file diff --git a/webkit/glue/plugins/plugin_string_stream.cc b/webkit/glue/plugins/plugin_string_stream.cc new file mode 100644 index 0000000..c32f5ab --- /dev/null +++ b/webkit/glue/plugins/plugin_string_stream.cc @@ -0,0 +1,56 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_string_stream.h" + +namespace NPAPI { + +PluginStringStream::PluginStringStream( + PluginInstance *instance, + const std::string &url, + bool notify_needed, + void *notify_data) + : PluginStream(instance, url.c_str(), notify_needed, notify_data) { +} + +PluginStringStream::~PluginStringStream() { +} + +void PluginStringStream::SendToPlugin(const std::string &data, + const std::string &mime_type) { + int length = static_cast<int>(data.length()); + if (Open(mime_type, std::string(), length, 0)) { + // TODO - check if it was not fully sent, and figure out a backup plan. + int written = Write(data.c_str(), length); + NPReason reason = written == length ? NPRES_DONE : NPRES_NETWORK_ERR; + Close(reason); + } +} + +} diff --git a/webkit/glue/plugins/plugin_string_stream.h b/webkit/glue/plugins/plugin_string_stream.h new file mode 100644 index 0000000..8c2e2c6 --- /dev/null +++ b/webkit/glue/plugins/plugin_string_stream.h @@ -0,0 +1,62 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H__ + +#include "webkit/glue/plugins/plugin_stream.h" + +namespace NPAPI { + +class PluginInstance; + +// An NPAPI stream from a string. +class PluginStringStream : public PluginStream { + public: + // Create a new stream for sending to the plugin. + // If notify_needed, will notify the plugin after the data has + // all been sent. + PluginStringStream(PluginInstance *instance, + const std::string &url, + bool notify_needed, + void *notify_data); + virtual ~PluginStringStream(); + + // Initiates the sending of data to the plugin. + void SendToPlugin(const std::string &data, + const std::string &mime_type); + + private: + + DISALLOW_EVIL_CONSTRUCTORS(PluginStringStream); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H__ diff --git a/webkit/glue/plugins/test/SConscript b/webkit/glue/plugins/test/SConscript new file mode 100644 index 0000000..f8113f6 --- /dev/null +++ b/webkit/glue/plugins/test/SConscript @@ -0,0 +1,109 @@ +# Copyright 2008, 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. + +Import('env', 'env_res') + +env = env.Clone() +env_res = env_res.Clone() + +input_files = [ + 'npapi_constants.cc', + 'npapi_test.cc', + env_res.RES('npapi_test.rc'), + 'plugin_arguments_test.cc', + 'plugin_client.cc', + 'plugin_delete_plugin_in_stream_test.cc', + 'plugin_execute_script_delete_test.cc', + 'plugin_get_javascript_url_test.cc', + 'plugin_geturl_test.cc', + 'plugin_new_fails_test.cc', + 'plugin_npobject_lifetime_test.cc', + 'plugin_npobject_proxy_test.cc', + 'plugin_test.cc', + 'plugin_window_size_test.cc', + 'npapi_test.def', + + env.File('$BASE_DIR/base.lib'), +] + +env.Append( + CCFLAGS = [ + '/TP', + '/wd4800', + '/wd4503', + '/wd4819', + ], + + LIBS = [ + 'comctl32.lib', + 'shlwapi.lib', + 'rpcrt4.lib', + 'winmm.lib', + 'wininet.lib', + 'version.lib', + 'msimg32.lib', + 'ws2_32.lib', + 'usp10.lib', + 'psapi.lib', + 'kernel32.lib', + 'user32.lib', + 'gdi32.lib', + 'winspool.lib', + 'comdlg32.lib', + 'advapi32.lib', + 'shell32.lib', + 'ole32.lib', + 'oleaut32.lib', + 'uuid.lib', + 'odbc32.lib', + 'odbccp32.lib', + + 'delayimp.lib', + ], + + LINKFLAGS = [ + '/DELAYLOAD:"dwmapi.dll"', + '/DELAYLOAD:"uxtheme.dll"', + '/FIXED:No', + '/SUBSYSTEM:CONSOLE', + '/MACHINE:X86', + '/safeseh', + '/dynamicbase', + '/ignore:4199', + '/nxcompat', + ], +) + +dll = env.SharedLibrary(['npapi_test_plugin', + 'npapi_test_plugin.ilk', + 'npapi_test_plugin.pdb'], + input_files) + +i = env.Install('$TARGET_ROOT', dll) +env.Alias('webkit', i) diff --git a/webkit/glue/plugins/test/npapi_constants.cc b/webkit/glue/plugins/test/npapi_constants.cc new file mode 100644 index 0000000..eadd9e0 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_constants.cc @@ -0,0 +1,35 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/npapi_constants.h" + +namespace NPAPIClient { +const char kTestCompleteCookie[] = "status"; +const char kTestCompleteSuccess[] = "OK"; +} diff --git a/webkit/glue/plugins/test/npapi_constants.h b/webkit/glue/plugins/test/npapi_constants.h new file mode 100644 index 0000000..0894e7a --- /dev/null +++ b/webkit/glue/plugins/test/npapi_constants.h @@ -0,0 +1,44 @@ +// Copyright 2008, 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. + +// Constants for the NPAPI test + +#ifndef WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__ +#define WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__ + +namespace NPAPIClient { +// The name of the cookie which will be used to communicate between +// the plugin and the test harness. +extern const char kTestCompleteCookie[]; + +// The cookie value which will be sent to the client upon successful +// test. +extern const char kTestCompleteSuccess[]; +} +#endif // WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__ diff --git a/webkit/glue/plugins/test/npapi_test.cc b/webkit/glue/plugins/test/npapi_test.cc new file mode 100644 index 0000000..4272dd4 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test.cc @@ -0,0 +1,92 @@ +// Copyright 2008, 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. + +// +// npapitest +// +// This is a NPAPI Plugin Program which is used to test the Browser's NPAPI +// host implementation. It is used in conjunction with the npapi_unittest. +// +// As a NPAPI Plugin, you can invoke it by creating a web page of the following +// type: +// +// <embed src="content-to-load" type="application/vnd.npapi-test" +// name="test-name"> +// +// arguments: +// src: This is the initial content which will be sent to the plugin. +// type: Must be "application/vnd.npapi-test" +// name: The testcase to run when invoked +// id: The id of the test being run (for testing concurrent plugins) +// +// The Plugin drives the actual test, calling host functions and validating the +// Host callbacks which it receives. It is the duty of the plugin to record +// all errors. +// +// To indicate test completion, the plugin expects the containing HTML page to +// implement two javascript functions: +// onSuccess(string testname); +// onFailure(string testname, string results); +// The HTML host pages used in this test will then set a document cookie +// which the automated test framework can poll for and discover that the +// test has completed. +// +// +// TESTS +// When the PluginClient receives a NPP_New callback from the browser, +// it looks at the "name" argument which is passed in. It verifies that +// the name matches a known test, and instantiates that test. The test is +// a subclass of PluginTest. +// +// + +#include <windows.h> +#include "webkit/glue/plugins/test/plugin_client.h" + +BOOL WINAPI DllMain(HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) { + return TRUE; +} + +extern "C" { +NPError WINAPI NP_GetEntryPoints(NPPluginFuncs* pFuncs) { + return NPAPIClient::PluginClient::GetEntryPoints(pFuncs); +} + +NPError WINAPI NP_Initialize(NPNetscapeFuncs* pFuncs) { + return NPAPIClient::PluginClient::Initialize(pFuncs); +} + +NPError WINAPI NP_Shutdown() { + return NPAPIClient::PluginClient::Shutdown(); +} +} // extern "C" + +namespace WebCore { + const char* currentTextBreakLocaleID() { return "en_us"; } +}
\ No newline at end of file diff --git a/webkit/glue/plugins/test/npapi_test.def b/webkit/glue/plugins/test/npapi_test.def new file mode 100644 index 0000000..4481c16 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test.def @@ -0,0 +1,6 @@ +LIBRARY npapi_test_plugin + +EXPORTS + NP_GetEntryPoints @1 + NP_Initialize @2 + NP_Shutdown @3 diff --git a/webkit/glue/plugins/test/npapi_test.rc b/webkit/glue/plugins/test/npapi_test.rc new file mode 100644 index 0000000..a337ce6 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test.rc @@ -0,0 +1,102 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "FileDescription", "npapites Dynamic Link Library" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "npapites" + VALUE "LegalCopyright", "Copyright (C) 2007" + VALUE "OriginalFilename", "npapites.dll" + VALUE "ProductName", " npapites Dynamic Link Library" + VALUE "ProductVersion", "1, 0, 0, 1" + VALUE "MIMEType", "application/vnd.npapi-test\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/webkit/glue/plugins/test/npapi_test_plugin.vcproj b/webkit/glue/plugins/test/npapi_test_plugin.vcproj new file mode 100644 index 0000000..ffcd76c --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test_plugin.vcproj @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="npapi_test_plugin" + ProjectGUID="{0D04AEC1-6B68-492C-BCCF-808DFD69ABC6}" + RootNamespace="npapi_test" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="2" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;..\..\..\build\webkit_common.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="winmm.lib" + ModuleDefinitionFile="npapi_test.def" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="2" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;..\..\..\build\webkit_common.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="winmm.lib" + ModuleDefinitionFile="npapi_test.def" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <File + RelativePath=".\npapi_constants.cc" + > + </File> + <File + RelativePath=".\npapi_constants.h" + > + </File> + <File + RelativePath=".\npapi_test.cc" + > + </File> + <File + RelativePath=".\npapi_test.rc" + > + </File> + <File + RelativePath=".\plugin_arguments_test.cc" + > + </File> + <File + RelativePath=".\plugin_arguments_test.h" + > + </File> + <File + RelativePath=".\plugin_client.cc" + > + </File> + <File + RelativePath=".\plugin_client.h" + > + </File> + <File + RelativePath=".\plugin_delete_plugin_in_stream_test.cc" + > + </File> + <File + RelativePath=".\plugin_delete_plugin_in_stream_test.h" + > + </File> + <File + RelativePath=".\plugin_execute_script_delete_test.cc" + > + </File> + <File + RelativePath=".\plugin_execute_script_delete_test.h" + > + </File> + <File + RelativePath=".\plugin_get_javascript_url_test.cc" + > + </File> + <File + RelativePath=".\plugin_get_javascript_url_test.h" + > + </File> + <File + RelativePath=".\plugin_geturl_test.cc" + > + </File> + <File + RelativePath=".\plugin_geturl_test.h" + > + </File> + <File + RelativePath=".\plugin_new_fails_test.cc" + > + </File> + <File + RelativePath=".\plugin_new_fails_test.h" + > + </File> + <File + RelativePath=".\plugin_npobject_lifetime_test.cc" + > + </File> + <File + RelativePath=".\plugin_npobject_lifetime_test.h" + > + </File> + <File + RelativePath=".\plugin_npobject_proxy_test.cc" + > + </File> + <File + RelativePath=".\plugin_npobject_proxy_test.h" + > + </File> + <File + RelativePath=".\plugin_test.cc" + > + </File> + <File + RelativePath=".\plugin_test.h" + > + </File> + <File + RelativePath=".\plugin_window_size_test.cc" + > + </File> + <File + RelativePath=".\plugin_window_size_test.h" + > + </File> + <File + RelativePath=".\resource.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/webkit/glue/plugins/test/plugin_arguments_test.cc b/webkit/glue/plugins/test/plugin_arguments_test.cc new file mode 100644 index 0000000..dfd34e4 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_arguments_test.cc @@ -0,0 +1,92 @@ +// Copyright 2008, 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. + +#define STRSAFE_NO_DEPRECATE +#include <strsafe.h> +#include "webkit/glue/plugins/test/plugin_arguments_test.h" + +namespace NPAPIClient { + +PluginArgumentsTest::PluginArgumentsTest(NPP id, + NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError PluginArgumentsTest::New(uint16 mode, int16 argc, + const char* argn[], const char* argv[], + NPSavedData* saved) { + // mode: should be the string either "NP_EMBED" or "NP_FULL", + // depending on the mode passed in. + // count: the count of "val" arguments. If the value is + // 2, then we'll find arguments "val1" and "val2". If + // the value is 0, then there will be no "val" arguments. + // size: each val string will be this size * the value's + // index. E.g if size is "10", val1 will be 10bytes, + // and val2 will be 20bytes. + const char *mode_string = GetArgValue("mode", argc, argn, argv); + ExpectAsciiStringNotEqual(mode_string, (const char *)NULL); + if (mode_string != NULL) { + std::string mode_dep_string = mode_string; + if (mode == NP_EMBED) + ExpectStringLowerCaseEqual(mode_dep_string, "np_embed"); + else if (mode == NP_FULL) + ExpectStringLowerCaseEqual(mode_dep_string, "np_full"); + } + + const char *count_string = GetArgValue("count", argc, argn, argv); + if (count_string != NULL) { + int max_args = atoi(count_string); + + const char *size_string = GetArgValue("size", argc, argn, argv); + ExpectAsciiStringNotEqual(size_string, (const char *)NULL); + if (size_string != NULL) { + int size = atoi(size_string); + + for (int index = 1; index <= max_args; index++) { + char arg_name[MAX_PATH]; // Use MAX_PATH for Max Name Length + StringCchPrintfA(arg_name, sizeof(arg_name), "%s%d", "val", index); + const char *val_string = GetArgValue(arg_name, argc, argn, argv); + ExpectAsciiStringNotEqual(val_string, (const char*)NULL); + if (val_string != NULL) + ExpectIntegerEqual((int)strlen(val_string), (index*size)); + } + } + } + + return PluginTest::New(mode, argc, argn, argv, saved); +} + +NPError PluginArgumentsTest::SetWindow(NPWindow* pNPWindow) { + // This test just tests the arguments. We're done now. + this->SignalTestCompleted(); + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_arguments_test.h b/webkit/glue/plugins/test/plugin_arguments_test.h new file mode 100644 index 0000000..57d9e6f --- /dev/null +++ b/webkit/glue/plugins/test/plugin_arguments_test.h @@ -0,0 +1,69 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The PluginArgumentsTest test that we properly receive arguments +// intended for the plugin. +// +// This is basically overkill for testing that the arguments passed +// to the plugin match what we expect. +// +// We expect to find the following arguments: +// mode: should be the string either "NP_EMBED" or "NP_FULL", +// depending on the mode passed in. +// count: the count of "val" arguments. If the value is +// 2, then we'll find arguments "val1" and "val2". If +// the value is 0, then there will be no "val" arguments. +// size: each val string will be this size * the value's +// index. E.g if size is "10", val1 will be 10bytes, +// and val2 will be 20bytes. +// +class PluginArgumentsTest : public PluginTest { + public: + // Constructor. + PluginArgumentsTest(NPP id, NPNetscapeFuncs *host_functions); + + // Initialize this PluginTest based on the arguments from NPP_New. + virtual NPError New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_client.cc b/webkit/glue/plugins/test/plugin_client.cc new file mode 100644 index 0000000..d8d409e --- /dev/null +++ b/webkit/glue/plugins/test/plugin_client.cc @@ -0,0 +1,300 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_client.h" +#include "webkit/glue/plugins/test/plugin_arguments_test.h" +#include "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h" +#include "webkit/glue/plugins/test/plugin_execute_script_delete_test.h" +#include "webkit/glue/plugins/test/plugin_get_javascript_url_test.h" +#include "webkit/glue/plugins/test/plugin_geturl_test.h" +#include "webkit/glue/plugins/test/plugin_new_fails_test.h" +#include "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h" +#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h" +#include "webkit/glue/plugins/test/plugin_window_size_test.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" + +namespace NPAPIClient { + +NPNetscapeFuncs* PluginClient::host_functions_; + +NPError PluginClient::GetEntryPoints(NPPluginFuncs* pFuncs) { + if (pFuncs == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + if (pFuncs->size < sizeof(NPPluginFuncs)) + return NPERR_INVALID_FUNCTABLE_ERROR; + + pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + pFuncs->newp = NPP_New; + pFuncs->destroy = NPP_Destroy; + pFuncs->setwindow = NPP_SetWindow; + pFuncs->newstream = NPP_NewStream; + pFuncs->destroystream = NPP_DestroyStream; + pFuncs->asfile = NPP_StreamAsFile; + pFuncs->writeready = NPP_WriteReady; + pFuncs->write = NPP_Write; + pFuncs->print = NPP_Print; + pFuncs->event = NPP_HandleEvent; + pFuncs->urlnotify = NPP_URLNotify; + pFuncs->getvalue = NPP_GetValue; + pFuncs->setvalue = NPP_SetValue; + pFuncs->javaClass = NPP_GetJavaClass; + + return NPERR_NO_ERROR; +} + +NPError PluginClient::Initialize(NPNetscapeFuncs* pFuncs) { + if (pFuncs == NULL) { + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + if (HIBYTE(pFuncs->version) > NP_VERSION_MAJOR) { + return NPERR_INCOMPATIBLE_VERSION_ERROR; + } + + host_functions_ = pFuncs; + + return NPERR_NO_ERROR; +} + +NPError PluginClient::Shutdown() { + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient + +extern "C" { +NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, + int16 argc, char* argn[], char* argv[], NPSavedData* saved) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // We look at the test name requested via the plugin arguments. We match + // that against a given test and try to instantiate it. + + // lookup the name parameter + int name_index = 0; + for (name_index = 0; name_index < argc; name_index++) + if (_stricmp(argn[name_index], "name") == 0) + break; + + if (name_index >= argc) + return NPERR_GENERIC_ERROR; // no name found + + NPError ret = NPERR_GENERIC_ERROR; + bool windowless_plugin = false; + + NPAPIClient::PluginTest *new_test = NULL; + if (_stricmp(argv[name_index], "arguments") == 0) { + new_test = new NPAPIClient::PluginArgumentsTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "geturl") == 0) { + new_test = new NPAPIClient::PluginGetURLTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "npobject_proxy") == 0) { + new_test = new NPAPIClient::NPObjectProxyTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "execute_script_delete_in_paint") == 0) { + new_test = new NPAPIClient::ExecuteScriptDeleteTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + windowless_plugin = true; + } else if (_stricmp(argv[name_index], "getjavascripturl") == 0) { + new_test = new NPAPIClient::ExecuteGetJavascriptUrlTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "checkwindowrect") == 0) { + new_test = new NPAPIClient::PluginWindowSizeTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "self_delete_plugin_stream") == 0) { + new_test = new NPAPIClient::DeletePluginInStreamTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "npobject_lifetime_test") == 0) { + new_test = new NPAPIClient::NPObjectLifetimeTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], + "npobject_lifetime_test_second_instance") == 0) { + new_test = new NPAPIClient::NPObjectLifetimeTestInstance2(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "new_fails") == 0) { + new_test = new NPAPIClient::NewFailsTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], + "npobject_delete_plugin_in_evaluate") == 0) { + new_test = new NPAPIClient::NPObjectDeletePluginInNPN_Evaluate(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else { + // If we don't have a test case for this, create a + // generic one which basically never fails. + new_test = new NPAPIClient::PluginTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } + + if (new_test) { + ret = new_test->New(mode, argc, (const char**)argn, + (const char**)argv, saved); + if ((ret == NPERR_NO_ERROR) && windowless_plugin) { + NPAPIClient::PluginClient::HostFunctions()->setvalue( + instance, NPPVpluginWindowBool, NULL); + } + } + + return ret; +} + +NPError NPP_Destroy(NPP instance, NPSavedData** save) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + delete plugin; + + // XXXMB - do work here. + return NPERR_GENERIC_ERROR; +} + +NPError NPP_SetWindow(NPP instance, NPWindow* pNPWindow) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (pNPWindow->window == NULL) { + return NPERR_NO_ERROR; + } + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->SetWindow(pNPWindow); +} + +NPError NPP_NewStream(NPP instance, NPMIMEType type, + NPStream* stream, NPBool seekable, uint16* stype) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->NewStream(type, stream, seekable, stype); +} + +int32 NPP_WriteReady(NPP instance, NPStream *stream) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->WriteReady(stream); +} + +int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, + int32 len, void *buffer) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->Write(stream, offset, len, buffer); +} + +NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->DestroyStream(stream, reason); +} + +void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) { + if (instance == NULL) + return; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->StreamAsFile(stream, fname); +} + +void NPP_Print(NPP instance, NPPrint* printInfo) { + if (instance == NULL) + return; + + // XXXMB - do work here. +} + +void NPP_URLNotify(NPP instance, const char* url, NPReason reason, + void* notifyData) { + if (instance == NULL) + return; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->URLNotify(url, reason, notifyData); +} + +NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // XXXMB - do work here. + return NPERR_GENERIC_ERROR; +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // XXXMB - do work here. + return NPERR_GENERIC_ERROR; +} + +int16 NPP_HandleEvent(NPP instance, void* event) { + if (instance == NULL) + return 0; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->HandleEvent(event); +} + +void* NPP_GetJavaClass(void) { + // XXXMB - do work here. + return NULL; +} +} // extern "C" + + + + diff --git a/webkit/glue/plugins/test/plugin_client.h b/webkit/glue/plugins/test/plugin_client.h new file mode 100644 index 0000000..70df0fe --- /dev/null +++ b/webkit/glue/plugins/test/plugin_client.h @@ -0,0 +1,71 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__ + +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +namespace NPAPIClient { + +// A PluginClient is a NPAPI Plugin. This class contains +// the bootstrapping functions used by the browser to load +// the plugin. +class PluginClient { + public: + // Although not documented in the NPAPI specification, this function + // gets the list of entry points in the NPAPI Plugin (client) for the + // NPAPI Host to call. + static NPError GetEntryPoints(NPPluginFuncs* pFuncs); + + // The browser calls this function only once: when a plug-in is loaded, + // before the first instance is created. This is the first function that + // the browser calls. NP_Initialize tells the plug-in that the browser has + // loaded it and provides global initialization. Allocate any memory or + // resources shared by all instances of your plug-in at this time. + static NPError Initialize(NPNetscapeFuncs* pFuncs); + + // The browser calls this function once after the last instance of your + // plug-in is destroyed, before unloading the plug-in library itself. Use + // NP_Shutdown to delete any data allocated in NP_Initialize to be shared + // by all instances of a plug-in. + static NPError Shutdown(); + + // The table of functions provided by the host. + static NPNetscapeFuncs *HostFunctions() { return host_functions_; } + + private: + static NPNetscapeFuncs* host_functions_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__ + diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc new file mode 100644 index 0000000..86fd917 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc @@ -0,0 +1,67 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h" + +#include "webkit/glue/plugins/test/plugin_client.h" + +namespace NPAPIClient { + +#define kUrl "javascript:window.location+\"\"" +#define kUrlStreamId 1 + +DeletePluginInStreamTest::DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + test_started_(false) { +} + +NPError DeletePluginInStreamTest::SetWindow(NPWindow* pNPWindow) { + if (!test_started_) { + std::string url = "self_delete_plugin_stream.html"; + HostFunctions()->geturlnotify(id(), url.c_str(), NULL, + reinterpret_cast<void*>(kUrlStreamId)); + test_started_ = true; + } + return NPERR_NO_ERROR; +} + +NPError DeletePluginInStreamTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + NPIdentifier delete_id = HostFunctions()->getstringidentifier("DeletePluginWithinScript"); + + NPObject *window_obj = NULL; + HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj); + + NPVariant rv; + HostFunctions()->invoke(id(), window_obj, delete_id, NULL, 0, &rv); + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h new file mode 100644 index 0000000..8eeaefb --- /dev/null +++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h @@ -0,0 +1,56 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests +class DeletePluginInStreamTest : public PluginTest { + public: + // Constructor. + DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions); + // + // NPAPI functions + // + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + private: + bool test_started_; + std::string self_url_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H + diff --git a/webkit/glue/plugins/test/plugin_execute_script_delete_test.cc b/webkit/glue/plugins/test/plugin_execute_script_delete_test.cc new file mode 100644 index 0000000..c18c91c --- /dev/null +++ b/webkit/glue/plugins/test/plugin_execute_script_delete_test.cc @@ -0,0 +1,53 @@ +// Copyright 2008, 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. + +#define STRSAFE_NO_DEPRECATE +#include "webkit/glue/plugins/test/plugin_execute_script_delete_test.h" +#include "webkit/glue/plugins/test/plugin_client.h" + +namespace NPAPIClient { + +ExecuteScriptDeleteTest::ExecuteScriptDeleteTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +int16 ExecuteScriptDeleteTest::HandleEvent(void* event) { + NPEvent* np_event = reinterpret_cast<NPEvent*>(event); + if (WM_PAINT == np_event->event ) { + NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions(); + NPUTF8* urlString = "javascript:DeletePluginWithinScript()"; + NPUTF8* targetString = NULL; + browser->geturl(id(), urlString, targetString); + SignalTestCompleted(); + } + // If this test failed, then we'd have crashed by now. + return PluginTest::HandleEvent(event); +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_execute_script_delete_test.h b/webkit/glue/plugins/test/plugin_execute_script_delete_test.h new file mode 100644 index 0000000..fe3c6ecf --- /dev/null +++ b/webkit/glue/plugins/test/plugin_execute_script_delete_test.h @@ -0,0 +1,50 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests the case where a plugin instance is deleted by invoking +// a javascript function in the context of a paint event. +class ExecuteScriptDeleteTest : public PluginTest { + public: + // Constructor. + ExecuteScriptDeleteTest(NPP id, NPNetscapeFuncs *host_functions); + // NPAPI HandleEvent handler + virtual int16 HandleEvent(void* event); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H + diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc new file mode 100644 index 0000000..1b6f1bd --- /dev/null +++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc @@ -0,0 +1,134 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_get_javascript_url_test.h" + + +// url for "self". +#define SELF_URL "javascript:window.location+\"\"" +// The identifier for the self url stream. +#define SELF_URL_STREAM_ID 1 + +// The identifier for the fetched url stream. +#define FETCHED_URL_STREAM_ID 2 + +// The maximum chunk size of stream data. +#define STREAM_CHUNK 197 + +namespace NPAPIClient { + +ExecuteGetJavascriptUrlTest::ExecuteGetJavascriptUrlTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + test_started_(false) { +} + +NPError ExecuteGetJavascriptUrlTest::SetWindow(NPWindow* pNPWindow) { + if (!test_started_) { + std::string url = SELF_URL; + HostFunctions()->geturlnotify(id(), url.c_str(), "_top", + reinterpret_cast<void*>(SELF_URL_STREAM_ID)); + test_started_ = true; + } + return NPERR_NO_ERROR; +} + +NPError ExecuteGetJavascriptUrlTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +int32 ExecuteGetJavascriptUrlTest::WriteReady(NPStream *stream) { + return STREAM_CHUNK; +} + +int32 ExecuteGetJavascriptUrlTest::Write(NPStream *stream, int32 offset, int32 len, + void *buffer) { + if (stream == NULL) + SetError("Write got null stream"); + if (len < 0 || len > STREAM_CHUNK) + SetError("Write got bogus stream chunk size"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + self_url_.append(static_cast<char*>(buffer), len); + break; + default: + SetError("Unexpected write callback"); + break; + } + // Pretend that we took all the data. + return len; +} + + +NPError ExecuteGetJavascriptUrlTest::DestroyStream(NPStream *stream, NPError reason) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + // don't care + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +void ExecuteGetJavascriptUrlTest::URLNotify(const char* url, NPReason reason, void* data) { + unsigned long stream_id = PtrToUlong(data); + switch (stream_id) { + case SELF_URL_STREAM_ID: + if (strcmp(url, SELF_URL) != 0) + SetError("URLNotify reported incorrect url for SELF_URL"); + if (self_url_.empty()) + SetError("Failed to obtain window location."); + SignalTestCompleted(); + break; + default: + SetError("Unexpected NewStream callback"); + break; + } +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.h b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h new file mode 100644 index 0000000..33b627e --- /dev/null +++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h @@ -0,0 +1,63 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests NPP_GetURLNotify for a javascript URL with _top +// as the target frame. +class ExecuteGetJavascriptUrlTest : public PluginTest { + public: + // Constructor. + ExecuteGetJavascriptUrlTest(NPP id, NPNetscapeFuncs *host_functions); + // + // NPAPI functions + // + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + virtual int32 WriteReady(NPStream *stream); + virtual int32 Write(NPStream *stream, int32 offset, int32 len, + void *buffer); + virtual NPError DestroyStream(NPStream *stream, NPError reason); + virtual void URLNotify(const char* url, NPReason reason, void* data); + + private: + bool test_started_; + std::string self_url_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H + diff --git a/webkit/glue/plugins/test/plugin_geturl_test.cc b/webkit/glue/plugins/test/plugin_geturl_test.cc new file mode 100644 index 0000000..33a2f17 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_geturl_test.cc @@ -0,0 +1,253 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_geturl_test.h" + +// url for "self". The %22%22 is to make a statement for javascript to +// evaluate and return. +#define SELF_URL "javascript:window.location+\"\"" + +// The identifier for the self url stream. +#define SELF_URL_STREAM_ID 1 + +// The identifier for the fetched url stream. +#define FETCHED_URL_STREAM_ID 2 + +// url for testing GetURL with a bogus URL. +#define BOGUS_URL "bogoproto:///x:/asdf.xysdhffieasdf.asdhj/" + +// The identifier for the bogus url stream. +#define BOGUS_URL_STREAM_ID 3 + +// The maximum chunk size of stream data. +#define STREAM_CHUNK 197 + +namespace NPAPIClient { + +PluginGetURLTest::PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + tests_started_(false), + tests_in_progress_(0), + test_file_handle_(INVALID_HANDLE_VALUE) { +} + +NPError PluginGetURLTest::SetWindow(NPWindow* pNPWindow) { + if (!tests_started_) { + tests_started_ = true; + + tests_in_progress_++; + + std::string url = SELF_URL; + HostFunctions()->geturlnotify(id(), url.c_str(), NULL, + reinterpret_cast<void*>(SELF_URL_STREAM_ID)); + + tests_in_progress_++; + std::string bogus_url = BOGUS_URL; + HostFunctions()->geturlnotify(id(), bogus_url.c_str(), NULL, + reinterpret_cast<void*>(BOGUS_URL_STREAM_ID)); + } + return NPERR_NO_ERROR; +} + +NPError PluginGetURLTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + break; + case FETCHED_URL_STREAM_ID: + { + std::string filename = self_url_; + if (filename.find("file:///", 0) != 0) { + SetError("Test expects a file-url."); + break; + } + + filename = filename.substr(8); // remove "file:///" + + test_file_handle_ = CreateFileA(filename.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (test_file_handle_ == INVALID_HANDLE_VALUE) + SetError("Could not open source file"); + } + break; + case BOGUS_URL_STREAM_ID: + SetError("Unexpected NewStream for BOGUS_URL"); + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +int32 PluginGetURLTest::WriteReady(NPStream *stream) { + unsigned long stream_id = PtrToUlong(stream->notifyData); + if (stream_id == BOGUS_URL_STREAM_ID) + SetError("Received WriteReady for BOGUS_URL"); + + return STREAM_CHUNK; +} + +int32 PluginGetURLTest::Write(NPStream *stream, int32 offset, int32 len, + void *buffer) { + if (stream == NULL) + SetError("Write got null stream"); + if (len < 0 || len > STREAM_CHUNK) + SetError("Write got bogus stream chunk size"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + self_url_.append(static_cast<char*>(buffer), len); + break; + case FETCHED_URL_STREAM_ID: + { + char read_buffer[STREAM_CHUNK]; + DWORD bytes = 0; + if (!ReadFile(test_file_handle_, read_buffer, len, + &bytes, NULL)) + SetError("Could not read data from source file"); + // Technically, readfile could return fewer than len + // bytes. But this is not likely. + if (bytes != len) + SetError("Did not read correct bytelength from source file"); + if (memcmp(read_buffer, buffer, len)) + SetError("Content mismatch between data and source!"); + } + break; + case BOGUS_URL_STREAM_ID: + SetError("Unexpected write callback for BOGUS_URL"); + break; + default: + SetError("Unexpected write callback"); + break; + } + // Pretend that we took all the data. + return len; +} + + +NPError PluginGetURLTest::DestroyStream(NPStream *stream, NPError reason) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + // don't care + break; + case FETCHED_URL_STREAM_ID: + { + char read_buffer[STREAM_CHUNK]; + DWORD bytes = 0; + if (!ReadFile(test_file_handle_, read_buffer, sizeof(read_buffer), + &bytes, NULL)) + SetError("Could not read data from source file"); + if (bytes != 0) + SetError("Data and source mismatch on length"); + CloseHandle(test_file_handle_); + } + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +void PluginGetURLTest::StreamAsFile(NPStream* stream, const char* fname) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + // don't care + break; + default: + SetError("Unexpected NewStream callback"); + break; + } +} + +void PluginGetURLTest::URLNotify(const char* url, NPReason reason, void* data) { + if (!tests_in_progress_) { + SetError("URLNotify received after tests completed"); + return; + } + + if (!url) { + SetError("URLNotify received NULL url"); + return; + } + + unsigned long stream_id = PtrToUlong(data); + switch (stream_id) { + case SELF_URL_STREAM_ID: + if (strcmp(url, SELF_URL) != 0) + SetError("URLNotify reported incorrect url for SELF_URL"); + + // We have our stream url. Go fetch it. + HostFunctions()->geturlnotify(id(), self_url_.c_str(), NULL, + reinterpret_cast<void*>(FETCHED_URL_STREAM_ID)); + break; + case FETCHED_URL_STREAM_ID: + if (!url || strcmp(url, self_url_.c_str()) != 0) + SetError("URLNotify reported incorrect url for FETCHED_URL"); + tests_in_progress_--; + break; + case BOGUS_URL_STREAM_ID: + if (reason != NPRES_NETWORK_ERR) { + std::string err = "BOGUS_URL received unexpected URLNotify status: "; + char buf[10]; + _itoa_s(reason, buf, 10, 10); + err.append(buf); + SetError(err); + } + tests_in_progress_--; + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + + if (tests_in_progress_ == 0) + SignalTestCompleted(); +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_geturl_test.h b/webkit/glue/plugins/test/plugin_geturl_test.h new file mode 100644 index 0000000..07e41aa --- /dev/null +++ b/webkit/glue/plugins/test/plugin_geturl_test.h @@ -0,0 +1,71 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The PluginGetURLTest test functionality of the NPN_GetURL +// and NPN_GetURLNotify methods. +// +// This test first discovers it's URL by sending a GetURL request +// for 'javascript:top.location'. After receiving that, the +// test will request the url itself (again via GetURL). +class PluginGetURLTest : public PluginTest { + public: + // Constructor. + PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions); + + // + // NPAPI functions + // + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + virtual int32 WriteReady(NPStream *stream); + virtual int32 Write(NPStream *stream, int32 offset, int32 len, + void *buffer); + virtual NPError DestroyStream(NPStream *stream, NPError reason); + virtual void StreamAsFile(NPStream* stream, const char* fname); + virtual void URLNotify(const char* url, NPReason reason, void* data); + + private: + bool tests_started_; + int tests_in_progress_; + std::string self_url_; + HANDLE test_file_handle_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.cc b/webkit/glue/plugins/test/plugin_new_fails_test.cc new file mode 100644 index 0000000..382bf4e --- /dev/null +++ b/webkit/glue/plugins/test/plugin_new_fails_test.cc @@ -0,0 +1,43 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_new_fails_test.h" + +namespace NPAPIClient { + +NewFailsTest::NewFailsTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError NewFailsTest::New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved) { + return NPERR_GENERIC_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.h b/webkit/glue/plugins/test/plugin_new_fails_test.h new file mode 100644 index 0000000..e864553 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_new_fails_test.h @@ -0,0 +1,46 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__ +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +class NewFailsTest : public PluginTest { + public: + NewFailsTest(NPP id, NPNetscapeFuncs *host_functions); + virtual NPError New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NPP_NEW_FAILS_TEST_H__ diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc new file mode 100644 index 0000000..24cc9e9 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc @@ -0,0 +1,189 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h" + +namespace NPAPIClient { + +const int kNPObjectLifetimeTimer = 100; +const int kNPObjectLifetimeTimerElapse = 2000; + +NPObject* NPObjectLifetimeTestInstance2::plugin_instance_object_ = NULL; + +NPObjectDeletePluginInNPN_Evaluate* + NPObjectDeletePluginInNPN_Evaluate::g_npn_evaluate_test_instance_ = NULL; + +NPObjectLifetimeTest::NPObjectLifetimeTest(NPP id, + NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + other_plugin_instance_object_(NULL) { +} + +NPError NPObjectLifetimeTest::SetWindow(NPWindow* pNPWindow) { + HWND window_handle = reinterpret_cast<HWND>(pNPWindow->window); + if (!::GetProp(window_handle, L"Plugin_Instance")) { + ::SetProp(window_handle, L"Plugin_Instance", this); + // We attempt to retreive the NPObject for the plugin instance identified + // by the NPObjectLifetimeTestInstance2 class as it may not have been + // instantiated yet. + SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse, + TimerProc); + } + return NPERR_NO_ERROR; +} + +void CALLBACK NPObjectLifetimeTest::TimerProc( + HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds) { + + KillTimer(window, kNPObjectLifetimeTimer); + NPObjectLifetimeTest* this_instance = + reinterpret_cast<NPObjectLifetimeTest*> + (::GetProp(window, L"Plugin_Instance")); + + this_instance->other_plugin_instance_object_ = + NPObjectLifetimeTestInstance2::plugin_instance_object_; + this_instance->HostFunctions()->retainobject( + this_instance->other_plugin_instance_object_); + + NPString script; + script.UTF8Characters = "javascript:DeleteSecondPluginInstance()"; + script.UTF8Length = static_cast<uint32_t>(strlen(script.UTF8Characters)); + + this_instance->HostFunctions()->geturlnotify( + this_instance->id(), "javascript:DeleteSecondPluginInstance()", NULL, + reinterpret_cast<void*>(1)); +} + +void NPObjectLifetimeTest::URLNotify(const char* url, NPReason reason, + void* data) { + // Create a "location" identifier. + NPIdentifier identifier = HostFunctions()->getstringidentifier("location"); + // Declare a local variant value. + NPVariant variantValue; + // Get the location property from the window object (which is another object). + bool b1 = HostFunctions()->getproperty(id(), other_plugin_instance_object_, + identifier, &variantValue ); + HostFunctions()->releaseobject(other_plugin_instance_object_); + other_plugin_instance_object_ = NULL; + // If this test failed, then we'd have crashed by now. + SignalTestCompleted(); +} + +NPObjectLifetimeTestInstance2::NPObjectLifetimeTestInstance2( + NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPObjectLifetimeTestInstance2::~NPObjectLifetimeTestInstance2() { + if (plugin_instance_object_) { + HostFunctions()->releaseobject(plugin_instance_object_); + plugin_instance_object_ = NULL; + } +} + +NPError NPObjectLifetimeTestInstance2::SetWindow(NPWindow* pNPWindow) { + if (!plugin_instance_object_) { + if (!HostFunctions()->getvalue(id(), NPNVWindowNPObject, + &plugin_instance_object_)) { + SetError("Failed to get NPObject for plugin instance2"); + SignalTestCompleted(); + return NPERR_GENERIC_ERROR; + } + } + + return NPERR_NO_ERROR; +} + + +NPObjectDeletePluginInNPN_Evaluate::NPObjectDeletePluginInNPN_Evaluate( + NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + plugin_instance_object_(NULL), + npn_evaluate_timer_proc_set_(false) { + g_npn_evaluate_test_instance_ = this; +} + +NPObjectDeletePluginInNPN_Evaluate::~NPObjectDeletePluginInNPN_Evaluate() { + if (plugin_instance_object_) { + HostFunctions()->releaseobject(plugin_instance_object_); + plugin_instance_object_ = NULL; + } +} + +NPError NPObjectDeletePluginInNPN_Evaluate::SetWindow(NPWindow* np_window) { + if (!::IsWindowVisible(reinterpret_cast<HWND>(np_window->window))) + return NPERR_NO_ERROR; + + HWND window_handle = reinterpret_cast<HWND>(np_window->window); + // We setup a timerproc to invoke NPN_Evaluate to destroy this plugin + // instance. This is to ensure that we don't destroy the plugin instance + // while it is being used in webkit as this leads to crashes and is a + // more accurate representation of the renderer crash as described in + // http://b/issue?id=1134683. + if (!npn_evaluate_timer_proc_set_) { + npn_evaluate_timer_proc_set_ = true; + SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse, + TimerProc); + } + + return NPERR_NO_ERROR; +} + +void CALLBACK NPObjectDeletePluginInNPN_Evaluate::TimerProc( + HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds) { + + KillTimer(window, kNPObjectLifetimeTimer); + NPObject *window_obj = NULL; + g_npn_evaluate_test_instance_->HostFunctions()->getvalue( + g_npn_evaluate_test_instance_->id(), NPNVWindowNPObject, + &window_obj); + + if (!window_obj) { + g_npn_evaluate_test_instance_->SetError( + "Failed to get NPObject for plugin instance2"); + g_npn_evaluate_test_instance_->SignalTestCompleted(); + return; + } + + std::string script = "javascript:DeletePluginWithinScript()"; + NPString script_string; + script_string.UTF8Characters = script.c_str(); + script_string.UTF8Length = + static_cast<unsigned int>(script.length()); + + NPVariant result_var; + NPError result = g_npn_evaluate_test_instance_->HostFunctions()->evaluate( + g_npn_evaluate_test_instance_->id(), window_obj, + &script_string, &result_var); + // If this test failed we would have crashed by now. +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h new file mode 100644 index 0000000..2a5d808 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h @@ -0,0 +1,96 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__ +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The NPObjectLifeTime class tests the case where a plugin has an NPObject +// which points to a different plugin instance on a different frame in the +// page and whether refcounts on this npobject are valid when the source frame +// is destroyed. +class NPObjectLifetimeTest : public PluginTest { + public: + // Constructor. + NPObjectLifetimeTest(NPP id, NPNetscapeFuncs *host_functions); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); + + virtual void URLNotify(const char* url, NPReason reason, void* data); + + protected: + NPObject* other_plugin_instance_object_; + static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds); +}; + +// The NPObjectLifetimeTestInstance2 class represents the plugin instance +// which is deleted by the NPObjectLifeTime class via a javascript function. +class NPObjectLifetimeTestInstance2 : public PluginTest { + public: + // Constructor. + NPObjectLifetimeTestInstance2(NPP id, NPNetscapeFuncs *host_functions); + ~NPObjectLifetimeTestInstance2(); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); + protected: + static NPObject* plugin_instance_object_; + friend class NPObjectLifetimeTest; + +}; + +// The NPObjectLifeTime class tests the case where a plugin instance is +// destroyed in NPN_Evaluate +class NPObjectDeletePluginInNPN_Evaluate : public PluginTest { + public: + // Constructor. + NPObjectDeletePluginInNPN_Evaluate(NPP id, NPNetscapeFuncs *host_functions); + ~NPObjectDeletePluginInNPN_Evaluate(); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); + + protected: + NPObject* plugin_instance_object_; + static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds); + private: + bool npn_evaluate_timer_proc_set_; + static NPObjectDeletePluginInNPN_Evaluate* g_npn_evaluate_test_instance_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc new file mode 100644 index 0000000..e7fa2fb --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc @@ -0,0 +1,68 @@ +// Copyright 2008, 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. + +#define STRSAFE_NO_DEPRECATE +#include <strsafe.h> +#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h" + +namespace NPAPIClient { + +NPObjectProxyTest::NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError NPObjectProxyTest::SetWindow(NPWindow* pNPWindow) { + NPIdentifier document_id = HostFunctions()->getstringidentifier("document"); + NPIdentifier create_text_node_id = HostFunctions()->getstringidentifier("createTextNode"); + NPIdentifier append_child_id = HostFunctions()->getstringidentifier("appendChild"); + + NPVariant docv; + NPObject *window_obj = NULL; + HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj); + + HostFunctions()->getproperty(id(), window_obj, document_id, &docv); + NPObject *doc = NPVARIANT_TO_OBJECT(docv); + + NPVariant strv; +#pragma warning(suppress: 4267) + STRINGZ_TO_NPVARIANT("div", strv); + + NPVariant textv; + HostFunctions()->invoke(id(), doc, create_text_node_id, &strv, 1, &textv); + + NPVariant v; + HostFunctions()->invoke(id(), doc, append_child_id, &textv, 1, &v); + + // If this test failed, then we'd have crashed by now. + SignalTestCompleted(); + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.h b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h new file mode 100644 index 0000000..e5c94d4 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h @@ -0,0 +1,53 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__ +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The NPObjectProxyTest tests that when we proxy an NPObject that is itself +// a proxy, we don't create a new proxy but instead just use the original +// pointer. + +class NPObjectProxyTest : public PluginTest { + public: + // Constructor. + NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_test.cc b/webkit/glue/plugins/test/plugin_test.cc new file mode 100644 index 0000000..043fef9 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_test.cc @@ -0,0 +1,160 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_test.h" +#include "webkit/glue/plugins/test/npapi_constants.h" + +namespace NPAPIClient { + +PluginTest::PluginTest(NPP id, NPNetscapeFuncs *host_functions) { + id_ = id; + id_->pdata = this; + host_functions_ = host_functions; +} + +NPError PluginTest::New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved) { + test_name_ = this->GetArgValue("name", argc, argn, argv); + test_id_ = this->GetArgValue("id", argc, argn, argv); + return NPERR_NO_ERROR; +} + +NPError PluginTest::SetWindow(NPWindow* pNPWindow) { + return NPERR_NO_ERROR; +} + +// It's a shame I have to implement URLEncode. But, using webkit's +// or using chrome's means a ball of string of dlls and dependencies that +// is very very long. After spending far too much time on it, +// I'll just encode it myself. Too bad Microsoft doesn't implement +// this in a reusable way either. Both webkit and chrome will +// end up using libicu, which is a string of dependencies we don't +// want. + +inline BYTE toHex(const BYTE &x) { + return x > 9 ? x + 55: x + 48; +} + +std::string URLEncode(const std::string &sIn) { + std::string sOut; + + const size_t length = sIn.length(); + for (size_t idx = 0; idx < length;) { + const char ch = sIn.at(idx); + if (isalnum(ch)) { + sOut.append(1, ch); + } else if (isspace(ch) && ((ch != '\n') && (ch != '\r'))) { + sOut.append(1, '+'); + } else { + sOut.append(1, '%'); + sOut.append(1, toHex(ch>>4)); + sOut.append(1, toHex(ch%16)); + } + idx++; + } + return sOut; +} + +void PluginTest::SignalTestCompleted() { + // To signal test completion, we expect a couple of + // javascript functions to be defined in the webpage + // which hosts this plugin: + // onSuccess(test_name, test_id) + // onFailure(test_name, test_id, error_message) + std::string script_result; + std::string script_url; + if (Succeeded()) { + script_url.append("onSuccess(\""); + script_url.append(test_name_); + script_url.append("\",\""); + script_url.append(test_id_); + script_url.append("\");"); + } else { + script_url.append("onFailure(\""); + script_url.append(test_name_); + script_url.append("\",\""); + script_url.append(test_id_); + script_url.append("\",\""); + script_url.append(test_status_); + script_url.append("\");"); + } + script_url = URLEncode(script_url); + script_result.append("javascript:"); + script_result.append(script_url); + host_functions_->geturl(id_, script_result.c_str(), "_self"); +} + +const char *PluginTest::GetArgValue(const char *name, const int16 argc, + const char *argn[], const char *argv[]) { + for (int idx = 0; idx < argc; idx++) + if (_stricmp(argn[idx], name) == 0) + return argv[idx]; + return NULL; +} + +void PluginTest::SetError(const std::string &msg) { + test_status_.append(msg); +} + +NPError PluginTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + // There is no default action here. + return NPERR_NO_ERROR; +} + +int32 PluginTest::WriteReady(NPStream *stream) { + // Take data in small chunks + return 4096; +} + +int32 PluginTest::Write(NPStream *stream, int32 offset, int32 len, + void *buffer) { + // Pretend that we took all the data. + return len; +} + +NPError PluginTest::DestroyStream(NPStream *stream, NPError reason) { + // There is no default action. + return NPERR_NO_ERROR; +} + +void PluginTest::StreamAsFile(NPStream* stream, const char* fname) { + // There is no default action. +} + +void PluginTest::URLNotify(const char* url, NPReason reason, void* data) { + // There is no default action +} + +int16 PluginTest::HandleEvent(void* event) { + // There is no default action + return 0; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_test.h b/webkit/glue/plugins/test/plugin_test.h new file mode 100644 index 0000000..0cdc1b7 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_test.h @@ -0,0 +1,153 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__ + +#include <string> + +#include "base/string_util.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +namespace NPAPIClient { + +// A PluginTest represents an instance of the plugin, which in +// our case is a test case. +class PluginTest { + public: + // Constructor. + PluginTest(NPP id, NPNetscapeFuncs *host_functions); + + // Destructor + virtual ~PluginTest() {} + + // + // NPAPI Functions + // + virtual NPError New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved); + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + virtual int32 WriteReady(NPStream *stream); + virtual int32 Write(NPStream *stream, int32 offset, int32 len, + void *buffer); + virtual NPError DestroyStream(NPStream *stream, NPError reason); + virtual void StreamAsFile(NPStream* stream, const char* fname); + virtual void URLNotify(const char* url, NPReason reason, void* data); + virtual int16 HandleEvent(void* event); + // Returns true if the test has not had any errors. + bool Succeeded() { return test_status_.length() == 0; } + + // Sets an error for the test case. Appends the msg to the + // error that will be returned from the test. + void SetError(const std::string &msg); + + // Expect two string values are equal, and if not, logs an + // appropriate error about it. + void ExpectStringLowerCaseEqual(const std::string &val1, const std::string &val2) { + if (!LowerCaseEqualsASCII(val1, val2.c_str())) { + std::string err; + err = "Expected Equal for '"; + err.append(val1); + err.append("' and '"); + err.append(val2); + err.append("'"); + SetError(err); + } + }; + + // Expect two values to not be equal, and if they are + // logs an appropriate error about it. + void ExpectAsciiStringNotEqual(const char *val1, const char *val2) { + if (val1 == val2) { + std::string err; + err = "Expected Not Equal for '"; + err.append(val1); + err.append("' and '"); + err.append(val2); + err.append("'"); + SetError(err); + } + } + // Expect two integer values are equal, and if not, logs an + // appropriate error about it. + void ExpectIntegerEqual(int val1, int val2) { + if (val1 != val2) { + std::string err; + char buf[64]; // what's the right size? + err = "Expected Equal for '"; + sprintf_s(buf, "%d", val1); + err.append(buf); + err.append("' and '"); + sprintf_s(buf, "%d", val2); + err.append(buf); + err.append("'"); + SetError(err); + } + } + + + protected: + // Signals to the Test that invoked us that the test is + // completed. This is done by forcing the plugin to + // set a cookie in the browser window, which the test program + // is waiting for. Note - because this is done by + // using javascript, the browser must have the frame + // setup before the plugin calls this function. So plugin + // tests MUST NOT call this function prior to having + // received the SetWindow() callback from the browser. + void SignalTestCompleted(); + + // Helper function to lookup names in the input array. + // If the name is found, returns the value, otherwise + // returns NULL. + const char *GetArgValue(const char *name, const int16 argc, + const char *argn[], const char *argv[]); + + // Access to the list of functions provided + // by the NPAPI host. + NPNetscapeFuncs *HostFunctions() { return host_functions_; } + + // The NPP Identifier for this plugin instance. + NPP id() { return id_; } + + private: + NPP id_; + NPNetscapeFuncs * host_functions_; + std::string test_name_; + std::string test_id_; + std::string test_status_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_window_size_test.cc b/webkit/glue/plugins/test/plugin_window_size_test.cc new file mode 100644 index 0000000..33aeafa --- /dev/null +++ b/webkit/glue/plugins/test/plugin_window_size_test.cc @@ -0,0 +1,67 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_window_size_test.h" +#include "webkit/glue/plugins/test/plugin_client.h" + +namespace NPAPIClient { + +PluginWindowSizeTest::PluginWindowSizeTest(NPP id, + NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError PluginWindowSizeTest::SetWindow(NPWindow* pNPWindow) { + if (!pNPWindow || + !::IsWindow(reinterpret_cast<HWND>(pNPWindow->window))) { + SetError("Invalid arguments passed in"); + return NPERR_INVALID_PARAM; + } + + RECT window_rect = {0}; + window_rect.left = pNPWindow->x; + window_rect.top = pNPWindow->y; + window_rect.right = pNPWindow->width; + window_rect.bottom = pNPWindow->height; + + if (!::IsRectEmpty(&window_rect)) { + RECT client_rect = {0}; + ::GetClientRect(reinterpret_cast<HWND>(pNPWindow->window), + &client_rect); + if (::IsRectEmpty(&client_rect)) { + SetError("The client rect of the plugin window is empty. Test failed"); + } + + SignalTestCompleted(); + } + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_window_size_test.h b/webkit/glue/plugins/test/plugin_window_size_test.h new file mode 100644 index 0000000..13b50aa --- /dev/null +++ b/webkit/glue/plugins/test/plugin_window_size_test.h @@ -0,0 +1,50 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests whether the plugin window has a non zero rect +// on the second SetWindow call. +class PluginWindowSizeTest : public PluginTest { + public: + // Constructor. + PluginWindowSizeTest(NPP id, NPNetscapeFuncs *host_functions); + // NPAPI SetWindow handler + virtual NPError SetWindow(NPWindow* pNPWindow); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H + diff --git a/webkit/glue/plugins/test/resource.h b/webkit/glue/plugins/test/resource.h new file mode 100644 index 0000000..2f2bf3b --- /dev/null +++ b/webkit/glue/plugins/test/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by npapitest.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/webkit/glue/plugins/webplugin_delegate_impl.cc b/webkit/glue/plugins/webplugin_delegate_impl.cc new file mode 100644 index 0000000..774cad7 --- /dev/null +++ b/webkit/glue/plugins/webplugin_delegate_impl.cc @@ -0,0 +1,1081 @@ +// Copyright 2008, 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 "webkit/glue/plugins/webplugin_delegate_impl.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/gfx/point.h" +#include "base/stats_counters.h" +#include "webkit/default_plugin/plugin_impl.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/plugins/plugin_stream_url.h" + +static StatsCounter windowless_queue(L"Plugin.ThrottleQueue"); + +static const wchar_t kNativeWindowClassName[] = L"NativeWindowClass"; +static const wchar_t kWebPluginDelegateProperty[] = + L"WebPluginDelegateProperty"; +static const wchar_t kPluginNameAtomProperty[] = L"PluginNameAtom"; +static const wchar_t kDummyActivationWindowName[] = L"DummyWindowForActivation"; +static const wchar_t kPluginOrigProc[] = L"OriginalPtr"; +static const wchar_t kPluginFlashThrottle[] = L"FlashThrottle"; + +// The fastest we are willing to process WM_USER+1 events for Flash. +// Flash can easily exceed the limits of our CPU if we don't throttle it. +// The throttle has been chosen by testing various delays and compromising +// on acceptable Flash performance and reasonable CPU consumption. +// +// I'd like to make the throttle delay variable, based on the amount of +// time currently required to paint Flash plugins. There isn't a good +// way to count the time spent in aggregate plugin painting, however, so +// this seems to work well enough. +static const int kFlashWMUSERMessageThrottleDelayMs = 5; + +std::list<MSG> WebPluginDelegateImpl::throttle_queue_; + +WebPluginDelegateImpl* WebPluginDelegateImpl::current_plugin_instance_ = NULL; + +WebPluginDelegateImpl* WebPluginDelegateImpl::Create( + const std::wstring& filename, + const std::string& mime_type, + HWND containing_window) { + scoped_refptr<NPAPI::PluginLib> plugin = + NPAPI::PluginLib::CreatePluginLib(filename); + if (plugin.get() == NULL) + return NULL; + + NPError err = plugin->NP_Initialize(); + if (err != NPERR_NO_ERROR) + return NULL; + + scoped_refptr<NPAPI::PluginInstance> instance = + plugin->CreateInstance(mime_type); + return new WebPluginDelegateImpl(containing_window, instance.get()); +} + +bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) { + // We use a buffer that is one char longer than we need to detect cases where + // kNativeWindowClassName is a prefix of the given window's class name. It + // happens that GetClassNameW will just silently truncate the class name to + // fit into the given buffer. + wchar_t class_name[arraysize(kNativeWindowClassName) + 1]; + if (!GetClassNameW(window, class_name, arraysize(class_name))) + return false; + return wcscmp(class_name, kNativeWindowClassName) == 0; +} + +bool WebPluginDelegateImpl::GetPluginNameFromWindow( + HWND window, std::wstring *plugin_name) { + if (NULL == plugin_name) { + return false; + } + if (!IsPluginDelegateWindow(window)) { + return false; + } + ATOM plugin_name_atom = reinterpret_cast<ATOM>( + GetPropW(window, kPluginNameAtomProperty)); + if (plugin_name_atom != 0) { + WCHAR plugin_name_local[MAX_PATH] = {0}; + GlobalGetAtomNameW(plugin_name_atom, + plugin_name_local, + ARRAYSIZE(plugin_name_local)); + *plugin_name = plugin_name_local; + return true; + } + return false; +} + +bool WebPluginDelegateImpl::IsDummyActivationWindow(HWND window) { + if (!IsWindow(window)) + return false; + + wchar_t window_title[MAX_PATH + 1] = {0}; + if (GetWindowText(window, window_title, arraysize(window_title))) { + return (0 == lstrcmpiW(window_title, kDummyActivationWindowName)); + } + return false; +} + +LRESULT CALLBACK WebPluginDelegateImpl::HandleEventMessageFilterHook( + int code, WPARAM wParam, LPARAM lParam) { + + DCHECK(current_plugin_instance_); + current_plugin_instance_->OnModalLoopEntered(); + return CallNextHookEx(NULL, code, wParam, lParam); +} + +WebPluginDelegateImpl::WebPluginDelegateImpl( + HWND containing_window, + NPAPI::PluginInstance *instance) + : parent_(containing_window), + instance_(instance), + quirks_(0), + plugin_(NULL), + windowless_(false), + windowed_handle_(NULL), + windowed_did_set_window_(false), + windowless_needs_set_window_(true), + plugin_wnd_proc_(NULL), + last_message_(0), + is_calling_wndproc(false), + initial_plugin_resize_done_(false), + dummy_window_for_activation_(NULL), + handle_event_message_filter_hook_(NULL), + handle_event_pump_messages_event_(NULL), + handle_event_depth_(0), + user_gesture_message_posted_(false), +#pragma warning(suppress: 4355) // can use this + user_gesture_msg_factory_(this), + load_manually_(false), + first_geometry_update_(true) { + memset(&window_, 0, sizeof(window_)); + + const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info(); + std::wstring filename = file_util::GetFilenameFromPath(plugin_info.file); + + if (instance_->mime_type() == "application/x-shockwave-flash" || + filename == L"npswf32.dll") { + // Flash only requests windowless plugins if we return a Mozilla user + // agent. + instance_->set_use_mozilla_user_agent(); + instance_->set_throttle_invalidate(true); + quirks_ |= PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE; + } else if (plugin_info.name.find(L"Windows Media Player") != + std::wstring::npos) { + // Windows Media Player needs two NPP_SetWindow calls. + quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE; + } else if (instance_->mime_type() == "audio/x-pn-realaudio-plugin" || + filename == L"nppl3260.dll") { + quirks_ |= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY; + } else if (plugin_info.name.find(L"VLC Multimedia Plugin") != + std::wstring::npos) { + // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window + // handle + quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY; + // VLC 0.8.6d and 0.8.6e crash if multiple instances are created. + quirks_ |= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES; + } +} + +WebPluginDelegateImpl::~WebPluginDelegateImpl() { + if (::IsWindow(dummy_window_for_activation_)) { + ::DestroyWindow(dummy_window_for_activation_); + } + + DestroyInstance(); + + if (!windowless_) + WindowedDestroyWindow(); + + if (handle_event_pump_messages_event_) { + CloseHandle(handle_event_pump_messages_event_); + } +} + +void WebPluginDelegateImpl::PluginDestroyed() { + delete this; +} + +bool WebPluginDelegateImpl::Initialize(const GURL& url, + char** argn, + char** argv, + int argc, + WebPlugin* plugin, + bool load_manually) { + plugin_ = plugin; + + instance_->set_web_plugin(plugin); + NPAPI::PluginInstance* old_instance = + NPAPI::PluginInstance::SetInitializingInstance(instance_); + + if (quirks_ & PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES) { + NPAPI::PluginLib* plugin_lib = instance()->plugin_lib(); + if (plugin_lib->instance_count() > 1) { + return false; + } + } + + bool start_result = instance_->Start(url, argn, argv, argc, load_manually); + + NPAPI::PluginInstance::SetInitializingInstance(old_instance); + + if (!start_result) + return false; + + windowless_ = instance_->windowless(); + if (windowless_) { + // For windowless plugins we should set the containing window handle + // as the instance window handle. This is what Safari does. Not having + // a valid window handle causes subtle bugs with plugins which retreive + // the window handle and validate the same. The window handle can be + // retreived via NPN_GetValue of NPNVnetscapeWindow. + instance_->set_window_handle(parent_); + CreateDummyWindowForActivation(); + handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); + } else { + if (!WindowedCreatePlugin()) + return false; + } + + plugin->SetWindow(windowed_handle_, handle_event_pump_messages_event_); + + load_manually_ = load_manually; + plugin_url_ = url.spec(); + return true; +} + +void WebPluginDelegateImpl::DestroyInstance() { + if (instance_ && (instance_->npp()->ndata != NULL)) { + // Shutdown all streams before destroying so that + // no streams are left "in progress". Need to do + // this before calling set_web_plugin(NULL) because the + // instance uses the helper to do the download. + instance_->CloseStreams(); + + window_.window = NULL; + if (!(quirks_ & PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY)) { + instance_->NPP_SetWindow(&window_); + } + + instance_->NPP_Destroy(); + + instance_->set_web_plugin(NULL); + + instance_ = 0; + } +} + +void WebPluginDelegateImpl::UpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + if (windowless_) { + window_.window = + reinterpret_cast<void *>(::GetDC(instance_->window_handle())); + WindowlessUpdateGeometry(window_rect, clip_rect); + ::ReleaseDC(instance_->window_handle(), + reinterpret_cast<HDC>(window_.window)); + } else { + WindowedUpdateGeometry(window_rect, clip_rect, visible); + } + + // Initiate a download on the plugin url. This should be done for the + // first update geometry sequence. + if (first_geometry_update_) { + first_geometry_update_ = false; + // An empty url corresponds to an EMBED tag with no src attribute. + if (!load_manually_ && !plugin_url_.empty()) { + instance_->SendStream(plugin_url_.c_str(), false, NULL); + } + } +} + +void WebPluginDelegateImpl::Paint(HDC hdc, const gfx::Rect& rect) { + if (windowless_) + WindowlessPaint(hdc, rect); +} + +void WebPluginDelegateImpl::Print(HDC hdc) { + // Disabling the call to NPP_Print as it causes a crash in + // flash in some cases. In any case this does not work as expected + // as the EMF meta file dc passed in needs to be created with the + // the plugin window dc as its sibling dc and the window rect + // in .01 mm units. +#if 0 + NPPrint npprint; + npprint.mode = NP_EMBED; + npprint.print.embedPrint.platformPrint = reinterpret_cast<void*>(hdc); + npprint.print.embedPrint.window = window_; + instance()->NPP_Print(&npprint); +#endif +} + +NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() { + return instance_->GetPluginScriptableObject(); +} + +void WebPluginDelegateImpl::DidFinishLoadWithReason(NPReason reason) { + instance()->DidFinishLoadWithReason(reason); +} + +int WebPluginDelegateImpl::GetProcessId() { + // We are in process, so the plugin pid is this current process pid. + return ::GetCurrentProcessId(); +} + +HWND WebPluginDelegateImpl::GetWindowHandle() { + return instance()->window_handle(); +} + +void WebPluginDelegateImpl::SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + instance()->SendJavaScriptStream(url, result, success, notify_needed, + notify_data); +} + +void WebPluginDelegateImpl::DidReceiveManualResponse( + const std::string& url, const std::string& mime_type, + const std::string& headers, uint32 expected_length, uint32 last_modified) { + instance()->DidReceiveManualResponse(url, mime_type, headers, + expected_length, last_modified); +} + +void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer, + int length) { + instance()->DidReceiveManualData(buffer, length); +} + +void WebPluginDelegateImpl::DidFinishManualLoading() { + instance()->DidFinishManualLoading(); +} + +void WebPluginDelegateImpl::DidManualLoadFail() { + instance()->DidManualLoadFail(); +} + +std::wstring WebPluginDelegateImpl::GetPluginPath() { + return instance()->plugin_lib()->plugin_info().file; +} + +void WebPluginDelegateImpl::InstallMissingPlugin() { + NPEvent evt; + evt.event = PluginInstallerImpl::kInstallMissingPluginMessage; + evt.lParam = 0; + evt.wParam = 0; + instance()->NPP_HandleEvent(&evt); +} + +void WebPluginDelegateImpl::WindowedUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + if (WindowedReposition(window_rect, clip_rect, visible) || + !windowed_did_set_window_) { + // Let the plugin know that it has been moved + WindowedSetWindow(); + } +} + +bool WebPluginDelegateImpl::WindowedCreatePlugin() { + DCHECK(!windowed_handle_); + + RegisterNativeWindowClass(); + + // The window will be sized and shown later. + windowed_handle_ = CreateWindowEx( + WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, + kNativeWindowClassName, + 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + 0, + 0, + 0, + 0, + parent_, + 0, + GetModuleHandle(NULL), + 0); + if (windowed_handle_ == 0) + return false; + + BOOL result = SetProp(windowed_handle_, kWebPluginDelegateProperty, this); + DCHECK(result == TRUE) << "SetProp failed, last error = " << GetLastError(); + // Get the name of the plugin, create an atom and set that in a window + // property. Use an atom so that other processes can access the name of + // the plugin that this window is hosting + if (instance_ != NULL) { + NPAPI::PluginLib* plugin_lib = instance()->plugin_lib(); + if (plugin_lib != NULL) { + std::wstring plugin_name = plugin_lib->plugin_info().name; + if (!plugin_name.empty()) { + ATOM plugin_name_atom = GlobalAddAtomW(plugin_name.c_str()); + DCHECK(0 != plugin_name_atom); + result = SetProp(windowed_handle_, + kPluginNameAtomProperty, + reinterpret_cast<HANDLE>(plugin_name_atom)); + DCHECK(result == TRUE) << "SetProp failed, last error = " << + GetLastError(); + } + } + } + + // Calling SetWindowLongPtrA here makes the window proc ASCII, which is + // required by at least the Shockwave Director plug-in. + SetWindowLongPtrA( + windowed_handle_, GWL_WNDPROC, reinterpret_cast<LONG>(DefWindowProcA)); + + return true; +} + +void WebPluginDelegateImpl::WindowedDestroyWindow() { + if (windowed_handle_ != NULL) { + // Unsubclass the window. + WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc == NativeWndProc) { + SetWindowLongPtr(windowed_handle_, + GWLP_WNDPROC, + reinterpret_cast<LONG>(plugin_wnd_proc_)); + } + + DestroyWindow(windowed_handle_); + windowed_handle_ = 0; + } +} + +// Erase all messages in the queue destined for a particular window. +// When windows are closing, callers should use this function to clear +// the queue. +// static +void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window) { + std::list<MSG>::iterator it; + for (it = throttle_queue_.begin(); it != throttle_queue_.end(); ) { + if (it->hwnd == window) { + it = throttle_queue_.erase(it); + windowless_queue.Decrement(); + } else { + it++; + } + } +} + +// Delayed callback for processing throttled messages. +// Throttled messages are aggregated globally across all plugins. +// static +void WebPluginDelegateImpl::OnThrottleMessage() { + // The current algorithm walks the list and processes the first + // message it finds for each plugin. It is important to service + // all active plugins with each pass through the throttle, otherwise + // we see video jankiness. + std::map<HWND, int> processed; + + std::list<MSG>::iterator it = throttle_queue_.begin(); + while (it != throttle_queue_.end()) { + const MSG& msg = *it; + if (processed.find(msg.hwnd) == processed.end()) { + WNDPROC proc = reinterpret_cast<WNDPROC>(msg.time); + CallWindowProc(proc, msg.hwnd, msg.message, msg.wParam, msg.lParam); + processed[msg.hwnd] = 1; + it = throttle_queue_.erase(it); + windowless_queue.Decrement(); + } else { + it++; + } + } + + if (throttle_queue_.size() > 0) + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage), + kFlashWMUSERMessageThrottleDelayMs); +} + +// Schedule a windows message for delivery later. +// static +void WebPluginDelegateImpl::ThrottleMessage(WNDPROC proc, HWND hwnd, + UINT message, WPARAM wParam, + LPARAM lParam) { + MSG msg; + msg.time = reinterpret_cast<DWORD>(proc); + msg.hwnd = hwnd; + msg.message = message; + msg.wParam = wParam; + msg.lParam = lParam; + throttle_queue_.push_back(msg); + windowless_queue.Increment(); + + if (throttle_queue_.size() == 1) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage), + kFlashWMUSERMessageThrottleDelayMs); + } +} + +// We go out of our way to find the hidden windows created by Flash for +// windowless plugins. We throttle the rate at which they deliver messages +// so that they will not consume outrageous amounts of CPU. +// static +LRESULT CALLBACK WebPluginDelegateImpl::FlashWindowlessWndProc(HWND hwnd, + UINT message, WPARAM wparam, LPARAM lparam) { + WNDPROC old_proc = reinterpret_cast<WNDPROC>(GetProp(hwnd, kPluginOrigProc)); + DCHECK(old_proc); + + switch (message) { + case WM_NCDESTROY: { + WebPluginDelegateImpl::ClearThrottleQueueForWindow(hwnd); + break; + } + // Flash may flood the message queue with WM_USER+1 message causing 100% CPU + // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We + // prevent this by throttling the messages. + case WM_USER + 1: { + WebPluginDelegateImpl::ThrottleMessage(old_proc, hwnd, message, wparam, + lparam); + return TRUE; + } + default: { + break; + } + } + return CallWindowProc(old_proc, hwnd, message, wparam, lparam); +} + +// Callback for enumerating the Flash windows. +BOOL CALLBACK EnumFlashWindows(HWND window, LPARAM arg) { + WNDPROC wnd_proc = reinterpret_cast<WNDPROC>(arg); + TCHAR class_name[1024]; + if (!RealGetWindowClass(window, class_name, + sizeof(class_name)/sizeof(TCHAR))) { + LOG(ERROR) << "RealGetWindowClass failure: " << GetLastError(); + return FALSE; + } + + if (wcscmp(class_name, L"SWFlash_PlaceholderX")) + return TRUE; + + WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( + GetWindowLongPtr(window, GWLP_WNDPROC)); + if (current_wnd_proc != wnd_proc) { + WNDPROC old_flash_proc = reinterpret_cast<WNDPROC>(SetWindowLongPtr( + window, GWLP_WNDPROC, + reinterpret_cast<LONG>(wnd_proc))); + DCHECK(old_flash_proc); + BOOL result = SetProp(window, kPluginOrigProc, old_flash_proc); + if (!result) { + LOG(ERROR) << "SetProp failed, last error = " << GetLastError(); + return FALSE; + } + } + + return TRUE; +} + +bool WebPluginDelegateImpl::CreateDummyWindowForActivation() { + DCHECK(!dummy_window_for_activation_); + dummy_window_for_activation_ = CreateWindowEx( + 0, + L"Static", + kDummyActivationWindowName, + WS_CHILD, + 0, + 0, + 0, + 0, + parent_, + 0, + GetModuleHandle(NULL), + 0); + + if (dummy_window_for_activation_ == 0) + return false; + + // Flash creates background windows which use excessive CPU in our + // environment; we wrap these windows and throttle them so that they don't + // get out of hand. + if (!EnumThreadWindows(::GetCurrentThreadId(), EnumFlashWindows, + reinterpret_cast<LPARAM>( + &WebPluginDelegateImpl::FlashWindowlessWndProc))) { + // Log that this happened. Flash will still work; it just means the + // throttle isn't installed (and Flash will use more CPU). + NOTREACHED(); + LOG(ERROR) << "Failed to wrap all windowless Flash windows"; + } + return true; +} + +void WebPluginDelegateImpl::MoveWindow(HWND window, + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + HRGN hrgn = ::CreateRectRgn(clip_rect.x(), + clip_rect.y(), + clip_rect.right(), + clip_rect.bottom()); + + // Note: System will own the hrgn after we call SetWindowRgn, + // so we don't need to call DeleteObject(hrgn) + ::SetWindowRgn(window, hrgn, FALSE); + + // Move the window now, but do not paint it. We expect to be painted later, + // when our parent window paints. + unsigned long flags = SWP_NOREDRAW; + if (visible) + flags |= SWP_SHOWWINDOW; + else + flags |= SWP_HIDEWINDOW; + + ::SetWindowPos(window, + NULL, + window_rect.x(), + window_rect.y(), + window_rect.width(), + window_rect.height(), + flags); +} + +bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + if (!windowed_handle_) { + NOTREACHED(); + return false; + } + + if (window_rect_ == window_rect && clip_rect_ == clip_rect && + initial_plugin_resize_done_) + return false; + + window_rect_ = window_rect; + clip_rect_ = clip_rect; + + if (!initial_plugin_resize_done_) { + // We need to ensure that the plugin process continues to reposition + // the plugin window until we receive an indication that it is now visible. + // Subsequent repositions will be done by the browser. + if (visible) + initial_plugin_resize_done_ = true; + // We created the window with 0 width and height since we didn't know it + // at the time. Now that we know the geometry, we we can update its size + // since the browser only calls SetWindowPos when scrolling occurs. + MoveWindow(windowed_handle_, window_rect, clip_rect, visible); + // Ensure that the entire window gets repainted. + ::InvalidateRect(windowed_handle_, NULL, FALSE); + } + + return true; +} + +void WebPluginDelegateImpl::WindowedSetWindow() { + if (!instance_) + return; + + if (!windowed_handle_) { + NOTREACHED(); + return; + } + + instance()->set_window_handle(windowed_handle_); + + DCHECK(!instance()->windowless()); + + window_.clipRect.top = clip_rect_.y(); + window_.clipRect.left = clip_rect_.x(); + window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); + window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); + window_.height = window_rect_.height(); + window_.width = window_rect_.width(); + window_.x = window_rect_.x(); + window_.y = window_rect_.y(); + + window_.window = windowed_handle_; + window_.type = NPWindowTypeWindow; + + // Reset this flag before entering the instance in case of side-effects. + windowed_did_set_window_ = true; + + NPError err = instance()->NPP_SetWindow(&window_); + if (quirks_ & PLUGIN_QUIRK_SETWINDOW_TWICE) + instance()->NPP_SetWindow(&window_); + + WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc != NativeWndProc) { + plugin_wnd_proc_ = reinterpret_cast<WNDPROC>(SetWindowLongPtr( + windowed_handle_, GWLP_WNDPROC, reinterpret_cast<LONG>(NativeWndProc))); + } +} + +ATOM WebPluginDelegateImpl::RegisterNativeWindowClass() { + static bool have_registered_window_class = false; + if (have_registered_window_class == true) + return true; + + have_registered_window_class = true; + + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_DBLCLKS; + wcex.lpfnWndProc = DefWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetModuleHandle(NULL); + wcex.hIcon = 0; + wcex.hCursor = 0; + // Some plugins like windows media player 11 create child windows parented + // by our plugin window, where the media content is rendered. These plugins + // dont implement WM_ERASEBKGND, which causes painting issues, when the + // window where the media is rendered is moved around. DefWindowProc does + // implement WM_ERASEBKGND correctly if we have a valid background brush. + wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); + wcex.lpszMenuName = 0; + wcex.lpszClassName = kNativeWindowClassName; + wcex.hIconSm = 0; + + return RegisterClassEx(&wcex); +} + +LRESULT CALLBACK WebPluginDelegateImpl::NativeWndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + WebPluginDelegateImpl* delegate = reinterpret_cast<WebPluginDelegateImpl*>( + GetProp(hwnd, kWebPluginDelegateProperty)); + if (!delegate) { + NOTREACHED(); + return 0; + } + + if (message == delegate->last_message_ && + delegate->quirks() & PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY && + delegate->is_calling_wndproc) { + // Real may go into a state where it recursively dispatches the same event + // when subclassed. See https://bugzilla.mozilla.org/show_bug.cgi?id=192914 + // We only do the recursive check for Real because it's possible and valid + // for a plugin to synchronously dispatch a message to itself such that it + // looks like it's in recursion. + return TRUE; + } + + switch (message) { + case WM_NCDESTROY: { + RemoveProp(hwnd, kWebPluginDelegateProperty); + ATOM plugin_name_atom = reinterpret_cast <ATOM>( + RemoveProp(hwnd, kPluginNameAtomProperty)); + if (plugin_name_atom != 0) + GlobalDeleteAtom(plugin_name_atom); + ClearThrottleQueueForWindow(hwnd); + break; + } + // Flash may flood the message queue with WM_USER+1 message causing 100% CPU + // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We + // prevent this by throttling the messages. + case WM_USER + 1: { + if (delegate->quirks() & PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE) { + WebPluginDelegateImpl::ThrottleMessage(delegate->plugin_wnd_proc_, hwnd, + message, wparam, lparam); + return FALSE; + } + break; + } + default: { + break; + } + } + + delegate->last_message_ = message; + delegate->is_calling_wndproc = true; + + if (!delegate->user_gesture_message_posted_ && + IsUserGestureMessage(message)) { + delegate->user_gesture_message_posted_ = true; + + delegate->instance()->PushPopupsEnabledState(true); + + MessageLoop::current()->PostTask(FROM_HERE, + delegate->user_gesture_msg_factory_.NewRunnableMethod( + &WebPluginDelegateImpl::OnUserGestureEnd)); + } + + LRESULT result = CallWindowProc(delegate->plugin_wnd_proc_, hwnd, message, + wparam, lparam); + delegate->is_calling_wndproc = false; + return result; +} + +void WebPluginDelegateImpl::WindowlessUpdateGeometry( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect) { + // Only resend to the instance if the geometry has changed. + if (window_rect == window_rect_ && clip_rect == clip_rect_) + return; + + // Set this flag before entering the instance in case of side-effects. + windowless_needs_set_window_ = true; + + // We will inform the instance of this change when we call NPP_SetWindow. + clip_rect_ = clip_rect; + + if (window_rect_ != window_rect) { + window_rect_ = window_rect; + + WindowlessSetWindow(true); + + WINDOWPOS win_pos = {0}; + win_pos.x = window_rect_.x(); + win_pos.y = window_rect_.y(); + win_pos.cx = window_rect_.width(); + win_pos.cy = window_rect_.height(); + + NPEvent pos_changed_event; + pos_changed_event.event = WM_WINDOWPOSCHANGED; + pos_changed_event.wParam = 0; + pos_changed_event.lParam = PtrToUlong(&win_pos); + + instance()->NPP_HandleEvent(&pos_changed_event); + } +} + +void WebPluginDelegateImpl::WindowlessPaint(HDC hdc, + const gfx::Rect& damage_rect) { + DCHECK(hdc); + + RECT damage_rect_win; + damage_rect_win.left = damage_rect.x(); // + window_rect_.x(); + damage_rect_win.top = damage_rect.y(); // + window_rect_.y(); + damage_rect_win.right = damage_rect_win.left + damage_rect.width(); + damage_rect_win.bottom = damage_rect_win.top + damage_rect.height(); + + window_.window = (void*)hdc; + + // TODO(darin): we should avoid calling NPP_SetWindow here since it may + // cause page layout to be invalidated. + + // We really don't need to continually call SetWindow. + // m_needsSetWindow flags when the geometry has changed. + if (windowless_needs_set_window_) + WindowlessSetWindow(false); + + NPEvent paint_event; + paint_event.event = WM_PAINT; + // NOTE: NPAPI is not 64bit safe. It puts pointers into 32bit values. + paint_event.wParam = PtrToUlong(hdc); + paint_event.lParam = PtrToUlong(&damage_rect_win); + static StatsRate plugin_paint(L"Plugin.Paint"); + StatsScope<StatsRate> scope(plugin_paint); + instance()->NPP_HandleEvent(&paint_event); +} + +void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { + if (!instance()) + return; + + if (window_rect_.IsEmpty()) // wait for geometry to be set. + return; + + DCHECK(instance()->windowless()); + + window_.clipRect.top = clip_rect_.y(); + window_.clipRect.left = clip_rect_.x(); + window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); + window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); + window_.height = window_rect_.height(); + window_.width = window_rect_.width(); + window_.x = window_rect_.x(); + window_.y = window_rect_.y(); + window_.type = NPWindowTypeDrawable; + + if (!force_set_window) + // Reset this flag before entering the instance in case of side-effects. + windowless_needs_set_window_ = false; + + NPError err = instance()->NPP_SetWindow(&window_); + DCHECK(err == NPERR_NO_ERROR); +} + +void WebPluginDelegateImpl::SetFocus() { + DCHECK(instance()->windowless()); + + NPEvent focus_event; + focus_event.event = WM_SETFOCUS; + focus_event.wParam = 0; + focus_event.lParam = 0; + + instance()->NPP_HandleEvent(&focus_event); +} + +bool WebPluginDelegateImpl::HandleEvent(NPEvent* event, + WebCursor* cursor) { + DCHECK(windowless_) << "events should only be received in windowless mode"; + DCHECK(cursor != NULL); + + // To ensure that the plugin receives keyboard events we set focus to the + // dummy window. + // TODO(iyengar) We need a framework in the renderer to identify which + // windowless plugin is under the mouse and to handle this. This would + // also require some changes in RenderWidgetHost to detect this in the + // WM_MOUSEACTIVATE handler and inform the renderer accordingly. + HWND prev_focus_window = NULL; + if (event->event == WM_RBUTTONDOWN) { + prev_focus_window = ::SetFocus(dummy_window_for_activation_); + } + + if (ShouldTrackEventForModalLoops(event)) { + // A windowless plugin can enter a modal loop in a NPP_HandleEvent call. + // For e.g. Flash puts up a context menu when we right click on the + // windowless plugin area. We detect this by setting up a message filter + // hook pror to calling NPP_HandleEvent on the plugin and unhook on + // return from NPP_HandleEvent. If the plugin does enter a modal loop + // in that context we unhook on receiving the first notification in + // the message filter hook. + handle_event_message_filter_hook_ = + SetWindowsHookEx(WH_MSGFILTER, HandleEventMessageFilterHook, NULL, + GetCurrentThreadId()); + } + + bool old_task_reentrancy_state = + MessageLoop::current()->NestableTasksAllowed(); + + current_plugin_instance_ = this; + + handle_event_depth_++; + + bool pop_user_gesture = false; + + if (IsUserGestureMessage(event->event)) { + pop_user_gesture = true; + instance()->PushPopupsEnabledState(true); + } + + bool ret = instance()->NPP_HandleEvent(event) != 0; + + if (pop_user_gesture) { + instance()->PopPopupsEnabledState(); + } + + handle_event_depth_--; + + current_plugin_instance_ = NULL; + + MessageLoop::current()->SetNestableTasksAllowed(old_task_reentrancy_state); + + if (handle_event_message_filter_hook_) { + UnhookWindowsHookEx(handle_event_message_filter_hook_); + handle_event_message_filter_hook_ = NULL; + } + + // We could have multiple NPP_HandleEvent calls nested together in case + // the plugin enters a modal loop. Reset the pump messages event when + // the outermost NPP_HandleEvent call unwinds. + if (handle_event_depth_ == 0) { + ResetEvent(handle_event_pump_messages_event_); + } + + if (::IsWindow(prev_focus_window)) { + ::SetFocus(prev_focus_window); + } + + if (WM_MOUSEMOVE == event->event) { + HCURSOR actual_cursor = ::GetCursor(); + *cursor = GetCursorType(actual_cursor); + } + + return ret; +} + +WebCursor::Type WebPluginDelegateImpl::GetCursorType( + HCURSOR cursor) const { + static HCURSOR standard_cursors[] = { + LoadCursor(NULL, IDC_ARROW), + LoadCursor(NULL, IDC_IBEAM), + LoadCursor(NULL, IDC_WAIT), + LoadCursor(NULL, IDC_CROSS), + LoadCursor(NULL, IDC_UPARROW), + LoadCursor(NULL, IDC_SIZE), + LoadCursor(NULL, IDC_ICON), + LoadCursor(NULL, IDC_SIZENWSE), + LoadCursor(NULL, IDC_SIZENESW), + LoadCursor(NULL, IDC_SIZEWE), + LoadCursor(NULL, IDC_SIZENS), + LoadCursor(NULL, IDC_SIZEALL), + LoadCursor(NULL, IDC_NO), + LoadCursor(NULL, IDC_HAND), + LoadCursor(NULL, IDC_APPSTARTING), + LoadCursor(NULL, IDC_HELP), + }; + + for (int cursor_index = 0; cursor_index < arraysize(standard_cursors); + cursor_index++) { + if (cursor == standard_cursors[cursor_index]) + return static_cast<WebCursor::Type>(cursor_index); + } + + return WebCursor::ARROW; +} + +WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient( + int resource_id, const std::string &url, bool notify_needed, + void *notify_data) { + if (notify_needed) { + instance()->SetURLLoadData(GURL(url.c_str()), notify_data); + } + std::string mime_type; + NPAPI::PluginStreamUrl *stream = instance()->CreateStream(resource_id, + url, + mime_type, + notify_needed, + notify_data); + return stream; +} + +void WebPluginDelegateImpl::URLRequestRouted(const std::string&url, + bool notify_needed, + void* notify_data) { + if (notify_needed) { + instance()->SetURLLoadData(GURL(url.c_str()), notify_data); + } +} + +void WebPluginDelegateImpl::OnModalLoopEntered() { + DCHECK(handle_event_pump_messages_event_ != NULL); + SetEvent(handle_event_pump_messages_event_); + + MessageLoop::current()->SetNestableTasksAllowed(true); + + UnhookWindowsHookEx(handle_event_message_filter_hook_); + handle_event_message_filter_hook_ = NULL; +} + +bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent* event) { + if (event->event == WM_RBUTTONDOWN) + return true; + return false; +} + +bool WebPluginDelegateImpl::IsUserGestureMessage(unsigned int message) { + switch (message) { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_KEYUP: + return true; + + default: + break; + } + + return false; +} + +void WebPluginDelegateImpl::OnUserGestureEnd() { + user_gesture_message_posted_ = false; + instance()->PopPopupsEnabledState(); +} diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h new file mode 100644 index 0000000..3b774e8 --- /dev/null +++ b/webkit/glue/plugins/webplugin_delegate_impl.h @@ -0,0 +1,287 @@ +// Copyright 2008, 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. + +#ifndef WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ +#define WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ + +#include <string> +#include <list> + +#include "base/ref_counted.h" +#include "base/task.h" +#include "webkit/glue/webplugin_delegate.h" +#include "third_party/npapi/bindings/npapi.h" +#include "webkit/glue/webcursor.h" + +namespace NPAPI { + class PluginInstance; +}; + +// An implementation of WebPluginDelegate that proxies all calls to +// the plugin process. +class WebPluginDelegateImpl : public WebPluginDelegate { + public: + static WebPluginDelegateImpl* Create(const std::wstring& filename, + const std::string& mime_type, + HWND containing_window); + static bool IsPluginDelegateWindow(HWND window); + static bool GetPluginNameFromWindow(HWND window, std::wstring *plugin_name); + + // Returns true if the window handle passed in is that of the dummy + // activation window for windowless plugins. + static bool IsDummyActivationWindow(HWND window); + + // WebPluginDelegate implementation + virtual void PluginDestroyed(); + virtual bool Initialize(const GURL& url, + char** argn, + char** argv, + int argc, + WebPlugin* plugin, + bool load_manually); + virtual void UpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + virtual void Paint(HDC hdc, const gfx::Rect& rect); + virtual void Print(HDC hdc); + virtual void SetFocus(); // only called when windowless +// only called when windowless + virtual bool HandleEvent(NPEvent* event, + WebCursor* cursor); + virtual NPObject* GetPluginScriptableObject(); + virtual void DidFinishLoadWithReason(NPReason reason); + virtual int GetProcessId(); + virtual HWND GetWindowHandle(); + + virtual void FlushGeometryUpdates() { + } + virtual void SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, bool notify_needed, + int notify_data); + virtual void DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified); + virtual void DidReceiveManualData(const char* buffer, int length); + virtual void DidFinishManualLoading(); + virtual void DidManualLoadFail(); + virtual std::wstring GetPluginPath(); + virtual void InstallMissingPlugin(); + virtual WebPluginResourceClient* CreateResourceClient(int resource_id, + const std::string &url, + bool notify_needed, + void *notify_data); + + virtual void URLRequestRouted(const std::string&url, bool notify_needed, + void* notify_data); + bool windowless() const { + return windowless_; + } + + enum PluginQuirks { + PLUGIN_QUIRK_SETWINDOW_TWICE = 1, + PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE = 2, + PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY = 4, + PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY = 8, + PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES = 16, + }; + + int quirks() { return quirks_; } + + static void MoveWindow(HWND window, + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible); + + private: + WebPluginDelegateImpl(HWND containing_window, + NPAPI::PluginInstance *instance); + ~WebPluginDelegateImpl(); + + //-------------------------- + // used for windowed plugins + void WindowedUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + // Create the native window. + // Returns true if the window is created (or already exists). + // Returns false if unable to create the window. + bool WindowedCreatePlugin(); + + // Destroy the native window. + void WindowedDestroyWindow(); + + // Reposition the native window to be in sync with the given geometry. + // Returns true if the native window has moved or been clipped differently. + bool WindowedReposition(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + + // Tells the plugin about the current state of the window. + // See NPAPI NPP_SetWindow for more information. + void WindowedSetWindow(); + + // Registers the window class for our window + ATOM RegisterNativeWindowClass(); + + // Our WndProc functions. + static LRESULT CALLBACK NativeWndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + static LRESULT CALLBACK FlashWindowlessWndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + // Used for throttling Flash messages. + static void ClearThrottleQueueForWindow(HWND window); + static void OnThrottleMessage(); + static void ThrottleMessage(WNDPROC proc, HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); + + //---------------------------- + // used for windowless plugins + void WindowlessUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect); + void WindowlessPaint(HDC hdc, const gfx::Rect& rect); + + // Tells the plugin about the current state of the window. + // See NPAPI NPP_SetWindow for more information. + void WindowlessSetWindow(bool force_set_window); + + + //----------------------------------------- + // used for windowed and windowless plugins + + NPAPI::PluginInstance* instance() { return instance_.get(); } + + // Closes down and destroys our plugin instance. + void DestroyInstance(); + + // Returns the cursor type. + // TODO(iyengar) Add support for custom cursors. + WebCursor::Type GetCursorType(HCURSOR cursor) const; + + // used for windowed plugins + HWND windowed_handle_; + bool windowed_did_set_window_; + gfx::Rect windowed_last_pos_; + + // this is an optimization to avoid calling SetWindow to the plugin + // when it is not necessary. Initially, we need to call SetWindow, + // and after that we only need to call it when the geometry changes. + // use this flag to indicate whether we really need it or not. + bool windowless_needs_set_window_; + + // used by windowed and windowless plugins + bool windowless_; + + WebPlugin* plugin_; + scoped_refptr<NPAPI::PluginInstance> instance_; + + // Original wndproc before we subclassed. + WNDPROC plugin_wnd_proc_; + + // Used to throttle WM_USER+1 messages in Flash. + uint32 last_message_; + bool is_calling_wndproc; + + HWND parent_; + NPWindow window_; + gfx::Rect window_rect_; + gfx::Rect clip_rect_; + int quirks_; + + // We only move/size the plugin window once after its creation. The + // rest of the moves are controlled by the browser. This flag controls + // this behaviour. + bool initial_plugin_resize_done_; + + // Windowless plugins don't have keyboard focus causing issues with the + // plugin not receiving keyboard events if the plugin enters a modal + // loop like TrackPopupMenuEx or MessageBox, etc. + // This is a basic issue with windows activation and focus arising due to + // the fact that these windows are created by different threads. Activation + // and focus are thread specific states, and if the browser has focus, + // the plugin may not have focus. + // To fix a majority of these activation issues we create a dummy visible + // child window to which we set focus whenever the windowless plugin + // receives a WM_LBUTTONDOWN/WM_RBUTTONDOWN message via NPP_HandleEvent. + HWND dummy_window_for_activation_; + bool CreateDummyWindowForActivation(); + + static std::list<MSG> throttle_queue_; + + // Returns true if the event passed in needs to be tracked for a potential + // modal loop. + static bool ShouldTrackEventForModalLoops(NPEvent* event); + + // The message filter hook procedure, which tracks modal loops entered by + // a plugin in the course of a NPP_HandleEvent call. + static LRESULT CALLBACK HandleEventMessageFilterHook(int code, WPARAM wParam, + LPARAM lParam); + + // Called by the message filter hook when the plugin enters a modal loop. + void OnModalLoopEntered(); + + // Returns true if the message passed in corresponds to a user gesture. + static bool IsUserGestureMessage(unsigned int message); + + // Indicates the end of a user gesture period. + void OnUserGestureEnd(); + + // Handle to the message filter hook + HHOOK handle_event_message_filter_hook_; + + // The current instance of the plugin which entered the modal loop. + static WebPluginDelegateImpl* current_plugin_instance_; + + // Event which is set when the plugin enters a modal loop in the course + // of a NPP_HandleEvent call. + HANDLE handle_event_pump_messages_event_; + + // Holds the depth of the HandleEvent callstack. + int handle_event_depth_; + + // This flag indicates whether we started tracking a user gesture message. + bool user_gesture_message_posted_; + + // Runnable Method Factory used to invoke the OnUserGestureEnd method + // asynchronously. + ScopedRunnableMethodFactory<WebPluginDelegateImpl> user_gesture_msg_factory_; + + // The url with which the plugin was instantiated. + std::string plugin_url_; + + // Indicates if the download would be initiated by the plugin or us. + bool load_manually_; + + // Indicates whether a geometry update sequence is the first. + bool first_geometry_update_; + + DISALLOW_EVIL_CONSTRUCTORS(WebPluginDelegateImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ |