TouchActionHelper.cpp (5314B)
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 "TouchActionHelper.h" 8 9 #include "mozilla/layers/IAPZCTreeManager.h" 10 #include "mozilla/PresShell.h" 11 #include "mozilla/ScrollContainerFrame.h" 12 #include "mozilla/TouchEvents.h" 13 #include "nsContainerFrame.h" 14 #include "nsIFrameInlines.h" 15 #include "nsLayoutUtils.h" 16 17 namespace mozilla::layers { 18 19 static void UpdateAllowedBehavior(StyleTouchAction aTouchActionValue, 20 bool aConsiderPanning, 21 TouchBehaviorFlags& aOutBehavior) { 22 if (aTouchActionValue != StyleTouchAction::AUTO) { 23 // Double-tap-zooming need property value AUTO 24 aOutBehavior &= ~AllowedTouchBehavior::ANIMATING_ZOOM; 25 if (aTouchActionValue != StyleTouchAction::MANIPULATION && 26 !(aTouchActionValue & StyleTouchAction::PINCH_ZOOM)) { 27 // Pinch-zooming needs value AUTO or MANIPULATION, or the PINCH_ZOOM bit 28 // set 29 aOutBehavior &= ~AllowedTouchBehavior::PINCH_ZOOM; 30 } 31 } 32 33 if (aConsiderPanning) { 34 if (aTouchActionValue == StyleTouchAction::NONE) { 35 aOutBehavior &= ~AllowedTouchBehavior::VERTICAL_PAN; 36 aOutBehavior &= ~AllowedTouchBehavior::HORIZONTAL_PAN; 37 } 38 39 // Values pan-x and pan-y set at the same time to the same element do not 40 // affect panning constraints. Therefore we need to check whether pan-x is 41 // set without pan-y and the same for pan-y. 42 if ((aTouchActionValue & StyleTouchAction::PAN_X) && 43 !(aTouchActionValue & StyleTouchAction::PAN_Y)) { 44 aOutBehavior &= ~AllowedTouchBehavior::VERTICAL_PAN; 45 } else if ((aTouchActionValue & StyleTouchAction::PAN_Y) && 46 !(aTouchActionValue & StyleTouchAction::PAN_X)) { 47 aOutBehavior &= ~AllowedTouchBehavior::HORIZONTAL_PAN; 48 } 49 } 50 } 51 52 static TouchBehaviorFlags GetAllowedTouchBehaviorForPoint( 53 nsIWidget* aWidget, RelativeTo aRootFrame, 54 const LayoutDeviceIntPoint& aPoint) { 55 nsPoint relativePoint = 56 nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aPoint, aRootFrame); 57 58 nsIFrame* target = nsLayoutUtils::GetFrameForPoint(aRootFrame, relativePoint); 59 60 return TouchActionHelper::GetAllowedTouchBehaviorForFrame(target); 61 } 62 63 nsTArray<TouchBehaviorFlags> TouchActionHelper::GetAllowedTouchBehavior( 64 nsIWidget* aWidget, dom::Document* aDocument, 65 const WidgetTouchEvent& aEvent) { 66 nsTArray<TouchBehaviorFlags> flags; 67 if (!aWidget || !aDocument) { 68 return flags; 69 } 70 if (PresShell* presShell = aDocument->GetPresShell()) { 71 if (nsIFrame* rootFrame = presShell->GetRootFrame()) { 72 for (const auto& touch : aEvent.mTouches) { 73 flags.AppendElement(GetAllowedTouchBehaviorForPoint( 74 aWidget, RelativeTo{rootFrame, ViewportType::Visual}, 75 touch->mRefPoint)); 76 } 77 } 78 } 79 return flags; 80 } 81 82 TouchBehaviorFlags TouchActionHelper::GetAllowedTouchBehaviorForFrame( 83 nsIFrame* aFrame) { 84 TouchBehaviorFlags behavior = AllowedTouchBehavior::VERTICAL_PAN | 85 AllowedTouchBehavior::HORIZONTAL_PAN | 86 AllowedTouchBehavior::PINCH_ZOOM | 87 AllowedTouchBehavior::ANIMATING_ZOOM; 88 89 if (!aFrame) { 90 return behavior; 91 } 92 93 nsIFrame* nearestScrollContainerFrame = 94 nsLayoutUtils::GetNearestScrollContainerFrame(aFrame, 0); 95 96 // We're walking up the DOM tree until we meet the element with touch behavior 97 // and accumulating touch-action restrictions of all elements in this chain. 98 // The exact quote from the spec, that clarifies more: 99 // To determine the effect of a touch, find the nearest ancestor (starting 100 // from the element itself) that has a default touch behavior. Then examine 101 // the touch-action property of each element between the hit tested element 102 // and the element with the default touch behavior (including both the hit 103 // tested element and the element with the default touch behavior). If the 104 // touch-action property of any of those elements disallows the default touch 105 // behavior, do nothing. Otherwise allow the element to start considering the 106 // touch for the purposes of executing a default touch behavior. 107 108 // Currently we support only two touch behaviors: panning and zooming. 109 // For panning we walk up until we meet the first scrollable element (the 110 // element that supports panning) or root element. For zooming we walk up 111 // until the root element since Firefox currently supports only zooming of the 112 // root frame but not the subframes. 113 114 bool considerPanning = true; 115 116 for (nsIFrame* frame = aFrame; frame && frame->GetContent() && behavior; 117 frame = frame->GetInFlowParent()) { 118 UpdateAllowedBehavior(frame->UsedTouchAction(), considerPanning, behavior); 119 120 if (frame == nearestScrollContainerFrame) { 121 // We met the scrollable element, after it we shouldn't consider 122 // touch-action values for the purpose of panning but only for zooming. 123 considerPanning = false; 124 } 125 } 126 127 return behavior; 128 } 129 130 } // namespace mozilla::layers