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