Index: webkit/glue/plugins/webplugin_delegate_impl_gtk.cc |
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 0000000000000000000000000000000000000000..9e98e019e429edb05cde94379f4418dbdc9afb1b |
--- /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); |
+ } |
+} |
+ |
+ |