michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsGIOService.h" michael@0: #include "nsStringAPI.h" michael@0: #include "nsIURI.h" michael@0: #include "nsTArray.h" michael@0: #include "nsIStringEnumerator.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #include michael@0: #include michael@0: #ifdef MOZ_ENABLE_DBUS michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: michael@0: char * michael@0: get_content_type_from_mime_type(const char *mimeType) michael@0: { michael@0: GList* contentTypes = g_content_types_get_registered(); michael@0: GList* ct_ptr = contentTypes; michael@0: char* foundContentType = nullptr; michael@0: michael@0: while (ct_ptr) { michael@0: char *mimeTypeFromContentType = g_content_type_get_mime_type((char*)ct_ptr->data); michael@0: if (strcmp(mimeTypeFromContentType, mimeType) == 0) { michael@0: foundContentType = g_strdup((char*)ct_ptr->data); michael@0: g_free(mimeTypeFromContentType); michael@0: break; michael@0: } michael@0: g_free(mimeTypeFromContentType); michael@0: ct_ptr = ct_ptr->next; michael@0: } michael@0: g_list_foreach(contentTypes, (GFunc) g_free, nullptr); michael@0: g_list_free(contentTypes); michael@0: return foundContentType; michael@0: } michael@0: michael@0: class nsGIOMimeApp MOZ_FINAL : public nsIGIOMimeApp michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIGIOMIMEAPP michael@0: michael@0: nsGIOMimeApp(GAppInfo* aApp) : mApp(aApp) {} michael@0: ~nsGIOMimeApp() { g_object_unref(mApp); } michael@0: michael@0: private: michael@0: GAppInfo *mApp; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsGIOMimeApp, nsIGIOMimeApp) michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::GetId(nsACString& aId) michael@0: { michael@0: aId.Assign(g_app_info_get_id(mApp)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::GetName(nsACString& aName) michael@0: { michael@0: aName.Assign(g_app_info_get_name(mApp)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::GetCommand(nsACString& aCommand) michael@0: { michael@0: const char *cmd = g_app_info_get_commandline(mApp); michael@0: if (!cmd) michael@0: return NS_ERROR_FAILURE; michael@0: aCommand.Assign(cmd); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::GetExpectsURIs(int32_t* aExpects) michael@0: { michael@0: *aExpects = g_app_info_supports_uris(mApp); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::Launch(const nsACString& aUri) michael@0: { michael@0: GList uris = { 0 }; michael@0: PromiseFlatCString flatUri(aUri); michael@0: uris.data = const_cast(flatUri.get()); michael@0: michael@0: GError *error = nullptr; michael@0: gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error); michael@0: michael@0: if (!result) { michael@0: g_warning("Cannot launch application: %s", error->message); michael@0: g_error_free(error); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class GIOUTF8StringEnumerator MOZ_FINAL : public nsIUTF8StringEnumerator michael@0: { michael@0: public: michael@0: GIOUTF8StringEnumerator() : mIndex(0) { } michael@0: ~GIOUTF8StringEnumerator() { } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIUTF8STRINGENUMERATOR michael@0: michael@0: nsTArray mStrings; michael@0: uint32_t mIndex; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(GIOUTF8StringEnumerator, nsIUTF8StringEnumerator) michael@0: michael@0: NS_IMETHODIMP michael@0: GIOUTF8StringEnumerator::HasMore(bool* aResult) michael@0: { michael@0: *aResult = mIndex < mStrings.Length(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GIOUTF8StringEnumerator::GetNext(nsACString& aResult) michael@0: { michael@0: if (mIndex >= mStrings.Length()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: aResult.Assign(mStrings[mIndex]); michael@0: ++mIndex; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::GetSupportedURISchemes(nsIUTF8StringEnumerator** aSchemes) michael@0: { michael@0: *aSchemes = nullptr; michael@0: michael@0: nsRefPtr array = new GIOUTF8StringEnumerator(); michael@0: NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: GVfs *gvfs = g_vfs_get_default(); michael@0: michael@0: if (!gvfs) { michael@0: g_warning("Cannot get GVfs object."); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: const gchar* const * uri_schemes = g_vfs_get_supported_uri_schemes(gvfs); michael@0: michael@0: while (*uri_schemes != nullptr) { michael@0: if (!array->mStrings.AppendElement(*uri_schemes)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: uri_schemes++; michael@0: } michael@0: michael@0: NS_ADDREF(*aSchemes = array); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::SetAsDefaultForMimeType(nsACString const& aMimeType) michael@0: { michael@0: char *content_type = michael@0: get_content_type_from_mime_type(PromiseFlatCString(aMimeType).get()); michael@0: if (!content_type) michael@0: return NS_ERROR_FAILURE; michael@0: GError *error = nullptr; michael@0: g_app_info_set_as_default_for_type(mApp, michael@0: content_type, michael@0: &error); michael@0: if (error) { michael@0: g_warning("Cannot set application as default for MIME type (%s): %s", michael@0: PromiseFlatCString(aMimeType).get(), michael@0: error->message); michael@0: g_error_free(error); michael@0: g_free(content_type); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: g_free(content_type); michael@0: return NS_OK; michael@0: } michael@0: /** michael@0: * Set default application for files with given extensions michael@0: * @param fileExts string of space separated extensions michael@0: * @return NS_OK when application was set as default for given extensions, michael@0: * NS_ERROR_FAILURE otherwise michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::SetAsDefaultForFileExtensions(nsACString const& fileExts) michael@0: { michael@0: GError *error = nullptr; michael@0: char *extensions = g_strdup(PromiseFlatCString(fileExts).get()); michael@0: char *ext_pos = extensions; michael@0: char *space_pos; michael@0: michael@0: while ( (space_pos = strchr(ext_pos, ' ')) || (*ext_pos != '\0') ) { michael@0: if (space_pos) { michael@0: *space_pos = '\0'; michael@0: } michael@0: g_app_info_set_as_default_for_extension(mApp, ext_pos, &error); michael@0: if (error) { michael@0: g_warning("Cannot set application as default for extension (%s): %s", michael@0: ext_pos, michael@0: error->message); michael@0: g_error_free(error); michael@0: g_free(extensions); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (space_pos) { michael@0: ext_pos = space_pos + 1; michael@0: } else { michael@0: *ext_pos = '\0'; michael@0: } michael@0: } michael@0: g_free(extensions); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Set default application for URI's of a particular scheme michael@0: * @param aURIScheme string containing the URI scheme michael@0: * @return NS_OK when application was set as default for URI scheme, michael@0: * NS_ERROR_FAILURE otherwise michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsGIOMimeApp::SetAsDefaultForURIScheme(nsACString const& aURIScheme) michael@0: { michael@0: GError *error = nullptr; michael@0: nsAutoCString contentType("x-scheme-handler/"); michael@0: contentType.Append(aURIScheme); michael@0: michael@0: g_app_info_set_as_default_for_type(mApp, michael@0: contentType.get(), michael@0: &error); michael@0: if (error) { michael@0: g_warning("Cannot set application as default for URI scheme (%s): %s", michael@0: PromiseFlatCString(aURIScheme).get(), michael@0: error->message); michael@0: g_error_free(error); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsGIOService, nsIGIOService) michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOService::GetMimeTypeFromExtension(const nsACString& aExtension, michael@0: nsACString& aMimeType) michael@0: { michael@0: nsAutoCString fileExtToUse("file."); michael@0: fileExtToUse.Append(aExtension); michael@0: michael@0: gboolean result_uncertain; michael@0: char *content_type = g_content_type_guess(fileExtToUse.get(), michael@0: nullptr, michael@0: 0, michael@0: &result_uncertain); michael@0: if (!content_type) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: char *mime_type = g_content_type_get_mime_type(content_type); michael@0: if (!mime_type) { michael@0: g_free(content_type); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aMimeType.Assign(mime_type); michael@0: michael@0: g_free(mime_type); michael@0: g_free(content_type); michael@0: michael@0: return NS_OK; michael@0: } michael@0: // used in nsGNOMERegistry michael@0: // ----------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsGIOService::GetAppForURIScheme(const nsACString& aURIScheme, michael@0: nsIGIOMimeApp** aApp) michael@0: { michael@0: *aApp = nullptr; michael@0: michael@0: GAppInfo *app_info = g_app_info_get_default_for_uri_scheme( michael@0: PromiseFlatCString(aURIScheme).get()); michael@0: if (app_info) { michael@0: nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info); michael@0: NS_ADDREF(*aApp = mozApp); michael@0: } else { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOService::GetAppForMimeType(const nsACString& aMimeType, michael@0: nsIGIOMimeApp** aApp) michael@0: { michael@0: *aApp = nullptr; michael@0: char *content_type = michael@0: get_content_type_from_mime_type(PromiseFlatCString(aMimeType).get()); michael@0: if (!content_type) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: GAppInfo *app_info = g_app_info_get_default_for_type(content_type, false); michael@0: if (app_info) { michael@0: nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info); michael@0: NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY); michael@0: NS_ADDREF(*aApp = mozApp); michael@0: } else { michael@0: g_free(content_type); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: g_free(content_type); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOService::GetDescriptionForMimeType(const nsACString& aMimeType, michael@0: nsACString& aDescription) michael@0: { michael@0: char *content_type = michael@0: get_content_type_from_mime_type(PromiseFlatCString(aMimeType).get()); michael@0: if (!content_type) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: char *desc = g_content_type_get_description(content_type); michael@0: if (!desc) { michael@0: g_free(content_type); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aDescription.Assign(desc); michael@0: g_free(content_type); michael@0: g_free(desc); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOService::ShowURI(nsIURI* aURI) michael@0: { michael@0: nsAutoCString spec; michael@0: aURI->GetSpec(spec); michael@0: GError *error = nullptr; michael@0: if (!g_app_info_launch_default_for_uri(spec.get(), nullptr, &error)) { michael@0: g_warning("Could not launch default application for URI: %s", error->message); michael@0: g_error_free(error); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOService::ShowURIForInput(const nsACString& aUri) michael@0: { michael@0: GFile *file = g_file_new_for_commandline_arg(PromiseFlatCString(aUri).get()); michael@0: char* spec = g_file_get_uri(file); michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: GError *error = nullptr; michael@0: michael@0: g_app_info_launch_default_for_uri(spec, nullptr, &error); michael@0: if (error) { michael@0: g_warning("Cannot launch default application: %s", error->message); michael@0: g_error_free(error); michael@0: } else { michael@0: rv = NS_OK; michael@0: } michael@0: g_object_unref(file); michael@0: g_free(spec); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGIOService::OrgFreedesktopFileManager1ShowItems(const nsACString& aPath) michael@0: { michael@0: #ifndef MOZ_ENABLE_DBUS michael@0: return NS_ERROR_FAILURE; michael@0: #else michael@0: GError* error = nullptr; michael@0: static bool org_freedesktop_FileManager1_exists = true; michael@0: michael@0: if (!org_freedesktop_FileManager1_exists) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: DBusGConnection* dbusGConnection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); michael@0: michael@0: if (!dbusGConnection) { michael@0: if (error) { michael@0: g_printerr("Failed to open connection to session bus: %s\n", error->message); michael@0: g_error_free(error); michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: char *uri = g_filename_to_uri(PromiseFlatCString(aPath).get(), nullptr, nullptr); michael@0: if (uri == nullptr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DBusConnection* dbusConnection = dbus_g_connection_get_connection(dbusGConnection); michael@0: // Make sure we do not exit the entire program if DBus connection get lost. michael@0: dbus_connection_set_exit_on_disconnect(dbusConnection, false); michael@0: michael@0: DBusGProxy* dbusGProxy = dbus_g_proxy_new_for_name(dbusGConnection, michael@0: "org.freedesktop.FileManager1", michael@0: "/org/freedesktop/FileManager1", michael@0: "org.freedesktop.FileManager1"); michael@0: michael@0: const char *uris[2] = { uri, nullptr }; michael@0: gboolean rv_dbus_call = dbus_g_proxy_call (dbusGProxy, "ShowItems", nullptr, G_TYPE_STRV, uris, michael@0: G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID); michael@0: michael@0: g_object_unref(dbusGProxy); michael@0: dbus_g_connection_unref(dbusGConnection); michael@0: g_free(uri); michael@0: michael@0: if (!rv_dbus_call) { michael@0: org_freedesktop_FileManager1_exists = false; michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Create or find already existing application info for specified command michael@0: * and application name. michael@0: * @param cmd command to execute michael@0: * @param appName application name michael@0: * @param appInfo location where created GAppInfo is stored michael@0: * @return NS_OK when object is created, NS_ERROR_FAILURE otherwise. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsGIOService::CreateAppFromCommand(nsACString const& cmd, michael@0: nsACString const& appName, michael@0: nsIGIOMimeApp** appInfo) michael@0: { michael@0: GError *error = nullptr; michael@0: *appInfo = nullptr; michael@0: michael@0: GAppInfo *app_info = nullptr, *app_info_from_list = nullptr; michael@0: GList *apps = g_app_info_get_all(); michael@0: GList *apps_p = apps; michael@0: michael@0: // Try to find relevant and existing GAppInfo in all installed application michael@0: // We do this by comparing each GAppInfo's executable with out own michael@0: while (apps_p) { michael@0: app_info_from_list = (GAppInfo*) apps_p->data; michael@0: if (!app_info) { michael@0: // If the executable is not absolute, get it's full path michael@0: char *executable = g_find_program_in_path(g_app_info_get_executable(app_info_from_list)); michael@0: michael@0: if (executable && strcmp(executable, PromiseFlatCString(cmd).get()) == 0) { michael@0: g_object_ref (app_info_from_list); michael@0: app_info = app_info_from_list; michael@0: } michael@0: g_free(executable); michael@0: } michael@0: michael@0: g_object_unref(app_info_from_list); michael@0: apps_p = apps_p->next; michael@0: } michael@0: g_list_free(apps); michael@0: michael@0: if (!app_info) { michael@0: app_info = g_app_info_create_from_commandline(PromiseFlatCString(cmd).get(), michael@0: PromiseFlatCString(appName).get(), michael@0: G_APP_INFO_CREATE_SUPPORTS_URIS, michael@0: &error); michael@0: } michael@0: michael@0: if (!app_info) { michael@0: g_warning("Cannot create application info from command: %s", error->message); michael@0: g_error_free(error); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info); michael@0: NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY); michael@0: NS_ADDREF(*appInfo = mozApp); michael@0: return NS_OK; michael@0: }