Bug 1542019 - Split the dispatch-to-content flag into three. r=botond,hsivonen

This patch extracts two additional CompositorHitTestInfo flags from the
eDispatchToContent flag; eApzAwareListeners for elements that have
APZ-aware listeners, and eInactiveScrollframe for inactive scrollframe
or unlayerized scrollthumbs. The eDispatchToContent is then renamed to
eIrregularArea to reflect the fact that it is used for irregular-shaped
areas that require main-thread hit-testing.

Additionally, it is important to note that when using the non-WebRender
codepath, all three of these flags still end up gettings squashed into
the "dispatch to content" region on the EventRegions; when APZ
reconstructs a CompositorHitTestInfo it will turn anything in this
region back into an eIrregularArea. So this is a lossy round-trip
conversion for the non-WebRender case. However it should still result in
correct behaviour because the semantics of eIrregularArea result in APZ
relying on the main-thread to do hit-testing and so any APZ-aware
listeners and inactive scrollframes are also handled by the main-thread.

Differential Revision: https://phabricator.services.mozilla.com/D26440

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Kartikaya Gupta 2019-04-11 13:31:53 +00:00
parent 4669671e38
commit 937b89a55a
13 changed files with 97 additions and 43 deletions

View File

@ -35,14 +35,17 @@ namespace APZHitResultFlags {
// These constants should be kept in sync with mozilla::gfx::CompositorHitTestInfo
const unsigned short INVISIBLE = 0;
const unsigned short VISIBLE = 0x0001;
const unsigned short DISPATCH_TO_CONTENT = 0x0002;
const unsigned short PAN_X_DISABLED = 0x0004;
const unsigned short PAN_Y_DISABLED = 0x0008;
const unsigned short PINCH_ZOOM_DISABLED = 0x0010;
const unsigned short DOUBLE_TAP_ZOOM_DISABLED = 0x0020;
const unsigned short SCROLLBAR = 0x0040;
const unsigned short SCROLLBAR_THUMB = 0x0080;
const unsigned short SCROLLBAR_VERTICAL = 0x0100;
const unsigned short IRREGULAR_AREA = 0x0002;
const unsigned short APZ_AWARE_LISTENERS = 0x0004;
const unsigned short INACTIVE_SCROLLFRAME = 0x0008;
const unsigned short PAN_X_DISABLED = 0x0010;
const unsigned short PAN_Y_DISABLED = 0x0020;
const unsigned short PINCH_ZOOM_DISABLED = 0x0040;
const unsigned short DOUBLE_TAP_ZOOM_DISABLED = 0x0080;
const unsigned short SCROLLBAR = 0x0100;
const unsigned short SCROLLBAR_THUMB = 0x0200;
const unsigned short SCROLLBAR_VERTICAL = 0x0400;
const unsigned short REQUIRES_TARGET_CONFIRMATION = 0x0800;
};
dictionary APZHitResult {

View File

@ -1627,7 +1627,10 @@ static TouchBehaviorFlags ConvertToTouchBehavior(
TouchBehaviorFlags result = AllowedTouchBehavior::UNKNOWN;
if (info == CompositorHitTestInvisibleToHit) {
result = AllowedTouchBehavior::NONE;
} else if (info.contains(CompositorHitTestFlags::eDispatchToContent)) {
} else if (info.contains(CompositorHitTestFlags::eIrregularArea)) {
// Note that eApzAwareListeners and eInactiveScrollframe are similar
// to eIrregularArea in some respects, but are not relevant for the
// purposes of this function, which deals specifically with touch-action.
result = AllowedTouchBehavior::UNKNOWN;
} else {
result = AllowedTouchBehavior::VERTICAL_PAN |

View File

@ -115,8 +115,7 @@ struct TargetConfirmationFlags {
const gfx::CompositorHitTestInfo& aHitTestInfo)
: mTargetConfirmed(
(aHitTestInfo != gfx::CompositorHitTestInvisibleToHit) &&
!aHitTestInfo.contains(
gfx::CompositorHitTestFlags::eDispatchToContent)),
(aHitTestInfo & gfx::CompositorHitTestDispatchToContent).isEmpty()),
mRequiresTargetConfirmation(aHitTestInfo.contains(
gfx::CompositorHitTestFlags::eRequiresTargetConfirmation)) {}

View File

@ -260,9 +260,16 @@ CompositorHitTestInfo HitTestingTreeNode::HitTest(
result = CompositorHitTestFlags::eVisibleToHitTest;
if ((mOverride & EventRegionsOverride::ForceDispatchToContent) ||
mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y)) {
result += CompositorHitTestFlags::eDispatchToContent;
if (mOverride & EventRegionsOverride::ForceDispatchToContent) {
result += CompositorHitTestFlags::eApzAwareListeners;
}
if (mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y)) {
// Technically this might be some combination of eInactiveScrollframe,
// eApzAwareListeners, and eIrregularArea, because the round-trip through
// mEventRegions is lossy. We just convert it back to eIrregularArea
// because that's the most conservative option (i.e. eIrregularArea makes
// APZ rely on the main thread for everything).
result += CompositorHitTestFlags::eIrregularArea;
if (mEventRegions.mDTCRequiresTargetConfirmation) {
result += CompositorHitTestFlags::eRequiresTargetConfirmation;
}

View File

@ -702,8 +702,20 @@ function hitTestScrollbar(params) {
// behaviour on different platforms which makes testing harder.
var expectedHitInfo = APZHitResultFlags.VISIBLE | APZHitResultFlags.SCROLLBAR;
if (params.expectThumb) {
// The thumb has listeners which are APZ-aware. With WebRender we are able
// to losslessly propagate this flag to APZ, but with non-WebRender the area
// ends up in the mDispatchToContentRegion which we then convert back to
// a IRREGULAR_AREA flag. This still works correctly since IRREGULAR_AREA
// will fall back to the main thread for everything.
if (config.isWebRender) {
expectedHitInfo |= APZHitResultFlags.APZ_AWARE_LISTENERS;
if (params.layerState == LayerState.INACTIVE) {
expectedHitInfo |= APZHitResultFlags.INACTIVE_SCROLLFRAME;
}
} else {
expectedHitInfo |= APZHitResultFlags.IRREGULAR_AREA;
}
// We do not generate the layers for thumbs on inactive scrollframes.
expectedHitInfo |= APZHitResultFlags.DISPATCH_TO_CONTENT;
if (params.layerState == LayerState.ACTIVE) {
expectedHitInfo |= APZHitResultFlags.SCROLLBAR_THUMB;
}

View File

@ -25,7 +25,9 @@ function* test(testDriver) {
var apzaware = document.getElementById("apzaware");
checkHitResult(hitTest(centerOf(scroller)),
APZHitResultFlags.VISIBLE | APZHitResultFlags.DISPATCH_TO_CONTENT,
APZHitResultFlags.VISIBLE |
(config.isWebRender ? APZHitResultFlags.INACTIVE_SCROLLFRAME
: APZHitResultFlags.IRREGULAR_AREA),
utils.getViewId(document.scrollingElement),
"inactive scrollframe");
@ -74,7 +76,9 @@ function* test(testDriver) {
var apzawarePosition = centerOf(apzaware); // main thread position
apzawarePosition.y -= scrollY; // APZ position
checkHitResult(hitTest(apzawarePosition),
APZHitResultFlags.VISIBLE | APZHitResultFlags.DISPATCH_TO_CONTENT,
APZHitResultFlags.VISIBLE |
(config.isWebRender ? APZHitResultFlags.APZ_AWARE_LISTENERS
: APZHitResultFlags.IRREGULAR_AREA),
scrollerViewId,
"active scrollframe - apzaware block");

View File

@ -135,11 +135,11 @@ function* test(testDriver) {
// dispatch-to-content.
if (testId == 1 || testId == 2) {
checkHitResult(hitTest({x: bounds.right - verticalScrollbarWidth - 1, y: bounds.y + 1}),
APZHitResultFlags.VISIBLE | APZHitResultFlags.DISPATCH_TO_CONTENT,
APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
rootViewId,
`top right of scroller in testcase ${testId}`);
checkHitResult(hitTest({x: bounds.right - verticalScrollbarWidth - 1, y: bounds.bottom - horizontalScrollbarHeight - 1}),
APZHitResultFlags.VISIBLE | APZHitResultFlags.DISPATCH_TO_CONTENT,
APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
rootViewId,
`bottom right of scroller in testcase ${testId}`);
} else {
@ -154,7 +154,7 @@ function* test(testDriver) {
}
checkHitResult(hitTest({x: bounds.right - 1, y: bounds.y + (bounds.height / 2)}),
APZHitResultFlags.VISIBLE | APZHitResultFlags.DISPATCH_TO_CONTENT,
APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
rootViewId,
`middle right of scroller in testcase ${testId}`);
}

View File

@ -114,6 +114,12 @@ function* test(testDriver) {
var scrollId = config.utils.getViewId(document.scrollingElement);
// Elements with APZ aware listeners round-trip through the dispatch-to-content
// region and end up as IRREGULAR_AREA when WebRender is disabled.
var touchListenerFlag = config.isWebRender
? APZHitResultFlags.APZ_AWARE_LISTENERS
: APZHitResultFlags.IRREGULAR_AREA;
checkHitResult(
hitTest(centerOf("taNone")),
APZHitResultFlags.VISIBLE |
@ -237,7 +243,7 @@ function* test(testDriver) {
checkHitResult(
hitTest(centerOf("taInnerManipListener")),
APZHitResultFlags.VISIBLE |
APZHitResultFlags.DISPATCH_TO_CONTENT |
touchListenerFlag |
APZHitResultFlags.DOUBLE_TAP_ZOOM_DISABLED,
scrollId,
"div with touch listener inside touch-action: manipulation");
@ -245,13 +251,13 @@ function* test(testDriver) {
checkHitResult(
hitTest(centerOf("taListener")),
APZHitResultFlags.VISIBLE |
APZHitResultFlags.DISPATCH_TO_CONTENT,
touchListenerFlag,
scrollId,
"div with touch listener");
checkHitResult(
hitTest(centerOf("taInnerListenerPanX")),
APZHitResultFlags.VISIBLE |
APZHitResultFlags.DISPATCH_TO_CONTENT |
touchListenerFlag |
APZHitResultFlags.PAN_Y_DISABLED |
APZHitResultFlags.PINCH_ZOOM_DISABLED |
APZHitResultFlags.DOUBLE_TAP_ZOOM_DISABLED,

View File

@ -751,9 +751,10 @@ struct DIGroup {
LINEAR; // nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
bool backfaceHidden = false;
// Emit a dispatch-to-content hit test region covering this area
// We don't really know the exact shape of this blob because it may contain
// SVG shapes so generate an irregular-area hit-test region for it.
CompositorHitTestInfo hitInfo(CompositorHitTestFlags::eVisibleToHitTest,
CompositorHitTestFlags::eDispatchToContent);
CompositorHitTestFlags::eIrregularArea);
// XXX - clipping the item against the paint rect breaks some content.
// cf. Bug 1455422.

View File

@ -22,33 +22,43 @@ namespace gfx {
// EnumSet (2 ^ <value of enumerator>), in hexadecimal.
enum class CompositorHitTestFlags : uint8_t {
// The frame participates in hit-testing
eVisibleToHitTest = 0, // 0x001
// The frame requires main-thread handling for events
eDispatchToContent, // 0x002
eVisibleToHitTest = 0, // 0x0001
// The frame may have odd shapes that requires the main thread to do accurate
// hit-testing.
eIrregularArea, // 0x0002
// The frame has APZ-aware listeners and so inputs targeted at this area
// need to be handled by the main thread before APZ can use them, as they
// might be prevent-defaulted.
eApzAwareListeners, // 0x0004
// This is an inactive scrollframe or unlayerized scrollthumb. In this state
// it cannot be used by APZ for async scrolling, so APZ will defer to the main
// thread.
eInactiveScrollframe, // 0x0008
// The touch action flags are set up so that the default of
// touch-action:auto on an element leaves all the flags as 0.
eTouchActionPanXDisabled, // 0x004
eTouchActionPanYDisabled, // 0x008
eTouchActionPinchZoomDisabled, // 0x010
eTouchActionDoubleTapZoomDisabled, // 0x020
eTouchActionPanXDisabled, // 0x0010
eTouchActionPanYDisabled, // 0x0020
eTouchActionPinchZoomDisabled, // 0x0040
eTouchActionDoubleTapZoomDisabled, // 0x0080
// The frame is a scrollbar or a subframe inside a scrollbar (including
// scroll thumbs)
eScrollbar, // 0x040
eScrollbar, // 0x0100
// The frame is a scrollthumb. If this is set then eScrollbar will also be
// set, unless gecko somehow generates a scroll thumb without a containing
// scrollbar.
eScrollbarThumb, // 0x080
eScrollbarThumb, // 0x0200
// If eScrollbar is set, this flag indicates if the scrollbar is a vertical
// one (if set) or a horizontal one (if not set)
eScrollbarVertical, // 0x100
eScrollbarVertical, // 0x0400
// Events targeting this frame should only be processed if a target
// confirmation is received from the main thread. If no such confirmation
// is received within a timeout period, the event may be dropped.
// Only meaningful in combination with eDispatchToContent.
eRequiresTargetConfirmation, // 0x200
eRequiresTargetConfirmation, // 0x0800
};
using CompositorHitTestInfo = EnumSet<CompositorHitTestFlags, uint32_t>;
@ -63,6 +73,13 @@ constexpr CompositorHitTestInfo CompositorHitTestTouchActionMask(
CompositorHitTestFlags::eTouchActionPinchZoomDisabled,
CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled);
// Mask to check all the flags that involve APZ waiting for results from the
// main thread
constexpr CompositorHitTestInfo CompositorHitTestDispatchToContent(
CompositorHitTestFlags::eIrregularArea,
CompositorHitTestFlags::eApzAwareListeners,
CompositorHitTestFlags::eInactiveScrollframe);
} // namespace gfx
// Used for IPDL serialization. The 'value' have to be the biggest enum from

View File

@ -10905,20 +10905,21 @@ CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
// Anything that didn't match the above conditions is visible to hit-testing.
result = CompositorHitTestFlags::eVisibleToHitTest;
if (aBuilder->IsBuildingNonLayerizedScrollbar() ||
aBuilder->GetAncestorHasApzAwareEventHandler()) {
if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
// Scrollbars may be painted into a layer below the actual layer they will
// scroll, and therefore wheel events may be dispatched to the outer frame
// instead of the intended scrollframe. To address this, we force a d-t-c
// region on scrollbar frames that won't be placed in their own layer. See
// bug 1213324 for details.
result += CompositorHitTestFlags::eDispatchToContent;
result += CompositorHitTestFlags::eInactiveScrollframe;
} else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
result += CompositorHitTestFlags::eApzAwareListeners;
} else if (IsObjectFrame()) {
// If the frame is a plugin frame and wants to handle wheel events as
// default action, we should add the frame to dispatch-to-content region.
nsPluginFrame* pluginFrame = do_QueryFrame(this);
if (pluginFrame && pluginFrame->WantsToHandleWheelEventAsDefaultAction()) {
result += CompositorHitTestFlags::eDispatchToContent;
result += CompositorHitTestFlags::eApzAwareListeners;
}
}
@ -10985,7 +10986,7 @@ CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
if (thumbGetsLayer) {
result += CompositorHitTestFlags::eScrollbarThumb;
} else {
result += CompositorHitTestFlags::eDispatchToContent;
result += CompositorHitTestFlags::eInactiveScrollframe;
}
}

View File

@ -3718,7 +3718,7 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
if (!mWillBuildScrollableLayer) {
if (aBuilder->BuildCompositorHitTestInfo()) {
CompositorHitTestInfo info(CompositorHitTestFlags::eVisibleToHitTest,
CompositorHitTestFlags::eDispatchToContent);
CompositorHitTestFlags::eInactiveScrollframe);
// If the scroll frame has non-default overscroll-behavior, instruct
// APZ to require a target confirmation before processing events that
// hit this scroll frame (that is, to drop the events if a

View File

@ -3950,7 +3950,8 @@ void PaintedLayerData::AccumulateHitTestItem(ContainerState* aState,
mHitRegion.OrWith(area);
}
if (flags.contains(CompositorHitTestFlags::eDispatchToContent)) {
const auto dtcFlags = flags & CompositorHitTestDispatchToContent;
if (!dtcFlags.isEmpty()) {
mDispatchToContentHitRegion.OrWith(area);
if (flags.contains(CompositorHitTestFlags::eRequiresTargetConfirmation)) {