/src/mozilla-central/ipc/glue/ProtocolUtils.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | | * vim: sw=4 ts=4 et : |
3 | | */ |
4 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
5 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
6 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
7 | | |
8 | | #ifndef mozilla_ipc_ProtocolUtils_h |
9 | | #define mozilla_ipc_ProtocolUtils_h 1 |
10 | | |
11 | | #include "base/id_map.h" |
12 | | #include "base/process.h" |
13 | | #include "base/process_util.h" |
14 | | #include "chrome/common/ipc_message_utils.h" |
15 | | |
16 | | #include "prenv.h" |
17 | | |
18 | | #include "IPCMessageStart.h" |
19 | | #include "mozilla/AlreadyAddRefed.h" |
20 | | #include "mozilla/Attributes.h" |
21 | | #include "mozilla/ipc/ByteBuf.h" |
22 | | #include "mozilla/ipc/FileDescriptor.h" |
23 | | #include "mozilla/ipc/MessageChannel.h" |
24 | | #include "mozilla/ipc/Shmem.h" |
25 | | #include "mozilla/ipc/Transport.h" |
26 | | #include "mozilla/ipc/MessageLink.h" |
27 | | #include "mozilla/recordreplay/ChildIPC.h" |
28 | | #include "mozilla/LinkedList.h" |
29 | | #include "mozilla/Maybe.h" |
30 | | #include "mozilla/MozPromise.h" |
31 | | #include "mozilla/Mutex.h" |
32 | | #include "mozilla/NotNull.h" |
33 | | #include "mozilla/Scoped.h" |
34 | | #include "mozilla/UniquePtr.h" |
35 | | #include "MainThreadUtils.h" |
36 | | #include "nsICrashReporter.h" |
37 | | #include "nsILabelableRunnable.h" |
38 | | |
39 | | #if defined(ANDROID) && defined(DEBUG) |
40 | | #include <android/log.h> |
41 | | #endif |
42 | | |
43 | | template<typename T> class nsTHashtable; |
44 | | template<typename T> class nsPtrHashKey; |
45 | | |
46 | | // WARNING: this takes into account the private, special-message-type |
47 | | // enum in ipc_channel.h. They need to be kept in sync. |
48 | | namespace { |
49 | | // XXX the max message ID is actually kuint32max now ... when this |
50 | | // changed, the assumptions of the special message IDs changed in that |
51 | | // they're not carving out messages from likely-unallocated space, but |
52 | | // rather carving out messages from the end of space allocated to |
53 | | // protocol 0. Oops! We can get away with this until protocol 0 |
54 | | // starts approaching its 65,536th message. |
55 | | enum { |
56 | | BUILD_IDS_MATCH_MESSAGE_TYPE = kuint16max - 8, |
57 | | BUILD_ID_MESSAGE_TYPE = kuint16max - 7, // unused |
58 | | CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6, |
59 | | SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5, |
60 | | SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4, |
61 | | GOODBYE_MESSAGE_TYPE = kuint16max - 3, |
62 | | CANCEL_MESSAGE_TYPE = kuint16max - 2, |
63 | | |
64 | | // kuint16max - 1 is used by ipc_channel.h. |
65 | | }; |
66 | | |
67 | | } // namespace |
68 | | |
69 | | class nsIEventTarget; |
70 | | |
71 | | namespace mozilla { |
72 | | class SchedulerGroup; |
73 | | |
74 | | namespace dom { |
75 | | class ContentParent; |
76 | | } // namespace dom |
77 | | |
78 | | namespace net { |
79 | | class NeckoParent; |
80 | | } // namespace net |
81 | | |
82 | | namespace ipc { |
83 | | |
84 | | #ifdef FUZZING |
85 | | class ProtocolFuzzerHelper; |
86 | | #endif |
87 | | |
88 | | class MessageChannel; |
89 | | |
90 | | #ifdef XP_WIN |
91 | | const base::ProcessHandle kInvalidProcessHandle = INVALID_HANDLE_VALUE; |
92 | | |
93 | | // In theory, on Windows, this is a valid process ID, but in practice they are |
94 | | // currently divisible by four. Process IDs share the kernel handle allocation |
95 | | // code and they are guaranteed to be divisible by four. |
96 | | // As this could change for process IDs we shouldn't generally rely on this |
97 | | // property, however even if that were to change, it seems safe to rely on this |
98 | | // particular value never being used. |
99 | | const base::ProcessId kInvalidProcessId = kuint32max; |
100 | | #else |
101 | | const base::ProcessHandle kInvalidProcessHandle = -1; |
102 | | const base::ProcessId kInvalidProcessId = -1; |
103 | | #endif |
104 | | |
105 | | // Scoped base::ProcessHandle to ensure base::CloseProcessHandle is called. |
106 | | struct ScopedProcessHandleTraits |
107 | | { |
108 | | typedef base::ProcessHandle type; |
109 | | |
110 | | static type empty() |
111 | | { |
112 | | return kInvalidProcessHandle; |
113 | | } |
114 | | |
115 | | static void release(type aProcessHandle) |
116 | | { |
117 | | if (aProcessHandle && aProcessHandle != kInvalidProcessHandle) { |
118 | | base::CloseProcessHandle(aProcessHandle); |
119 | | } |
120 | | } |
121 | | }; |
122 | | typedef mozilla::Scoped<ScopedProcessHandleTraits> ScopedProcessHandle; |
123 | | |
124 | | class ProtocolFdMapping; |
125 | | class ProtocolCloneContext; |
126 | | |
127 | | // Used to pass references to protocol actors across the wire. |
128 | | // Actors created on the parent-side have a positive ID, and actors |
129 | | // allocated on the child side have a negative ID. |
130 | | struct ActorHandle |
131 | | { |
132 | | int mId; |
133 | | }; |
134 | | |
135 | | // What happens if Interrupt calls race? |
136 | | enum RacyInterruptPolicy { |
137 | | RIPError, |
138 | | RIPChildWins, |
139 | | RIPParentWins |
140 | | }; |
141 | | |
142 | | |
143 | | class IToplevelProtocol; |
144 | | |
145 | | class IProtocol : public HasResultCodes |
146 | | { |
147 | | #ifdef FUZZING |
148 | | friend class mozilla::ipc::ProtocolFuzzerHelper; |
149 | | #endif |
150 | | |
151 | | public: |
152 | | enum ActorDestroyReason { |
153 | | FailedConstructor, |
154 | | Deletion, |
155 | | AncestorDeletion, |
156 | | NormalShutdown, |
157 | | AbnormalShutdown |
158 | | }; |
159 | | |
160 | | // A lot of the functionality of IProtocol only differs between toplevel |
161 | | // protocols (IToplevelProtocol) and managed protocols (everything else). |
162 | | // If we put such functionality in IProtocol via virtual methods, that |
163 | | // means that *every* protocol inherits that functionality through said |
164 | | // virtual methods, then every protocol needs a (largely redundant) |
165 | | // entry in its vtable. That redundancy adds up quickly with several |
166 | | // hundred protocols. |
167 | | // |
168 | | // This class (and its two subclasses) ensure that we don't have a bunch |
169 | | // of redundant entries in protocol vtables: we have a single vtable per |
170 | | // subclass, and then each protocol has its own instance of one of the |
171 | | // subclasses. This setup makes things a bit slower, but the space |
172 | | // savings are worth it. |
173 | | class ProtocolState |
174 | | { |
175 | | public: |
176 | 0 | ProtocolState() : mChannel(nullptr) {} |
177 | 0 | virtual ~ProtocolState() = default; |
178 | | |
179 | | // Shared memory functions. |
180 | | virtual Shmem::SharedMemory* CreateSharedMemory( |
181 | | size_t, SharedMemory::SharedMemoryType, bool, int32_t*) = 0; |
182 | | virtual Shmem::SharedMemory* LookupSharedMemory(int32_t) = 0; |
183 | | virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*) = 0; |
184 | | virtual bool DestroySharedMemory(Shmem&) = 0; |
185 | | |
186 | | // Protocol management functions. |
187 | | virtual int32_t Register(IProtocol*) = 0; |
188 | | virtual int32_t RegisterID(IProtocol*, int32_t) = 0; |
189 | | virtual IProtocol* Lookup(int32_t) = 0; |
190 | | virtual void Unregister(int32_t) = 0; |
191 | | |
192 | | // Returns the event target set by SetEventTargetForActor() if available. |
193 | | virtual nsIEventTarget* GetActorEventTarget() = 0; |
194 | | |
195 | | virtual void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) = 0; |
196 | | virtual void ReplaceEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) = 0; |
197 | | |
198 | | virtual already_AddRefed<nsIEventTarget> |
199 | | GetActorEventTarget(IProtocol* aActor) = 0; |
200 | | |
201 | | virtual const MessageChannel* GetIPCChannel() const = 0; |
202 | | virtual MessageChannel* GetIPCChannel() = 0; |
203 | | |
204 | | // XXX we have this weird setup where ProtocolState has an mChannel |
205 | | // member, but it (probably?) only gets set for protocols that have |
206 | | // a manager. That is, for toplevel protocols, this member is dead |
207 | | // weight and should be removed, since toplevel protocols maintain |
208 | | // their own channel. |
209 | 0 | void SetIPCChannel(MessageChannel* aChannel) { mChannel = aChannel; } |
210 | | |
211 | | protected: |
212 | | MessageChannel* mChannel; |
213 | | }; |
214 | | |
215 | | // Managed protocols just forward all of their operations to the topmost |
216 | | // managing protocol. |
217 | | class ManagedState final : public ProtocolState |
218 | | { |
219 | | public: |
220 | | explicit ManagedState(IProtocol* aProtocol) |
221 | | : ProtocolState() |
222 | | , mProtocol(aProtocol) |
223 | | {} |
224 | | |
225 | | Shmem::SharedMemory* CreateSharedMemory( |
226 | | size_t, SharedMemory::SharedMemoryType, bool, int32_t*) override; |
227 | | Shmem::SharedMemory* LookupSharedMemory(int32_t) override; |
228 | | bool IsTrackingSharedMemory(Shmem::SharedMemory*) override; |
229 | | bool DestroySharedMemory(Shmem&) override; |
230 | | |
231 | | int32_t Register(IProtocol*) override; |
232 | | int32_t RegisterID(IProtocol*, int32_t) override; |
233 | | IProtocol* Lookup(int32_t) override; |
234 | | void Unregister(int32_t) override; |
235 | | |
236 | | nsIEventTarget* GetActorEventTarget() override; |
237 | | void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override; |
238 | | void ReplaceEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override; |
239 | | already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor) override; |
240 | | |
241 | | const MessageChannel* GetIPCChannel() const override; |
242 | | MessageChannel* GetIPCChannel() override; |
243 | | |
244 | | private: |
245 | | IProtocol* const mProtocol; |
246 | | }; |
247 | | |
248 | | typedef base::ProcessId ProcessId; |
249 | | typedef IPC::Message Message; |
250 | | typedef IPC::MessageInfo MessageInfo; |
251 | | |
252 | | explicit IProtocol(Side aSide) |
253 | | : IProtocol(aSide, MakeUnique<ManagedState>(this)) |
254 | | {} |
255 | | |
256 | | int32_t Register(IProtocol* aRouted) |
257 | 0 | { |
258 | 0 | return mState->Register(aRouted); |
259 | 0 | } |
260 | | int32_t RegisterID(IProtocol* aRouted, int32_t aId) |
261 | 0 | { |
262 | 0 | return mState->RegisterID(aRouted, aId); |
263 | 0 | } |
264 | | IProtocol* Lookup(int32_t aId) |
265 | 0 | { |
266 | 0 | return mState->Lookup(aId); |
267 | 0 | } |
268 | | void Unregister(int32_t aId) |
269 | 0 | { |
270 | 0 | return mState->Unregister(aId); |
271 | 0 | } |
272 | | |
273 | | virtual void RemoveManagee(int32_t, IProtocol*) = 0; |
274 | | |
275 | | Shmem::SharedMemory* CreateSharedMemory( |
276 | | size_t aSize, SharedMemory::SharedMemoryType aType, bool aUnsafe, int32_t* aId) |
277 | 0 | { |
278 | 0 | return mState->CreateSharedMemory(aSize, aType, aUnsafe, aId); |
279 | 0 | } |
280 | | Shmem::SharedMemory* LookupSharedMemory(int32_t aId) |
281 | 0 | { |
282 | 0 | return mState->LookupSharedMemory(aId); |
283 | 0 | } |
284 | | bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) |
285 | 0 | { |
286 | 0 | return mState->IsTrackingSharedMemory(aSegment); |
287 | 0 | } |
288 | | bool DestroySharedMemory(Shmem& aShmem) |
289 | 0 | { |
290 | 0 | return mState->DestroySharedMemory(aShmem); |
291 | 0 | } |
292 | | |
293 | | MessageChannel* GetIPCChannel() |
294 | 0 | { |
295 | 0 | return mState->GetIPCChannel(); |
296 | 0 | } |
297 | | const MessageChannel* GetIPCChannel() const |
298 | 0 | { |
299 | 0 | return mState->GetIPCChannel(); |
300 | 0 | } |
301 | | void SetMiddlemanIPCChannel(MessageChannel* aChannel) |
302 | | { |
303 | | // Middleman processes sometimes need to change the channel used by a |
304 | | // protocol. |
305 | | MOZ_RELEASE_ASSERT(recordreplay::IsMiddleman()); |
306 | | mState->SetIPCChannel(aChannel); |
307 | | } |
308 | | |
309 | | // XXX odd ducks, acknowledged |
310 | | virtual ProcessId OtherPid() const; |
311 | 0 | Side GetSide() const { return mSide; } |
312 | | |
313 | | void FatalError(const char* const aErrorMsg) const; |
314 | | virtual void HandleFatalError(const char* aErrorMsg) const; |
315 | | |
316 | | Maybe<IProtocol*> ReadActor(const IPC::Message* aMessage, PickleIterator* aIter, bool aNullable, |
317 | | const char* aActorDescription, int32_t aProtocolTypeId); |
318 | | |
319 | | virtual Result OnMessageReceived(const Message& aMessage) = 0; |
320 | | virtual Result OnMessageReceived(const Message& aMessage, Message *& aReply) = 0; |
321 | | virtual Result OnCallReceived(const Message& aMessage, Message *& aReply) = 0; |
322 | | |
323 | | virtual int32_t GetProtocolTypeId() = 0; |
324 | | |
325 | 0 | int32_t Id() const { return mId; } |
326 | 0 | IProtocol* Manager() const { return mManager; } |
327 | | |
328 | | bool AllocShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aOutMem); |
329 | | bool AllocUnsafeShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aOutMem); |
330 | | bool DeallocShmem(Shmem& aMem); |
331 | | |
332 | | // Sets an event target to which all messages for aActor will be |
333 | | // dispatched. This method must be called before right before the SendPFoo |
334 | | // message for aActor is sent. And SendPFoo *must* be called if |
335 | | // SetEventTargetForActor is called. The receiver when calling |
336 | | // SetEventTargetForActor must be the actor that will be the manager for |
337 | | // aActor. |
338 | | void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget); |
339 | | |
340 | | // Replace the event target for the messages of aActor. There must not be |
341 | | // any messages of aActor in the task queue, or we might run into some |
342 | | // unexpected behavior. |
343 | | void ReplaceEventTargetForActor(IProtocol* aActor, |
344 | | nsIEventTarget* aEventTarget); |
345 | | |
346 | | nsIEventTarget* GetActorEventTarget(); |
347 | | already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor); |
348 | | |
349 | | protected: |
350 | | IProtocol(Side aSide, UniquePtr<ProtocolState> aState) |
351 | | : mId(0) |
352 | | , mSide(aSide) |
353 | | , mManager(nullptr) |
354 | | , mState(std::move(aState)) |
355 | 0 | {} |
356 | | |
357 | | friend class IToplevelProtocol; |
358 | | |
359 | 0 | void SetId(int32_t aId) { mId = aId; } |
360 | | void ResetManager() { mManager = nullptr; } |
361 | | // We have separate functions because the accessibility code manually |
362 | | // calls SetManager. |
363 | | void SetManager(IProtocol* aManager); |
364 | | |
365 | | // Sets the manager for the protocol and registers the protocol with |
366 | | // its manager, setting up channels for the protocol as well. Not |
367 | | // for use outside of IPDL. |
368 | | void SetManagerAndRegister(IProtocol* aManager); |
369 | | void SetManagerAndRegister(IProtocol* aManager, int32_t aId); |
370 | | |
371 | | static const int32_t kNullActorId = 0; |
372 | | static const int32_t kFreedActorId = 1; |
373 | | |
374 | | private: |
375 | | int32_t mId; |
376 | | Side mSide; |
377 | | IProtocol* mManager; |
378 | | UniquePtr<ProtocolState> mState; |
379 | | }; |
380 | | |
381 | | typedef IPCMessageStart ProtocolId; |
382 | | |
383 | 0 | #define IPC_OK() mozilla::ipc::IPCResult::Ok() |
384 | | #define IPC_FAIL(actor, why) mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__, (why)) |
385 | | #define IPC_FAIL_NO_REASON(actor) mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__) |
386 | | |
387 | | /** |
388 | | * All message deserializer and message handler should return this |
389 | | * type via above macros. We use a less generic name here to avoid |
390 | | * conflict with mozilla::Result because we have quite a few using |
391 | | * namespace mozilla::ipc; in the code base. |
392 | | */ |
393 | | class IPCResult { |
394 | | public: |
395 | | static IPCResult Ok() { return IPCResult(true); } |
396 | | static IPCResult Fail(NotNull<IProtocol*> aActor, const char* aWhere, const char* aWhy = ""); |
397 | | MOZ_IMPLICIT operator bool() const { return mSuccess; } |
398 | | private: |
399 | | explicit IPCResult(bool aResult) : mSuccess(aResult) {} |
400 | | bool mSuccess; |
401 | | }; |
402 | | |
403 | | template<class PFooSide> |
404 | | class Endpoint; |
405 | | |
406 | | /** |
407 | | * All top-level protocols should inherit this class. |
408 | | * |
409 | | * IToplevelProtocol tracks all top-level protocol actors created from |
410 | | * this protocol actor. |
411 | | */ |
412 | | class IToplevelProtocol : public IProtocol |
413 | | { |
414 | | template<class PFooSide> friend class Endpoint; |
415 | | |
416 | | protected: |
417 | | explicit IToplevelProtocol(const char* aName, ProtocolId aProtoId, |
418 | | Side aSide); |
419 | | ~IToplevelProtocol(); |
420 | | |
421 | | public: |
422 | | enum ProcessIdState { |
423 | | eUnstarted, |
424 | | ePending, |
425 | | eReady, |
426 | | eError |
427 | | }; |
428 | | |
429 | | class ToplevelState final : public ProtocolState |
430 | | { |
431 | | #ifdef FUZZING |
432 | | friend class mozilla::ipc::ProtocolFuzzerHelper; |
433 | | #endif |
434 | | |
435 | | public: |
436 | | ToplevelState(const char* aName, IToplevelProtocol* aProtocol, Side aSide); |
437 | | |
438 | | Shmem::SharedMemory* CreateSharedMemory( |
439 | | size_t, SharedMemory::SharedMemoryType, bool, int32_t*) override; |
440 | | Shmem::SharedMemory* LookupSharedMemory(int32_t) override; |
441 | | bool IsTrackingSharedMemory(Shmem::SharedMemory*) override; |
442 | | bool DestroySharedMemory(Shmem&) override; |
443 | | |
444 | | void DeallocShmems(); |
445 | | |
446 | | bool ShmemCreated(const Message& aMsg); |
447 | | bool ShmemDestroyed(const Message& aMsg); |
448 | | |
449 | | int32_t Register(IProtocol*) override; |
450 | | int32_t RegisterID(IProtocol*, int32_t) override; |
451 | | IProtocol* Lookup(int32_t) override; |
452 | | void Unregister(int32_t) override; |
453 | | |
454 | | nsIEventTarget* GetActorEventTarget() override; |
455 | | void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override; |
456 | | void ReplaceEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override; |
457 | | already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor) override; |
458 | | |
459 | | virtual already_AddRefed<nsIEventTarget> |
460 | | GetMessageEventTarget(const Message& aMsg); |
461 | | |
462 | | const MessageChannel* GetIPCChannel() const override; |
463 | | MessageChannel* GetIPCChannel() override; |
464 | | |
465 | | private: |
466 | | IToplevelProtocol* const mProtocol; |
467 | | IDMap<IProtocol*> mActorMap; |
468 | | int32_t mLastRouteId; |
469 | | IDMap<Shmem::SharedMemory*> mShmemMap; |
470 | | Shmem::id_t mLastShmemId; |
471 | | |
472 | | Mutex mEventTargetMutex; |
473 | | IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap; |
474 | | |
475 | | MessageChannel mChannel; |
476 | | }; |
477 | | |
478 | | using SchedulerGroupSet = nsILabelableRunnable::SchedulerGroupSet; |
479 | | |
480 | | void SetTransport(UniquePtr<Transport> aTrans) |
481 | 0 | { |
482 | 0 | mTrans = std::move(aTrans); |
483 | 0 | } |
484 | | |
485 | | Transport* GetTransport() const { return mTrans.get(); } |
486 | | |
487 | | ProtocolId GetProtocolId() const { return mProtocolId; } |
488 | | |
489 | | base::ProcessId OtherPid() const final; |
490 | | void SetOtherProcessId(base::ProcessId aOtherPid, |
491 | | ProcessIdState aState = ProcessIdState::eReady); |
492 | | |
493 | | bool TakeMinidump(nsIFile** aDump, uint32_t* aSequence); |
494 | | |
495 | | virtual void OnChannelClose() = 0; |
496 | | virtual void OnChannelError() = 0; |
497 | 0 | virtual void ProcessingError(Result aError, const char* aMsgName) {} |
498 | | virtual void OnChannelConnected(int32_t peer_pid) {} |
499 | | |
500 | | bool Open(mozilla::ipc::Transport* aTransport, |
501 | | base::ProcessId aOtherPid, |
502 | | MessageLoop* aThread = nullptr, |
503 | | mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide); |
504 | | |
505 | | bool Open(MessageChannel* aChannel, |
506 | | MessageLoop* aMessageLoop, |
507 | | mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide); |
508 | | |
509 | | bool Open(MessageChannel* aChannel, |
510 | | nsIEventTarget* aEventTarget, |
511 | | mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide); |
512 | | |
513 | | bool OpenWithAsyncPid(mozilla::ipc::Transport* aTransport, |
514 | | MessageLoop* aThread = nullptr, |
515 | | mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide); |
516 | | |
517 | | void Close(); |
518 | | |
519 | | void SetReplyTimeoutMs(int32_t aTimeoutMs); |
520 | | |
521 | | void DeallocShmems() { DowncastState()->DeallocShmems(); } |
522 | | |
523 | | bool ShmemCreated(const Message& aMsg) { return DowncastState()->ShmemCreated(aMsg); } |
524 | | bool ShmemDestroyed(const Message& aMsg) { return DowncastState()->ShmemDestroyed(aMsg); } |
525 | | |
526 | 0 | virtual bool ShouldContinueFromReplyTimeout() { |
527 | 0 | return false; |
528 | 0 | } |
529 | | |
530 | | // WARNING: This function is called with the MessageChannel monitor held. |
531 | | virtual void IntentionalCrash() { |
532 | | MOZ_CRASH("Intentional IPDL crash"); |
533 | | } |
534 | | |
535 | | // The code here is only useful for fuzzing. It should not be used for any |
536 | | // other purpose. |
537 | | #ifdef DEBUG |
538 | | // Returns true if we should simulate a timeout. |
539 | | // WARNING: This is a testing-only function that is called with the |
540 | | // MessageChannel monitor held. Don't do anything fancy here or we could |
541 | | // deadlock. |
542 | | virtual bool ArtificialTimeout() { |
543 | | return false; |
544 | | } |
545 | | |
546 | | // Returns true if we want to cause the worker thread to sleep with the |
547 | | // monitor unlocked. |
548 | | virtual bool NeedArtificialSleep() { |
549 | | return false; |
550 | | } |
551 | | |
552 | | // This function should be implemented to sleep for some amount of time on |
553 | | // the worker thread. Will only be called if NeedArtificialSleep() returns |
554 | | // true. |
555 | | virtual void ArtificialSleep() {} |
556 | | #else |
557 | | bool ArtificialTimeout() { return false; } |
558 | 0 | bool NeedArtificialSleep() { return false; } |
559 | 0 | void ArtificialSleep() {} |
560 | | #endif |
561 | | |
562 | 0 | virtual void EnteredCxxStack() {} |
563 | 0 | virtual void ExitedCxxStack() {} |
564 | 0 | virtual void EnteredCall() {} |
565 | 0 | virtual void ExitedCall() {} |
566 | | |
567 | | bool IsOnCxxStack() const; |
568 | | |
569 | | virtual RacyInterruptPolicy MediateInterruptRace(const MessageInfo& parent, |
570 | | const MessageInfo& child) |
571 | | { |
572 | | return RIPChildWins; |
573 | | } |
574 | | |
575 | | /** |
576 | | * Return true if windows messages can be handled while waiting for a reply |
577 | | * to a sync IPDL message. |
578 | | */ |
579 | | virtual bool HandleWindowsMessages(const Message& aMsg) const { return true; } |
580 | | |
581 | | virtual void OnEnteredSyncSend() { |
582 | | } |
583 | | virtual void OnExitedSyncSend() { |
584 | | } |
585 | | |
586 | | virtual void ProcessRemoteNativeEventsInInterruptCall() { |
587 | | } |
588 | | |
589 | | // Override this method in top-level protocols to change the SchedulerGroups |
590 | | // that a message might affect. This should be used only as a last resort |
591 | | // when it's difficult to determine an EventTarget ahead of time. See the |
592 | | // comment in nsILabelableRunnable.h for more information. |
593 | | virtual bool |
594 | | GetMessageSchedulerGroups(const Message& aMsg, SchedulerGroupSet& aGroups) |
595 | | { |
596 | | return false; |
597 | | } |
598 | | |
599 | | // This method is only used for collecting telemetry bits in various places, |
600 | | // and we shouldn't pay the overhead of having it in protocol vtables when |
601 | | // it's not being used. |
602 | | #ifdef EARLY_BETA_OR_EARLIER |
603 | | virtual void OnChannelReceivedMessage(const Message& aMsg) {} |
604 | | #endif |
605 | | |
606 | | bool IsMainThreadProtocol() const { return mIsMainThreadProtocol; } |
607 | 0 | void SetIsMainThreadProtocol() { mIsMainThreadProtocol = NS_IsMainThread(); } |
608 | | |
609 | | already_AddRefed<nsIEventTarget> |
610 | | GetMessageEventTarget(const Message& aMsg) |
611 | 0 | { |
612 | 0 | return DowncastState()->GetMessageEventTarget(aMsg); |
613 | 0 | } |
614 | | |
615 | | protected: |
616 | | ToplevelState* DowncastState() const |
617 | 0 | { |
618 | 0 | return static_cast<ToplevelState*>(mState.get()); |
619 | 0 | } |
620 | | |
621 | | // Override this method in top-level protocols to change the event target |
622 | | // for a new actor (and its sub-actors). |
623 | | virtual already_AddRefed<nsIEventTarget> |
624 | | GetConstructedEventTarget(const Message& aMsg) { return nullptr; } |
625 | | |
626 | | // Override this method in top-level protocols to change the event target |
627 | | // for specific messages. |
628 | | virtual already_AddRefed<nsIEventTarget> |
629 | | GetSpecificMessageEventTarget(const Message& aMsg) { return nullptr; } |
630 | | |
631 | | // This monitor protects mOtherPid and mOtherPidState. All other fields |
632 | | // should only be accessed on the worker thread. |
633 | | mutable mozilla::Monitor mMonitor; |
634 | | private: |
635 | | base::ProcessId OtherPidMaybeInvalid() const; |
636 | | |
637 | | ProtocolId mProtocolId; |
638 | | UniquePtr<Transport> mTrans; |
639 | | base::ProcessId mOtherPid; |
640 | | ProcessIdState mOtherPidState; |
641 | | bool mIsMainThreadProtocol; |
642 | | }; |
643 | | |
644 | | class IShmemAllocator |
645 | | { |
646 | | public: |
647 | | virtual bool AllocShmem(size_t aSize, |
648 | | mozilla::ipc::SharedMemory::SharedMemoryType aShmType, |
649 | | mozilla::ipc::Shmem* aShmem) = 0; |
650 | | virtual bool AllocUnsafeShmem(size_t aSize, |
651 | | mozilla::ipc::SharedMemory::SharedMemoryType aShmType, |
652 | | mozilla::ipc::Shmem* aShmem) = 0; |
653 | | virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0; |
654 | | }; |
655 | | |
656 | | #define FORWARD_SHMEM_ALLOCATOR_TO(aImplClass) \ |
657 | | virtual bool AllocShmem(size_t aSize, \ |
658 | | mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \ |
659 | | mozilla::ipc::Shmem* aShmem) override \ |
660 | | { return aImplClass::AllocShmem(aSize, aShmType, aShmem); } \ |
661 | | virtual bool AllocUnsafeShmem(size_t aSize, \ |
662 | | mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \ |
663 | | mozilla::ipc::Shmem* aShmem) override \ |
664 | | { return aImplClass::AllocUnsafeShmem(aSize, aShmType, aShmem); } \ |
665 | | virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override \ |
666 | | { return aImplClass::DeallocShmem(aShmem); } |
667 | | |
668 | | inline bool |
669 | | LoggingEnabled() |
670 | | { |
671 | | #if defined(DEBUG) || defined(FUZZING) |
672 | | return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG"); |
673 | | #else |
674 | | return false; |
675 | | #endif |
676 | | } |
677 | | |
678 | | inline bool |
679 | | LoggingEnabledFor(const char *aTopLevelProtocol) |
680 | | { |
681 | | #if defined(DEBUG) || defined(FUZZING) |
682 | | const char *filter = PR_GetEnv("MOZ_IPC_MESSAGE_LOG"); |
683 | | if (!filter) { |
684 | | return false; |
685 | | } |
686 | | return strcmp(filter, "1") == 0 || strcmp(filter, aTopLevelProtocol) == 0; |
687 | | #else |
688 | | return false; |
689 | | #endif |
690 | | } |
691 | | |
692 | | enum class MessageDirection { |
693 | | eSending, |
694 | | eReceiving, |
695 | | }; |
696 | | |
697 | | MOZ_NEVER_INLINE void |
698 | | LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid, |
699 | | const char* aContextDescription, |
700 | | uint32_t aMessageId, |
701 | | MessageDirection aDirection); |
702 | | |
703 | | MOZ_NEVER_INLINE void |
704 | | ProtocolErrorBreakpoint(const char* aMsg); |
705 | | |
706 | | // The code generator calls this function for errors which come from the |
707 | | // methods of protocols. Doing this saves codesize by making the error |
708 | | // cases significantly smaller. |
709 | | MOZ_NEVER_INLINE void |
710 | | FatalError(const char* aMsg, bool aIsParent); |
711 | | |
712 | | // The code generator calls this function for errors which are not |
713 | | // protocol-specific: errors in generated struct methods or errors in |
714 | | // transition functions, for instance. Doing this saves codesize by |
715 | | // by making the error cases significantly smaller. |
716 | | MOZ_NEVER_INLINE void |
717 | | LogicError(const char* aMsg); |
718 | | |
719 | | MOZ_NEVER_INLINE void |
720 | | ActorIdReadError(const char* aActorDescription); |
721 | | |
722 | | MOZ_NEVER_INLINE void |
723 | | BadActorIdError(const char* aActorDescription); |
724 | | |
725 | | MOZ_NEVER_INLINE void |
726 | | ActorLookupError(const char* aActorDescription); |
727 | | |
728 | | MOZ_NEVER_INLINE void |
729 | | MismatchedActorTypeError(const char* aActorDescription); |
730 | | |
731 | | MOZ_NEVER_INLINE void |
732 | | UnionTypeReadError(const char* aUnionName); |
733 | | |
734 | | MOZ_NEVER_INLINE void |
735 | | ArrayLengthReadError(const char* aElementName); |
736 | | |
737 | | MOZ_NEVER_INLINE void |
738 | | SentinelReadError(const char* aElementName); |
739 | | |
740 | | struct PrivateIPDLInterface {}; |
741 | | |
742 | | nsresult |
743 | | Bridge(const PrivateIPDLInterface&, |
744 | | MessageChannel*, base::ProcessId, MessageChannel*, base::ProcessId, |
745 | | ProtocolId, ProtocolId); |
746 | | |
747 | | bool |
748 | | Open(const PrivateIPDLInterface&, |
749 | | MessageChannel*, base::ProcessId, Transport::Mode, |
750 | | ProtocolId, ProtocolId); |
751 | | |
752 | | bool |
753 | | UnpackChannelOpened(const PrivateIPDLInterface&, |
754 | | const IPC::Message&, |
755 | | TransportDescriptor*, base::ProcessId*, ProtocolId*); |
756 | | |
757 | | #if defined(XP_WIN) |
758 | | // This is a restricted version of Windows' DuplicateHandle() function |
759 | | // that works inside the sandbox and can send handles but not retrieve |
760 | | // them. Unlike DuplicateHandle(), it takes a process ID rather than |
761 | | // a process handle. It returns true on success, false otherwise. |
762 | | bool |
763 | | DuplicateHandle(HANDLE aSourceHandle, |
764 | | DWORD aTargetProcessId, |
765 | | HANDLE* aTargetHandle, |
766 | | DWORD aDesiredAccess, |
767 | | DWORD aOptions); |
768 | | #endif |
769 | | |
770 | | /** |
771 | | * Annotate the crash reporter with the error code from the most recent system |
772 | | * call. Returns the system error. |
773 | | */ |
774 | | void AnnotateSystemError(); |
775 | | |
776 | | enum class LivenessState |
777 | | { |
778 | | Dead, |
779 | | Null, |
780 | | Start = Null |
781 | | }; |
782 | | |
783 | | bool |
784 | | StateTransition(bool aIsDelete, LivenessState* aNext); |
785 | | |
786 | | enum class ReEntrantDeleteLivenessState |
787 | | { |
788 | | Dead, |
789 | | Null, |
790 | | Dying, |
791 | | Start = Null, |
792 | | }; |
793 | | |
794 | | bool |
795 | | ReEntrantDeleteStateTransition(bool aIsDelete, |
796 | | bool aIsDeleteReply, |
797 | | ReEntrantDeleteLivenessState* aNext); |
798 | | |
799 | | /** |
800 | | * An endpoint represents one end of a partially initialized IPDL channel. To |
801 | | * set up a new top-level protocol: |
802 | | * |
803 | | * Endpoint<PFooParent> parentEp; |
804 | | * Endpoint<PFooChild> childEp; |
805 | | * nsresult rv; |
806 | | * rv = PFoo::CreateEndpoints(parentPid, childPid, &parentEp, &childEp); |
807 | | * |
808 | | * You're required to pass in parentPid and childPid, which are the pids of the |
809 | | * processes in which the parent and child endpoints will be used. |
810 | | * |
811 | | * Endpoints can be passed in IPDL messages or sent to other threads using |
812 | | * PostTask. Once an Endpoint has arrived at its destination process and thread, |
813 | | * you need to create the top-level actor and bind it to the endpoint: |
814 | | * |
815 | | * FooParent* parent = new FooParent(); |
816 | | * bool rv1 = parentEp.Bind(parent, processActor); |
817 | | * bool rv2 = parent->SendBar(...); |
818 | | * |
819 | | * (See Bind below for an explanation of processActor.) Once the actor is bound |
820 | | * to the endpoint, it can send and receive messages. |
821 | | */ |
822 | | template<class PFooSide> |
823 | | class Endpoint |
824 | | { |
825 | | public: |
826 | | typedef base::ProcessId ProcessId; |
827 | | |
828 | | Endpoint() |
829 | | : mValid(false) |
830 | | , mMode(static_cast<mozilla::ipc::Transport::Mode>(0)) |
831 | | , mMyPid(0) |
832 | | , mOtherPid(0) |
833 | 0 | { |
834 | 0 | } Unexecuted instantiation: mozilla::ipc::Endpoint<mozilla::ipc::PBackgroundParent>::Endpoint() Unexecuted instantiation: mozilla::ipc::Endpoint<mozilla::ipc::PBackgroundChild>::Endpoint() |
835 | | |
836 | | Endpoint(const PrivateIPDLInterface&, |
837 | | mozilla::ipc::Transport::Mode aMode, |
838 | | TransportDescriptor aTransport, |
839 | | ProcessId aMyPid, |
840 | | ProcessId aOtherPid) |
841 | | : mValid(true) |
842 | | , mMode(aMode) |
843 | | , mTransport(aTransport) |
844 | | , mMyPid(aMyPid) |
845 | | , mOtherPid(aOtherPid) |
846 | | {} |
847 | | |
848 | | Endpoint(Endpoint&& aOther) |
849 | | : mValid(aOther.mValid) |
850 | | , mTransport(aOther.mTransport) |
851 | | , mMyPid(aOther.mMyPid) |
852 | | , mOtherPid(aOther.mOtherPid) |
853 | 0 | { |
854 | 0 | if (aOther.mValid) { |
855 | 0 | mMode = aOther.mMode; |
856 | 0 | } |
857 | 0 | aOther.mValid = false; |
858 | 0 | } |
859 | | |
860 | | Endpoint& operator=(Endpoint&& aOther) |
861 | | { |
862 | | mValid = aOther.mValid; |
863 | | if (aOther.mValid) { |
864 | | mMode = aOther.mMode; |
865 | | } |
866 | | mTransport = aOther.mTransport; |
867 | | mMyPid = aOther.mMyPid; |
868 | | mOtherPid = aOther.mOtherPid; |
869 | | |
870 | | aOther.mValid = false; |
871 | | return *this; |
872 | | } |
873 | | |
874 | 0 | ~Endpoint() { |
875 | 0 | if (mValid) { |
876 | 0 | CloseDescriptor(mTransport); |
877 | 0 | } |
878 | 0 | } Unexecuted instantiation: mozilla::ipc::Endpoint<mozilla::ipc::PBackgroundParent>::~Endpoint() Unexecuted instantiation: mozilla::ipc::Endpoint<mozilla::ipc::PBackgroundChild>::~Endpoint() |
879 | | |
880 | | ProcessId OtherPid() const { |
881 | | return mOtherPid; |
882 | | } |
883 | | |
884 | | // This method binds aActor to this endpoint. After this call, the actor can |
885 | | // be used to send and receive messages. The endpoint becomes invalid. |
886 | | bool Bind(PFooSide* aActor) |
887 | 0 | { |
888 | 0 | MOZ_RELEASE_ASSERT(mValid); |
889 | 0 | if (mMyPid != base::GetCurrentProcId()) { |
890 | 0 | // These pids must match, unless we are recording or replaying, in |
891 | 0 | // which case the parent process will have supplied the pid for the |
892 | 0 | // middleman process instead. Fix this here. If we're replaying |
893 | 0 | // we'll see the pid of the middleman used while recording. |
894 | 0 | MOZ_RELEASE_ASSERT(recordreplay::IsRecordingOrReplaying()); |
895 | 0 | MOZ_RELEASE_ASSERT(recordreplay::IsReplaying() || |
896 | 0 | mMyPid == recordreplay::child::MiddlemanProcessId()); |
897 | 0 | mMyPid = base::GetCurrentProcId(); |
898 | 0 | } |
899 | 0 |
|
900 | 0 | UniquePtr<Transport> t = mozilla::ipc::OpenDescriptor(mTransport, mMode); |
901 | 0 | if (!t) { |
902 | 0 | return false; |
903 | 0 | } |
904 | 0 | if (!aActor->Open(t.get(), mOtherPid, XRE_GetIOMessageLoop(), |
905 | 0 | mMode == Transport::MODE_SERVER ? ParentSide : ChildSide)) { |
906 | 0 | return false; |
907 | 0 | } |
908 | 0 | mValid = false; |
909 | 0 | aActor->SetTransport(std::move(t)); |
910 | 0 | return true; |
911 | 0 | } Unexecuted instantiation: mozilla::ipc::Endpoint<mozilla::ipc::PBackgroundParent>::Bind(mozilla::ipc::PBackgroundParent*) Unexecuted instantiation: mozilla::ipc::Endpoint<mozilla::ipc::PBackgroundChild>::Bind(mozilla::ipc::PBackgroundChild*) |
912 | | |
913 | | bool IsValid() const { |
914 | | return mValid; |
915 | | } |
916 | | |
917 | | private: |
918 | | friend struct IPC::ParamTraits<Endpoint<PFooSide>>; |
919 | | |
920 | | Endpoint(const Endpoint&) = delete; |
921 | | Endpoint& operator=(const Endpoint&) = delete; |
922 | | |
923 | | bool mValid; |
924 | | mozilla::ipc::Transport::Mode mMode; |
925 | | TransportDescriptor mTransport; |
926 | | ProcessId mMyPid, mOtherPid; |
927 | | }; |
928 | | |
929 | | #if defined(XP_MACOSX) |
930 | | void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error); |
931 | | #else |
932 | | static inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error) |
933 | 0 | {} Unexecuted instantiation: Unified_cpp_ipc_glue0.cpp:mozilla::ipc::AnnotateCrashReportWithErrno(CrashReporter::Annotation, int) Unexecuted instantiation: Unified_cpp_ipc_glue1.cpp:mozilla::ipc::AnnotateCrashReportWithErrno(CrashReporter::Annotation, int) |
934 | | #endif |
935 | | |
936 | | // This function is used internally to create a pair of Endpoints. See the |
937 | | // comment above Endpoint for a description of how it might be used. |
938 | | template<class PFooParent, class PFooChild> |
939 | | nsresult |
940 | | CreateEndpoints(const PrivateIPDLInterface& aPrivate, |
941 | | base::ProcessId aParentDestPid, |
942 | | base::ProcessId aChildDestPid, |
943 | | Endpoint<PFooParent>* aParentEndpoint, |
944 | | Endpoint<PFooChild>* aChildEndpoint) |
945 | | { |
946 | | MOZ_RELEASE_ASSERT(aParentDestPid); |
947 | | MOZ_RELEASE_ASSERT(aChildDestPid); |
948 | | |
949 | | TransportDescriptor parentTransport, childTransport; |
950 | | nsresult rv; |
951 | | if (NS_FAILED(rv = CreateTransport(aParentDestPid, &parentTransport, &childTransport))) { |
952 | | AnnotateCrashReportWithErrno( |
953 | | CrashReporter::Annotation::IpcCreateEndpointsNsresult, int(rv)); |
954 | | return rv; |
955 | | } |
956 | | |
957 | | *aParentEndpoint = Endpoint<PFooParent>(aPrivate, mozilla::ipc::Transport::MODE_SERVER, |
958 | | parentTransport, aParentDestPid, aChildDestPid); |
959 | | |
960 | | *aChildEndpoint = Endpoint<PFooChild>(aPrivate, mozilla::ipc::Transport::MODE_CLIENT, |
961 | | childTransport, aChildDestPid, aParentDestPid); |
962 | | |
963 | | return NS_OK; |
964 | | } |
965 | | |
966 | | void |
967 | | TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable, |
968 | | nsTArray<void*>& aArray); |
969 | | |
970 | | } // namespace ipc |
971 | | |
972 | | template<typename Protocol> |
973 | | class ManagedContainer : public nsTHashtable<nsPtrHashKey<Protocol>> |
974 | | { |
975 | | typedef nsTHashtable<nsPtrHashKey<Protocol>> BaseClass; |
976 | | |
977 | | public: |
978 | | // Having the core logic work on void pointers, rather than typed pointers, |
979 | | // means that we can have one instance of this code out-of-line, rather |
980 | | // than several hundred instances of this code out-of-lined. (Those |
981 | | // repeated instances don't necessarily get folded together by the linker |
982 | | // because they contain member offsets and such that differ between the |
983 | | // functions.) We do have to pay for it with some eye-bleedingly bad casts, |
984 | | // though. |
985 | | void ToArray(nsTArray<Protocol*>& aArray) const { |
986 | | ::mozilla::ipc::TableToArray(*reinterpret_cast<const nsTHashtable<nsPtrHashKey<void>>*> |
987 | | (static_cast<const BaseClass*>(this)), |
988 | | reinterpret_cast<nsTArray<void*>&>(aArray)); |
989 | | } |
990 | | }; |
991 | | |
992 | | template<typename Protocol> |
993 | | Protocol* |
994 | | LoneManagedOrNullAsserts(const ManagedContainer<Protocol>& aManagees) |
995 | | { |
996 | | if (aManagees.IsEmpty()) { |
997 | | return nullptr; |
998 | | } |
999 | | MOZ_ASSERT(aManagees.Count() == 1); |
1000 | | return aManagees.ConstIter().Get()->GetKey(); |
1001 | | } |
1002 | | |
1003 | | // appId's are for B2G only currently, where managees.Count() == 1. This is |
1004 | | // not guaranteed currently in Desktop, so for paths used for desktop, |
1005 | | // don't assert there's one managee. |
1006 | | template<typename Protocol> |
1007 | | Protocol* |
1008 | | SingleManagedOrNull(const ManagedContainer<Protocol>& aManagees) |
1009 | | { |
1010 | | if (aManagees.Count() != 1) { |
1011 | | return nullptr; |
1012 | | } |
1013 | | return aManagees.ConstIter().Get()->GetKey(); |
1014 | | } |
1015 | | |
1016 | | } // namespace mozilla |
1017 | | |
1018 | | |
1019 | | namespace IPC { |
1020 | | |
1021 | | template <> |
1022 | | struct ParamTraits<mozilla::ipc::ActorHandle> |
1023 | | { |
1024 | | typedef mozilla::ipc::ActorHandle paramType; |
1025 | | |
1026 | | static void Write(Message* aMsg, const paramType& aParam) |
1027 | | { |
1028 | | IPC::WriteParam(aMsg, aParam.mId); |
1029 | | } |
1030 | | |
1031 | | static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) |
1032 | 0 | { |
1033 | 0 | int id; |
1034 | 0 | if (IPC::ReadParam(aMsg, aIter, &id)) { |
1035 | 0 | aResult->mId = id; |
1036 | 0 | return true; |
1037 | 0 | } |
1038 | 0 | return false; |
1039 | 0 | } |
1040 | | |
1041 | | static void Log(const paramType& aParam, std::wstring* aLog) |
1042 | | { |
1043 | | aLog->append(StringPrintf(L"(%d)", aParam.mId)); |
1044 | | } |
1045 | | }; |
1046 | | |
1047 | | template<class PFooSide> |
1048 | | struct ParamTraits<mozilla::ipc::Endpoint<PFooSide>> |
1049 | | { |
1050 | | typedef mozilla::ipc::Endpoint<PFooSide> paramType; |
1051 | | |
1052 | | static void Write(Message* aMsg, const paramType& aParam) |
1053 | | { |
1054 | | IPC::WriteParam(aMsg, aParam.mValid); |
1055 | | if (!aParam.mValid) { |
1056 | | return; |
1057 | | } |
1058 | | |
1059 | | IPC::WriteParam(aMsg, static_cast<uint32_t>(aParam.mMode)); |
1060 | | |
1061 | | // We duplicate the descriptor so that our own file descriptor remains |
1062 | | // valid after the write. An alternative would be to set |
1063 | | // aParam.mTransport.mValid to false, but that won't work because aParam |
1064 | | // is const. |
1065 | | mozilla::ipc::TransportDescriptor desc = mozilla::ipc::DuplicateDescriptor(aParam.mTransport); |
1066 | | IPC::WriteParam(aMsg, desc); |
1067 | | |
1068 | | IPC::WriteParam(aMsg, aParam.mMyPid); |
1069 | | IPC::WriteParam(aMsg, aParam.mOtherPid); |
1070 | | } |
1071 | | |
1072 | | static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) |
1073 | | { |
1074 | | MOZ_RELEASE_ASSERT(!aResult->mValid); |
1075 | | |
1076 | | if (!IPC::ReadParam(aMsg, aIter, &aResult->mValid)) { |
1077 | | return false; |
1078 | | } |
1079 | | if (!aResult->mValid) { |
1080 | | // Object is empty, but read succeeded. |
1081 | | return true; |
1082 | | } |
1083 | | |
1084 | | uint32_t mode; |
1085 | | if (!IPC::ReadParam(aMsg, aIter, &mode) || |
1086 | | !IPC::ReadParam(aMsg, aIter, &aResult->mTransport) || |
1087 | | !IPC::ReadParam(aMsg, aIter, &aResult->mMyPid) || |
1088 | | !IPC::ReadParam(aMsg, aIter, &aResult->mOtherPid)) { |
1089 | | return false; |
1090 | | } |
1091 | | aResult->mMode = Channel::Mode(mode); |
1092 | | return true; |
1093 | | } |
1094 | | |
1095 | | static void Log(const paramType& aParam, std::wstring* aLog) |
1096 | | { |
1097 | | aLog->append(StringPrintf(L"Endpoint")); |
1098 | | } |
1099 | | }; |
1100 | | |
1101 | | } // namespace IPC |
1102 | | |
1103 | | |
1104 | | #endif // mozilla_ipc_ProtocolUtils_h |