/* -*- 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/. */
NS_IMETHODIMP
nsFlatpakHandlerApp::SetName(const nsAString& aName) { // We don't implement SetName because flatpak system handler name is fixed return NS_OK;
}
// 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, getter_Transfers(error)); if (error) {
NS_WARNING(
nsPrintfCString("Cannot launch flatpak handler: %s", error->message)
.get()); return NS_ERROR_FAILURE;
} return NS_OK;
}
/** * 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) {
GUniquePtr<GError> error;
gchar** argv = nullptr; if (!g_shell_parse_argv(aCommandWithArguments.BeginReading(), nullptr, &argv,
getter_Transfers(error)) ||
!argv[0]) {
g_warning("Cannot parse command with arguments: %s", error->message);
g_strfreev(argv); return NS_ERROR_FAILURE;
}
aCommand.Assign(argv[0]);
g_strfreev(argv); return NS_OK;
}
class nsGIOHandlerApp final : public nsIGIOHandlerApp { public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
NS_DECL_NSIGIOHANDLERAPP
NS_IMETHODIMP
nsGIOHandlerApp::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::SetName(const nsAString& aName) { // We don't implement SetName because we're using mGIOMimeApp instance for // obtaining application name return NS_OK;
}
// We can only compare with nsILocalHandlerApp and nsGIOMimeApp
*_retval = false; return NS_OK;
}
static RefPtr<GAppLaunchContext> GetLaunchContext( constchar* aXDGToken = nullptr) {
RefPtr<GAppLaunchContext> context = dont_AddRef(g_app_launch_context_new()); // Unset this before launching third-party MIME handlers. Otherwise, if // Thunderbird sets this in its startup script (as it does in Debian and // Fedora), and Firefox does not set this in its startup script (it doesn't in // Debian), then Firefox will think it is part of Thunderbird and try to make // Thunderbird the default browser. See bug 1494436.
g_app_launch_context_unsetenv(context, "MOZ_APP_LAUNCHER"); if (aXDGToken) {
g_app_launch_context_setenv(context, "XDG_ACTIVATION_TOKEN", aXDGToken);
} return context;
}
#ifdef __OpenBSD__ // wrappers required for OpenBSD sandboxing with unveil()
gboolean g_app_info_launch_uris_openbsd(GAppInfo* mApp, constchar* uri,
GAppLaunchContext* context,
GError** error) {
gchar* path = g_filename_from_uri(uri, NULL, NULL); auto releasePath = MakeScopeExit([&] { g_free(path); }); const gchar* bin = g_app_info_get_executable(mApp); if (!bin) {
g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "no executable found for %s, maybe not unveiled ?",
g_app_info_get_name(mApp)); returnFALSE;
}
g_debug("spawning %s %s for %s", bin, path, uri); const gchar* const argv[] = {bin, path, NULL};
while (*uri_schemes != nullptr) { // XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier.
array->mStrings.AppendElement(*uri_schemes);
uri_schemes++;
}
array.forget(aSchemes); return NS_OK;
}
NS_IMETHODIMP
nsGIOMimeApp::SetAsDefaultForMimeType(nsACString const& aMimeType) {
GUniquePtr<char> content_type(
g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get())); if (!content_type) return NS_ERROR_FAILURE;
GUniquePtr<GError> error;
g_app_info_set_as_default_for_type(mApp, content_type.get(),
getter_Transfers(error)); if (error) {
g_warning("Cannot set application as default for MIME type (%s): %s",
PromiseFlatCString(aMimeType).get(), error->message); return NS_ERROR_FAILURE;
} return NS_OK;
} /** * Set default application for files with given extensions * @param fileExts string of space separated extensions * @return NS_OK when application was set as default for given extensions, * NS_ERROR_FAILURE otherwise
*/
NS_IMETHODIMP
nsGIOMimeApp::SetAsDefaultForFileExtensions(nsACString const& fileExts) {
GUniquePtr<GError> error;
GUniquePtr<char> extensions(g_strdup(PromiseFlatCString(fileExts).get())); char* ext_pos = extensions.get(); char* space_pos;
while ((space_pos = strchr(ext_pos, ' ')) || (*ext_pos != '\0')) { if (space_pos) {
*space_pos = '\0';
}
g_app_info_set_as_default_for_extension(mApp, ext_pos,
getter_Transfers(error)); if (error) {
g_warning("Cannot set application as default for extension (%s): %s",
ext_pos, error->message); return NS_ERROR_FAILURE;
} if (space_pos) {
ext_pos = space_pos + 1;
} else {
*ext_pos = '\0';
}
} return NS_OK;
}
/** * Set default application for URI's of a particular scheme * @param aURIScheme string containing the URI scheme * @return NS_OK when application was set as default for URI scheme, * NS_ERROR_FAILURE otherwise
*/
NS_IMETHODIMP
nsGIOMimeApp::SetAsDefaultForURIScheme(nsACString const& aURIScheme) {
GUniquePtr<GError> error;
nsAutoCString contentType("x-scheme-handler/");
contentType.Append(aURIScheme);
g_app_info_set_as_default_for_type(mApp, contentType.get(),
getter_Transfers(error)); if (error) {
g_warning("Cannot set application as default for URI scheme (%s): %s",
PromiseFlatCString(aURIScheme).get(), error->message); return NS_ERROR_FAILURE;
}
// Its consistent with the way `gio mime x-scheme-handler/contentType // some.desktop` registers mime types to also add the registered application // to the list of supported applications for the handler.
g_app_info_set_as_last_used_for_type(mApp, contentType.get(),
getter_Transfers(error)); if (error) {
g_warning("Cannot register as compatible URI scheme handler for %s: %s",
PromiseFlatCString(aURIScheme).get(), error->message); return NS_ERROR_FAILURE;
}
// Application in flatpak sandbox does not have access to the list // of installed applications on the system. We use SchemeSupported // method to check if the URI scheme is supported and then use // generic nsFlatpakHandlerApp which forwards launch call to the system. if (widget::ShouldUsePortal(widget::PortalKind::MimeHandler)) { if (mozilla::net::IsLoopbackHostname(aURIScheme)) { // When the user writes foo:1234, we try to handle it natively using // GetAppForURIScheme, and if that fails, we carry on. On flatpak there's // no way to know if an app has handlers or not. Some things like // localhost:1234 are really unlikely to be handled by native // apps, and we're much better off returning an error here instead. return NS_ERROR_FAILURE;
}
GUniquePtr<GError> error;
RefPtr<GDBusProxy> proxy;
RefPtr<GVariant> result;
proxy = g_dbus_proxy_new_for_bus_sync(
G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, nullptr, OPENURI_BUS_NAME,
OPENURI_OBJECT_PATH, OPENURI_INTERFACE_NAME,
nullptr, // cancellable
getter_Transfers(error)); if (error) {
g_warning("Failed to create proxy: %s\n", error->message); return NS_ERROR_FAILURE;
} // Construct the dictionary of options (empty in current implementaiton)
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
result = dont_AddRef(g_dbus_proxy_call_sync(
proxy, SCHEME_SUPPORTED_METHOD,
g_variant_new("(sa{sv})", PromiseFlatCString(aURIScheme).get(),
&builder),
G_DBUS_CALL_FLAGS_NONE,
-1, // timeout
nullptr, // cancellable
getter_Transfers(error))); if (error) { if (error->code == G_DBUS_ERROR_UNKNOWN_METHOD) { // The method "SchemeSupported" not available, seems like we're running // older portal. Be optimistic about supported scheme handler
LOG("SchemeSupported method not found, fallback to flatpak handler");
RefPtr<nsFlatpakHandlerApp> mozApp = new nsFlatpakHandlerApp();
mozApp.forget(aApp); return NS_OK;
}
g_warning("Failed to call SchemeSupported method: %s\n", error->message); return NS_ERROR_FAILURE;
}
gboolean supported;
g_variant_get(result, "(b)", &supported); if (!supported) {
LOG("Scheme '%s' is NOT supported.\n",
PromiseFlatCString(aURIScheme).get()); return NS_ERROR_FAILURE;
}
LOG("Scheme '%s' is supported.\n", PromiseFlatCString(aURIScheme).get());
RefPtr<nsFlatpakHandlerApp> mozApp = new nsFlatpakHandlerApp();
mozApp.forget(aApp); return NS_OK;
}
RefPtr<GAppInfo> app_info = dont_AddRef(g_app_info_get_default_for_uri_scheme(
PromiseFlatCString(aURIScheme).get())); if (!app_info) { return NS_ERROR_FAILURE;
}
RefPtr<nsGIOMimeApp> mozApp = new nsGIOMimeApp(app_info.forget());
mozApp.forget(aApp); return NS_OK;
}
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<nsIMutableArray> handlersArray =
do_CreateInstance(NS_ARRAY_CONTRACTID);
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(dont_AddRef(G_APP_INFO(appInfo->data)));
handlersArray->AppendElement(mimeApp);
appInfo = appInfo->next;
}
g_list_free(appInfoList);
}
handlersArray.forget(aResult); return NS_OK;
}
// Flatpak does not reveal installed application to the sandbox, // we need to create generic system handler. if (widget::ShouldUsePortal(widget::PortalKind::MimeHandler)) {
RefPtr<nsFlatpakHandlerApp> mozApp = new nsFlatpakHandlerApp();
mozApp.forget(aApp); return NS_OK;
}
GUniquePtr<char> content_type(
g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get())); if (!content_type) { return NS_ERROR_FAILURE;
}
// GIO returns "unknown" appinfo for the application/octet-stream, which is // useless. It's better to fallback to create appinfo from file extension // later. if (g_content_type_is_unknown(content_type.get())) { return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<GAppInfo> app_info =
dont_AddRef(g_app_info_get_default_for_type(content_type.get(), false)); if (!app_info) { return NS_ERROR_FAILURE;
} #ifdef __OpenBSD__ char* t;
t = g_find_program_in_path(g_app_info_get_executable(app_info)); if (t != nullptr) {
g_debug("%s is registered as handler for %s, binary available as %s",
g_app_info_get_executable(app_info), content_type.get(), t);
} else {
g_warning( "%s is registered as handler for %s but not available in PATH " "(missing unveil ?)",
g_app_info_get_executable(app_info), content_type.get());
} #endif
RefPtr<nsGIOMimeApp> mozApp = new nsGIOMimeApp(app_info.forget());
mozApp.forget(aApp); return NS_OK;
}
// Will take ownership of the fd, so we dont have to care about it anymore
RefPtr<GUnixFDList> fd_list =
dont_AddRef(g_unix_fd_list_new_from_array(&fd, 1));
/** * 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::FindAppFromCommand(nsACString const& aCmd,
nsIGIOMimeApp** aAppInfo) {
RefPtr<GAppInfo> app_info;
GList* apps = g_app_info_get_all();
// Try to find relevant and existing GAppInfo in all installed application // We do this by comparing each GAppInfo's executable with out own for (GList* node = apps; node; node = node->next) {
RefPtr<GAppInfo> app_info_from_list = dont_AddRef((GAppInfo*)node->data);
node->data = nullptr; if (!app_info) { // If the executable is not absolute, get it's full path
GUniquePtr<char> executable(g_find_program_in_path(
g_app_info_get_executable(app_info_from_list)));
if (executable &&
strcmp(executable.get(), PromiseFlatCString(aCmd).get()) == 0) {
app_info = std::move(app_info_from_list); // Can't break here because we need to keep iterating to unref the other // nodes.
}
}
}
g_list_free(apps); if (!app_info) {
*aAppInfo = nullptr; return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<nsGIOMimeApp> app = new nsGIOMimeApp(app_info.forget());
app.forget(aAppInfo); return NS_OK;
}
/** * Create GIOHandlerApp specified by application id. The id is the basename * of the desktop file including the .desktop extension. For example: * org.mozilla.firefox.desktop
*/
NS_IMETHODIMP
nsGIOService::CreateHandlerAppFromAppId(constchar* aAppId,
nsIGIOHandlerApp** aResult) {
RefPtr<GAppInfo> appInfo =
dont_AddRef((GAppInfo*)g_desktop_app_info_new(aAppId)); if (!appInfo) {
g_warning("Appinfo not found for: %s", aAppId); return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIGIOHandlerApp> mozApp = new nsGIOHandlerApp(appInfo.forget());
mozApp.forget(aResult); return NS_OK;
}
/** * 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) {
*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);
GUniquePtr<GError> error;
RefPtr<GAppInfo> app_info = dont_AddRef(g_app_info_create_from_commandline(
commandWithoutArgs.BeginReading(), PromiseFlatCString(appName).get(),
G_APP_INFO_CREATE_SUPPORTS_URIS, getter_Transfers(error))); if (!app_info) {
g_warning("Cannot create application info from command: %s",
error->message); return NS_ERROR_FAILURE;
}
// Check if executable exist in path
GUniquePtr<gchar> executableWithFullPath(
g_find_program_in_path(commandWithoutArgs.BeginReading())); if (!executableWithFullPath) { return NS_ERROR_FILE_NOT_FOUND;
}
RefPtr<nsGIOMimeApp> mozApp = new nsGIOMimeApp(app_info.forget());
mozApp.forget(appInfo); return NS_OK;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.