diff options
Diffstat (limited to 'webkit/glue/plugins/webplugin_delegate_impl_gtk.cc')
-rw-r--r-- | webkit/glue/plugins/webplugin_delegate_impl_gtk.cc | 585 |
1 files changed, 585 insertions, 0 deletions
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 <string> +#include <vector> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#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<NPAPI::PluginLib> plugin = + NPAPI::PluginLib::CreatePluginLib(filename); + if (plugin.get() == NULL) + return NULL; + + NPError err = plugin->NP_Initialize(); + if (err != NPERR_NO_ERROR) + return NULL; + + scoped_refptr<NPAPI::PluginInstance> instance = + plugin->CreateInstance(mime_type); + return new WebPluginDelegateImpl(containing_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<WNDPROC>( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc == NativeWndProc) { + SetWindowLongPtr(windowed_handle_, + GWLP_WNDPROC, + reinterpret_cast<LONG>(plugin_wnd_proc_)); + } + + DestroyWindow(windowed_handle_); + windowed_handle_ = 0; + } +#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<WNDPROC>( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc != NativeWndProc) { + plugin_wnd_proc_ = reinterpret_cast<WNDPROC>(SetWindowLongPtr( + windowed_handle_, GWLP_WNDPROC, reinterpret_cast<LONG>(NativeWndProc))); + } +#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<StatsRate> 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<NPAPI::PluginStream*>(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); + } +} + + |