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 95696 0ae2ca987cf916445f253bfda6d08a00842f988a
parent 95695 28c54a0bfd3b9a2257a3d8d3e1080629d383f2c8
child 95697 b9de12c3aedafa72bd22a6d5671a1b9a847165e5
push id10215
push userfelipc@gmail.com
push dateMon, 04 Jun 2012 01:58:52 +0000
treeherdermozilla-inbound@b9de12c3aeda [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst
bugs745018
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
+}