Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ImageContainer.h"
8 #include <string.h> // for memcpy, memset
9 #include "SharedTextureImage.h" // for SharedTextureImage
10 #include "gfx2DGlue.h"
11 #include "gfxPlatform.h" // for gfxPlatform
12 #include "gfxUtils.h" // for gfxUtils
13 #include "mozilla/RefPtr.h" // for TemporaryRef
14 #include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
15 #include "mozilla/layers/CompositorTypes.h"
16 #include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
17 #include "mozilla/layers/ImageClient.h" // for ImageClient
18 #include "nsISupportsUtils.h" // for NS_IF_ADDREF
19 #include "YCbCrUtils.h" // for YCbCr conversions
20 #ifdef MOZ_WIDGET_GONK
21 #include "GrallocImages.h"
22 #endif
23 #include "gfx2DGlue.h"
24 #include "mozilla/gfx/2D.h"
26 #ifdef XP_MACOSX
27 #include "mozilla/gfx/QuartzSupport.h"
28 #include "MacIOSurfaceImage.h"
29 #endif
31 #ifdef XP_WIN
32 #include "gfxD2DSurface.h"
33 #include "gfxWindowsPlatform.h"
34 #include <d3d10_1.h>
35 #include "d3d10/ImageLayerD3D10.h"
36 #include "D3D9SurfaceImage.h"
37 #endif
39 using namespace mozilla::ipc;
40 using namespace android;
41 using namespace mozilla::gfx;
44 namespace mozilla {
45 namespace layers {
48 Atomic<int32_t> Image::sSerialCounter(0);
50 already_AddRefed<Image>
51 ImageFactory::CreateImage(ImageFormat aFormat,
52 const gfx::IntSize &,
53 BufferRecycleBin *aRecycleBin)
54 {
55 nsRefPtr<Image> img;
56 #ifdef MOZ_WIDGET_GONK
57 if (aFormat == ImageFormat::GRALLOC_PLANAR_YCBCR) {
58 img = new GrallocImage();
59 return img.forget();
60 }
61 #endif
62 if (aFormat == ImageFormat::PLANAR_YCBCR) {
63 img = new PlanarYCbCrImage(aRecycleBin);
64 return img.forget();
65 }
66 if (aFormat == ImageFormat::CAIRO_SURFACE) {
67 img = new CairoImage();
68 return img.forget();
69 }
70 if (aFormat == ImageFormat::SHARED_TEXTURE) {
71 img = new SharedTextureImage();
72 return img.forget();
73 }
74 #ifdef XP_MACOSX
75 if (aFormat == ImageFormat::MAC_IOSURFACE) {
76 img = new MacIOSurfaceImage();
77 return img.forget();
78 }
79 #endif
80 #ifdef XP_WIN
81 if (aFormat == ImageFormat::D3D9_RGB32_TEXTURE) {
82 img = new D3D9SurfaceImage();
83 return img.forget();
84 }
85 #endif
86 return nullptr;
87 }
89 BufferRecycleBin::BufferRecycleBin()
90 : mLock("mozilla.layers.BufferRecycleBin.mLock")
91 {
92 }
94 void
95 BufferRecycleBin::RecycleBuffer(uint8_t* aBuffer, uint32_t aSize)
96 {
97 MutexAutoLock lock(mLock);
99 if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
100 mRecycledBuffers.Clear();
101 }
102 mRecycledBufferSize = aSize;
103 mRecycledBuffers.AppendElement(aBuffer);
104 }
106 uint8_t*
107 BufferRecycleBin::GetBuffer(uint32_t aSize)
108 {
109 MutexAutoLock lock(mLock);
111 if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
112 return new uint8_t[aSize];
114 uint32_t last = mRecycledBuffers.Length() - 1;
115 uint8_t* result = mRecycledBuffers[last].forget();
116 mRecycledBuffers.RemoveElementAt(last);
117 return result;
118 }
120 ImageContainer::ImageContainer(int flag)
121 : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
122 mPaintCount(0),
123 mPreviousImagePainted(false),
124 mImageFactory(new ImageFactory()),
125 mRecycleBin(new BufferRecycleBin()),
126 mRemoteData(nullptr),
127 mRemoteDataMutex(nullptr),
128 mCompositionNotifySink(nullptr),
129 mImageClient(nullptr)
130 {
131 if (flag == ENABLE_ASYNC && ImageBridgeChild::IsCreated()) {
132 // the refcount of this ImageClient is 1. we don't use a RefPtr here because the refcount
133 // of this class must be done on the ImageBridge thread.
134 mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(BUFFER_IMAGE_SINGLE).drop();
135 MOZ_ASSERT(mImageClient);
136 }
137 }
139 ImageContainer::~ImageContainer()
140 {
141 if (IsAsync()) {
142 ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
143 }
144 }
146 already_AddRefed<Image>
147 ImageContainer::CreateImage(ImageFormat aFormat)
148 {
149 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
151 if (mImageClient) {
152 nsRefPtr<Image> img = mImageClient->CreateImage(aFormat);
153 if (img) {
154 return img.forget();
155 }
156 }
157 return mImageFactory->CreateImage(aFormat, mScaleHint, mRecycleBin);
158 }
160 void
161 ImageContainer::SetCurrentImageInternal(Image *aImage)
162 {
163 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
165 if (mRemoteData) {
166 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
167 mRemoteDataMutex->Lock();
168 // This is important since it ensures we won't change the active image
169 // when we currently have a locked image that depends on mRemoteData.
170 }
172 mActiveImage = aImage;
173 CurrentImageChanged();
175 if (mRemoteData) {
176 mRemoteDataMutex->Unlock();
177 }
178 }
180 void
181 ImageContainer::ClearCurrentImage()
182 {
183 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
184 SetCurrentImageInternal(nullptr);
185 }
187 void
188 ImageContainer::SetCurrentImage(Image *aImage)
189 {
190 if (!aImage) {
191 ClearAllImages();
192 return;
193 }
195 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
196 if (IsAsync()) {
197 ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
198 }
199 SetCurrentImageInternal(aImage);
200 }
202 void
203 ImageContainer::ClearAllImages()
204 {
205 if (IsAsync()) {
206 // Let ImageClient release all TextureClients.
207 ImageBridgeChild::FlushAllImages(mImageClient, this, false);
208 return;
209 }
211 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
212 SetCurrentImageInternal(nullptr);
213 }
215 void
216 ImageContainer::ClearAllImagesExceptFront()
217 {
218 if (IsAsync()) {
219 // Let ImageClient release all TextureClients except front one.
220 ImageBridgeChild::FlushAllImages(mImageClient, this, true);
221 }
222 }
224 void
225 ImageContainer::SetCurrentImageInTransaction(Image *aImage)
226 {
227 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
228 NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
230 SetCurrentImageInternal(aImage);
231 }
233 bool ImageContainer::IsAsync() const {
234 return mImageClient != nullptr;
235 }
237 uint64_t ImageContainer::GetAsyncContainerID() const
238 {
239 NS_ASSERTION(IsAsync(),"Shared image ID is only relevant to async ImageContainers");
240 if (IsAsync()) {
241 return mImageClient->GetAsyncID();
242 } else {
243 return 0; // zero is always an invalid AsyncID
244 }
245 }
247 bool
248 ImageContainer::HasCurrentImage()
249 {
250 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
252 if (mRemoteData) {
253 CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
255 EnsureActiveImage();
257 return !!mActiveImage.get();
258 }
260 return !!mActiveImage.get();
261 }
263 already_AddRefed<Image>
264 ImageContainer::LockCurrentImage()
265 {
266 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
268 if (mRemoteData) {
269 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
270 mRemoteDataMutex->Lock();
271 }
273 EnsureActiveImage();
275 nsRefPtr<Image> retval = mActiveImage;
276 return retval.forget();
277 }
279 TemporaryRef<gfx::SourceSurface>
280 ImageContainer::LockCurrentAsSourceSurface(gfx::IntSize *aSize, Image** aCurrentImage)
281 {
282 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
284 if (mRemoteData) {
285 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
286 mRemoteDataMutex->Lock();
288 EnsureActiveImage();
290 if (aCurrentImage) {
291 NS_IF_ADDREF(mActiveImage);
292 *aCurrentImage = mActiveImage.get();
293 }
295 if (!mActiveImage) {
296 return nullptr;
297 }
299 if (mActiveImage->GetFormat() == ImageFormat::REMOTE_IMAGE_BITMAP) {
300 gfxImageFormat fmt = mRemoteData->mFormat == RemoteImageData::BGRX32
301 ? gfxImageFormat::ARGB32
302 : gfxImageFormat::RGB24;
304 RefPtr<gfx::DataSourceSurface> newSurf
305 = gfx::Factory::CreateWrappingDataSourceSurface(mRemoteData->mBitmap.mData,
306 mRemoteData->mBitmap.mStride,
307 mRemoteData->mSize,
308 gfx::ImageFormatToSurfaceFormat(fmt));
309 *aSize = newSurf->GetSize();
311 return newSurf;
312 }
314 *aSize = mActiveImage->GetSize();
315 return mActiveImage->GetAsSourceSurface();
316 }
318 if (aCurrentImage) {
319 NS_IF_ADDREF(mActiveImage);
320 *aCurrentImage = mActiveImage.get();
321 }
323 if (!mActiveImage) {
324 return nullptr;
325 }
327 *aSize = mActiveImage->GetSize();
328 return mActiveImage->GetAsSourceSurface();
329 }
331 void
332 ImageContainer::UnlockCurrentImage()
333 {
334 if (mRemoteData) {
335 NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
336 mRemoteDataMutex->Unlock();
337 }
338 }
340 TemporaryRef<gfx::SourceSurface>
341 ImageContainer::GetCurrentAsSourceSurface(gfx::IntSize *aSize)
342 {
343 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
345 if (mRemoteData) {
346 CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
347 EnsureActiveImage();
349 if (!mActiveImage)
350 return nullptr;
351 *aSize = mRemoteData->mSize;
352 } else {
353 if (!mActiveImage)
354 return nullptr;
355 *aSize = mActiveImage->GetSize();
356 }
357 return mActiveImage->GetAsSourceSurface();
358 }
360 gfx::IntSize
361 ImageContainer::GetCurrentSize()
362 {
363 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
365 if (mRemoteData) {
366 CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
368 // We don't need to ensure we have an active image here, as we need to
369 // be in the mutex anyway, and this is easiest to return from there.
370 return mRemoteData->mSize;
371 }
373 if (!mActiveImage) {
374 return gfx::IntSize(0, 0);
375 }
377 return mActiveImage->GetSize();
378 }
380 void
381 ImageContainer::SetRemoteImageData(RemoteImageData *aData, CrossProcessMutex *aMutex)
382 {
383 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
385 NS_ASSERTION(!mActiveImage || !aData, "No active image expected when SetRemoteImageData is called with non-NULL aData.");
386 NS_ASSERTION(!mRemoteData || !aData, "No remote data expected when SetRemoteImageData is called with non-NULL aData.");
388 mRemoteData = aData;
390 if (aData) {
391 memset(aData, 0, sizeof(RemoteImageData));
392 } else {
393 mActiveImage = nullptr;
394 }
396 mRemoteDataMutex = aMutex;
397 }
399 void
400 ImageContainer::EnsureActiveImage()
401 {
402 if (mRemoteData) {
403 if (mRemoteData->mWasUpdated) {
404 mActiveImage = nullptr;
405 }
407 if (mRemoteData->mType == RemoteImageData::RAW_BITMAP &&
408 mRemoteData->mBitmap.mData && !mActiveImage) {
409 nsRefPtr<RemoteBitmapImage> newImg = new RemoteBitmapImage();
411 newImg->mFormat = mRemoteData->mFormat;
412 newImg->mData = mRemoteData->mBitmap.mData;
413 newImg->mSize = mRemoteData->mSize;
414 newImg->mStride = mRemoteData->mBitmap.mStride;
415 mRemoteData->mWasUpdated = false;
417 mActiveImage = newImg;
418 }
419 #ifdef XP_WIN
420 else if (mRemoteData->mType == RemoteImageData::DXGI_TEXTURE_HANDLE &&
421 mRemoteData->mTextureHandle && !mActiveImage) {
422 nsRefPtr<RemoteDXGITextureImage> newImg = new RemoteDXGITextureImage();
423 newImg->mSize = mRemoteData->mSize;
424 newImg->mHandle = mRemoteData->mTextureHandle;
425 newImg->mFormat = mRemoteData->mFormat;
426 mRemoteData->mWasUpdated = false;
428 mActiveImage = newImg;
429 }
430 #endif
431 }
432 }
435 PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin)
436 : Image(nullptr, ImageFormat::PLANAR_YCBCR)
437 , mBufferSize(0)
438 , mOffscreenFormat(gfxImageFormat::Unknown)
439 , mRecycleBin(aRecycleBin)
440 {
441 }
443 PlanarYCbCrImage::~PlanarYCbCrImage()
444 {
445 if (mBuffer) {
446 mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
447 }
448 }
450 size_t
451 PlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
452 {
453 // Ignoring:
454 // - mData - just wraps mBuffer
455 // - Surfaces should be reported under gfx-surfaces-*:
456 // - mSourceSurface
457 // - Base class:
458 // - mImplData is not used
459 // Not owned:
460 // - mRecycleBin
461 size_t size = mBuffer.SizeOfExcludingThis(aMallocSizeOf);
463 // Could add in the future:
464 // - mBackendData (from base class)
466 return size;
467 }
469 uint8_t*
470 PlanarYCbCrImage::AllocateBuffer(uint32_t aSize)
471 {
472 return mRecycleBin->GetBuffer(aSize);
473 }
475 static void
476 CopyPlane(uint8_t *aDst, const uint8_t *aSrc,
477 const gfx::IntSize &aSize, int32_t aStride, int32_t aSkip)
478 {
479 if (!aSkip) {
480 // Fast path: planar input.
481 memcpy(aDst, aSrc, aSize.height * aStride);
482 } else {
483 int32_t height = aSize.height;
484 int32_t width = aSize.width;
485 for (int y = 0; y < height; ++y) {
486 const uint8_t *src = aSrc;
487 uint8_t *dst = aDst;
488 // Slow path
489 for (int x = 0; x < width; ++x) {
490 *dst++ = *src++;
491 src += aSkip;
492 }
493 aSrc += aStride;
494 aDst += aStride;
495 }
496 }
497 }
499 void
500 PlanarYCbCrImage::CopyData(const Data& aData)
501 {
502 mData = aData;
504 // update buffer size
505 size_t size = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
506 mData.mYStride * mData.mYSize.height;
508 // get new buffer
509 mBuffer = AllocateBuffer(size);
510 if (!mBuffer)
511 return;
513 // update buffer size
514 mBufferSize = size;
516 mData.mYChannel = mBuffer;
517 mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
518 mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
520 CopyPlane(mData.mYChannel, aData.mYChannel,
521 mData.mYSize, mData.mYStride, mData.mYSkip);
522 CopyPlane(mData.mCbChannel, aData.mCbChannel,
523 mData.mCbCrSize, mData.mCbCrStride, mData.mCbSkip);
524 CopyPlane(mData.mCrChannel, aData.mCrChannel,
525 mData.mCbCrSize, mData.mCbCrStride, mData.mCrSkip);
527 mSize = aData.mPicSize;
528 }
530 void
531 PlanarYCbCrImage::SetData(const Data &aData)
532 {
533 CopyData(aData);
534 }
536 gfxImageFormat
537 PlanarYCbCrImage::GetOffscreenFormat()
538 {
539 return mOffscreenFormat == gfxImageFormat::Unknown ?
540 gfxPlatform::GetPlatform()->GetOffscreenFormat() :
541 mOffscreenFormat;
542 }
544 void
545 PlanarYCbCrImage::SetDataNoCopy(const Data &aData)
546 {
547 mData = aData;
548 mSize = aData.mPicSize;
549 }
551 uint8_t*
552 PlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
553 {
554 // get new buffer
555 mBuffer = AllocateBuffer(aSize);
556 if (mBuffer) {
557 // update buffer size
558 mBufferSize = aSize;
559 }
560 return mBuffer;
561 }
563 TemporaryRef<gfx::SourceSurface>
564 PlanarYCbCrImage::GetAsSourceSurface()
565 {
566 if (mSourceSurface) {
567 return mSourceSurface.get();
568 }
570 gfx::IntSize size(mSize);
571 gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
572 gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
573 if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
574 mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
575 NS_ERROR("Illegal image dest width or height");
576 return nullptr;
577 }
579 RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
581 gfx::ConvertYCbCrToRGB(mData, format, size, surface->GetData(), surface->Stride());
583 mSourceSurface = surface;
585 return surface.forget();
586 }
588 TemporaryRef<gfx::SourceSurface>
589 RemoteBitmapImage::GetAsSourceSurface()
590 {
591 gfx::SurfaceFormat fmt = mFormat == RemoteImageData::BGRX32
592 ? gfx::SurfaceFormat::B8G8R8X8
593 : gfx::SurfaceFormat::B8G8R8A8;
594 RefPtr<gfx::DataSourceSurface> newSurf = gfx::Factory::CreateDataSourceSurface(mSize, fmt);
596 for (int y = 0; y < mSize.height; y++) {
597 memcpy(newSurf->GetData() + newSurf->Stride() * y,
598 mData + mStride * y,
599 mSize.width * 4);
600 }
602 return newSurf;
603 }
605 CairoImage::CairoImage()
606 : Image(nullptr, ImageFormat::CAIRO_SURFACE)
607 {}
609 CairoImage::~CairoImage()
610 {
611 }
613 TextureClient*
614 CairoImage::GetTextureClient(CompositableClient *aClient)
615 {
616 CompositableForwarder* forwarder = aClient->GetForwarder();
617 RefPtr<TextureClient> textureClient = mTextureClients.Get(forwarder->GetSerial());
618 if (textureClient) {
619 return textureClient;
620 }
622 RefPtr<SourceSurface> surface = GetAsSourceSurface();
623 MOZ_ASSERT(surface);
625 // gfx::BackendType::NONE means default to content backend
626 textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(),
627 TEXTURE_FLAGS_DEFAULT,
628 gfx::BackendType::NONE,
629 surface->GetSize());
630 MOZ_ASSERT(textureClient->CanExposeDrawTarget());
631 if (!textureClient->AllocateForSurface(surface->GetSize()) ||
632 !textureClient->Lock(OPEN_WRITE_ONLY)) {
633 return nullptr;
634 }
636 {
637 // We must not keep a reference to the DrawTarget after it has been unlocked.
638 RefPtr<DrawTarget> dt = textureClient->GetAsDrawTarget();
639 dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
640 }
642 textureClient->Unlock();
644 mTextureClients.Put(forwarder->GetSerial(), textureClient);
645 return textureClient;
646 }
648 } // namespace
649 } // namespace