tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HitTestInfoManager.cpp (5805B)


      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 "mozilla/gfx/CompositorHitTestInfo.h"
     11 #include "mozilla/layers/ScrollableLayerGuid.h"
     12 #include "nsDisplayList.h"
     13 
     14 #define DEBUG_HITTEST_INFO 0
     15 #if DEBUG_HITTEST_INFO
     16 #  define HITTEST_INFO_LOG(...) printf_stderr(__VA_ARGS__)
     17 #else
     18 #  define HITTEST_INFO_LOG(...)
     19 #endif
     20 
     21 namespace mozilla::layers {
     22 
     23 using ViewID = ScrollableLayerGuid::ViewID;
     24 
     25 /**
     26 * TODO(miko): This used to be a performance bottle-neck, but it does not show
     27 * up in profiles anymore, see bugs 1424637 and 1424968.
     28 * A better way of doing this would be to store current app units per dev pixel
     29 * in wr::DisplayListBuilder, and update it whenever display items that separate
     30 * presshell boundaries are encountered.
     31 */
     32 static int32_t GetAppUnitsFromDisplayItem(nsDisplayItem* aItem) {
     33  nsIFrame* frame = aItem->Frame();
     34  MOZ_ASSERT(frame);
     35  return frame->PresContext()->AppUnitsPerDevPixel();
     36 }
     37 
     38 static void CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
     39                                    nsDisplayItem* aItem, const nsRect& aArea,
     40                                    const gfx::CompositorHitTestInfo& aFlags,
     41                                    const ViewID& aViewId) {
     42  const Maybe<SideBits> sideBits =
     43      aBuilder.GetContainingFixedPosSideBits(aItem->GetActiveScrolledRoot());
     44 
     45  const LayoutDeviceRect devRect =
     46      LayoutDeviceRect::FromAppUnits(aArea, GetAppUnitsFromDisplayItem(aItem));
     47  const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
     48 
     49  aBuilder.PushHitTest(rect, rect, !aItem->BackfaceIsHidden(), aViewId, aFlags,
     50                       sideBits.valueOr(SideBits::eNone));
     51 }
     52 
     53 HitTestInfoManager::HitTestInfoManager()
     54    : mFlags(gfx::CompositorHitTestInvisibleToHit),
     55      mViewId(ScrollableLayerGuid::NULL_SCROLL_ID),
     56      mSpaceAndClipChain(wr::InvalidScrollNodeWithChain()) {}
     57 
     58 void HitTestInfoManager::Reset() {
     59  mArea = nsRect();
     60  mFlags = gfx::CompositorHitTestInvisibleToHit;
     61  mViewId = ScrollableLayerGuid::NULL_SCROLL_ID;
     62  mSpaceAndClipChain = wr::InvalidScrollNodeWithChain();
     63 
     64  HITTEST_INFO_LOG("* HitTestInfoManager::Reset\n");
     65 }
     66 
     67 bool HitTestInfoManager::ProcessItem(
     68    nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
     69    nsDisplayListBuilder* aDisplayListBuilder) {
     70  MOZ_ASSERT(aItem);
     71 
     72  HITTEST_INFO_LOG("* HitTestInfoManager::ProcessItem(%d, %s, has=%d)\n",
     73                   getpid(), aItem->Frame()->ListTag().get(),
     74                   aItem->HasHitTestInfo());
     75 
     76  if (MOZ_UNLIKELY(aItem->GetType() == DisplayItemType::TYPE_REMOTE)) {
     77    // Remote frames might contain hit-test-info items inside (but those
     78    // aren't processed by this process of course), so we can't optimize out the
     79    // next hit-test info item because it might be on top of the iframe.
     80    Reset();
     81  }
     82 
     83  if (!aItem->HasHitTestInfo()) {
     84    return false;
     85  }
     86 
     87  const HitTestInfo& hitTestInfo = aItem->GetHitTestInfo();
     88  const nsRect& area = hitTestInfo.Area();
     89  const gfx::CompositorHitTestInfo& flags = hitTestInfo.Info();
     90 
     91  if (flags == gfx::CompositorHitTestInvisibleToHit || area.IsEmpty()) {
     92    return false;
     93  }
     94 
     95  const auto viewId =
     96      hitTestInfo.GetViewId(aBuilder, aItem->GetActiveScrolledRoot());
     97  const auto spaceAndClipChain = aBuilder.CurrentSpaceAndClipChain();
     98 
     99  if (!Update(area, flags, viewId, spaceAndClipChain)) {
    100    // The previous hit test information is still valid.
    101    return false;
    102  }
    103 
    104  HITTEST_INFO_LOG("+ [%d, %d, %d, %d]: flags: 0x%x, viewId: %lu\n", area.x,
    105                   area.y, area.width, area.height, flags.serialize(), viewId);
    106 
    107  CreateWebRenderCommands(aBuilder, aItem, area, flags, viewId);
    108 
    109  return true;
    110 }
    111 
    112 void HitTestInfoManager::ProcessItemAsImage(
    113    nsDisplayItem* aItem, const wr::LayoutRect& aRect,
    114    wr::DisplayListBuilder& aBuilder,
    115    nsDisplayListBuilder* aDisplayListBuilder) {
    116  // Optimization: if the item has no children, we don't need to tell the
    117  // compositor about it because there isn't going to be anything inside it
    118  // that would produce a different hit-test result.
    119  if (!aItem->HasChildren()) {
    120    return;
    121  }
    122  const auto* asr = aItem->GetActiveScrolledRoot();
    123  SideBits sideBits =
    124      aBuilder.GetContainingFixedPosSideBits(asr).valueOr(SideBits::eNone);
    125  gfx::CompositorHitTestInfo hitInfo = mFlags;
    126  if (hitInfo.contains(gfx::CompositorHitTestFlags::eVisibleToHitTest)) {
    127    hitInfo += gfx::CompositorHitTestFlags::eIrregularArea;
    128  }
    129  aBuilder.PushHitTest(aRect, aRect, !aItem->BackfaceIsHidden(), mViewId,
    130                       hitInfo, sideBits);
    131 }
    132 
    133 /**
    134 * Updates the current hit testing information if necessary.
    135 * Returns true if the hit testing information was changed.
    136 */
    137 bool HitTestInfoManager::Update(const nsRect& aArea,
    138                                const gfx::CompositorHitTestInfo& aFlags,
    139                                const ViewID& aViewId,
    140                                const wr::WrSpaceAndClipChain& aSpaceAndClip) {
    141  if (mViewId == aViewId && mFlags == aFlags && mArea.Contains(aArea) &&
    142      mSpaceAndClipChain == aSpaceAndClip) {
    143    // The previous hit testing information can be reused.
    144    HITTEST_INFO_LOG("s [%d, %d, %d, %d]: flags: 0x%x, viewId: %lu\n", aArea.x,
    145                     aArea.y, aArea.width, aArea.height, aFlags.serialize(),
    146                     aViewId);
    147    return false;
    148  }
    149 
    150  mArea = aArea;
    151  mFlags = aFlags;
    152  mViewId = aViewId;
    153  mSpaceAndClipChain = aSpaceAndClip;
    154  return true;
    155 }
    156 
    157 }  // namespace mozilla::layers