From: Steven Luo Date: Tue, 2 Feb 2010 09:23:23 +0000 (-0800) Subject: Initial support for launching MicroB on Fremantle X-Git-Tag: v3.2~33 X-Git-Url: http://git.maemo.org/git/?p=browser-switch;a=commitdiff_plain;h=6b78dde6c6a57b7bcf5fe2cbfab467e24d8174bf;ds=sidebyside Initial support for launching MicroB on Fremantle MicroB in Fremantle has been changed to accommodate keeping the UI in the background full-time as well as the browser engine (browserd). As a result, running "browser" no longer shows a browser window, and the browser process doesn't relinquish the com.nokia.osso_browser D-Bus name and quit when the last browser window closes. Instead, launch the browser by starting the UI process in the background, waiting for it to acquire the com.nokia.osso_browser name, then using the D-Bus API to open a window. Detecting when the last browser window is closed is trickier. The method we use is to notice that when the last browser window closes, the UI process closes and reopens the browserd renderer, causing a change in the ownership of the Mozilla.MicroB D-Bus name (or com.nokia.microb-engine in Maemo 5 PR1.1). We detect this change and send the UI process an exit_browser D-Bus method call, causing it to quit and relinquish the com.nokia.osso_browser name back to us. Unfortunately, this method cannot detect when the bookmarks window is closed. This means two unpleasant surprises, both needed to make sure that the bookmarks window is never the only MicroB window open (in that situation, if the user closes the bookmarks window without first opening another normal browser window, we wouldn't be able to detect the window closing and ensure we get the com.nokia.osso_browser name back): (1) Our top_application method, which should behave exactly like opening MicroB from the menu normally does (bring up the bookmarks window), brings up about:blank instead. (2) We kill off MicroB and reclaim the com.nokia.osso_browser name when the last normal browser window closes, regardless of whether the bookmarks window is open. Ideas for better ways to detect when all MicroB windows have closed are welcome. Code for monitoring when a D-Bus name changes hands draws on ideas from the dbus-monitor source (tools/dbus-monitor.c in the D-Bus distribution). --- diff --git a/launcher.c b/launcher.c index f738c63..f229469 100644 --- a/launcher.c +++ b/launcher.c @@ -28,12 +28,85 @@ #include #include +#ifdef FREMANTLE +#include +#include +#include +#include +#include +#endif + #include "browser-switchboard.h" #include "launcher.h" #include "dbus-server-bindings.h" #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 + static void launch_tear(struct swb_context *ctx, char *uri) { int status; static DBusGProxy *tear_proxy = NULL; @@ -79,6 +152,13 @@ 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"; @@ -89,7 +169,11 @@ void launch_microb(struct swb_context *ctx, char *uri) { status = system("pidof /usr/sbin/browserd > /dev/null"); if (WIFEXITED(status) && WEXITSTATUS(status)) { kill_browserd = 1; +#ifdef FREMANTLE + system("/usr/sbin/browserd -d -b"); +#else system("/usr/sbin/browserd -d"); +#endif } /* Release the osso_browser D-Bus name so that MicroB can take it */ @@ -99,6 +183,194 @@ void launch_microb(struct swb_context *ctx, char *uri) { 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 */ + /* 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); @@ -115,6 +387,7 @@ 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)