(Modified from original spec proposed by Oliver Yeoh. Modifier: Danielle Pham)
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 (for forward tab focus traversal)
Sent from the browser to plugin when focus should be given to the
plugin's first child widget in the plugin's tab order. Or,
Sent from plugin to the browser when focus should be given to the
first widget in the browser's tab order that is right after the plugin.
NPEventType_GetFocusLastEvent (for reverse tab focus traversal)
Sent from the browser to plugin when focus should be given to the
plugin's last child widget in the plugin's tab order. Or,
Sent from the plugin to the browser when focus should be given to the
last child widget in the browser's tabl order that is right before the
plugin.
1.2. The plugin should query the browser's NPNVSupportsFocusBool to make sure tab focus traversal handling is available on the browser side.
In handling of tab focus events, the browser will not send any NPEventType_GetFocusEvent (existing event type defined in NPAPI) to plugin.
The browser sends the GetFocusFirstEvent to the plugin when tabbing forward and GetFocusLastEvent when tabbing backward into the plugin. This can be done via NPP_HandleEvent(). The plugin should implement NPP_HandleEvent() to handle these events according to the plugin's internal focus system (which is likely platform dependent.)
The underlying keyboard event (tab or modifier+tab) will be dropped, because:
- That tab-key event took place outside of the plugin.
- Its meaning is conveyed in GetFocusFirstEvent or GetFocusLastEvent
- Sending that tab-key event easily confuses the plugin into advancing its internal focus.
Once received the GetFocusFirstEvent or the GetFocusLastEvent, plugin should take over input focus. That is, the plugin should listen to the event loop and forward any unconsumed key-strokes to the browser until it reaches the end of its tab order or until the user mouse-click to steal focus away from the plugin.
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, e.g, when the plugin reaches the end of its tab order, the plugin should send either the GetFocusFirstEvent to the browser when tabbing forward and GetFocusLastEvent when tabbing backward out of the plugin. This can be done via NPN_HandleEvent(). The browser should implement NPN_HandleEvent() to handle these event according to the browsers internal focus system.
The underlying keyboard event (tab or modifier+tab) will be dropped, because:
- That tab-key event took place within the plugin, not the browser.
- Its meaning is now conveyed in GetFocusFirstEvent or GetFocusLastEvent
- Sending that tab-key event easily confuses the browser into advancing its internal focus.
Once received the GetFocusFirstEvent or the GetFocusLastEvent, the browser should take over input focus. That is, the browser should listen to the event loop.
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.
No change to how mouse click events are handled.
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; } }