summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/APZInputBridge.cpp
blob: 3472c497b5bfca68349a5ead9c7668ce0d4ff552 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* -*- 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 "mozilla/layers/APZInputBridge.h"

#include "InputData.h"                      // for MouseInput, etc
#include "InputBlockState.h"                // for InputBlockState
#include "mozilla/dom/WheelEventBinding.h"  // for WheelEvent constants
#include "mozilla/EventStateManager.h"      // for EventStateManager
#include "mozilla/layers/APZThreadUtils.h"  // for AssertOnControllerThread, etc
#include "mozilla/MouseEvents.h"            // for WidgetMouseEvent
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_general.h"
#include "mozilla/StaticPrefs_test.h"
#include "mozilla/TextEvents.h"           // for WidgetKeyboardEvent
#include "mozilla/TouchEvents.h"          // for WidgetTouchEvent
#include "mozilla/WheelHandlingHelper.h"  // for WheelDeltaHorizontalizer,
                                          //     WheelDeltaAdjustmentStrategy

namespace mozilla {
namespace layers {

APZEventResult::APZEventResult()
    : mStatus(nsEventStatus_eIgnore),
      mInputBlockId(InputBlockState::NO_BLOCK_ID) {}

static bool WillHandleMouseEvent(const WidgetMouseEventBase& aEvent) {
  return aEvent.mMessage == eMouseMove || aEvent.mMessage == eMouseDown ||
         aEvent.mMessage == eMouseUp || aEvent.mMessage == eDragEnd ||
         (StaticPrefs::test_events_async_enabled() &&
          aEvent.mMessage == eMouseHitTest);
}

/* static */
Maybe<APZWheelAction> APZInputBridge::ActionForWheelEvent(
    WidgetWheelEvent* aEvent) {
  if (!(aEvent->mDeltaMode == dom::WheelEvent_Binding::DOM_DELTA_LINE ||
        aEvent->mDeltaMode == dom::WheelEvent_Binding::DOM_DELTA_PIXEL ||
        aEvent->mDeltaMode == dom::WheelEvent_Binding::DOM_DELTA_PAGE)) {
    return Nothing();
  }
  return EventStateManager::APZWheelActionFor(aEvent);
}

APZEventResult APZInputBridge::ReceiveInputEvent(WidgetInputEvent& aEvent) {
  APZThreadUtils::AssertOnControllerThread();

  APZEventResult result;

  switch (aEvent.mClass) {
    case eMouseEventClass:
    case eDragEventClass: {
      WidgetMouseEvent& mouseEvent = *aEvent.AsMouseEvent();

      // Note, we call this before having transformed the reference point.
      if (mouseEvent.IsReal()) {
        UpdateWheelTransaction(mouseEvent.mRefPoint, mouseEvent.mMessage);
      }

      if (WillHandleMouseEvent(mouseEvent)) {
        MouseInput input(mouseEvent);
        input.mOrigin =
            ScreenPoint(mouseEvent.mRefPoint.x, mouseEvent.mRefPoint.y);

        result = ReceiveInputEvent(input);

        mouseEvent.mRefPoint.x = input.mOrigin.x;
        mouseEvent.mRefPoint.y = input.mOrigin.y;
        mouseEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
        mouseEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
        aEvent.mLayersId = input.mLayersId;
        return result;
      }

      ProcessUnhandledEvent(&mouseEvent.mRefPoint, &result.mTargetGuid,
                            &aEvent.mFocusSequenceNumber, &aEvent.mLayersId);
      return result;
    }
    case eTouchEventClass: {
      WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
      MultiTouchInput touchInput(touchEvent);
      result = ReceiveInputEvent(touchInput);
      // touchInput was modified in-place to possibly remove some
      // touch points (if we are overscrolled), and the coordinates were
      // modified using the APZ untransform. We need to copy these changes
      // back into the WidgetInputEvent.
      touchEvent.mTouches.Clear();
      touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length());
      for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
        *touchEvent.mTouches.AppendElement() =
            touchInput.mTouches[i].ToNewDOMTouch();
      }
      touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
      touchEvent.mFocusSequenceNumber = touchInput.mFocusSequenceNumber;
      aEvent.mLayersId = touchInput.mLayersId;
      return result;
    }
    case eWheelEventClass: {
      WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();

      if (Maybe<APZWheelAction> action = ActionForWheelEvent(&wheelEvent)) {
        ScrollWheelInput::ScrollMode scrollMode =
            ScrollWheelInput::SCROLLMODE_INSTANT;
        if (StaticPrefs::general_smoothScroll() &&
            ((wheelEvent.mDeltaMode ==
                  dom::WheelEvent_Binding::DOM_DELTA_LINE &&
              StaticPrefs::general_smoothScroll_mouseWheel()) ||
             (wheelEvent.mDeltaMode ==
                  dom::WheelEvent_Binding::DOM_DELTA_PAGE &&
              StaticPrefs::general_smoothScroll_pages()))) {
          scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
        }

        WheelDeltaAdjustmentStrategy strategy =
            EventStateManager::GetWheelDeltaAdjustmentStrategy(wheelEvent);
        // Adjust the delta values of the wheel event if the current default
        // action is to horizontalize scrolling. I.e., deltaY values are set to
        // deltaX and deltaY and deltaZ values are set to 0.
        // If horizontalized, the delta values will be restored and its overflow
        // deltaX will become 0 when the WheelDeltaHorizontalizer instance is
        // being destroyed.
        WheelDeltaHorizontalizer horizontalizer(wheelEvent);
        if (WheelDeltaAdjustmentStrategy::eHorizontalize == strategy) {
          horizontalizer.Horizontalize();
        }

        // If the wheel event becomes no-op event, don't handle it as scroll.
        if (wheelEvent.mDeltaX || wheelEvent.mDeltaY) {
          ScreenPoint origin(wheelEvent.mRefPoint.x, wheelEvent.mRefPoint.y);
          ScrollWheelInput input(
              wheelEvent.mTime, wheelEvent.mTimeStamp, 0, scrollMode,
              ScrollWheelInput::DeltaTypeForDeltaMode(wheelEvent.mDeltaMode),
              origin, wheelEvent.mDeltaX, wheelEvent.mDeltaY,
              wheelEvent.mAllowToOverrideSystemScrollSpeed, strategy);
          input.mAPZAction = action.value();

          // We add the user multiplier as a separate field, rather than
          // premultiplying it, because if the input is converted back to a
          // WidgetWheelEvent, then EventStateManager would apply the delta a
          // second time. We could in theory work around this by asking ESM to
          // customize the event much sooner, and then save the
          // "mCustomizedByUserPrefs" bit on ScrollWheelInput - but for now,
          // this seems easier.
          EventStateManager::GetUserPrefsForWheelEvent(
              &wheelEvent, &input.mUserDeltaMultiplierX,
              &input.mUserDeltaMultiplierY);

          result = ReceiveInputEvent(input);
          wheelEvent.mRefPoint.x = input.mOrigin.x;
          wheelEvent.mRefPoint.y = input.mOrigin.y;
          wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
          wheelEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
          aEvent.mLayersId = input.mLayersId;

          return result;
        }
      }

      UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
      ProcessUnhandledEvent(&aEvent.mRefPoint, &result.mTargetGuid,
                            &aEvent.mFocusSequenceNumber, &aEvent.mLayersId);
      result.mStatus = nsEventStatus_eIgnore;
      return result;
    }
    case eKeyboardEventClass: {
      WidgetKeyboardEvent& keyboardEvent = *aEvent.AsKeyboardEvent();

      KeyboardInput input(keyboardEvent);

      result = ReceiveInputEvent(input);

      keyboardEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
      keyboardEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
      return result;
    }
    default: {
      UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
      ProcessUnhandledEvent(&aEvent.mRefPoint, &result.mTargetGuid,
                            &aEvent.mFocusSequenceNumber, &aEvent.mLayersId);
      return result;
    }
  }

  MOZ_ASSERT_UNREACHABLE("Invalid WidgetInputEvent type.");
  result.mStatus = nsEventStatus_eConsumeNoDefault;
  return result;
}

std::ostream& operator<<(std::ostream& aOut,
                         const APZHandledResult& aHandledResult) {
  switch (aHandledResult) {
    case APZHandledResult::Unhandled:
      aOut << "unhandled";
      break;
    case APZHandledResult::HandledByRoot: {
      aOut << "handled-by-root";
      break;
    }
    case APZHandledResult::HandledByContent: {
      aOut << "handled-by-content";
      break;
    }
    case APZHandledResult::Invalid: {
      aOut << "INVALID";
      break;
    }
  }
  return aOut;
}

}  // namespace layers
}  // namespace mozilla