diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 76c0f65c1a9..2ff02e1834e 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -912,12 +912,18 @@ struct ParamTraits { WriteParam(aMsg, aParam.mHitRegion); WriteParam(aMsg, aParam.mDispatchToContentHitRegion); + WriteParam(aMsg, aParam.mNoActionRegion); + WriteParam(aMsg, aParam.mHorizontalPanRegion); + WriteParam(aMsg, aParam.mVerticalPanRegion); } static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { return (ReadParam(aMsg, aIter, &aResult->mHitRegion) && - ReadParam(aMsg, aIter, &aResult->mDispatchToContentHitRegion)); + ReadParam(aMsg, aIter, &aResult->mDispatchToContentHitRegion) && + ReadParam(aMsg, aIter, &aResult->mNoActionRegion) && + ReadParam(aMsg, aIter, &aResult->mHorizontalPanRegion) && + ReadParam(aMsg, aIter, &aResult->mVerticalPanRegion)); } }; diff --git a/gfx/layers/LayersLogging.cpp b/gfx/layers/LayersLogging.cpp index 07c77280f02..f7241d06d0d 100644 --- a/gfx/layers/LayersLogging.cpp +++ b/gfx/layers/LayersLogging.cpp @@ -152,6 +152,15 @@ AppendToString(std::stringstream& aStream, const EventRegions& e, if (!e.mDispatchToContentHitRegion.IsEmpty()) { AppendToString(aStream, e.mDispatchToContentHitRegion, " dispatchtocontentregion=", ""); } + if (!e.mNoActionRegion.IsEmpty()) { + AppendToString(aStream, e.mNoActionRegion, " NoActionRegion=",""); + } + if (!e.mHorizontalPanRegion.IsEmpty()) { + AppendToString(aStream, e.mHorizontalPanRegion, " HorizontalPanRegion=", ""); + } + if (!e.mVerticalPanRegion.IsEmpty()) { + AppendToString(aStream, e.mVerticalPanRegion, " VerticalPanRegion=", ""); + } aStream << "}" << sfx; } diff --git a/gfx/layers/LayersTypes.h b/gfx/layers/LayersTypes.h index 11153c7cb4f..8b6af63cac8 100644 --- a/gfx/layers/LayersTypes.h +++ b/gfx/layers/LayersTypes.h @@ -158,9 +158,24 @@ enum class ScaleMode : int8_t { }; struct EventRegions { + // The hit region for a layer contains all areas on the layer that are + // sensitive to events. This region is an over-approximation and may + // contain regions that are not actually sensitive, but any such regions + // will be included in the mDispatchToContentHitRegion. nsIntRegion mHitRegion; + // The mDispatchToContentHitRegion for a layer contains all areas for + // which the main-thread must be consulted before responding to events. + // This region will be a subregion of mHitRegion. nsIntRegion mDispatchToContentHitRegion; + // The following regions represent the touch-action areas of this layer. + // All of these regions are approximations to the true region, but any + // variance between the approximation and the true region is guaranteed + // to be included in the mDispatchToContentHitRegion. + nsIntRegion mNoActionRegion; + nsIntRegion mHorizontalPanRegion; + nsIntRegion mVerticalPanRegion; + EventRegions() { } diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index cd2458061a0..8182c7256ca 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -385,6 +385,9 @@ public: mHitRegion.Or(mHitRegion, aEventRegions->HitRegion()); mMaybeHitRegion.Or(mMaybeHitRegion, aEventRegions->MaybeHitRegion()); mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aEventRegions->DispatchToContentHitRegion()); + mNoActionRegion.Or(mNoActionRegion, aEventRegions->NoActionRegion()); + mHorizontalPanRegion.Or(mHorizontalPanRegion, aEventRegions->HorizontalPanRegion()); + mVerticalPanRegion.Or(mVerticalPanRegion, aEventRegions->VerticalPanRegion()); } /** @@ -433,6 +436,27 @@ public: * The dispatch-to-content hit region for this PaintedLayer. */ nsRegion mDispatchToContentHitRegion; + /** + * The region for this PaintedLayer that is sensitive to events + * but disallows panning and zooming. This is an approximation + * and any deviation from the true region will be part of the + * mDispatchToContentHitRegion. + */ + nsRegion mNoActionRegion; + /** + * The region for this PaintedLayer that is sensitive to events and + * allows horizontal panning but not zooming. This is an approximation + * and any deviation from the true region will be part of the + * mDispatchToContentHitRegion. + */ + nsRegion mHorizontalPanRegion; + /** + * The region for this PaintedLayer that is sensitive to events and + * allows vertical panning but not zooming. This is an approximation + * and any deviation from the true region will be part of the + * mDispatchToContentHitRegion. + */ + nsRegion mVerticalPanRegion; /** * The "active scrolled root" for all content in the layer. Must * be non-null; all content in a PaintedLayer must have the same @@ -2409,24 +2433,38 @@ ContainerState::PopPaintedLayerData() containingPaintedLayerData->mMaybeHitRegion.Or( containingPaintedLayerData->mMaybeHitRegion, rect); } - if (!data->mHitRegion.GetBounds().IsEmpty()) { - // Our definitely-hit region must go to the maybe-hit-region since - // this function is an approximation. - Matrix4x4 matrix = nsLayoutUtils::GetTransformToAncestor( - mContainerReferenceFrame, containingPaintedLayerData->mReferenceFrame); - Matrix matrix2D; - bool isPrecise = matrix.Is2D(&matrix2D) && !matrix2D.HasNonAxisAlignedTransform(); - nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor( - mContainerReferenceFrame, - data->mHitRegion.GetBounds(), - containingPaintedLayerData->mReferenceFrame); - nsRegion* dest = isPrecise ? &containingPaintedLayerData->mHitRegion - : &containingPaintedLayerData->mMaybeHitRegion; - dest->Or(*dest, rect); - } + nsLayoutUtils::TransformToAncestorAndCombineRegions( + data->mHitRegion.GetBounds(), + mContainerReferenceFrame, + containingPaintedLayerData->mReferenceFrame, + &containingPaintedLayerData->mHitRegion, + &containingPaintedLayerData->mMaybeHitRegion); + nsLayoutUtils::TransformToAncestorAndCombineRegions( + data->mNoActionRegion.GetBounds(), + mContainerReferenceFrame, + containingPaintedLayerData->mReferenceFrame, + &containingPaintedLayerData->mNoActionRegion, + &containingPaintedLayerData->mDispatchToContentHitRegion); + nsLayoutUtils::TransformToAncestorAndCombineRegions( + data->mHorizontalPanRegion.GetBounds(), + mContainerReferenceFrame, + containingPaintedLayerData->mReferenceFrame, + &containingPaintedLayerData->mHorizontalPanRegion, + &containingPaintedLayerData->mDispatchToContentHitRegion); + nsLayoutUtils::TransformToAncestorAndCombineRegions( + data->mVerticalPanRegion.GetBounds(), + mContainerReferenceFrame, + containingPaintedLayerData->mReferenceFrame, + &containingPaintedLayerData->mVerticalPanRegion, + &containingPaintedLayerData->mDispatchToContentHitRegion); + } else { EventRegions regions; regions.mHitRegion = ScaleRegionToOutsidePixels(data->mHitRegion); + regions.mNoActionRegion = ScaleRegionToOutsidePixels(data->mNoActionRegion); + regions.mHorizontalPanRegion = ScaleRegionToOutsidePixels(data->mHorizontalPanRegion); + regions.mVerticalPanRegion = ScaleRegionToOutsidePixels(data->mVerticalPanRegion); + // Points whose hit-region status we're not sure about need to be dispatched // to the content thread. If a point is in both maybeHitRegion and hitRegion // then it's not a "maybe" any more, and doesn't go into the dispatch-to- @@ -2440,6 +2478,9 @@ ContainerState::PopPaintedLayerData() nsIntPoint translation = -GetTranslationForPaintedLayer(data->mLayer); regions.mHitRegion.MoveBy(translation); regions.mDispatchToContentHitRegion.MoveBy(translation); + regions.mNoActionRegion.MoveBy(translation); + regions.mHorizontalPanRegion.MoveBy(translation); + regions.mVerticalPanRegion.MoveBy(translation); layer->SetEventRegions(regions); } diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index e149981fdb8..5c61969b1cb 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -3229,6 +3229,20 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder, if (aBuilder->GetAncestorHasApzAwareEventHandler()) { mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox); } + + // Touch action region + + uint32_t touchAction = nsLayoutUtils::GetTouchActionFromFrame(aFrame); + if (touchAction & NS_STYLE_TOUCH_ACTION_NONE) { + mNoActionRegion.Or(mNoActionRegion, borderBox); + } else { + if ((touchAction & NS_STYLE_TOUCH_ACTION_PAN_X)) { + mHorizontalPanRegion.Or(mHorizontalPanRegion, borderBox); + } + if ((touchAction & NS_STYLE_TOUCH_ACTION_PAN_Y)) { + mVerticalPanRegion.Or(mVerticalPanRegion, borderBox); + } + } } void diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index d9c3a03c853..4c95b92c1cd 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -2711,6 +2711,9 @@ public: const nsRegion& HitRegion() { return mHitRegion; } const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; } const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; } + const nsRegion& NoActionRegion() { return mNoActionRegion; } + const nsRegion& HorizontalPanRegion() { return mHorizontalPanRegion; } + const nsRegion& VerticalPanRegion() { return mVerticalPanRegion; } virtual void WriteDebugInfo(std::stringstream& aStream) override; @@ -2724,6 +2727,15 @@ private: // These are points that need to be dispatched to the content thread for // resolution. Always contained in the union of mHitRegion and mMaybeHitRegion. nsRegion mDispatchToContentHitRegion; + // These are points where panning is disabled, as determined by the touch-action + // property. Always contained in the union of mHitRegion and mMaybeHitRegion. + nsRegion mNoActionRegion; + // These are points where panning is horizontal, as determined by the touch-action + // property. Always contained in the union of mHitRegion and mMaybeHitRegion. + nsRegion mHorizontalPanRegion; + // These are points where panning is vertical, as determined by the touch-action + // property. Always contained in the union of mHitRegion and mMaybeHitRegion. + nsRegion mVerticalPanRegion; }; /** diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 3bc3f243153..5d5144db741 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8005,3 +8005,50 @@ nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(nsIPresShell* aShell) } return false; } + +/* static */ uint32_t +nsLayoutUtils::GetTouchActionFromFrame(nsIFrame* aFrame) +{ + // If aFrame is null then return default value + if (!aFrame) { + return NS_STYLE_TOUCH_ACTION_AUTO; + } + + // The touch-action CSS property applies to: all elements except: + // non-replaced inline elements, table rows, row groups, table columns, and column groups + bool isNonReplacedInlineElement = aFrame->IsFrameOfType(nsIFrame::eLineParticipant); + if (isNonReplacedInlineElement) { + return NS_STYLE_TOUCH_ACTION_AUTO; + } + + const nsStyleDisplay* disp = aFrame->StyleDisplay(); + bool isTableElement = disp->IsInnerTableStyle() && + disp->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL && + disp->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION; + if (isTableElement) { + return NS_STYLE_TOUCH_ACTION_AUTO; + } + + return disp->mTouchAction; +} + +/* static */ void +nsLayoutUtils::TransformToAncestorAndCombineRegions( + const nsRect& aBounds, + nsIFrame* aFrame, + const nsIFrame* aAncestorFrame, + nsRegion* aPreciseTargetDest, + nsRegion* aImpreciseTargetDest) +{ + if (aBounds.IsEmpty()) { + return; + } + Matrix4x4 matrix = GetTransformToAncestor(aFrame, aAncestorFrame); + Matrix matrix2D; + bool isPrecise = (matrix.Is2D(&matrix2D) + && !matrix2D.HasNonAxisAlignedTransform()); + nsRect transformed = TransformFrameRectToAncestor( + aFrame, aBounds, aAncestorFrame); + nsRegion* dest = isPrecise ? aPreciseTargetDest : aImpreciseTargetDest; + dest->OrWith(transformed); +} diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 5c91aac75b8..48b8ead6433 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -2417,6 +2417,25 @@ public: AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot); #endif + /** + * Helper method to get touch action behaviour from the frame + */ + static uint32_t + GetTouchActionFromFrame(nsIFrame* aFrame); + + /** + * Helper method to transform |aBounds| from aFrame to aAncestorFrame, + * and combine it with |aPreciseTargetDest| if it is axis-aligned, or + * combine it with |aImpreciseTargetDest| if not. + */ + static void + TransformToAncestorAndCombineRegions( + const nsRect& aBounds, + nsIFrame* aFrame, + const nsIFrame* aAncestorFrame, + nsRegion* aPreciseTargetDest, + nsRegion* aImpreciseTargetDest); + /** * Determine if aImageFrame (which is an nsImageFrame, nsImageControlFrame, or * nsSVGImageFrame) is visible or close to being visible via scrolling and diff --git a/widget/ContentHelper.cpp b/widget/ContentHelper.cpp index a1025079e96..12e14a35780 100644 --- a/widget/ContentHelper.cpp +++ b/widget/ContentHelper.cpp @@ -15,32 +15,6 @@ namespace mozilla { namespace widget { -uint32_t -ContentHelper::GetTouchActionFromFrame(nsIFrame* aFrame) -{ - // If aFrame is null then return default value - if (!aFrame) { - return NS_STYLE_TOUCH_ACTION_AUTO; - } - - // The touch-action CSS property applies to: all elements except: - // non-replaced inline elements, table rows, row groups, table columns, and column groups - bool isNonReplacedInlineElement = aFrame->IsFrameOfType(nsIFrame::eLineParticipant); - if (isNonReplacedInlineElement) { - return NS_STYLE_TOUCH_ACTION_AUTO; - } - - const nsStyleDisplay* disp = aFrame->StyleDisplay(); - bool isTableElement = disp->IsInnerTableStyle() && - disp->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL && - disp->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION; - if (isTableElement) { - return NS_STYLE_TOUCH_ACTION_AUTO; - } - - return disp->mTouchAction; -} - void ContentHelper::UpdateAllowedBehavior(uint32_t aTouchActionValue, bool aConsiderPanning, TouchBehaviorFlags& aOutBehavior) { @@ -103,7 +77,7 @@ ContentHelper::GetAllowedTouchBehavior(nsIWidget* aWidget, const LayoutDeviceInt AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::DOUBLE_TAP_ZOOM; for (nsIFrame *frame = target; frame && frame->GetContent() && behavior; frame = frame->GetParent()) { - UpdateAllowedBehavior(GetTouchActionFromFrame(frame), considerPanning, behavior); + UpdateAllowedBehavior(nsLayoutUtils::GetTouchActionFromFrame(frame), considerPanning, behavior); if (frame == nearestScrollableFrame) { // We met the scrollable element, after it we shouldn't consider touch-action diff --git a/widget/ContentHelper.h b/widget/ContentHelper.h index fbef2ed79df..5c454e14bf3 100644 --- a/widget/ContentHelper.h +++ b/widget/ContentHelper.h @@ -22,7 +22,6 @@ class ContentHelper typedef uint32_t TouchBehaviorFlags; private: - static uint32_t GetTouchActionFromFrame(nsIFrame* aFrame); static void UpdateAllowedBehavior(uint32_t aTouchActionValue, bool aConsiderPanning, TouchBehaviorFlags& aOutBehavior); public: