Merge inbound to mozilla-centra r=merge a=merge
authorAndreea Pavel <apavel@mozilla.com>
Fri, 29 Dec 2017 23:31:35 +0200
changeset 449248 4a314985a49e46979a3e67e81e0a153c7b8cfc53
parent 449246 06f53c37de7a20fe90665212ba6236285b22a1ae (current diff)
parent 449247 9e35fd376b8b30857de6c380efe43e5696bc0f5e (diff)
child 449249 28058addf0c296971c00bfba402175afb11e8597
child 449260 a3ed5bb21ec17ea841999c341c4ebf0e6a51efcc
child 449274 5074d8f272687f8cb1b27750cc4f9c25aaa09246
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
4a314985a49e / 59.0a1 / 20171229220051 / files
nightly linux64
4a314985a49e / 59.0a1 / 20171229220051 / files
nightly mac
4a314985a49e / 59.0a1 / 20171229220051 / files
nightly win32
4a314985a49e / 59.0a1 / 20171229220051 / files
nightly win64
4a314985a49e / 59.0a1 / 20171229220051 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-centra r=merge a=merge
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1701,16 +1701,19 @@ var gMainPane = {
       return this._isValidHandlerExecutable(aHandlerApp.executable);
 
     if (aHandlerApp instanceof Ci.nsIWebHandlerApp)
       return aHandlerApp.uriTemplate;
 
     if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo)
       return aHandlerApp.uri;
 
+    if (aHandlerApp instanceof Ci.nsIGIOMimeApp)
+      return aHandlerApp.command;
+
     return false;
   },
 
   _isValidHandlerExecutable(aExecutable) {
     let leafName;
     if (AppConstants.platform == "win") {
       leafName = `${AppConstants.MOZ_APP_NAME}.exe`;
     } else if (AppConstants.platform == "macosx") {
@@ -1838,16 +1841,56 @@ var gMainPane = {
 
       // Attach the handler app object to the menu item so we can use it
       // to make changes to the datastore when the user selects the item.
       menuItem.handlerApp = possibleApp;
 
       menuPopup.appendChild(menuItem);
       possibleAppMenuItems.push(menuItem);
     }
+    // Add gio handlers
+    if (Cc["@mozilla.org/gio-service;1"]) {
+      let gIOSvc = Cc["@mozilla.org/gio-service;1"].
+                   getService(Ci.nsIGIOService);
+      var gioApps = gIOSvc.getAppsForURIScheme(typeItem.type);
+      let enumerator = gioApps.enumerate();
+      let possibleHandlers = handlerInfo.possibleApplicationHandlers;
+      while (enumerator.hasMoreElements()) {
+        let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp);
+        // OS handler share the same name, it's most likely the same app, skipping...
+        if (handler.name == handlerInfo.defaultDescription) {
+          continue;
+        }
+        // Check if the handler is already in possibleHandlers
+        let appAlreadyInHandlers = false;
+        for (let i = possibleHandlers.length - 1; i >= 0; --i) {
+          let app = possibleHandlers.queryElementAt(i, Ci.nsIHandlerApp);
+          // nsGIOMimeApp::Equals is able to compare with nsILocalHandlerApp
+          if (handler.equals(app)) {
+            appAlreadyInHandlers = true;
+            break;
+          }
+        }
+        if (!appAlreadyInHandlers) {
+          let menuItem = document.createElement("menuitem");
+          menuItem.setAttribute("action", Ci.nsIHandlerInfo.useHelperApp);
+          let label = this._prefsBundle.getFormattedString("useApp", [handler.name]);
+          menuItem.setAttribute("label", label);
+          menuItem.setAttribute("tooltiptext", label);
+          menuItem.setAttribute("image", this._getIconURLForHandlerApp(handler));
+
+          // Attach the handler app object to the menu item so we can use it
+          // to make changes to the datastore when the user selects the item.
+          menuItem.handlerApp = handler;
+
+          menuPopup.appendChild(menuItem);
+          possibleAppMenuItems.push(menuItem);
+        }
+      }
+    }
 
     // Create a menu item for the plugin.
     if (handlerInfo.pluginName) {
       var pluginMenuItem = document.createElement("menuitem");
       pluginMenuItem.setAttribute("action", kActionUsePlugin);
       let label = this._prefsBundle.getFormattedString("usePluginIn",
         [handlerInfo.pluginName,
         this._brandShortName]);
--- a/browser/components/shell/nsGNOMEShellService.cpp
+++ b/browser/components/shell/nsGNOMEShellService.cpp
@@ -289,20 +289,25 @@ nsGNOMEShellService::SetDefaultBrowser(b
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoString brandShortName;
     brandBundle->GetStringFromName("brandShortName", brandShortName);
 
     // use brandShortName as the application id.
     NS_ConvertUTF16toUTF8 id(brandShortName);
     nsCOMPtr<nsIGIOMimeApp> appInfo;
-    rv = giovfs->CreateAppFromCommand(mAppPath,
-                                      id,
-                                      getter_AddRefs(appInfo));
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = giovfs->FindAppFromCommand(mAppPath, getter_AddRefs(appInfo));
+    if (NS_FAILED(rv)) {
+      // Application was not found in the list of installed applications provided
+      // by OS. Fallback to create appInfo from command and name.
+      rv = giovfs->CreateAppFromCommand(mAppPath,
+                                        id,
+                                        getter_AddRefs(appInfo));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
 
     // set handler for the protocols
     for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) {
       if (appProtocols[i].essential || aClaimAllTypes) {
         appInfo->SetAsDefaultForURIScheme(nsDependentCString(appProtocols[i].name));
       }
     }
 
--- a/toolkit/mozapps/handling/content/dialog.js
+++ b/toolkit/mozapps/handling/content/dialog.js
@@ -127,18 +127,20 @@ var dialog = {
           // and users won't visit the handler's URL template, they'll only
           // visit URLs derived from that template (i.e. with %s in the template
           // replaced by the URL of the content being handled).
           elm.setAttribute("image", uri.prePath + "/favicon.ico");
         }
         elm.setAttribute("description", uri.prePath);
       } else if (app instanceof Ci.nsIDBusHandlerApp) {
         elm.setAttribute("description", app.method);
-      } else
+      } else if (!(app instanceof Ci.nsIGIOMimeApp)) {
+        // We support GIO application handler, but no action required there
         throw "unknown handler type";
+      }
 
       items.insertBefore(elm, this._itemChoose);
       if (preferredHandler && app == preferredHandler)
         this.selectedItem = elm;
     }
 
     if (this._handlerInfo.hasDefaultHandler) {
       let elm = document.createElement("richlistitem");
@@ -146,16 +148,49 @@ var dialog = {
       elm.id = "os-default-handler";
       elm.setAttribute("name", this._handlerInfo.defaultDescription);
 
       items.insertBefore(elm, items.firstChild);
       if (this._handlerInfo.preferredAction ==
           Ci.nsIHandlerInfo.useSystemDefault)
           this.selectedItem = elm;
     }
+
+    // Add gio handlers
+    if (Cc["@mozilla.org/gio-service;1"]) {
+      let gIOSvc = Cc["@mozilla.org/gio-service;1"]
+                     .getService(Ci.nsIGIOService);
+      var gioApps = gIOSvc.getAppsForURIScheme(this._URI.scheme);
+      let enumerator = gioApps.enumerate();
+      while (enumerator.hasMoreElements()) {
+        let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp);
+        // OS handler share the same name, it's most likely the same app, skipping...
+        if (handler.name == this._handlerInfo.defaultDescription) {
+          continue;
+        }
+        // Check if the handler is already in possibleHandlers
+        let appAlreadyInHandlers = false;
+        for (let i = possibleHandlers.length - 1; i >= 0; --i) {
+          let app = possibleHandlers.queryElementAt(i, Ci.nsIHandlerApp);
+          // nsGIOMimeApp::Equals is able to compare with nsILocalHandlerApp
+          if (handler.equals(app)) {
+            appAlreadyInHandlers = true;
+            break;
+          }
+        }
+        if (!appAlreadyInHandlers) {
+          let elm = document.createElement("richlistitem");
+          elm.setAttribute("type", "handler");
+          elm.setAttribute("name", handler.name);
+          elm.obj = handler;
+          items.insertBefore(elm, this._itemChoose);
+        }
+      }
+    }
+
     items.ensureSelectedElementIsVisible();
   },
 
  /**
   * Brings up a filepicker and allows a user to choose an application.
   */
   chooseApplication: function chooseApplication() {
     var bundle = document.getElementById("base-strings");
--- a/toolkit/system/gnome/nsGIOService.cpp
+++ b/toolkit/system/gnome/nsGIOService.cpp
@@ -4,52 +4,87 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsGIOService.h"
 #include "nsString.h"
 #include "nsIURI.h"
 #include "nsTArray.h"
 #include "nsIStringEnumerator.h"
 #include "nsAutoPtr.h"
+#include "nsIMIMEInfo.h"
+#include "nsComponentManagerUtils.h"
+#include "nsArray.h"
+#include "nsIFile.h"
 
 #include <gio/gio.h>
 #include <gtk/gtk.h>
 #ifdef MOZ_ENABLE_DBUS
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 #endif
 
 
+/**
+ * Get command without any additional arguments
+ * @param aCommandWithArguments full commandline input string
+ * @param aCommand string for storing command without arguments
+ * @return NS_ERROR_FAILURE when unable to parse commandline
+ */
+static nsresult
+GetCommandFromCommandline(nsACString const& aCommandWithArguments, nsACString& aCommand) {
+  GError *error = nullptr;
+  gchar **argv = nullptr;
+  if (!g_shell_parse_argv(aCommandWithArguments.BeginReading(), nullptr, &argv, &error) ||
+      !argv[0]) {
+    g_warning("Cannot parse command with arguments: %s", error->message);
+    g_error_free(error);
+    g_strfreev(argv);
+    return NS_ERROR_FAILURE;
+  }
+  aCommand.Assign(argv[0]);
+  g_strfreev(argv);
+  return NS_OK;
+}
+
 class nsGIOMimeApp final : public nsIGIOMimeApp
 {
 public:
   NS_DECL_ISUPPORTS
+  NS_DECL_NSIHANDLERAPP
   NS_DECL_NSIGIOMIMEAPP
 
   explicit nsGIOMimeApp(GAppInfo* aApp) : mApp(aApp) {}
 
 private:
   ~nsGIOMimeApp() { g_object_unref(mApp); }
 
   GAppInfo *mApp;
 };
 
-NS_IMPL_ISUPPORTS(nsGIOMimeApp, nsIGIOMimeApp)
+NS_IMPL_ISUPPORTS(nsGIOMimeApp, nsIGIOMimeApp, nsIHandlerApp)
 
 NS_IMETHODIMP
 nsGIOMimeApp::GetId(nsACString& aId)
 {
   aId.Assign(g_app_info_get_id(mApp));
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsGIOMimeApp::GetName(nsACString& aName)
+nsGIOMimeApp::GetName(nsAString& aName)
 {
-  aName.Assign(g_app_info_get_name(mApp));
+  aName.Assign(NS_ConvertUTF8toUTF16(g_app_info_get_name(mApp)));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::SetName(const nsAString& aName)
+{
+  // We don't implement SetName because we're using mGIOMimeApp instance for
+  // obtaining application name
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGIOMimeApp::GetCommand(nsACString& aCommand)
 {
   const char *cmd = g_app_info_get_commandline(mApp);
   if (!cmd)
@@ -79,16 +114,81 @@ nsGIOMimeApp::Launch(const nsACString& a
     g_warning("Cannot launch application: %s", error->message);
     g_error_free(error);
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsGIOMimeApp::GetDetailedDescription(nsAString& aDetailedDescription)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::SetDetailedDescription(const nsAString& aDetailedDescription)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::Equals(nsIHandlerApp* aHandlerApp, bool* _retval)
+{
+  if (!aHandlerApp)
+    return NS_ERROR_FAILURE;
+
+  // Compare with nsILocalHandlerApp instance by name
+  nsCOMPtr<nsILocalHandlerApp> localHandlerApp = do_QueryInterface(aHandlerApp);
+  if (localHandlerApp) {
+    nsAutoString theirName;
+    nsAutoString thisName;
+    GetName(thisName);
+    localHandlerApp->GetName(theirName);
+    *_retval = thisName.Equals(theirName);
+    return NS_OK;
+  }
+
+  // Compare with nsIGIOMimeApp instance by command with stripped arguments
+  nsCOMPtr<nsIGIOMimeApp> gioMimeApp = do_QueryInterface(aHandlerApp);
+  if (gioMimeApp) {
+    nsAutoCString thisCommandline, thisCommand;
+    nsresult rv = GetCommand(thisCommandline);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = GetCommandFromCommandline(thisCommandline,
+                                   thisCommand);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoCString theirCommandline, theirCommand;
+    gioMimeApp->GetCommand(theirCommandline);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = GetCommandFromCommandline(theirCommandline,
+                                   theirCommand);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *_retval = thisCommand.Equals(theirCommand);
+    return NS_OK;
+  }
+
+  // We can only compare with nsILocalHandlerApp and nsGIOMimeApp
+  *_retval = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGIOMimeApp::LaunchWithURI(nsIURI* aUri, nsIInterfaceRequestor* aRequestor)
+{
+  nsCString uri_string;
+  aUri->GetSpec(uri_string);
+  return Launch(uri_string);
+}
+
 class GIOUTF8StringEnumerator final : public nsIUTF8StringEnumerator
 {
   ~GIOUTF8StringEnumerator() = default;
 
 public:
   GIOUTF8StringEnumerator() : mIndex(0) { }
 
   NS_DECL_ISUPPORTS
@@ -278,16 +378,43 @@ nsGIOService::GetAppForURIScheme(const n
     NS_ADDREF(*aApp = mozApp);
   } else {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsGIOService::GetAppsForURIScheme(const nsACString& aURIScheme,
+                                  nsIMutableArray** aResult)
+{
+  nsCOMPtr<nsIMutableArray> handlersArray =
+    do_CreateInstance(NS_ARRAY_CONTRACTID);
+
+  nsAutoCString contentType("x-scheme-handler/");
+  contentType.Append(aURIScheme);
+
+  GList* appInfoList = g_app_info_get_all_for_type(contentType.get());
+  // g_app_info_get_all_for_type returns NULL when no appinfo is found
+  // or error occurs (contentType is NULL). We are fine with empty app list
+  // and we're sure that contentType is not NULL, so we won't return failure.
+  if (appInfoList) {
+    GList* appInfo = appInfoList;
+    while (appInfo) {
+      nsCOMPtr<nsIGIOMimeApp> mimeApp = new nsGIOMimeApp(G_APP_INFO(appInfo->data));
+      handlersArray->AppendElement(mimeApp);
+      appInfo = appInfo->next;
+    }
+    g_list_free(appInfoList);
+  }
+  NS_ADDREF(*aResult = handlersArray);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsGIOService::GetAppForMimeType(const nsACString& aMimeType,
                                 nsIGIOMimeApp**   aApp)
 {
   *aApp = nullptr;
   char *content_type =
     g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
   if (!content_type)
     return NS_ERROR_FAILURE;
@@ -412,64 +539,101 @@ nsGIOService::OrgFreedesktopFileManager1
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return NS_OK;
 #endif
 }
 
 /**
- * Create or find already existing application info for specified command
- * and application name.
- * @param cmd command to execute
- * @param appName application name
- * @param appInfo location where created GAppInfo is stored
- * @return NS_OK when object is created, NS_ERROR_FAILURE otherwise.
+ * Find GIO Mime App from given commandline.
+ * This is different from CreateAppFromCommand because instead of creating the
+ * GIO Mime App in case it's not found in the GIO application list, the method
+ * returns error.
+ * @param aCmd command with parameters used to start the application
+ * @return NS_OK when application is found, NS_ERROR_NOT_AVAILABLE otherwise
  */
 NS_IMETHODIMP
-nsGIOService::CreateAppFromCommand(nsACString const& cmd,
-                                   nsACString const& appName,
-                                   nsIGIOMimeApp**   appInfo)
+nsGIOService::FindAppFromCommand(nsACString const& aCmd,
+                                 nsIGIOMimeApp** aAppInfo)
 {
-  GError *error = nullptr;
-  *appInfo = nullptr;
-
   GAppInfo *app_info = nullptr, *app_info_from_list = nullptr;
   GList *apps = g_app_info_get_all();
   GList *apps_p = apps;
 
   // Try to find relevant and existing GAppInfo in all installed application
   // We do this by comparing each GAppInfo's executable with out own
   while (apps_p) {
     app_info_from_list = (GAppInfo*) apps_p->data;
     if (!app_info) {
       // If the executable is not absolute, get it's full path
       char *executable = g_find_program_in_path(g_app_info_get_executable(app_info_from_list));
 
-      if (executable && strcmp(executable, PromiseFlatCString(cmd).get()) == 0) {
+      if (executable && strcmp(executable, PromiseFlatCString(aCmd).get()) == 0) {
         g_object_ref (app_info_from_list);
         app_info = app_info_from_list;
       }
       g_free(executable);
     }
 
     g_object_unref(app_info_from_list);
     apps_p = apps_p->next;
   }
   g_list_free(apps);
-
-  if (!app_info) {
-    app_info = g_app_info_create_from_commandline(PromiseFlatCString(cmd).get(),
-                                                  PromiseFlatCString(appName).get(),
-                                                  G_APP_INFO_CREATE_SUPPORTS_URIS,
-                                                  &error);
+  if (app_info) {
+    nsGIOMimeApp* app = new nsGIOMimeApp(app_info);
+    NS_ENSURE_TRUE(app, NS_ERROR_OUT_OF_MEMORY);
+    NS_ADDREF(*aAppInfo = app);
+    return NS_OK;
   }
 
+  *aAppInfo = nullptr;
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+/**
+ * Create application info for specified command and application name.
+ * Command arguments are ignored and the "%u" is always added.
+ * @param cmd command to execute
+ * @param appName application name
+ * @param appInfo location where created GAppInfo is stored
+ * @return NS_OK when object is created, NS_ERROR_FILE_NOT_FOUND when executable
+ * is not found in the system path or NS_ERROR_FAILURE otherwise.
+ */
+NS_IMETHODIMP
+nsGIOService::CreateAppFromCommand(nsACString const& cmd,
+                                   nsACString const& appName,
+                                   nsIGIOMimeApp**   appInfo)
+{
+  GError *error = nullptr;
+  *appInfo = nullptr;
+
+  // Using G_APP_INFO_CREATE_SUPPORTS_URIS calling g_app_info_create_from_commandline
+  // appends %u to the cmd even when cmd already contains this parameter.
+  // To avoid that we're going to remove arguments before passing to it.
+  nsAutoCString commandWithoutArgs;
+  nsresult rv = GetCommandFromCommandline(cmd,
+                                          commandWithoutArgs);
+  NS_ENSURE_SUCCESS(rv, rv);
+  GAppInfo *app_info = g_app_info_create_from_commandline(
+      commandWithoutArgs.BeginReading(),
+      PromiseFlatCString(appName).get(),
+      G_APP_INFO_CREATE_SUPPORTS_URIS,
+      &error);
   if (!app_info) {
     g_warning("Cannot create application info from command: %s", error->message);
     g_error_free(error);
     return NS_ERROR_FAILURE;
   }
+
+  // Check if executable exist in path
+  gchar* executableWithFullPath =
+    g_find_program_in_path(commandWithoutArgs.BeginReading());
+  if (!executableWithFullPath) {
+    return NS_ERROR_FILE_NOT_FOUND;
+  }
+  g_free(executableWithFullPath);
+
   nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info);
   NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(*appInfo = mozApp);
   return NS_OK;
 }
--- a/uriloader/exthandler/nsHandlerService-json.js
+++ b/uriloader/exthandler/nsHandlerService-json.js
@@ -426,16 +426,21 @@ HandlerService.prototype = {
     } else if (handler instanceof Ci.nsIDBusHandlerApp) {
       return {
         name: handler.name,
         service: handler.service,
         method: handler.method,
         objectPath: handler.objectPath,
         dBusInterface: handler.dBusInterface,
       };
+    } else if (handler instanceof Ci.nsIGIOMimeApp) {
+      return {
+        name: handler.name,
+        command: handler.command,
+      };
     }
     // If the handler is an unknown handler type, return null.
     // Android default application handler is the case.
     return null;
   },
 
   /**
    * @param handlerObj
@@ -462,16 +467,25 @@ HandlerService.prototype = {
       handlerApp.uriTemplate = handlerObj.uriTemplate;
     } else if ("service" in handlerObj) {
       handlerApp = Cc["@mozilla.org/uriloader/dbus-handler-app;1"]
                      .createInstance(Ci.nsIDBusHandlerApp);
       handlerApp.service = handlerObj.service;
       handlerApp.method = handlerObj.method;
       handlerApp.objectPath = handlerObj.objectPath;
       handlerApp.dBusInterface = handlerObj.dBusInterface;
+    } else if ("command" in handlerObj &&
+               "@mozilla.org/gio-service;1" in Cc) {
+      try {
+        handlerApp = Cc["@mozilla.org/gio-service;1"]
+                       .getService(Ci.nsIGIOService)
+                       .createAppFromCommand(handlerObj.command, handlerObj.name);
+      } catch (ex) {
+        return null;
+      }
     } else {
       return null;
     }
 
     handlerApp.name = handlerObj.name;
     return handlerApp;
   },
 
--- a/uriloader/exthandler/tests/unit/test_handlerService_json.js
+++ b/uriloader/exthandler/tests/unit/test_handlerService_json.js
@@ -131,8 +131,63 @@ add_task(async function test_migration_r
 
   // Repeat the migration with the JSON file present.
   await unloadHandlerStore();
   await unloadHandlerStoreRDF();
   Services.prefs.setBoolPref("gecko.handlerService.migrated", false);
   await assertAllHandlerInfosMatchDefaultHandlers();
   Assert.ok(Services.prefs.getBoolPref("gecko.handlerService.migrated"));
 });
+
+/**
+ * Test saving and reloading an instance of nsIGIOMimeApp.
+ */
+add_task(async function test_store_gioHandlerApp() {
+  if (!("@mozilla.org/gio-service;1" in Cc)) {
+    info("Skipping test because it does not apply to this platform.");
+    return;
+  }
+
+  // Create dummy exec file that following won't fail because file not found error
+  let dummyHandlerFile = FileUtils.getFile("TmpD", ["dummyHandler"]);
+  dummyHandlerFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("777", 8));
+
+  // Set up an nsIGIOMimeApp instance for testing.
+  let handlerApp = Cc["@mozilla.org/gio-service;1"]
+                     .getService(Ci.nsIGIOService)
+                     .createAppFromCommand(dummyHandlerFile.path, "Dummy GIO handler");
+  let expectedGIOMimeHandlerApp = {
+    name: handlerApp.name,
+    command: handlerApp.command,
+  };
+
+  await deleteHandlerStore();
+
+  let handlerInfo = getKnownHandlerInfo("example/new");
+  handlerInfo.preferredApplicationHandler = handlerApp;
+  handlerInfo.possibleApplicationHandlers.appendElement(handlerApp);
+  handlerInfo.possibleApplicationHandlers.appendElement(webHandlerApp);
+  gHandlerService.store(handlerInfo);
+
+  await unloadHandlerStore();
+
+  let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new");
+  HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, {
+    type: "example/new",
+    preferredAction: Ci.nsIHandlerInfo.saveToDisk,
+    alwaysAskBeforeHandling: false,
+    preferredApplicationHandler: expectedGIOMimeHandlerApp,
+    possibleApplicationHandlers: [expectedGIOMimeHandlerApp, webHandlerApp],
+  });
+
+  await OS.File.remove(dummyHandlerFile.path);
+
+  // After removing dummyHandlerFile, the handler should disappear from the
+  // list of possibleApplicationHandlers and preferredAppHandler should be null.
+  actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new");
+  HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, {
+    type: "example/new",
+    preferredAction: Ci.nsIHandlerInfo.saveToDisk,
+    alwaysAskBeforeHandling: false,
+    preferredApplicationHandler: null,
+    possibleApplicationHandlers: [webHandlerApp],
+  });
+});
--- a/uriloader/exthandler/unix/nsGNOMERegistry.cpp
+++ b/uriloader/exthandler/unix/nsGNOMERegistry.cpp
@@ -39,24 +39,21 @@ nsGNOMERegistry::LoadURL(nsIURI *aURL)
 /* static */ void
 nsGNOMERegistry::GetAppDescForScheme(const nsACString& aScheme,
                                      nsAString& aDesc)
 {
   nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
   if (!giovfs)
     return;
 
-  nsAutoCString name;
   nsCOMPtr<nsIGIOMimeApp> app;
   if (NS_FAILED(giovfs->GetAppForURIScheme(aScheme, getter_AddRefs(app))))
     return;
 
-  app->GetName(name);
-
-  CopyUTF8toUTF16(name, aDesc);
+  app->GetName(aDesc);
 }
 
 
 /* static */ already_AddRefed<nsMIMEInfoBase>
 nsGNOMERegistry::GetFromExtension(const nsACString& aFileExt)
 {
   nsAutoCString mimeType;
   nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
@@ -80,30 +77,30 @@ nsGNOMERegistry::GetFromExtension(const 
 }
 
 /* static */ already_AddRefed<nsMIMEInfoBase>
 nsGNOMERegistry::GetFromType(const nsACString& aMIMEType)
 {
   RefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(aMIMEType);
   NS_ENSURE_TRUE(mimeInfo, nullptr);
 
-  nsAutoCString name;
+  nsAutoString name;
   nsAutoCString description;
 
   nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
   if (!giovfs) {
     return nullptr;
   }
 
   nsCOMPtr<nsIGIOMimeApp> gioHandlerApp;
   if (NS_FAILED(giovfs->GetAppForMimeType(aMIMEType, getter_AddRefs(gioHandlerApp))) ||
       !gioHandlerApp) {
     return nullptr;
   }
   gioHandlerApp->GetName(name);
   giovfs->GetDescriptionForMimeType(aMIMEType, description);
 
-  mimeInfo->SetDefaultDescription(NS_ConvertUTF8toUTF16(name));
+  mimeInfo->SetDefaultDescription(name);
   mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
   mimeInfo->SetDescription(NS_ConvertUTF8toUTF16(description));
 
   return mimeInfo.forget();
 }
--- a/xpcom/system/nsIGIOService.idl
+++ b/xpcom/system/nsIGIOService.idl
@@ -1,31 +1,32 @@
 /* -*- Mode: IDL; 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/. */
 
 #include "nsISupports.idl"
+#include "nsIMIMEInfo.idl"
 
 interface nsIUTF8StringEnumerator;
 interface nsIURI;
+interface nsIMutableArray;
 
 /* nsIGIOMimeApp holds information about an application that is looked up
    with nsIGIOService::GetAppForMimeType. */
 // 66009894-9877-405b-9321-bf30420e34e6 prev uuid
 
-[scriptable, uuid(ca6bad0c-8a48-48ac-82c7-27bb8f510fbe)] 
-interface nsIGIOMimeApp : nsISupports
+[scriptable, uuid(ca6bad0c-8a48-48ac-82c7-27bb8f510fbe)]
+interface nsIGIOMimeApp : nsIHandlerApp
 {
   const long EXPECTS_URIS  = 0;
   const long EXPECTS_PATHS = 1;
   const long EXPECTS_URIS_FOR_NON_FILES = 2;
 
   readonly attribute AUTF8String         id;
-  readonly attribute AUTF8String         name;
   readonly attribute AUTF8String         command;
   readonly attribute long                expectsURIs;  // see constants above
   readonly attribute nsIUTF8StringEnumerator supportedURISchemes;
 
   void launch(in AUTF8String uri);
   void setAsDefaultForMimeType(in AUTF8String mimeType);
   void setAsDefaultForFileExtensions(in AUTF8String extensions);
   void setAsDefaultForURIScheme(in AUTF8String uriScheme);
@@ -52,23 +53,29 @@ interface nsIGIOService : nsISupports
 
   /* Obtain the MIME type registered for an extension.  The extension
      should not include a leading dot. */
   AUTF8String        getMimeTypeFromExtension(in AUTF8String extension);
 
   /* Obtain the preferred application for opening a given URI scheme */
   nsIGIOMimeApp      getAppForURIScheme(in AUTF8String aURIScheme);
 
+  /* Obtain list of application capable of opening given URI scheme */
+  nsIMutableArray    getAppsForURIScheme(in AUTF8String aURIScheme);
+
   /* Obtain the preferred application for opening a given MIME type */
   nsIGIOMimeApp      getAppForMimeType(in AUTF8String mimeType);
 
-  /* Obtain the preferred application for opening a given MIME type */
-  nsIGIOMimeApp      createAppFromCommand(in AUTF8String cmd, 
+  /* Create application info for given command and name */
+  nsIGIOMimeApp      createAppFromCommand(in AUTF8String cmd,
                                           in AUTF8String appName);
 
+  /* Find the application info by given command */
+  nsIGIOMimeApp      findAppFromCommand(in AUTF8String cmd);
+
   /* Obtain a description for the given MIME type */
   AUTF8String        getDescriptionForMimeType(in AUTF8String mimeType);
 
   /*** Misc. methods ***/
 
   /* Open the given URI in the default application */
   void               showURI(in nsIURI uri);
   [noscript] void    showURIForInput(in ACString uri);