NPAPI Changes

Variables

Events

Plugin side changes

Enable tab navigation support

The plugin should return true when the browser queries its NPPVpluginSupportsFocusBool value.

Handle the tab focus events

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.

Tabbing out of the plugin

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.

Forward unused keystrokes to the browser

Windows

For windowed plugins, the plugin forwards unconsumed WM_KEYDOWN messages to the subclassed window procedure.
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.

Linux

For XEmbed plugins, unused keystrokes should be forwarded to the socket window.
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;
  }
}
    

Mac

The plugin should simply return false when processing the keyDown event in the NPP_HandleEvent() function.