TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) { // Create the layer tree. const char* layerTreeSyntax = "c(tt)"; // LayerID 0 12 nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0,0,200,200)), nsIntRegion(IntRect(0,0,200,200)), nsIntRegion(IntRect(0,0,200,100)) }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); // Add root scroll metadata to the first painted layer. SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200)); // Add root and subframe scroll metadata to the second painted layer. // Give the subframe metadata a scroll clip corresponding to the subframe's // composition bounds. // Importantly, give the layer a layer clip which leaks outside of the // subframe's composition bounds. ScrollMetadata rootMetadata = BuildScrollMetadata( FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200), ParentLayerRect(0,0,200,200)); ScrollMetadata subframeMetadata = BuildScrollMetadata( FrameMetrics::START_SCROLL_ID + 1, CSSRect(0,0,200,200), ParentLayerRect(0,0,200,100)); subframeMetadata.SetScrollClip(Some(LayerClip(ParentLayerIntRect(0,0,200,100)))); layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata}); layers[2]->SetClipRect(Some(ParentLayerIntRect(0,0,200,200))); SetEventRegionsBasedOnBottommostMetrics(layers[2]); // Build the hit testing tree. ScopedLayerTreeRegistration registration(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); // Pan on a region that's inside layers[2]'s layer clip, but outside // its subframe metadata's scroll clip. Pan(manager, 120, 110); // Test that the subframe hasn't scrolled. EXPECT_EQ(CSSPoint(0,0), ApzcOf(layers[2], 0)->GetFrameMetrics().GetScrollOffset()); }
void LayerManagerComposite::Render() { PROFILER_LABEL("LayerManagerComposite", "Render", js::ProfileEntry::Category::GRAPHICS); if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } // At this time, it doesn't really matter if these preferences change // during the execution of the function; we should be safe in all // permutations. However, may as well just get the values onces and // then use them, just in case the consistency becomes important in // the future. bool invertVal = gfxPrefs::LayersEffectInvert(); bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); float contrastVal = gfxPrefs::LayersEffectContrast(); bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // Dump to console if (gfxPrefs::LayersDump()) { this->Dump(); } else if (profiler_feature_active("layersdump")) { std::stringstream ss; Dump(ss); profiler_log(ss.str().c_str()); } // Dump to LayerScope Viewer if (LayerScope::CheckSendable()) { // Create a LayersPacket, dump Layers into it and transfer the // packet('s ownership) to LayerScope. auto packet = MakeUnique<layerscope::Packet>(); layerscope::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(Move(packet)); } /** Our more efficient but less powerful alter ego, if one is available. */ nsRefPtr<Composer2D> composer2D; composer2D = mCompositor->GetWidget()->GetComposer2D(); // We can't use composert2D if we have layer effects if (!mTarget && !haveLayerEffects && gfxPrefs::Composer2DCompositionEnabled() && composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot, mCompositor->GetWidget(), mGeometryChanged)) { LayerScope::SetHWComposed(); if (mFPS) { double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now()); if (gfxPrefs::LayersDrawFPS()) { printf_stderr("HWComposer: FPS is %g\n", fps); } } mCompositor->EndFrameForExternalComposition(Matrix()); // Reset the invalid region as compositing is done mInvalidRegion.SetEmpty(); mLastFrameMissedHWC = false; return; } else if (!mTarget && !haveLayerEffects) { mLastFrameMissedHWC = !!composer2D; } { PROFILER_LABEL("LayerManagerComposite", "PreRender", js::ProfileEntry::Category::GRAPHICS); if (!mCompositor->GetWidget()->PreRender(this)) { return; } } nsIntRegion invalid; if (mTarget) { invalid = mTargetBounds; } else { invalid = mInvalidRegion; // Reset the invalid region now that we've begun compositing. mInvalidRegion.SetEmpty(); } ParentLayerIntRect clipRect; Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); Rect actualBounds; CompositorBench(mCompositor, bounds); if (mRoot->GetClipRect()) { clipRect = *mRoot->GetClipRect(); Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); mCompositor->BeginFrame(invalid, &rect, bounds, nullptr, &actualBounds); } else { gfx::Rect rect; mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds); clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height); } if (actualBounds.IsEmpty()) { mCompositor->GetWidget()->PostRender(this); return; } // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay(this, IntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height)); RefPtr<CompositingRenderTarget> previousTarget; if (haveLayerEffects) { previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Render our layers. RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot)); RootLayer()->RenderLayer(ParentLayerIntRect::ToUntyped(clipRect)); if (!mRegionToClear.IsEmpty()) { nsIntRegionRectIterator iter(mRegionToClear); const IntRect *r; while ((r = iter.Next())) { mCompositor->ClearRect(Rect(r->x, r->y, r->width, r->height)); } } if (mTwoPassTmpTarget) { MOZ_ASSERT(haveLayerEffects); PopGroupForLayerEffects(previousTarget, ParentLayerIntRect::ToUntyped(clipRect), grayscaleVal, invertVal, contrastVal); } // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay(this, IntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height)); // Debugging RenderDebugOverlay(actualBounds); { PROFILER_LABEL("LayerManagerComposite", "EndFrame", js::ProfileEntry::Category::GRAPHICS); mCompositor->EndFrame(); mCompositor->SetDispAcquireFence(mRoot); // Call after EndFrame() } if (composer2D) { composer2D->Render(mCompositor->GetWidget()); } mCompositor->GetWidget()->PostRender(this); RecordFrame(); }
void LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion) { PROFILER_LABEL("LayerManagerComposite", "Render", js::ProfileEntry::Category::GRAPHICS); if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) { NS_WARNING("Call on destroyed layer manager"); return; } ClearLayerFlags(mRoot); // At this time, it doesn't really matter if these preferences change // during the execution of the function; we should be safe in all // permutations. However, may as well just get the values onces and // then use them, just in case the consistency becomes important in // the future. bool invertVal = gfxPrefs::LayersEffectInvert(); bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); float contrastVal = gfxPrefs::LayersEffectContrast(); bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // Dump to console if (gfxPrefs::LayersDump()) { this->Dump(/* aSorted= */true); } else if (profiler_feature_active("layersdump")) { std::stringstream ss; Dump(ss); profiler_log(ss.str().c_str()); } // Dump to LayerScope Viewer if (LayerScope::CheckSendable()) { // Create a LayersPacket, dump Layers into it and transfer the // packet('s ownership) to LayerScope. auto packet = MakeUnique<layerscope::Packet>(); layerscope::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(Move(packet)); } mozilla::widget::WidgetRenderingContext widgetContext; #if defined(XP_MACOSX) widgetContext.mLayerManager = this; #elif defined(MOZ_WIDGET_ANDROID) widgetContext.mCompositor = GetCompositor(); #endif { PROFILER_LABEL("LayerManagerComposite", "PreRender", js::ProfileEntry::Category::GRAPHICS); if (!mCompositor->GetWidget()->PreRender(&widgetContext)) { return; } } ParentLayerIntRect clipRect; IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); IntRect actualBounds; CompositorBench(mCompositor, bounds); MOZ_ASSERT(mRoot->GetOpacity() == 1); #if defined(MOZ_WIDGET_ANDROID) LayerMetricsWrapper wrapper = GetRootContentLayer(); if (wrapper) { mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor()); } else { mCompositor->SetClearColorToDefault(); } #endif if (mRoot->GetClipRect()) { clipRect = *mRoot->GetClipRect(); IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds); } else { gfx::IntRect rect; mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds); clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height); } if (actualBounds.IsEmpty()) { mCompositor->GetWidget()->PostRender(&widgetContext); return; } // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); RefPtr<CompositingRenderTarget> previousTarget; if (haveLayerEffects) { previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Render our layers. RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot)); RootLayer()->RenderLayer(clipRect.ToUnknownRect()); if (!mRegionToClear.IsEmpty()) { for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) { const IntRect& r = iter.Get(); mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height)); } } if (mTwoPassTmpTarget) { MOZ_ASSERT(haveLayerEffects); PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(), grayscaleVal, invertVal, contrastVal); } // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); // Debugging RenderDebugOverlay(actualBounds); { PROFILER_LABEL("LayerManagerComposite", "EndFrame", js::ProfileEntry::Category::GRAPHICS); mCompositor->EndFrame(); // Call after EndFrame() mCompositor->SetDispAcquireFence(mRoot); } mCompositor->GetWidget()->PostRender(&widgetContext); RecordFrame(); }