From d5c1af6b0da21c11e9327375d18fcd528023c7ad Mon Sep 17 00:00:00 2001 From: "evan@chromium.org" Date: Wed, 4 Feb 2009 20:56:57 +0000 Subject: Get windowed plugins (Flash) limping along on Linux. We still crash when you navigate away from the page. (PS: the plan is to unfork the _gtk.cc file once it gets closer to what we want, so you don't need to look at that too closely. I just wanted to check in what I have since it's getting big.) Review URL: http://codereview.chromium.org/19413 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9170 0039d316-1c4b-4281-b951-d872f2087c98 --- third_party/npapi/bindings/npapi.h | 7 + webkit/glue/SConscript | 1 + webkit/glue/plugins/plugin_host.cc | 24 + webkit/glue/plugins/webplugin_delegate_impl.h | 40 +- webkit/glue/plugins/webplugin_delegate_impl_gtk.cc | 585 +++++++++++++++++++++ webkit/glue/webplugin_delegate.h | 14 + webkit/glue/webplugin_impl.cc | 11 +- .../tools/test_shell/test_webview_delegate_gtk.cc | 17 +- 8 files changed, 686 insertions(+), 13 deletions(-) create mode 100644 webkit/glue/plugins/webplugin_delegate_impl_gtk.cc diff --git a/third_party/npapi/bindings/npapi.h b/third_party/npapi/bindings/npapi.h index 954a0ed..1ab6463 100644 --- a/third_party/npapi/bindings/npapi.h +++ b/third_party/npapi/bindings/npapi.h @@ -103,6 +103,13 @@ # endif /* XP_WIN */ #endif /* _WINDOWS */ +// BEGIN GOOGLE MODIFICATIONS +// On Linux, be sure to set the Mozilla-specific flag. +#ifdef OS_LINUX +#define XP_UNIX 1 +#endif +// END GOOGLE MODIFICATIONS + #ifdef __MWERKS__ # define _declspec __declspec # ifdef __INTEL__ diff --git a/webkit/glue/SConscript b/webkit/glue/SConscript index 025c67b..e9e818a 100644 --- a/webkit/glue/SConscript +++ b/webkit/glue/SConscript @@ -107,6 +107,7 @@ elif env.Bit('linux'): input_files.extend([ 'plugins/plugin_lib_linux.cc', 'plugins/plugin_list_linux.cc', + 'plugins/webplugin_delegate_impl_gtk.cc', 'webcursor_gtk.cc', 'webinputevent_linux.cc', 'webkit_glue_gtk.cc', diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc index c12ee5c..8afa505 100644 --- a/webkit/glue/plugins/plugin_host.cc +++ b/webkit/glue/plugins/plugin_host.cc @@ -2,6 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// HACK: we need this #define in place before npapi.h is included for +// plugins to work. However, all sorts of headers include npapi.h, so +// the only way to be certain the define is in place is to put it +// here. You might ask, "Why not set it in npapi.h directly, or in +// this directory's SConscript, then?" but it turns out this define +// makes npapi.h include Xlib.h, which in turn defines a ton of symbols +// like None and Status, causing conflicts with the aforementioned +// many headers that include npapi.h. Ugh. +// See also webplugin_delegate_impl.cc. +#define MOZ_X11 1 + #include "config.h" #include "webkit/glue/plugins/plugin_host.h" @@ -743,6 +754,19 @@ NPError NPN_GetValue(NPP id, NPNVariable variable, void *value) { rv = NPERR_NO_ERROR; break; } +#if defined(OS_LINUX) + case NPNVToolkit: + // Tell them we are GTK2. (The alternative is GTK 1.2.) + *reinterpret_cast(value) = NPNVGtk2; + rv = NPERR_NO_ERROR; + break; + + case NPNVSupportsXEmbedBool: + // Yes, we support XEmbed. + *reinterpret_cast(value) = TRUE; + rv = NPERR_NO_ERROR; + break; +#endif case NPNVSupportsWindowless: { NPBool* supports_windowless = reinterpret_cast(value); diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h index fcb849d..61ee33c 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl.h +++ b/webkit/glue/plugins/webplugin_delegate_impl.h @@ -5,6 +5,8 @@ #ifndef WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ #define WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ +#include "build/build_config.h" + #include #include @@ -27,12 +29,14 @@ class WebPluginDelegateImpl : public WebPluginDelegate { static WebPluginDelegateImpl* Create(const FilePath& filename, const std::string& mime_type, gfx::NativeView containing_view); +#if defined(OS_WIN) 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); +#endif // OS_WIN) // WebPluginDelegate implementation virtual void PluginDestroyed(); @@ -44,8 +48,14 @@ class WebPluginDelegateImpl : public WebPluginDelegate { bool load_manually); virtual void UpdateGeometry(const gfx::Rect& window_rect, const gfx::Rect& clip_rect); +#if defined(OS_WIN) virtual void Paint(HDC hdc, const gfx::Rect& rect); virtual void Print(HDC hdc); +#else + // TODO(port): temporary workaround. + virtual void Paint(void* dc, const gfx::Rect& rect); + virtual void Print(void* dc); +#endif virtual void SetFocus(); // only called when windowless // only called when windowless virtual bool HandleEvent(NPEvent* event, @@ -82,6 +92,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate { gfx::Rect rect() const { return window_rect_; } gfx::Rect clip_rect() const { return clip_rect_; } +#if defined(OS_WIN) enum PluginQuirks { PLUGIN_QUIRK_SETWINDOW_TWICE = 1, PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE = 2, @@ -93,6 +104,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate { PLUGIN_QUIRK_PATCH_SETCURSOR = 128, PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS = 256, }; +#endif int quirks() { return quirks_; } @@ -122,6 +134,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate { // See NPAPI NPP_SetWindow for more information. void WindowedSetWindow(); +#if defined(OS_WIN) // Registers the window class for our window ATOM RegisterNativeWindowClass(); @@ -138,12 +151,18 @@ class WebPluginDelegateImpl : public WebPluginDelegate { static void OnThrottleMessage(); static void ThrottleMessage(WNDPROC proc, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif //---------------------------- // used for windowless plugins void WindowlessUpdateGeometry(const gfx::Rect& window_rect, const gfx::Rect& clip_rect); +#if defined(OS_WIN) void WindowlessPaint(HDC hdc, const gfx::Rect& rect); +#else + // TODO(port): implement. + void WindowlessPaint(void* dc, const gfx::Rect& rect); +#endif // Tells the plugin about the current state of the window. // See NPAPI NPP_SetWindow for more information. @@ -159,15 +178,19 @@ class WebPluginDelegateImpl : public WebPluginDelegate { void DestroyInstance(); // used for windowed plugins - HWND windowed_handle_; + gfx::NativeView windowed_handle_; +#if defined(OS_WIN) bool windowed_did_set_window_; gfx::Rect windowed_last_pos_; +#endif +#if defined(OS_WIN) // 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_; +#endif // used by windowed and windowless plugins bool windowless_; @@ -175,20 +198,23 @@ class WebPluginDelegateImpl : public WebPluginDelegate { WebPlugin* plugin_; scoped_refptr instance_; +#if defined(OS_WIN) // 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; +#endif // OS_WIN - HWND parent_; + gfx::NativeView parent_; NPWindow window_; gfx::Rect window_rect_; gfx::Rect clip_rect_; std::vector cutout_rects_; int quirks_; +#if defined(OS_WIN) // 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. @@ -210,7 +236,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate { // a plugin in the course of a NPP_HandleEvent call. static LRESULT CALLBACK HandleEventMessageFilterHook(int code, WPARAM wParam, LPARAM lParam); - +#endif // Called by the message filter hook when the plugin enters a modal loop. void OnModalLoopEntered(); @@ -220,6 +246,10 @@ class WebPluginDelegateImpl : public WebPluginDelegate { // Indicates the end of a user gesture period. void OnUserGestureEnd(); + // The url with which the plugin was instantiated. + std::string plugin_url_; + +#if defined(OS_WIN) // Handle to the message filter hook HHOOK handle_event_message_filter_hook_; @@ -237,9 +267,6 @@ class WebPluginDelegateImpl : public WebPluginDelegate { // 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_; @@ -254,6 +281,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate { // Holds the current cursor set by the windowless plugin. WebCursor current_windowless_cursor_; +#endif DISALLOW_EVIL_CONSTRUCTORS(WebPluginDelegateImpl); }; diff --git a/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc new file mode 100644 index 0000000..9e98e01 --- /dev/null +++ b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc @@ -0,0 +1,585 @@ +// 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. + +// HACK: we need this #define in place before npapi.h is included for +// plugins to work. However, all sorts of headers include npapi.h, so +// the only way to be certain the define is in place is to put it +// here. You might ask, "Why not set it in npapi.h directly, or in +// this directory's SConscript, then?" but it turns out this define +// makes npapi.h include Xlib.h, which in turn defines a ton of symbols +// like None and Status, causing conflicts with the aforementioned +// many headers that include npapi.h. Ugh. +// See also plugin_host.cc. +#define MOZ_X11 1 + +#include "webkit/glue/plugins/webplugin_delegate_impl.h" + +#include +#include + +#include +#include + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/process_util.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/plugins/plugin_constants_win.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/plugins/plugin_stream_url.h" +#include "webkit/glue/webkit_glue.h" + +WebPluginDelegateImpl* WebPluginDelegateImpl::Create( + const FilePath& filename, + const std::string& mime_type, + gfx::NativeView containing_view) { + scoped_refptr plugin = + NPAPI::PluginLib::CreatePluginLib(filename); + if (plugin.get() == NULL) + return NULL; + + NPError err = plugin->NP_Initialize(); + if (err != NPERR_NO_ERROR) + return NULL; + + scoped_refptr instance = + plugin->CreateInstance(mime_type); + return new WebPluginDelegateImpl(containing_view, instance.get()); +} + +WebPluginDelegateImpl::WebPluginDelegateImpl( + gfx::NativeView containing_view, + NPAPI::PluginInstance *instance) + : + windowed_handle_(0), + windowless_(false), + plugin_(NULL), + instance_(instance), + parent_(containing_view), + quirks_(0) + { + memset(&window_, 0, sizeof(window_)); + +} + +WebPluginDelegateImpl::~WebPluginDelegateImpl() { + DestroyInstance(); + + if (!windowless_) + WindowedDestroyWindow(); +} + +void WebPluginDelegateImpl::PluginDestroyed() { + delete this; +} + +bool WebPluginDelegateImpl::Initialize(const GURL& url, + char** argn, + char** argv, + int argc, + WebPlugin* plugin, + bool load_manually) { + plugin_ = plugin; + + instance_->set_web_plugin(plugin); + NPAPI::PluginInstance* old_instance = + NPAPI::PluginInstance::SetInitializingInstance(instance_); + + bool start_result = instance_->Start(url, argn, argv, argc, load_manually); + + NPAPI::PluginInstance::SetInitializingInstance(old_instance); + + if (!start_result) + return false; + + windowless_ = instance_->windowless(); + if (windowless_) { + // For windowless plugins we should set the containing window handle + // as the instance window handle. This is what Safari does. Not having + // a valid window handle causes subtle bugs with plugins which retreive + // the window handle and validate the same. The window handle can be + // retreived via NPN_GetValue of NPNVnetscapeWindow. + NOTIMPLEMENTED() << "windowless not implemented"; + return false; + // 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_, /* unused event param */ NULL); + plugin_url_ = url.spec(); + + return true; +} + +void WebPluginDelegateImpl::DestroyInstance() { + if (instance_ && (instance_->npp()->ndata != NULL)) { + // Shutdown all streams before destroying so that + // no streams are left "in progress". Need to do + // this before calling set_web_plugin(NULL) because the + // instance uses the helper to do the download. + instance_->CloseStreams(); + + window_.window = NULL; + // if (!(quirks_ & PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY)) { + instance_->NPP_SetWindow(&window_); + // } + + instance_->NPP_Destroy(); + + instance_->set_web_plugin(NULL); + + instance_ = 0; + } +} + +void WebPluginDelegateImpl::UpdateGeometry( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect) { + if (windowless_) { + WindowlessUpdateGeometry(window_rect, clip_rect); + } else { + WindowedUpdateGeometry(window_rect, clip_rect); + } +} + +void WebPluginDelegateImpl::Paint(void* dc, const gfx::Rect& rect) { + if (windowless_) { + // TODO(port): windowless painting. + // WindowlessPaint(dc, rect); + } +} + +void WebPluginDelegateImpl::Print(void* dc) { + NOTIMPLEMENTED(); +} + +NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() { + return instance_->GetPluginScriptableObject(); +} + +void WebPluginDelegateImpl::DidFinishLoadWithReason(NPReason reason) { + instance()->DidFinishLoadWithReason(reason); +} + +int WebPluginDelegateImpl::GetProcessId() { + // We are in process, so the plugin pid is this current process pid. + return base::GetCurrentProcId(); +} + +void WebPluginDelegateImpl::SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + instance()->SendJavaScriptStream(url, result, success, notify_needed, + notify_data); +} + +void WebPluginDelegateImpl::DidReceiveManualResponse( + const std::string& url, const std::string& mime_type, + const std::string& headers, uint32 expected_length, uint32 last_modified) { + if (!windowless_) { + // Calling NPP_WriteReady before NPP_SetWindow causes movies to not load in + // Flash. See http://b/issue?id=892174. + // XXX DCHECK(windowed_did_set_window_); + } + + instance()->DidReceiveManualResponse(url, mime_type, headers, + expected_length, last_modified); +} + +void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer, + int length) { + instance()->DidReceiveManualData(buffer, length); +} + +void WebPluginDelegateImpl::DidFinishManualLoading() { + instance()->DidFinishManualLoading(); +} + +void WebPluginDelegateImpl::DidManualLoadFail() { + instance()->DidManualLoadFail(); +} + +FilePath WebPluginDelegateImpl::GetPluginPath() { + return instance()->plugin_lib()->plugin_info().path; +} + +void WebPluginDelegateImpl::InstallMissingPlugin() { + /* XXX NPEvent evt; + evt.event = PluginInstallerImpl::kInstallMissingPluginMessage; + evt.lParam = 0; + evt.wParam = 0; + instance()->NPP_HandleEvent(&evt); */ +} + +void WebPluginDelegateImpl::WindowedUpdateGeometry( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect) { + if (WindowedReposition(window_rect, clip_rect) || + false) { // !windowed_did_set_window_) { + // Let the plugin know that it has been moved + WindowedSetWindow(); + } +} + +bool WebPluginDelegateImpl::WindowedCreatePlugin() { + DCHECK(!windowed_handle_); + + bool xembed; + NPError err = instance_->NPP_GetValue(NPPVpluginNeedsXEmbed, &xembed); + DCHECK(err == NPERR_NO_ERROR); + if (!xembed) { + NOTIMPLEMENTED() << "Windowed plugin but without xembed."; + return false; + } + + windowed_handle_ = gtk_socket_new(); + gtk_widget_set_parent(windowed_handle_, parent_); + // TODO(evanm): connect to signals on the socket, like when the other side + // goes away. + + window_.window = GINT_TO_POINTER( + gtk_socket_get_id(GTK_SOCKET(windowed_handle_))); + gtk_widget_show(windowed_handle_); + + NPSetWindowCallbackStruct* extra = new NPSetWindowCallbackStruct; + extra->display = GDK_WINDOW_XDISPLAY(windowed_handle_->window); + GdkVisual* visual = gdk_drawable_get_visual(windowed_handle_->window); + extra->visual = GDK_VISUAL_XVISUAL(visual); + extra->depth = visual->depth; + extra->colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(windowed_handle_->window)); + window_.ws_info = extra; + + return true; +} + +void WebPluginDelegateImpl::WindowedDestroyWindow() { +#if 0 + 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; + } +#endif +} + +bool WebPluginDelegateImpl::WindowedReposition( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect) { + if (!windowed_handle_) { + NOTREACHED(); + return false; + } + + if (window_rect_ == window_rect && clip_rect_ == clip_rect) + return false; + + // Clipping is handled by WebPlugin. + if (window_rect.size() != window_rect_.size()) { + gdk_window_resize(windowed_handle_->window, + window_rect.width(), + window_rect.height()); + } + + GtkAllocation allocation = { window_rect_.x(), window_rect_.y(), + window_rect_.width(), window_rect_.height() }; + gtk_widget_size_allocate(windowed_handle_, &allocation); + + window_rect_ = window_rect; + clip_rect_ = clip_rect; + + // Ensure that the entire window gets repainted. + gtk_widget_queue_draw(windowed_handle_); + + return true; +} + +void WebPluginDelegateImpl::WindowedSetWindow() { + if (!instance_) + return; + + if (!windowed_handle_) { + NOTREACHED(); + return; + } + + // XXX 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. + // XXX windowed_did_set_window_ = true; + + NPError err = instance()->NPP_SetWindow(&window_); + DCHECK(err == NPERR_NO_ERROR); +#if 0 + 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))); + } +#endif +} + +void WebPluginDelegateImpl::WindowlessUpdateGeometry( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect) { + // Only resend to the instance if the geometry has changed. + if (window_rect == window_rect_ && clip_rect == clip_rect_) + return; + /* + // Set this flag before entering the instance in case of side-effects. + windowless_needs_set_window_ = true; + + // We will inform the instance of this change when we call NPP_SetWindow. + clip_rect_ = clip_rect; + 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); + } + */ +} + +#if 0 +void WebPluginDelegateImpl::WindowlessPaint(HDC hdc, + const gfx::Rect& damage_rect) { + DCHECK(hdc); + + RECT damage_rect_win; + damage_rect_win.left = damage_rect.x(); // + window_rect_.x(); + damage_rect_win.top = damage_rect.y(); // + window_rect_.y(); + damage_rect_win.right = damage_rect_win.left + damage_rect.width(); + damage_rect_win.bottom = damage_rect_win.top + damage_rect.height(); + + // 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); +} +#endif + +void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { + if (!instance()) + return; + + if (window_rect_.IsEmpty()) // wait for geometry to be set. + return; + + DCHECK(instance()->windowless()); + + window_.clipRect.top = clip_rect_.y(); + window_.clipRect.left = clip_rect_.x(); + window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); + window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); + window_.height = window_rect_.height(); + window_.width = window_rect_.width(); + window_.x = window_rect_.x(); + window_.y = window_rect_.y(); + window_.type = NPWindowTypeDrawable; + + NPError err = instance()->NPP_SetWindow(&window_); + DCHECK(err == NPERR_NO_ERROR); +} + +void WebPluginDelegateImpl::SetFocus() { + DCHECK(instance()->windowless()); + + NOTIMPLEMENTED(); + /* NPEvent focus_event; + focus_event.event = WM_SETFOCUS; + focus_event.wParam = 0; + focus_event.lParam = 0; + + instance()->NPP_HandleEvent(&focus_event);*/ +} + +bool WebPluginDelegateImpl::HandleEvent(NPEvent* event, + WebCursor* cursor) { + NOTIMPLEMENTED(); +#if 0 + DCHECK(windowless_) << "events should only be received in windowless mode"; + DCHECK(cursor != NULL); + + // To ensure that the plugin receives keyboard events we set focus to the + // dummy window. + // TODO(iyengar) We need a framework in the renderer to identify which + // windowless plugin is under the mouse and to handle this. This would + // also require some changes in RenderWidgetHost to detect this in the + // WM_MOUSEACTIVATE handler and inform the renderer accordingly. + HWND prev_focus_window = NULL; + if (event->event == WM_RBUTTONDOWN) { + prev_focus_window = ::SetFocus(dummy_window_for_activation_); + } + + if (ShouldTrackEventForModalLoops(event)) { + // A windowless plugin can enter a modal loop in a NPP_HandleEvent call. + // For e.g. Flash puts up a context menu when we right click on the + // windowless plugin area. We detect this by setting up a message filter + // hook pror to calling NPP_HandleEvent on the plugin and unhook on + // return from NPP_HandleEvent. If the plugin does enter a modal loop + // in that context we unhook on receiving the first notification in + // the message filter hook. + handle_event_message_filter_hook_ = + SetWindowsHookEx(WH_MSGFILTER, HandleEventMessageFilterHook, NULL, + GetCurrentThreadId()); + } + + bool old_task_reentrancy_state = + MessageLoop::current()->NestableTasksAllowed(); + + current_plugin_instance_ = this; + + handle_event_depth_++; + + bool pop_user_gesture = false; + + if (IsUserGestureMessage(event->event)) { + pop_user_gesture = true; + instance()->PushPopupsEnabledState(true); + } + + bool ret = instance()->NPP_HandleEvent(event) != 0; + + if (event->event == WM_MOUSEMOVE) { + // 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. + *cursor = current_windowless_cursor_; + } + + 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); + } + + return ret; +#endif + return 0; +} + +WebPluginResourceClient* WebPluginDelegateImpl::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::PluginStream* 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::PluginStreamUrl *stream = instance()->CreateStream(resource_id, + url, + mime_type, + notify_needed, + notify_data); + return stream; +} + +void WebPluginDelegateImpl::URLRequestRouted(const std::string&url, + bool notify_needed, + void* notify_data) { + if (notify_needed) { + instance()->SetURLLoadData(GURL(url.c_str()), notify_data); + } +} + + diff --git a/webkit/glue/webplugin_delegate.h b/webkit/glue/webplugin_delegate.h index 16d9c50..4581a829 100644 --- a/webkit/glue/webplugin_delegate.h +++ b/webkit/glue/webplugin_delegate.h @@ -12,8 +12,10 @@ #include "base/file_path.h" #include "base/gfx/native_widget_types.h" #include "base/gfx/rect.h" +#include "build/build_config.h" #include "third_party/npapi/bindings/npapi.h" +// TODO(port): put in OS_WIN check. typedef struct HDC__* HDC; struct NPObject; @@ -53,12 +55,24 @@ class WebPluginDelegate { virtual void UpdateGeometry(const gfx::Rect& window_rect, const gfx::Rect& clip_rect) = 0; +#if defined(OS_WIN) // Tells the plugin to paint the damaged rect. The HDC is only used for // windowless plugins. virtual void Paint(HDC hdc, const gfx::Rect& rect) = 0; // Tells the plugin to print itself. virtual void Print(HDC hdc) = 0; +#else + // TODO(port): these are not intended to be implementable for now, + // and will have the prototypes fixed once they are implemented. + + // Tells the plugin to paint the damaged rect. The HDC is only used for + // windowless plugins. + virtual void Paint(void* dc, const gfx::Rect& rect) = 0; + + // Tells the plugin to print itself. + virtual void Print(void* dc) = 0; +#endif // Informs the plugin that it now has focus. virtual void SetFocus() = 0; diff --git a/webkit/glue/webplugin_impl.cc b/webkit/glue/webplugin_impl.cc index deb2ff9..2ebe5f5 100644 --- a/webkit/glue/webplugin_impl.cc +++ b/webkit/glue/webplugin_impl.cc @@ -702,19 +702,20 @@ void WebPluginImpl::paint(WebCore::GraphicsContext* gc, static_cast(origin.y())); #if defined(OS_WIN) - // HDC is only used when in windowless mode. - HDC hdc = gc->platformContext()->canvas()->beginPlatformPaint(); + // Note that HDC is only used when in windowless mode. + HDC dc = gc->platformContext()->canvas()->beginPlatformPaint(); #else - NOTIMPLEMENTED(); + // TODO(port): the equivalent of the above. + void* dc = NULL; // Temporary, to reduce ifdefs. #endif WebCore::IntRect window_rect = WebCore::IntRect(view->contentsToWindow(damage_rect.location()), damage_rect.size()); -#if defined(OS_WIN) - delegate_->Paint(hdc, webkit_glue::FromIntRect(window_rect)); + delegate_->Paint(dc, webkit_glue::FromIntRect(window_rect)); +#if defined(OS_WIN) gc->platformContext()->canvas()->endPlatformPaint(); #endif gc->restore(); diff --git a/webkit/tools/test_shell/test_webview_delegate_gtk.cc b/webkit/tools/test_shell/test_webview_delegate_gtk.cc index 831691a..8482080 100644 --- a/webkit/tools/test_shell/test_webview_delegate_gtk.cc +++ b/webkit/tools/test_shell/test_webview_delegate_gtk.cc @@ -22,7 +22,9 @@ #include "webkit/glue/weburlrequest.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/webview.h" +#include "webkit/glue/plugins/plugin_list.h" #include "webkit/glue/window_open_disposition.h" +#include "webkit/glue/plugins/webplugin_delegate_impl.h" #include "webkit/tools/test_shell/test_navigation_controller.h" #include "webkit/tools/test_shell/test_shell.h" @@ -77,8 +79,19 @@ WebPluginDelegate* TestWebViewDelegate::CreatePluginDelegate( const std::string& mime_type, const std::string& clsid, std::string* actual_mime_type) { - NOTIMPLEMENTED(); - return NULL; + bool allow_wildcard = true; + 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.path, *actual_mime_type, + shell_->webViewHost()->view_handle()); + else + return WebPluginDelegateImpl::Create(info.path, mime_type, + shell_->webViewHost()->view_handle()); } void TestWebViewDelegate::ShowJavaScriptAlert(const std::wstring& message) { -- cgit v1.1