1/*
2 * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
4 * Portions Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved.
5 * Copyright (C) 2017 Sony Interactive Entertainment Inc.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#pragma once
30
31#include "Decoder.h"
32#include "Encoder.h"
33#include "HandleMessage.h"
34#include "MessageReceiver.h"
35#include <WebCore/ScriptDisallowedScope.h>
36#include <atomic>
37#include <wtf/Condition.h>
38#include <wtf/Deque.h>
39#include <wtf/Forward.h>
40#include <wtf/HashMap.h>
41#include <wtf/Lock.h>
42#include <wtf/ObjectIdentifier.h>
43#include <wtf/OptionSet.h>
44#include <wtf/RunLoop.h>
45#include <wtf/WorkQueue.h>
46#include <wtf/text/CString.h>
47
48#if OS(DARWIN) && !USE(UNIX_DOMAIN_SOCKETS)
49#include <mach/mach_port.h>
50#include <wtf/OSObjectPtr.h>
51#include <wtf/spi/darwin/XPCSPI.h>
52#endif
53
54#if USE(GLIB)
55#include "GSocketMonitor.h"
56#endif
57
58namespace IPC {
59
60enum class SendOption {
61 // Whether this message should be dispatched when waiting for a sync reply.
62 // This is the default for synchronous messages.
63 DispatchMessageEvenWhenWaitingForSyncReply = 1 << 0,
64 IgnoreFullySynchronousMode = 1 << 1,
65};
66
67enum class SendSyncOption {
68 // Use this to inform that this sync call will suspend this process until the user responds with input.
69 InformPlatformProcessWillSuspend = 1 << 0,
70 UseFullySynchronousModeForTesting = 1 << 1,
71};
72
73enum class WaitForOption {
74 // Use this to make waitForMessage be interrupted immediately by any incoming sync messages.
75 InterruptWaitingIfSyncMessageArrives = 1 << 0,
76};
77
78#define MESSAGE_CHECK_BASE(assertion, connection) do \
79 if (!(assertion)) { \
80 ASSERT(assertion); \
81 (connection)->markCurrentlyDispatchedMessageAsInvalid(); \
82 return; \
83 } \
84while (0)
85
86template<typename AsyncReplyResult> struct AsyncReplyError {
87 static AsyncReplyResult create() { return { }; };
88};
89
90class MachMessage;
91class UnixMessage;
92
93class Connection : public ThreadSafeRefCounted<Connection, WTF::DestructionThread::Main> {
94public:
95 class Client : public MessageReceiver {
96 public:
97 virtual void didClose(Connection&) = 0;
98 virtual void didReceiveInvalidMessage(Connection&, StringReference messageReceiverName, StringReference messageName) = 0;
99
100 protected:
101 virtual ~Client() { }
102 };
103
104 class WorkQueueMessageReceiver : public MessageReceiver, public ThreadSafeRefCounted<WorkQueueMessageReceiver> {
105 };
106
107#if USE(UNIX_DOMAIN_SOCKETS)
108 typedef int Identifier;
109 static bool identifierIsValid(Identifier identifier) { return identifier != -1; }
110
111 struct SocketPair {
112 int client;
113 int server;
114 };
115
116 enum ConnectionOptions {
117 SetCloexecOnClient = 1 << 0,
118 SetCloexecOnServer = 1 << 1,
119 };
120
121 static Connection::SocketPair createPlatformConnection(unsigned options = SetCloexecOnClient | SetCloexecOnServer);
122#elif OS(DARWIN)
123 struct Identifier {
124 Identifier()
125 {
126 }
127
128 Identifier(mach_port_t port)
129 : port(port)
130 {
131 }
132
133 Identifier(mach_port_t port, OSObjectPtr<xpc_connection_t> xpcConnection)
134 : port(port)
135 , xpcConnection(WTFMove(xpcConnection))
136 {
137 }
138
139 mach_port_t port { MACH_PORT_NULL };
140 OSObjectPtr<xpc_connection_t> xpcConnection;
141 };
142 static bool identifierIsValid(Identifier identifier) { return MACH_PORT_VALID(identifier.port); }
143 xpc_connection_t xpcConnection() const { return m_xpcConnection.get(); }
144 Optional<audit_token_t> getAuditToken();
145 pid_t remoteProcessID() const;
146#elif OS(WINDOWS)
147 typedef HANDLE Identifier;
148 static bool createServerAndClientIdentifiers(Identifier& serverIdentifier, Identifier& clientIdentifier);
149 static bool identifierIsValid(Identifier identifier) { return !!identifier; }
150#endif
151
152 static Ref<Connection> createServerConnection(Identifier, Client&);
153 static Ref<Connection> createClientConnection(Identifier, Client&);
154 ~Connection();
155
156 Client& client() const { return m_client; }
157
158 enum UniqueIDType { };
159 using UniqueID = ObjectIdentifier<UniqueIDType>;
160
161 static Connection* connection(UniqueID);
162 UniqueID uniqueID() const { return m_uniqueID; }
163
164 void setOnlySendMessagesAsDispatchWhenWaitingForSyncReplyWhenProcessingSuchAMessage(bool);
165 void setShouldExitOnSyncMessageSendFailure(bool);
166
167 // The set callback will be called on the connection work queue when the connection is closed,
168 // before didCall is called on the client thread. Must be called before the connection is opened.
169 // In the future we might want a more generic way to handle sync or async messages directly
170 // on the work queue, for example if we want to handle them on some other thread we could avoid
171 // handling the message on the client thread first.
172 typedef void (*DidCloseOnConnectionWorkQueueCallback)(Connection*);
173 void setDidCloseOnConnectionWorkQueueCallback(DidCloseOnConnectionWorkQueueCallback);
174
175 void addWorkQueueMessageReceiver(StringReference messageReceiverName, WorkQueue&, WorkQueueMessageReceiver*);
176 void removeWorkQueueMessageReceiver(StringReference messageReceiverName);
177
178 bool open();
179 void invalidate();
180 void markCurrentlyDispatchedMessageAsInvalid();
181
182 void postConnectionDidCloseOnConnectionWorkQueue();
183
184 template<typename T, typename C> void sendWithAsyncReply(T&& message, C&& completionHandler, uint64_t destinationID = 0);
185 template<typename T> bool send(T&& message, uint64_t destinationID, OptionSet<SendOption> sendOptions = { });
186 template<typename T> void sendWithReply(T&& message, uint64_t destinationID, FunctionDispatcher& replyDispatcher, Function<void(Optional<typename CodingType<typename T::Reply>::Type>)>&& replyHandler);
187 template<typename T> bool sendSync(T&& message, typename T::Reply&& reply, uint64_t destinationID, Seconds timeout = Seconds::infinity(), OptionSet<SendSyncOption> sendSyncOptions = { });
188 template<typename T> bool waitForAndDispatchImmediately(uint64_t destinationID, Seconds timeout, OptionSet<WaitForOption> waitForOptions = { });
189
190 template<typename T, typename C, typename U>
191 void sendWithAsyncReply(T&& message, C&& completionHandler, ObjectIdentifier<U> destinationID = { })
192 {
193 sendWithAsyncReply<T, C>(WTFMove(message), WTFMove(completionHandler), destinationID.toUInt64());
194 }
195
196 template<typename T, typename U>
197 bool send(T&& message, ObjectIdentifier<U> destinationID, OptionSet<SendOption> sendOptions = { })
198 {
199 return send<T>(WTFMove(message), destinationID.toUInt64(), sendOptions);
200 }
201
202 template<typename T, typename U>
203 void sendWithReply(T&& message, ObjectIdentifier<U> destinationID, FunctionDispatcher& replyDispatcher, Function<void(Optional<typename CodingType<typename T::Reply>::Type>)>&& replyHandler)
204 {
205 return sendWithReply<T>(WTFMove(message), destinationID.toUInt64(), replyDispatcher, WTFMove(replyHandler));
206 }
207
208 template<typename T, typename U>
209 bool sendSync(T&& message, typename T::Reply&& reply, ObjectIdentifier<U> destinationID, Seconds timeout = Seconds::infinity(), OptionSet<SendSyncOption> sendSyncOptions = { })
210 {
211 return sendSync<T>(WTFMove(message), WTFMove(reply), destinationID.toUInt64(), timeout, sendSyncOptions);
212 }
213
214 template<typename T, typename U>
215 bool waitForAndDispatchImmediately(ObjectIdentifier<U> destinationID, Seconds timeout, OptionSet<WaitForOption> waitForOptions = { })
216 {
217 return waitForAndDispatchImmediately<T>(destinationID.toUInt64(), timeout, waitForOptions);
218 }
219
220 bool sendMessage(std::unique_ptr<Encoder>, OptionSet<SendOption> sendOptions);
221 void sendMessageWithReply(uint64_t requestID, std::unique_ptr<Encoder>, FunctionDispatcher& replyDispatcher, Function<void(std::unique_ptr<Decoder>)>&& replyHandler);
222 std::unique_ptr<Encoder> createSyncMessageEncoder(StringReference messageReceiverName, StringReference messageName, uint64_t destinationID, uint64_t& syncRequestID);
223 std::unique_ptr<Decoder> sendSyncMessage(uint64_t syncRequestID, std::unique_ptr<Encoder>, Seconds timeout, OptionSet<SendSyncOption> sendSyncOptions);
224 bool sendSyncReply(std::unique_ptr<Encoder>);
225
226 void wakeUpRunLoop();
227
228 void incrementDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount() { ++m_inDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount; }
229 void decrementDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount() { --m_inDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount; }
230
231 bool inSendSync() const { return m_inSendSyncCount; }
232
233 Identifier identifier() const;
234
235#if PLATFORM(COCOA)
236 bool kill();
237 void terminateSoon(Seconds);
238#endif
239
240 bool isValid() const { return m_isValid; }
241
242#if HAVE(QOS_CLASSES)
243 void setShouldBoostMainThreadOnSyncMessage(bool b) { m_shouldBoostMainThreadOnSyncMessage = b; }
244#endif
245
246 uint64_t installIncomingSyncMessageCallback(WTF::Function<void()>&&);
247 void uninstallIncomingSyncMessageCallback(uint64_t);
248 bool hasIncomingSyncMessage();
249
250 void allowFullySynchronousModeForTesting() { m_fullySynchronousModeIsAllowedForTesting = true; }
251
252 void ignoreTimeoutsForTesting() { m_ignoreTimeoutsForTesting = true; }
253
254 void enableIncomingMessagesThrottling();
255
256private:
257 Connection(Identifier, bool isServer, Client&);
258 void platformInitialize(Identifier);
259 void platformInvalidate();
260
261 std::unique_ptr<Decoder> waitForMessage(StringReference messageReceiverName, StringReference messageName, uint64_t destinationID, Seconds timeout, OptionSet<WaitForOption>);
262
263 std::unique_ptr<Decoder> waitForSyncReply(uint64_t syncRequestID, Seconds timeout, OptionSet<SendSyncOption>);
264
265 // Called on the connection work queue.
266 void processIncomingMessage(std::unique_ptr<Decoder>);
267 void processIncomingSyncReply(std::unique_ptr<Decoder>);
268
269 void dispatchWorkQueueMessageReceiverMessage(WorkQueueMessageReceiver&, Decoder&);
270
271 bool canSendOutgoingMessages() const;
272 bool platformCanSendOutgoingMessages() const;
273 void sendOutgoingMessages();
274 bool sendOutgoingMessage(std::unique_ptr<Encoder>);
275 void connectionDidClose();
276
277 // Called on the listener thread.
278 void dispatchOneIncomingMessage();
279 void dispatchIncomingMessages();
280 void dispatchMessage(std::unique_ptr<Decoder>);
281 void dispatchMessage(Decoder&);
282 void dispatchSyncMessage(Decoder&);
283 void dispatchDidReceiveInvalidMessage(const CString& messageReceiverNameString, const CString& messageNameString);
284 void didFailToSendSyncMessage();
285
286 // Can be called on any thread.
287 void enqueueIncomingMessage(std::unique_ptr<Decoder>);
288 size_t incomingMessagesDispatchingBatchSize() const;
289
290 void willSendSyncMessage(OptionSet<SendSyncOption>);
291 void didReceiveSyncReply(OptionSet<SendSyncOption>);
292
293 Seconds timeoutRespectingIgnoreTimeoutsForTesting(Seconds) const;
294
295#if PLATFORM(COCOA)
296 bool sendMessage(std::unique_ptr<MachMessage>);
297#endif
298
299 class MessagesThrottler {
300 WTF_MAKE_FAST_ALLOCATED;
301 public:
302 typedef void (Connection::*DispatchMessagesFunction)();
303 MessagesThrottler(Connection&, DispatchMessagesFunction);
304
305 size_t numberOfMessagesToProcess(size_t totalMessages);
306 void scheduleMessagesDispatch();
307
308 private:
309 RunLoop::Timer<Connection> m_dispatchMessagesTimer;
310 Connection& m_connection;
311 DispatchMessagesFunction m_dispatchMessages;
312 unsigned m_throttlingLevel { 0 };
313 };
314
315 Client& m_client;
316 UniqueID m_uniqueID;
317 bool m_isServer;
318 std::atomic<bool> m_isValid { true };
319 std::atomic<uint64_t> m_syncRequestID;
320
321 bool m_onlySendMessagesAsDispatchWhenWaitingForSyncReplyWhenProcessingSuchAMessage;
322 bool m_shouldExitOnSyncMessageSendFailure;
323 DidCloseOnConnectionWorkQueueCallback m_didCloseOnConnectionWorkQueueCallback;
324
325 bool m_isConnected;
326 Ref<WorkQueue> m_connectionQueue;
327
328 HashMap<StringReference, std::pair<RefPtr<WorkQueue>, RefPtr<WorkQueueMessageReceiver>>> m_workQueueMessageReceivers;
329
330 unsigned m_inSendSyncCount;
331 unsigned m_inDispatchMessageCount;
332 unsigned m_inDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount;
333 unsigned m_inDispatchMessageMarkedToUseFullySynchronousModeForTesting { 0 };
334 bool m_fullySynchronousModeIsAllowedForTesting { false };
335 bool m_ignoreTimeoutsForTesting { false };
336 bool m_didReceiveInvalidMessage;
337
338 // Incoming messages.
339 Lock m_incomingMessagesMutex;
340 Deque<std::unique_ptr<Decoder>> m_incomingMessages;
341 std::unique_ptr<MessagesThrottler> m_incomingMessagesThrottler;
342
343 // Outgoing messages.
344 Lock m_outgoingMessagesMutex;
345 Deque<std::unique_ptr<Encoder>> m_outgoingMessages;
346
347 Condition m_waitForMessageCondition;
348 Lock m_waitForMessageMutex;
349
350 struct ReplyHandler;
351
352 Lock m_replyHandlersLock;
353 HashMap<uint64_t, ReplyHandler> m_replyHandlers;
354
355 struct WaitForMessageState;
356 WaitForMessageState* m_waitingForMessage;
357
358 class SyncMessageState;
359
360 Lock m_syncReplyStateMutex;
361 bool m_shouldWaitForSyncReplies;
362 struct PendingSyncReply;
363 Vector<PendingSyncReply> m_pendingSyncReplies;
364
365 Lock m_incomingSyncMessageCallbackMutex;
366 HashMap<uint64_t, WTF::Function<void()>> m_incomingSyncMessageCallbacks;
367 RefPtr<WorkQueue> m_incomingSyncMessageCallbackQueue;
368 uint64_t m_nextIncomingSyncMessageCallbackID { 0 };
369
370#if HAVE(QOS_CLASSES)
371 pthread_t m_mainThread { 0 };
372 bool m_shouldBoostMainThreadOnSyncMessage { false };
373#endif
374
375#if USE(UNIX_DOMAIN_SOCKETS)
376 // Called on the connection queue.
377 void readyReadHandler();
378 bool processMessage();
379 bool sendOutputMessage(UnixMessage&);
380
381 Vector<uint8_t> m_readBuffer;
382 Vector<int> m_fileDescriptors;
383 int m_socketDescriptor;
384 std::unique_ptr<UnixMessage> m_pendingOutputMessage;
385#if USE(GLIB)
386 GRefPtr<GSocket> m_socket;
387 GSocketMonitor m_readSocketMonitor;
388 GSocketMonitor m_writeSocketMonitor;
389#endif
390#elif OS(DARWIN)
391 // Called on the connection queue.
392 void receiveSourceEventHandler();
393 void initializeSendSource();
394 void resumeSendSource();
395 void cancelReceiveSource();
396
397 mach_port_t m_sendPort { MACH_PORT_NULL };
398 dispatch_source_t m_sendSource { nullptr };
399
400 mach_port_t m_receivePort { MACH_PORT_NULL };
401 dispatch_source_t m_receiveSource { nullptr };
402
403 std::unique_ptr<MachMessage> m_pendingOutgoingMachMessage;
404 bool m_isInitializingSendSource { false };
405
406 OSObjectPtr<xpc_connection_t> m_xpcConnection;
407 bool m_wasKilled { false };
408#elif OS(WINDOWS)
409 // Called on the connection queue.
410 void readEventHandler();
411 void writeEventHandler();
412 void invokeReadEventHandler();
413 void invokeWriteEventHandler();
414
415 class EventListener {
416 public:
417 void open(Function<void()>&&);
418 void close();
419
420 OVERLAPPED& state() { return m_state; }
421
422 private:
423 static void callback(void*, BOOLEAN);
424
425 OVERLAPPED m_state;
426 HANDLE m_waitHandle { INVALID_HANDLE_VALUE };
427 Function<void()> m_handler;
428 };
429
430 Vector<uint8_t> m_readBuffer;
431 EventListener m_readListener;
432 std::unique_ptr<Encoder> m_pendingWriteEncoder;
433 EventListener m_writeListener;
434 HANDLE m_connectionPipe { INVALID_HANDLE_VALUE };
435#endif
436};
437
438template<typename T>
439bool Connection::send(T&& message, uint64_t destinationID, OptionSet<SendOption> sendOptions)
440{
441 COMPILE_ASSERT(!T::isSync, AsyncMessageExpected);
442
443 auto encoder = std::make_unique<Encoder>(T::receiverName(), T::name(), destinationID);
444 encoder->encode(message.arguments());
445
446 return sendMessage(WTFMove(encoder), sendOptions);
447}
448
449uint64_t nextAsyncReplyHandlerID();
450void addAsyncReplyHandler(Connection&, uint64_t, CompletionHandler<void(Decoder*)>&&);
451CompletionHandler<void(Decoder*)> takeAsyncReplyHandler(Connection&, uint64_t);
452
453template<typename T, typename C>
454void Connection::sendWithAsyncReply(T&& message, C&& completionHandler, uint64_t destinationID)
455{
456 COMPILE_ASSERT(!T::isSync, AsyncMessageExpected);
457
458 auto encoder = std::make_unique<Encoder>(T::receiverName(), T::name(), destinationID);
459 uint64_t listenerID = nextAsyncReplyHandlerID();
460 encoder->encode(listenerID);
461 encoder->encode(message.arguments());
462 sendMessage(WTFMove(encoder), { });
463 addAsyncReplyHandler(*this, listenerID, [completionHandler = WTFMove(completionHandler)] (Decoder* decoder) mutable {
464 if (decoder && !decoder->isInvalid())
465 T::callReply(*decoder, WTFMove(completionHandler));
466 else
467 T::cancelReply(WTFMove(completionHandler));
468 });
469}
470
471template<typename T>
472void Connection::sendWithReply(T&& message, uint64_t destinationID, FunctionDispatcher& replyDispatcher, Function<void(Optional<typename CodingType<typename T::Reply>::Type>)>&& replyHandler)
473{
474 uint64_t requestID = 0;
475 std::unique_ptr<Encoder> encoder = createSyncMessageEncoder(T::receiverName(), T::name(), destinationID, requestID);
476
477 encoder->encode(message.arguments());
478
479 sendMessageWithReply(requestID, WTFMove(encoder), replyDispatcher, [replyHandler = WTFMove(replyHandler)](std::unique_ptr<Decoder> decoder) {
480 if (decoder) {
481 Optional<typename CodingType<typename T::Reply>::Type> reply;
482 *decoder >> reply;
483 if (reply) {
484 replyHandler(WTFMove(*reply));
485 return;
486 }
487 }
488
489 replyHandler(WTF::nullopt);
490 });
491}
492
493template<size_t i, typename A, typename B> struct TupleMover {
494 static void move(A&& a, B& b)
495 {
496 std::get<i - 1>(b) = WTFMove(std::get<i - 1>(a));
497 TupleMover<i - 1, A, B>::move(WTFMove(a), b);
498 }
499};
500
501template<typename A, typename B> struct TupleMover<0, A, B> {
502 static void move(A&&, B&) { }
503};
504
505template<typename... A, typename... B>
506void moveTuple(std::tuple<A...>&& a, std::tuple<B...>& b)
507{
508 static_assert(sizeof...(A) == sizeof...(B), "Should be used with two tuples of same size");
509 TupleMover<sizeof...(A), std::tuple<A...>, std::tuple<B...>>::move(WTFMove(a), b);
510}
511
512template<typename T> bool Connection::sendSync(T&& message, typename T::Reply&& reply, uint64_t destinationID, Seconds timeout, OptionSet<SendSyncOption> sendSyncOptions)
513{
514 COMPILE_ASSERT(T::isSync, SyncMessageExpected);
515
516 uint64_t syncRequestID = 0;
517 std::unique_ptr<Encoder> encoder = createSyncMessageEncoder(T::receiverName(), T::name(), destinationID, syncRequestID);
518
519 if (sendSyncOptions.contains(SendSyncOption::UseFullySynchronousModeForTesting)) {
520 encoder->setFullySynchronousModeForTesting();
521 m_fullySynchronousModeIsAllowedForTesting = true;
522 }
523
524 // Encode the rest of the input arguments.
525 encoder->encode(message.arguments());
526
527 // Now send the message and wait for a reply.
528 std::unique_ptr<Decoder> replyDecoder = sendSyncMessage(syncRequestID, WTFMove(encoder), timeout, sendSyncOptions);
529 if (!replyDecoder)
530 return false;
531
532 // Decode the reply.
533 Optional<typename T::ReplyArguments> replyArguments;
534 *replyDecoder >> replyArguments;
535 if (!replyArguments)
536 return false;
537 moveTuple(WTFMove(*replyArguments), reply);
538 return true;
539}
540
541template<typename T> bool Connection::waitForAndDispatchImmediately(uint64_t destinationID, Seconds timeout, OptionSet<WaitForOption> waitForOptions)
542{
543 std::unique_ptr<Decoder> decoder = waitForMessage(T::receiverName(), T::name(), destinationID, timeout, waitForOptions);
544 if (!decoder)
545 return false;
546
547 ASSERT(decoder->destinationID() == destinationID);
548 m_client.didReceiveMessage(*this, *decoder);
549 return true;
550}
551
552} // namespace IPC
553