1 | /* |
2 | * Copyright (C) 2010 Apple Inc. All rights reserved. |
3 | * Copyright (C) 2010 University of Szeged |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
16 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
18 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
24 | * THE POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | #include "NetscapePluginX11.h" |
29 | |
30 | #if PLATFORM(X11) && ENABLE(NETSCAPE_PLUGIN_API) |
31 | |
32 | #include "NetscapePlugin.h" |
33 | #include "PluginController.h" |
34 | #include "WebEvent.h" |
35 | #include <WebCore/GraphicsContext.h> |
36 | #include <WebCore/NotImplemented.h> |
37 | #include <WebCore/PlatformDisplayX11.h> |
38 | #include <WebCore/XUniquePtr.h> |
39 | |
40 | #if PLATFORM(GTK) |
41 | #include <gtk/gtk.h> |
42 | #ifndef GTK_API_VERSION_2 |
43 | #include <gtk/gtkx.h> |
44 | #endif |
45 | #include <gdk/gdkx.h> |
46 | #include <WebCore/GtkVersioning.h> |
47 | #endif |
48 | |
49 | #if USE(CAIRO) |
50 | #include <WebCore/PlatformContextCairo.h> |
51 | #include <WebCore/RefPtrCairo.h> |
52 | #include <cairo/cairo-xlib.h> |
53 | #endif |
54 | |
55 | namespace WebKit { |
56 | using namespace WebCore; |
57 | |
58 | static inline Display* x11HostDisplay() |
59 | { |
60 | return downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(); |
61 | } |
62 | |
63 | static Display* getPluginDisplay() |
64 | { |
65 | #if PLATFORM(GTK) |
66 | // Since we're a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based |
67 | // plugins, so we can return that. We might want to add other implementations here later. |
68 | return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); |
69 | #else |
70 | return nullptr; |
71 | #endif |
72 | } |
73 | |
74 | static inline int x11Screen() |
75 | { |
76 | #if PLATFORM(GTK) |
77 | return gdk_screen_get_number(gdk_screen_get_default()); |
78 | #else |
79 | return 0; |
80 | #endif |
81 | } |
82 | |
83 | static inline int displayDepth() |
84 | { |
85 | #if PLATFORM(GTK) |
86 | return gdk_visual_get_depth(gdk_screen_get_system_visual(gdk_screen_get_default())); |
87 | #else |
88 | return 0; |
89 | #endif |
90 | } |
91 | |
92 | static inline unsigned long rootWindowID() |
93 | { |
94 | #if PLATFORM(GTK) |
95 | return GDK_ROOT_WINDOW(); |
96 | #else |
97 | return 0; |
98 | #endif |
99 | } |
100 | |
101 | #if PLATFORM(GTK) |
102 | static gboolean socketPlugRemovedCallback(GtkSocket*) |
103 | { |
104 | // Default action is to destroy the GtkSocket, so we just return TRUE here |
105 | // to be able to reuse the socket. For some obscure reason, newer versions |
106 | // of flash plugin remove the plug from the socket, probably because the plug |
107 | // created by the plugin is re-parented. |
108 | return TRUE; |
109 | } |
110 | #endif |
111 | |
112 | std::unique_ptr<NetscapePluginX11> NetscapePluginX11::create(NetscapePlugin& plugin) |
113 | { |
114 | #if PLATFORM(GTK) |
115 | uint64_t windowID = 0; |
116 | #endif |
117 | if (plugin.isWindowed()) { |
118 | #if PLATFORM(GTK) |
119 | // NPPVplugiNeedsXEmbed is a boolean value, but at least the |
120 | // Flash player plugin is using an 'int' instead. |
121 | int needsXEmbed = 0; |
122 | plugin.NPP_GetValue(NPPVpluginNeedsXEmbed, &needsXEmbed); |
123 | if (needsXEmbed) { |
124 | windowID = plugin.controller()->createPluginContainer(); |
125 | if (!windowID) |
126 | return nullptr; |
127 | } else { |
128 | notImplemented(); |
129 | return nullptr; |
130 | } |
131 | #else |
132 | notImplemented(); |
133 | return nullptr; |
134 | #endif |
135 | } |
136 | |
137 | Display* display = getPluginDisplay(); |
138 | if (!display) |
139 | return nullptr; |
140 | |
141 | #if PLATFORM(GTK) |
142 | if (plugin.isWindowed()) |
143 | return std::make_unique<NetscapePluginX11>(plugin, display, windowID); |
144 | #endif |
145 | |
146 | return std::make_unique<NetscapePluginX11>(plugin, display); |
147 | } |
148 | |
149 | NetscapePluginX11::NetscapePluginX11(NetscapePlugin& plugin, Display* display) |
150 | : m_plugin(plugin) |
151 | , m_pluginDisplay(display) |
152 | { |
153 | Display* hostDisplay = x11HostDisplay(); |
154 | int depth = displayDepth(); |
155 | m_setWindowCallbackStruct.display = hostDisplay; |
156 | m_setWindowCallbackStruct.depth = depth; |
157 | |
158 | XVisualInfo visualTemplate; |
159 | visualTemplate.screen = x11Screen(); |
160 | visualTemplate.depth = depth; |
161 | visualTemplate.c_class = TrueColor; |
162 | int numMatching; |
163 | XUniquePtr<XVisualInfo> visualInfo(XGetVisualInfo(hostDisplay, VisualScreenMask | VisualDepthMask | VisualClassMask, &visualTemplate, &numMatching)); |
164 | ASSERT(visualInfo); |
165 | Visual* visual = visualInfo.get()[0].visual; |
166 | ASSERT(visual); |
167 | |
168 | m_setWindowCallbackStruct.type = NP_SETWINDOW; |
169 | m_setWindowCallbackStruct.visual = visual; |
170 | m_setWindowCallbackStruct.colormap = XCreateColormap(hostDisplay, rootWindowID(), visual, AllocNone); |
171 | } |
172 | |
173 | #if PLATFORM(GTK) |
174 | NetscapePluginX11::NetscapePluginX11(NetscapePlugin& plugin, Display* display, uint64_t windowID) |
175 | : m_plugin(plugin) |
176 | , m_pluginDisplay(display) |
177 | { |
178 | // It seems flash needs the socket to be in the same process, |
179 | // I guess it uses gdk_window_lookup(), so we create a new socket here |
180 | // containing a plug with the UI process socket embedded. |
181 | m_platformPluginWidget = gtk_plug_new(static_cast<Window>(windowID)); |
182 | |
183 | // Hide the GtkPlug on delete-event since we assume the widget is valid while the plugin is active. |
184 | // platformDestroy() will be called anyway right after the delete-event. |
185 | g_signal_connect(m_platformPluginWidget, "delete-event" , G_CALLBACK(gtk_widget_hide_on_delete), nullptr); |
186 | |
187 | GtkWidget* socket = gtk_socket_new(); |
188 | // Do not show the plug widget until the socket is connected. |
189 | g_signal_connect_swapped(socket, "plug-added" , G_CALLBACK(gtk_widget_show), m_platformPluginWidget); |
190 | g_signal_connect(socket, "plug-removed" , G_CALLBACK(socketPlugRemovedCallback), nullptr); |
191 | gtk_container_add(GTK_CONTAINER(m_platformPluginWidget), socket); |
192 | gtk_widget_show(socket); |
193 | |
194 | Display* hostDisplay = x11HostDisplay(); |
195 | m_npWindowID = gtk_socket_get_id(GTK_SOCKET(socket)); |
196 | GdkWindow* window = gtk_widget_get_window(socket); |
197 | m_setWindowCallbackStruct.type = NP_SETWINDOW; |
198 | m_setWindowCallbackStruct.display = GDK_WINDOW_XDISPLAY(window); |
199 | m_setWindowCallbackStruct.visual = GDK_VISUAL_XVISUAL(gdk_window_get_visual(window)); |
200 | m_setWindowCallbackStruct.depth = gdk_visual_get_depth(gdk_window_get_visual(window)); |
201 | m_setWindowCallbackStruct.colormap = XCreateColormap(hostDisplay, GDK_ROOT_WINDOW(), m_setWindowCallbackStruct.visual, AllocNone); |
202 | |
203 | XFlush(hostDisplay); |
204 | } |
205 | #endif |
206 | |
207 | NetscapePluginX11::~NetscapePluginX11() |
208 | { |
209 | XFreeColormap(x11HostDisplay(), m_setWindowCallbackStruct.colormap); |
210 | |
211 | m_drawable.reset(); |
212 | |
213 | #if PLATFORM(GTK) |
214 | if (m_platformPluginWidget) |
215 | gtk_widget_destroy(m_platformPluginWidget); |
216 | #endif |
217 | } |
218 | |
219 | NPWindowType NetscapePluginX11::windowType() const |
220 | { |
221 | return m_plugin.isWindowed() ? NPWindowTypeWindow : NPWindowTypeDrawable; |
222 | } |
223 | |
224 | void* NetscapePluginX11::window() const |
225 | { |
226 | #if PLATFORM(GTK) |
227 | return m_plugin.isWindowed() ? GINT_TO_POINTER(m_npWindowID) : nullptr; |
228 | #else |
229 | return nullptr; |
230 | #endif |
231 | } |
232 | |
233 | void NetscapePluginX11::geometryDidChange() |
234 | { |
235 | if (m_plugin.isWindowed()) { |
236 | uint64_t windowID = 0; |
237 | #if PLATFORM(GTK) |
238 | if (!gtk_plug_get_embedded(GTK_PLUG(m_platformPluginWidget))) |
239 | return; |
240 | windowID = static_cast<uint64_t>(GDK_WINDOW_XID(gtk_plug_get_socket_window(GTK_PLUG(m_platformPluginWidget)))); |
241 | #endif |
242 | m_plugin.controller()->windowedPluginGeometryDidChange(m_plugin.frameRectInWindowCoordinates(), m_plugin.clipRect(), windowID); |
243 | return; |
244 | } |
245 | |
246 | m_drawable.reset(); |
247 | if (m_plugin.size().isEmpty()) |
248 | return; |
249 | |
250 | m_drawable = XCreatePixmap(x11HostDisplay(), rootWindowID(), m_plugin.size().width(), m_plugin.size().height(), displayDepth()); |
251 | XSync(x11HostDisplay(), false); // Make sure that the server knows about the Drawable. |
252 | } |
253 | |
254 | void NetscapePluginX11::visibilityDidChange() |
255 | { |
256 | ASSERT(m_plugin.isWindowed()); |
257 | uint64_t windowID = 0; |
258 | #if PLATFORM(GTK) |
259 | if (!gtk_plug_get_embedded(GTK_PLUG(m_platformPluginWidget))) |
260 | return; |
261 | windowID = static_cast<uint64_t>(GDK_WINDOW_XID(gtk_plug_get_socket_window(GTK_PLUG(m_platformPluginWidget)))); |
262 | #endif |
263 | m_plugin.controller()->windowedPluginVisibilityDidChange(m_plugin.isVisible(), windowID); |
264 | m_plugin.controller()->windowedPluginGeometryDidChange(m_plugin.frameRectInWindowCoordinates(), m_plugin.clipRect(), windowID); |
265 | } |
266 | |
267 | void NetscapePluginX11::paint(GraphicsContext& context, const IntRect& dirtyRect) |
268 | { |
269 | ASSERT(!m_plugin.isWindowed()); |
270 | |
271 | if (context.paintingDisabled() || !m_drawable) |
272 | return; |
273 | |
274 | XEvent xevent; |
275 | memset(&xevent, 0, sizeof(XEvent)); |
276 | XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; |
277 | exposeEvent.type = GraphicsExpose; |
278 | exposeEvent.display = x11HostDisplay(); |
279 | exposeEvent.drawable = m_drawable.get(); |
280 | |
281 | IntRect exposedRect(dirtyRect); |
282 | exposeEvent.x = exposedRect.x(); |
283 | exposeEvent.y = exposedRect.y(); |
284 | |
285 | // Note: in transparent mode Flash thinks width is the right and height is the bottom. |
286 | // We should take it into account if we want to support transparency. |
287 | exposeEvent.width = exposedRect.width(); |
288 | exposeEvent.height = exposedRect.height(); |
289 | |
290 | m_plugin.NPP_HandleEvent(&xevent); |
291 | |
292 | if (m_pluginDisplay != x11HostDisplay()) |
293 | XSync(m_pluginDisplay, false); |
294 | |
295 | #if PLATFORM(GTK) |
296 | RefPtr<cairo_surface_t> drawableSurface = adoptRef(cairo_xlib_surface_create(m_pluginDisplay, m_drawable.get(), |
297 | m_setWindowCallbackStruct.visual, m_plugin.size().width(), m_plugin.size().height())); |
298 | cairo_t* cr = context.platformContext()->cr(); |
299 | cairo_save(cr); |
300 | |
301 | cairo_set_source_surface(cr, drawableSurface.get(), 0, 0); |
302 | |
303 | cairo_rectangle(cr, exposedRect.x(), exposedRect.y(), exposedRect.width(), exposedRect.height()); |
304 | cairo_clip(cr); |
305 | cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); |
306 | cairo_paint(cr); |
307 | |
308 | cairo_restore(cr); |
309 | #else |
310 | notImplemented(); |
311 | #endif |
312 | } |
313 | |
314 | static inline void initializeXEvent(XEvent& event) |
315 | { |
316 | memset(&event, 0, sizeof(XEvent)); |
317 | event.xany.serial = 0; |
318 | event.xany.send_event = false; |
319 | event.xany.display = x11HostDisplay(); |
320 | event.xany.window = 0; |
321 | } |
322 | |
323 | static inline uint64_t xTimeStamp(WallTime timestamp) |
324 | { |
325 | return timestamp.secondsSinceEpoch().milliseconds(); |
326 | } |
327 | |
328 | static inline unsigned xKeyModifiers(const WebEvent& event) |
329 | { |
330 | unsigned xModifiers = 0; |
331 | if (event.controlKey()) |
332 | xModifiers |= ControlMask; |
333 | if (event.shiftKey()) |
334 | xModifiers |= ShiftMask; |
335 | if (event.altKey()) |
336 | xModifiers |= Mod1Mask; |
337 | if (event.metaKey()) |
338 | xModifiers |= Mod4Mask; |
339 | |
340 | return xModifiers; |
341 | } |
342 | |
343 | template <typename XEventType, typename WebEventType> |
344 | static inline void setCommonMouseEventFields(XEventType& xEvent, const WebEventType& webEvent, const WebCore::IntPoint& pluginLocation) |
345 | { |
346 | xEvent.root = rootWindowID(); |
347 | xEvent.subwindow = 0; |
348 | xEvent.time = xTimeStamp(webEvent.timestamp()); |
349 | xEvent.x = webEvent.position().x() - pluginLocation.x(); |
350 | xEvent.y = webEvent.position().y() - pluginLocation.y(); |
351 | xEvent.x_root = webEvent.globalPosition().x(); |
352 | xEvent.y_root = webEvent.globalPosition().y(); |
353 | xEvent.state = xKeyModifiers(webEvent); |
354 | xEvent.same_screen = true; |
355 | } |
356 | |
357 | static inline void setXMotionEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) |
358 | { |
359 | XMotionEvent& xMotion = xEvent.xmotion; |
360 | setCommonMouseEventFields(xMotion, webEvent, pluginLocation); |
361 | xMotion.type = MotionNotify; |
362 | } |
363 | |
364 | static inline void setXButtonEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) |
365 | { |
366 | XButtonEvent& xButton = xEvent.xbutton; |
367 | setCommonMouseEventFields(xButton, webEvent, pluginLocation); |
368 | |
369 | xButton.type = (webEvent.type() == WebEvent::MouseDown) ? ButtonPress : ButtonRelease; |
370 | switch (webEvent.button()) { |
371 | case WebMouseEvent::LeftButton: |
372 | xButton.button = Button1; |
373 | break; |
374 | case WebMouseEvent::MiddleButton: |
375 | xButton.button = Button2; |
376 | break; |
377 | case WebMouseEvent::RightButton: |
378 | xButton.button = Button3; |
379 | break; |
380 | default: |
381 | ASSERT_NOT_REACHED(); |
382 | break; |
383 | } |
384 | } |
385 | |
386 | static inline void setXButtonEventFieldsByWebWheelEvent(XEvent& xEvent, const WebWheelEvent& webEvent, const WebCore::IntPoint& pluginLocation) |
387 | { |
388 | XButtonEvent& xButton = xEvent.xbutton; |
389 | setCommonMouseEventFields(xButton, webEvent, pluginLocation); |
390 | |
391 | xButton.type = ButtonPress; |
392 | FloatSize ticks = webEvent.wheelTicks(); |
393 | if (ticks.height()) { |
394 | if (ticks.height() > 0) |
395 | xButton.button = 4; // up |
396 | else |
397 | xButton.button = 5; // down |
398 | } else { |
399 | if (ticks.width() > 0) |
400 | xButton.button = 6; // left |
401 | else |
402 | xButton.button = 7; // right |
403 | } |
404 | } |
405 | |
406 | static inline void setXCrossingEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation, int type) |
407 | { |
408 | XCrossingEvent& xCrossing = xEvent.xcrossing; |
409 | setCommonMouseEventFields(xCrossing, webEvent, pluginLocation); |
410 | |
411 | xCrossing.type = type; |
412 | xCrossing.mode = NotifyNormal; |
413 | xCrossing.detail = NotifyDetailNone; |
414 | xCrossing.focus = false; |
415 | } |
416 | |
417 | bool NetscapePluginX11::handleMouseEvent(const WebMouseEvent& event) |
418 | { |
419 | ASSERT(!m_plugin.isWindowed()); |
420 | |
421 | XEvent xEvent; |
422 | initializeXEvent(xEvent); |
423 | |
424 | switch (event.type()) { |
425 | case WebEvent::MouseDown: |
426 | case WebEvent::MouseUp: |
427 | setXButtonEventFields(xEvent, event, m_plugin.convertToRootView(IntPoint())); |
428 | break; |
429 | case WebEvent::MouseMove: |
430 | setXMotionEventFields(xEvent, event, m_plugin.convertToRootView(IntPoint())); |
431 | break; |
432 | case WebEvent::MouseForceChanged: |
433 | case WebEvent::MouseForceDown: |
434 | case WebEvent::MouseForceUp: |
435 | case WebEvent::NoType: |
436 | case WebEvent::Wheel: |
437 | case WebEvent::KeyDown: |
438 | case WebEvent::KeyUp: |
439 | case WebEvent::RawKeyDown: |
440 | case WebEvent::Char: |
441 | #if ENABLE(TOUCH_EVENTS) |
442 | case WebEvent::TouchStart: |
443 | case WebEvent::TouchMove: |
444 | case WebEvent::TouchEnd: |
445 | case WebEvent::TouchCancel: |
446 | #endif |
447 | return false; |
448 | } |
449 | |
450 | return !m_plugin.NPP_HandleEvent(&xEvent); |
451 | } |
452 | |
453 | // We undefine these constants in npruntime_internal.h to avoid collision |
454 | // with WebKit and platform headers. Values are defined in X.h. |
455 | const int kKeyPressType = 2; |
456 | const int kKeyReleaseType = 3; |
457 | const int kFocusInType = 9; |
458 | const int kFocusOutType = 10; |
459 | |
460 | bool NetscapePluginX11::handleWheelEvent(const WebWheelEvent& event) |
461 | { |
462 | ASSERT(!m_plugin.isWindowed()); |
463 | |
464 | XEvent xEvent; |
465 | initializeXEvent(xEvent); |
466 | setXButtonEventFieldsByWebWheelEvent(xEvent, event, m_plugin.convertToRootView(IntPoint())); |
467 | |
468 | return !m_plugin.NPP_HandleEvent(&xEvent); |
469 | } |
470 | |
471 | void NetscapePluginX11::setFocus(bool focusIn) |
472 | { |
473 | ASSERT(!m_plugin.isWindowed()); |
474 | |
475 | XEvent xEvent; |
476 | initializeXEvent(xEvent); |
477 | XFocusChangeEvent& focusEvent = xEvent.xfocus; |
478 | focusEvent.type = focusIn ? kFocusInType : kFocusOutType; |
479 | focusEvent.mode = NotifyNormal; |
480 | focusEvent.detail = NotifyDetailNone; |
481 | |
482 | m_plugin.NPP_HandleEvent(&xEvent); |
483 | } |
484 | |
485 | bool NetscapePluginX11::handleMouseEnterEvent(const WebMouseEvent& event) |
486 | { |
487 | ASSERT(!m_plugin.isWindowed()); |
488 | |
489 | XEvent xEvent; |
490 | initializeXEvent(xEvent); |
491 | setXCrossingEventFields(xEvent, event, m_plugin.convertToRootView(IntPoint()), EnterNotify); |
492 | |
493 | return !m_plugin.NPP_HandleEvent(&xEvent); |
494 | } |
495 | |
496 | bool NetscapePluginX11::handleMouseLeaveEvent(const WebMouseEvent& event) |
497 | { |
498 | ASSERT(!m_plugin.isWindowed()); |
499 | |
500 | XEvent xEvent; |
501 | initializeXEvent(xEvent); |
502 | setXCrossingEventFields(xEvent, event, m_plugin.convertToRootView(IntPoint()), LeaveNotify); |
503 | |
504 | return !m_plugin.NPP_HandleEvent(&xEvent); |
505 | } |
506 | |
507 | static inline void setXKeyEventFields(XEvent& xEvent, const WebKeyboardEvent& webEvent) |
508 | { |
509 | xEvent.xany.type = (webEvent.type() == WebEvent::KeyDown) ? kKeyPressType : kKeyReleaseType; |
510 | XKeyEvent& xKey = xEvent.xkey; |
511 | xKey.root = rootWindowID(); |
512 | xKey.subwindow = 0; |
513 | xKey.time = xTimeStamp(webEvent.timestamp()); |
514 | xKey.state = xKeyModifiers(webEvent); |
515 | xKey.keycode = webEvent.nativeVirtualKeyCode(); |
516 | |
517 | xKey.same_screen = true; |
518 | |
519 | // Key events propagated to the plugin does not need to have position. |
520 | // source: https://developer.mozilla.org/en/NPEvent |
521 | xKey.x = 0; |
522 | xKey.y = 0; |
523 | xKey.x_root = 0; |
524 | xKey.y_root = 0; |
525 | } |
526 | |
527 | bool NetscapePluginX11::handleKeyboardEvent(const WebKeyboardEvent& event) |
528 | { |
529 | ASSERT(!m_plugin.isWindowed()); |
530 | // We don't generate other types of keyboard events via WebEventFactory. |
531 | ASSERT(event.type() == WebEvent::KeyDown || event.type() == WebEvent::KeyUp); |
532 | |
533 | XEvent xEvent; |
534 | initializeXEvent(xEvent); |
535 | setXKeyEventFields(xEvent, event); |
536 | |
537 | return !m_plugin.NPP_HandleEvent(&xEvent); |
538 | } |
539 | |
540 | } // namespace WebKit |
541 | |
542 | #endif // PLATFORM(X11) && ENABLE(NETSCAPE_PLUGIN_API) |
543 | |