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 "HitTestInfoManager.h"
8 #include "HitTestInfo.h"
9 
10 #include "nsDisplayList.h"
11 
12 #define DEBUG_HITTEST_INFO 0
13 #if DEBUG_HITTEST_INFO
14 #  define HITTEST_INFO_LOG(...) printf_stderr(__VA_ARGS__)
15 #else
16 #  define HITTEST_INFO_LOG(...)
17 #endif
18 
19 namespace mozilla::layers {
20 
21 using ViewID = ScrollableLayerGuid::ViewID;
22 
23 /**
24  * TODO(miko): This used to be a performance bottle-neck, but it does not show
25  * up in profiles anymore, see bugs 1424637 and 1424968.
26  * A better way of doing this would be to store current app units per dev pixel
27  * in wr::DisplayListBuilder, and update it whenever display items that separate
28  * presshell boundaries are encountered.
29  */
GetAppUnitsFromDisplayItem(nsDisplayItem * aItem)30 static int32_t GetAppUnitsFromDisplayItem(nsDisplayItem* aItem) {
31   nsIFrame* frame = aItem->Frame();
32   MOZ_ASSERT(frame);
33   return frame->PresContext()->AppUnitsPerDevPixel();
34 }
35 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,nsDisplayItem * aItem,const nsRect & aArea,const gfx::CompositorHitTestInfo & aFlags,const ViewID & aViewId)36 static void CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
37                                     nsDisplayItem* aItem, const nsRect& aArea,
38                                     const gfx::CompositorHitTestInfo& aFlags,
39                                     const ViewID& aViewId) {
40   const Maybe<SideBits> sideBits =
41       aBuilder.GetContainingFixedPosSideBits(aItem->GetActiveScrolledRoot());
42 
43   const LayoutDeviceRect devRect =
44       LayoutDeviceRect::FromAppUnits(aArea, GetAppUnitsFromDisplayItem(aItem));
45   const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
46 
47   aBuilder.PushHitTest(rect, rect, !aItem->BackfaceIsHidden(), aViewId, aFlags,
48                        sideBits.valueOr(SideBits::eNone));
49 }
50 
Reset()51 void HitTestInfoManager::Reset() {
52   mArea = nsRect();
53   mFlags = gfx::CompositorHitTestInvisibleToHit;
54   HITTEST_INFO_LOG("* HitTestInfoManager::Reset\n");
55 }
56 
ProcessItem(nsDisplayItem * aItem,wr::DisplayListBuilder & aBuilder,nsDisplayListBuilder * aDisplayListBuilder)57 void HitTestInfoManager::ProcessItem(
58     nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
59     nsDisplayListBuilder* aDisplayListBuilder) {
60   MOZ_ASSERT(aItem);
61 
62   if (!aItem->HasHitTestInfo()) {
63     return;
64   }
65 
66   const HitTestInfo& hitTestInfo = aItem->GetHitTestInfo();
67   const nsRect& area = hitTestInfo.Area();
68   const gfx::CompositorHitTestInfo& flags = hitTestInfo.Info();
69 
70   if (flags == gfx::CompositorHitTestInvisibleToHit || area.IsEmpty()) {
71     return;
72   }
73 
74   const auto viewId =
75       hitTestInfo.GetViewId(aBuilder, aItem->GetActiveScrolledRoot());
76   const auto spaceAndClipChain = aBuilder.CurrentSpaceAndClipChain();
77 
78   if (!Update(area, flags, viewId, spaceAndClipChain)) {
79     // The previous hit test information is still valid.
80     return;
81   }
82 
83   HITTEST_INFO_LOG("+ [%d, %d, %d, %d]: flags: 0x%x, viewId: %llu\n", area.x,
84                    area.y, area.width, area.height, flags.serialize(), viewId);
85 
86   CreateWebRenderCommands(aBuilder, aItem, area, flags, viewId);
87 }
88 
89 /**
90  * Updates the current hit testing information if necessary.
91  * Returns true if the the hit testing information was changed.
92  */
Update(const nsRect & aArea,const gfx::CompositorHitTestInfo & aFlags,const ViewID & aViewId,const wr::WrSpaceAndClipChain & aSpaceAndClip)93 bool HitTestInfoManager::Update(const nsRect& aArea,
94                                 const gfx::CompositorHitTestInfo& aFlags,
95                                 const ViewID& aViewId,
96                                 const wr::WrSpaceAndClipChain& aSpaceAndClip) {
97   if (mViewId == aViewId && mFlags == aFlags && mArea.Contains(aArea) &&
98       mSpaceAndClipChain == aSpaceAndClip) {
99     // The previous hit testing information can be reused.
100     HITTEST_INFO_LOG("s [%d, %d, %d, %d]: flags: 0x%x, viewId: %llu\n", aArea.x,
101                      aArea.y, aArea.width, aArea.height, aFlags.serialize(),
102                      aViewId);
103     return false;
104   }
105 
106   mArea = aArea;
107   mFlags = aFlags;
108   mViewId = aViewId;
109   mSpaceAndClipChain = aSpaceAndClip;
110   return true;
111 }
112 
113 }  // namespace mozilla::layers
114