xdg_desktop_portal_utils.cc (7437B)
1 /* 2 * Copyright 2022 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 #include "modules/portal/xdg_desktop_portal_utils.h" 11 12 #include <cstdint> 13 #include <string> 14 #include <vector> 15 16 #include "absl/strings/str_replace.h" 17 #include "absl/strings/string_view.h" 18 #include "modules/portal/portal_request_response.h" 19 #include "modules/portal/scoped_glib.h" 20 #include "rtc_base/logging.h" 21 #include "rtc_base/strings/str_join.h" 22 23 namespace webrtc { 24 namespace xdg_portal { 25 26 std::string RequestResponseToString(RequestResponse request) { 27 switch (request) { 28 case RequestResponse::kUnknown: 29 return "kUnknown"; 30 case RequestResponse::kSuccess: 31 return "kSuccess"; 32 case RequestResponse::kUserCancelled: 33 return "kUserCancelled"; 34 case RequestResponse::kError: 35 return "kError"; 36 default: 37 return "Uknown"; 38 } 39 } 40 41 RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response) { 42 // See: 43 // https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-signal-org-freedesktop-portal-Request.Response 44 switch (portal_response) { 45 case 0: 46 return RequestResponse::kSuccess; 47 case 1: 48 return RequestResponse::kUserCancelled; 49 case 2: 50 return RequestResponse::kError; 51 default: 52 return RequestResponse::kUnknown; 53 } 54 } 55 56 std::string PrepareSignalHandle(absl::string_view token, 57 GDBusConnection* connection) { 58 const char* unique_name = g_dbus_connection_get_unique_name(connection); 59 if (unique_name == nullptr || *unique_name == '\0') { 60 return std::string(); 61 } 62 63 absl::string_view unique_name_sv = unique_name; 64 if (!unique_name_sv.empty()) { 65 unique_name_sv.remove_prefix(1); 66 } 67 68 std::string sender = absl::StrReplaceAll(unique_name_sv, {{".", "_"}}); 69 std::vector<absl::string_view> parts = {kDesktopRequestObjectPath, sender, 70 token}; 71 return webrtc::StrJoin(parts, "/"); 72 } 73 74 uint32_t SetupRequestResponseSignal(absl::string_view object_path, 75 const GDBusSignalCallback callback, 76 gpointer user_data, 77 GDBusConnection* connection) { 78 return g_dbus_connection_signal_subscribe( 79 connection, kDesktopBusName, kRequestInterfaceName, "Response", 80 std::string(object_path).c_str(), /*arg0=*/nullptr, 81 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, callback, user_data, 82 /*user_data_free_func=*/nullptr); 83 } 84 85 void RequestSessionProxy(absl::string_view interface_name, 86 const ProxyRequestCallback proxy_request_callback, 87 GCancellable* cancellable, 88 gpointer user_data) { 89 g_dbus_proxy_new_for_bus( 90 G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, 91 kDesktopBusName, kDesktopObjectPath, std::string(interface_name).c_str(), 92 cancellable, 93 reinterpret_cast<GAsyncReadyCallback>(proxy_request_callback), user_data); 94 } 95 96 void SetupSessionRequestHandlers( 97 absl::string_view portal_prefix, 98 const SessionRequestCallback session_request_callback, 99 const SessionRequestResponseSignalHandler request_response_signale_handler, 100 GDBusConnection* connection, 101 GDBusProxy* proxy, 102 GCancellable* cancellable, 103 std::string& portal_handle, 104 guint& session_request_signal_id, 105 gpointer user_data) { 106 GVariantBuilder builder; 107 Scoped<char> variant_string; 108 109 g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); 110 variant_string = 111 g_strdup_printf("%.*s_session%d", static_cast<int>(portal_prefix.size()), 112 portal_prefix.data(), g_random_int_range(0, G_MAXINT)); 113 g_variant_builder_add(&builder, "{sv}", "session_handle_token", 114 g_variant_new_string(variant_string.get())); 115 116 variant_string = 117 g_strdup_printf("%.*s_%d", static_cast<int>(portal_prefix.size()), 118 portal_prefix.data(), g_random_int_range(0, G_MAXINT)); 119 g_variant_builder_add(&builder, "{sv}", "handle_token", 120 g_variant_new_string(variant_string.get())); 121 122 portal_handle = PrepareSignalHandle(variant_string.get(), connection); 123 session_request_signal_id = SetupRequestResponseSignal( 124 portal_handle.c_str(), request_response_signale_handler, user_data, 125 connection); 126 127 RTC_LOG(LS_INFO) << "Desktop session requested."; 128 g_dbus_proxy_call( 129 proxy, "CreateSession", g_variant_new("(a{sv})", &builder), 130 G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, 131 reinterpret_cast<GAsyncReadyCallback>(session_request_callback), 132 user_data); 133 } 134 135 void StartSessionRequest( 136 absl::string_view prefix, 137 absl::string_view session_handle, 138 const StartRequestResponseSignalHandler signal_handler, 139 const SessionStartRequestedHandler session_started_handler, 140 GDBusProxy* proxy, 141 GDBusConnection* connection, 142 GCancellable* cancellable, 143 guint& start_request_signal_id, 144 std::string& start_handle, 145 gpointer user_data) { 146 GVariantBuilder builder; 147 Scoped<char> variant_string; 148 149 g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); 150 variant_string = 151 g_strdup_printf("%.*s%d", static_cast<int>(prefix.size()), prefix.data(), 152 g_random_int_range(0, G_MAXINT)); 153 g_variant_builder_add(&builder, "{sv}", "handle_token", 154 g_variant_new_string(variant_string.get())); 155 156 start_handle = PrepareSignalHandle(variant_string.get(), connection); 157 start_request_signal_id = SetupRequestResponseSignal( 158 start_handle.c_str(), signal_handler, user_data, connection); 159 160 // "Identifier for the application window", this is Wayland, so not "x11:...". 161 const char parent_window[] = ""; 162 163 RTC_LOG(LS_INFO) << "Starting the portal session."; 164 g_dbus_proxy_call( 165 proxy, "Start", 166 g_variant_new("(osa{sv})", std::string(session_handle).c_str(), 167 parent_window, &builder), 168 G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, 169 reinterpret_cast<GAsyncReadyCallback>(session_started_handler), 170 user_data); 171 } 172 173 void TearDownSession(absl::string_view session_handle, 174 GDBusProxy* proxy, 175 GCancellable* cancellable, 176 GDBusConnection* connection) { 177 if (!session_handle.empty()) { 178 Scoped<GDBusMessage> message(g_dbus_message_new_method_call( 179 kDesktopBusName, std::string(session_handle).c_str(), 180 kSessionInterfaceName, "Close")); 181 if (message.get()) { 182 Scoped<GError> error; 183 g_dbus_connection_send_message(connection, message.get(), 184 G_DBUS_SEND_MESSAGE_FLAGS_NONE, 185 /*out_serial=*/nullptr, error.receive()); 186 if (error.get()) { 187 RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; 188 } 189 } 190 } 191 192 if (cancellable) { 193 g_cancellable_cancel(cancellable); 194 g_object_unref(cancellable); 195 } 196 197 if (proxy) { 198 g_object_unref(proxy); 199 } 200 } 201 202 } // namespace xdg_portal 203 } // namespace webrtc