Bug 1389149 - Extract the logic from nsDisplayLayerEventRegions::AddFrame into a more reusable form. r=mstange

This introduces a enum bitset type that encapsulates some of the
interesting properties that frames have that make it interesting for
hit-testing in the compositor. This type is designed so it can be sent
directly to webrender and gotten back in the hit-test.

MozReview-Commit-ID: GCxV7ZaoJd1

--HG--
extra : rebase_source : a9cc5ecfc7c5baeab2f6e08cd2ee2c2a7756e20c
This commit is contained in:
Kartikaya Gupta 2017-11-15 11:39:44 -05:00
parent cb81178b8a
commit 26f0373ad0
5 changed files with 141 additions and 47 deletions

View File

@ -0,0 +1,44 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_COMPOSITORHITTESTINFO_H_
#define MOZILLA_GFX_COMPOSITORHITTESTINFO_H_
#include "mozilla/TypedEnumBits.h"
namespace mozilla {
namespace gfx {
// This set of flags is used to figure out what information a frame has
// that is relevant to hit-testing in the compositor. The flags are
// intentionally set up so that if all of them are 0 the item is effectively
// invisible to hit-testing, and no information for this frame needs to be
// sent to the compositor.
enum class CompositorHitTestInfo : uint8_t {
// Shortcut for checking that none of the flags are set
eInvisibleToHitTest = 0,
// The frame participates in hit-testing
eVisibleToHitTest = 1 << 0,
// The frame requires main-thread handling for events
eDispatchToContent = 1 << 1,
// 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 = 1 << 2,
eTouchActionPanYDisabled = 1 << 3,
eTouchActionPinchZoomDisabled = 1 << 4,
eTouchActionDoubleTapZoomDisabled = 1 << 5,
// Mask to check for all the touch-action flags at once
eTouchActionMask = (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5),
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CompositorHitTestInfo)
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_COMPOSITORHITTESTINFO_H_ */

View File

@ -47,6 +47,7 @@ EXPORTS.mozilla += [
]
EXPORTS.mozilla.gfx += [
'CompositorHitTestInfo.h',
'TiledRegion.h',
]

View File

@ -22,6 +22,7 @@
#include "nsCOMPtr.h"
#include "nsFrameList.h"
#include "nsPlaceholderFrame.h"
#include "nsPluginFrame.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "nsContentUtils.h"
@ -11182,6 +11183,80 @@ nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const
}
}
CompositorHitTestInfo
nsIFrame::GetCompositorHitTestInfo(nsDisplayListBuilder* aBuilder)
{
CompositorHitTestInfo result = CompositorHitTestInfo::eInvisibleToHitTest;
if (aBuilder->IsInsidePointerEventsNoneDoc()) {
// Somewhere up the parent document chain is a subdocument with pointer-
// events:none set on it.
return result;
}
if (!GetParent()) {
MOZ_ASSERT(IsViewportFrame());
// Viewport frames are never event targets, other frames, like canvas frames,
// are the event targets for any regions viewport frames may cover.
return result;
}
uint8_t pointerEvents = StyleUserInterface()->GetEffectivePointerEvents(this);
if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) {
return result;
}
if (!StyleVisibility()->IsVisible()) {
return result;
}
// Anything that didn't match the above conditions is visible to hit-testing.
result |= CompositorHitTestInfo::eVisibleToHitTest;
if (aBuilder->IsBuildingNonLayerizedScrollbar() ||
aBuilder->GetAncestorHasApzAwareEventHandler()) {
// 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 |= CompositorHitTestInfo::eDispatchToContent;
} 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 |= CompositorHitTestInfo::eDispatchToContent;
}
}
nsIFrame* touchActionFrame = this;
if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this)) {
touchActionFrame = do_QueryFrame(scrollFrame);
}
uint32_t touchAction = nsLayoutUtils::GetTouchActionFromFrame(touchActionFrame);
// The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
// so we can eliminate some combinations of things.
if (touchAction == NS_STYLE_TOUCH_ACTION_AUTO) {
// nothing to do
} else if (touchAction & NS_STYLE_TOUCH_ACTION_MANIPULATION) {
result |= CompositorHitTestInfo::eTouchActionDoubleTapZoomDisabled;
} else {
if (!(touchAction & NS_STYLE_TOUCH_ACTION_PAN_X)) {
result |= CompositorHitTestInfo::eTouchActionPanXDisabled;
}
if (!(touchAction & NS_STYLE_TOUCH_ACTION_PAN_Y)) {
result |= CompositorHitTestInfo::eTouchActionPanYDisabled;
}
if (touchAction & NS_STYLE_TOUCH_ACTION_NONE) {
result |= CompositorHitTestInfo::eTouchActionPinchZoomDisabled
| CompositorHitTestInfo::eTouchActionDoubleTapZoomDisabled;
// pan-x and pan-y disabled flags will already have been set above
MOZ_ASSERT(result & CompositorHitTestInfo::eTouchActionPanXDisabled);
MOZ_ASSERT(result & CompositorHitTestInfo::eTouchActionPanYDisabled);
}
}
return result;
}
// Box layout debugging
#ifdef DEBUG_REFLOW
int32_t gIndent2 = 0;

View File

@ -43,6 +43,7 @@
#include "Visibility.h"
#include "nsChangeHint.h"
#include "nsStyleContextInlines.h"
#include "mozilla/gfx/CompositorHitTestInfo.h"
#include "mozilla/gfx/MatrixFwd.h"
#include "nsDisplayItemTypes.h"
@ -4145,6 +4146,8 @@ public:
bool BuiltBlendContainer() { return mBuiltBlendContainer; }
void SetBuiltBlendContainer(bool aBuilt) { mBuiltBlendContainer = aBuilt; }
mozilla::gfx::CompositorHitTestInfo GetCompositorHitTestInfo(nsDisplayListBuilder* aBuilder);
protected:
static void DestroyAnonymousContent(nsPresContext* aPresContext,
already_AddRefed<nsIContent>&& aContent);

View File

@ -82,7 +82,6 @@
#include "nsDOMTokenList.h"
#include "mozilla/RuleNodeCacheConditions.h"
#include "nsCSSProps.h"
#include "nsPluginFrame.h"
#include "nsSVGMaskFrame.h"
#include "nsTableCellFrame.h"
#include "nsTableColFrame.h"
@ -4838,30 +4837,11 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
{
NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame),
"Reference frame mismatch");
if (aBuilder->IsInsidePointerEventsNoneDoc()) {
// Somewhere up the parent document chain is a subdocument with pointer-
// events:none set on it.
CompositorHitTestInfo hitInfo =
aFrame->GetCompositorHitTestInfo(aBuilder);
if (hitInfo == CompositorHitTestInfo::eInvisibleToHitTest) {
return;
}
if (!aFrame->GetParent()) {
MOZ_ASSERT(aFrame->IsViewportFrame());
// Viewport frames are never event targets, other frames, like canvas frames,
// are the event targets for any regions viewport frames may cover.
return;
}
uint8_t pointerEvents =
aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame);
if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) {
return;
}
bool simpleRegions = aFrame->HasAnyStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
if (!simpleRegions) {
if (!aFrame->StyleVisibility()->IsVisible()) {
return;
}
}
// XXX handle other pointerEvents values for SVG
@ -4892,6 +4872,11 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
borderBox += aBuilder->ToReferenceFrame(aFrame);
bool borderBoxHasRoundedCorners = false;
// use the NS_FRAME_SIMPLE_EVENT_REGIONS to avoid calling the slightly
// expensive HasNonZeroCorner function if we know from a previous run that
// the frame has zero corners.
bool simpleRegions = aFrame->HasAnyStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
if (!simpleRegions) {
if (nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius)) {
borderBoxHasRoundedCorners = true;
@ -4917,39 +4902,25 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
mHitRegion.Add(aFrame, borderBox);
}
if (aBuilder->IsBuildingNonLayerizedScrollbar() ||
aBuilder->GetAncestorHasApzAwareEventHandler())
{
// 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.
if (hitInfo & CompositorHitTestInfo::eDispatchToContent) {
mDispatchToContentHitRegion.Add(aFrame, borderBox);
} else if (aFrame->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(aFrame);
if (pluginFrame && pluginFrame->WantsToHandleWheelEventAsDefaultAction()) {
mDispatchToContentHitRegion.Add(aFrame, borderBox);
}
}
// Touch action region
nsIFrame* touchActionFrame = aFrame;
if (scrollFrame) {
touchActionFrame = do_QueryFrame(scrollFrame);
}
uint32_t touchAction = nsLayoutUtils::GetTouchActionFromFrame(touchActionFrame);
if (touchAction != NS_STYLE_TOUCH_ACTION_AUTO) {
if (touchAction & NS_STYLE_TOUCH_ACTION_NONE) {
auto touchFlags = hitInfo & CompositorHitTestInfo::eTouchActionMask;
if (touchFlags) {
// something was disabled
if (touchFlags == CompositorHitTestInfo::eTouchActionMask) {
// everything was disabled, so touch-action:none
mNoActionRegion.Add(aFrame, borderBox);
} else {
if ((touchAction & NS_STYLE_TOUCH_ACTION_PAN_X)) {
if (!(hitInfo & CompositorHitTestInfo::eTouchActionPanXDisabled)) {
// pan-x is allowed
mHorizontalPanRegion.Add(aFrame, borderBox);
}
if ((touchAction & NS_STYLE_TOUCH_ACTION_PAN_Y)) {
if (!(hitInfo & CompositorHitTestInfo::eTouchActionPanYDisabled)) {
// pan-y is allowed
mVerticalPanRegion.Add(aFrame, borderBox);
}
}