From ffde3e4e6178837958d3fc5dd45e1709b10eb798 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 31 May 2016 14:35:54 -0400 Subject: [PATCH] Bug 1274720 - Don't use DrawTargetCG for drawing popups. r=jrmuizel MozReview-Commit-ID: 6oFnX2Ovbl6 --HG-- extra : rebase_source : 1efbf13a21d91b7bec172eaa919956919a42c0e5 --- widget/cocoa/nsChildView.h | 5 ++ widget/cocoa/nsChildView.mm | 109 ++++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 42 deletions(-) diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index c580f91010a4..a3a51850e9d6 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -483,6 +483,8 @@ public: void WillPaintWindow(); bool PaintWindow(LayoutDeviceIntRegion aRegion); + bool PaintWindowInContext(CGContextRef aContext, const LayoutDeviceIntRegion& aRegion, + mozilla::gfx::IntSize aSurfaceSize); #ifdef ACCESSIBILITY already_AddRefed GetDocumentAccessible(); @@ -687,6 +689,9 @@ protected: RefPtr mSwipeTracker; mozilla::UniquePtr mSwipeEventQueue; + // Only used for drawRect-based painting in popups. + RefPtr mBackingSurface; + // This flag is only used when APZ is off. It indicates that the current pan // gesture was processed as a swipe. Sometimes the swipe animation can finish // before momentum events of the pan gesture have stopped firing, so this diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 6b53a4a1c688..82adfb650b08 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -97,6 +97,7 @@ #include "nsIDOMWindowUtils.h" #include "Units.h" #include "UnitTransforms.h" +#include "mozilla/UniquePtrExtensions.h" using namespace mozilla; using namespace mozilla::layers; @@ -1533,6 +1534,71 @@ bool nsChildView::PaintWindow(LayoutDeviceIntRegion aRegion) return returnValue; } +bool +nsChildView::PaintWindowInContext(CGContextRef aContext, const LayoutDeviceIntRegion& aRegion, gfx::IntSize aSurfaceSize) +{ + if (!mBackingSurface || mBackingSurface->GetSize() != aSurfaceSize) { + mBackingSurface = + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aSurfaceSize, + gfx::SurfaceFormat::B8G8R8A8); + if (!mBackingSurface) { + return false; + } + } + + RefPtr targetContext = gfxContext::ForDrawTarget(mBackingSurface); + MOZ_ASSERT(targetContext); // already checked the draw target above + + // Set up the clip region and clear existing contents in the backing surface. + targetContext->NewPath(); + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + const LayoutDeviceIntRect& r = iter.Get(); + targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); + mBackingSurface->ClearRect(gfx::Rect(r.ToUnknownRect())); + } + targetContext->Clip(); + + nsAutoRetainCocoaObject kungFuDeathGrip(mView); + bool painted = false; + if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { + nsBaseWidget::AutoLayerManagerSetup + setupLayerManager(this, targetContext, BufferMode::BUFFER_NONE); + painted = PaintWindow(aRegion); + } else if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { + // We only need this so that we actually get DidPaintWindow fired + painted = PaintWindow(aRegion); + } + + uint8_t* data; + gfx::IntSize size; + int32_t stride; + gfx::SurfaceFormat format; + + if (!mBackingSurface->LockBits(&data, &size, &stride, &format)) { + return false; + } + + // Draw the backing surface onto the window. + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, stride * size.height, NULL); + NSColorSpace* colorSpace = [[mView window] colorSpace]; + CGImageRef image = CGImageCreate(size.width, size.height, 8, 32, stride, + [colorSpace CGColorSpace], + kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst, + provider, NULL, false, kCGRenderingIntentDefault); + CGContextSaveGState(aContext); + CGContextTranslateCTM(aContext, 0, size.height); + CGContextScaleCTM(aContext, 1, -1); + CGContextSetBlendMode(aContext, kCGBlendModeCopy); + CGContextDrawImage(aContext, CGRectMake(0, 0, size.width, size.height), image); + CGImageRelease(image); + CGDataProviderRelease(provider); + CGContextRestoreGState(aContext); + + mBackingSurface->ReleaseBits(data); + + return painted; +} + #pragma mark - void nsChildView::ReportMoveEvent() @@ -3815,50 +3881,9 @@ NSEvent* gLastDragMouseDownEvent = nil; NSSize viewSize = [self bounds].size; nsIntSize backingSize(viewSize.width * scale, viewSize.height * scale); - - CGContextSaveGState(aContext); - LayoutDeviceIntRegion region = [self nativeDirtyRegionWithBoundingRect:aRect]; - // Create Cairo objects. - RefPtr targetSurface; - - RefPtr dt = - gfx::Factory::CreateDrawTargetForCairoCGContext(aContext, - gfx::IntSize(backingSize.width, - backingSize.height)); - if (!dt || !dt->IsValid()) { - // This used to be an assertion, so keep crashing in nightly+aurora - gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) << "Cannot create target with CreateDrawTargetForCairoCGContext " << backingSize; - return; - } - dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr); - RefPtr targetContext = gfxContext::ForDrawTarget(dt); - MOZ_ASSERT(targetContext); // already checked the draw target above - - // Set up the clip region. - targetContext->NewPath(); - for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) { - const LayoutDeviceIntRect& r = iter.Get(); - targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); - } - targetContext->Clip(); - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - bool painted = false; - if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { - nsBaseWidget::AutoLayerManagerSetup - setupLayerManager(mGeckoChild, targetContext, BufferMode::BUFFER_NONE); - painted = mGeckoChild->PaintWindow(region); - } else if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { - // We only need this so that we actually get DidPaintWindow fired - painted = mGeckoChild->PaintWindow(region); - } - - targetContext = nullptr; - targetSurface = nullptr; - - CGContextRestoreGState(aContext); + bool painted = mGeckoChild->PaintWindowInContext(aContext, region, backingSize); // Undo the scale transform so that from now on the context is in // CocoaPoints again.