From a1df65b34a42937a5381612c5000a7817afcf826 Mon Sep 17 00:00:00 2001 From: Mathias Hasselmann Date: Sun, 6 Dec 2009 20:46:21 +0100 Subject: WIP --- Makefile.am | 23 +++- configure.ac | 8 +- src/facebook-connection.c | 2 +- src/login.c | 172 ++++++++++++++++++++++++ tests/test-browser.cc | 336 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 533 insertions(+), 8 deletions(-) create mode 100644 src/login.c create mode 100644 tests/test-browser.cc diff --git a/Makefile.am b/Makefile.am index 8302211..a0ae24f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,15 @@ -AM_CFLAGS = -Wall -Werror -Wmissing-prototypes -AM_CPPFLAGS = -DLIBEXECDIR=\"$(libexecdir)\" ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = -Wall -Werror -Wmissing-prototypes +AM_CXXFLAGS = -Wall -Werror +AM_CPPFLAGS = -DMOZILLA_LIBDIR=\"$(mozilla_libdir)\" \ + -DTELEPATHY_LIBDIR=\"$(telepathy_libdir)\" + telepathy_libdir=$(libdir)/telepathy -telepathy_lib_PROGRAMS = src/telepathy-gruschler -libexec_PROGRAMS = src/telepathy-gruschler-login +telepathy_lib_PROGRAMS = src/telepathy-gruschler \ + src/telepathy-gruschler-login + +mozilla_libdir = @mozilla_libdir@ src_telepathy_gruschler_CFLAGS = $(AM_CFLAGS) $(GRUSCHLER_CFLAGS) src_telepathy_gruschler_LDADD = $(AM_LDFLAGS) $(GRUSCHLER_LIBS) @@ -17,5 +22,13 @@ src_telepathy_gruschler_SOURCES = src/connection-manager.c \ src/main.c src_telepathy_gruschler_login_CFLAGS = $(AM_CFLAGS) $(LOGIN_CFLAGS) -src_telepathy_gruschler_login_LDADD = $(AM_LDFLAGS) $(LOGIN_LIBS) +src_telepathy_gruschler_login_LDFLAGS = $(AM_LDFLAGS) -rpath $(mozilla_libdir) +src_telepathy_gruschler_login_LDADD = $(LOGIN_LIBS) src_telepathy_gruschler_login_SOURCES = src/login.c + +noinst_PROGRAMS = tests/test-browser +tests_test_browser_CXXFLAGS = $(AM_CXXFLAGS) $(LOGIN_CFLAGS) -fshort-wchar +tests_test_browser_LDFLAGS = $(AM_LDFLAGS) -rpath $(mozilla_libdir) +tests_test_browser_LDADD = $(LOGIN_LIBS) -L/usr/lib/microb-engine-devel/lib -lxpcomglue_s +tests_test_browser_SOURCES = tests/test-browser.cc + diff --git a/configure.ac b/configure.ac index d00d457..bd731ac 100644 --- a/configure.ac +++ b/configure.ac @@ -17,8 +17,12 @@ PKG_CHECK_MODULES([GRUSCHLER], rtcom-telepathy-glib >= 0.1.38 telepathy-glib >= 0.7.35]) PKG_CHECK_MODULES([LOGIN], - [gtkembedmoz >= 1.7 - gtk+-2.0 >= 2.14]) + [gtk+-2.0 >= 2.14 + hildon-1 >= 2.2]) + +LOGIN_CFLAGS="$LOGIN_CFLAGS -I/usr/lib/microb-engine-devel/include" +LOGIN_LIBS="$LOGIN_LIBS -lgtkembedmoz" +AC_SUBST([mozilla_libdir],[/usr/lib/microb-engine]) FACEBOOK_APPKEY([GRUSCHLER]) diff --git a/src/facebook-connection.c b/src/facebook-connection.c index 8883354..914be3a 100644 --- a/src/facebook-connection.c +++ b/src/facebook-connection.c @@ -1150,7 +1150,7 @@ static gboolean _run_login_helper (GruschlerFacebookConnection *self, GError **error) { - char *argv[] = { LIBEXECDIR "/telepathy-gruschler-login", NULL }; + char *argv[] = { TELEPATHY_LIBDIR "/telepathy-gruschler-login", NULL }; int fd; g_return_val_if_fail (0 == self->priv->login_helper_pid, FALSE); diff --git a/src/login.c b/src/login.c new file mode 100644 index 0000000..1d03eae --- /dev/null +++ b/src/login.c @@ -0,0 +1,172 @@ +#include "config.h" + +#include +//FIXME #include + +#include + +typedef struct { + const char *api_key; + const char *next_url; + const char *cancel_url; + const char *permissions; +} AppData; + +static char * +get_login_url (AppData *app) +{ + char *args[] = { + g_uri_escape_string (app->api_key, NULL, TRUE), + g_uri_escape_string (app->next_url, NULL, TRUE), + g_uri_escape_string (app->cancel_url, NULL, TRUE), + g_uri_escape_string (app->permissions, NULL, TRUE) + }; + + char *url; + int i; + + url = g_strdup_printf ("http://www.facebook.com/login.php?v=1.0&" + "fbconnect=true&display=popup&return_session=true&" + "api_key=%s&next=%s&cancel_url=%s&req_perm=%s", + args[0], args[1], args[2], args[3]); + + for (i = 0; i < G_N_ELEMENTS (args); ++i) + g_free (args[i]); + + return url; +} + +static char * +get_session (const char *query) +{ + const char *start, *end; + + if ('?' != query[0] || + NULL == (start = strstr (query, "session=")) || + NULL == strchr ("?&", start[-1])) + return NULL; + + start += strlen ("session="); + + if (!(end = strchr (start, '&'))) + end = query + strlen (query); + + return g_uri_unescape_segment (start, end, NULL); +} + +static void +location_cb (GtkMozEmbed *embed, + AppData *app) +{ + const char *location; + + location = gtk_moz_embed_get_location (embed); + + if (!strcmp (location, app->cancel_url)) + { + gtk_main_quit (); + return; + } + + if (g_str_has_prefix (location, app->next_url)) + { + char *session; + + session = get_session (location + strlen (app->next_url)); + + if (session) + g_print ("GRUSCHLER-SESSION:%s\n", session); + + g_free (session); + gtk_main_quit (); + + return; + } +} + +#if 0 +static void +set_boolean (char *key, + gboolean value) +{ + gtk_moz_embed_common_set_pref (G_TYPE_BOOLEAN, key, &value); +} + +static void +set_integer (char *key, + int value) +{ + gtk_moz_embed_common_set_pref (G_TYPE_INT, key, &value); +} +#endif + +int +main (int argc, + char **argv) +{ + AppData app = { + GRUSCHLER_FACEBOOK_APIKEY, + "http://www.facebook.com/connect/login_success.html", + "http://www.facebook.com/connect/login_failure.html", + "offline_access", + }; + + GOptionEntry options[] = { + { "api-key", 0, 0, G_OPTION_ARG_STRING, &app.api_key, + "Facebook API key of the application", "KEY" }, + { "next-url", 0, 0, G_OPTION_ARG_STRING, &app.next_url, + "URL of the page to visit after successful login", "URL" }, + { "cancel-url", 0, 0, G_OPTION_ARG_STRING, &app.next_url, + "URL of the page to visit when login was aborted", "URL" }, + { "permissions", 0, 0, G_OPTION_ARG_STRING, &app.permissions, + "Comma separated list of requested permissions", "LIST" }, + { NULL, } + }; + + GtkWidget *window, *embed; + GError *error = NULL; + unsigned chrome; + char *url; + + if (!gtk_init_with_args (&argc, &argv, NULL, options, NULL, &error)) + { + g_printerr ("Usage Error: %s\n", error->message); + g_error_free (error); + return 2; + } + + gtk_moz_embed_set_path (MOZILLA_LIBDIR); + +#if 0 + set_boolean ("gtkmozembed.no_destroy_on_last_window", TRUE); + set_integer ("gtkmozembed.kinetic.type", 1); + + gtk_moz_embed_common_save_prefs (); +#endif + + embed = gtk_moz_embed_new (); + + chrome = gtk_moz_embed_get_chrome_mask (GTK_MOZ_EMBED (embed)); +g_print ("%x\n", chrome); + chrome &= ~GTK_MOZ_EMBED_FLAG_SCROLLBARSON; +g_print ("%x\n", chrome); + gtk_moz_embed_set_chrome_mask (GTK_MOZ_EMBED (embed), chrome); + + + g_signal_connect (embed, "location", G_CALLBACK (location_cb), &app); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); + gtk_container_add (GTK_CONTAINER (window), embed); + gtk_widget_show_all (window); + + url = get_login_url (&app); + url = g_strdup ("http://www.spiegel.de/"); + g_print ("loading %s\n", url); + gtk_moz_embed_load_url (GTK_MOZ_EMBED (embed), url); + g_free (url); + + gtk_main (); + + return 0; +} diff --git a/tests/test-browser.cc b/tests/test-browser.cc new file mode 100644 index 0000000..3e50853 --- /dev/null +++ b/tests/test-browser.cc @@ -0,0 +1,336 @@ +#include "config.h" + +#define MOZILLA_CLIENT + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +static gboolean +expose_event_cb (GtkWidget *widget, GdkEventExpose *event) +{ + + cairo_t *cr = gdk_cairo_create (widget->window); + + GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget)); + gdk_cairo_set_source_pixmap (cr, child->window, child->allocation.x, child->allocation.y); + + /* draw no more than our expose event intersects our child */ + GdkRegion *region = gdk_region_rectangle (&child->allocation); + gdk_region_intersect (region, event->region); + gdk_cairo_region (cr, region); + cairo_clip (cr); + + /* composite, with a 50% opacity */ + cairo_paint_with_alpha (cr, 254./255); + + /* scollbars */ + cairo_set_source_rgba (cr, 0.8, 0.8, 0.8, 0.8); + + cairo_rectangle (cr, + child->allocation.x + child->allocation.width - + HILDON_MARGIN_DEFAULT - HILDON_MARGIN_HALF, + child->allocation.y + HILDON_MARGIN_HALF, + HILDON_MARGIN_DEFAULT, + 0.6 * (child->allocation.height - HILDON_MARGIN_DOUBLE)); + cairo_rectangle (cr, + child->allocation.x + HILDON_MARGIN_HALF, + child->allocation.y + child->allocation.height - + HILDON_MARGIN_DEFAULT - HILDON_MARGIN_HALF, + 0.6 * (child->allocation.width - HILDON_MARGIN_DOUBLE), + HILDON_MARGIN_DEFAULT); + + cairo_fill (cr); + + cairo_destroy (cr); + return FALSE; +} + +static GdkEvent *last_button_event = NULL; + +#if 0 +#include + +static const char * +event_name (GdkEventType type) +{ + static GEnumClass *enum_class = NULL; + GEnumValue *value; + + if (!enum_class) + enum_class = static_cast (g_type_class_ref (GDK_TYPE_EVENT_TYPE)); + + value = g_enum_get_value (enum_class, type); + return (value ? value->value_nick : "unknown"); +} + +static void +send_motion_event (GtkWidget *widget, GdkEventMotion *event) +{ + XEvent xev; + + xev.xmotion.type = MotionNotify; + xev.xmotion.serial = 0; + xev.xmotion.send_event = True; + xev.xmotion.display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)); + xev.xmotion.window = GDK_WINDOW_XWINDOW (gtk_widget_get_window (widget)); + xev.xmotion.root = GDK_WINDOW_XWINDOW (gtk_widget_get_root_window (widget)); + xev.xmotion.subwindow = GDK_WINDOW_XWINDOW (gtk_widget_get_window (widget)); + xev.xmotion.time = event->time; + xev.xmotion.x = event->x; + xev.xmotion.y = event->y; + xev.xmotion.x_root = event->x_root; + xev.xmotion.y_root = event->y_root; + xev.xmotion.same_screen = True; + + xev.xmotion.state = event->state; + xev.xmotion.is_hint = event->is_hint; + + XSendEvent(xev.xbutton.display, xev.xbutton.window, True, 0, &xev); +} + +static void +send_crossing_event (GtkWidget *widget, GdkEventCrossing *event) +{ + XEvent xev; + + switch (event->type) + { + case GDK_ENTER_NOTIFY: + xev.xcrossing.type = EnterNotify; + break; + case GDK_LEAVE_NOTIFY: + xev.xcrossing.type = LeaveNotify; + break; + default: + g_return_if_reached (); + } + + xev.xcrossing.serial = 0; + xev.xcrossing.send_event = True; + xev.xcrossing.display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)); + xev.xcrossing.window = GDK_WINDOW_XWINDOW (gtk_widget_get_window (widget)); + xev.xcrossing.root = GDK_WINDOW_XWINDOW (gtk_widget_get_root_window (widget)); + xev.xcrossing.subwindow = GDK_WINDOW_XWINDOW (gtk_widget_get_window (widget)); + xev.xcrossing.time = event->time; + xev.xcrossing.x = event->x; + xev.xcrossing.y = event->y; + xev.xcrossing.x_root = event->x_root; + xev.xcrossing.y_root = event->y_root; + xev.xcrossing.same_screen = True; + + xev.xcrossing.mode = event->mode; + xev.xcrossing.detail = event->detail; + xev.xcrossing.focus = event->focus; + xev.xcrossing.state = event->state; + + XSendEvent(xev.xbutton.display, xev.xbutton.window, True, 0, &xev); +} + +static gboolean +event_cb (GtkWidget *widget, GdkEvent *event) +{ + GtkWidget *child; + + g_print ("%s: %s event (%d) for %s (%p/%p)\n", + G_STRFUNC, event_name (event->type), + event->type, gtk_widget_get_name (widget), + widget->window, event->any.window); + + if (!GTK_IS_BIN (widget) || widget->window != event->any.window) + return FALSE; + + child = gtk_bin_get_child (GTK_BIN (widget)); + + switch (event->type) + { + case GDK_MOTION_NOTIFY: + send_motion_event (child, &event->motion); + break; + + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + send_crossing_event (child, &event->crossing); + break; + + default: + break; + } + + return FALSE; +} +#endif + +static void +send_mouse_event (GtkWidget *embed, GdkEvent *event, const nsAString &name) +{ + nsCOMPtr browser; + gtk_moz_embed_get_nsIWebBrowser (GTK_MOZ_EMBED (embed), getter_AddRefs (browser)); + + nsCOMPtr window; + browser->GetContentDOMWindow (getter_AddRefs (window)); + + nsCOMPtr utils (do_GetInterface (window)); + + utils->SendMouseEvent(name, + float(event->button.x), float(event->button.y), + event->button.button, 1, 0, PR_FALSE); +} + +static gboolean +button_press_cb (GtkWidget *widget, GdkEvent *event) +{ + g_print ("%s: event-type=%d\n", G_STRFUNC, event->type); + + if (last_button_event) + gdk_event_free (last_button_event); + + last_button_event = gdk_event_copy (event); + + GtkWidget *embed = gtk_bin_get_child (GTK_BIN (widget)); + send_mouse_event (embed, event, NS_LITERAL_STRING("mousedown")); + + return TRUE; +} + +static gboolean +button_release_cb (GtkWidget *widget, GdkEvent *event) +{ + g_print ("%s: event-type=%d, %p %p\n", G_STRFUNC, event->type, widget->window, event->any.window); + + GtkWidget *embed = gtk_bin_get_child (GTK_BIN (widget)); + send_mouse_event (embed, event, NS_LITERAL_STRING("mouseup")); + + if (last_button_event) + { + int dx = abs (event->button.x - last_button_event->button.x); + int dy = abs (event->button.y - last_button_event->button.y); + + if (3 >= dx && 3 >= dy) + { + g_print ("foo\n"); + send_mouse_event (embed, event, NS_LITERAL_STRING("click")); +#if 0 +XEvent xev; +xev.xbutton.type = ButtonPress; +xev.xbutton.serial = 0; +xev.xbutton.send_event = True; +xev.xbutton.display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (child)); +xev.xbutton.window = GDK_WINDOW_XWINDOW (gtk_widget_get_window (child)); +xev.xbutton.root = GDK_WINDOW_XWINDOW (gtk_widget_get_root_window (child)); +xev.xbutton.subwindow = GDK_WINDOW_XWINDOW (gtk_widget_get_window (child)); +xev.xbutton.time = last_button_event->button.time; +xev.xbutton.x = last_button_event->button.x; +xev.xbutton.y = last_button_event->button.y; +xev.xbutton.x_root = last_button_event->button.x_root; +xev.xbutton.y_root = last_button_event->button.y_root; +xev.xbutton.state = last_button_event->button.state; +xev.xbutton.button = last_button_event->button.button; +xev.xbutton.same_screen = True; +XSendEvent(xev.xbutton.display, xev.xbutton.window, True, 0, &xev); + +xev.xbutton.type = ButtonRelease; +xev.xbutton.serial = 0; +xev.xbutton.send_event = True; +xev.xbutton.display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (child)); +xev.xbutton.window = GDK_WINDOW_XWINDOW (gtk_widget_get_window (child)); +xev.xbutton.root = GDK_WINDOW_XWINDOW (gtk_widget_get_root_window (child)); +xev.xbutton.subwindow = GDK_WINDOW_XWINDOW (gtk_widget_get_window (child)); +xev.xbutton.time = event->button.time; +xev.xbutton.x = event->button.x; +xev.xbutton.y = event->button.y; +xev.xbutton.x_root = event->button.x_root; +xev.xbutton.y_root = event->button.y_root; +xev.xbutton.state = event->button.state; +xev.xbutton.button = event->button.button; +xev.xbutton.same_screen = True; +XSendEvent(xev.xbutton.display, xev.xbutton.window, True, 0, &xev); +#endif +#if 0 + last_button_event->button.window = child->window; + gtk_widget_event (child, last_button_event); + + event = gdk_event_copy (event); + event->button.window = child->window; + gtk_widget_event (child, event); + //gdk_event_free (event); +#endif + } + else + gdk_event_free (last_button_event); + + last_button_event = NULL; + } + + return FALSE; +} + +static gboolean +moz_button_press_cb (GtkWidget *widget, GdkEvent *event) +{ + g_print ("%s: window=%p/%p, event=%p/%p\n", G_STRFUNC, widget->window, event->any.window, event, last_button_event); + return TRUE; +} + +static gboolean +moz_button_release_cb (GtkWidget *widget, GdkEvent *event) +{ + g_print ("%s: window=%p/%p, event=%p/%p\n", G_STRFUNC, widget->window, event->any.window, event, last_button_event); + return TRUE; +} + +static int +open_uri_cb (GtkMozEmbed *embed, const char *aURI) +{ + g_print ("%s: %s\n", __func__, aURI); + return 0; +} + +int +main (int argc, char **argv) +{ + guint32 chrome; + + hildon_gtk_init (&argc, &argv); + + GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); + g_signal_connect_after (window, "expose-event", G_CALLBACK (expose_event_cb), NULL); + + GtkWidget *ebox = gtk_event_box_new (); + gtk_container_add (GTK_CONTAINER (window), ebox); + gtk_event_box_set_above_child (GTK_EVENT_BOX (ebox), TRUE); + //g_signal_connect (ebox, "event", G_CALLBACK (event_cb), NULL); + g_signal_connect (ebox, "button-press-event", G_CALLBACK (button_press_cb), NULL); + g_signal_connect (ebox, "button-release-event", G_CALLBACK (button_release_cb), NULL); + gtk_widget_add_events (ebox, GDK_POINTER_MOTION_MASK); + + gtk_moz_embed_set_path (MOZILLA_LIBDIR); + GtkWidget *embed = gtk_moz_embed_new (); + + chrome = gtk_moz_embed_get_chrome_mask (GTK_MOZ_EMBED (embed)); + gtk_moz_embed_set_chrome_mask (GTK_MOZ_EMBED (embed), chrome & ~GTK_MOZ_EMBED_FLAG_SCROLLBARSON); + gtk_moz_embed_load_url (GTK_MOZ_EMBED (embed), "file:///home/mathias/.dashboard.html"); + g_signal_connect (embed, "button-press-event", G_CALLBACK (moz_button_press_cb), NULL); + g_signal_connect (embed, "button-release-event", G_CALLBACK (moz_button_release_cb), NULL); + g_signal_connect (embed, "open-uri", G_CALLBACK (open_uri_cb), NULL); + //g_signal_connect (embed, "event", G_CALLBACK (event_cb), NULL); + gtk_container_add (GTK_CONTAINER (ebox), embed); + + gtk_widget_show_all (window); + + gdk_window_set_composited (ebox->window, TRUE); + + gtk_main (); + + return 0; + +} -- cgit v1.2.3