NPPVpluginSupportsFocusBool
Plugin side variable which must be set to true if it can be focused by
keyboard navigation.
NPNVSupportsFocusBool
Browser side variable that plugins can query if tab focus integration is
available.
NPEventType_GetFocusFirstEvent
Sent from the browser to plugins when focus should be given to the
plugin's first child widget.
NPEventType_GetFocusLastEvent
Sent from the browser to plugins when focus should be given to the
plugin's last child widget.
The browser sends the GetFocusFirst event to the plugin when tabbing forward and GetFocusLast when tabbing backwards into the plugin. The plugin should handle these events accordingly to place its internal focus.
XEmbed plugins do not need to handle these events. XEmbed plugins will receive XEMBED messages which should be automatically handled by the plugin's toolkit.
When user tab navigates out of the plugin, the plugin should send a tab or shift-tab keystroke to the browser.
XEmbed plugins need not to do this as it's toolkit should be sending the required XEmbed messages when tabbing out of the plugin. However, XEmbed plugins should still forward other unconsumed keystrokes to the browser.
NPBool nsPluginInstance::init(NPWindow* aWindow) { .... // Save the subclassed WNDPROC. We need it to forward unused keystrokes // back to the browser. mOldWndProc = SubclassWindow(mhWnd, (WNDPROC)PluginWinProc); } LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_KEYDOWN: if (!IsKeyUseful(wParam) { // We don't need this key. Forward to browser. return mOldWndProc(hWnd, msg, wParam, lParam); } // We need this key. Consume it. ... return 0; } }For windowless plugins, the plugin should simply return false when processing the WM_KEYDOWN in the NPP_HandleEvent() function.
NPError nsPluginInstance::SetWindow(NPWindow* aWindow) { GtkPlug *plug = gtk_plug_new((XID)aWindow->window); g_signal_connect_after(plug, "key_press_event", G_CALLBACK(plug_key_press_event_cb), NULL); ... } gboolean plug_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { // forward unconsumed keystrokes to embedder XEvent xevent; GtkPlug *plug = GTK_PLUG(widget); GdkScreen *screen = gdk_drawable_get_screen (plug->socket_window); xevent.xkey.type = KeyPress; xevent.xkey.window = GDK_WINDOW_XWINDOW(plug->socket_window); xevent.xkey.root = GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(screen)); xevent.xkey.subwindow = None; xevent.xkey.time = event->time; xevent.xkey.x = 0; xevent.xkey.y = 0; xevent.xkey.x_root = 0; xevent.xkey.y_root = 0; xevent.xkey.state = event->state; xevent.xkey.keycode = event->hardware_keycode; xevent.xkey.same_screen = True; XSendEvent (GDK_WINDOW_XDISPLAY (plug->socket_window), GDK_WINDOW_XWINDOW (plug->socket_window), False, NoEventMask, &xevent); gdk_display_sync(gdk_screen_get_display (screen)); return TRUE; }For X11 plugins, the unused keystrokes should be forwarded to the plugin's parent window.
void xt_event_handler(Widget xtwidget, nsPluginInstance *plugin, XEvent *xevent) { switch (xevent->type) { case KeyPress: if (!IsUsefulKey(xevent->xkey)) { /* we don't need this key. forward it to the embedder. */ Window parentWindow = 0, rootWindow = 0, *childWindows; unsigned int childCount; XQueryTree(mDisplay, mWindow, &rootWindow, &parentWindow, &childWindows, &childCount); XFree(childWindows); XEvent event; event.xkey.type = xevent->type; event.xkey.window = parentWindow; event.xkey.root = rootWindow; event.xkey.subwindow = None; event.xkey.time = xevent->xkey.time; event.xkey.x = 0; event.xkey.y = 0; event.xkey.x_root = 0; event.xkey.y_root = 0; event.xkey.state = xevent->xkey.state; event.xkey.keycode = xevent->xkey.keycode; event.xkey.same_screen = True; XSendEvent(mDisplay, parentWindow, False, NoEventMask, &event); return; } ... break; } }