Two statements of fact first, and then a proposal afterwards.  Please
correct my facts if I'm wrong.

1) On Windows, when you have a windowed plugin and paint the main
window, it synchronously (?) paints the child windows including
plugins.  (This is the major point I'm unsure about; I don't
understand Windows very well.  See
webkit/glue/plugins/test/plugin_create_instance_in_paint.cc [1] for a
test that indicates that this is the problem.)  Because of this, we
can end up with a deadlock when the plugin WM_PAINT handler calls into
renderer javascript which calls back into the browser process (for
example, to query something like the screen size).  We work around
this by handling those synchronous queries on the IO thread, not the
UI thread, in the browser process.

2) On Linux, queries about the screen (and clipboard handling) go via
X and GTK wraps X.  GTK/X aren't implicitly threadsafe so we can't
directly handle the above queries on the IO thread; if we add locks we
recreate the deadlock problem.  The alternative Adam hacked up is an
*additional* thread with its own connection to X, and then we must be
careful to not touch any GTK functions there.  (This is especially
annoying because the point of using toolkits like GTK is that it
provides a nice interface to these functions.)


Ok, those were the facts as I understand them.  Here's the thought:
maybe we don't have this synchronous painting problem on X.  (It seems
strange to me it exists on Windows, maybe because I misunderstand the
problem.)  I created a test app (code attached) that stuffs a child
process with a button in the host process, where clicking the button
makes the child process hang for five seconds.  While the child is
hung, the button obviously doesn't repaint, but you can continue to
resize and observe the main window repainting.

Conclusion, if the above is all correct: we don't need to go through
convolutions to avoid this deadlock problem.
- With little code change, we can proxy those renderer->browser calls
back to the UI thread on Linux only.  r15028 [2] did just that.
- To be cleaner, we could just terminate those calls on the UI thread
(again, only on Linux) using the existing messaging infrastructure.

(PS: I'm not sure if windowless plugins play into this at all.  I
recall there's a potential for a synchronous paint in some
circumstances, but I believe that's synchronous between the renderer
and the plugin, right?)

[1] 
http://src.chromium.org/viewvc/chrome/trunk/src/webkit/glue/plugins/test/plugin_create_instance_in_paint.cc
[2] http://src.chromium.org/viewvc/chrome?view=rev&revision=15028

--~--~---------~--~----~------------~-------~--~----~
Chromium Developers mailing list: [email protected] 
View archives, change email options, or unsubscribe: 
    http://groups.google.com/group/chromium-dev
-~----------~----~----~----~------~----~------~--~---

// g++ -o xembed `pkg-config --cflags --libs gtk+-2.0` xembed.cc

#include <gtk/gtk.h>
#include <stdlib.h>
#include <unistd.h>

// === "Plugin" process ================================================

void hang() {
  // Simulate a plugin hanging to see if it blocks the outer process.
  sleep(5);
}

int plugin_main(const char* arg) {
  GdkNativeWindow xid = (GdkNativeWindow) atoi(arg);
  printf("embed xid %x\n", xid);
  if (xid == 0)
    return 1;

  GtkWidget* plug = gtk_plug_new(xid);
  GtkWidget* button = gtk_button_new_with_label("Hang Plugin");
  g_signal_connect(G_OBJECT(button), "clicked",
                   G_CALLBACK(hang), NULL);
  gtk_container_add(GTK_CONTAINER(plug), button);
  gtk_widget_show_all(plug);

  gtk_main();

  return 0;
}

// === "Host" process ==================================================

void start_plugin(GdkNativeWindow xid, const char* command) {
  pid_t pid = fork();
  if (pid == -1) {
    perror("fork");
    return;
  } else if (pid != 0) {
    gchar* id_str = g_strdup_printf("%d", xid);
    execl(command, command, id_str, NULL);
    g_free(id_str);
  }
}

int plugin_host_main(const char* command) {
  GtkWidget* win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(G_OBJECT(win), "delete-event",
                   G_CALLBACK(gtk_main_quit), NULL);

  GtkWidget* vbox = gtk_vbox_new(FALSE, 6);
  gtk_box_pack_start(GTK_BOX(vbox),
                     gtk_label_new("Some text to test repainting"),
                     FALSE, FALSE, 0);

  GtkWidget* socket = gtk_socket_new();
  gtk_box_pack_start(GTK_BOX(vbox), socket, FALSE, FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox),
                     gtk_label_new("Some more text to test repainting"),
                     FALSE, FALSE, 0);

  gtk_container_add(GTK_CONTAINER(win), vbox);
  gtk_widget_show_all(win);

  // Now that the socket is visible, we can get its X id.
  GdkNativeWindow xid = gtk_socket_get_id(GTK_SOCKET(socket));
  start_plugin(xid, command);

  gtk_main();

  return 0;
}

// === main ============================================================

int main(int argc, char* argv[]) {
  gtk_init(&argc, &argv);

  if (argc > 1)
    return plugin_main(argv[1]);
  return plugin_host_main(argv[0]);
}

Reply via email to