зеркало из https://github.com/mozilla/gecko-dev.git
Don't allow gtk's event loop to run while waiting for clipboard operations to complete. This prevents a whole mess of problems related to dispatching focus events in the middle of dispatching blur events, or vice versa. Bug 214583, r=blizzard. Not part of the default build.
This commit is contained in:
Родитель
f910b4d9b7
Коммит
0536e92d07
|
@ -48,6 +48,14 @@
|
|||
#include <gtk/gtkclipboard.h>
|
||||
#include <gtk/gtkinvisible.h>
|
||||
|
||||
// For manipulation of the X event queue
|
||||
#include <X11/Xlib.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Callback when someone asks us for the selection
|
||||
void
|
||||
invisible_selection_get_cb (GtkWidget *aWidget,
|
||||
|
@ -70,6 +78,34 @@ ConvertHTMLtoUCS2 (guchar *data,
|
|||
static void
|
||||
GetHTMLCharset (guchar * data, PRInt32 dataLength, nsCString& str);
|
||||
|
||||
|
||||
// Our own versions of gtk_clipboard_wait_for_contents and
|
||||
// gtk_clipboard_wait_for_text, which don't run the event loop while
|
||||
// waiting for the data. This prevents a lot of problems related to
|
||||
// dispatching events at unexpected times.
|
||||
|
||||
static GtkSelectionData *
|
||||
wait_for_contents (GtkClipboard *clipboard, GdkAtom target);
|
||||
|
||||
static gchar *
|
||||
wait_for_text (GtkClipboard *clipboard);
|
||||
|
||||
static Bool
|
||||
checkEventProc(Display *display, XEvent *event, XPointer arg);
|
||||
|
||||
static void
|
||||
WaitForRetrieval(GtkClipboard *clipboard, void **transferData);
|
||||
|
||||
static void
|
||||
clipboard_contents_received(GtkClipboard *clipboard,
|
||||
GtkSelectionData *selection_data,
|
||||
gpointer data);
|
||||
|
||||
static void
|
||||
clipboard_text_received(GtkClipboard *clipboard,
|
||||
const gchar *text,
|
||||
gpointer data);
|
||||
|
||||
nsClipboard::nsClipboard()
|
||||
{
|
||||
mWidget = nsnull;
|
||||
|
@ -216,10 +252,10 @@ nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
|
|||
// Special case text/unicode since we can convert any
|
||||
// string into text/unicode
|
||||
if (!strcmp(flavorStr, kUnicodeMime)) {
|
||||
gchar *new_text = gtk_clipboard_wait_for_text(clipboard);
|
||||
gchar* new_text = wait_for_text(clipboard);
|
||||
if (new_text) {
|
||||
// Convert utf-8 into our unicode format.
|
||||
nsString ucs2string = NS_ConvertUTF8toUCS2(new_text);
|
||||
NS_ConvertUTF8toUCS2 ucs2string(new_text);
|
||||
data = (guchar *)ToNewUnicode(ucs2string);
|
||||
length = ucs2string.Length() * 2;
|
||||
g_free(new_text);
|
||||
|
@ -237,8 +273,7 @@ nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
|
|||
// the clipboard.
|
||||
GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
|
||||
GtkSelectionData *selectionData;
|
||||
selectionData = gtk_clipboard_wait_for_contents(clipboard,
|
||||
atom);
|
||||
selectionData = wait_for_contents(clipboard, atom);
|
||||
if (selectionData) {
|
||||
length = selectionData->length * selectionData->format / 8;
|
||||
// Special case text/html since we can convert into UCS2
|
||||
|
@ -393,9 +428,7 @@ GtkSelectionData *
|
|||
nsClipboard::GetTargets(GdkAtom aWhichClipboard)
|
||||
{
|
||||
GtkClipboard *clipboard = gtk_clipboard_get(aWhichClipboard);
|
||||
return gtk_clipboard_wait_for_contents (clipboard,
|
||||
gdk_atom_intern ("TARGETS",
|
||||
FALSE));
|
||||
return wait_for_contents(clipboard, gdk_atom_intern("TARGETS", FALSE));
|
||||
}
|
||||
|
||||
nsITransferable *
|
||||
|
@ -695,3 +728,154 @@ void GetHTMLCharset(guchar * data, PRInt32 dataLength, nsCString& str)
|
|||
str.Assign(NS_LITERAL_CSTRING("UNKNOWN"));
|
||||
}
|
||||
|
||||
static void
|
||||
DispatchSelectionNotifyEvent(GdkDisplay *display, GtkWidget *widget,
|
||||
XEvent *xevent)
|
||||
{
|
||||
GdkEvent event;
|
||||
event.selection.type = GDK_SELECTION_NOTIFY;
|
||||
event.selection.window = widget->window;
|
||||
event.selection.selection = gdk_x11_xatom_to_atom_for_display(display, xevent->xselection.selection);
|
||||
event.selection.target = gdk_x11_xatom_to_atom_for_display(display, xevent->xselection.target);
|
||||
event.selection.property = gdk_x11_xatom_to_atom_for_display(display, xevent->xselection.property);
|
||||
event.selection.time = xevent->xselection.time;
|
||||
|
||||
gtk_widget_event(widget, &event);
|
||||
}
|
||||
|
||||
static void
|
||||
DispatchPropertyNotifyEvent(GdkDisplay *display, GtkWidget *widget,
|
||||
XEvent *xevent)
|
||||
{
|
||||
if (((GdkWindowObject *) widget->window)->event_mask & GDK_PROPERTY_CHANGE_MASK) {
|
||||
GdkEvent event;
|
||||
event.property.type = GDK_PROPERTY_NOTIFY;
|
||||
event.property.window = widget->window;
|
||||
event.property.atom = gdk_x11_xatom_to_atom_for_display(display, xevent->xproperty.atom);
|
||||
event.property.time = xevent->xproperty.time;
|
||||
event.property.state = xevent->xproperty.state;
|
||||
|
||||
gtk_widget_event(widget, &event);
|
||||
}
|
||||
}
|
||||
|
||||
struct checkEventContext
|
||||
{
|
||||
Window window;
|
||||
Atom selAtom;
|
||||
};
|
||||
|
||||
static Bool
|
||||
checkEventProc(Display *display, XEvent *event, XPointer arg)
|
||||
{
|
||||
checkEventContext *context = (checkEventContext *) arg;
|
||||
|
||||
return (event->xany.window == context->window &&
|
||||
(event->xany.type == SelectionNotify ||
|
||||
(event->xany.type == PropertyNotify &&
|
||||
event->xproperty.atom == context->selAtom)));
|
||||
}
|
||||
|
||||
// Idle timeout for receiving selection and property notify events (microsec)
|
||||
static const int kClipboardTimeout = 500000;
|
||||
|
||||
static void
|
||||
WaitForRetrieval(GtkClipboard *clipboard, void **transferData)
|
||||
{
|
||||
if (*transferData) // the request completed synchronously
|
||||
return;
|
||||
|
||||
GdkDisplay *cbDisplay = gtk_clipboard_get_display(clipboard);
|
||||
Display *xDisplay = gdk_x11_display_get_xdisplay(cbDisplay);
|
||||
|
||||
GtkWidget *cbWidget = NS_STATIC_CAST(GtkWidget *,
|
||||
g_object_get_data(G_OBJECT(cbDisplay),
|
||||
"gtk-clipboard-widget"));
|
||||
|
||||
checkEventContext context;
|
||||
context.window = GDK_WINDOW_XWINDOW(cbWidget->window);
|
||||
context.selAtom = gdk_x11_atom_to_xatom_for_display(cbDisplay,
|
||||
gdk_atom_intern("GDK_SELECTION", FALSE));
|
||||
|
||||
// Send X events which are relevant to the ongoing selection retrieval
|
||||
// to the clipboard widget. Wait until either the operation completes, or
|
||||
// we hit our timeout. All other X events remain queued.
|
||||
|
||||
int cnumber;
|
||||
#ifdef VMS
|
||||
cnumber = XConnectionNumber(xDisplay);
|
||||
#else
|
||||
cnumber = ConnectionNumber(xDisplay);
|
||||
#endif
|
||||
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(cnumber++, &rfds);
|
||||
|
||||
do {
|
||||
XEvent xevent;
|
||||
|
||||
while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
|
||||
(XPointer) &context)) {
|
||||
|
||||
if (xevent.xany.type == SelectionNotify)
|
||||
DispatchSelectionNotifyEvent(cbDisplay, cbWidget, &xevent);
|
||||
else
|
||||
DispatchPropertyNotifyEvent(cbDisplay, cbWidget, &xevent);
|
||||
|
||||
if (*transferData)
|
||||
return;
|
||||
}
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = kClipboardTimeout;
|
||||
|
||||
} while (select(cnumber, &rfds, NULL, NULL, &tv) == 1);
|
||||
|
||||
#ifdef DEBUG_CLIPBOARD
|
||||
printf("exceeded clipboard timeout\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_contents_received(GtkClipboard *clipboard,
|
||||
GtkSelectionData *selection_data,
|
||||
gpointer data)
|
||||
{
|
||||
GtkSelectionData **sel_copy = NS_STATIC_CAST(GtkSelectionData **, data);
|
||||
*sel_copy = gtk_selection_data_copy(selection_data);
|
||||
}
|
||||
|
||||
|
||||
static GtkSelectionData *
|
||||
wait_for_contents(GtkClipboard *clipboard, GdkAtom target)
|
||||
{
|
||||
GtkSelectionData *selection_data = NULL;
|
||||
gtk_clipboard_request_contents(clipboard, target,
|
||||
clipboard_contents_received,
|
||||
&selection_data);
|
||||
|
||||
WaitForRetrieval(clipboard, (void **) &selection_data);
|
||||
return selection_data;
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_text_received(GtkClipboard *clipboard,
|
||||
const gchar *text,
|
||||
gpointer data)
|
||||
{
|
||||
gchar **text_copy = NS_STATIC_CAST(gchar **, data);
|
||||
*text_copy = g_strdup(text);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
wait_for_text(GtkClipboard *clipboard)
|
||||
{
|
||||
gchar *text = NULL;
|
||||
gtk_clipboard_request_text(clipboard, clipboard_text_received, &text);
|
||||
|
||||
WaitForRetrieval(clipboard, (void **) &text);
|
||||
return text;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче