summaryrefslogtreecommitdiffstats
path: root/gfx/layers/wr/HitTestInfoManager.cpp
blob: 552221b14c6816bfb03be38ee3b3d3ba4c3d4a0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* -*- 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"

#define DEBUG_HITTEST_INFO 0
#if DEBUG_HITTEST_INFO
#  define HITTEST_INFO_LOG(...) printf_stderr(__VA_ARGS__)
#else
#  define HITTEST_INFO_LOG(...)
#endif

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,
                                    nsDisplayItem* aItem, const nsRect& aArea,
                                    const gfx::CompositorHitTestInfo& aFlags,
                                    const ViewID& aViewId) {
  const Maybe<SideBits> 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));
}

HitTestInfoManager::HitTestInfoManager()
    : mFlags(gfx::CompositorHitTestInvisibleToHit),
      mViewId(ScrollableLayerGuid::NULL_SCROLL_ID),
      mSpaceAndClipChain(wr::InvalidScrollNodeWithChain()) {}

void HitTestInfoManager::Reset() {
  mArea = nsRect();
  mFlags = gfx::CompositorHitTestInvisibleToHit;
  mViewId = ScrollableLayerGuid::NULL_SCROLL_ID;
  mSpaceAndClipChain = wr::InvalidScrollNodeWithChain();

  HITTEST_INFO_LOG("* HitTestInfoManager::Reset\n");
}

bool HitTestInfoManager::ProcessItem(
    nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
    nsDisplayListBuilder* aDisplayListBuilder) {
  MOZ_ASSERT(aItem);

  HITTEST_INFO_LOG("* HitTestInfoManager::ProcessItem(%d, %s, has=%d)\n",
                   getpid(), aItem->Frame()->ListTag().get(),
                   aItem->HasHitTestInfo());

  if (MOZ_UNLIKELY(aItem->GetType() == DisplayItemType::TYPE_REMOTE)) {
    // Remote frames might contain hit-test-info items inside (but those
    // aren't processed by this process of course), so we can't optimize out the
    // next hit-test info item because it might be on top of the iframe.
    Reset();
  }

  if (!aItem->HasHitTestInfo()) {
    return false;
  }

  const HitTestInfo& hitTestInfo = aItem->GetHitTestInfo();
  const nsRect& area = hitTestInfo.Area();
  const gfx::CompositorHitTestInfo& flags = hitTestInfo.Info();

  if (flags == gfx::CompositorHitTestInvisibleToHit || area.IsEmpty()) {
    return false;
  }

  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 false;
  }

  HITTEST_INFO_LOG("+ [%d, %d, %d, %d]: flags: 0x%x, viewId: %lu\n", area.x,
                   area.y, area.width, area.height, flags.serialize(), viewId);

  CreateWebRenderCommands(aBuilder, aItem, area, flags, viewId);

  return true;
}

/**
 * Updates the current hit testing information if necessary.
 * Returns true if the hit testing information was changed.
 */
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.
    HITTEST_INFO_LOG("s [%d, %d, %d, %d]: flags: 0x%x, viewId: %lu\n", aArea.x,
                     aArea.y, aArea.width, aArea.height, aFlags.serialize(),
                     aViewId);
    return false;
  }

  mArea = aArea;
  mFlags = aFlags;
  mViewId = aViewId;
  mSpaceAndClipChain = aSpaceAndClip;
  return true;
}

}  // namespace mozilla::layers