1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "ImageBridgeChild.h"
8 
9 #include <vector>  // for vector
10 
11 #include "ImageBridgeParent.h"  // for ImageBridgeParent
12 #include "ImageContainer.h"     // for ImageContainer
13 #include "Layers.h"             // for Layer, etc
14 #include "SynchronousTask.h"
15 #include "mozilla/Assertions.h"        // for MOZ_ASSERT, etc
16 #include "mozilla/Monitor.h"           // for Monitor, MonitorAutoLock
17 #include "mozilla/ReentrantMonitor.h"  // for ReentrantMonitor, etc
18 #include "mozilla/StaticMutex.h"
19 #include "mozilla/StaticPtr.h"  // for StaticRefPtr
20 #include "mozilla/dom/ContentChild.h"
21 #include "mozilla/gfx/Point.h"  // for IntSize
22 #include "mozilla/gfx/gfxVars.h"
23 #include "mozilla/ipc/Endpoint.h"
24 #include "mozilla/ipc/MessageChannel.h"         // for MessageChannel, etc
25 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild, etc
26 #include "mozilla/layers/CompositorThread.h"
27 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
28 #include "mozilla/layers/ImageClient.h"        // for ImageClient
29 #include "mozilla/layers/LayersMessages.h"     // for CompositableOperation
30 #include "mozilla/layers/TextureClient.h"      // for TextureClient
31 #include "mozilla/layers/TextureClient.h"
32 #include "mozilla/media/MediaSystemResourceManager.h"  // for MediaSystemResourceManager
33 #include "mozilla/media/MediaSystemResourceManagerChild.h"  // for MediaSystemResourceManagerChild
34 #include "mozilla/mozalloc.h"  // for operator new, etc
35 #include "transport/runnable_utils.h"
36 #include "nsContentUtils.h"
37 #include "nsISupportsImpl.h"         // for ImageContainer::AddRef, etc
38 #include "nsTArray.h"                // for AutoTArray, nsTArray, etc
39 #include "nsTArrayForwardDeclare.h"  // for AutoTArray
40 #include "nsThreadUtils.h"           // for NS_IsMainThread
41 
42 #if defined(XP_WIN)
43 #  include "mozilla/gfx/DeviceManagerDx.h"
44 #endif
45 
46 #ifdef MOZ_WIDGET_ANDROID
47 #  include "mozilla/layers/AndroidHardwareBuffer.h"
48 #endif
49 
50 namespace mozilla {
51 namespace ipc {
52 class Shmem;
53 }  // namespace ipc
54 
55 namespace layers {
56 
57 using namespace mozilla::ipc;
58 using namespace mozilla::gfx;
59 using namespace mozilla::media;
60 
61 typedef std::vector<CompositableOperation> OpVector;
62 typedef nsTArray<OpDestroy> OpDestroyVector;
63 
64 struct CompositableTransaction {
CompositableTransactionmozilla::layers::CompositableTransaction65   CompositableTransaction() : mFinished(true) {}
~CompositableTransactionmozilla::layers::CompositableTransaction66   ~CompositableTransaction() { End(); }
Finishedmozilla::layers::CompositableTransaction67   bool Finished() const { return mFinished; }
Beginmozilla::layers::CompositableTransaction68   void Begin() {
69     MOZ_ASSERT(mFinished);
70     mFinished = false;
71   }
Endmozilla::layers::CompositableTransaction72   void End() {
73     mFinished = true;
74     mOperations.clear();
75     mDestroyedActors.Clear();
76   }
IsEmptymozilla::layers::CompositableTransaction77   bool IsEmpty() const {
78     return mOperations.empty() && mDestroyedActors.IsEmpty();
79   }
AddNoSwapEditmozilla::layers::CompositableTransaction80   void AddNoSwapEdit(const CompositableOperation& op) {
81     MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
82     mOperations.push_back(op);
83   }
84 
85   OpVector mOperations;
86   OpDestroyVector mDestroyedActors;
87 
88   bool mFinished;
89 };
90 
91 struct AutoEndTransaction final {
AutoEndTransactionmozilla::layers::AutoEndTransaction92   explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
~AutoEndTransactionmozilla::layers::AutoEndTransaction93   ~AutoEndTransaction() { mTxn->End(); }
94   CompositableTransaction* mTxn;
95 };
96 
UseTextures(CompositableClient * aCompositable,const nsTArray<TimedTextureClient> & aTextures)97 void ImageBridgeChild::UseTextures(
98     CompositableClient* aCompositable,
99     const nsTArray<TimedTextureClient>& aTextures) {
100   MOZ_ASSERT(aCompositable);
101   MOZ_ASSERT(aCompositable->GetIPCHandle());
102   MOZ_ASSERT(aCompositable->IsConnected());
103 
104   AutoTArray<TimedTexture, 4> textures;
105 
106   for (auto& t : aTextures) {
107     MOZ_ASSERT(t.mTextureClient);
108     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
109 
110     if (!t.mTextureClient->IsSharedWithCompositor()) {
111       return;
112     }
113 
114     bool readLocked = t.mTextureClient->OnForwardedToHost();
115 
116     auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence();
117     if (fenceFd.IsValid()) {
118       mTxn->AddNoSwapEdit(CompositableOperation(
119           aCompositable->GetIPCHandle(),
120           OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(),
121                                 fenceFd)));
122     }
123 
124     textures.AppendElement(
125         TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), t.mTimeStamp,
126                      t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
127 
128     // Wait end of usage on host side if TextureFlags::RECYCLE is set
129     HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
130   }
131   mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
132                                             OpUseTexture(textures)));
133 }
134 
HoldUntilCompositableRefReleasedIfNecessary(TextureClient * aClient)135 void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
136     TextureClient* aClient) {
137   if (!aClient) {
138     return;
139   }
140 
141 #ifdef MOZ_WIDGET_ANDROID
142   auto bufferId = aClient->GetInternalData()->GetBufferId();
143   if (bufferId.isSome()) {
144     MOZ_ASSERT(aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END);
145     AndroidHardwareBufferManager::Get()->HoldUntilNotifyNotUsed(
146         bufferId.ref(), GetFwdTransactionId(), /* aUsesImageBridge */ true);
147   }
148 #endif
149 
150   // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE or
151   // TextureFlags::WAIT_HOST_USAGE_END is set on ImageBridge.
152   bool waitNotifyNotUsed =
153       aClient->GetFlags() & TextureFlags::RECYCLE ||
154       aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
155   if (!waitNotifyNotUsed) {
156     return;
157   }
158 
159   aClient->SetLastFwdTransactionId(GetFwdTransactionId());
160   mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
161 }
162 
NotifyNotUsed(uint64_t aTextureId,uint64_t aFwdTransactionId)163 void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId,
164                                      uint64_t aFwdTransactionId) {
165   auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
166   if (it != mTexturesWaitingNotifyNotUsed.end()) {
167     if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
168       // Released on host side, but client already requested newer use texture.
169       return;
170     }
171     mTexturesWaitingNotifyNotUsed.erase(it);
172   }
173 }
174 
CancelWaitForNotifyNotUsed(uint64_t aTextureId)175 void ImageBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
176   MOZ_ASSERT(InImageBridgeChildThread());
177   mTexturesWaitingNotifyNotUsed.erase(aTextureId);
178 }
179 
180 // Singleton
181 static StaticMutex sImageBridgeSingletonLock;
182 static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
183 static StaticRefPtr<nsIThread> sImageBridgeChildThread;
184 
185 // dispatched function
ShutdownStep1(SynchronousTask * aTask)186 void ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask) {
187   AutoCompleteTask complete(aTask);
188 
189   MOZ_ASSERT(InImageBridgeChildThread(),
190              "Should be in ImageBridgeChild thread.");
191 
192   MediaSystemResourceManager::Shutdown();
193 
194   // Force all managed protocols to shut themselves down cleanly
195   nsTArray<PTextureChild*> textures;
196   ManagedPTextureChild(textures);
197   for (int i = textures.Length() - 1; i >= 0; --i) {
198     RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
199     if (client) {
200       client->Destroy();
201     }
202   }
203 
204   if (mCanSend) {
205     SendWillClose();
206   }
207   MarkShutDown();
208 
209   // From now on, no message can be sent through the image bridge from the
210   // client side except the final Stop message.
211 }
212 
213 // dispatched function
ShutdownStep2(SynchronousTask * aTask)214 void ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask) {
215   AutoCompleteTask complete(aTask);
216 
217   MOZ_ASSERT(InImageBridgeChildThread(),
218              "Should be in ImageBridgeChild thread.");
219   if (!mDestroyed) {
220     Close();
221   }
222 }
223 
ActorDestroy(ActorDestroyReason aWhy)224 void ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
225   mCanSend = false;
226   mDestroyed = true;
227   {
228     MutexAutoLock lock(mContainerMapLock);
229     mImageContainerListeners.clear();
230   }
231 }
232 
ActorDealloc()233 void ImageBridgeChild::ActorDealloc() { this->Release(); }
234 
CreateImageClientSync(SynchronousTask * aTask,RefPtr<ImageClient> * result,CompositableType aType,ImageContainer * aImageContainer)235 void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
236                                              RefPtr<ImageClient>* result,
237                                              CompositableType aType,
238                                              ImageContainer* aImageContainer) {
239   AutoCompleteTask complete(aTask);
240   *result = CreateImageClientNow(aType, aImageContainer);
241 }
242 
ImageBridgeChild(uint32_t aNamespace)243 ImageBridgeChild::ImageBridgeChild(uint32_t aNamespace)
244     : mNamespace(aNamespace),
245       mCanSend(false),
246       mDestroyed(false),
247       mFwdTransactionId(0),
248       mContainerMapLock("ImageBridgeChild.mContainerMapLock") {
249   MOZ_ASSERT(mNamespace);
250   MOZ_ASSERT(NS_IsMainThread());
251 
252   mTxn = new CompositableTransaction();
253 }
254 
~ImageBridgeChild()255 ImageBridgeChild::~ImageBridgeChild() { delete mTxn; }
256 
MarkShutDown()257 void ImageBridgeChild::MarkShutDown() {
258   mTexturesWaitingNotifyNotUsed.clear();
259 
260   mCanSend = false;
261 }
262 
Connect(CompositableClient * aCompositable,ImageContainer * aImageContainer)263 void ImageBridgeChild::Connect(CompositableClient* aCompositable,
264                                ImageContainer* aImageContainer) {
265   MOZ_ASSERT(aCompositable);
266   MOZ_ASSERT(InImageBridgeChildThread());
267   MOZ_ASSERT(CanSend());
268 
269   // Note: this is static, rather than per-IBC, so IDs are not re-used across
270   // ImageBridgeChild instances. This is relevant for the GPU process, where
271   // we don't want old IDs to potentially leak into a recreated ImageBridge.
272   static uint64_t sNextID = 1;
273   uint64_t id = sNextID++;
274 
275   // ImageClient of ImageContainer provides aImageContainer.
276   // But offscreen canvas does not provide it.
277   if (aImageContainer) {
278     MutexAutoLock lock(mContainerMapLock);
279     MOZ_ASSERT(mImageContainerListeners.find(id) ==
280                mImageContainerListeners.end());
281     mImageContainerListeners.emplace(
282         id, aImageContainer->GetImageContainerListener());
283   }
284 
285   CompositableHandle handle(id);
286   aCompositable->InitIPDL(handle);
287   SendNewCompositable(handle, aCompositable->GetTextureInfo());
288 }
289 
ForgetImageContainer(const CompositableHandle & aHandle)290 void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) {
291   MutexAutoLock lock(mContainerMapLock);
292   mImageContainerListeners.erase(aHandle.Value());
293 }
294 
295 /* static */
GetSingleton()296 RefPtr<ImageBridgeChild> ImageBridgeChild::GetSingleton() {
297   StaticMutexAutoLock lock(sImageBridgeSingletonLock);
298   return sImageBridgeChildSingleton;
299 }
300 
UpdateImageClient(RefPtr<ImageContainer> aContainer)301 void ImageBridgeChild::UpdateImageClient(RefPtr<ImageContainer> aContainer) {
302   if (!aContainer) {
303     return;
304   }
305 
306   if (!InImageBridgeChildThread()) {
307     RefPtr<Runnable> runnable =
308         WrapRunnable(RefPtr<ImageBridgeChild>(this),
309                      &ImageBridgeChild::UpdateImageClient, aContainer);
310     GetThread()->Dispatch(runnable.forget());
311     return;
312   }
313 
314   if (!CanSend()) {
315     return;
316   }
317 
318   RefPtr<ImageClient> client = aContainer->GetImageClient();
319   if (NS_WARN_IF(!client)) {
320     return;
321   }
322 
323   // If the client has become disconnected before this event was dispatched,
324   // early return now.
325   if (!client->IsConnected()) {
326     return;
327   }
328 
329   BeginTransaction();
330   client->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
331   EndTransaction();
332 }
333 
FlushAllImagesSync(SynchronousTask * aTask,ImageClient * aClient,ImageContainer * aContainer)334 void ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
335                                           ImageClient* aClient,
336                                           ImageContainer* aContainer) {
337   AutoCompleteTask complete(aTask);
338 
339   if (!CanSend()) {
340     return;
341   }
342 
343   MOZ_ASSERT(aClient);
344   BeginTransaction();
345   if (aContainer) {
346     aContainer->ClearImagesFromImageBridge();
347   }
348   aClient->FlushAllImages();
349   EndTransaction();
350 }
351 
FlushAllImages(ImageClient * aClient,ImageContainer * aContainer)352 void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
353                                       ImageContainer* aContainer) {
354   MOZ_ASSERT(aClient);
355   MOZ_ASSERT(!InImageBridgeChildThread());
356 
357   if (InImageBridgeChildThread()) {
358     NS_ERROR(
359         "ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
360     return;
361   }
362 
363   SynchronousTask task("FlushAllImages Lock");
364 
365   // RefPtrs on arguments are not needed since this dispatches synchronously.
366   RefPtr<Runnable> runnable = WrapRunnable(
367       RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::FlushAllImagesSync,
368       &task, aClient, aContainer);
369   GetThread()->Dispatch(runnable.forget());
370 
371   task.Wait();
372 }
373 
BeginTransaction()374 void ImageBridgeChild::BeginTransaction() {
375   MOZ_ASSERT(CanSend());
376   MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
377   UpdateFwdTransactionId();
378   mTxn->Begin();
379 }
380 
EndTransaction()381 void ImageBridgeChild::EndTransaction() {
382   MOZ_ASSERT(CanSend());
383   MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
384 
385   AutoEndTransaction _(mTxn);
386 
387   if (mTxn->IsEmpty()) {
388     return;
389   }
390 
391   AutoTArray<CompositableOperation, 10> cset;
392   cset.SetCapacity(mTxn->mOperations.size());
393   if (!mTxn->mOperations.empty()) {
394     cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
395   }
396 
397   if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
398     NS_WARNING("could not send async texture transaction");
399     return;
400   }
401 }
402 
InitForContent(Endpoint<PImageBridgeChild> && aEndpoint,uint32_t aNamespace)403 bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
404                                       uint32_t aNamespace) {
405   MOZ_ASSERT(NS_IsMainThread());
406 
407   gfxPlatform::GetPlatform();
408 
409   if (!sImageBridgeChildThread) {
410     nsCOMPtr<nsIThread> thread;
411     nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
412     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
413                        "Failed to start ImageBridgeChild thread!");
414     sImageBridgeChildThread = thread.forget();
415   }
416 
417   RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
418 
419   child->GetThread()->Dispatch(NS_NewRunnableFunction(
420       "layers::ImageBridgeChild::Bind",
421       [child, endpoint = std::move(aEndpoint)]() mutable {
422         child->Bind(std::move(endpoint));
423       }));
424 
425   // Assign this after so other threads can't post messages before we connect to
426   // IPDL.
427   {
428     StaticMutexAutoLock lock(sImageBridgeSingletonLock);
429     sImageBridgeChildSingleton = child;
430   }
431 
432   return true;
433 }
434 
ReinitForContent(Endpoint<PImageBridgeChild> && aEndpoint,uint32_t aNamespace)435 bool ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
436                                         uint32_t aNamespace) {
437   MOZ_ASSERT(NS_IsMainThread());
438 
439   // Note that at this point, ActorDestroy may not have been called yet,
440   // meaning mCanSend is still true. In this case we will try to send a
441   // synchronous WillClose message to the parent, and will certainly get a
442   // false result and a MsgDropped processing error. This is okay.
443   ShutdownSingleton();
444 
445   return InitForContent(std::move(aEndpoint), aNamespace);
446 }
447 
Bind(Endpoint<PImageBridgeChild> && aEndpoint)448 void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) {
449   if (!aEndpoint.Bind(this)) {
450     return;
451   }
452 
453   // This reference is dropped in DeallocPImageBridgeChild.
454   this->AddRef();
455 
456   mCanSend = true;
457 }
458 
BindSameProcess(RefPtr<ImageBridgeParent> aParent)459 void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) {
460   Open(aParent, aParent->GetThread(), mozilla::ipc::ChildSide);
461 
462   // This reference is dropped in DeallocPImageBridgeChild.
463   this->AddRef();
464 
465   mCanSend = true;
466 }
467 
468 /* static */
ShutDown()469 void ImageBridgeChild::ShutDown() {
470   MOZ_ASSERT(NS_IsMainThread());
471 
472   ShutdownSingleton();
473 
474   if (sImageBridgeChildThread) {
475     sImageBridgeChildThread->Shutdown();
476     sImageBridgeChildThread = nullptr;
477   }
478 }
479 
480 /* static */
ShutdownSingleton()481 void ImageBridgeChild::ShutdownSingleton() {
482   MOZ_ASSERT(NS_IsMainThread());
483 
484   if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
485     child->WillShutdown();
486 
487     StaticMutexAutoLock lock(sImageBridgeSingletonLock);
488     sImageBridgeChildSingleton = nullptr;
489   }
490 }
491 
WillShutdown()492 void ImageBridgeChild::WillShutdown() {
493   {
494     SynchronousTask task("ImageBridge ShutdownStep1 lock");
495 
496     RefPtr<Runnable> runnable =
497         WrapRunnable(RefPtr<ImageBridgeChild>(this),
498                      &ImageBridgeChild::ShutdownStep1, &task);
499     GetThread()->Dispatch(runnable.forget());
500 
501     task.Wait();
502   }
503 
504   {
505     SynchronousTask task("ImageBridge ShutdownStep2 lock");
506 
507     RefPtr<Runnable> runnable =
508         WrapRunnable(RefPtr<ImageBridgeChild>(this),
509                      &ImageBridgeChild::ShutdownStep2, &task);
510     GetThread()->Dispatch(runnable.forget());
511 
512     task.Wait();
513   }
514 }
515 
InitSameProcess(uint32_t aNamespace)516 void ImageBridgeChild::InitSameProcess(uint32_t aNamespace) {
517   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
518 
519   MOZ_ASSERT(!sImageBridgeChildSingleton);
520   MOZ_ASSERT(!sImageBridgeChildThread);
521 
522   nsCOMPtr<nsIThread> thread;
523   nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
524   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
525                      "Failed to start ImageBridgeChild thread!");
526   sImageBridgeChildThread = thread.forget();
527 
528   RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
529   RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
530 
531   RefPtr<Runnable> runnable =
532       WrapRunnable(child, &ImageBridgeChild::BindSameProcess, parent);
533   child->GetThread()->Dispatch(runnable.forget());
534 
535   // Assign this after so other threads can't post messages before we connect to
536   // IPDL.
537   {
538     StaticMutexAutoLock lock(sImageBridgeSingletonLock);
539     sImageBridgeChildSingleton = child;
540   }
541 }
542 
543 /* static */
InitWithGPUProcess(Endpoint<PImageBridgeChild> && aEndpoint,uint32_t aNamespace)544 void ImageBridgeChild::InitWithGPUProcess(
545     Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace) {
546   MOZ_ASSERT(NS_IsMainThread());
547   MOZ_ASSERT(!sImageBridgeChildSingleton);
548   MOZ_ASSERT(!sImageBridgeChildThread);
549 
550   nsCOMPtr<nsIThread> thread;
551   nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
552   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
553                      "Failed to start ImageBridgeChild thread!");
554   sImageBridgeChildThread = thread.forget();
555 
556   RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
557 
558   child->GetThread()->Dispatch(NS_NewRunnableFunction(
559       "layers::ImageBridgeChild::Bind",
560       [child, endpoint = std::move(aEndpoint)]() mutable {
561         child->Bind(std::move(endpoint));
562       }));
563 
564   // Assign this after so other threads can't post messages before we connect to
565   // IPDL.
566   {
567     StaticMutexAutoLock lock(sImageBridgeSingletonLock);
568     sImageBridgeChildSingleton = child;
569   }
570 }
571 
InImageBridgeChildThread()572 bool InImageBridgeChildThread() {
573   return sImageBridgeChildThread &&
574          sImageBridgeChildThread->IsOnCurrentThread();
575 }
576 
GetThread() const577 nsISerialEventTarget* ImageBridgeChild::GetThread() const {
578   return sImageBridgeChildThread;
579 }
580 
581 /* static */
IdentifyCompositorTextureHost(const TextureFactoryIdentifier & aIdentifier)582 void ImageBridgeChild::IdentifyCompositorTextureHost(
583     const TextureFactoryIdentifier& aIdentifier) {
584   if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
585     child->UpdateTextureFactoryIdentifier(aIdentifier);
586   }
587 }
588 
UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier & aIdentifier)589 void ImageBridgeChild::UpdateTextureFactoryIdentifier(
590     const TextureFactoryIdentifier& aIdentifier) {
591   IdentifyTextureHost(aIdentifier);
592 }
593 
CreateImageClient(CompositableType aType,ImageContainer * aImageContainer)594 RefPtr<ImageClient> ImageBridgeChild::CreateImageClient(
595     CompositableType aType, ImageContainer* aImageContainer) {
596   if (InImageBridgeChildThread()) {
597     return CreateImageClientNow(aType, aImageContainer);
598   }
599 
600   SynchronousTask task("CreateImageClient Lock");
601 
602   RefPtr<ImageClient> result = nullptr;
603 
604   RefPtr<Runnable> runnable = WrapRunnable(
605       RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateImageClientSync,
606       &task, &result, aType, aImageContainer);
607   GetThread()->Dispatch(runnable.forget());
608 
609   task.Wait();
610 
611   return result;
612 }
613 
CreateImageClientNow(CompositableType aType,ImageContainer * aImageContainer)614 RefPtr<ImageClient> ImageBridgeChild::CreateImageClientNow(
615     CompositableType aType, ImageContainer* aImageContainer) {
616   MOZ_ASSERT(InImageBridgeChildThread());
617   if (!CanSend()) {
618     return nullptr;
619   }
620 
621   RefPtr<ImageClient> client =
622       ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
623   MOZ_ASSERT(client, "failed to create ImageClient");
624   if (client) {
625     client->Connect(aImageContainer);
626   }
627   return client;
628 }
629 
AllocUnsafeShmem(size_t aSize,ipc::SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem)630 bool ImageBridgeChild::AllocUnsafeShmem(
631     size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
632     ipc::Shmem* aShmem) {
633   if (!InImageBridgeChildThread()) {
634     return DispatchAllocShmemInternal(aSize, aType, aShmem,
635                                       true);  // true: unsafe
636   }
637 
638   if (!CanSend()) {
639     return false;
640   }
641   return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
642 }
643 
AllocShmem(size_t aSize,ipc::SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem)644 bool ImageBridgeChild::AllocShmem(size_t aSize,
645                                   ipc::SharedMemory::SharedMemoryType aType,
646                                   ipc::Shmem* aShmem) {
647   if (!InImageBridgeChildThread()) {
648     return DispatchAllocShmemInternal(aSize, aType, aShmem,
649                                       false);  // false: unsafe
650   }
651 
652   if (!CanSend()) {
653     return false;
654   }
655   return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
656 }
657 
ProxyAllocShmemNow(SynchronousTask * aTask,size_t aSize,SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem,bool aUnsafe,bool * aSuccess)658 void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
659                                           SharedMemory::SharedMemoryType aType,
660                                           ipc::Shmem* aShmem, bool aUnsafe,
661                                           bool* aSuccess) {
662   AutoCompleteTask complete(aTask);
663 
664   if (!CanSend()) {
665     return;
666   }
667 
668   bool ok = false;
669   if (aUnsafe) {
670     ok = AllocUnsafeShmem(aSize, aType, aShmem);
671   } else {
672     ok = AllocShmem(aSize, aType, aShmem);
673   }
674   *aSuccess = ok;
675 }
676 
DispatchAllocShmemInternal(size_t aSize,SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem,bool aUnsafe)677 bool ImageBridgeChild::DispatchAllocShmemInternal(
678     size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem,
679     bool aUnsafe) {
680   SynchronousTask task("AllocatorProxy alloc");
681 
682   bool success = false;
683   RefPtr<Runnable> runnable = WrapRunnable(
684       RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow,
685       &task, aSize, aType, aShmem, aUnsafe, &success);
686   GetThread()->Dispatch(runnable.forget());
687 
688   task.Wait();
689 
690   return success;
691 }
692 
ProxyDeallocShmemNow(SynchronousTask * aTask,ipc::Shmem * aShmem,bool * aResult)693 void ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
694                                             ipc::Shmem* aShmem, bool* aResult) {
695   AutoCompleteTask complete(aTask);
696 
697   if (!CanSend()) {
698     return;
699   }
700   *aResult = DeallocShmem(*aShmem);
701 }
702 
DeallocShmem(ipc::Shmem & aShmem)703 bool ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
704   if (InImageBridgeChildThread()) {
705     if (!CanSend()) {
706       return false;
707     }
708     return PImageBridgeChild::DeallocShmem(aShmem);
709   }
710 
711   // If we can't post a task, then we definitely cannot send, so there's
712   // no reason to queue up this send.
713   if (!CanPostTask()) {
714     return false;
715   }
716 
717   SynchronousTask task("AllocatorProxy Dealloc");
718   bool result = false;
719 
720   RefPtr<Runnable> runnable = WrapRunnable(
721       RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyDeallocShmemNow,
722       &task, &aShmem, &result);
723   GetThread()->Dispatch(runnable.forget());
724 
725   task.Wait();
726   return result;
727 }
728 
AllocPTextureChild(const SurfaceDescriptor &,ReadLockDescriptor &,const LayersBackend &,const TextureFlags &,const uint64_t & aSerial,const wr::MaybeExternalImageId & aExternalImageId)729 PTextureChild* ImageBridgeChild::AllocPTextureChild(
730     const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&,
731     const TextureFlags&, const uint64_t& aSerial,
732     const wr::MaybeExternalImageId& aExternalImageId) {
733   MOZ_ASSERT(CanSend());
734   return TextureClient::CreateIPDLActor();
735 }
736 
DeallocPTextureChild(PTextureChild * actor)737 bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
738   return TextureClient::DestroyIPDLActor(actor);
739 }
740 
741 PMediaSystemResourceManagerChild*
AllocPMediaSystemResourceManagerChild()742 ImageBridgeChild::AllocPMediaSystemResourceManagerChild() {
743   MOZ_ASSERT(CanSend());
744   return new mozilla::media::MediaSystemResourceManagerChild();
745 }
746 
DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild * aActor)747 bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(
748     PMediaSystemResourceManagerChild* aActor) {
749   MOZ_ASSERT(aActor);
750   delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
751   return true;
752 }
753 
RecvParentAsyncMessages(nsTArray<AsyncParentMessageData> && aMessages)754 mozilla::ipc::IPCResult ImageBridgeChild::RecvParentAsyncMessages(
755     nsTArray<AsyncParentMessageData>&& aMessages) {
756   for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
757     const AsyncParentMessageData& message = aMessages[i];
758 
759     switch (message.type()) {
760       case AsyncParentMessageData::TOpNotifyNotUsed: {
761         const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
762         NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
763         break;
764       }
765       case AsyncParentMessageData::TOpDeliverReleaseFence: {
766 #ifdef MOZ_WIDGET_ANDROID
767         const OpDeliverReleaseFence& op = message.get_OpDeliverReleaseFence();
768         ipc::FileDescriptor fenceFd;
769         if (op.fenceFd().isSome()) {
770           fenceFd = *op.fenceFd();
771         }
772         AndroidHardwareBufferManager::Get()->NotifyNotUsed(
773             std::move(fenceFd), op.bufferId(), op.fwdTransactionId(),
774             op.usesImageBridge());
775 #else
776         MOZ_ASSERT_UNREACHABLE("unexpected to be called");
777 #endif
778         break;
779       }
780       default:
781         NS_ERROR("unknown AsyncParentMessageData type");
782         return IPC_FAIL_NO_REASON(this);
783     }
784   }
785   return IPC_OK();
786 }
787 
FindListener(const CompositableHandle & aHandle)788 RefPtr<ImageContainerListener> ImageBridgeChild::FindListener(
789     const CompositableHandle& aHandle) {
790   RefPtr<ImageContainerListener> listener;
791   MutexAutoLock lock(mContainerMapLock);
792   auto it = mImageContainerListeners.find(aHandle.Value());
793   if (it != mImageContainerListeners.end()) {
794     listener = it->second;
795   }
796   return listener;
797 }
798 
RecvDidComposite(nsTArray<ImageCompositeNotification> && aNotifications)799 mozilla::ipc::IPCResult ImageBridgeChild::RecvDidComposite(
800     nsTArray<ImageCompositeNotification>&& aNotifications) {
801   for (auto& n : aNotifications) {
802     RefPtr<ImageContainerListener> listener = FindListener(n.compositable());
803     if (listener) {
804       listener->NotifyComposite(n);
805     }
806   }
807   return IPC_OK();
808 }
809 
RecvReportFramesDropped(const CompositableHandle & aHandle,const uint32_t & aFrames)810 mozilla::ipc::IPCResult ImageBridgeChild::RecvReportFramesDropped(
811     const CompositableHandle& aHandle, const uint32_t& aFrames) {
812   RefPtr<ImageContainerListener> listener = FindListener(aHandle);
813   if (listener) {
814     listener->NotifyDropped(aFrames);
815   }
816 
817   return IPC_OK();
818 }
819 
CreateTexture(const SurfaceDescriptor & aSharedData,ReadLockDescriptor && aReadLock,LayersBackend aLayersBackend,TextureFlags aFlags,uint64_t aSerial,wr::MaybeExternalImageId & aExternalImageId)820 PTextureChild* ImageBridgeChild::CreateTexture(
821     const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
822     LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
823     wr::MaybeExternalImageId& aExternalImageId) {
824   MOZ_ASSERT(CanSend());
825   return SendPTextureConstructor(aSharedData, std::move(aReadLock),
826                                  aLayersBackend, aFlags, aSerial,
827                                  aExternalImageId);
828 }
829 
IBCAddOpDestroy(CompositableTransaction * aTxn,const OpDestroy & op)830 static bool IBCAddOpDestroy(CompositableTransaction* aTxn,
831                             const OpDestroy& op) {
832   if (aTxn->Finished()) {
833     return false;
834   }
835 
836   aTxn->mDestroyedActors.AppendElement(op);
837   return true;
838 }
839 
DestroyInTransaction(PTextureChild * aTexture)840 bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture) {
841   return IBCAddOpDestroy(mTxn, OpDestroy(aTexture));
842 }
843 
DestroyInTransaction(const CompositableHandle & aHandle)844 bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) {
845   return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
846 }
847 
RemoveTextureFromCompositable(CompositableClient * aCompositable,TextureClient * aTexture)848 void ImageBridgeChild::RemoveTextureFromCompositable(
849     CompositableClient* aCompositable, TextureClient* aTexture) {
850   MOZ_ASSERT(CanSend());
851   MOZ_ASSERT(aTexture);
852   MOZ_ASSERT(aTexture->IsSharedWithCompositor());
853   MOZ_ASSERT(aCompositable->IsConnected());
854   if (!aTexture || !aTexture->IsSharedWithCompositor() ||
855       !aCompositable->IsConnected()) {
856     return;
857   }
858 
859   mTxn->AddNoSwapEdit(CompositableOperation(
860       aCompositable->GetIPCHandle(),
861       OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
862 }
863 
IsSameProcess() const864 bool ImageBridgeChild::IsSameProcess() const {
865   return OtherPid() == base::GetCurrentProcId();
866 }
867 
CanPostTask() const868 bool ImageBridgeChild::CanPostTask() const {
869   // During shutdown, the cycle collector may free objects that are holding a
870   // reference to ImageBridgeChild. Since this happens on the main thread,
871   // ImageBridgeChild will attempt to post a task to the ImageBridge thread.
872   // However the thread manager has already been shut down, so the task cannot
873   // post.
874   //
875   // It's okay if this races. We only care about the shutdown case where
876   // everything's happening on the main thread. Even if it races outside of
877   // shutdown, it's still harmless to post the task, since the task must
878   // check CanSend().
879   return !mDestroyed;
880 }
881 
ReleaseCompositable(const CompositableHandle & aHandle)882 void ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle) {
883   if (!InImageBridgeChildThread()) {
884     // If we can't post a task, then we definitely cannot send, so there's
885     // no reason to queue up this send.
886     if (!CanPostTask()) {
887       return;
888     }
889 
890     RefPtr<Runnable> runnable =
891         WrapRunnable(RefPtr<ImageBridgeChild>(this),
892                      &ImageBridgeChild::ReleaseCompositable, aHandle);
893     GetThread()->Dispatch(runnable.forget());
894     return;
895   }
896 
897   if (!CanSend()) {
898     return;
899   }
900 
901   if (!DestroyInTransaction(aHandle)) {
902     SendReleaseCompositable(aHandle);
903   }
904 
905   {
906     MutexAutoLock lock(mContainerMapLock);
907     mImageContainerListeners.erase(aHandle.Value());
908   }
909 }
910 
CanSend() const911 bool ImageBridgeChild::CanSend() const {
912   MOZ_ASSERT(InImageBridgeChildThread());
913   return mCanSend;
914 }
915 
HandleFatalError(const char * aMsg) const916 void ImageBridgeChild::HandleFatalError(const char* aMsg) const {
917   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
918 }
919 
GetNextExternalImageId()920 wr::MaybeExternalImageId ImageBridgeChild::GetNextExternalImageId() {
921   static uint32_t sNextID = 1;
922   ++sNextID;
923   MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
924 
925   uint64_t imageId = mNamespace;
926   imageId = imageId << 32 | sNextID;
927   return Some(wr::ToExternalImageId(imageId));
928 }
929 
930 }  // namespace layers
931 }  // namespace mozilla
932