diff --git a/CHROMIUM_BUILD_COMPATIBILITY.txt b/CHROMIUM_BUILD_COMPATIBILITY.txt index 3efdeff68..dc24217ba 100644 --- a/CHROMIUM_BUILD_COMPATIBILITY.txt +++ b/CHROMIUM_BUILD_COMPATIBILITY.txt @@ -10,3 +10,4 @@ Date | CEF Revision | Chromium Revision 2008-12-02 | /trunk@2 | /trunk@6213 2008-12-05 | /trunk@3 | /trunk@6430 2008-12-13 | /trunk@4 | /trunk@6968 +2008-12-13 | /trunk@5 | /trunk@6975 diff --git a/include/cef_nplugin.h b/include/cef_nplugin.h new file mode 100644 index 000000000..f54aacc0a --- /dev/null +++ b/include/cef_nplugin.h @@ -0,0 +1,76 @@ +// Copyright (c) 2008 Marshall A. Greenblatt. 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 name Chromium Embedded +// Framework 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 _CEF_PLUGIN_H +#define _CEF_PLUGIN_H + +#include +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +// Netscape plugins are normally built at separate DLLs that are loaded by the +// browser when needed. This interface supports the creation of plugins that +// are an embedded component of the application. Embedded plugins built using +// this interface use the same Netscape Plugin API as DLL-based plugins. +// See https://developer.mozilla.org/En/Gecko_Plugin_API_Reference for complete +// documentation on how to use the Netscape Plugin API. + +// This structure fully describes a plugin. +struct CefPluginVersionInfo { + // Unique name used to identify a plugin. The unique name is used in place + // of the file path that would be available with normal plugin DLLs. + std::wstring unique_name; + std::wstring product_name; + std::wstring description; + std::wstring version; + // List of supported mime type values, delimited with a pipe (|) character. + std::wstring mime_types; + // List of supported file extensions, delimited with a pipe (|) character. + std::wstring file_extensions; + // List of descriptions for the file extensions, delimited with a pipe (|) + // character. + std::wstring file_open_names; +}; + +// This structure provides version information and entry point functions. +struct CefPluginInfo { + CefPluginVersionInfo version_info; + NP_GetEntryPointsFunc np_getentrypoints; + NP_InitializeFunc np_initialize; + NP_ShutdownFunc np_shutdown; +}; + +// Register the plugin with the system. +bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info); + +// Unregister the plugin with the system. +bool CefUnregisterPlugin(const struct CefPluginInfo& plugin_info); + +#endif // _CEF_PLUGIN_H diff --git a/libcef/browser_webkit_glue.cc b/libcef/browser_webkit_glue.cc index cc022a11d..394c46972 100644 --- a/libcef/browser_webkit_glue.cc +++ b/libcef/browser_webkit_glue.cc @@ -150,4 +150,12 @@ std::string GetDocumentString(WebFrame* frame) { return StringToStdString(WebCore::createMarkup(core_frame->document())); } +uint64 VisitedLinkHash(const char* canonical_url, size_t length) { + return 0; +} + +bool IsLinkVisited(uint64 link_hash) { + return false; +} + } // namespace webkit_glue diff --git a/libcef/browser_webkit_glue_win.cc b/libcef/browser_webkit_glue_win.cc index 95aa2f74d..958ceb879 100644 --- a/libcef/browser_webkit_glue_win.cc +++ b/libcef/browser_webkit_glue_win.cc @@ -5,6 +5,7 @@ #include "precompiled_libcef.h" #include "browser_webkit_glue.h" +#include "plugins/browser_plugin_list.h" #include #include @@ -45,7 +46,9 @@ HCURSOR LoadCursor(int cursor_id) { } bool GetPlugins(bool refresh, std::vector* plugins) { - return NPAPI::PluginList::Singleton()->GetPlugins(refresh, plugins); + NPAPI::PluginList::Singleton()->GetPlugins(refresh, plugins); + NPAPI::BrowserPluginList::Singleton()->GetPlugins(refresh, plugins); + return true; } bool EnsureFontLoaded(HFONT font) { diff --git a/libcef/browser_webview_delegate_win.cc b/libcef/browser_webview_delegate_win.cc index b1c09d245..1610bbd07 100644 --- a/libcef/browser_webview_delegate_win.cc +++ b/libcef/browser_webview_delegate_win.cc @@ -14,6 +14,8 @@ #include "browser_navigation_controller.h" #include "browser_impl.h" #include "context.h" +#include "plugins/browser_webplugin_delegate_impl.h" +#include "plugins/browser_plugin_list.h" #include #include @@ -55,16 +57,33 @@ WebPluginDelegate* BrowserWebViewDelegate::CreatePluginDelegate( return NULL; bool allow_wildcard = true; + + // first, look for plugins using the normal plugin list WebPluginInfo info; - if (!NPAPI::PluginList::Singleton()->GetPluginInfo(url, mime_type, clsid, - allow_wildcard, &info, - actual_mime_type)) - return NULL; - - if (actual_mime_type && !actual_mime_type->empty()) - return WebPluginDelegateImpl::Create(info.file, *actual_mime_type, hwnd); - else - return WebPluginDelegateImpl::Create(info.file, mime_type, hwnd); + if (NPAPI::PluginList::Singleton()->GetPluginInfo(url, mime_type, clsid, + allow_wildcard, &info, + actual_mime_type)) { + if (actual_mime_type && !actual_mime_type->empty()) + return WebPluginDelegateImpl::Create(info.file, *actual_mime_type, hwnd); + else + return WebPluginDelegateImpl::Create(info.file, mime_type, hwnd); + } + + // second, look for plugins using the embedded plugin list + CefPluginInfo plugin_info; + if (NPAPI::BrowserPluginList::Singleton()->GetPluginInfo(url, mime_type, + clsid, + allow_wildcard, + &plugin_info, + actual_mime_type)) { + if (actual_mime_type && !actual_mime_type->empty()) + return BrowserWebPluginDelegateImpl::Create(plugin_info, + *actual_mime_type, hwnd); + else + return BrowserWebPluginDelegateImpl::Create(plugin_info, mime_type, hwnd); + } + + return NULL; } void BrowserWebViewDelegate::Show(WebWidget* webwidget, WindowOpenDisposition) { diff --git a/libcef/context.cc b/libcef/context.cc index 98ae29241..d2fdef28b 100644 --- a/libcef/context.cc +++ b/libcef/context.cc @@ -8,6 +8,7 @@ #include "browser_impl.h" #include "browser_resource_loader_bridge.h" #include "browser_request_context.h" +#include "plugins/browser_plugin_list.h" #include "base/at_exit.h" #include "base/icu_util.h" @@ -52,6 +53,48 @@ void CefShutdown() _Context = NULL; } +bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info) +{ + if(!_Context.get()) + return false; + + CefPluginInfo* pPluginInfo = new CefPluginInfo; + *pPluginInfo = plugin_info; + + PostTask(FROM_HERE, NewRunnableMethod(_Context.get(), + &CefContext::UIT_RegisterPlugin, pPluginInfo)); + + return true; +} + +void CefContext::UIT_RegisterPlugin(struct CefPluginInfo* plugin_info) +{ + REQUIRE_UIT(); + NPAPI::BrowserPluginList::Singleton()->AddPlugin(*plugin_info); + delete plugin_info; +} + +// Unregister the plugin with the system. +bool CefUnregisterPlugin(const struct CefPluginInfo& plugin_info) +{ + if(!_Context.get()) + return false; + + CefPluginInfo* pPluginInfo = new CefPluginInfo; + *pPluginInfo = plugin_info; + + PostTask(FROM_HERE, NewRunnableMethod(_Context.get(), + &CefContext::UIT_UnregisterPlugin, pPluginInfo)); + + return true; +} + +void CefContext::UIT_UnregisterPlugin(struct CefPluginInfo* plugin_info) +{ + REQUIRE_UIT(); + NPAPI::BrowserPluginList::Singleton()->RemovePlugin(*plugin_info); + delete plugin_info; +} StringPiece GetRawDataResource(HMODULE module, int resource_id) { void* data_ptr; diff --git a/libcef/context.h b/libcef/context.h index 510a0e02e..f4373d9e5 100644 --- a/libcef/context.h +++ b/libcef/context.h @@ -38,6 +38,13 @@ public: // Returns true if the calling thread is the same as the UI thread bool RunningOnUIThread() { return (GetCurrentThreadId() == idthreadui_); } + //////////////////////////////////////////////////////////// + // ALL UIT_* METHODS MUST ONLY BE CALLED ON THE UI THREAD // + //////////////////////////////////////////////////////////// + + void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info); + void UIT_UnregisterPlugin(struct CefPluginInfo* plugin_info); + private: void SetMessageLoopForUI(MessageLoopForUI* loop); void NotifyEvent(); diff --git a/libcef/libcef.vcproj b/libcef/libcef.vcproj index c05bc15f1..49c63fd95 100644 --- a/libcef/libcef.vcproj +++ b/libcef/libcef.vcproj @@ -184,6 +184,10 @@ RelativePath="..\include\cef.h" > + + @@ -193,6 +197,78 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libcef/plugins/browser_mozilla_extensions.cc b/libcef/plugins/browser_mozilla_extensions.cc new file mode 100644 index 000000000..45defb4b0 --- /dev/null +++ b/libcef/plugins/browser_mozilla_extensions.cc @@ -0,0 +1,373 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" +#include "browser_mozilla_extensions.h" +#include "browser_plugin_instance.h" + +#include + +#include "base/logging.h" +#include "base/string_util.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_errors.h" +#include "net/proxy/proxy_service.h" +#include "net/proxy/proxy_resolver_winhttp.h" +#include "third_party/npapi/bindings/npapi.h" +#include "webkit/glue/webkit_glue.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(this); \ + return NS_OK; \ + } + +namespace NPAPI +{ + +void BrowserMozillaExtensionApi::DetachFromInstance() { + plugin_instance_ = NULL; +} + +bool BrowserMozillaExtensionApi::FindProxyForUrl(const char* url, + std::string* proxy) { + bool result = false; + + if ((!url) || (!proxy)) { + NOTREACHED(); + return result; + } + + scoped_ptr proxy_service(net::ProxyService::Create(NULL)); + if (!proxy_service.get()) { + NOTREACHED(); + return result; + } + + net::ProxyInfo proxy_info; + if (proxy_service->ResolveProxy(GURL(std::string(url)), + &proxy_info, + NULL, + NULL) == net::OK) { + if (!proxy_info.is_direct()) { + std::string 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::string::npos == winhttp_proxy.find('=')) { + // Proxy is in the form: "111.111.111.111:11" + winhttp_proxy.insert(0, "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(), '='), ' '); + } + + *proxy = winhttp_proxy; + result = true; + } + } + + return result; +} + +// nsISupports implementation +NS_IMETHODIMP BrowserMozillaExtensionApi::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) BrowserMozillaExtensionApi::AddRef(void) { + return InterlockedIncrement(reinterpret_cast(&ref_count_)); +} + +NS_IMETHODIMP_(nsrefcnt) BrowserMozillaExtensionApi::Release(void) { + DCHECK(static_cast(ref_count_) > 0); + if (InterlockedDecrement(reinterpret_cast(&ref_count_)) == 0) { + delete this; + return 0; + } + + return ref_count_; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::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 BrowserMozillaExtensionApi::GetServiceByContractID( + const char* contract_id, + REFNSIID iid, + void** result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::IsServiceInstantiated(REFNSIID class_guid, + REFNSIID iid, + PRBool* result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::IsServiceInstantiatedByContractID( + const char* contract_id, + REFNSIID iid, + PRBool* result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + + +NS_IMETHODIMP BrowserMozillaExtensionApi::GetValue(nsPluginManagerVariable variable, + void * value) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::ReloadPlugins(PRBool reloadPages) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::UserAgent( + const char** resultingAgentString) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::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 BrowserMozillaExtensionApi::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 BrowserMozillaExtensionApi::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 BrowserMozillaExtensionApi::UnregisterPlugin(REFNSIID aCID) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::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 BrowserMozillaExtensionApi::BeginWaitCursor() { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::EndWaitCursor() { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::SupportsURLProtocol(const char* aProtocol, + PRBool* aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::NotifyStatusChange(nsIPlugin* aPlugin, + nsresult aStatus) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::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(NPN_MemAlloc(proxy.length() + 1)); + strncpy(result, proxy.c_str(), proxy.length() + 1); + + *aResult = result; + return NS_OK; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::RegisterWindow( + nsIEventHandler* handler, + nsPluginPlatformWindowRef window) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::UnregisterWindow( + nsIEventHandler* handler, + nsPluginPlatformWindowRef win) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::AllocateMenuID(nsIEventHandler* aHandler, + PRBool aIsSubmenu, + PRInt16 *aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::DeallocateMenuID(nsIEventHandler* aHandler, + PRInt16 aMenuID) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::HasAllocatedMenuID(nsIEventHandler* aHandler, + PRInt16 aMenuID, + PRBool* aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +// nsICookieStorage +NS_IMETHODIMP BrowserMozillaExtensionApi::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(cookie_buffer), + cookies.c_str(), + cookies.length() + 1); + + buffer_size = cookies.length(); + return NS_OK; +} + +NS_IMETHODIMP BrowserMozillaExtensionApi::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(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/libcef/plugins/browser_mozilla_extensions.h b/libcef/plugins/browser_mozilla_extensions.h new file mode 100644 index 000000000..ae0b70887 --- /dev/null +++ b/libcef/plugins/browser_mozilla_extensions.h @@ -0,0 +1,101 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _BROWSER_MOZILLA_EXTENSIONS_H +#define _BROWSER_MOZILLA_EXTENSIONS_H + +#include + +// 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 BrowserPluginInstance; + +// Implementation of extended Mozilla interfaces needed to support +// Sun's new Java plugin. +class BrowserMozillaExtensionApi : public nsIServiceManager, + public nsIPluginManager2, + public nsICookieStorage { + public: + BrowserMozillaExtensionApi(BrowserPluginInstance* 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 plugin_instance_; + unsigned long ref_count_; +}; + +} // namespace NPAPI + +#endif // _BROWSER_MOZILLA_EXTENSIONS_H + diff --git a/libcef/plugins/browser_plugin_instance.cc b/libcef/plugins/browser_plugin_instance.cc new file mode 100644 index 000000000..a1a0562c8 --- /dev/null +++ b/libcef/plugins/browser_plugin_instance.cc @@ -0,0 +1,512 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" +#include "browser_plugin_instance.h" +#include "browser_plugin_lib.h" +#include "browser_plugin_stream_url.h" +#include "browser_plugin_string_stream.h" +#if defined(OS_WIN) +#include "browser_mozilla_extensions.h" +#endif + +#include "base/file_util.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_host.h" +#include "net/base/escape.h" + +namespace NPAPI +{ + +// TODO(evanm): don't rely on static initialization. +ThreadLocalStorage::Slot BrowserPluginInstance::plugin_instance_tls_index_; + +BrowserPluginInstance::BrowserPluginInstance(BrowserPluginLib *plugin, + const std::string &mime_type) + : plugin_(plugin), + npp_(0), + host_(PluginHost::Singleton()), + npp_functions_(plugin->functions()), +#if defined(OS_WIN) + hwnd_(0), +#endif + windowless_(false), + transparent_(true), + webplugin_(0), + mime_type_(mime_type), + get_notify_data_(NULL), + use_mozilla_user_agent_(false), + message_loop_(MessageLoop::current()), + load_manually_(false), + in_close_streams_(false) { + npp_ = new NPP_t(); + npp_->ndata = 0; + npp_->pdata = 0; + + memset(&zero_padding_, 0, sizeof(zero_padding_)); + DCHECK(message_loop_); +} + +BrowserPluginInstance::~BrowserPluginInstance() { + CloseStreams(); + + if (npp_ != 0) { + delete npp_; + npp_ = 0; + } + + if (plugin_) + plugin_->CloseInstance(); +} + +BrowserPluginStreamUrl *BrowserPluginInstance::CreateStream(int resource_id, + const std::string &url, + const std::string &mime_type, + bool notify_needed, + void *notify_data) { + BrowserPluginStreamUrl *stream = new BrowserPluginStreamUrl( + resource_id, GURL(url), this, notify_needed, notify_data); + + AddStream(stream); + return stream; +} + +void BrowserPluginInstance::AddStream(BrowserPluginStream* stream) { + open_streams_.push_back(stream); +} + +void BrowserPluginInstance::RemoveStream(BrowserPluginStream* stream) { + if (in_close_streams_) + return; + + std::vector >::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 BrowserPluginInstance::IsValidStream(const NPStream* stream) { + std::vector >::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 BrowserPluginInstance::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; +} + +#if defined(OS_WIN) +bool BrowserPluginInstance::HandleEvent(UINT message, WPARAM wParam, LPARAM lParam) { + if (!windowless_) + return false; + + NPEvent windowEvent; + windowEvent.event = message; + windowEvent.lParam = static_cast(lParam); + windowEvent.wParam = static_cast(wParam); + return NPP_HandleEvent(&windowEvent) != 0; +} +#endif + +bool BrowserPluginInstance::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(param_names), const_cast(param_values)); + return err == NPERR_NO_ERROR; +} + +NPObject *BrowserPluginInstance::GetPluginScriptableObject() { + NPObject *value = NULL; + NPError error = NPP_GetValue(NPPVpluginScriptableNPObject, &value); + if (error != NPERR_NO_ERROR || value == NULL) + return NULL; + return value; +} + +void BrowserPluginInstance::SetURLLoadData(const GURL& url, + void* notify_data) { + get_url_ = url; + get_notify_data_ = notify_data; +} + +// WebPluginLoadDelegate methods +void BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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); + } + +#if defined(OS_WIN) + // Clean up back references to this instance if any + if (mozilla_extenstions_) { + mozilla_extenstions_->DetachFromInstance(); + mozilla_extenstions_ = NULL; + } +#endif + + for (unsigned int file_index = 0; file_index < files_created_.size(); + file_index++) { + file_util::Delete(files_created_[file_index], false); + } +} + +NPError BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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); + } + + // Creating a temporary FilePath instance on the stack as the explicit + // FilePath constructor with StringType as an argument causes a compiler + // error when invoked via vector push back. + FilePath file_name(UTF8ToWide(fname)); + files_created_.push_back(file_name); +} + +void BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::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 BrowserPluginInstance::SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + if (success) { + BrowserPluginStringStream *stream = + new BrowserPluginStringStream(this, url, notify_needed, + reinterpret_cast(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(notify_data)); + } + } +} + +void BrowserPluginInstance::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(); + } + + bool cancel = false; + + plugin_data_stream_ = CreateStream(-1, url, mime_type, false, NULL); + + plugin_data_stream_->DidReceiveResponse(mime_type, headers, expected_length, + last_modified, &cancel); + AddStream(plugin_data_stream_.get()); +} + +void BrowserPluginInstance::DidReceiveManualData(const char* buffer, + int length) { + DCHECK(load_manually_); + if (plugin_data_stream_.get() != NULL) { + plugin_data_stream_->DidReceiveData(buffer, length, 0); + } +} + +void BrowserPluginInstance::DidFinishManualLoading() { + DCHECK(load_manually_); + if (plugin_data_stream_.get() != NULL) { + plugin_data_stream_->DidFinishLoading(); + plugin_data_stream_->Close(NPRES_DONE); + plugin_data_stream_ = NULL; + } +} + +void BrowserPluginInstance::DidManualLoadFail() { + DCHECK(load_manually_); + if (plugin_data_stream_.get() != NULL) { + plugin_data_stream_->DidFail(); + plugin_data_stream_ = NULL; + } +} + +void BrowserPluginInstance::PluginThreadAsyncCall(void (*func)(void *), + void *userData) { + message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &BrowserPluginInstance::OnPluginThreadAsyncCall, func, userData)); +} + +void BrowserPluginInstance::OnPluginThreadAsyncCall(void (*func)(void *), + void *userData) { +#if defined(OS_WIN) + // 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. + } +#else + NOTIMPLEMENTED(); +#endif +} + +BrowserPluginInstance* BrowserPluginInstance::SetInitializingInstance( + BrowserPluginInstance* instance) { + BrowserPluginInstance* old_instance = + static_cast(plugin_instance_tls_index_.Get()); + plugin_instance_tls_index_.Set(instance); + return old_instance; +} + +BrowserPluginInstance* BrowserPluginInstance::GetInitializingInstance() { + BrowserPluginInstance* instance = + static_cast(plugin_instance_tls_index_.Get()); + return instance; +} + +NPError BrowserPluginInstance::GetServiceManager(void** service_manager) { +#if defined(OS_WIN) + if (!mozilla_extenstions_) { + mozilla_extenstions_ = new BrowserMozillaExtensionApi(this); + } + + DCHECK(mozilla_extenstions_); + mozilla_extenstions_->QueryInterface(nsIServiceManager::GetIID(), + service_manager); +#else + NOTIMPLEMENTED(); +#endif + return NPERR_NO_ERROR; +} + +void BrowserPluginInstance::PushPopupsEnabledState(bool enabled) { + popups_enabled_stack_.push(enabled); +} + +void BrowserPluginInstance::PopPopupsEnabledState() { + popups_enabled_stack_.pop(); +} + +void BrowserPluginInstance::RequestRead(NPStream* stream, + NPByteRange* range_list) { + std::string range_info = "bytes="; + + while (range_list) { + range_info += IntToString(range_list->offset); + range_info += "-"; + range_info += IntToString(range_list->offset + range_list->length - 1); + range_list = range_list->next; + if (range_list) { + range_info += ","; + } + } + + if (plugin_data_stream_) { + if (plugin_data_stream_->stream() == stream) { + webplugin_->CancelDocumentLoad(); + plugin_data_stream_ = NULL; + } + } + + // The lifetime of a NPStream instance depends on the BrowserPluginStream + // instance which owns it. When a plugin invokes NPN_RequestRead on a seekable + // stream, we don't want to create a new stream when the corresponding + // response is received. We send over a cookie which represents the + // BrowserPluginStream instance which is sent back from the renderer when the + // response is received. + std::vector >::iterator stream_index; + for (stream_index = open_streams_.begin(); + stream_index != open_streams_.end(); ++stream_index) { + BrowserPluginStream* plugin_stream = *stream_index; + if (plugin_stream->stream() == stream) { + // A stream becomes seekable the first time NPN_RequestRead + // is called on it. + plugin_stream->set_seekable(true); + + webplugin_->InitiateHTTPRangeRequest( + stream->url, range_info.c_str(), + plugin_stream, + plugin_stream->notify_needed(), + plugin_stream->notify_data()); + break; + } + } +} + +} // namespace NPAPI + diff --git a/libcef/plugins/browser_plugin_instance.h b/libcef/plugins/browser_plugin_instance.h new file mode 100644 index 000000000..2ad403e1d --- /dev/null +++ b/libcef/plugins/browser_plugin_instance.h @@ -0,0 +1,268 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO: Need to deal with NPAPI's NPSavedData. +// I haven't seen plugins use it yet. + +#ifndef _BROWSER_PLUGIN_INSTANCE_H +#define _BROWSER_PLUGIN_INSTANCE_H + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/thread_local_storage.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 BrowserPluginLib; +class BrowserPluginStream; +class BrowserPluginStreamUrl; +class BrowserPluginDataStream; +class BrowserMozillaExtensionApi; +class PluginHost; + +// A BrowserPluginInstance is an active, running instance of a Plugin. +// A single plugin may have many BrowserPluginInstances. +class BrowserPluginInstance : public base::RefCountedThreadSafe { + public: + // Create a new instance of a plugin. The BrowserPluginInstance + // will hold a reference to the plugin. + BrowserPluginInstance(BrowserPluginLib *plugin, const std::string &mime_type); + virtual ~BrowserPluginInstance(); + + // 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_; } + +#if defined(OS_WIN) + // Get/Set for the instance's HWND. + HWND window_handle() { return hwnd_; } + void set_window_handle(HWND value) { hwnd_ = value; } +#endif + + // 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::BrowserPluginLib* plugin_lib() { return plugin_; } + +#if defined(OS_WIN) + // Handles a windows native message which this BrowserPluginInstance should deal + // with. Returns true if the event is handled, false otherwise. + bool HandleEvent(UINT message, WPARAM wParam, LPARAM lParam); +#endif + + // 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]. + BrowserPluginStreamUrl *CreateStream(int resource_id, + const std::string &url, + const std::string &mime_type, + 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(BrowserPluginStream* 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(BrowserPluginStream* 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; } + + // 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 BrowserPluginInstance* SetInitializingInstance(BrowserPluginInstance* instance); + static BrowserPluginInstance* GetInitializingInstance(); + + void PushPopupsEnabledState(bool enabled); + void PopPopupsEnabledState(); + + bool popups_allowed() const { + return popups_enabled_stack_.empty() ? false : popups_enabled_stack_.top(); + } + + // Initiates byte range reads for plugins. + void RequestRead(NPStream* stream, NPByteRange* range_list); + + 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 BrowserPluginInstance 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 plugin_; + NPP npp_; + scoped_refptr host_; + NPPluginFuncs* npp_functions_; + std::vector > open_streams_; +#if defined(OS_WIN) + HWND hwnd_; +#endif + bool windowless_; + bool transparent_; + WebPlugin* webplugin_; + std::string mime_type_; + GURL get_url_; + void* get_notify_data_; + bool use_mozilla_user_agent_; +#if defined(OS_WIN) + scoped_refptr mozilla_extenstions_; +#endif + MessageLoop* message_loop_; + // Using TLS to store BrowserPluginInstance 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 ThreadLocalStorage::Slot plugin_instance_tls_index_; + scoped_refptr 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_; + + // Stack indicating if popups are to be enabled for the outgoing + // NPN_GetURL/NPN_GetURLNotify calls. + std::stack popups_enabled_stack_; + + // True if in CloseStreams(). + bool in_close_streams_; + + // List of files created for the current plugin instance. File names are + // added to the list every time the NPP_StreamAsFile function is called. + std::vector files_created_; + + DISALLOW_EVIL_CONSTRUCTORS(BrowserPluginInstance); +}; + +} // namespace NPAPI + +#endif // _BROWSER_PLUGIN_INSTANCE_H diff --git a/libcef/plugins/browser_plugin_lib.cc b/libcef/plugins/browser_plugin_lib.cc new file mode 100644 index 000000000..e39ef6e7d --- /dev/null +++ b/libcef/plugins/browser_plugin_lib.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" + +#include "config.h" + +#include "browser_plugin_lib.h" +#include "browser_plugin_instance.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "net/base/mime_util.h" + + +namespace NPAPI +{ + +BrowserPluginLib::PluginMap* BrowserPluginLib::loaded_libs_; + +BrowserPluginLib* BrowserPluginLib::GetOrCreatePluginLib(const struct CefPluginInfo& plugin_info) { + // We can only have one BrowserPluginLib 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 BrowserPluginLib objects. + if (!loaded_libs_) + loaded_libs_ = new PluginMap(); + + PluginMap::const_iterator iter = + loaded_libs_->find(plugin_info.version_info.unique_name); + if (iter != loaded_libs_->end()) + return iter->second; + + WebPluginInfo* info = CreateWebPluginInfo(plugin_info.version_info); + return new BrowserPluginLib(info, plugin_info); +} + +BrowserPluginLib::BrowserPluginLib(WebPluginInfo* info, + const CefPluginInfo& plugin_info) + : web_plugin_info_(info), + plugin_info_(plugin_info), + initialized_(false), + saved_data_(0), + instance_count_(0) { + memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); + + (*loaded_libs_)[info->file] = this; +} + +BrowserPluginLib::~BrowserPluginLib() { + if (saved_data_ != 0) { + // TODO - delete the savedData object here + } +} + +NPPluginFuncs *BrowserPluginLib::functions() { + return &plugin_funcs_; +} + +bool BrowserPluginLib::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 (net::MatchesMimeType(mime_info.mime_type, mime_type)) { + if (!allow_wildcard && (mime_info.mime_type == "*")) { + continue; + } + return true; + } + } + return false; +} + +NPError BrowserPluginLib::NP_Initialize() { + if (initialized_) + return NPERR_NO_ERROR; + + PluginHost *host = PluginHost::Singleton(); + if (host == 0) + return NPERR_GENERIC_ERROR; + + NPError rv = plugin_info_.np_initialize(host->host_functions()); + initialized_ = (rv == NPERR_NO_ERROR); + + if(initialized_) { + plugin_funcs_.size = sizeof(plugin_funcs_); + plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + if (plugin_info_.np_getentrypoints(&plugin_funcs_) != NPERR_NO_ERROR) + rv = false; + } + + return rv; +} + +void BrowserPluginLib::NP_Shutdown(void) { + DCHECK(initialized_); + plugin_info_.np_shutdown(); +} + +BrowserPluginInstance *BrowserPluginLib::CreateInstance(const std::string &mime_type) { + // The PluginInstance class uses the PluginLib member only for calling + // CloseInstance() from the PluginInstance destructor. We explicitly call + // CloseInstance() from BrowserWebPluginDelegateImpl::DestroyInstance(). + BrowserPluginInstance *new_instance = + new BrowserPluginInstance(this, mime_type); + instance_count_++; + DCHECK(new_instance != 0); + return new_instance; +} + +void BrowserPluginLib::CloseInstance() { + instance_count_--; + if (instance_count_ == 0) { + NP_Shutdown(); + initialized_ = false; + loaded_libs_->erase(web_plugin_info_->file); + if (loaded_libs_->empty()) { + delete loaded_libs_; + loaded_libs_ = NULL; + } + } +} + +WebPluginInfo* BrowserPluginLib::CreateWebPluginInfo(const CefPluginVersionInfo& plugin_info) { + std::vector mime_types, file_extensions; + std::vector descriptions; + SplitString(base::SysWideToNativeMB(plugin_info.mime_types), '|', + &mime_types); + SplitString(base::SysWideToNativeMB(plugin_info.file_extensions), '|', + &file_extensions); + SplitString(plugin_info.file_open_names, '|', &descriptions); + + if (mime_types.empty()) + return NULL; + + WebPluginInfo *info = new WebPluginInfo(); + info->name = plugin_info.product_name; + info->desc = plugin_info.description; + info->version = plugin_info.version; + info->file = StringToLowerASCII(plugin_info.unique_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; +} + +} // namespace NPAPI diff --git a/libcef/plugins/browser_plugin_lib.h b/libcef/plugins/browser_plugin_lib.h new file mode 100644 index 000000000..6eadc70c2 --- /dev/null +++ b/libcef/plugins/browser_plugin_lib.h @@ -0,0 +1,89 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _BROWSER_PLUGIN_LIB_H +#define _BROWSER_PLUGIN_LIB_H + +#include + +#include "../../include/cef_nplugin.h" + +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" + +struct WebPluginInfo; + +namespace NPAPI +{ + +class BrowserPluginInstance; + +// A BrowserPluginLib is a single NPAPI Plugin Library, and is the lifecycle +// manager for new PluginInstances. +class BrowserPluginLib : public base::RefCounted { + public: + virtual ~BrowserPluginLib(); + static BrowserPluginLib* GetOrCreatePluginLib(const struct CefPluginInfo& plugin_info); + + // 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. + BrowserPluginInstance *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& web_plugin_info() { return *web_plugin_info_; } + const struct CefPluginInfo& plugin_info() { return 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); + + int instance_count() const { return instance_count_; } + + private: + // Creates a new BrowserPluginLib. The WebPluginInfo object is owned by this + // object. internal_plugin_info must not be NULL. + BrowserPluginLib(WebPluginInfo* info, + const CefPluginInfo& plugin_info); + + // Creates WebPluginInfo structure based on read in or built in + // CefPluginVersionInfo. + static WebPluginInfo* CreateWebPluginInfo(const CefPluginVersionInfo& info); + + scoped_ptr web_plugin_info_; // supported mime types, description + struct CefPluginInfo plugin_info_; + 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 base::hash_map > PluginMap; + static PluginMap* loaded_libs_; + + DISALLOW_EVIL_CONSTRUCTORS(BrowserPluginLib); +}; + +} // namespace NPAPI + +#endif // _BROWSER_PLUGIN_LIB_H diff --git a/libcef/plugins/browser_plugin_list.cc b/libcef/plugins/browser_plugin_list.cc new file mode 100644 index 000000000..aec1ba456 --- /dev/null +++ b/libcef/plugins/browser_plugin_list.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" + +#include +#include + +#include "browser_plugin_list.h" +#include "browser_plugin_lib.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "googleurl/src/gurl.h" + + +namespace NPAPI +{ + +scoped_refptr BrowserPluginList::singleton_; + +BrowserPluginList* BrowserPluginList::Singleton() { + if (singleton_.get() == NULL) { + singleton_ = new BrowserPluginList(); + } + + return singleton_; +} + +BrowserPluginList::BrowserPluginList() { +} + +BrowserPluginList::~BrowserPluginList() { + plugins_.clear(); +} + +void BrowserPluginList::AddPlugin(const struct CefPluginInfo& plugin_info) { + scoped_refptr new_plugin + = BrowserPluginLib::GetOrCreatePluginLib(plugin_info); + if (!new_plugin.get()) + return; + + const WebPluginInfo& web_plugin_info = new_plugin->web_plugin_info(); + for (size_t i = 0; i < web_plugin_info.mime_types.size(); ++i) { + const std::string &mime_type = web_plugin_info.mime_types[i].mime_type; + if (mime_type == "*") + continue; + + if (!SupportsType(mime_type)) + plugins_.push_back(new_plugin); + } +} + +void BrowserPluginList::RemovePlugin(const struct CefPluginInfo& plugin_info) +{ + PluginList::iterator it = plugins_.begin(); + for(; it != plugins_.end(); ++it) { + if((*it)->web_plugin_info().file == plugin_info.version_info.unique_name) { + plugins_.erase(it); + } + } +} + +BrowserPluginLib* BrowserPluginList::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)) { + return plugins_[idx]; + } + } + + return NULL; +} + +BrowserPluginLib* BrowserPluginList::FindPlugin(const GURL &url, + std::string* actual_mime_type) { + std::wstring path = base::SysNativeMBToWide(url.path()); + std::wstring extension_wide = file_util::GetFileExtensionFromPath(path); + if (extension_wide.empty()) + return NULL;; + + std::string extension = + StringToLowerASCII(base::SysWideToNativeMB(extension_wide)); + + for (size_t idx = 0; idx < plugins_.size(); ++idx) { + if (SupportsExtension(plugins_[idx]->web_plugin_info(), extension, + actual_mime_type)) { + return plugins_[idx]; + } + } + + return NULL; +} + +bool BrowserPluginList::SupportsType(const std::string &mime_type) { + DCHECK(mime_type == StringToLowerASCII(mime_type)); + bool allow_wildcard = true; + return (FindPlugin(mime_type, "", allow_wildcard ) != 0); +} + +bool BrowserPluginList::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 BrowserPluginList::GetPlugins(bool refresh, + std::vector* plugins) { + plugins->resize(plugins_.size()); + for (size_t i = 0; i < plugins->size(); ++i) + (*plugins)[i] = plugins_[i]->web_plugin_info(); + + return true; +} + +bool BrowserPluginList::GetPluginInfo(const GURL& url, + const std::string& mime_type, + const std::string& clsid, + bool allow_wildcard, + struct CefPluginInfo* plugin_info, + std::string* actual_mime_type) { + scoped_refptr plugin = FindPlugin(mime_type, clsid, + allow_wildcard); + + if (plugin.get() == NULL) + plugin = FindPlugin(url, actual_mime_type); + + if (plugin.get() == NULL) + return false; + + *plugin_info = plugin->plugin_info(); + return true; +} + +void BrowserPluginList::Shutdown() { + // TODO +} + +} // namespace NPAPI diff --git a/libcef/plugins/browser_plugin_list.h b/libcef/plugins/browser_plugin_list.h new file mode 100644 index 000000000..46a696a68 --- /dev/null +++ b/libcef/plugins/browser_plugin_list.h @@ -0,0 +1,99 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO: Need mechanism to cleanup the static instance + +#ifndef _BROWSER_PLUGIN_LIST_H +#define _BROWSER_PLUGIN_LIST_H + +#include +#include + +#include "../../include/cef_nplugin.h" + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "webkit/glue/webplugin.h" + +class GURL; + +namespace NPAPI +{ + +class BrowserPluginLib; +class PluginInstance; + +// The BrowserPluginList is responsible for loading internal NPAPI based +// plugins. Call the LoadPlugin() method to load an internal plugin. +class BrowserPluginList : public base::RefCounted { + public: + // Gets the one instance of the BrowserPluginList. + static BrowserPluginList* Singleton(); + + virtual ~BrowserPluginList(); + + // Add a plugin using the specified info structure. + void AddPlugin(const struct CefPluginInfo& plugin_info); + + // Remove the plugin matching the specified info structure. + void RemovePlugin(const struct CefPluginInfo& plugin_info); + + // Find a plugin by mime type and clsid. + // If clsid is empty, we will just find the plugin that supports mime type. + // The allow_wildcard parameter controls whether this function returns + // plugins which support wildcard mime types (* as the mime type) + BrowserPluginLib* FindPlugin(const std::string &mime_type, + const std::string& clsid, bool allow_wildcard); + + // Find a plugin to by extension. Returns the corresponding mime type. + BrowserPluginLib* 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* 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, + struct CefPluginInfo* plugin_info, + std::string* actual_mime_type); + + private: + // Constructors are private for singletons + BrowserPluginList(); + + static scoped_refptr singleton_; + + typedef std::vector > PluginList; + PluginList plugins_; + + DISALLOW_EVIL_CONSTRUCTORS(BrowserPluginList); +}; + +} // namespace NPAPI + +#endif // _BROWSER_PLUGIN_LIST_H + diff --git a/libcef/plugins/browser_plugin_stream.cc b/libcef/plugins/browser_plugin_stream.cc new file mode 100644 index 000000000..2e8dffe01 --- /dev/null +++ b/libcef/plugins/browser_plugin_stream.cc @@ -0,0 +1,247 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO : Support NP_ASFILEONLY mode +// TODO : Support NP_SEEK mode +// TODO : Support SEEKABLE=true in NewStream + +#include "precompiled_libcef.h" +#include "browser_plugin_stream.h" +#include "browser_plugin_instance.h" + +#include "base/string_util.h" +#include "base/message_loop.h" +#include "webkit/glue/webkit_glue.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI { + +BrowserPluginStream::~BrowserPluginStream() { + // always close our temporary files. + CloseTempFile(); + free(const_cast(stream_.url)); +} + +bool BrowserPluginStream::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_; + + bool seekable_stream = false; + if (!headers_.empty()) { + stream_.headers = headers_.c_str(); + if (headers_.find("Accept-Ranges: bytes") != std::string::npos) { + seekable_stream = true; + } + } + + 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_, seekable_stream, + &requested_plugin_mode_); + if (err != NPERR_NO_ERROR) { + Notify(err); + return false; + } + + opened_ = true; + + if (requested_plugin_mode_ == NP_SEEK) { + seekable_stream_ = 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) { + if (OpenTempFile() == false) + return false; + } + + mime_type_ = char_mime_type; + return true; +} + +int BrowserPluginStream::Write(const char *buffer, const int length, + int data_offset) { + // 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, data_offset)) + return length; + + return -1; +} + +bool BrowserPluginStream::WriteToFile(const char *buf, size_t length) { + // For ASFILEONLY, ASFILE, and SEEK modes, we need to write + // to the disk + if (TempFileIsValid() && + (requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_ASFILEONLY) ) { + size_t totalBytesWritten = 0, bytes; + do { + bytes = WriteBytes(buf, length); + totalBytesWritten += bytes; + } while (bytes > 0U && totalBytesWritten < length); + + if (totalBytesWritten != length) + return false; + } + + return true; +} + +bool BrowserPluginStream::WriteToPlugin(const char *buf, const int length, + const int data_offset) { + // For NORMAL and ASFILE modes, we send the data to the plugin now + if (requested_plugin_mode_ != NP_NORMAL && + requested_plugin_mode_ != NP_ASFILE && + requested_plugin_mode_ != NP_SEEK) + return true; + + int written = TryWriteToPlugin(buf, length, data_offset); + 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); + data_offset_ = data_offset; + memcpy(&delivery_data_[previous_size], buf + written, remaining); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &BrowserPluginStream::OnDelayDelivery)); + } + + return true; +} + +void BrowserPluginStream::OnDelayDelivery() { + // It is possible that the plugin stream may have closed before the task + // was hit. + if (!opened_) { + return; + } + + int size = static_cast(delivery_data_.size()); + int written = TryWriteToPlugin(&delivery_data_.front(), size, + data_offset_); + if (written > 0) { + // Remove the data that we already wrote. + delivery_data_.erase(delivery_data_.begin(), + delivery_data_.begin() + written); + } +} + +int BrowserPluginStream::TryWriteToPlugin(const char *buf, const int length, + const int data_offset) { + int byte_offset = 0; + + if (data_offset > 0) + data_offset_ = data_offset; + + 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 bytes_consumed = instance_->NPP_Write( + &stream_, data_offset_, bytes_to_write, + const_cast(buf + byte_offset)); + if (bytes_consumed < 0) { + // The plugin failed, which means that we need to close the stream. + Close(NPRES_NETWORK_ERR); + return -1; + } + if (bytes_consumed == 0) { + // The plugin couldn't take all of the data now. + return byte_offset; + } + + // The plugin might report more that we gave it. + bytes_consumed = std::min(bytes_consumed, bytes_to_write); + + data_offset_ += bytes_consumed; + byte_offset += bytes_consumed; + } + + if (close_on_write_data_) + Close(NPRES_DONE); + + return length; +} + +bool BrowserPluginStream::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 (TempFileIsValid()) { + 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; +} + +void BrowserPluginStream::Notify(NPReason reason) { + if (notify_needed_) { + instance_->NPP_URLNotify(stream_.url, reason, notify_data_); + notify_needed_ = false; + } +} + +} // namespace NPAPI + diff --git a/libcef/plugins/browser_plugin_stream.h b/libcef/plugins/browser_plugin_stream.h new file mode 100644 index 000000000..e44116ea5 --- /dev/null +++ b/libcef/plugins/browser_plugin_stream.h @@ -0,0 +1,137 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _BROWSER_PLUGIN_STREAM_H +#define _BROWSER_PLUGIN_STREAM_H + +#include +#include + +#include "base/file_path.h" +#include "base/ref_counted.h" +#include "third_party/npapi/bindings/npapi.h" + +class WebPluginResourceClient; + +namespace NPAPI { + +class BrowserPluginInstance; + +// Base class for a NPAPI stream. Tracks basic elements +// of a stream for NPAPI notifications and stream position. +class BrowserPluginStream : public base::RefCounted { + public: + // Create a new BrowserPluginStream object. If needNotify is true, then the + // plugin will be notified when the stream has been fully sent. + BrowserPluginStream(BrowserPluginInstance *instance, + const char *url, + bool need_notify, + void *notify_data); + virtual ~BrowserPluginStream(); + + // 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, int data_offset); + + // 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); + + virtual WebPluginResourceClient* AsResourceClient() { return NULL; } + + // Cancels any HTTP requests initiated by the stream. + virtual void CancelRequest() {} + + const NPStream* stream() const { return &stream_; } + + // setter/getter for the seekable attribute on the stream. + bool seekable() const { return seekable_stream_; } + + void set_seekable(bool seekable) { seekable_stream_ = seekable; } + + // getters for reading the notification related attributes on the stream. + bool notify_needed() const { return notify_needed_; } + + void* notify_data() const { return notify_data_; } + + protected: + BrowserPluginInstance* 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(); + + // Sends the data to the file. Called From WriteToFile. + size_t WriteBytes(const char *buf, size_t length); + + // Sends the data to the file if it's open. + bool WriteToFile(const char *buf, size_t 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, const int data_offset); + + // 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, const int data_offset); + + // The callback which calls TryWriteToPlugin. + void OnDelayDelivery(); + + // Returns true if the temp file is valid and open for writing. + bool TempFileIsValid(); + + private: + NPStream stream_; + std::string headers_; + scoped_refptr instance_; + bool notify_needed_; + void * notify_data_; + bool close_on_write_data_; + uint16 requested_plugin_mode_; + bool opened_; +#if defined(OS_WIN) + char temp_file_name_[MAX_PATH]; + HANDLE temp_file_handle_; +#elif defined(OS_POSIX) + FILE* temp_file_; + FilePath temp_file_path_; +#endif + std::vector delivery_data_; + int data_offset_; + bool seekable_stream_; + std::string mime_type_; + DISALLOW_EVIL_CONSTRUCTORS(BrowserPluginStream); +}; + +} // namespace NPAPI + +#endif // _BROWSER_PLUGIN_STREAM_H + diff --git a/libcef/plugins/browser_plugin_stream_url.cc b/libcef/plugins/browser_plugin_stream_url.cc new file mode 100644 index 000000000..bc31f7ce9 --- /dev/null +++ b/libcef/plugins/browser_plugin_stream_url.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" +#include "browser_plugin_stream_url.h" +#include "browser_plugin_instance.h" + +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_host.h" + +namespace NPAPI { + +BrowserPluginStreamUrl::BrowserPluginStreamUrl( + int resource_id, + const GURL &url, + BrowserPluginInstance *instance, + bool notify_needed, + void *notify_data) + : BrowserPluginStream(instance, url.spec().c_str(), notify_needed, notify_data), + url_(url), + id_(resource_id) { +} + +BrowserPluginStreamUrl::~BrowserPluginStreamUrl() { +} + +bool BrowserPluginStreamUrl::Close(NPReason reason) { + CancelRequest(); + bool result = BrowserPluginStream::Close(reason); + instance()->RemoveStream(this); + return result; +} + +void BrowserPluginStreamUrl::WillSendRequest(const GURL& url) { + url_ = url; + UpdateUrl(url.spec().c_str()); +} + +void BrowserPluginStreamUrl::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 BrowserPluginStreamUrl::DidReceiveData(const char* buffer, int length, + int data_offset) { + if (!open()) + return; + + if (length > 0) + Write(const_cast(buffer), length, data_offset); +} + +void BrowserPluginStreamUrl::DidFinishLoading() { + if (!seekable()) { + Close(NPRES_DONE); + } +} + +void BrowserPluginStreamUrl::DidFail() { + Close(NPRES_NETWORK_ERR); +} + +void BrowserPluginStreamUrl::CancelRequest() { + if (id_ > 0) { + if (instance()->webplugin()) { + instance()->webplugin()->CancelResource(id_); + } + id_ = 0; + } +} + +} // namespace NPAPI + diff --git a/libcef/plugins/browser_plugin_stream_url.h b/libcef/plugins/browser_plugin_stream_url.h new file mode 100644 index 000000000..7c4ffffad --- /dev/null +++ b/libcef/plugins/browser_plugin_stream_url.h @@ -0,0 +1,67 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _BROWSER_PLUGIN_STREAM_URL_H +#define _BROWSER_PLUGIN_STREAM_URL_H + +#include "browser_plugin_stream.h" + +#include "webkit/glue/webplugin.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI { + +class BrowserPluginInstance; + +// A NPAPI Stream based on a URL. +class BrowserPluginStreamUrl : public BrowserPluginStream, + 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. + BrowserPluginStreamUrl(int resource_id, + const GURL &url, + BrowserPluginInstance *instance, + bool notify_needed, + void *notify_data); + virtual ~BrowserPluginStreamUrl(); + + // Stop sending the stream to the client. + // Overrides the base Close so we can cancel our fetching the URL if + // it is still loading. + virtual bool Close(NPReason reason); + + virtual WebPluginResourceClient* AsResourceClient() { + return static_cast(this); + } + + virtual void CancelRequest(); + + // + // 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, int data_offset); + void DidFinishLoading(); + void DidFail(); + + + private: + GURL url_; + int id_; + + DISALLOW_EVIL_CONSTRUCTORS(BrowserPluginStreamUrl); +}; + +} // namespace NPAPI + +#endif // _BROWSER_PLUGIN_STREAM_URL_H diff --git a/libcef/plugins/browser_plugin_stream_win.cc b/libcef/plugins/browser_plugin_stream_win.cc new file mode 100644 index 000000000..ee6007d86 --- /dev/null +++ b/libcef/plugins/browser_plugin_stream_win.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" +#include "browser_plugin_stream.h" +#include "browser_plugin_instance.h" + +#include "base/logging.h" + +namespace NPAPI { + +BrowserPluginStream::BrowserPluginStream( + BrowserPluginInstance *instance, + const char *url, + bool need_notify, + void *notify_data) + : instance_(instance), + 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), + seekable_stream_(false), + data_offset_(0) { + memset(&stream_, 0, sizeof(stream_)); + stream_.url = _strdup(url); + temp_file_name_[0] = '\0'; +} + +void BrowserPluginStream::UpdateUrl(const char* url) { + DCHECK(!opened_); + free(const_cast(stream_.url)); + stream_.url = _strdup(url); +} + +void BrowserPluginStream::WriteAsFile() { + if (requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_ASFILEONLY) + instance_->NPP_StreamAsFile(&stream_, temp_file_name_); +} + +size_t BrowserPluginStream::WriteBytes(const char *buf, size_t length) { + DWORD bytes; + + if (!WriteFile(temp_file_handle_, buf, length, &bytes, 0)) + return 0U; + + return static_cast(bytes); +} + +bool BrowserPluginStream::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 BrowserPluginStream::CloseTempFile() { + if (temp_file_handle_ != INVALID_HANDLE_VALUE) { + CloseHandle(temp_file_handle_); + temp_file_handle_ = INVALID_HANDLE_VALUE; + } +} + +bool BrowserPluginStream::TempFileIsValid() { + return temp_file_handle_ != INVALID_HANDLE_VALUE; +} + +} // namespace NPAPI + diff --git a/libcef/plugins/browser_plugin_string_stream.cc b/libcef/plugins/browser_plugin_string_stream.cc new file mode 100644 index 000000000..9b04c2d69 --- /dev/null +++ b/libcef/plugins/browser_plugin_string_stream.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" +#include "browser_plugin_string_stream.h" + +namespace NPAPI { + +BrowserPluginStringStream::BrowserPluginStringStream( + BrowserPluginInstance *instance, + const std::string &url, + bool notify_needed, + void *notify_data) + : BrowserPluginStream(instance, url.c_str(), notify_needed, notify_data) { +} + +BrowserPluginStringStream::~BrowserPluginStringStream() { +} + +void BrowserPluginStringStream::SendToPlugin(const std::string &data, + const std::string &mime_type) { + int length = static_cast(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, 0); + NPReason reason = written == length ? NPRES_DONE : NPRES_NETWORK_ERR; + Close(reason); + } +} + +} + diff --git a/libcef/plugins/browser_plugin_string_stream.h b/libcef/plugins/browser_plugin_string_stream.h new file mode 100644 index 000000000..1c3dc3309 --- /dev/null +++ b/libcef/plugins/browser_plugin_string_stream.h @@ -0,0 +1,39 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _BROWSER_PLUGIN_STRING_STREAM_H +#define _BROWSER_PLUGIN_STRING_STREAM_H + +#include "browser_plugin_stream.h" + +namespace NPAPI { + +class BrowserPluginInstance; + +// An NPAPI stream from a string. +class BrowserPluginStringStream : public BrowserPluginStream { + public: + // Create a new stream for sending to the plugin. + // If notify_needed, will notify the plugin after the data has + // all been sent. + BrowserPluginStringStream(BrowserPluginInstance *instance, + const std::string &url, + bool notify_needed, + void *notify_data); + virtual ~BrowserPluginStringStream(); + + // Initiates the sending of data to the plugin. + void SendToPlugin(const std::string &data, + const std::string &mime_type); + + private: + + DISALLOW_EVIL_CONSTRUCTORS(BrowserPluginStringStream); +}; + +} // namespace NPAPI + +#endif // _BROWSER_PLUGIN_STRING_STREAM_H + diff --git a/libcef/plugins/browser_webplugin_delegate_impl.cc b/libcef/plugins/browser_webplugin_delegate_impl.cc new file mode 100644 index 000000000..38b1418c6 --- /dev/null +++ b/libcef/plugins/browser_webplugin_delegate_impl.cc @@ -0,0 +1,1092 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "precompiled_libcef.h" + +#include "browser_webplugin_delegate_impl.h" +#include "browser_plugin_instance.h" +#include "browser_plugin_lib.h" +#include "browser_plugin_list.h" +#include "browser_plugin_stream_url.h" + +#include +#include + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/gfx/gdi_util.h" +#include "base/gfx/point.h" +#include "base/stats_counters.h" +#include "base/string_util.h" +#include "webkit/default_plugin/plugin_impl.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webkit_glue.h" + +static StatsCounter windowless_queue("BrowserPlugin.ThrottleQueue"); + +static const wchar_t kNativeWindowClassName[] = L"BrowserNativeWindowClass"; +static const wchar_t kWebPluginDelegateProperty[] = + L"BrowserWebPluginDelegateProperty"; +static const wchar_t kPluginNameAtomProperty[] = L"BrowserPluginNameAtom"; +static const wchar_t kDummyActivationWindowName[] = + L"BrowserDummyWindowForActivation"; +static const wchar_t kPluginOrigProc[] = L"BrowserOriginalPtr"; + +// 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 BrowserWebPluginDelegateImpl::throttle_queue_; + +BrowserWebPluginDelegateImpl* BrowserWebPluginDelegateImpl::current_plugin_instance_ = NULL; + +bool BrowserWebPluginDelegateImpl::track_popup_menu_patched_ = false; +iat_patch::IATPatchFunction BrowserWebPluginDelegateImpl::iat_patch_helper_; + +BrowserWebPluginDelegateImpl* BrowserWebPluginDelegateImpl::Create( + const struct CefPluginInfo& plugin_info, + const std::string& mime_type, + HWND containing_window) { + + scoped_refptr plugin = + NPAPI::BrowserPluginLib::GetOrCreatePluginLib(plugin_info); + if (plugin.get() == NULL) + return NULL; + + NPError err = plugin->NP_Initialize(); + if (err != NPERR_NO_ERROR) + return NULL; + + scoped_refptr instance = + plugin->CreateInstance(mime_type); + return new BrowserWebPluginDelegateImpl(containing_window, instance.get()); +} + +bool BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::GetPluginNameFromWindow( + HWND window, std::wstring *plugin_name) { + if (NULL == plugin_name) { + return false; + } + if (!IsPluginDelegateWindow(window)) { + return false; + } + ATOM plugin_name_atom = reinterpret_cast( + 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 BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::HandleEventMessageFilterHook( + int code, WPARAM wParam, LPARAM lParam) { + + DCHECK(current_plugin_instance_); + current_plugin_instance_->OnModalLoopEntered(); + return CallNextHookEx(NULL, code, wParam, lParam); +} + +BrowserWebPluginDelegateImpl::BrowserWebPluginDelegateImpl( + HWND containing_window, + NPAPI::BrowserPluginInstance *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), + plugin_module_handle_(NULL) { + memset(&window_, 0, sizeof(window_)); + + const WebPluginInfo& plugin_info = instance_->plugin_lib()->web_plugin_info(); + std::wstring unique_name = file_util::GetFilenameFromPath(plugin_info.file); + + // Assign quirks here if required + + plugin_module_handle_ = ::GetModuleHandle(NULL); +} + +BrowserWebPluginDelegateImpl::~BrowserWebPluginDelegateImpl() { + 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 BrowserWebPluginDelegateImpl::PluginDestroyed() { + delete this; +} + +bool BrowserWebPluginDelegateImpl::Initialize(const GURL& url, + char** argn, + char** argv, + int argc, + WebPlugin* plugin, + bool load_manually) { + plugin_ = plugin; + + instance_->set_web_plugin(plugin); + NPAPI::BrowserPluginInstance* old_instance = + NPAPI::BrowserPluginInstance::SetInitializingInstance(instance_); + + if (quirks_ & PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES) { + if (instance_->plugin_lib()->instance_count() > 1) { + return false; + } + } + + if (quirks_ & PLUGIN_QUIRK_DIE_AFTER_UNLOAD) + webkit_glue::SetForcefullyTerminatePluginProcess(true); + + bool start_result = instance_->Start(url, argn, argv, argc, load_manually); + + NPAPI::BrowserPluginInstance::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_); + plugin_url_ = url.spec(); + + // The windowless version of the Silverlight plugin calls the + // WindowFromPoint API and passes the result of that to the + // TrackPopupMenu API call as the owner window. This causes the API + // to fail as the API expects the window handle to live on the same + // thread as the caller. It works in the other browsers as the plugin + // lives on the browser thread. Our workaround is to intercept the + // TrackPopupMenu API for Silverlight and replace the window handle + // with the dummy activation window. + if (windowless_ && !track_popup_menu_patched_ && + (quirks_ & PLUGIN_QUIRK_PATCH_TRACKPOPUP_MENU)) { + iat_patch_helper_.Patch(plugin_module_handle_, "user32.dll", + "TrackPopupMenu", + BrowserWebPluginDelegateImpl::TrackPopupMenuPatch); + track_popup_menu_patched_ = true; + } + return true; +} + +void BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::UpdateGeometry( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + const std::vector& cutout_rects, + bool visible) { + if (windowless_) { + WindowlessUpdateGeometry(window_rect, clip_rect); + } else { + WindowedUpdateGeometry(window_rect, clip_rect, cutout_rects, visible); + } +} + +void BrowserWebPluginDelegateImpl::Paint(HDC hdc, const gfx::Rect& rect) { + if (windowless_) + WindowlessPaint(hdc, rect); +} + +void BrowserWebPluginDelegateImpl::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(hdc); + npprint.print.embedPrint.window = window_; + instance()->NPP_Print(&npprint); +#endif +} + +NPObject* BrowserWebPluginDelegateImpl::GetPluginScriptableObject() { + return instance_->GetPluginScriptableObject(); +} + +void BrowserWebPluginDelegateImpl::DidFinishLoadWithReason(NPReason reason) { + instance()->DidFinishLoadWithReason(reason); +} + +int BrowserWebPluginDelegateImpl::GetProcessId() { + // We are in process, so the plugin pid is this current process pid. + return ::GetCurrentProcessId(); +} + +void BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::DidReceiveManualData(const char* buffer, + int length) { + instance()->DidReceiveManualData(buffer, length); +} + +void BrowserWebPluginDelegateImpl::DidFinishManualLoading() { + instance()->DidFinishManualLoading(); +} + +void BrowserWebPluginDelegateImpl::DidManualLoadFail() { + instance()->DidManualLoadFail(); +} + +std::wstring BrowserWebPluginDelegateImpl::GetPluginPath() { + return instance_->plugin_lib()->web_plugin_info().file; +} + +void BrowserWebPluginDelegateImpl::InstallMissingPlugin() { + NPEvent evt; + evt.event = PluginInstallerImpl::kInstallMissingPluginMessage; + evt.lParam = 0; + evt.wParam = 0; + instance()->NPP_HandleEvent(&evt); +} + +void BrowserWebPluginDelegateImpl::WindowedUpdateGeometry( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + const std::vector& cutout_rects, + bool visible) { + if (WindowedReposition(window_rect, clip_rect, cutout_rects, visible) || + !windowed_did_set_window_) { + // Let the plugin know that it has been moved + WindowedSetWindow(); + } +} + +bool BrowserWebPluginDelegateImpl::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_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + 0, + 0, + 0, + 0, + parent_, + 0, + GetModuleHandle(NULL), + 0); + if (windowed_handle_ == 0) + return false; + + if (IsWindow(parent_)) { + // This is a tricky workaround for Issue 2673 in chromium "Flash: IME not + // available". To use IMEs in this window, we have to make Windows attach + // IMEs to this window (i.e. load IME DLLs, attach them to this process, + // and add their message hooks to this window). Windows attaches IMEs while + // this process creates a top-level window. On the other hand, to layout + // this window correctly in the given parent window (RenderWidgetHostHWND), + // this window should be a child window of the parent window. + // To satisfy both of the above conditions, this code once creates a + // top-level window and change it to a child window of the parent window. + SetWindowLongPtr(windowed_handle_, GWL_STYLE, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + SetParent(windowed_handle_, parent_); + } + + 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) { + std::wstring plugin_name = instance_->plugin_lib()->web_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(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(DefWindowProcA)); + + return true; +} + +void BrowserWebPluginDelegateImpl::WindowedDestroyWindow() { + if (windowed_handle_ != NULL) { + // Unsubclass the window. + WNDPROC current_wnd_proc = reinterpret_cast( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc == NativeWndProc) { + SetWindowLongPtr(windowed_handle_, + GWLP_WNDPROC, + reinterpret_cast(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 BrowserWebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window) { + std::list::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 BrowserWebPluginDelegateImpl::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 processed; + + std::list::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(msg.time); + // It is possible that the window was closed after we queued + // this message. This is a rare event; just verify the window + // is alive. (see also bug 1259488) + if (IsWindow(msg.hwnd)) + 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(&BrowserWebPluginDelegateImpl::OnThrottleMessage), + kFlashWMUSERMessageThrottleDelayMs); +} + +// Schedule a windows message for delivery later. +// static +void BrowserWebPluginDelegateImpl::ThrottleMessage(WNDPROC proc, HWND hwnd, + UINT message, WPARAM wParam, + LPARAM lParam) { + MSG msg; + msg.time = reinterpret_cast(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(&BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::FlashWindowlessWndProc(HWND hwnd, + UINT message, WPARAM wparam, LPARAM lparam) { + WNDPROC old_proc = reinterpret_cast(GetProp(hwnd, kPluginOrigProc)); + DCHECK(old_proc); + + switch (message) { + case WM_NCDESTROY: { + BrowserWebPluginDelegateImpl::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: { + BrowserWebPluginDelegateImpl::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 BrowserEnumFlashWindows(HWND window, LPARAM arg) { + WNDPROC wnd_proc = reinterpret_cast(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( + GetWindowLongPtr(window, GWLP_WNDPROC)); + if (current_wnd_proc != wnd_proc) { + WNDPROC old_flash_proc = reinterpret_cast(SetWindowLongPtr( + window, GWLP_WNDPROC, + reinterpret_cast(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 BrowserWebPluginDelegateImpl::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(), BrowserEnumFlashWindows, + reinterpret_cast( + &BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::MoveWindow( + HWND window, + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + const std::vector& cutout_rects, + bool visible) { + HRGN hrgn = ::CreateRectRgn(clip_rect.x(), + clip_rect.y(), + clip_rect.right(), + clip_rect.bottom()); + gfx::SubtractRectanglesFromRegion(hrgn, cutout_rects); + + // Note: System will own the hrgn after we call SetWindowRgn, + // so we don't need to call DeleteObject(hrgn) + ::SetWindowRgn(window, hrgn, FALSE); + + unsigned long flags = 0; + 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 BrowserWebPluginDelegateImpl::WindowedReposition( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + const std::vector& cutout_rects, + bool visible) { + if (!windowed_handle_) { + NOTREACHED(); + return false; + } + + if (window_rect_ == window_rect && clip_rect_ == clip_rect && + cutout_rects == cutout_rects_ && + initial_plugin_resize_done_) + return false; + + window_rect_ = window_rect; + clip_rect_ = clip_rect; + cutout_rects_ = cutout_rects; + + 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, cutout_rects, visible); + // Ensure that the entire window gets repainted. + ::InvalidateRect(windowed_handle_, NULL, FALSE); + } + + return true; +} + +void BrowserWebPluginDelegateImpl::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( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc != NativeWndProc) { + plugin_wnd_proc_ = reinterpret_cast(SetWindowLongPtr( + windowed_handle_, GWLP_WNDPROC, reinterpret_cast(NativeWndProc))); + } +} + +ATOM BrowserWebPluginDelegateImpl::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 = DummyWindowProc; + 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(COLOR_WINDOW+1); + wcex.lpszMenuName = 0; + wcex.lpszClassName = kNativeWindowClassName; + wcex.hIconSm = 0; + + return RegisterClassEx(&wcex); +} + +LRESULT CALLBACK BrowserWebPluginDelegateImpl::DummyWindowProc( + HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + // This is another workaround for Issue 2673 in chromium "Flash: IME not + // available". Somehow, the CallWindowProc() function does not dispatch + // window messages when its first parameter is a handle representing the + // DefWindowProc() function. To avoid this problem, this code creates a + // wrapper function which just encapsulates the DefWindowProc() function + // and set it as the window procedure of a windowed plug-in. + return DefWindowProc(hWnd, message, wParam, lParam); +} + +LRESULT CALLBACK BrowserWebPluginDelegateImpl::NativeWndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + BrowserWebPluginDelegateImpl* delegate = + reinterpret_cast( + 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 ( + 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) { + BrowserWebPluginDelegateImpl::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( + &BrowserWebPluginDelegateImpl::OnUserGestureEnd)); + } + + LRESULT result = CallWindowProc(delegate->plugin_wnd_proc_, hwnd, message, + wparam, lparam); + delegate->is_calling_wndproc = false; + return result; +} + +void BrowserWebPluginDelegateImpl::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; + cutout_rects_.clear(); + + 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 BrowserWebPluginDelegateImpl::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(); + + // We need to pass the HDC to the plugin via NPP_SetWindow in the + // first paint to ensure that it initiates rect invalidations. + if (window_.window == NULL) + windowless_needs_set_window_ = true; + + window_.window = 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("Plugin.Paint"); + StatsScope scope(plugin_paint); + instance()->NPP_HandleEvent(&paint_event); +} + +void BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::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; + + // Snag a reference to the current cursor ASAP in case the plugin modified + // it. There is a nasty race condition here with the multiprocess browser + // as someone might be setting the cursor in the main process as well. + HCURSOR last_cursor; + if (WM_MOUSEMOVE == event->event) { + last_cursor = ::GetCursor(); + } + + 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 (event->event == WM_RBUTTONUP && ::IsWindow(prev_focus_window)) { + ::SetFocus(prev_focus_window); + } + + if (WM_MOUSEMOVE == event->event) { + cursor->InitFromCursor(last_cursor); + } + + return ret; +} + +WebPluginResourceClient* BrowserWebPluginDelegateImpl::CreateResourceClient( + int resource_id, const std::string &url, bool notify_needed, + void *notify_data, void* existing_stream) { + // Stream already exists. This typically happens for range requests + // initiated via NPN_RequestRead. + if (existing_stream) { + NPAPI::BrowserPluginStream* plugin_stream = + reinterpret_cast(existing_stream); + + plugin_stream->CancelRequest(); + + return plugin_stream->AsResourceClient(); + } + + if (notify_needed) { + instance()->SetURLLoadData(GURL(url.c_str()), notify_data); + } + std::string mime_type; + NPAPI::BrowserPluginStreamUrl *stream = instance()->CreateStream(resource_id, + url, + mime_type, + notify_needed, + notify_data); + return stream; +} + +void BrowserWebPluginDelegateImpl::URLRequestRouted(const std::string&url, + bool notify_needed, + void* notify_data) { + if (notify_needed) { + instance()->SetURLLoadData(GURL(url.c_str()), notify_data); + } +} + +void BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent* event) { + if (event->event == WM_RBUTTONDOWN) + return true; + return false; +} + +bool BrowserWebPluginDelegateImpl::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 BrowserWebPluginDelegateImpl::OnUserGestureEnd() { + user_gesture_message_posted_ = false; + instance()->PopPopupsEnabledState(); +} + +BOOL WINAPI BrowserWebPluginDelegateImpl::TrackPopupMenuPatch( + HMENU menu, unsigned int flags, int x, int y, int reserved, + HWND window, const RECT* rect) { + if (current_plugin_instance_) { + unsigned long window_process_id = 0; + unsigned long window_thread_id = + GetWindowThreadProcessId(window, &window_process_id); + // TrackPopupMenu fails if the window passed in belongs to a different + // thread. + if (::GetCurrentThreadId() != window_thread_id) { + window = current_plugin_instance_->dummy_window_for_activation_; + } + } + return TrackPopupMenu(menu, flags, x, y, reserved, window, rect); +} diff --git a/libcef/plugins/browser_webplugin_delegate_impl.h b/libcef/plugins/browser_webplugin_delegate_impl.h new file mode 100644 index 000000000..322a98ca0 --- /dev/null +++ b/libcef/plugins/browser_webplugin_delegate_impl.h @@ -0,0 +1,283 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _BROWSER_WEBPLUGIN_DELEGATE_IMPL_H +#define _BROWSER_WEBPLUGIN_DELEGATE_IMPL_H + +#include +#include + +#include "browser_plugin_lib.h" + +#include "base/iat_patch.h" +#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 BrowserPluginInstance; +}; + +// An implementation of WebPluginDelegate that proxies all calls to +// the plugin process. +class BrowserWebPluginDelegateImpl : public WebPluginDelegate { + public: + static BrowserWebPluginDelegateImpl* Create(const struct CefPluginInfo& plugin_info, + 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, + const std::vector& cutout_rects, + 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 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, + void* stream); + + virtual void URLRequestRouted(const std::string&url, bool notify_needed, + void* notify_data); + bool windowless() const { return windowless_ ; } + gfx::Rect rect() const { return window_rect_; } + gfx::Rect clip_rect() const { return clip_rect_; } + + 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, + PLUGIN_QUIRK_DIE_AFTER_UNLOAD = 32, + PLUGIN_QUIRK_PATCH_TRACKPOPUP_MENU = 64, + }; + + int quirks() { return quirks_; } + + static void MoveWindow(HWND window, + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + const std::vector& cutout_rects, + bool visible); + + private: + BrowserWebPluginDelegateImpl(HWND containing_window, + NPAPI::BrowserPluginInstance *instance); + ~BrowserWebPluginDelegateImpl(); + + //-------------------------- + // used for windowed plugins + void WindowedUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + const std::vector& cutout_rects, + 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, + const std::vector& cutout_rects, + 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 DummyWindowProc( + HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + 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::BrowserPluginInstance* instance() { return instance_.get(); } + + // Closes down and destroys our plugin instance. + void DestroyInstance(); + + // 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 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_; + std::vector cutout_rects_; + 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 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 BrowserWebPluginDelegateImpl* 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 user_gesture_msg_factory_; + + // The url with which the plugin was instantiated. + std::string plugin_url_; + + // The plugin module handle. + HMODULE plugin_module_handle_; + + // Indicates whether we IAT patched the TrackPopupMenu function. + static bool track_popup_menu_patched_; + + // Helper object for patching the import table of Silverlight. + static iat_patch::IATPatchFunction iat_patch_helper_; + + // TrackPopupMenu interceptor. Parameters are the same as the Win32 function + // TrackPopupMenu. + static BOOL WINAPI TrackPopupMenuPatch(HMENU menu, unsigned int flags, int x, + int y, int reserved, HWND window, + const RECT* rect); + + DISALLOW_EVIL_CONSTRUCTORS(BrowserWebPluginDelegateImpl); +}; + +#endif // #ifndef _BROWSER_WEBPLUGIN_DELEGATE_IMPL_H diff --git a/tests/cefclient/cefclient.cpp b/tests/cefclient/cefclient.cpp index fc5457c1e..42df2b73b 100644 --- a/tests/cefclient/cefclient.cpp +++ b/tests/cefclient/cefclient.cpp @@ -5,6 +5,7 @@ #include "stdafx.h" #include "cefclient.h" +#include "clientplugin.h" #include "cef.h" #include @@ -36,6 +37,9 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, // Initialize the CEF CefInitialize(); + // Register the internal client plugin + CefRegisterPlugin(ClientPluginInfo); + MSG msg; HACCEL hAccelTable; @@ -716,7 +720,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) L"http://www.google.com"); // Start the timer that will be used to update child window state - SetTimer(hWnd, 1, 500, NULL); + SetTimer(hWnd, 1, 250, NULL); } return 0; @@ -781,6 +785,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) browser->LoadString(html, L"about:blank"); } return 0; + case ID_TESTS_PLUGIN: // Test our custom plugin + if(browser.get()) + { + std::wstring html = + L"Client Plugin:
" + L"" + L""; + browser->LoadString(html, L"about:blank"); + } } } break; diff --git a/tests/cefclient/cefclient.rc b/tests/cefclient/cefclient.rc index d2fd2fed5..ed65a677d 100644 --- a/tests/cefclient/cefclient.rc +++ b/tests/cefclient/cefclient.rc @@ -58,6 +58,7 @@ BEGIN POPUP "Tests" BEGIN MENUITEM "JavaScript", ID_TESTS_JAVASCRIPT + MENUITEM "Plugin", ID_TESTS_PLUGIN END END diff --git a/tests/cefclient/cefclient.vcproj b/tests/cefclient/cefclient.vcproj index 0d37635e5..8d20a8e1c 100644 --- a/tests/cefclient/cefclient.vcproj +++ b/tests/cefclient/cefclient.vcproj @@ -181,6 +181,14 @@ RelativePath=".\cefclient.rc" >
+ + + + diff --git a/tests/cefclient/clientplugin.cpp b/tests/cefclient/clientplugin.cpp new file mode 100644 index 000000000..d2b0f949b --- /dev/null +++ b/tests/cefclient/clientplugin.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" +#include "clientplugin.h" + +// Initialized in NP_Initialize. +NPNetscapeFuncs* g_browser = NULL; + + +NPError NPP_New(NPMIMEType plugin_type, NPP instance, uint16 mode, int16 argc, + char* argn[], char* argv[], NPSavedData* saved) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + ClientPlugin* plugin_impl = new ClientPlugin(mode); + plugin_impl->Initialize(GetModuleHandle(NULL), instance, plugin_type, argc, + argn, argv); + instance->pdata = reinterpret_cast(plugin_impl); + return NPERR_NO_ERROR; +} + +NPError NPP_Destroy(NPP instance, NPSavedData** save) { + ClientPlugin* plugin_impl = reinterpret_cast(instance->pdata); + + if (plugin_impl) { + plugin_impl->Shutdown(); + delete plugin_impl; + } + + return NPERR_NO_ERROR; +} + +NPError NPP_SetWindow(NPP instance, NPWindow* window_info) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (window_info == NULL) + return NPERR_GENERIC_ERROR; + + ClientPlugin* plugin_impl = reinterpret_cast(instance->pdata); + + if (plugin_impl == NULL) + return NPERR_GENERIC_ERROR; + + HWND window_handle = reinterpret_cast(window_info->window); + if (!plugin_impl->SetWindow(window_handle)) { + delete plugin_impl; + return NPERR_GENERIC_ERROR; + } + + return NPERR_NO_ERROR; +} + +NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* pFuncs) +{ + pFuncs->newp = NPP_New; + pFuncs->destroy = NPP_Destroy; + pFuncs->setwindow = NPP_SetWindow; + return NPERR_NO_ERROR; +} + +NPError API_CALL NP_Initialize(NPNetscapeFuncs* pFuncs) +{ + g_browser = pFuncs; + return NPERR_NO_ERROR; +} + +NPError API_CALL NP_Shutdown(void) +{ + g_browser = NULL; + return NPERR_NO_ERROR; +} + + +// ClientPlugin Implementation + +ClientPlugin::ClientPlugin(int16 mode) + : mode_(mode) +{ +} + +ClientPlugin::~ClientPlugin() +{ +} + +bool ClientPlugin::Initialize(HINSTANCE module_handle, NPP instance, + NPMIMEType mime_type, int16 argc, char* argn[], + char* argv[]) +{ + RefreshDisplay(); + return true; +} + +bool ClientPlugin::SetWindow(HWND parent_window) +{ + if (!::IsWindow(parent_window)) { + // No window created yet. Ignore this call. + if (!IsWindow()) + return true; + // Parent window has been destroyed. + Shutdown(); + return true; + } + + RECT parent_rect; + + if (IsWindow()) { + ::GetClientRect(parent_window, &parent_rect); + SetWindowPos(NULL, &parent_rect, SWP_SHOWWINDOW); + return true; + } + // First time in -- no window created by plugin yet. + ::GetClientRect(parent_window, &parent_rect); + Create(parent_window, parent_rect, NULL, WS_CHILD | WS_BORDER); + + UpdateWindow(); + ShowWindow(SW_SHOW); + + return true; +} + +void ClientPlugin::Shutdown() +{ + if (IsWindow()) { + DestroyWindow(); + } +} + +LRESULT ClientPlugin::OnPaint(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled) +{ + static LPCWSTR text = L"Left click in the green area for a message box!"; + + PAINTSTRUCT paint_struct; + BeginPaint(&paint_struct); + + RECT client_rect; + GetClientRect(&client_rect); + + int old_mode = SetBkMode(paint_struct.hdc, TRANSPARENT); + COLORREF old_color = SetTextColor(paint_struct.hdc, RGB(0, 0, 255)); + + RECT text_rect = client_rect; + DrawText(paint_struct.hdc, text, -1, &text_rect, DT_CENTER | DT_CALCRECT); + + client_rect.top = ((client_rect.bottom - client_rect.top) + - (text_rect.bottom - text_rect.top)) / 2; + DrawText(paint_struct.hdc, text, -1, &client_rect, DT_CENTER); + + SetBkMode(paint_struct.hdc, old_mode); + SetTextColor(paint_struct.hdc, old_color); + + EndPaint(&paint_struct); + return 0; +} + +LRESULT ClientPlugin::OnEraseBackGround(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) +{ + HDC paint_device_context = reinterpret_cast(wparam); + RECT erase_rect; + GetClipBox(paint_device_context, &erase_rect); + HBRUSH brush = CreateSolidBrush(RGB(0, 255, 0)); + FillRect(paint_device_context, &erase_rect, brush); + DeleteObject(brush); + return 1; +} + +LRESULT ClientPlugin::OnLButtonDown(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled) +{ + MessageBox(L"You clicked on the client plugin!", L"Client Plugin", MB_OK); + return 0; +} + +void ClientPlugin::RefreshDisplay() { + if (!IsWindow()) + return; + + InvalidateRect(NULL, TRUE); + UpdateWindow(); +} diff --git a/tests/cefclient/clientplugin.h b/tests/cefclient/clientplugin.h new file mode 100644 index 000000000..2f10c768c --- /dev/null +++ b/tests/cefclient/clientplugin.h @@ -0,0 +1,103 @@ +// Copyright (c) 2008 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Portions of this implementation are borrowed from webkit\default_plugin\ +// plugin_impl.h + +#pragma once + +#include +#include +#include "cef_nplugin.h" + +extern NPNetscapeFuncs* g_browser; + +NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* pFuncs); +NPError API_CALL NP_Initialize(NPNetscapeFuncs* pFuncs); +NPError API_CALL NP_Shutdown(void); + +// Structure providing information about the client plugin. +const CefPluginInfo ClientPluginInfo = { + { + L"client_plugin", + L"Client Plugin", + L"My Example Client Plugin", + L"1, 0, 0, 1", + L"application/x-client-plugin", + L"*", + L"" + }, + NP_GetEntryPoints, + NP_Initialize, + NP_Shutdown +}; + +// Provides the client plugin functionality. +class ClientPlugin : public CWindowImpl { + public: + // mode is the plugin instantiation mode, i.e. whether it is a full + // page plugin (NP_FULL) or an embedded plugin (NP_EMBED) + explicit ClientPlugin(int16 mode); + virtual ~ClientPlugin(); + + BEGIN_MSG_MAP(ClientPlugin) + MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackGround) + MESSAGE_HANDLER(WM_PAINT, OnPaint) + MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) + END_MSG_MAP() + + // Initializes the plugin with the instance information, mime type + // and the list of parameters passed down to the plugin from the webpage. + // + // Parameters: + // module_handle + // The handle to the dll in which this object is instantiated. + // instance + // The plugins opaque instance handle. + // mime_type + // Identifies the third party plugin which would be eventually installed. + // argc + // Indicates the count of arguments passed in from the webpage. + // argv + // Pointer to the arguments. + // Returns true on success. + bool Initialize(HINSTANCE module_handle, NPP instance, NPMIMEType mime_type, + int16 argc, char* argn[], char* argv[]); + + // Displays the default plugin UI. + // + // Parameters: + // parent_window + // Handle to the parent window. + bool SetWindow(HWND parent_window); + + // Destroys the install dialog and the plugin window. + void Shutdown(); + + HWND window() const { return m_hWnd; } + + // Getter for the NPP instance member. + const NPP instance() const { + return instance_; + } + + protected: + // Window message handlers. + LRESULT OnPaint(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnEraseBackGround(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + LRESULT OnLButtonDown(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + + // Enables the plugin window if required and initiates an update of the + // the plugin window. + void RefreshDisplay(); + + private: + // The plugins opaque instance handle + NPP instance_; + // The plugin instantiation mode (NP_FULL or NP_EMBED) + int16 mode_; +}; \ No newline at end of file diff --git a/tests/cefclient/resource.h b/tests/cefclient/resource.h index 63e0462a5..922cb099d 100644 --- a/tests/cefclient/resource.h +++ b/tests/cefclient/resource.h @@ -22,6 +22,7 @@ #define IDC_NAV_RELOAD 202 #define IDC_NAV_STOP 203 #define ID_TESTS_JAVASCRIPT 32771 +#define ID_TESTS_PLUGIN 32772 #define IDC_STATIC -1 #define IDS_LOGO 1000 @@ -31,7 +32,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 130 -#define _APS_NEXT_COMMAND_VALUE 32772 +#define _APS_NEXT_COMMAND_VALUE 32773 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 110 #endif