From cf0c7ae9e3db3d7216d0540d1875cbc30292de1d Mon Sep 17 00:00:00 2001 From: Miko Mynttinen Date: Sat, 6 Feb 2021 22:30:37 +0000 Subject: [PATCH] Bug 1534549 - Part 2: Allow all display items to carry hit testing information r=mstange Differential Revision: https://phabricator.services.mozilla.com/D102515 --- gfx/layers/moz.build | 2 + gfx/layers/wr/HitTestInfoManager.cpp | 95 +++++++++++++ gfx/layers/wr/HitTestInfoManager.h | 63 +++++++++ gfx/layers/wr/WebRenderCommandBuilder.cpp | 14 +- gfx/layers/wr/WebRenderCommandBuilder.h | 2 + layout/base/nsLayoutDebugger.cpp | 8 +- layout/forms/nsButtonFrameRenderer.cpp | 8 +- layout/generic/nsGfxScrollFrame.cpp | 58 ++++---- layout/generic/nsIFrame.cpp | 70 ++++------ layout/painting/FrameLayerBuilder.cpp | 81 ++++++------ layout/painting/FrameLayerBuilder.h | 1 - layout/painting/HitTestInfo.cpp | 58 ++++++++ layout/painting/HitTestInfo.h | 53 ++++++++ layout/painting/moz.build | 2 + layout/painting/nsDisplayList.cpp | 154 +++++++--------------- layout/painting/nsDisplayList.h | 146 +++++++++----------- layout/svg/SVGGeometryFrame.cpp | 3 + layout/svg/SVGOuterSVGFrame.cpp | 2 + layout/tables/nsTableCellFrame.cpp | 8 +- 19 files changed, 515 insertions(+), 313 deletions(-) create mode 100644 gfx/layers/wr/HitTestInfoManager.cpp create mode 100644 gfx/layers/wr/HitTestInfoManager.h create mode 100644 layout/painting/HitTestInfo.cpp create mode 100644 layout/painting/HitTestInfo.h diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index f2c206f9a860..7944a1379901 100755 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -266,6 +266,7 @@ EXPORTS.mozilla.layers += [ "wr/AsyncImagePipelineManager.h", "wr/ClipManager.h", "wr/DisplayItemCache.h", + "wr/HitTestInfoManager.h", "wr/IpcResourceUpdateQueue.h", "wr/OMTAController.h", "wr/OMTASampler.h", @@ -545,6 +546,7 @@ UNIFIED_SOURCES += [ "wr/AsyncImagePipelineManager.cpp", "wr/ClipManager.cpp", "wr/DisplayItemCache.cpp", + "wr/HitTestInfoManager.cpp", "wr/IpcResourceUpdateQueue.cpp", "wr/OMTAController.cpp", "wr/OMTASampler.cpp", diff --git a/gfx/layers/wr/HitTestInfoManager.cpp b/gfx/layers/wr/HitTestInfoManager.cpp new file mode 100644 index 000000000000..1799b493762d --- /dev/null +++ b/gfx/layers/wr/HitTestInfoManager.cpp @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HitTestInfoManager.h" +#include "HitTestInfo.h" + +#include "nsDisplayList.h" + +namespace mozilla::layers { + +using ViewID = ScrollableLayerGuid::ViewID; + +/** + * TODO(miko): This used to be a performance bottle-neck, but it does not show + * up in profiles anymore, see bugs 1424637 and 1424968. + * A better way of doing this would be to store current app units per dev pixel + * in wr::DisplayListBuilder, and update it whenever display items that separate + * presshell boundaries are encountered. + */ +static int32_t GetAppUnitsFromDisplayItem(nsDisplayItem* aItem) { + nsIFrame* frame = aItem->Frame(); + MOZ_ASSERT(frame); + return frame->PresContext()->AppUnitsPerDevPixel(); +} + +static void CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder, + nsPaintedDisplayItem* aItem, + const nsRect& aArea, + const gfx::CompositorHitTestInfo& aFlags, + const ViewID& aViewId) { + const Maybe sideBits = + aBuilder.GetContainingFixedPosSideBits(aItem->GetActiveScrolledRoot()); + + const LayoutDeviceRect devRect = + LayoutDeviceRect::FromAppUnits(aArea, GetAppUnitsFromDisplayItem(aItem)); + const wr::LayoutRect rect = wr::ToLayoutRect(devRect); + + aBuilder.PushHitTest(rect, rect, !aItem->BackfaceIsHidden(), aViewId, aFlags, + sideBits.valueOr(SideBits::eNone)); +} + +void HitTestInfoManager::Reset() { + mArea = nsRect(); + mFlags = gfx::CompositorHitTestInvisibleToHit; +} + +void HitTestInfoManager::SwitchItem(nsPaintedDisplayItem* aItem, + wr::DisplayListBuilder& aBuilder, + nsDisplayListBuilder* aDisplayListBuilder) { + if (!aItem || aItem->HasChildren()) { + // Either the item is not a painted display item, or it is a container item. + return; + } + + const HitTestInfo& hitTestInfo = aItem->GetHitTestInfo(); + const nsRect& area = hitTestInfo.Area(); + const gfx::CompositorHitTestInfo& flags = hitTestInfo.Info(); + + if (area.IsEmpty() || flags == gfx::CompositorHitTestInvisibleToHit) { + return; + } + + const auto viewId = + hitTestInfo.GetViewId(aBuilder, aItem->GetActiveScrolledRoot()); + const auto spaceAndClipChain = aBuilder.CurrentSpaceAndClipChain(); + + if (!Update(area, flags, viewId, spaceAndClipChain)) { + // The previous hit test information is still valid. + return; + } + + CreateWebRenderCommands(aBuilder, aItem, area, flags, viewId); +} + +bool HitTestInfoManager::Update(const nsRect& aArea, + const gfx::CompositorHitTestInfo& aFlags, + const ViewID& aViewId, + const wr::WrSpaceAndClipChain& aSpaceAndClip) { + if (mViewId == aViewId && mFlags == aFlags && mArea.Contains(aArea) && + mSpaceAndClipChain == aSpaceAndClip) { + // The previous hit testing information can be reused. + return false; + } + + mArea = aArea; + mFlags = aFlags; + mViewId = aViewId; + mSpaceAndClipChain = aSpaceAndClip; + return true; +} + +} // namespace mozilla::layers diff --git a/gfx/layers/wr/HitTestInfoManager.h b/gfx/layers/wr/HitTestInfoManager.h new file mode 100644 index 000000000000..5080af04bf4b --- /dev/null +++ b/gfx/layers/wr/HitTestInfoManager.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_HITTESTINFOMANAGER_H +#define GFX_HITTESTINFOMANAGER_H + +#include "mozilla/gfx/CompositorHitTestInfo.h" +#include "mozilla/layers/ScrollableLayerGuid.h" +#include "mozilla/webrender/WebRenderAPI.h" +#include "nsRect.h" +#include "nsTArray.h" + +class nsPaintedDisplayItem; +class nsDisplayListBuilder; + +namespace mozilla { + +namespace wr { +class DisplayListBuilder; +} + +namespace layers { + +/** + * This class extracts the hit testing information (area, flags, ViewId) from + * Gecko display items and pushes them into WebRender display list. + * + * The hit testing information is deduplicated: a new hit test item is only + * added if the new area is not contained in the previous area, or if the flags, + * ViewId, or current spatial id is different. + */ +class HitTestInfoManager { + public: + /** + * Resets the previous hit testing information. + */ + void Reset(); + + /** + * Extracts the hit testing information from |aItem|, and if necessary, adds + * a new WebRender hit test item using |aBuilder|. + */ + void SwitchItem(nsPaintedDisplayItem* aItem, wr::DisplayListBuilder& aBuilder, + nsDisplayListBuilder* aDisplayListBuilder); + + private: + bool Update(const nsRect& aArea, const gfx::CompositorHitTestInfo& aFlags, + const ScrollableLayerGuid::ViewID& aViewId, + const wr::WrSpaceAndClipChain& aSpaceAndClip); + + nsRect mArea; + gfx::CompositorHitTestInfo mFlags; + ScrollableLayerGuid::ViewID mViewId; + wr::WrSpaceAndClipChain mSpaceAndClipChain; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/wr/WebRenderCommandBuilder.cpp b/gfx/layers/wr/WebRenderCommandBuilder.cpp index 8fbfb655b66a..a49b7be7df81 100644 --- a/gfx/layers/wr/WebRenderCommandBuilder.cpp +++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp @@ -207,6 +207,7 @@ struct Grouper { int32_t mAppUnitsPerDevPixel; nsDisplayListBuilder* mDisplayListBuilder; ClipManager& mClipManager; + HitTestInfoManager mHitTestInfoManager; Matrix mTransform; // Paint the list of aChildren display items. @@ -1230,17 +1231,19 @@ void Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder, { auto spaceAndClipChain = mClipManager.SwitchItem(item); wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain); + mHitTestInfoManager.SwitchItem(item->AsPaintedDisplayItem(), aBuilder, + aDisplayListBuilder); sIndent++; // Note: this call to CreateWebRenderCommands can recurse back into // this function. bool createdWRCommands = item->CreateWebRenderCommands( aBuilder, aResources, aSc, manager, mDisplayListBuilder); - sIndent--; MOZ_RELEASE_ASSERT( createdWRCommands, "active transforms should always succeed at creating " "WebRender commands"); + sIndent--; } currentGroup = &groupData->mFollowingGroup; @@ -1412,6 +1415,7 @@ void WebRenderCommandBuilder::DoGroupingForDisplayList( GP("DoGroupingForDisplayList\n"); mClipManager.BeginList(aSc); + mHitTestInfoManager.Reset(); Grouper g(mClipManager); int32_t appUnitsPerDevPixel = @@ -1566,6 +1570,8 @@ void WebRenderCommandBuilder::BuildWebRenderCommands( aScrollData = WebRenderScrollData(mManager, aDisplayListBuilder); MOZ_ASSERT(mLayerScrollData.empty()); mClipManager.BeginBuild(mManager, aBuilder); + mHitTestInfoManager.Reset(); + mBuilderDumpIndex = 0; mLastCanvasDatas.Clear(); mLastLocalCanvasDatas.Clear(); @@ -1650,6 +1656,12 @@ void WebRenderCommandBuilder::CreateWebRenderCommands( auto* item = aItem->AsPaintedDisplayItem(); MOZ_RELEASE_ASSERT(item, "Tried to paint item that cannot be painted"); + mHitTestInfoManager.SwitchItem(item, aBuilder, aDisplayListBuilder); + if (item->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { + // The hit test information was processed above. + return; + } + if (aBuilder.ReuseItem(item)) { // No further processing should be needed, since the item was reused. return; diff --git a/gfx/layers/wr/WebRenderCommandBuilder.h b/gfx/layers/wr/WebRenderCommandBuilder.h index 22ebac8d4f31..1080f1727701 100644 --- a/gfx/layers/wr/WebRenderCommandBuilder.h +++ b/gfx/layers/wr/WebRenderCommandBuilder.h @@ -9,6 +9,7 @@ #include "mozilla/webrender/WebRenderAPI.h" #include "mozilla/layers/ClipManager.h" +#include "mozilla/layers/HitTestInfoManager.h" #include "mozilla/layers/WebRenderMessages.h" #include "mozilla/layers/WebRenderScrollData.h" #include "mozilla/layers/WebRenderUserData.h" @@ -178,6 +179,7 @@ class WebRenderCommandBuilder final { nsDisplayListBuilder* aDisplayListBuilder); ClipManager mClipManager; + HitTestInfoManager mHitTestInfoManager; // We use this as a temporary data structure while building the mScrollData // inside a layers-free transaction. diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 3c7fcd41c33c..581edbfd80ec 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -116,13 +116,11 @@ static void PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, aStream << ")"; } - if (aItem->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { - auto* hitTestInfoItem = static_cast(aItem); - + if (auto* paintedItem = aItem->AsPaintedDisplayItem()) { aStream << nsPrintfCString(" hitTestInfo(0x%x)", - hitTestInfoItem->HitTestFlags().serialize()); + paintedItem->HitTestFlags().serialize()); - nsRect area = hitTestInfoItem->HitTestArea(); + nsRect area = paintedItem->HitTestArea(); aStream << nsPrintfCString(" hitTestArea(%d,%d,%d,%d)", area.x, area.y, area.width, area.height); } diff --git a/layout/forms/nsButtonFrameRenderer.cpp b/layout/forms/nsButtonFrameRenderer.cpp index 647c2c0dd901..59b23e7c9f54 100644 --- a/layout/forms/nsButtonFrameRenderer.cpp +++ b/layout/forms/nsButtonFrameRenderer.cpp @@ -385,8 +385,12 @@ nsresult nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder, nsRect buttonRect = mFrame->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mFrame); - nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, mFrame, - buttonRect, aBackground); + const AppendedBackgroundType result = + nsDisplayBackgroundImage::AppendBackgroundItemsToTop( + aBuilder, mFrame, buttonRect, aBackground); + if (result == AppendedBackgroundType::None) { + aBuilder->BuildCompositorHitTestInfoIfNeeded(GetFrame(), aBackground); + } aBackground->AppendNewToTop(aBuilder, GetFrame(), this); diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index d55fac87fdc8..5b2ecbb86d25 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3852,10 +3852,9 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, if (info != CompositorHitTestInvisibleToHit) { auto* hitInfo = MakeDisplayItemWithIndex( - aBuilder, mScrolledFrame, 1, info); + aBuilder, mScrolledFrame, 1); if (hitInfo) { - aBuilder->SetCompositorHitTestInfo(hitInfo->HitTestArea(), - hitInfo->HitTestFlags()); + aBuilder->SetCompositorHitTestInfo(info); set.BorderBackground()->AppendToTop(hitInfo); } } @@ -4072,37 +4071,36 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, // Make sure that APZ will dispatch events back to content so we can // create a displayport for this frame. We'll add the item later on. - if (!mWillBuildScrollableLayer) { - if (aBuilder->BuildCompositorHitTestInfo()) { - int32_t zIndex = MaxZIndexInListOfItemsContainedInFrame( - scrolledContent.PositionedDescendants(), mOuter); - if (aBuilder->IsPartialUpdate()) { - if (auto* items = - mScrolledFrame->GetProperty(nsIFrame::DisplayItems())) { - for (nsDisplayItemBase* item : *items) { - if (item->GetType() == - DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { - auto* hitTestItem = - static_cast(item); - if (hitTestItem->HitTestFlags().contains( - CompositorHitTestFlags::eInactiveScrollframe)) { - zIndex = std::max(zIndex, hitTestItem->ZIndex()); - item->SetCantBeReused(); - } + if (!mWillBuildScrollableLayer && aBuilder->BuildCompositorHitTestInfo()) { + int32_t zIndex = MaxZIndexInListOfItemsContainedInFrame( + scrolledContent.PositionedDescendants(), mOuter); + if (aBuilder->IsPartialUpdate()) { + if (auto* items = + mScrolledFrame->GetProperty(nsIFrame::DisplayItems())) { + for (nsDisplayItemBase* item : *items) { + if (item->GetType() == + DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { + auto* hitTestItem = + static_cast(item); + if (hitTestItem->HitTestFlags().contains( + CompositorHitTestFlags::eInactiveScrollframe)) { + zIndex = std::max(zIndex, hitTestItem->ZIndex()); + item->SetCantBeReused(); } } } } - // Make sure the z-index of the inactive item is at least zero. - // Otherwise, it will end up behind non-positioned items in the scrolled - // content. - zIndex = std::max(zIndex, 0); - nsDisplayCompositorHitTestInfo* hitInfo = - MakeDisplayItemWithIndex( - aBuilder, mScrolledFrame, 1, info, Some(area)); - if (hitInfo) { - AppendInternalItemToTop(scrolledContent, hitInfo, Some(zIndex)); - } + } + // Make sure the z-index of the inactive item is at least zero. + // Otherwise, it will end up behind non-positioned items in the scrolled + // content. + zIndex = std::max(zIndex, 0); + nsDisplayCompositorHitTestInfo* hitInfo = + MakeDisplayItemWithIndex( + aBuilder, mScrolledFrame, 1, area, info); + if (hitInfo) { + AppendInternalItemToTop(scrolledContent, hitInfo, Some(zIndex)); + aBuilder->SetCompositorHitTestInfo(info); } } diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index f0052095a219..f98e36da8fbd 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -2584,19 +2584,26 @@ bool nsIFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder, return false; } + AppendedBackgroundType result = AppendedBackgroundType::None; + // Here we don't try to detect background propagation. Frames that might // receive a propagated background should just set aForceBackground to // true. if (hitTesting || aForceBackground || !StyleBackground()->IsTransparent(this) || StyleDisplay()->HasAppearance()) { - return nsDisplayBackgroundImage::AppendBackgroundItemsToTop( + result = nsDisplayBackgroundImage::AppendBackgroundItemsToTop( aBuilder, this, GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this), aLists.BorderBackground()); } - return false; + if (result == AppendedBackgroundType::None) { + aBuilder->BuildCompositorHitTestInfoIfNeeded(this, + aLists.BorderBackground()); + } + + return result == AppendedBackgroundType::ThemedBackground; } void nsIFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, @@ -2875,6 +2882,19 @@ static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, } } +static void UpdateCurrentHitTestInfo(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame) { + if (!aBuilder->BuildCompositorHitTestInfo()) { + // Compositor hit test info is not used. + return; + } + + CheckForApzAwareEventHandlers(aBuilder, aFrame); + + const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(aBuilder); + aBuilder->SetCompositorHitTestInfo(info); +} + /** * True if aDescendant participates the context aAncestor participating. */ @@ -3287,6 +3307,8 @@ void nsIFrame::BuildDisplayListForStackingContext( nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList( aBuilder, this, visibleRect, dirtyRect, isTransformed); + UpdateCurrentHitTestInfo(aBuilder, this); + // Depending on the effects that are applied to this frame, we can create // multiple container display items and wrap them around our contents. // This enum lists all the potential container display items, in the order @@ -3376,8 +3398,6 @@ void nsIFrame::BuildDisplayListForStackingContext( ApplyClipProp(transformedCssClip); } - mozilla::UniquePtr hitTestInfo; - nsDisplayListCollection set(aBuilder); Maybe clipForMask; bool insertBackdropRoot; @@ -3390,8 +3410,6 @@ void nsIFrame::BuildDisplayListForStackingContext( nsDisplayListBuilder::AutoInEventsOnly inEventsSetter( aBuilder, opacityItemForEventsOnly); - CheckForApzAwareEventHandlers(aBuilder, this); - // If we have a mask, compute a clip to bound the masked content. // This is necessary in case the content moves with an ancestor // ASR of the mask. @@ -3420,8 +3438,6 @@ void nsIFrame::BuildDisplayListForStackingContext( } aBuilder->AdjustWindowDraggingRegion(this); - aBuilder->BuildCompositorHitTestInfoIfNeeded(this, set.BorderBackground(), - true); MarkAbsoluteFramesForDisplayList(aBuilder); aBuilder->Check(); @@ -3950,11 +3966,7 @@ void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder, nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( aBuilder, aChild, visible, dirty, false); - CheckForApzAwareEventHandlers(aBuilder, aChild); - - aBuilder->BuildCompositorHitTestInfoIfNeeded( - aChild, aLists.BorderBackground(), - buildingForChild.IsAnimatedGeometryRoot()); + UpdateCurrentHitTestInfo(aBuilder, aChild); aChild->MarkAbsoluteFramesForDisplayList(aBuilder); aBuilder->AdjustWindowDraggingRegion(aChild); @@ -4168,9 +4180,11 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( aBuilder, child, visible, dirty); + + UpdateCurrentHitTestInfo(aBuilder, child); + DisplayListClipState::AutoClipMultiple clipState(aBuilder); nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder); - CheckForApzAwareEventHandlers(aBuilder, child); if (savedOutOfFlowData) { aBuilder->SetBuildingInvisibleItems(false); @@ -4241,8 +4255,6 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, child->MarkAbsoluteFramesForDisplayList(aBuilder); - const bool differentAGR = buildingForChild.IsAnimatedGeometryRoot(); - if (!awayFromCommonPath && // Some SVG frames might change opacity without invalidating the frame, // so exclude them from the fast-path. @@ -4255,10 +4267,6 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // THIS IS THE COMMON CASE. // Not a pseudo or real stacking context. Do the simple thing and // return early. - - aBuilder->BuildCompositorHitTestInfoIfNeeded( - child, aLists.BorderBackground(), differentAGR); - aBuilder->AdjustWindowDraggingRegion(child); aBuilder->Check(); child->BuildDisplayList(aBuilder, aLists); @@ -4276,16 +4284,6 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // z-index:non-auto nsDisplayListCollection pseudoStack(aBuilder); - // If this frame has z-index != 0, then the display item might get sorted - // into a different place in the list, and we can't rely on the previous - // hit test info to still be behind us. Force a new hit test info for this - // item, and for the item after it, so that we always have the right hit - // test info. - const bool mayBeSorted = ZIndex().valueOr(0) != 0; - - aBuilder->BuildCompositorHitTestInfoIfNeeded( - child, pseudoStack.BorderBackground(), differentAGR || mayBeSorted); - aBuilder->AdjustWindowDraggingRegion(child); nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder); aBuilder->Check(); @@ -4294,16 +4292,6 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) { builtContainerItem = false; } - - // If we forced a new hit-test info because this frame is going to be - // sorted, then clear the 'previous' data on the builder so that the next - // item also gets a new hit test info. That way we're guaranteeing hit-test - // info before and after each item that might get moved to a different spot. - if (mayBeSorted) { - aBuilder->SetCompositorHitTestInfo( - nsRect(), CompositorHitTestFlags::eVisibleToHitTest); - } - wrapListASR = contASRTracker.GetContainerASR(); list.AppendToTop(pseudoStack.BorderBackground()); @@ -11202,7 +11190,7 @@ CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo( // that code, but woven into the top-down recursive display list building // process. CompositorHitTestInfo inheritedTouchAction = - aBuilder->GetHitTestInfo() & CompositorHitTestTouchActionMask; + aBuilder->GetCompositorHitTestInfo() & CompositorHitTestTouchActionMask; nsIFrame* touchActionFrame = this; if (nsIScrollableFrame* scrollFrame = diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index 2567a418a6bf..9702f732da4c 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -1737,7 +1737,6 @@ class FLBDisplayListIterator : public FlattenedDisplayListIterator { nsDisplayItem* next = PeekNext(); const DisplayItemType type = next->GetType(); - if (type == DisplayItemType::TYPE_SVG_WRAPPER) { // We mark SetContainsSVG for the CONTENT_FRAME_TIME_WITH_SVG metric if (RefPtr lm = mState->mBuilder->GetWidgetLayerManager()) { @@ -4039,8 +4038,9 @@ void PaintedLayerData::AccumulateHitTestItem(ContainerState* aState, nsDisplayItem* aItem, const DisplayItemClip& aClip, TransformClipNode* aTransform) { - MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO); - auto* item = static_cast(aItem); + auto* item = aItem->AsPaintedDisplayItem(); + MOZ_ASSERT(item); + nsRect area = item->HitTestArea(); const CompositorHitTestInfo& flags = item->HitTestFlags(); @@ -4549,10 +4549,7 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { nsRect itemContent = item->GetBounds(mBuilder, &snap); if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { - // Override the marker for nsDisplayCompositorHitTestInfo items. - marker = DisplayItemEntryType::HitTestInfo; - itemContent = - static_cast(item)->HitTestArea(); + itemContent = static_cast(item)->HitTestArea(); } const bool inEffect = InTransform() || InOpacity(); @@ -4606,18 +4603,22 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { const DisplayItemClip& itemClip = itemClipPtr ? *itemClipPtr : DisplayItemClip::NoClip(); - if (inEffect && marker == DisplayItemEntryType::HitTestInfo) { - // Fast-path for hit test items inside flattened inactive layers. - MOZ_ASSERT(selectedLayer); - selectedLayer->AccumulateHitTestItem(this, item, itemClip, transformNode); - continue; - } - if (inEffect && marker == DisplayItemEntryType::Item) { // Fast-path for items inside flattened inactive layers. This works // because the layer state of the item cannot be active, otherwise the // parent item would not have been flattened. MOZ_ASSERT(selectedLayer); + + if (item->AsPaintedDisplayItem()->GetHitTestInfo().Info() != + mozilla::gfx::CompositorHitTestInvisibleToHit) { + selectedLayer->AccumulateHitTestItem(this, item, itemClip, nullptr); + } + + if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { + // The hit test info was processed above. + continue; + } + selectedLayer->Accumulate(this, item->AsPaintedDisplayItem(), nsIntRect(), nsRect(), itemClip, layerState, aList, marker, opacityIndices, transformNode); @@ -4678,7 +4679,7 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { #ifdef DEBUG nsRect bounds = itemContent; - if (marker == DisplayItemEntryType::HitTestInfo || inEffect) { + if (inEffect) { bounds.SetEmpty(); } @@ -5109,9 +5110,6 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { ? mContainerReferenceFrame : item->ReferenceFrame(); - MOZ_ASSERT(item != mContainerItem || - marker == DisplayItemEntryType::HitTestInfo); - PaintedLayerData* paintedLayerData = selectedLayer; if (!paintedLayerData) { @@ -5125,31 +5123,36 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { } MOZ_ASSERT(paintedLayerData); - if (marker == DisplayItemEntryType::HitTestInfo) { - MOZ_ASSERT(!transformNode); + if (item->AsPaintedDisplayItem()->GetHitTestInfo().Info() != + mozilla::gfx::CompositorHitTestInvisibleToHit) { paintedLayerData->AccumulateHitTestItem(this, item, itemClip, nullptr); - } else { - paintedLayerData->Accumulate( - this, item->AsPaintedDisplayItem(), itemVisibleRect, itemContent, - itemClip, layerState, aList, marker, opacityIndices, transformNode); + } - if (!paintedLayerData->mLayer) { - // Try to recycle the old layer of this display item. - RefPtr layer = AttemptToRecyclePaintedLayer( - itemAGR, item, topLeft, - inEffect ? containerReferenceFrame : referenceFrame); - if (layer) { - paintedLayerData->mLayer = layer; + if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { + // The hit test info was processed above. + continue; + } - auto* userData = GetPaintedDisplayItemLayerUserData(layer); - paintedLayerData->mAssignedDisplayItems.reserve( - userData->mLastItemCount); + paintedLayerData->Accumulate( + this, item->AsPaintedDisplayItem(), itemVisibleRect, itemContent, + itemClip, layerState, aList, marker, opacityIndices, transformNode); - NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0, - "Layer already in list???"); - mNewChildLayers[paintedLayerData->mNewChildLayersIndex].mLayer = - std::move(layer); - } + if (!paintedLayerData->mLayer) { + // Try to recycle the old layer of this display item. + RefPtr layer = AttemptToRecyclePaintedLayer( + itemAGR, item, topLeft, + inEffect ? containerReferenceFrame : referenceFrame); + if (layer) { + paintedLayerData->mLayer = layer; + + auto* userData = GetPaintedDisplayItemLayerUserData(layer); + paintedLayerData->mAssignedDisplayItems.reserve( + userData->mLastItemCount); + + NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0, + "Layer already in list???"); + mNewChildLayers[paintedLayerData->mNewChildLayersIndex].mLayer = + std::move(layer); } } diff --git a/layout/painting/FrameLayerBuilder.h b/layout/painting/FrameLayerBuilder.h index 8446ce634e0c..357a7df13e7f 100644 --- a/layout/painting/FrameLayerBuilder.h +++ b/layout/painting/FrameLayerBuilder.h @@ -77,7 +77,6 @@ enum class DisplayItemEntryType : uint8_t { PopOpacity, PushTransform, PopTransform, - HitTestInfo, }; /** diff --git a/layout/painting/HitTestInfo.cpp b/layout/painting/HitTestInfo.cpp new file mode 100644 index 000000000000..ea782ee23fca --- /dev/null +++ b/layout/painting/HitTestInfo.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HitTestInfo.h" +#include "mozilla/webrender/WebRenderAPI.h" +#include "nsDisplayList.h" +#include "nsIFrame.h" + +namespace mozilla { + +using ViewID = layers::ScrollableLayerGuid::ViewID; + +ViewID HitTestInfo::GetViewId(wr::DisplayListBuilder& aBuilder, + const ActiveScrolledRoot* aASR) const { + if (mScrollTarget) { + return *mScrollTarget; + } + + Maybe fixedTarget = + aBuilder.GetContainingFixedPosScrollTarget(aASR); + + if (fixedTarget) { + return *fixedTarget; + } + + if (aASR) { + return aASR->GetViewId(); + } + + return ScrollableLayerGuid::NULL_SCROLL_ID; +} + +void HitTestInfo::Initialize(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { + if (!aBuilder->BuildCompositorHitTestInfo()) { + return; + } + + mInfo = aFrame->GetCompositorHitTestInfo(aBuilder); + if (mInfo != gfx::CompositorHitTestInvisibleToHit) { + mArea = aFrame->GetCompositorHitTestArea(aBuilder); + InitializeScrollTarget(aBuilder); + } +} + +void HitTestInfo::InitializeScrollTarget(nsDisplayListBuilder* aBuilder) { + if (aBuilder->GetCurrentScrollbarDirection().isSome()) { + // In the case of scrollbar frames, we use the scrollbar's target + // scrollframe instead of the scrollframe with which the scrollbar actually + // moves. + MOZ_ASSERT(Info().contains(CompositorHitTestFlags::eScrollbar)); + mScrollTarget = Some(aBuilder->GetCurrentScrollbarTarget()); + } +} + +} // namespace mozilla diff --git a/layout/painting/HitTestInfo.h b/layout/painting/HitTestInfo.h new file mode 100644 index 000000000000..0bdb2ba70c56 --- /dev/null +++ b/layout/painting/HitTestInfo.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_HITTESTINFO_H +#define GFX_HITTESTINFO_H + +#include "mozilla/gfx/CompositorHitTestInfo.h" +#include "mozilla/layers/ScrollableLayerGuid.h" +#include "nsRect.h" + +class nsIFrame; +class nsDisplayListBuilder; + +namespace mozilla { + +struct ActiveScrolledRoot; + +/** + * A helper class that manages compositor hit testing information. + */ +class HitTestInfo { + public: + using CompositorHitTestInfo = gfx::CompositorHitTestInfo; + using ViewID = layers::ScrollableLayerGuid::ViewID; + + ViewID GetViewId(wr::DisplayListBuilder& aBuilder, + const ActiveScrolledRoot* aASR) const; + + void Initialize(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); + void InitializeScrollTarget(nsDisplayListBuilder* aBuilder); + + void SetAreaAndInfo(const nsRect& aArea, const CompositorHitTestInfo& aInfo) { + mArea = aArea; + mInfo = aInfo; + } + + static void Shutdown(); + + const nsRect& Area() const { return mArea; } + const CompositorHitTestInfo& Info() const { return mInfo; } + + private: + nsRect mArea; + CompositorHitTestInfo mInfo; + mozilla::Maybe mScrollTarget; +}; + +} // namespace mozilla + +#endif diff --git a/layout/painting/moz.build b/layout/painting/moz.build index 4a1aad01ee1d..f27a971afa30 100644 --- a/layout/painting/moz.build +++ b/layout/painting/moz.build @@ -13,6 +13,7 @@ EXPORTS += [ "DisplayItemClipChain.h", "DisplayListClipState.h", "FrameLayerBuilder.h", + "HitTestInfo.h", "LayerState.h", "MatrixStack.h", "nsCSSRenderingBorders.h", @@ -40,6 +41,7 @@ UNIFIED_SOURCES += [ "DisplayListClipState.cpp", "DottedCornerFinder.cpp", "FrameLayerBuilder.cpp", + "HitTestInfo.cpp", "MaskLayerImageCache.cpp", "nsCSSRendering.cpp", "nsCSSRenderingBorders.cpp", diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 4c21e3c2c19a..5893d677b40c 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -173,6 +173,25 @@ void UpdateDisplayItemData(nsPaintedDisplayItem* aItem) { } } +static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) { + switch (aType) { + case DisplayItemType::TYPE_BACKGROUND: + case DisplayItemType::TYPE_BACKGROUND_COLOR: + case DisplayItemType::TYPE_THEMED_BACKGROUND: + return true; + default: + return false; + } +} + +void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder, + nsPaintedDisplayItem* aItem, + const DisplayItemType aType) { + if (ItemTypeSupportsHitTesting(aType)) { + aItem->GetHitTestInfo().Initialize(aBuilder, aItem->Frame()); + } +} + /* static */ already_AddRefed ActiveScrolledRoot::CreateASRForFrame( const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame, @@ -613,9 +632,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mBuildAsyncZoomContainer(false), mContainsBackdropFilter(false), mIsRelativeToLayoutViewport(false), - mUseOverlayScrollbars(false), - mHitTestArea(), - mHitTestInfo(CompositorHitTestInvisibleToHit) { + mUseOverlayScrollbars(false) { MOZ_COUNT_CTOR(nsDisplayListBuilder); mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting(); @@ -2085,7 +2102,7 @@ void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting( } void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded( - nsIFrame* aFrame, nsDisplayList* aList, const bool aBuildNew) { + nsIFrame* aFrame, nsDisplayList* aList) { MOZ_ASSERT(aFrame); MOZ_ASSERT(aList); @@ -2094,22 +2111,9 @@ void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded( } const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this); - if (info == CompositorHitTestInvisibleToHit) { - return; + if (info != CompositorHitTestInvisibleToHit) { + aList->AppendNewToTop(this, aFrame); } - - const nsRect area = aFrame->GetCompositorHitTestArea(this); - if (!aBuildNew && GetHitTestInfo() == info && - GetHitTestArea().Contains(area)) { - return; - } - - auto* item = MakeDisplayItem( - this, aFrame, info, Some(area)); - MOZ_ASSERT(item); - - SetCompositorHitTestInfo(area, info); - aList->AppendToTop(item); } void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const { @@ -3545,7 +3549,7 @@ static nsDisplayBackgroundColor* CreateBackgroundColor( } /*static*/ -bool nsDisplayBackgroundImage::AppendBackgroundItemsToTop( +AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect, nsDisplayList* aList, bool aAllowWillPaintBorderOptimization, ComputedStyle* aComputedStyle, @@ -3591,7 +3595,7 @@ bool nsDisplayBackgroundImage::AppendBackgroundItemsToTop( if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList, color)) { - return false; + return AppendedBackgroundType::None; } const nsStyleBorder* borderStyle = aFrame->StyleBorder(); @@ -3660,13 +3664,21 @@ bool nsDisplayBackgroundImage::AppendBackgroundItemsToTop( bgItemList.AppendToTop(bgItem); } - aList->AppendToTop(&bgItemList); - return true; + if (!bgItemList.IsEmpty()) { + aList->AppendToTop(&bgItemList); + return AppendedBackgroundType::ThemedBackground; + } + + return AppendedBackgroundType::None; } if (!bg || !drawBackgroundImage) { - aList->AppendToTop(&bgItemList); - return false; + if (!bgItemList.IsEmpty()) { + aList->AppendToTop(&bgItemList); + return AppendedBackgroundType::Background; + } + + return AppendedBackgroundType::None; } const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot(); @@ -3783,8 +3795,12 @@ bool nsDisplayBackgroundImage::AppendBackgroundItemsToTop( aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr)); } - aList->AppendToTop(&bgItemList); - return false; + if (!bgItemList.IsEmpty()) { + aList->AppendToTop(&bgItemList); + return AppendedBackgroundType::Background; + } + + return AppendedBackgroundType::None; } // Check that the rounded border of aFrame, added to aToReferenceFrame, @@ -4848,90 +4864,12 @@ void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder, aOutFrames->AppendElement(mFrame); } -nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo( - nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags, - const mozilla::Maybe& aArea) - : nsPaintedDisplayItem(aBuilder, aFrame), - mAppUnitsPerDevPixel(mFrame->PresContext()->AppUnitsPerDevPixel()) { - MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo); - // We should never even create this display item if we're not building - // compositor hit-test info or if the computed hit info indicated the - // frame is invisible to hit-testing - MOZ_ASSERT(aBuilder->BuildCompositorHitTestInfo()); - MOZ_ASSERT(aHitTestFlags != CompositorHitTestInvisibleToHit); - - const nsRect& area = - aArea.isSome() ? *aArea : aFrame->GetCompositorHitTestArea(aBuilder); - - SetHitTestInfo(area, aHitTestFlags); - InitializeScrollTarget(aBuilder); -} - -nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo( - nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - mozilla::UniquePtr&& aHitTestInfo) - : nsPaintedDisplayItem(aBuilder, aFrame), - mAppUnitsPerDevPixel(mFrame->PresContext()->AppUnitsPerDevPixel()) { - MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo); - SetHitTestInfo(std::move(aHitTestInfo)); - InitializeScrollTarget(aBuilder); -} - -void nsDisplayCompositorHitTestInfo::InitializeScrollTarget( - nsDisplayListBuilder* aBuilder) { - if (aBuilder->GetCurrentScrollbarDirection().isSome()) { - // In the case of scrollbar frames, we use the scrollbar's target - // scrollframe instead of the scrollframe with which the scrollbar actually - // moves. - MOZ_ASSERT(HitTestFlags().contains(CompositorHitTestFlags::eScrollbar)); - mScrollTarget = mozilla::Some(aBuilder->GetCurrentScrollbarTarget()); - } -} - bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::RenderRootStateManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) { - if (HitTestArea().IsEmpty()) { - return true; - } - - // XXX: eventually this scrollId computation and the SetHitTestInfo - // call will get moved out into the WR display item iteration code so that - // we don't need to do it as often, and so that we can do it for other - // display item types as well (reducing the need for as many instances of - // this display item). - ScrollableLayerGuid::ViewID scrollId = - mScrollTarget.valueOrFrom([&]() -> ScrollableLayerGuid::ViewID { - const ActiveScrolledRoot* asr = GetActiveScrolledRoot(); - Maybe fixedTarget = - aBuilder.GetContainingFixedPosScrollTarget(asr); - if (fixedTarget) { - return *fixedTarget; - } - if (asr) { - return asr->GetViewId(); - } - return ScrollableLayerGuid::NULL_SCROLL_ID; - }); - - Maybe sideBits = - aBuilder.GetContainingFixedPosSideBits(GetActiveScrolledRoot()); - - // Insert a transparent rectangle with the hit-test info - const LayoutDeviceRect devRect = - LayoutDeviceRect::FromAppUnits(HitTestArea(), mAppUnitsPerDevPixel); - - const wr::LayoutRect rect = wr::ToLayoutRect(devRect); - - aBuilder.StartGroup(this); - aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, - HitTestFlags(), sideBits.valueOr(SideBits::eNone)); - aBuilder.FinishGroup(); - return true; } @@ -7363,7 +7301,8 @@ bool nsDisplayAsyncZoom::UpdateScrollData( // #ifndef DEBUG -static_assert(sizeof(nsDisplayTransform) < 512, "nsDisplayTransform has grown"); +static_assert(sizeof(nsDisplayTransform) <= 512, + "nsDisplayTransform has grown"); #endif nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, @@ -10231,13 +10170,12 @@ nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList( : mBuilder(aBuilder), mPrevFrame(aBuilder->mCurrentFrame), mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame), - mPrevHitTestArea(aBuilder->mHitTestArea), - mPrevHitTestInfo(aBuilder->mHitTestInfo), mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame), mPrevAdditionalOffset(aBuilder->mAdditionalOffset), mPrevVisibleRect(aBuilder->mVisibleRect), mPrevDirtyRect(aBuilder->mDirtyRect), mPrevAGR(aBuilder->mCurrentAGR), + mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo), mPrevAncestorHasApzAwareEventHandler( aBuilder->mAncestorHasApzAwareEventHandler), mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems), diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 2992bcd27e87..62f775b95feb 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -34,6 +34,7 @@ #include "DisplayListClipState.h" #include "LayerState.h" #include "FrameMetrics.h" +#include "HitTestInfo.h" #include "ImgDrawResult.h" #include "mozilla/dom/EffectsInfo.h" #include "mozilla/dom/RemoteBrowser.h" @@ -680,29 +681,20 @@ class nsDisplayListBuilder { mAllowMergingAndFlattening = aAllow; } - /** - * Sets the current compositor hit test area and info to |aHitTestArea| and - * |aHitTestInfo|. - * This is used during display list building to determine if the parent frame - * hit test info contains the same information that child frame needs. - */ - void SetCompositorHitTestInfo(const nsRect& aHitTestArea, - const CompositorHitTestInfo& aHitTestInfo) { - mHitTestArea = aHitTestArea; - mHitTestInfo = aHitTestInfo; + void SetCompositorHitTestInfo(const CompositorHitTestInfo& aInfo) { + mCompositorHitTestInfo = aInfo; } - const nsRect& GetHitTestArea() const { return mHitTestArea; } - const CompositorHitTestInfo& GetHitTestInfo() const { return mHitTestInfo; } + const CompositorHitTestInfo& GetCompositorHitTestInfo() const { + return mCompositorHitTestInfo; + } /** * Builds a new nsDisplayCompositorHitTestInfo for the frame |aFrame| if - * needed, and adds it to the top of |aList|. If |aBuildNew| is true, the - * previous hit test info will not be reused. + * needed, and adds it to the top of |aList|. */ void BuildCompositorHitTestInfoIfNeeded(nsIFrame* aFrame, - nsDisplayList* aList, - const bool aBuildNew); + nsDisplayList* aList); bool IsInsidePointerEventsNoneDoc() { return CurrentPresShellState()->mInsidePointerEventsNoneDoc; @@ -1134,8 +1126,6 @@ class nsDisplayListBuilder { ~AutoBuildingDisplayList() { mBuilder->mCurrentFrame = mPrevFrame; mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame; - mBuilder->mHitTestArea = mPrevHitTestArea; - mBuilder->mHitTestInfo = mPrevHitTestInfo; mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset; mBuilder->mVisibleRect = mPrevVisibleRect; mBuilder->mDirtyRect = mPrevDirtyRect; @@ -1145,6 +1135,7 @@ class nsDisplayListBuilder { mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems; mBuilder->mInInvalidSubtree = mPrevInInvalidSubtree; mBuilder->mAdditionalOffset = mPrevAdditionalOffset; + mBuilder->mCompositorHitTestInfo = mPrevCompositorHitTestInfo; } private: @@ -1152,13 +1143,12 @@ class nsDisplayListBuilder { AGRState mCurrentAGRState; const nsIFrame* mPrevFrame; const nsIFrame* mPrevReferenceFrame; - nsRect mPrevHitTestArea; - CompositorHitTestInfo mPrevHitTestInfo; nsPoint mPrevOffset; mozilla::Maybe mPrevAdditionalOffset; nsRect mPrevVisibleRect; nsRect mPrevDirtyRect; RefPtr mPrevAGR; + CompositorHitTestInfo mPrevCompositorHitTestInfo; bool mPrevAncestorHasApzAwareEventHandler; bool mPrevBuildingInvisibleItems; bool mPrevInInvalidSubtree; @@ -1832,7 +1822,6 @@ class nsDisplayListBuilder { friend class nsDisplayItem; friend class nsDisplayOwnLayer; friend struct RetainedDisplayListBuilder; - friend struct HitTestInfo; AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame); AnimatedGeometryRoot* WrapAGRForFrame( @@ -2029,8 +2018,7 @@ class nsDisplayListBuilder { bool mUseOverlayScrollbars; mozilla::Maybe mVisibleThreshold; - nsRect mHitTestArea; - CompositorHitTestInfo mHitTestInfo; + mozilla::gfx::CompositorHitTestInfo mCompositorHitTestInfo; }; class nsDisplayItem; @@ -2083,8 +2071,18 @@ void AssertUniqueItem(nsDisplayItem* aItem); */ bool ShouldBuildItemForEvents(const DisplayItemType aType); +/** + * Updates the item DisplayItemData if needed. + */ void UpdateDisplayItemData(nsPaintedDisplayItem* aItem); +/** + * Initializes the hit test information of |aItem| if the item type supports it. + */ +void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder, + nsPaintedDisplayItem* aItem, + const DisplayItemType aType); + template MOZ_ALWAYS_INLINE T* MakeDisplayItemWithIndex(nsDisplayListBuilder* aBuilder, F* aFrame, const uint16_t aIndex, @@ -2112,6 +2110,7 @@ MOZ_ALWAYS_INLINE T* MakeDisplayItemWithIndex(nsDisplayListBuilder* aBuilder, nsPaintedDisplayItem* paintedItem = item->AsPaintedDisplayItem(); if (paintedItem) { UpdateDisplayItemData(paintedItem); + InitializeHitTestInfo(aBuilder, paintedItem, type); } if (aBuilder->InInvalidSubtree() || @@ -2164,8 +2163,6 @@ class nsDisplayItemLink { friend class nsDisplayList; }; -class nsPaintedDisplayItem; - /* * nsDisplayItemBase is a base-class for all display items. It is mainly * responsible for handling the frame-display item 1:n relationship, as well as @@ -3255,9 +3252,16 @@ class nsPaintedDisplayItem : public nsDisplayItem { mCacheIndex = mozilla::Nothing(); } + mozilla::HitTestInfo& GetHitTestInfo() { return mHitTestInfo; } + const nsRect& HitTestArea() const { return mHitTestInfo.Area(); } + const mozilla::gfx::CompositorHitTestInfo& HitTestFlags() const { + return mHitTestInfo.Info(); + } + protected: nsPaintedDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) - : nsDisplayItem(aBuilder, aFrame) {} + : nsPaintedDisplayItem(aBuilder, aFrame, + aBuilder->CurrentActiveScrolledRoot()) {} nsPaintedDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const ActiveScrolledRoot* aActiveScrolledRoot) @@ -3265,12 +3269,15 @@ class nsPaintedDisplayItem : public nsDisplayItem { nsPaintedDisplayItem(nsDisplayListBuilder* aBuilder, const nsPaintedDisplayItem& aOther) - : nsDisplayItem(aBuilder, aOther) {} + : nsDisplayItem(aBuilder, aOther), mHitTestInfo(aOther.mHitTestInfo) {} private: mozilla::DisplayItemData* mDisplayItemData = nullptr; mozilla::layers::LayerManager* mDisplayItemDataLayerManager = nullptr; mozilla::Maybe mCacheIndex; + + protected: + mozilla::HitTestInfo mHitTestInfo; }; /** @@ -4438,6 +4445,12 @@ class nsDisplaySolidColorRegion : public nsPaintedDisplayItem { sRGBColor mColor; }; +enum class AppendedBackgroundType : uint8_t { + None, + Background, + ThemedBackground, +}; + /** * A display item to paint one background-image for a frame. Each background * image layer gets its own nsDisplayBackgroundImage. @@ -4483,12 +4496,14 @@ class nsDisplayBackgroundImage : public nsDisplayImageContainer { NS_DISPLAY_DECL_NAME("Background", TYPE_BACKGROUND) - // This will create and append new items for all the layers of the - // background. Returns whether we appended a themed background. - // aAllowWillPaintBorderOptimization should usually be left at true, unless - // aFrame has special border drawing that causes opaque borders to not - // actually be opaque. - static bool AppendBackgroundItemsToTop( + /** + * This will create and append new items for all the layers of the + * background. Returns the type of background that was appended. + * aAllowWillPaintBorderOptimization should usually be left at true, unless + * aFrame has special border drawing that causes opaque borders to not + * actually be opaque. + */ + static AppendedBackgroundType AppendBackgroundItemsToTop( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect, nsDisplayList* aList, bool aAllowWillPaintBorderOptimization = true, @@ -5160,20 +5175,6 @@ class nsDisplayEventReceiver final : public nsDisplayItem { HitTestState* aState, nsTArray* aOutFrames) final; }; -struct HitTestInfo { - HitTestInfo(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags) - : mArea(aFrame->GetCompositorHitTestArea(aBuilder)), - mFlags(aHitTestFlags) {} - - HitTestInfo(const nsRect& aArea, - const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags) - : mArea(aArea), mFlags(aHitTestFlags) {} - - nsRect mArea; - mozilla::gfx::CompositorHitTestInfo mFlags; -}; - /** * Similar to nsDisplayEventReceiver in that it is used for hit-testing. However * this gets built when we're doing widget painting and we need to send the @@ -5182,21 +5183,26 @@ struct HitTestInfo { */ class nsDisplayCompositorHitTestInfo : public nsPaintedDisplayItem { public: - nsDisplayCompositorHitTestInfo( - nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags, - const mozilla::Maybe& aArea = mozilla::Nothing()); + nsDisplayCompositorHitTestInfo(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame) + : nsPaintedDisplayItem(aBuilder, aFrame) { + MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo); + mHitTestInfo.Initialize(aBuilder, aFrame); + } nsDisplayCompositorHitTestInfo( - nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - mozilla::UniquePtr&& aHitTestInfo); + nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aArea, + const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags) + : nsPaintedDisplayItem(aBuilder, aFrame) { + MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo); + mHitTestInfo.SetAreaAndInfo(aArea, aHitTestFlags); + mHitTestInfo.InitializeScrollTarget(aBuilder); + } MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayCompositorHitTestInfo) NS_DISPLAY_DECL_NAME("CompositorHitTestInfo", TYPE_COMPOSITOR_HITTEST_INFO) - void InitializeScrollTarget(nsDisplayListBuilder* aBuilder); - bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, @@ -5207,26 +5213,6 @@ class nsDisplayCompositorHitTestInfo : public nsPaintedDisplayItem { int32_t ZIndex() const override; void SetOverrideZIndex(int32_t aZIndex); - void SetHitTestInfo(mozilla::UniquePtr&& aHitTestInfo) { - MOZ_ASSERT(aHitTestInfo); - MOZ_ASSERT(aHitTestInfo->mFlags != - mozilla::gfx::CompositorHitTestInvisibleToHit); - - mHitTestInfo = std::move(aHitTestInfo); - } - - void SetHitTestInfo( - const nsRect& aArea, - const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags) { - MOZ_ASSERT(aHitTestFlags != mozilla::gfx::CompositorHitTestInvisibleToHit); - mHitTestInfo = mozilla::MakeUnique(aArea, aHitTestFlags); - } - - const HitTestInfo& GetHitTestInfo() const { - MOZ_ASSERT(mHitTestInfo); - return *mHitTestInfo; - } - /** * ApplyOpacity() is overriden for opacity flattening. */ @@ -5243,18 +5229,8 @@ class nsDisplayCompositorHitTestInfo : public nsPaintedDisplayItem { return nsRect(); } - const nsRect& HitTestArea() const { return mHitTestInfo->mArea; } - - const mozilla::gfx::CompositorHitTestInfo& HitTestFlags() const { - return mHitTestInfo->mFlags; - } - private: - mozilla::Maybe mScrollTarget; mozilla::Maybe mOverrideZIndex; - int32_t mAppUnitsPerDevPixel; - - mozilla::UniquePtr mHitTestInfo; }; /** diff --git a/layout/svg/SVGGeometryFrame.cpp b/layout/svg/SVGGeometryFrame.cpp index 1dd1caa78a51..d0b29948b65c 100644 --- a/layout/svg/SVGGeometryFrame.cpp +++ b/layout/svg/SVGGeometryFrame.cpp @@ -215,6 +215,9 @@ void SVGGeometryFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, styleSVG->mMarkerMid.IsNone() && styleSVG->mMarkerStart.IsNone()) { return; } + + aBuilder->BuildCompositorHitTestInfoIfNeeded(this, + aLists.BorderBackground()); } DisplayOutline(aBuilder, aLists); diff --git a/layout/svg/SVGOuterSVGFrame.cpp b/layout/svg/SVGOuterSVGFrame.cpp index 43444da0e796..64d6e032a3f8 100644 --- a/layout/svg/SVGOuterSVGFrame.cpp +++ b/layout/svg/SVGOuterSVGFrame.cpp @@ -765,6 +765,8 @@ void SVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, contentList, contentList); BuildDisplayListForNonBlockChildren(aBuilder, set); } else if (IsVisibleForPainting() || !aBuilder->IsForPainting()) { + aBuilder->BuildCompositorHitTestInfoIfNeeded(this, + aLists.BorderBackground()); aLists.Content()->AppendNewToTop(aBuilder, this); } } diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index d11f80780c0f..fb2709efee92 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -463,13 +463,19 @@ void nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this); // display background if we need to. + AppendedBackgroundType result = AppendedBackgroundType::None; if (aBuilder->IsForEventDelivery() || !StyleBackground()->IsTransparent(this) || StyleDisplay()->HasAppearance()) { - nsDisplayBackgroundImage::AppendBackgroundItemsToTop( + result = nsDisplayBackgroundImage::AppendBackgroundItemsToTop( aBuilder, this, bgRect, aLists.BorderBackground()); } + if (result == AppendedBackgroundType::None) { + aBuilder->BuildCompositorHitTestInfoIfNeeded(this, + aLists.BorderBackground()); + } + // display inset box-shadows if we need to. if (hasBoxShadow) { aLists.BorderBackground()->AppendNewToTop(