Bug 745018. Implement webapp runtime for Linux. r=jst
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Sun, 03 Jun 2012 22:54:38 -0300
changeset 99771 0ae2ca987cf916445f253bfda6d08a00842f988a
parent 99770 28c54a0bfd3b9a2257a3d8d3e1080629d383f2c8
child 99772 b9de12c3aedafa72bd22a6d5671a1b9a847165e5
push idunknown
push userunknown
push dateunknown
reviewersjst
bugs745018
milestone15.0a1
Bug 745018. Implement webapp runtime for Linux. r=jst
browser/confvars.sh
configure.in
webapprt/Makefile.in
webapprt/linux/Makefile.in
webapprt/linux/webapprt.cpp
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -10,20 +10,16 @@ MOZ_PHOENIX=1
 
 if test "$OS_ARCH" = "WINNT"; then
   if ! test "$HAVE_64BIT_OS"; then
     MOZ_VERIFY_MAR_SIGNATURE=1
     MOZ_MAINTENANCE_SERVICE=1
   fi
 fi
 
-if (test "$OS_TARGET" = "WINNT" -o "$OS_TARGET" = "Darwin"); then
-  MOZ_WEBAPP_RUNTIME=1
-fi
-
 MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
 MOZ_SERVICES_SYNC=1
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_EXTENSIONS_DEFAULT=" gnomevfs"
 # MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
 # Changing MOZ_*BRANDING_DIRECTORY requires a clobber to ensure correct results,
 # because branding dependencies are broken.
@@ -39,8 +35,9 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3
 # If more than one ID is needed, then you should use a comma separated list
 # of values.
 ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
 # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
 MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
+MOZ_WEBAPP_RUNTIME=1
--- a/configure.in
+++ b/configure.in
@@ -6408,16 +6408,19 @@ fi
 
 dnl ========================================================
 dnl Web App Runtime
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(webapp-runtime,
 [  --disable-webapp-runtime  Disable Web App Runtime],
     MOZ_WEBAPP_RUNTIME=,
     MOZ_WEBAPP_RUNTIME=1)
+if test "$MOZ_WIDGET_TOOLKIT" != "windows" -a "$MOZ_WIDGET_TOOLKIT" != "cocoa" -a "$MOZ_WIDGET_TOOLKIT" != "gtk2"; then
+    MOZ_WEBAPP_RUNTIME=
+fi
 if test "$OS_ARCH" = "WINNT" -a -z "$MAKENSISU" -a -n "$CROSS_COMPILE"; then
     MOZ_WEBAPP_RUNTIME=
 fi
 AC_SUBST(MOZ_WEBAPP_RUNTIME)
 if test "$MOZ_WEBAPP_RUNTIME"; then
     AC_DEFINE(MOZ_WEBAPP_RUNTIME)
 fi
 
--- a/webapprt/Makefile.in
+++ b/webapprt/Makefile.in
@@ -18,16 +18,20 @@ include $(topsrcdir)/config/config.mk
 # into a WebappRT-specific subdirectory, so we redefine it here.
 FINAL_TARGET = $(DIST)/bin/webapprt
 
 ifneq (,$(filter WINNT,$(OS_ARCH)))
 DIRS += win
 else
 ifeq ($(OS_ARCH),Darwin)
 DIRS += mac
+else
+ifeq ($(OS_ARCH),Linux)
+DIRS += linux
+endif # linux
 endif # mac
 endif # windows
 
 EXTRA_PP_COMPONENTS = \
   components.manifest \
   CommandLineHandler.js \
   ContentPermission.js \
   ContentPolicy.js \
new file mode 100644
--- /dev/null
+++ b/webapprt/linux/Makefile.in
@@ -0,0 +1,39 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+# Build a binary bootstrapping with XRE_main
+PROGRAM = webapprt-stub
+
+CPPSRCS = webapprt.cpp
+
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
+LOCAL_INCLUDES += -I$(DEPTH)/build
+
+DEFINES += -DXPCOM_GLUE
+STL_FLAGS=
+
+LIBS = \
+  $(XPCOM_STANDALONE_GLUE_LDOPTS) \
+  $(MOZ_GTK2_LIBS) \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+CXXFLAGS += \
+	$(MOZ_GTK2_CFLAGS) \
+	$(NULL)
+
+GRE_BUILDID := $(shell cat $(DEPTH)/config/buildid)
+DEFINES += -DGRE_BUILDID=$(GRE_BUILDID)
+
+webapprt.$(OBJ_SUFFIX): $(DEPTH)/config/buildid
new file mode 100644
--- /dev/null
+++ b/webapprt/linux/webapprt.cpp
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// GTK headers
+#include <gtk/gtk.h>
+
+// Linux headers
+#include <fcntl.h>
+#include <unistd.h>
+
+// Mozilla headers
+#include "nsILocalFile.h"
+#include "nsINIParser.h"
+#include "nsXPCOMGlue.h"
+#include "nsXPCOMPrivate.h"              // for MAXPATHLEN and XPCOM_DLL
+#include "nsXULAppAPI.h"
+
+const char kAPP_INI[] = "application.ini";
+const char kWEBAPP_INI[] = "webapp.ini";
+const char kWEBAPPRT_INI[] = "webapprt.ini";
+const char kWEBAPPRT_PATH[] = "webapprt";
+const char kAPP_ENV_VAR[] = "XUL_APP_FILE";
+const char kAPP_RT[] = "webapprt-stub";
+
+int* pargc;
+char*** pargv;
+
+XRE_GetFileFromPathType XRE_GetFileFromPath;
+XRE_CreateAppDataType XRE_CreateAppData;
+XRE_FreeAppDataType XRE_FreeAppData;
+XRE_mainType XRE_main;
+
+const nsDynamicFunctionLoad kXULFuncs[] = {
+  { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
+  { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
+  { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
+  { "XRE_main", (NSFuncPtr*) &XRE_main },
+  { nsnull, nsnull }
+};
+
+class ScopedLogging
+{
+  public:
+    ScopedLogging() { NS_LogInit(); }
+    ~ScopedLogging() { NS_LogTerm(); }
+};
+
+// Copied from toolkit/xre/nsAppData.cpp.
+void SetAllocatedString(const char *&str, const char *newvalue)
+{
+  NS_Free(const_cast<char*>(str));
+  if (newvalue) {
+    str = NS_strdup(newvalue);
+  }
+  else {
+    str = nsnull;
+  }
+}
+
+// Function to open a dialog box displaying the message provided
+void ErrorDialog(const char* message)
+{
+  GtkWidget* dialog;
+
+  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message);
+  gtk_dialog_run(GTK_DIALOG(dialog));
+  gtk_widget_destroy(dialog);
+}
+
+// Function to get the parent dir of a file
+void GetDirFromPath(char* parentDir, char* filePath)
+{
+  char* base = strrchr(filePath, '/');
+
+  if (!base) {
+    strcpy(parentDir, ".");
+  }
+  else {
+    while (base > filePath && *base == '/')
+      base--;
+
+    int len = 1 + base - filePath;
+    strncpy(parentDir, filePath, len);
+    parentDir[len] = '\0';
+  }
+}
+
+bool CopyFile(const char* inputFile, const char* outputFile)
+{
+  // Open input file
+  int inputFD = open(inputFile, O_RDONLY);
+  if (!inputFD)
+    return false;
+
+  // Open output file
+  int outputFD = creat(outputFile, S_IRWXU);
+  if (!outputFD)
+    return false;
+
+  // Copy file
+  char buf[BUFSIZ];
+  ssize_t bytesRead;
+
+  while ((bytesRead = read(inputFD, buf, BUFSIZ)) > 0) {
+    ssize_t bytesWritten = write(outputFD, buf, bytesRead);
+    if (bytesWritten < 0) {
+      bytesRead = -1;
+      break;
+    }
+  }
+
+  // Close file descriptors
+  close(inputFD);
+  close(outputFD);
+
+  return (bytesRead >= 0);
+}
+
+bool GRELoadAndLaunch(const char* firefoxDir, const char* profile)
+{
+  char xpcomDllPath[MAXPATHLEN];
+  snprintf(xpcomDllPath, MAXPATHLEN, "%s/%s", firefoxDir, XPCOM_DLL);
+
+  if (NS_FAILED(XPCOMGlueStartup(xpcomDllPath))) {
+    ErrorDialog("Couldn't load the XPCOM library");
+    return false;
+  }
+
+  if (NS_FAILED(XPCOMGlueLoadXULFunctions(kXULFuncs))) {
+    ErrorDialog("Couldn't load libxul");
+    return false;
+  }
+
+  // NOTE: The GRE has successfully loaded, so we can use XPCOM now
+  { // Scope for any XPCOM stuff we create
+    ScopedLogging log;
+
+    // Get the path to the runtime
+    char rtPath[MAXPATHLEN];
+    snprintf(rtPath, MAXPATHLEN, "%s/%s", firefoxDir, kWEBAPPRT_PATH);
+
+    // Get the path to the runtime's INI file
+    char rtIniPath[MAXPATHLEN];
+    snprintf(rtIniPath, MAXPATHLEN, "%s/%s", rtPath, kWEBAPPRT_INI);
+
+    // Load the runtime's INI from its path
+    nsCOMPtr<nsILocalFile> rtINI;
+    if (NS_FAILED(XRE_GetFileFromPath(rtIniPath, getter_AddRefs(rtINI)))) {
+      ErrorDialog("Couldn't load the runtime INI");
+      return false;
+    }
+
+    bool exists;
+    nsresult rv = rtINI->Exists(&exists);
+    if (NS_FAILED(rv) || !exists) {
+      ErrorDialog("The runtime INI doesn't exist");
+      return false;
+    }
+
+    nsXREAppData *webShellAppData;
+    if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) {
+      ErrorDialog("Couldn't read WebappRT application.ini");
+      return false;
+    }
+
+    SetAllocatedString(webShellAppData->profile, profile);
+
+    nsCOMPtr<nsILocalFile> directory;
+    if (NS_FAILED(XRE_GetFileFromPath(rtPath, getter_AddRefs(directory)))) {
+      ErrorDialog("Couldn't open runtime directory");
+      return false;
+    }
+
+    nsCOMPtr<nsILocalFile> xreDir;
+    if (NS_FAILED(XRE_GetFileFromPath(firefoxDir, getter_AddRefs(xreDir)))) {
+      ErrorDialog("Couldn't open XRE directory");
+      return false;
+    }
+
+    xreDir.forget(&webShellAppData->xreDirectory);
+    NS_IF_RELEASE(webShellAppData->directory);
+    directory.forget(&webShellAppData->directory);
+
+    XRE_main(*pargc, *pargv, webShellAppData, 0);
+
+    XRE_FreeAppData(webShellAppData);
+  }
+
+  return true;
+}
+
+void CopyAndRelaunch(const char* firefoxDir, const char* curExePath)
+{
+  char newExePath[MAXPATHLEN];
+  snprintf(newExePath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_RT);
+
+  if (unlink(curExePath) == -1) {
+    ErrorDialog("Couldn't remove the old webapprt-stub executable");
+    return;
+  }
+
+  if (!CopyFile(newExePath, curExePath)) {
+    ErrorDialog("Couldn't copy the new webapprt-stub executable");
+    return;
+  }
+
+  execv(curExePath, *pargv);
+
+  ErrorDialog("Couldn't execute the new webapprt-stub executable");
+}
+
+int main(int argc, char *argv[])
+{
+  gtk_init(&argc, &argv);
+
+  pargc = &argc;
+  pargv = &argv;
+
+  // Get current executable path
+  char curExePath[MAXPATHLEN];
+  if (readlink("/proc/self/exe", curExePath, MAXPATHLEN) == -1) {
+    ErrorDialog("Couldn't read current executable path");
+    return 255;
+  }
+
+  // Set up webAppIniPath with path to webapp.ini
+  char curExeDir[MAXPATHLEN];
+  GetDirFromPath(curExeDir, curExePath);
+  char webAppIniPath[MAXPATHLEN];
+  snprintf(webAppIniPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_INI);
+
+  // Open webapp.ini as an INI file
+  nsINIParser parser;
+  if (NS_FAILED(parser.Init(webAppIniPath))) {
+    ErrorDialog("Couldn't open webapp.ini");
+    return 255;
+  }
+
+  // Set up our environment to know where webapp.ini was loaded from
+  if (setenv(kAPP_ENV_VAR, webAppIniPath, 1) == -1) {
+    ErrorDialog("Couldn't set up app environment");
+    return 255;
+  }
+
+  // Get profile dir from webapp.ini
+  char profile[MAXPATHLEN];
+  if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) {
+    ErrorDialog("Couldn't retrieve profile from web app INI file");
+    return 255;
+  }
+
+  // Get the location of Firefox from our webapp.ini
+  char firefoxDir[MAXPATHLEN];
+  if (NS_FAILED(parser.GetString("WebappRT", "InstallDir", firefoxDir, MAXPATHLEN))) {
+    ErrorDialog("Couldn't find your Firefox install directory.");
+    return 255;
+  }
+
+  // Set up appIniPath with path to application.ini.
+  // This is in the Firefox installation directory.
+  char appIniPath[MAXPATHLEN];
+  snprintf(appIniPath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_INI);
+
+  if (NS_FAILED(parser.Init(appIniPath))) {
+    ErrorDialog("Couldn't open Firefox application.ini");
+    return 255;
+  }
+
+  // Get buildid of Firefox we're trying to load (MAXPATHLEN only for convenience)
+  char buildid[MAXPATHLEN];
+  if (NS_FAILED(parser.GetString("App", "BuildID", buildid, MAXPATHLEN))) {
+    ErrorDialog("Couldn't read BuildID from Firefox application.ini");
+    return 255;
+  }
+
+  // If WebAppRT version == Firefox version, load XUL and execute the application
+  if (!strcmp(buildid, NS_STRINGIFY(GRE_BUILDID))) {
+    if (GRELoadAndLaunch(firefoxDir, profile))
+      return 0;
+  }
+  // Else, copy WebAppRT from Firefox installation and re-execute the process
+  else
+    CopyAndRelaunch(firefoxDir, curExePath);
+
+  return 255;
+}