Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/layers/apz/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 10 kB image not shown  

Quelle  WRHitTester.cpp   Sprache: C

 
/* -*- 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 "WRHitTester.h"
#include "AsyncPanZoomController.h"
#include "APZCTreeManager.h"
#include "TreeTraversal.h"  // for BreadthFirstSearch
#include "mozilla/gfx/CompositorHitTestInfo.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "nsDebug.h"        // for NS_ASSERTION
#include "nsIXULRuntime.h"  // for FissionAutostart
#include "mozilla/gfx/Matrix.h"

#define APZCTM_LOG(...) \
  MOZ_LOG(APZCTreeManager::sLog, LogLevel::Debug, (__VA_ARGS__))

namespace mozilla {
namespace layers {

using mozilla::gfx::CompositorHitTestFlags;
using mozilla::gfx::CompositorHitTestInvisibleToHit;

static bool CheckCloseToIdentity(const gfx::Matrix4x4& aMatrix) {
  // We allow a factor of 1/2048 in the multiply part of the matrix, so that if
  // we multiply by a point on a screen of size 2048 we would be off by at most
  // 1 pixel approximately.
  const float multiplyEps = 1 / 2048.f;
  // We allow 1 pixel in the translate part of the matrix.
  const float translateEps = 1.f;

  if (!FuzzyEqualsAdditive(aMatrix._11, 1.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._12, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._13, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._14, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._21, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._22, 1.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._23, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._24, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._31, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._32, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._33, 1.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._34, 0.f, multiplyEps) ||
      !FuzzyEqualsAdditive(aMatrix._41, 0.f, translateEps) ||
      !FuzzyEqualsAdditive(aMatrix._42, 0.f, translateEps) ||
      !FuzzyEqualsAdditive(aMatrix._43, 0.f, translateEps) ||
      !FuzzyEqualsAdditive(aMatrix._44, 1.f, multiplyEps)) {
    return false;
  }
  return true;
}

// Checks that within the constraints of floating point math we can invert it
// reasonably enough that multiplying by the computed inverse is close to the
// identity.
static bool CheckInvertibleWithFinitePrecision(const gfx::Matrix4x4& aMatrix) {
  auto inverse = aMatrix.MaybeInverse();
  if (inverse.isNothing()) {
    // Should we return false?
    return true;
  }
  if (!CheckCloseToIdentity(aMatrix * *inverse)) {
    return false;
  }
  if (!CheckCloseToIdentity(*inverse * aMatrix)) {
    return false;
  }
  return true;
}

IAPZHitTester::HitTestResult WRHitTester::GetAPZCAtPoint(
    const ScreenPoint& aHitTestPoint,
    const RecursiveMutexAutoLock& aProofOfTreeLock) {
  HitTestResult hit;
  RefPtr<wr::WebRenderAPI> wr = mTreeManager->GetWebRenderAPI();
  if (!wr) {
    // If WebRender isn't running, fall back to the root APZC.
    // This is mostly for the benefit of GTests which do not
    // run a WebRender instance, but gracefully falling back
    // here allows those tests which are not specifically
    // testing the hit-test algorithm to still work.
    hit.mTargetApzc = FindRootApzcForLayersId(GetRootLayersId());
    hit.mHitResult = CompositorHitTestFlags::eVisibleToHitTest;
    return hit;
  }

  APZCTM_LOG("Hit-testing point %s with WR\n", ToString(aHitTestPoint).c_str());
  std::vector<wr::WrHitResult> results =
      wr->HitTest(wr::ToWorldPoint(aHitTestPoint));

  Maybe<wr::WrHitResult> chosenResult;
  // It's possible for the WebRender hit test to produce a result with
  // a LayersId whose corresponding layer tree has already been
  // torn down (e.g. during window/tab shutdown, or navigation to
  // a different domain). This is expected, so avoid debug assertions
  // firing in this scenario.
  DebugOnly<bool> layersIdExists = true;
  for (const wr::WrHitResult& result : results) {
    ScrollableLayerGuid guid{result.mLayersId, 0, result.mScrollId};
    APZCTM_LOG("Examining result with guid %s hit info 0x%x... ",
               ToString(guid).c_str(), result.mHitInfo.serialize());
    if (result.mHitInfo == CompositorHitTestInvisibleToHit) {
      APZCTM_LOG("skipping due to invisibility.\n");
      continue;
    }
    RefPtr<HitTestingTreeNode> node =
        GetTargetNode(guid, &ScrollableLayerGuid::EqualsIgnoringPresShell);
    if (!node) {
      APZCTM_LOG("no corresponding node found, falling back to root.\n");

#ifdef DEBUG
      // We can enter here during normal codepaths for cases where the
      // nsDisplayCompositorHitTestInfo item emitted a scrollId of
      // NULL_SCROLL_ID to the webrender display list. The semantics of that
      // is to fall back to the root APZC for the layers id, so that's what
      // we do here.
      // If we enter this codepath and scrollId is not NULL_SCROLL_ID, then
      // that's more likely to be due to a race condition between rebuilding
      // the APZ tree and updating the WR scene/hit-test information, resulting
      // in WR giving us a hit result for a scene that is not active in APZ.
      // Such a scenario would need debugging and fixing.
      // In non-Fission mode, make this assertion non-fatal because there is
      // a known issue related to inactive scroll frames that can cause this
      // to fire (see bug 1634763), which is fixed in Fission mode and not
      // worth fixing in non-Fission mode.
      layersIdExists =
          CompositorBridgeParent::HasIndirectShadowTree(result.mLayersId);
      if (FissionAutostart()) {
        MOZ_ASSERT(result.mScrollId == ScrollableLayerGuid::NULL_SCROLL_ID ||
                   !layersIdExists);
      } else {
        NS_ASSERTION(
            result.mScrollId == ScrollableLayerGuid::NULL_SCROLL_ID,
            "Inconsistency between WebRender display list and APZ scroll data");
      }
#endif
      node = FindRootNodeForLayersId(result.mLayersId);
      if (!node) {
        // Should never happen, but handle gracefully in release builds just
        // in case.
        MOZ_ASSERT(!layersIdExists,
                   "No root node found for hit-test result layers id");
        chosenResult = Some(result);
        break;
      }
    }
    MOZ_ASSERT(node->GetApzc());  // any node returned must have an APZC
    EventRegionsOverride flags = node->GetEventRegionsOverride();
    if (flags & EventRegionsOverride::ForceEmptyHitRegion) {
      // This result is inside a subtree that is invisible to hit-testing.
      APZCTM_LOG("skipping due to FEHR subtree.\n");
      continue;
    }

    if (!CheckInvertibleWithFinitePrecision(
            mTreeManager->GetScreenToApzcTransform(node->GetApzc())
                .ToUnknownMatrix())) {
      APZCTM_LOG("skipping due to check inverse accuracy\n");
      continue;
    }

    APZCTM_LOG("selecting as chosen result.\n");
    chosenResult = Some(result);
    hit.mTargetApzc = node->GetApzc();
    if (flags & EventRegionsOverride::ForceDispatchToContent) {
      chosenResult->mHitInfo += CompositorHitTestFlags::eApzAwareListeners;
    }
    break;
  }
  if (!chosenResult) {
    return hit;
  }

  MOZ_ASSERT(
      hit.mTargetApzc || !layersIdExists,
      "A hit-test result with a valid layers id should have a target APZC");
  hit.mLayersId = chosenResult->mLayersId;
  ScrollableLayerGuid::ViewID scrollId = chosenResult->mScrollId;
  gfx::CompositorHitTestInfo hitInfo = chosenResult->mHitInfo;
  Maybe<uint64_t> animationId = chosenResult->mAnimationId;
  SideBits sideBits = chosenResult->mSideBits;

  APZCTM_LOG("Successfully matched APZC %p (hit result 0x%x)\n",
             hit.mTargetApzc.get(), hitInfo.serialize());

  const bool isScrollbar =
      hitInfo.contains(gfx::CompositorHitTestFlags::eScrollbar);
  const bool isScrollbarThumb =
      hitInfo.contains(gfx::CompositorHitTestFlags::eScrollbarThumb);
  const ScrollDirection direction =
      hitInfo.contains(gfx::CompositorHitTestFlags::eScrollbarVertical)
          ? ScrollDirection::eVertical
          : ScrollDirection::eHorizontal;
  HitTestingTreeNode* scrollbarNode = nullptr;
  if (isScrollbar || isScrollbarThumb) {
    scrollbarNode = BreadthFirstSearch<ReverseIterator>(
        GetRootNode(), [&](HitTestingTreeNode* aNode) {
          return (aNode->GetLayersId() == hit.mLayersId) &&
                 (aNode->IsScrollbarNode() == isScrollbar) &&
                 (aNode->IsScrollThumbNode() == isScrollbarThumb) &&
                 (aNode->GetScrollbarDirection() == direction) &&
                 (aNode->GetScrollTargetId() == scrollId);
        });
  }

  hit.mHitResult = hitInfo;

  if (scrollbarNode) {
    RefPtr<HitTestingTreeNode> scrollbarRef = scrollbarNode;
    InitializeHitTestingTreeNodeAutoLock(hit.mScrollbarNode, aProofOfTreeLock,
                                         scrollbarRef);
  }

  hit.mFixedPosSides = sideBits;
  if (animationId.isSome()) {
    RefPtr<HitTestingTreeNode> positionedNode = nullptr;

    positionedNode = BreadthFirstSearch<ReverseIterator>(
        GetRootNode(), [&](HitTestingTreeNode* aNode) {
          return (aNode->GetFixedPositionAnimationId() == animationId ||
                  aNode->GetStickyPositionAnimationId() == animationId);
        });

    if (positionedNode) {
      MOZ_ASSERT(positionedNode->GetLayersId() == chosenResult->mLayersId,
                 "Found node layers id does not match the hit result");
      MOZ_ASSERT((positionedNode->GetFixedPositionAnimationId().isSome() ||
                  positionedNode->GetStickyPositionAnimationId().isSome()),
                 "A a matching fixed/sticky position node should be found");
      InitializeHitTestingTreeNodeAutoLock(hit.mNode, aProofOfTreeLock,
                                           positionedNode);
    }

#if defined(MOZ_WIDGET_ANDROID)
    if (hit.mNode && hit.mNode->GetFixedPositionAnimationId().isSome()) {
      // If the hit element is a fixed position element, the side bits from
      // the hit-result item tag are used. For now just ensure that these
      // match what is found in the hit-testing tree node.
      MOZ_ASSERT(sideBits == hit.mNode->GetFixedPosSides(),
                 "Fixed position side bits do not match");
    } else if (hit.mTargetApzc && hit.mTargetApzc->IsRootContent()) {
      // If the hit element is not a fixed position element, then the hit test
      // result item's side bits should not be populated.
      MOZ_ASSERT(sideBits == SideBits::eNone,
                 "Hit test results have side bits only for pos:fixed");
    }
#endif
  }

  hit.mHitOverscrollGutter =
      hit.mTargetApzc && hit.mTargetApzc->IsInOverscrollGutter(aHitTestPoint);

  return hit;
}

}  // namespace layers
}  // namespace mozilla

Messung V0.5
C=82 H=98 G=90

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.