X-Git-Url: http://git.maemo.org/git/?p=browser-switch;a=blobdiff_plain;f=launcher.c;h=02bc3eb4b549867f90d168f86bcd9aec160b6fb6;hp=5a6b9517b8e111aa726253ee4b4a266c263c0157;hb=5b7e4808269f8247a7d147b387007e5d15e0d7c8;hpb=4c3459d73a3e6a145230490af14dbba647fe4896;ds=inline diff --git a/launcher.c b/launcher.c index 5a6b951..02bc3eb 100644 --- a/launcher.c +++ b/launcher.c @@ -1,7 +1,7 @@ /* * launcher.c -- functions for launching web browsers for browser-switchboard * - * Copyright (C) 2009 Steven Luo + * Copyright (C) 2009-2010 Steven Luo * Derived from a Python implementation by Jason Simpson and Steven Luo * * This program is free software; you can redistribute it and/or @@ -26,13 +26,98 @@ #include #include #include +#include +#include #include +#ifdef FREMANTLE +#include +#endif + #include "browser-switchboard.h" #include "launcher.h" #include "dbus-server-bindings.h" -#define LAUNCH_DEFAULT_BROWSER launch_tear +#define LAUNCH_DEFAULT_BROWSER launch_microb + +#ifdef FREMANTLE +static int microb_started = 0; +static int kill_microb = 0; + +/* Check to see whether MicroB is ready to handle D-Bus requests yet + See the comments in launch_microb to understand how this works. */ +static DBusHandlerResult check_microb_started(DBusConnection *connection, + DBusMessage *message, + void *user_data) { + DBusError error; + char *name, *old, *new; + + printf("Checking to see if MicroB is ready\n"); + dbus_error_init(&error); + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) { + printf("%s\n", error.message); + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + /* If old is an empty string, then the name has been acquired, and + MicroB should be ready to handle our request */ + if (strlen(old) == 0) { + printf("MicroB ready\n"); + microb_started = 1; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/* Check to see whether the last MicroB window has closed + See the comments in launch_microb to understand how this works. */ +static DBusHandlerResult check_microb_finished(DBusConnection *connection, + DBusMessage *message, + void *user_data) { + DBusError error; + char *name, *old, *new; + + printf("Checking to see if we should kill MicroB\n"); + /* Check to make sure that the Mozilla.MicroB name is being released, + not acquired -- if it's being acquired, we might be seeing an event + at MicroB startup, in which case killing the browser isn't + appropriate */ + dbus_error_init(&error); + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) { + printf("%s\n", error.message); + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + /* If old isn't an empty string, the name is being released, and + we should now kill MicroB */ + if (strlen(old) > 0) + kill_microb = 1; + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} +#endif + +/* Close stdin/stdout/stderr and replace with /dev/null */ +static int close_stdio(void) { + int fd; + + if ((fd = open("/dev/null", O_RDWR)) == -1) + return -1; + + if (dup2(fd, 0) == -1 || dup2(fd, 1) == -1 || dup2(fd, 2) == -1) + return -1; + + close(fd); + return 0; +} static void launch_tear(struct swb_context *ctx, char *uri) { int status; @@ -45,16 +130,31 @@ static void launch_tear(struct swb_context *ctx, char *uri) { printf("launch_tear with uri '%s'\n", uri); + /* We should be able to just call the D-Bus service to open Tear ... + but if Tear's not open, that cuases D-Bus to start Tear and then + pass it the OpenAddress call, which results in two browser windows. + Properly fixing this probably requires Tear to provide a D-Bus + method that opens an address in an existing window, but for now work + around by just invoking Tear with exec() if it's not running. */ status = system("pidof tear > /dev/null"); - if (!WIFEXITED(status)) - exit(1); - if (!WEXITSTATUS(status)) { - if (!tear_proxy) - tear_proxy = dbus_g_proxy_new_for_name(ctx->session_bus, - "com.nokia.tear", "/com/nokia/tear", - "com.nokia.Tear"); - dbus_g_proxy_call(tear_proxy, "OpenAddress", &error, - G_TYPE_STRING, uri, G_TYPE_INVALID); + if (WIFEXITED(status) && !WEXITSTATUS(status)) { + if (!tear_proxy) { + if (!(tear_proxy = dbus_g_proxy_new_for_name( + ctx->session_bus, + "com.nokia.tear", + "/com/nokia/tear", + "com.nokia.Tear"))) { + printf("Failed to create proxy for com.nokia.Tear D-Bus interface\n"); + exit(1); + } + } + + if (!dbus_g_proxy_call(tear_proxy, "OpenAddress", &error, + G_TYPE_STRING, uri, G_TYPE_INVALID, + G_TYPE_INVALID)) { + printf("Opening window failed: %s\n", error->message); + exit(1); + } if (!ctx->continuous_mode) exit(0); } else { @@ -66,6 +166,7 @@ static void launch_tear(struct swb_context *ctx, char *uri) { } /* Child process */ setsid(); + close_stdio(); } execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL); } @@ -75,29 +176,237 @@ void launch_microb(struct swb_context *ctx, char *uri) { int kill_browserd = 0; int status; pid_t pid; +#ifdef FREMANTLE + DBusConnection *raw_connection; + DBusError dbus_error; + DBusHandleMessageFunction filter_func; + DBusGProxy *g_proxy; + GError *gerror = NULL; +#endif if (!uri) uri = "new_window"; - status = system("pidof /usr/sbin/browserd > /dev/null"); - if (!WIFEXITED(status)) - exit(1); - if (WEXITSTATUS(status)) { + printf("launch_microb with uri '%s'\n", uri); + + /* Launch browserd if it's not running */ + status = system("pidof browserd > /dev/null"); + if (WIFEXITED(status) && WEXITSTATUS(status)) { kill_browserd = 1; - system("/usr/sbin/browserd -d"); +#ifdef FREMANTLE + system("/usr/sbin/browserd -d -b > /dev/null 2>&1"); +#else + system("/usr/sbin/browserd -d > /dev/null 2>&1"); +#endif } + /* Release the osso_browser D-Bus name so that MicroB can take it */ dbus_release_osso_browser_name(ctx); if ((pid = fork()) == -1) { perror("fork"); exit(1); } +#ifdef FREMANTLE + if (pid > 0) { + /* Parent process */ + /* Wait for our child to start the browser UI process and + for it to acquire the com.nokia.osso_browser D-Bus name, + then make the appropriate method call to open the browser + window. + + Ideas for how to do this monitoring derived from the + dbus-monitor code (tools/dbus-monitor.c in the D-Bus + codebase). */ + microb_started = 0; + dbus_error_init(&dbus_error); + + raw_connection = dbus_bus_get_private(DBUS_BUS_SESSION, + &dbus_error); + if (!raw_connection) { + fprintf(stderr, + "Failed to open connection to session bus: %s\n", + dbus_error.message); + dbus_error_free(&dbus_error); + exit(1); + } + + dbus_bus_add_match(raw_connection, + "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.osso_browser'", + &dbus_error); + if (dbus_error_is_set(&dbus_error)) { + fprintf(stderr, + "Failed to set up watch for browser UI start: %s\n", + dbus_error.message); + dbus_error_free(&dbus_error); + exit(1); + } + filter_func = check_microb_started; + if (!dbus_connection_add_filter(raw_connection, + filter_func, NULL, NULL)) { + fprintf(stderr, "Failed to set up watch filter!\n"); + exit(1); + } + printf("Waiting for MicroB to start\n"); + while (!microb_started && + dbus_connection_read_write_dispatch(raw_connection, + -1)); + dbus_connection_remove_filter(raw_connection, + filter_func, NULL); + dbus_bus_remove_match(raw_connection, + "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.osso_browser'", + &dbus_error); + if (dbus_error_is_set(&dbus_error)) { + fprintf(stderr, + "Failed to remove watch for browser UI start: %s\n", + dbus_error.message); + dbus_error_free(&dbus_error); + exit(1); + } + + /* Browser UI's started, send it the request for a new window + via D-Bus */ + g_proxy = dbus_g_proxy_new_for_name(ctx->session_bus, + "com.nokia.osso_browser", + "/com/nokia/osso_browser/request", + "com.nokia.osso_browser"); + if (!g_proxy) { + printf("Couldn't get a com.nokia.osso_browser proxy\n"); + exit(1); + } + if (!strcmp(uri, "new_window")) { +#if 0 /* Since we can't detect when the bookmark window closes, we'd have a + corner case where, if the user just closes the bookmark window + without opening any browser windows, we don't kill off MicroB or + resume handling com.nokia.osso_browser */ + if (!dbus_g_proxy_call(g_proxy, "top_application", + &gerror, G_TYPE_INVALID, + G_TYPE_INVALID)) { + printf("Opening window failed: %s\n", + gerror->message); + exit(1); + } +#endif + if (!dbus_g_proxy_call(g_proxy, "load_url", + &gerror, + G_TYPE_STRING, "about:blank", + G_TYPE_INVALID, + G_TYPE_INVALID)) { + printf("Opening window failed: %s\n", + gerror->message); + exit(1); + } + } else { + if (!dbus_g_proxy_call(g_proxy, "load_url", + &gerror, + G_TYPE_STRING, uri, + G_TYPE_INVALID, + G_TYPE_INVALID)) { + printf("Opening window failed: %s\n", + gerror->message); + exit(1); + } + } + + /* Workaround: the browser process we started is going to want + to hang around forever, hogging the com.nokia.osso_browser + D-Bus interface while at it. To fix this, we notice that + when the last browser window closes, the browser UI restarts + its attached browserd process, which causes an observable + change in the ownership of the Mozilla.MicroB D-Bus name. + Watch for this change and kill off the browser UI process + when it happens. + + This has the problem of not being able to detect whether + the bookmark window is open and/or in use, but it's the best + that I can think of. Better suggestions would be greatly + appreciated. */ + kill_microb = 0; + dbus_bus_add_match(raw_connection, + "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='Mozilla.MicroB'", + &dbus_error); + if (dbus_error_is_set(&dbus_error)) { + fprintf(stderr, + "Failed to set up watch for browserd restart: %s\n", + dbus_error.message); + dbus_error_free(&dbus_error); + exit(1); + } + /* Maemo 5 PR1.1 seems to have changed the name browserd takes + to com.nokia.microb-engine; look for this too */ + dbus_bus_add_match(raw_connection, + "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.microb-engine'", + &dbus_error); + if (dbus_error_is_set(&dbus_error)) { + fprintf(stderr, + "Failed to set up watch for browserd restart: %s\n", + dbus_error.message); + dbus_error_free(&dbus_error); + exit(1); + } + filter_func = check_microb_finished; + if (!dbus_connection_add_filter(raw_connection, + filter_func, NULL, NULL)) { + fprintf(stderr, "Failed to set up watch filter!\n"); + exit(1); + } + while (!kill_microb && + dbus_connection_read_write_dispatch(raw_connection, + -1)); + dbus_connection_remove_filter(raw_connection, + filter_func, NULL); + dbus_bus_remove_match(raw_connection, + "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='Mozilla.MicroB'", + &dbus_error); + if (dbus_error_is_set(&dbus_error)) + /* Don't really care -- about to disconnect from the + bus anyhow */ + dbus_error_free(&dbus_error); + dbus_bus_remove_match(raw_connection, + "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.microb-engine'", + &dbus_error); + if (dbus_error_is_set(&dbus_error)) + dbus_error_free(&dbus_error); + dbus_connection_close(raw_connection); + dbus_connection_unref(raw_connection); + + /* Tell browser UI to exit nicely */ + printf("Closing MicroB\n"); + if (!dbus_g_proxy_call(g_proxy, "exit_browser", &gerror, + G_TYPE_INVALID, G_TYPE_INVALID)) { + /* We don't expect a reply; any other error indicates + a problem */ + if (gerror->domain != DBUS_GERROR || + gerror->code != DBUS_GERROR_NO_REPLY) { + printf("exit_browser failed: %s\n", + gerror->message); + exit(1); + } + } + g_object_unref(g_proxy); + } else { + /* Child process */ + close_stdio(); + + /* exec maemo-invoker directly instead of relying on the + /usr/bin/browser symlink, since /usr/bin/browser may have + been replaced with a shell script calling us via D-Bus */ + /* Launch the browser in the background -- our parent will + wait for it to claim the D-Bus name and then display the + window using D-Bus */ + execl("/usr/bin/maemo-invoker", "browser", (char *)NULL); + } +#else /* !FREMANTLE */ if (pid > 0) { /* Parent process */ waitpid(pid, &status, 0); } else { /* Child process */ + close_stdio(); + + /* exec maemo-invoker directly instead of relying on the + /usr/bin/browser symlink, since /usr/bin/browser may have + been replaced with a shell script calling us via D-Bus */ if (!strcmp(uri, "new_window")) { execl("/usr/bin/maemo-invoker", "browser", (char *)NULL); @@ -106,9 +415,11 @@ void launch_microb(struct swb_context *ctx, char *uri) { "browser", "--url", uri, (char *)NULL); } } +#endif /* FREMANTLE */ + /* Kill off browserd if we started it */ if (kill_browserd) - system("kill `pidof /usr/sbin/browserd`"); + system("kill `pidof browserd`"); if (!ctx || !ctx->continuous_mode) exit(0); @@ -126,9 +437,11 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) { if (!uri || !strcmp(uri, "new_window")) uri = ""; - urilen = strlen(uri); - if (urilen > 0) { - /* Quote the URI */ + + printf("launch_other_browser with uri '%s'\n", uri); + + if ((urilen = strlen(uri)) > 0) { + /* Quote the URI to prevent the shell from interpreting it */ /* urilen+3 = length of URI + 2x \' + \0 */ if (!(quoted_uri = calloc(urilen+3, sizeof(char)))) exit(1); @@ -158,7 +471,7 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) { /* Move the string after the ', including the \0, over two chars */ - memmove(quote+3, quote+1, strlen(quote)+1); + memmove(quote+3, quote+1, strlen(quote)); memcpy(quote, "%27", 3); quote = quote + 3; } @@ -186,10 +499,15 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) { } /* Child process */ setsid(); + close_stdio(); } execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL); } +/* Use launch_other_browser as the default browser launcher, with the string + passed in as the other_browser_cmd + Resulting other_browser_cmd is always safe to free(), even if a pointer + to a string constant is passed in */ static void use_other_browser_cmd(struct swb_context *ctx, char *cmd) { size_t len = strlen(cmd); @@ -210,6 +528,7 @@ void update_default_browser(struct swb_context *ctx, char *default_browser) { return; if (!default_browser) { + /* No default_browser configured -- use built-in default */ ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER; return; } @@ -219,6 +538,8 @@ void update_default_browser(struct swb_context *ctx, char *default_browser) { else if (!strcmp(default_browser, "microb")) ctx->default_browser_launcher = launch_microb; else if (!strcmp(default_browser, "fennec")) + /* Cheat and reuse launch_other_browser, since we don't appear + to need to do anything special */ use_other_browser_cmd(ctx, "fennec %s"); else if (!strcmp(default_browser, "midori")) use_other_browser_cmd(ctx, "midori %s");