webapprt/gtk2/webapprt.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/webapprt/gtk2/webapprt.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,408 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +// GTK headers
    1.11 +#include <gtk/gtk.h>
    1.12 +
    1.13 +// Linux headers
    1.14 +#include <fcntl.h>
    1.15 +#include <unistd.h>
    1.16 +#include <dlfcn.h>
    1.17 +
    1.18 +// Mozilla headers
    1.19 +#include "nsIFile.h"
    1.20 +#include "nsINIParser.h"
    1.21 +#include "nsXPCOMGlue.h"
    1.22 +#include "nsXPCOMPrivate.h"              // for MAXPATHLEN and XPCOM_DLL
    1.23 +#include "nsXULAppAPI.h"
    1.24 +#include "BinaryPath.h"
    1.25 +
    1.26 +const char kAPP_INI[] = "application.ini";
    1.27 +const char kWEBAPP_INI[] = "webapp.ini";
    1.28 +const char kWEBAPP_JSON[] = "webapp.json";
    1.29 +const char kWEBAPP_PACKAGE[] = "application.zip";
    1.30 +const char kWEBAPPRT_INI[] = "webapprt.ini";
    1.31 +const char kWEBAPPRT_PATH[] = "webapprt";
    1.32 +const char kAPP_ENV_VAR[] = "XUL_APP_FILE";
    1.33 +const char kAPP_RT[] = "webapprt-stub";
    1.34 +
    1.35 +int* pargc;
    1.36 +char*** pargv;
    1.37 +char profile[MAXPATHLEN];
    1.38 +bool isProfileOverridden = false;
    1.39 +
    1.40 +XRE_GetFileFromPathType XRE_GetFileFromPath;
    1.41 +XRE_CreateAppDataType XRE_CreateAppData;
    1.42 +XRE_FreeAppDataType XRE_FreeAppData;
    1.43 +XRE_mainType XRE_main;
    1.44 +
    1.45 +const nsDynamicFunctionLoad kXULFuncs[] = {
    1.46 +  { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
    1.47 +  { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
    1.48 +  { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
    1.49 +  { "XRE_main", (NSFuncPtr*) &XRE_main },
    1.50 +  { nullptr, nullptr }
    1.51 +};
    1.52 +
    1.53 +class ScopedLogging
    1.54 +{
    1.55 +  public:
    1.56 +    ScopedLogging() { NS_LogInit(); }
    1.57 +    ~ScopedLogging() { NS_LogTerm(); }
    1.58 +};
    1.59 +
    1.60 +// Copied from toolkit/xre/nsAppData.cpp.
    1.61 +void SetAllocatedString(const char *&str, const char *newvalue)
    1.62 +{
    1.63 +  NS_Free(const_cast<char*>(str));
    1.64 +  if (newvalue) {
    1.65 +    str = NS_strdup(newvalue);
    1.66 +  }
    1.67 +  else {
    1.68 +    str = nullptr;
    1.69 +  }
    1.70 +}
    1.71 +
    1.72 +// Function to open a dialog box displaying the message provided
    1.73 +void ErrorDialog(const char* message)
    1.74 +{
    1.75 +  gtk_init(pargc, pargv);
    1.76 +
    1.77 +  GtkWidget* dialog = gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message);
    1.78 +  gtk_window_set_title(GTK_WINDOW(dialog), "Error launching webapp");
    1.79 +  gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), false);
    1.80 +  gtk_dialog_run(GTK_DIALOG(dialog));
    1.81 +  gtk_widget_destroy(dialog);
    1.82 +}
    1.83 +
    1.84 +// Function to get the parent dir of a file
    1.85 +void GetDirFromPath(char* parentDir, char* filePath)
    1.86 +{
    1.87 +  char* base = strrchr(filePath, '/');
    1.88 +
    1.89 +  if (!base) {
    1.90 +    strcpy(parentDir, ".");
    1.91 +  }
    1.92 +  else {
    1.93 +    while (base > filePath && *base == '/')
    1.94 +      base--;
    1.95 +
    1.96 +    int len = 1 + base - filePath;
    1.97 +    strncpy(parentDir, filePath, len);
    1.98 +    parentDir[len] = '\0';
    1.99 +  }
   1.100 +}
   1.101 +
   1.102 +bool CopyFile(const char* inputFile, const char* outputFile)
   1.103 +{
   1.104 +  // Open input file
   1.105 +  int inputFD = open(inputFile, O_RDONLY);
   1.106 +  if (!inputFD)
   1.107 +    return false;
   1.108 +
   1.109 +  // Open output file
   1.110 +  int outputFD = creat(outputFile, S_IRWXU);
   1.111 +  if (!outputFD)
   1.112 +    return false;
   1.113 +
   1.114 +  // Copy file
   1.115 +  char buf[BUFSIZ];
   1.116 +  ssize_t bytesRead;
   1.117 +
   1.118 +  while ((bytesRead = read(inputFD, buf, BUFSIZ)) > 0) {
   1.119 +    ssize_t bytesWritten = write(outputFD, buf, bytesRead);
   1.120 +    if (bytesWritten < 0) {
   1.121 +      bytesRead = -1;
   1.122 +      break;
   1.123 +    }
   1.124 +  }
   1.125 +
   1.126 +  // Close file descriptors
   1.127 +  close(inputFD);
   1.128 +  close(outputFD);
   1.129 +
   1.130 +  return (bytesRead >= 0);
   1.131 +}
   1.132 +
   1.133 +bool GRELoadAndLaunch(const char* firefoxDir, bool silentFail)
   1.134 +{
   1.135 +  char xpcomDllPath[MAXPATHLEN];
   1.136 +  snprintf(xpcomDllPath, MAXPATHLEN, "%s/%s", firefoxDir, XPCOM_DLL);
   1.137 +
   1.138 +  if (silentFail && access(xpcomDllPath, F_OK) != 0)
   1.139 +    return false;
   1.140 +
   1.141 +  if (NS_FAILED(XPCOMGlueStartup(xpcomDllPath))) {
   1.142 +    ErrorDialog("Couldn't load the XPCOM library");
   1.143 +    return false;
   1.144 +  }
   1.145 +
   1.146 +  if (NS_FAILED(XPCOMGlueLoadXULFunctions(kXULFuncs))) {
   1.147 +    ErrorDialog("Couldn't load libxul");
   1.148 +    return false;
   1.149 +  }
   1.150 +
   1.151 +  // NOTE: The GRE has successfully loaded, so we can use XPCOM now
   1.152 +  { // Scope for any XPCOM stuff we create
   1.153 +    ScopedLogging log;
   1.154 +
   1.155 +    // Get the path to the runtime
   1.156 +    char rtPath[MAXPATHLEN];
   1.157 +    snprintf(rtPath, MAXPATHLEN, "%s/%s", firefoxDir, kWEBAPPRT_PATH);
   1.158 +
   1.159 +    // Get the path to the runtime's INI file
   1.160 +    char rtIniPath[MAXPATHLEN];
   1.161 +    snprintf(rtIniPath, MAXPATHLEN, "%s/%s", rtPath, kWEBAPPRT_INI);
   1.162 +
   1.163 +    // Load the runtime's INI from its path
   1.164 +    nsCOMPtr<nsIFile> rtINI;
   1.165 +    if (NS_FAILED(XRE_GetFileFromPath(rtIniPath, getter_AddRefs(rtINI)))) {
   1.166 +      ErrorDialog("Couldn't load the runtime INI");
   1.167 +      return false;
   1.168 +    }
   1.169 +
   1.170 +    bool exists;
   1.171 +    nsresult rv = rtINI->Exists(&exists);
   1.172 +    if (NS_FAILED(rv) || !exists) {
   1.173 +      ErrorDialog("The runtime INI doesn't exist");
   1.174 +      return false;
   1.175 +    }
   1.176 +
   1.177 +    nsXREAppData *webShellAppData;
   1.178 +    if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) {
   1.179 +      ErrorDialog("Couldn't read WebappRT application.ini");
   1.180 +      return false;
   1.181 +    }
   1.182 +
   1.183 +    if (!isProfileOverridden) {
   1.184 +      SetAllocatedString(webShellAppData->profile, profile);
   1.185 +      // nsXREAppData::name is used for the class name part of the WM_CLASS
   1.186 +      // property. Set it so that the DE can match our window to the correct
   1.187 +      // launcher.
   1.188 +      char programClass[MAXPATHLEN];
   1.189 +      snprintf(programClass, MAXPATHLEN, "owa-%s", profile);
   1.190 +      SetAllocatedString(webShellAppData->name, programClass);
   1.191 +    }
   1.192 +
   1.193 +    nsCOMPtr<nsIFile> directory;
   1.194 +    if (NS_FAILED(XRE_GetFileFromPath(rtPath, getter_AddRefs(directory)))) {
   1.195 +      ErrorDialog("Couldn't open runtime directory");
   1.196 +      return false;
   1.197 +    }
   1.198 +
   1.199 +    nsCOMPtr<nsIFile> xreDir;
   1.200 +    if (NS_FAILED(XRE_GetFileFromPath(firefoxDir, getter_AddRefs(xreDir)))) {
   1.201 +      ErrorDialog("Couldn't open XRE directory");
   1.202 +      return false;
   1.203 +    }
   1.204 +
   1.205 +    xreDir.forget(&webShellAppData->xreDirectory);
   1.206 +    NS_IF_RELEASE(webShellAppData->directory);
   1.207 +    directory.forget(&webShellAppData->directory);
   1.208 +
   1.209 +    XRE_main(*pargc, *pargv, webShellAppData, 0);
   1.210 +
   1.211 +    XRE_FreeAppData(webShellAppData);
   1.212 +  }
   1.213 +
   1.214 +  return true;
   1.215 +}
   1.216 +
   1.217 +void CopyAndRelaunch(const char* firefoxDir, const char* curExePath)
   1.218 +{
   1.219 +  char newExePath[MAXPATHLEN];
   1.220 +  snprintf(newExePath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_RT);
   1.221 +
   1.222 +  if (unlink(curExePath) == -1) {
   1.223 +    ErrorDialog("Couldn't remove the old webapprt-stub executable");
   1.224 +    return;
   1.225 +  }
   1.226 +
   1.227 +  if (!CopyFile(newExePath, curExePath)) {
   1.228 +    ErrorDialog("Couldn't copy the new webapprt-stub executable");
   1.229 +    return;
   1.230 +  }
   1.231 +
   1.232 +  execv(curExePath, *pargv);
   1.233 +
   1.234 +  ErrorDialog("Couldn't execute the new webapprt-stub executable");
   1.235 +}
   1.236 +
   1.237 +void RemoveApplication(nsINIParser& parser, const char* curExeDir, const char* profile)  {
   1.238 +  if (!isProfileOverridden) {
   1.239 +    // Remove the desktop entry file.
   1.240 +    char desktopEntryFilePath[MAXPATHLEN];
   1.241 +
   1.242 +    char* dataDir = getenv("XDG_DATA_HOME");
   1.243 +
   1.244 +    if (dataDir && *dataDir) {
   1.245 +      snprintf(desktopEntryFilePath, MAXPATHLEN, "%s/applications/owa-%s.desktop", dataDir, profile);
   1.246 +    } else {
   1.247 +      char* home = getenv("HOME");
   1.248 +      snprintf(desktopEntryFilePath, MAXPATHLEN, "%s/.local/share/applications/owa-%s.desktop", home, profile);
   1.249 +    }
   1.250 +
   1.251 +    unlink(desktopEntryFilePath);
   1.252 +  }
   1.253 +
   1.254 +  // Remove the files from the installation directory.
   1.255 +  char webAppIniPath[MAXPATHLEN];
   1.256 +  snprintf(webAppIniPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_INI);
   1.257 +  unlink(webAppIniPath);
   1.258 +
   1.259 +  char curExePath[MAXPATHLEN];
   1.260 +  snprintf(curExePath, MAXPATHLEN, "%s/%s", curExeDir, kAPP_RT);
   1.261 +  unlink(curExePath);
   1.262 +
   1.263 +  char webAppJsonPath[MAXPATHLEN];
   1.264 +  snprintf(webAppJsonPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_JSON);
   1.265 +  unlink(webAppJsonPath);
   1.266 +
   1.267 +  char iconPath[MAXPATHLEN];
   1.268 +  snprintf(iconPath, MAXPATHLEN, "%s/icon.png", curExeDir);
   1.269 +  unlink(iconPath);
   1.270 +
   1.271 +  char packagePath[MAXPATHLEN];
   1.272 +  snprintf(packagePath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_PACKAGE);
   1.273 +  unlink(packagePath);
   1.274 +
   1.275 +  char appName[MAXPATHLEN];
   1.276 +  if (NS_FAILED(parser.GetString("Webapp", "Name", appName, MAXPATHLEN))) {
   1.277 +    strcpy(appName, profile);
   1.278 +  }
   1.279 +
   1.280 +  char uninstallMsg[MAXPATHLEN];
   1.281 +  if (NS_SUCCEEDED(parser.GetString("Webapp", "UninstallMsg", uninstallMsg, MAXPATHLEN))) {
   1.282 +    /**
   1.283 +     * The only difference between libnotify.so.4 and libnotify.so.1 for these symbols
   1.284 +     * is that notify_notification_new takes three arguments in libnotify.so.4 and
   1.285 +     * four in libnotify.so.1.
   1.286 +     * Passing the fourth argument as nullptr is binary compatible.
   1.287 +     */
   1.288 +    typedef void  (*notify_init_t)(const char*);
   1.289 +    typedef void* (*notify_notification_new_t)(const char*, const char*, const char*, const char*);
   1.290 +    typedef void  (*notify_notification_show_t)(void*, void**);
   1.291 +
   1.292 +    void *handle = dlopen("libnotify.so.4", RTLD_LAZY);
   1.293 +    if (!handle) {
   1.294 +      handle = dlopen("libnotify.so.1", RTLD_LAZY);
   1.295 +      if (!handle)
   1.296 +        return;
   1.297 +    }
   1.298 +
   1.299 +    notify_init_t nn_init = (notify_init_t)(uintptr_t)dlsym(handle, "notify_init");
   1.300 +    notify_notification_new_t nn_new = (notify_notification_new_t)(uintptr_t)dlsym(handle, "notify_notification_new");
   1.301 +    notify_notification_show_t nn_show = (notify_notification_show_t)(uintptr_t)dlsym(handle, "notify_notification_show");
   1.302 +    if (!nn_init || !nn_new || !nn_show) {
   1.303 +      dlclose(handle);
   1.304 +      return;
   1.305 +    }
   1.306 +
   1.307 +    nn_init(appName);
   1.308 +
   1.309 +    void* n = nn_new(uninstallMsg, nullptr, "dialog-information", nullptr);
   1.310 +
   1.311 +    nn_show(n, nullptr);
   1.312 +
   1.313 +    dlclose(handle);
   1.314 +  }
   1.315 +}
   1.316 +
   1.317 +int main(int argc, char *argv[])
   1.318 +{
   1.319 +  pargc = &argc;
   1.320 +  pargv = &argv;
   1.321 +
   1.322 +  // Get current executable path
   1.323 +  char curExePath[MAXPATHLEN];
   1.324 +  if (NS_FAILED(mozilla::BinaryPath::Get(argv[0], curExePath))) {
   1.325 +    ErrorDialog("Couldn't read current executable path");
   1.326 +    return 255;
   1.327 +  }
   1.328 +  char curExeDir[MAXPATHLEN];
   1.329 +  GetDirFromPath(curExeDir, curExePath);
   1.330 +
   1.331 +  bool removeApp = false;
   1.332 +  for (int i = 1; i < argc; i++) {
   1.333 +    if (!strcmp(argv[i], "-profile")) {
   1.334 +      isProfileOverridden = true;
   1.335 +    }
   1.336 +    else if (!strcmp(argv[i], "-remove")) {
   1.337 +      removeApp = true;
   1.338 +    }
   1.339 +  }
   1.340 +
   1.341 +  char firefoxDir[MAXPATHLEN];
   1.342 +
   1.343 +  // Check if Firefox is in the same directory as the webapp runtime.
   1.344 +  // This is the case for webapprt chrome and content tests.
   1.345 +  if (GRELoadAndLaunch(curExeDir, true)) {
   1.346 +    return 0;
   1.347 +  }
   1.348 +
   1.349 +  // Set up webAppIniPath with path to webapp.ini
   1.350 +  char webAppIniPath[MAXPATHLEN];
   1.351 +  snprintf(webAppIniPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_INI);
   1.352 +
   1.353 +  // Open webapp.ini as an INI file
   1.354 +  nsINIParser parser;
   1.355 +  if (NS_FAILED(parser.Init(webAppIniPath))) {
   1.356 +    ErrorDialog("Couldn't open webapp.ini");
   1.357 +    return 255;
   1.358 +  }
   1.359 +
   1.360 +  // Set up our environment to know where webapp.ini was loaded from
   1.361 +  if (setenv(kAPP_ENV_VAR, webAppIniPath, 1) == -1) {
   1.362 +    ErrorDialog("Couldn't set up app environment");
   1.363 +    return 255;
   1.364 +  }
   1.365 +
   1.366 +  // Get profile dir from webapp.ini
   1.367 +  if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) {
   1.368 +    ErrorDialog("Couldn't retrieve profile from web app INI file");
   1.369 +    return 255;
   1.370 +  }
   1.371 +
   1.372 +  if (removeApp) {
   1.373 +    RemoveApplication(parser, curExeDir, profile);
   1.374 +    return 0;
   1.375 +  }
   1.376 +
   1.377 +  // Get the location of Firefox from our webapp.ini
   1.378 +  if (NS_FAILED(parser.GetString("WebappRT", "InstallDir", firefoxDir, MAXPATHLEN))) {
   1.379 +    ErrorDialog("Couldn't find your Firefox install directory.");
   1.380 +    return 255;
   1.381 +  }
   1.382 +
   1.383 +  // Set up appIniPath with path to application.ini.
   1.384 +  // This is in the Firefox installation directory.
   1.385 +  char appIniPath[MAXPATHLEN];
   1.386 +  snprintf(appIniPath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_INI);
   1.387 +
   1.388 +  if (NS_FAILED(parser.Init(appIniPath))) {
   1.389 +    ErrorDialog("This app requires that Firefox version 16 or above is installed."
   1.390 +                " Firefox 16+ has not been detected.");
   1.391 +    return 255;
   1.392 +  }
   1.393 +
   1.394 +  // Get buildid of Firefox we're trying to load (MAXPATHLEN only for convenience)
   1.395 +  char buildid[MAXPATHLEN];
   1.396 +  if (NS_FAILED(parser.GetString("App", "BuildID", buildid, MAXPATHLEN))) {
   1.397 +    ErrorDialog("Couldn't read BuildID from Firefox application.ini");
   1.398 +    return 255;
   1.399 +  }
   1.400 +
   1.401 +  // If WebAppRT version == Firefox version, load XUL and execute the application
   1.402 +  if (!strcmp(buildid, NS_STRINGIFY(GRE_BUILDID))) {
   1.403 +    if (GRELoadAndLaunch(firefoxDir, false))
   1.404 +      return 0;
   1.405 +  }
   1.406 +  // Else, copy WebAppRT from Firefox installation and re-execute the process
   1.407 +  else
   1.408 +    CopyAndRelaunch(firefoxDir, curExePath);
   1.409 +
   1.410 +  return 255;
   1.411 +}

mercurial