From e55996cc86916e58d3e4b78197ebfa633f067c5a Mon Sep 17 00:00:00 2001 From: Jan Horak Date: Fri, 12 Jan 2018 16:32:53 +0100 Subject: [PATCH] Bug 1411579 - add system handler when Firefox runs in flatpak; r=stransky Firefox in Flatpak sandboxed environment does not get the list of installed applications on the system because application should know about the environment as little as possible. Introducing nsFlatpakHandlerApp which forwards requests for opening downloaded files to the system by utilizing gtk_show_uri fuction. This changeset also removes nsIGIOMimeApp::Launch method from the interface because it can be fully replaced with LaunchWithUri from nsIHandlerApp interface. The TMPDIR where files are downloaded when user choose to open them needs to be accessible from sandbox and host. The default settings TMPDIR=/tmp is accessible only to the sandbox. To workaround for is to set TMPDIR environment variable to $XDG_CACHE_HOME/tmp before executing Firefox. MozReview-Commit-ID: CSBv0QcETpd --HG-- extra : rebase_source : 8155c33fa9c402d2668bdfb07094ba6758fe6203 --- .../components/shell/nsGNOMEShellService.cpp | 12 +- toolkit/system/gnome/nsGIOService.cpp | 155 +++++++++++++++--- uriloader/exthandler/unix/nsGNOMERegistry.cpp | 12 +- uriloader/exthandler/unix/nsMIMEInfoUnix.cpp | 6 +- widget/gtk/mozgtk/mozgtk.c | 1 + xpcom/system/nsIGIOService.idl | 5 +- 6 files changed, 149 insertions(+), 42 deletions(-) diff --git a/browser/components/shell/nsGNOMEShellService.cpp b/browser/components/shell/nsGNOMEShellService.cpp index ee237bbf3d7a..5c06fab29281 100644 --- a/browser/components/shell/nsGNOMEShellService.cpp +++ b/browser/components/shell/nsGNOMEShellService.cpp @@ -229,8 +229,10 @@ nsGNOMEShellService::IsDefaultBrowser(bool aStartupCheck, if (giovfs) { handler.Truncate(); + nsCOMPtr handlerApp; giovfs->GetAppForURIScheme(nsDependentCString(appProtocols[i].name), - getter_AddRefs(gioApp)); + getter_AddRefs(handlerApp)); + gioApp = do_QueryInterface(handlerApp); if (!gioApp) return NS_OK; @@ -571,10 +573,10 @@ nsGNOMEShellService::OpenApplication(int32_t aApplication) nsCOMPtr giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); if (giovfs) { - nsCOMPtr gioApp; - giovfs->GetAppForURIScheme(scheme, getter_AddRefs(gioApp)); - if (gioApp) - return gioApp->Launch(EmptyCString()); + nsCOMPtr handlerApp; + giovfs->GetAppForURIScheme(scheme, getter_AddRefs(handlerApp)); + if (handlerApp) + return handlerApp->LaunchWithURI(nullptr, nullptr); } nsCOMPtr gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); diff --git a/toolkit/system/gnome/nsGIOService.cpp b/toolkit/system/gnome/nsGIOService.cpp index bb2882bf170d..d1048bfab699 100644 --- a/toolkit/system/gnome/nsGIOService.cpp +++ b/toolkit/system/gnome/nsGIOService.cpp @@ -13,6 +13,7 @@ #include "nsComponentManagerUtils.h" #include "nsArray.h" #include "nsIFile.h" +#include "nsPrintfCString.h" #include #include @@ -21,6 +22,93 @@ #include #endif +// We use the same code as gtk_should_use_portal() to detect if we're in flatpak env +// https://github.com/GNOME/gtk/blob/e0ce028c88858b96aeda9e41734a39a3a04f705d/gtk/gtkprivate.c#L272 +static bool GetShouldUseFlatpakPortal() { + bool shouldUsePortal; + char *path; + path = g_build_filename(g_get_user_runtime_dir(), "flatpak-info", nullptr); + if (g_file_test(path, G_FILE_TEST_EXISTS)) { + shouldUsePortal = true; + } else { + shouldUsePortal = (g_getenv("GTK_USE_PORTAL") != nullptr); + } + g_free(path); + return shouldUsePortal; +} + +static bool ShouldUseFlatpakPortal() { + static bool sShouldUseFlatpakPortal = GetShouldUseFlatpakPortal(); + return sShouldUseFlatpakPortal; +} + +class nsFlatpakHandlerApp : public nsIHandlerApp +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIHANDLERAPP + nsFlatpakHandlerApp() = default; +private: + virtual ~nsFlatpakHandlerApp() = default; + +}; + +NS_IMPL_ISUPPORTS(nsFlatpakHandlerApp, nsIHandlerApp) + +NS_IMETHODIMP +nsFlatpakHandlerApp::GetName(nsAString& aName) +{ + aName.AssignLiteral("System Handler"); + return NS_OK; +} + +NS_IMETHODIMP +nsFlatpakHandlerApp::SetName(const nsAString& aName) +{ + // We don't implement SetName because flatpak system handler name is fixed + return NS_OK; +} + +NS_IMETHODIMP +nsFlatpakHandlerApp::GetDetailedDescription(nsAString& aDetailedDescription) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsFlatpakHandlerApp::SetDetailedDescription(const nsAString& aDetailedDescription) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsFlatpakHandlerApp::Equals(nsIHandlerApp* aHandlerApp, bool* _retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsFlatpakHandlerApp::LaunchWithURI(nsIURI* aUri, nsIInterfaceRequestor* aRequestor) +{ + nsCString spec; + aUri->GetSpec(spec); + GError *error = nullptr; + + // The TMPDIR where files are downloaded when user choose to open them + // needs to be accessible from sandbox and host. The default settings + // TMPDIR=/tmp is accessible only to the sandbox. That can be the reason + // why the gtk_show_uri fails there. + // The workaround is to set TMPDIR environment variable in sandbox to + // $XDG_CACHE_HOME/tmp before executing Firefox. + gtk_show_uri(nullptr, spec.get(), GDK_CURRENT_TIME, &error); + if (error) { + NS_WARNING(nsPrintfCString("Cannot launch flatpak handler: %s", + error->message).get()); + g_error_free(error); + return NS_ERROR_FAILURE; + } + return NS_OK; +} /** * Get command without any additional arguments @@ -100,25 +188,6 @@ nsGIOMimeApp::GetExpectsURIs(int32_t* aExpects) return NS_OK; } -NS_IMETHODIMP -nsGIOMimeApp::Launch(const nsACString& aUri) -{ - GList uris = { 0 }; - nsPromiseFlatCString flatUri(aUri); - uris.data = const_cast(flatUri.get()); - - GError *error = nullptr; - gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error); - - if (!result) { - 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) { @@ -179,9 +248,22 @@ nsGIOMimeApp::Equals(nsIHandlerApp* aHandlerApp, bool* _retval) NS_IMETHODIMP nsGIOMimeApp::LaunchWithURI(nsIURI* aUri, nsIInterfaceRequestor* aRequestor) { - nsCString uri_string; - aUri->GetSpec(uri_string); - return Launch(uri_string); + GList uris = { 0 }; + nsCString spec; + aUri->GetSpec(spec); + //nsPromiseFlatCString flatUri(aUri); + uris.data = const_cast(spec.get()); + + GError *error = nullptr; + gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error); + + if (!result) { + g_warning("Cannot launch application: %s", error->message); + g_error_free(error); + return NS_ERROR_FAILURE; + } + + return NS_OK; } class GIOUTF8StringEnumerator final : public nsIUTF8StringEnumerator @@ -367,10 +449,19 @@ nsGIOService::GetMimeTypeFromExtension(const nsACString& aExtension, // ----------------------------------------------------------------------------- NS_IMETHODIMP nsGIOService::GetAppForURIScheme(const nsACString& aURIScheme, - nsIGIOMimeApp** aApp) + nsIHandlerApp** aApp) { *aApp = nullptr; + // Application in flatpak sandbox does not have access to the list + // of installed applications on the system. We use generic + // nsFlatpakHandlerApp which forwards launch call to the system. + if (ShouldUseFlatpakPortal()) { + nsFlatpakHandlerApp *mozApp = new nsFlatpakHandlerApp(); + NS_ADDREF(*aApp = mozApp); + return NS_OK; + } + GAppInfo *app_info = g_app_info_get_default_for_uri_scheme( PromiseFlatCString(aURIScheme).get()); if (app_info) { @@ -386,6 +477,13 @@ NS_IMETHODIMP nsGIOService::GetAppsForURIScheme(const nsACString& aURIScheme, nsIMutableArray** aResult) { + // We don't need to return the nsFlatpakHandlerApp here because + // it would be skipped by the callers anyway. + // The preferred handler is provided by GetAppForURIScheme. + // This method returns all possible application handlers + // including preferred one. The callers skips the preferred + // handler in this list to avoid duplicate records in the list + // they create. nsCOMPtr handlersArray = do_CreateInstance(NS_ARRAY_CONTRACTID); @@ -411,9 +509,18 @@ nsGIOService::GetAppsForURIScheme(const nsACString& aURIScheme, NS_IMETHODIMP nsGIOService::GetAppForMimeType(const nsACString& aMimeType, - nsIGIOMimeApp** aApp) + nsIHandlerApp** aApp) { *aApp = nullptr; + + // Flatpak does not reveal installed application to the sandbox, + // we need to create generic system handler. + if (ShouldUseFlatpakPortal()) { + nsFlatpakHandlerApp *mozApp = new nsFlatpakHandlerApp(); + NS_ADDREF(*aApp = mozApp); + return NS_OK; + } + char *content_type = g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get()); if (!content_type) diff --git a/uriloader/exthandler/unix/nsGNOMERegistry.cpp b/uriloader/exthandler/unix/nsGNOMERegistry.cpp index 5f1a4148bf2e..908b241f31d8 100644 --- a/uriloader/exthandler/unix/nsGNOMERegistry.cpp +++ b/uriloader/exthandler/unix/nsGNOMERegistry.cpp @@ -18,7 +18,7 @@ nsGNOMERegistry::HandlerExists(const char *aProtocolScheme) return false; } - nsCOMPtr app; + nsCOMPtr app; return NS_SUCCEEDED(giovfs->GetAppForURIScheme(nsDependentCString(aProtocolScheme), getter_AddRefs(app))); } @@ -44,7 +44,7 @@ nsGNOMERegistry::GetAppDescForScheme(const nsACString& aScheme, if (!giovfs) return; - nsCOMPtr app; + nsCOMPtr app; if (NS_FAILED(giovfs->GetAppForURIScheme(aScheme, getter_AddRefs(app)))) return; @@ -90,12 +90,12 @@ nsGNOMERegistry::GetFromType(const nsACString& aMIMEType) return nullptr; } - nsCOMPtr gioHandlerApp; - if (NS_FAILED(giovfs->GetAppForMimeType(aMIMEType, getter_AddRefs(gioHandlerApp))) || - !gioHandlerApp) { + nsCOMPtr handlerApp; + if (NS_FAILED(giovfs->GetAppForMimeType(aMIMEType, getter_AddRefs(handlerApp))) || + !handlerApp) { return nullptr; } - gioHandlerApp->GetName(name); + handlerApp->GetName(name); giovfs->GetDescriptionForMimeType(aMIMEType, description); mimeInfo->SetDefaultDescription(name); diff --git a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp index 0d508a0f59e8..f102815c7dd4 100644 --- a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp +++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp @@ -76,14 +76,12 @@ nsMIMEInfoUnix::LaunchDefaultWithFile(nsIFile *aFile) nsCOMPtr uri; rv = ioservice->NewFileURI(aFile, getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); - nsAutoCString uriSpec; - uri->GetSpec(uriSpec); - nsCOMPtr app; + nsCOMPtr app; if (NS_FAILED(giovfs->GetAppForMimeType(mSchemeOrType, getter_AddRefs(app))) || !app) { return NS_ERROR_FILE_NOT_FOUND; } - return app->Launch(uriSpec); + return app->LaunchWithURI(uri, nullptr); } diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c index 07f053457fd3..bf0298c7aac8 100644 --- a/widget/gtk/mozgtk/mozgtk.c +++ b/widget/gtk/mozgtk/mozgtk.c @@ -391,6 +391,7 @@ STUB(gtk_separator_menu_item_new) STUB(gtk_separator_tool_item_new) STUB(gtk_settings_get_default) STUB(gtk_settings_get_for_screen) +STUB(gtk_show_uri) STUB(gtk_socket_add_id) STUB(gtk_socket_get_id) STUB(gtk_socket_get_type) diff --git a/xpcom/system/nsIGIOService.idl b/xpcom/system/nsIGIOService.idl index 5ed23b225c65..ebb1c8f0d89d 100644 --- a/xpcom/system/nsIGIOService.idl +++ b/xpcom/system/nsIGIOService.idl @@ -26,7 +26,6 @@ interface nsIGIOMimeApp : nsIHandlerApp 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); @@ -56,13 +55,13 @@ interface nsIGIOService : nsISupports AUTF8String getMimeTypeFromExtension(in AUTF8String extension); /* Obtain the preferred application for opening a given URI scheme */ - nsIGIOMimeApp getAppForURIScheme(in AUTF8String aURIScheme); + nsIHandlerApp 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); + nsIHandlerApp getAppForMimeType(in AUTF8String mimeType); /* Create application info for given command and name */ nsIGIOMimeApp createAppFromCommand(in AUTF8String cmd,