1 | /* |
2 | * Copyright (C) 2017 Igalia S.L. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "RemoteInspectorServer.h" |
28 | |
29 | #if ENABLE(REMOTE_INSPECTOR) |
30 | |
31 | #include "RemoteInspectorUtils.h" |
32 | #include <gio/gio.h> |
33 | #include <wtf/Vector.h> |
34 | #include <wtf/glib/GUniquePtr.h> |
35 | |
36 | #define INSPECTOR_DBUS_INTERFACE "org.webkit.Inspector" |
37 | #define INSPECTOR_DBUS_OBJECT_PATH "/org/webkit/Inspector" |
38 | #define REMOTE_INSPECTOR_DBUS_INTERFACE "org.webkit.RemoteInspector" |
39 | #define REMOTE_INSPECTOR_DBUS_OBJECT_PATH "/org/webkit/RemoteInspector" |
40 | #define REMOTE_INSPECTOR_CLIENT_DBUS_INTERFACE "org.webkit.RemoteInspectorClient" |
41 | #define REMOTE_INSPECTOR_CLIENT_OBJECT_PATH "/org/webkit/RemoteInspectorClient" |
42 | |
43 | namespace Inspector { |
44 | |
45 | static uint64_t generateConnectionID() |
46 | { |
47 | static uint64_t connectionID = 0; |
48 | return ++connectionID; |
49 | } |
50 | |
51 | namespace RemoteInspectorServerInternal { |
52 | static const char introspectionXML[] = |
53 | "<node>" |
54 | " <interface name='" INSPECTOR_DBUS_INTERFACE "'>" |
55 | " <method name='SetTargetList'>" |
56 | " <arg type='a(tsssb)' name='list' direction='in'/>" |
57 | " <arg type='b' name='remoteAutomationEnabled' direction='in'/>" |
58 | " </method>" |
59 | " <method name='SetupInspectorClient'>" |
60 | " <arg type='ay' name='backendCommandsHash' direction='in'/>" |
61 | " <arg type='ay' name='backendCommands' direction='out'/>" |
62 | " </method>" |
63 | " <method name='Setup'>" |
64 | " <arg type='t' name='connection' direction='in'/>" |
65 | " <arg type='t' name='target' direction='in'/>" |
66 | " </method>" |
67 | " <method name='FrontendDidClose'>" |
68 | " <arg type='t' name='connection' direction='in'/>" |
69 | " <arg type='t' name='target' direction='in'/>" |
70 | " </method>" |
71 | " <method name='SendMessageToFrontend'>" |
72 | " <arg type='t' name='target' direction='in'/>" |
73 | " <arg type='s' name='message' direction='in'/>" |
74 | " </method>" |
75 | " <method name='SendMessageToBackend'>" |
76 | " <arg type='t' name='connection' direction='in'/>" |
77 | " <arg type='t' name='target' direction='in'/>" |
78 | " <arg type='s' name='message' direction='in'/>" |
79 | " </method>" |
80 | " <method name='StartAutomationSession'>" |
81 | " <arg type='s' name='sessionID' direction='in'/>" |
82 | " <arg type='a{sv}' name='capabilities' direction='in'/>" |
83 | " <arg type='s' name='browserName' direction='out'/>" |
84 | " <arg type='s' name='browserVersion' direction='out'/>" |
85 | " </method>" |
86 | " </interface>" |
87 | "</node>" ; |
88 | } |
89 | |
90 | static RemoteInspector::Client::SessionCapabilities processSessionCapabilities(GVariant* sessionCapabilities) |
91 | { |
92 | RemoteInspector::Client::SessionCapabilities capabilities; |
93 | |
94 | gboolean acceptInsecureCerts; |
95 | if (g_variant_lookup(sessionCapabilities, "acceptInsecureCerts" , "b" , &acceptInsecureCerts)) |
96 | capabilities.acceptInsecureCertificates = acceptInsecureCerts; |
97 | |
98 | if (GRefPtr<GVariant> certificates = g_variant_lookup_value(sessionCapabilities, "certificates" , G_VARIANT_TYPE("a(ss)" ))) { |
99 | GVariantIter iter; |
100 | auto childCount = g_variant_iter_init(&iter, certificates.get()); |
101 | capabilities.certificates.reserveCapacity(childCount); |
102 | const char* host; |
103 | const char* certificateFile; |
104 | while (g_variant_iter_loop(&iter, "(&s&s)" , &host, &certificateFile)) |
105 | capabilities.certificates.uncheckedAppend({ String::fromUTF8(host), String::fromUTF8(certificateFile) }); |
106 | } |
107 | |
108 | return capabilities; |
109 | } |
110 | const GDBusInterfaceVTable RemoteInspectorServer::s_interfaceVTable = { |
111 | // method_call |
112 | [] (GDBusConnection* connection, const gchar* /*sender*/, const gchar* /*objectPath*/, const gchar* /*interfaceName*/, const gchar* methodName, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer userData) { |
113 | auto* inspectorServer = static_cast<RemoteInspectorServer*>(userData); |
114 | if (!g_strcmp0(methodName, "SetTargetList" )) { |
115 | inspectorServer->setTargetList(connection, parameters); |
116 | g_dbus_method_invocation_return_value(invocation, nullptr); |
117 | } else if (!g_strcmp0(methodName, "SetupInspectorClient" )) { |
118 | GRefPtr<GVariant> backendCommandsHash; |
119 | g_variant_get(parameters, "(@ay)" , &backendCommandsHash.outPtr()); |
120 | auto* backendCommands = inspectorServer->setupInspectorClient(connection, g_variant_get_bytestring(backendCommandsHash.get())); |
121 | g_dbus_method_invocation_return_value(invocation, g_variant_new("(@ay)" , backendCommands)); |
122 | } else if (!g_strcmp0(methodName, "Setup" )) { |
123 | guint64 connectionID, targetID; |
124 | g_variant_get(parameters, "(tt)" , &connectionID, &targetID); |
125 | inspectorServer->setup(connection, connectionID, targetID); |
126 | g_dbus_method_invocation_return_value(invocation, nullptr); |
127 | } else if (!g_strcmp0(methodName, "FrontendDidClose" )) { |
128 | guint64 connectionID, targetID; |
129 | g_variant_get(parameters, "(tt)" , &connectionID, &targetID); |
130 | inspectorServer->close(connection, connectionID, targetID); |
131 | g_dbus_method_invocation_return_value(invocation, nullptr); |
132 | } else if (!g_strcmp0(methodName, "SendMessageToFrontend" )) { |
133 | guint64 targetID; |
134 | const char* message; |
135 | g_variant_get(parameters, "(t&s)" , &targetID, &message); |
136 | inspectorServer->sendMessageToFrontend(connection, targetID, message); |
137 | g_dbus_method_invocation_return_value(invocation, nullptr); |
138 | } else if (!g_strcmp0(methodName, "SendMessageToBackend" )) { |
139 | guint64 connectionID, targetID; |
140 | const char* message; |
141 | g_variant_get(parameters, "(tt&s)" , &connectionID, &targetID, &message); |
142 | inspectorServer->sendMessageToBackend(connection, connectionID, targetID, message); |
143 | g_dbus_method_invocation_return_value(invocation, nullptr); |
144 | } else if (!g_strcmp0(methodName, "StartAutomationSession" )) { |
145 | const char* sessionID; |
146 | GRefPtr<GVariant> sessionCapabilities; |
147 | g_variant_get(parameters, "(&s@a{sv})" , &sessionID, &sessionCapabilities.outPtr()); |
148 | auto capabilities = processSessionCapabilities(sessionCapabilities.get()); |
149 | inspectorServer->startAutomationSession(connection, sessionID, capabilities); |
150 | auto clientCapabilities = RemoteInspector::singleton().clientCapabilities(); |
151 | g_dbus_method_invocation_return_value(invocation, g_variant_new("(ss)" , |
152 | clientCapabilities ? clientCapabilities->browserName.utf8().data() : "" , |
153 | clientCapabilities ? clientCapabilities->browserVersion.utf8().data() : "" )); |
154 | } else |
155 | g_dbus_method_invocation_return_value(invocation, nullptr); |
156 | }, |
157 | // get_property |
158 | nullptr, |
159 | // set_property |
160 | nullptr, |
161 | // padding |
162 | { 0 } |
163 | }; |
164 | |
165 | RemoteInspectorServer& RemoteInspectorServer::singleton() |
166 | { |
167 | static RemoteInspectorServer server; |
168 | return server; |
169 | } |
170 | |
171 | RemoteInspectorServer::~RemoteInspectorServer() |
172 | { |
173 | g_cancellable_cancel(m_cancellable.get()); |
174 | if (m_dbusServer) |
175 | g_dbus_server_stop(m_dbusServer.get()); |
176 | if (m_introspectionData) |
177 | g_dbus_node_info_unref(m_introspectionData); |
178 | } |
179 | |
180 | GDBusInterfaceInfo* RemoteInspectorServer::interfaceInfo() |
181 | { |
182 | if (!m_introspectionData) { |
183 | m_introspectionData = g_dbus_node_info_new_for_xml(RemoteInspectorServerInternal::introspectionXML, nullptr); |
184 | ASSERT(m_introspectionData); |
185 | } |
186 | return m_introspectionData->interfaces[0]; |
187 | } |
188 | |
189 | bool RemoteInspectorServer::start(const char* address, unsigned port) |
190 | { |
191 | GUniquePtr<char> dbusAddress(g_strdup_printf("tcp:host=%s,port=%u" , address, port)); |
192 | GUniquePtr<char> uid(g_dbus_generate_guid()); |
193 | |
194 | m_cancellable = adoptGRef(g_cancellable_new()); |
195 | |
196 | GUniqueOutPtr<GError> error; |
197 | m_dbusServer = adoptGRef(g_dbus_server_new_sync(dbusAddress.get(), G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, uid.get(), nullptr, m_cancellable.get(), &error.outPtr())); |
198 | if (!m_dbusServer) { |
199 | if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
200 | g_warning("Failed to start remote inspector server on %s: %s\n" , dbusAddress.get(), error->message); |
201 | return false; |
202 | } |
203 | |
204 | g_signal_connect(m_dbusServer.get(), "new-connection" , G_CALLBACK(newConnectionCallback), this); |
205 | g_dbus_server_start(m_dbusServer.get()); |
206 | |
207 | return true; |
208 | } |
209 | |
210 | gboolean RemoteInspectorServer::newConnectionCallback(GDBusServer*, GDBusConnection* connection, RemoteInspectorServer* inspectorServer) |
211 | { |
212 | inspectorServer->newConnection(connection); |
213 | return TRUE; |
214 | } |
215 | |
216 | void RemoteInspectorServer::connectionClosedCallback(GDBusConnection* connection, gboolean /*remotePeerVanished*/, GError*, RemoteInspectorServer* server) |
217 | { |
218 | server->connectionClosed(connection); |
219 | } |
220 | |
221 | void RemoteInspectorServer::newConnection(GDBusConnection* connection) |
222 | { |
223 | ASSERT(!m_connections.contains(connection)); |
224 | m_connections.add(connection); |
225 | g_signal_connect(connection, "closed" , G_CALLBACK(connectionClosedCallback), this); |
226 | |
227 | g_dbus_connection_register_object(connection, INSPECTOR_DBUS_OBJECT_PATH, interfaceInfo(), &s_interfaceVTable, this, nullptr, nullptr); |
228 | } |
229 | |
230 | namespace RemoteInspectorServerInternal { |
231 | static void dbusConnectionCallAsyncReadyCallback(GObject* source, GAsyncResult* result, gpointer) |
232 | { |
233 | GUniqueOutPtr<GError> error; |
234 | GRefPtr<GVariant> resultVariant = adoptGRef(g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error.outPtr())); |
235 | if (!resultVariant && !g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
236 | WTFLogAlways("RemoteInspectorServer failed to send DBus message: %s" , error->message); |
237 | } |
238 | } |
239 | |
240 | void RemoteInspectorServer::setTargetList(GDBusConnection* remoteInspectorConnection, GVariant* parameters) |
241 | { |
242 | ASSERT(m_connections.contains(remoteInspectorConnection)); |
243 | auto addResult = m_remoteInspectorConnectionToIDMap.add(remoteInspectorConnection, 0); |
244 | if (addResult.isNewEntry) { |
245 | addResult.iterator->value = generateConnectionID(); |
246 | m_idToRemoteInspectorConnectionMap.add(addResult.iterator->value, remoteInspectorConnection); |
247 | } |
248 | |
249 | gboolean remoteAutomationEnabled; |
250 | GRefPtr<GVariant> targetList; |
251 | g_variant_get(parameters, "(@a(tsssb)b)" , &targetList.outPtr(), &remoteAutomationEnabled); |
252 | GDBusConnection* clientConnection = remoteAutomationEnabled && m_automationConnection ? m_automationConnection.get() : m_clientConnection.get(); |
253 | if (!clientConnection) |
254 | return; |
255 | |
256 | g_dbus_connection_call(clientConnection, nullptr, |
257 | REMOTE_INSPECTOR_CLIENT_OBJECT_PATH, |
258 | REMOTE_INSPECTOR_CLIENT_DBUS_INTERFACE, |
259 | "SetTargetList" , |
260 | g_variant_new("(t@a(tsssb))" , m_remoteInspectorConnectionToIDMap.get(remoteInspectorConnection), targetList.get()), |
261 | nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, |
262 | -1, m_cancellable.get(), RemoteInspectorServerInternal::dbusConnectionCallAsyncReadyCallback, nullptr); |
263 | } |
264 | |
265 | void RemoteInspectorServer::clientConnectionClosedCallback(GDBusConnection* connection, gboolean /*remotePeerVanished*/, GError*, RemoteInspectorServer* server) |
266 | { |
267 | server->clientConnectionClosed(connection); |
268 | } |
269 | |
270 | GVariant* RemoteInspectorServer::setupInspectorClient(GDBusConnection* clientConnection, const char* clientBackendCommandsHash) |
271 | { |
272 | ASSERT(!m_clientConnection); |
273 | m_clientConnection = clientConnection; |
274 | g_signal_connect(m_clientConnection.get(), "closed" , G_CALLBACK(clientConnectionClosedCallback), this); |
275 | |
276 | GVariant* backendCommands; |
277 | if (strcmp(clientBackendCommandsHash, backendCommandsHash().data())) { |
278 | auto bytes = Inspector::backendCommands(); |
279 | backendCommands = g_variant_new_bytestring(static_cast<const char*>(g_bytes_get_data(bytes.get(), nullptr))); |
280 | } else |
281 | backendCommands = g_variant_new_bytestring("" ); |
282 | |
283 | // Ask all remote inspectors to push their target lists to notify the new client. |
284 | for (auto* remoteInspectorConnection : m_remoteInspectorConnectionToIDMap.keys()) { |
285 | g_dbus_connection_call(remoteInspectorConnection, nullptr, |
286 | REMOTE_INSPECTOR_DBUS_OBJECT_PATH, |
287 | REMOTE_INSPECTOR_DBUS_INTERFACE, |
288 | "GetTargetList" , |
289 | nullptr, |
290 | nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, |
291 | -1, m_cancellable.get(), RemoteInspectorServerInternal::dbusConnectionCallAsyncReadyCallback, nullptr); |
292 | } |
293 | |
294 | return backendCommands; |
295 | } |
296 | |
297 | void RemoteInspectorServer::setup(GDBusConnection* clientConnection, uint64_t connectionID, uint64_t targetID) |
298 | { |
299 | ASSERT(m_clientConnection.get() == clientConnection || m_automationConnection.get() == clientConnection); |
300 | ASSERT(m_idToRemoteInspectorConnectionMap.contains(connectionID)); |
301 | if (clientConnection == m_automationConnection.get()) { |
302 | m_automationTargets.add(std::make_pair(connectionID, targetID)); |
303 | RemoteInspector::singleton().setup(targetID); |
304 | return; |
305 | } |
306 | |
307 | m_inspectionTargets.add(std::make_pair(connectionID, targetID)); |
308 | auto* remoteInspectorConnection = m_idToRemoteInspectorConnectionMap.get(connectionID); |
309 | g_dbus_connection_call(remoteInspectorConnection, nullptr, |
310 | REMOTE_INSPECTOR_DBUS_OBJECT_PATH, |
311 | REMOTE_INSPECTOR_DBUS_INTERFACE, |
312 | "Setup" , |
313 | g_variant_new("(t)" , targetID), |
314 | nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, |
315 | -1, m_cancellable.get(), RemoteInspectorServerInternal::dbusConnectionCallAsyncReadyCallback, nullptr); |
316 | } |
317 | |
318 | void RemoteInspectorServer::close(GDBusConnection* clientConnection, uint64_t connectionID, uint64_t targetID) |
319 | { |
320 | ASSERT(m_clientConnection.get() == clientConnection || m_automationConnection.get() == clientConnection); |
321 | ASSERT(m_idToRemoteInspectorConnectionMap.contains(connectionID)); |
322 | if (clientConnection == m_automationConnection.get()) { |
323 | // FIXME: automation. |
324 | return; |
325 | } |
326 | |
327 | ASSERT(m_inspectionTargets.contains(std::make_pair(connectionID, targetID))); |
328 | auto* remoteInspectorConnection = m_idToRemoteInspectorConnectionMap.get(connectionID); |
329 | g_dbus_connection_call(remoteInspectorConnection, nullptr, |
330 | REMOTE_INSPECTOR_DBUS_OBJECT_PATH, |
331 | REMOTE_INSPECTOR_DBUS_INTERFACE, |
332 | "FrontendDidClose" , |
333 | g_variant_new("(t)" , targetID), |
334 | nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, |
335 | -1, m_cancellable.get(), RemoteInspectorServerInternal::dbusConnectionCallAsyncReadyCallback, nullptr); |
336 | m_inspectionTargets.remove(std::make_pair(connectionID, targetID)); |
337 | } |
338 | |
339 | void RemoteInspectorServer::clientConnectionClosed(GDBusConnection* clientConnection) |
340 | { |
341 | ASSERT(m_clientConnection.get() == clientConnection || m_automationConnection.get() == clientConnection); |
342 | if (clientConnection == m_automationConnection.get()) { |
343 | for (auto connectionTargetPair : m_automationTargets) |
344 | close(clientConnection, connectionTargetPair.first, connectionTargetPair.second); |
345 | m_automationConnection = nullptr; |
346 | return; |
347 | } |
348 | |
349 | for (auto connectionTargetPair : m_inspectionTargets) |
350 | close(clientConnection, connectionTargetPair.first, connectionTargetPair.second); |
351 | m_clientConnection = nullptr; |
352 | } |
353 | |
354 | void RemoteInspectorServer::connectionClosed(GDBusConnection* remoteInspectorConnection) |
355 | { |
356 | ASSERT(m_connections.contains(remoteInspectorConnection)); |
357 | if (m_remoteInspectorConnectionToIDMap.contains(remoteInspectorConnection)) { |
358 | uint64_t connectionID = m_remoteInspectorConnectionToIDMap.take(remoteInspectorConnection); |
359 | m_idToRemoteInspectorConnectionMap.remove(connectionID); |
360 | // Send an empty target list to the clients. |
361 | Vector<GRefPtr<GDBusConnection>> clientConnections = { m_automationConnection, m_clientConnection }; |
362 | for (auto& clientConnection : clientConnections) { |
363 | if (!clientConnection) |
364 | continue; |
365 | g_dbus_connection_call(clientConnection.get(), nullptr, |
366 | REMOTE_INSPECTOR_CLIENT_OBJECT_PATH, |
367 | REMOTE_INSPECTOR_CLIENT_DBUS_INTERFACE, |
368 | "SetTargetList" , |
369 | g_variant_new("(t@a(tsssb))" , connectionID, g_variant_new_array(G_VARIANT_TYPE("(tsssb)" ), nullptr, 0)), |
370 | nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, |
371 | -1, m_cancellable.get(), RemoteInspectorServerInternal::dbusConnectionCallAsyncReadyCallback, nullptr); |
372 | } |
373 | } |
374 | m_connections.remove(remoteInspectorConnection); |
375 | } |
376 | |
377 | void RemoteInspectorServer::sendMessageToBackend(GDBusConnection* clientConnection, uint64_t connectionID, uint64_t targetID, const char* message) |
378 | { |
379 | ASSERT(m_clientConnection.get() == clientConnection || m_automationConnection.get() == clientConnection); |
380 | ASSERT(m_idToRemoteInspectorConnectionMap.contains(connectionID)); |
381 | if (clientConnection == m_automationConnection.get()) { |
382 | RemoteInspector::singleton().sendMessageToTarget(targetID, message); |
383 | return; |
384 | } |
385 | |
386 | auto* remoteInspectorConnection = m_idToRemoteInspectorConnectionMap.get(connectionID); |
387 | g_dbus_connection_call(remoteInspectorConnection, nullptr, |
388 | REMOTE_INSPECTOR_DBUS_OBJECT_PATH, |
389 | REMOTE_INSPECTOR_DBUS_INTERFACE, |
390 | "SendMessageToTarget" , |
391 | g_variant_new("(t&s)" , targetID, message), |
392 | nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, |
393 | -1, m_cancellable.get(), RemoteInspectorServerInternal::dbusConnectionCallAsyncReadyCallback, nullptr); |
394 | } |
395 | |
396 | void RemoteInspectorServer::sendMessageToFrontend(GDBusConnection* remoteInspectorConnection, uint64_t targetID, const char* message) |
397 | { |
398 | ASSERT(m_connections.contains(remoteInspectorConnection)); |
399 | ASSERT(m_remoteInspectorConnectionToIDMap.contains(remoteInspectorConnection)); |
400 | |
401 | uint64_t connectionID = m_remoteInspectorConnectionToIDMap.get(remoteInspectorConnection); |
402 | auto connectionTargetPair = std::make_pair(connectionID, targetID); |
403 | ASSERT(m_automationTargets.contains(connectionTargetPair) || m_inspectionTargets.contains(connectionTargetPair)); |
404 | GDBusConnection* clientConnection = m_inspectionTargets.contains(connectionTargetPair) ? m_clientConnection.get() : m_automationConnection.get(); |
405 | ASSERT(clientConnection); |
406 | |
407 | g_dbus_connection_call(clientConnection, nullptr, |
408 | REMOTE_INSPECTOR_CLIENT_OBJECT_PATH, |
409 | REMOTE_INSPECTOR_CLIENT_DBUS_INTERFACE, |
410 | "SendMessageToFrontend" , |
411 | g_variant_new("(tt&s)" , m_remoteInspectorConnectionToIDMap.get(remoteInspectorConnection), targetID, message), |
412 | nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, |
413 | -1, m_cancellable.get(), RemoteInspectorServerInternal::dbusConnectionCallAsyncReadyCallback, nullptr); |
414 | } |
415 | |
416 | void RemoteInspectorServer::startAutomationSession(GDBusConnection* automationConnection, const char* sessionID, const RemoteInspector::Client::SessionCapabilities& capabilities) |
417 | { |
418 | if (!m_automationConnection) |
419 | m_automationConnection = automationConnection; |
420 | ASSERT(m_automationConnection.get() == automationConnection); |
421 | |
422 | RemoteInspector::singleton().requestAutomationSession(sessionID, capabilities); |
423 | } |
424 | |
425 | } // namespace Inspector |
426 | |
427 | #endif // ENABLE(REMOTE_INSPECTOR) |
428 | |