widget/windows/winrt/MetroInput.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/windows/winrt/MetroInput.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1611 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// Moz headers (alphabetical)
    1.10 +#include "MetroInput.h"
    1.11 +#include "MetroUtils.h" // Logging, POINT_CEIL_*, ActivateGenericInstance, etc
    1.12 +#include "MetroWidget.h" // MetroInput::mWidget
    1.13 +#include "mozilla/dom/Touch.h"  // Touch
    1.14 +#include "nsTArray.h" // Touch lists
    1.15 +#include "nsIDOMSimpleGestureEvent.h" // Constants for gesture events
    1.16 +#include "InputData.h"
    1.17 +#include "UIABridgePrivate.h"
    1.18 +#include "MetroAppShell.h"
    1.19 +#include "mozilla/EventStateManager.h"
    1.20 +#include "mozilla/EventStates.h"
    1.21 +#include "mozilla/MouseEvents.h"
    1.22 +#include "mozilla/TouchEvents.h"
    1.23 +#include "mozilla/Preferences.h"  // for Preferences
    1.24 +#include "WinUtils.h"
    1.25 +#include "nsIPresShell.h"
    1.26 +
    1.27 +// System headers (alphabetical)
    1.28 +#include <windows.ui.core.h> // ABI::Window::UI::Core namespace
    1.29 +#include <windows.ui.input.h> // ABI::Window::UI::Input namespace
    1.30 +
    1.31 +//#define DEBUG_INPUT
    1.32 +
    1.33 +// Using declarations
    1.34 +using namespace ABI::Windows; // UI, System, Foundation namespaces
    1.35 +using namespace Microsoft; // WRL namespace (ComPtr, possibly others)
    1.36 +using namespace mozilla;
    1.37 +using namespace mozilla::widget;
    1.38 +using namespace mozilla::widget::winrt;
    1.39 +using namespace mozilla::dom;
    1.40 +
    1.41 +// File-scoped statics (unnamed namespace)
    1.42 +namespace {
    1.43 +  // XXX: Set these min values appropriately
    1.44 +  const double SWIPE_MIN_DISTANCE = 5.0;
    1.45 +  const double SWIPE_MIN_VELOCITY = 5.0;
    1.46 +
    1.47 +  // Convenience typedefs for event handler types
    1.48 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CEdgeGesture_Windows__CUI__CInput__CEdgeGestureEventArgs_t EdgeGestureHandler;
    1.49 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreDispatcher_Windows__CUI__CCore__CAcceleratorKeyEventArgs_t AcceleratorKeyActivatedHandler;
    1.50 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CPointerEventArgs_t PointerEventHandler;
    1.51 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CTappedEventArgs_t TappedEventHandler;
    1.52 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CRightTappedEventArgs_t RightTappedEventHandler;
    1.53 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationStartedEventArgs_t ManipulationStartedEventHandler;
    1.54 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationUpdatedEventArgs_t ManipulationUpdatedEventHandler;
    1.55 +  typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationCompletedEventArgs_t ManipulationCompletedEventHandler;
    1.56 +
    1.57 +  // Other convenience typedefs
    1.58 +  typedef ABI::Windows::UI::Core::ICoreAcceleratorKeys ICoreAcceleratorKeys;
    1.59 +
    1.60 +  /**
    1.61 +   * Specifies whether touch-action property is in force.
    1.62 +   */
    1.63 +  static bool gTouchActionPropertyEnabled = false;
    1.64 +
    1.65 +  /**
    1.66 +   * Creates and returns a new {@link Touch} from the given
    1.67 +   * ABI::Windows::UI::Input::IPointerPoint.  Note that the caller is
    1.68 +   * responsible for freeing the memory for the Touch returned from
    1.69 +   * this function.
    1.70 +   *
    1.71 +   * @param aPoint the ABI::Windows::UI::Input::IPointerPoint containing the
    1.72 +   *               metadata from which to create our new {@link Touch}
    1.73 +   * @return a new {@link Touch} representing the touch point. The caller
    1.74 +   *         is responsible for freeing the memory for this touch point.
    1.75 +   */
    1.76 +  Touch*
    1.77 +  CreateDOMTouch(UI::Input::IPointerPoint* aPoint) {
    1.78 +    WRL::ComPtr<UI::Input::IPointerPointProperties> props;
    1.79 +    Foundation::Point position;
    1.80 +    uint32_t pointerId;
    1.81 +    Foundation::Rect contactRect;
    1.82 +    float pressure;
    1.83 +    float tiltX;
    1.84 +    float tiltY;
    1.85 +
    1.86 +    aPoint->get_Properties(props.GetAddressOf());
    1.87 +    aPoint->get_Position(&position);
    1.88 +    aPoint->get_PointerId(&pointerId);
    1.89 +    props->get_ContactRect(&contactRect);
    1.90 +    props->get_Pressure(&pressure);
    1.91 +    props->get_XTilt(&tiltX);
    1.92 +    props->get_YTilt(&tiltY);
    1.93 +
    1.94 +    nsIntPoint touchPoint = MetroUtils::LogToPhys(position);
    1.95 +    nsIntPoint touchRadius;
    1.96 +    touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2;
    1.97 +    touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2;
    1.98 +    Touch* touch =
    1.99 +           new Touch(pointerId,
   1.100 +                     touchPoint,
   1.101 +                     // Rotation radius and angle.
   1.102 +                     // W3C touch events v1 do not use these.
   1.103 +                     // The draft for W3C touch events v2 explains that
   1.104 +                     // radius and angle should describe the ellipse that
   1.105 +                     // most closely circumscribes the touching area.  Since
   1.106 +                     // Windows gives us a bounding rectangle rather than an
   1.107 +                     // ellipse, we provide the ellipse that is most closely
   1.108 +                     // circumscribed by the bounding rectangle that Windows
   1.109 +                     // gave us.
   1.110 +                     touchRadius,
   1.111 +                     0.0f,
   1.112 +                     // Pressure
   1.113 +                     // W3C touch events v1 do not use this.
   1.114 +                     // The current draft for W3C touch events v2 says that
   1.115 +                     // this should be a value between 0.0 and 1.0, which is
   1.116 +                     // consistent with what Windows provides us here.
   1.117 +                     // XXX: Windows defaults to 0.5, but the current W3C
   1.118 +                     // draft says that the value should be 0.0 if no value
   1.119 +                     // known.
   1.120 +                     pressure);
   1.121 +    touch->tiltX = tiltX;
   1.122 +    touch->tiltY = tiltY;
   1.123 +    return touch;
   1.124 +  }
   1.125 +
   1.126 +  /**
   1.127 +   * Test if a touchpoint position has moved. See Touch.Equals for
   1.128 +   * criteria.
   1.129 +   *
   1.130 +   * @param aTouch previous touch point
   1.131 +   * @param aPoint new winrt touch point
   1.132 +   * @return true if the point has moved
   1.133 +   */
   1.134 +  bool
   1.135 +  HasPointMoved(Touch* aTouch, UI::Input::IPointerPoint* aPoint) {
   1.136 +    WRL::ComPtr<UI::Input::IPointerPointProperties> props;
   1.137 +    Foundation::Point position;
   1.138 +    Foundation::Rect contactRect;
   1.139 +    float pressure;
   1.140 +
   1.141 +    aPoint->get_Properties(props.GetAddressOf());
   1.142 +    aPoint->get_Position(&position);
   1.143 +    props->get_ContactRect(&contactRect);
   1.144 +    props->get_Pressure(&pressure);
   1.145 +    nsIntPoint touchPoint = MetroUtils::LogToPhys(position);
   1.146 +    nsIntPoint touchRadius;
   1.147 +    touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2;
   1.148 +    touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2;
   1.149 +
   1.150 +    // from Touch.Equals
   1.151 +    return touchPoint != aTouch->mRefPoint ||
   1.152 +           pressure != aTouch->Force() ||
   1.153 +           /* mRotationAngle == aTouch->RotationAngle() || */
   1.154 +           touchRadius.x != aTouch->RadiusX() ||
   1.155 +           touchRadius.y != aTouch->RadiusY();
   1.156 +  }
   1.157 +
   1.158 +  /**
   1.159 +   * Converts from the Devices::Input::PointerDeviceType enumeration
   1.160 +   * to a nsIDOMMouseEvent::MOZ_SOURCE_* value.
   1.161 +   *
   1.162 +   * @param aDeviceType the value to convert
   1.163 +   * @param aMozInputSource the converted value
   1.164 +   */
   1.165 +  void
   1.166 +  MozInputSourceFromDeviceType(
   1.167 +              Devices::Input::PointerDeviceType const& aDeviceType,
   1.168 +              unsigned short& aMozInputSource) {
   1.169 +    if (Devices::Input::PointerDeviceType::PointerDeviceType_Mouse
   1.170 +                  == aDeviceType) {
   1.171 +      aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
   1.172 +    } else if (Devices::Input::PointerDeviceType::PointerDeviceType_Touch
   1.173 +                  == aDeviceType) {
   1.174 +      aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   1.175 +    } else if (Devices::Input::PointerDeviceType::PointerDeviceType_Pen
   1.176 +                  == aDeviceType) {
   1.177 +      aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_PEN;
   1.178 +    }
   1.179 +  }
   1.180 +
   1.181 +  int16_t
   1.182 +  ButtonsForPointerPoint(UI::Input::IPointerPoint* aPoint) {
   1.183 +    WRL::ComPtr<UI::Input::IPointerPointProperties> props;
   1.184 +    aPoint->get_Properties(props.GetAddressOf());
   1.185 +
   1.186 +    int16_t buttons = 0;
   1.187 +    boolean buttonPressed;
   1.188 +
   1.189 +    props->get_IsLeftButtonPressed(&buttonPressed);
   1.190 +    if (buttonPressed) {
   1.191 +      buttons |= WidgetMouseEvent::eLeftButtonFlag;
   1.192 +    }
   1.193 +    props->get_IsMiddleButtonPressed(&buttonPressed);
   1.194 +    if (buttonPressed) {
   1.195 +      buttons |= WidgetMouseEvent::eMiddleButtonFlag;
   1.196 +    }
   1.197 +    props->get_IsRightButtonPressed(&buttonPressed);
   1.198 +    if (buttonPressed) {
   1.199 +      buttons |= WidgetMouseEvent::eRightButtonFlag;
   1.200 +    }
   1.201 +    props->get_IsXButton1Pressed(&buttonPressed);
   1.202 +    if (buttonPressed) {
   1.203 +      buttons |= WidgetMouseEvent::e4thButtonFlag;
   1.204 +    }
   1.205 +    props->get_IsXButton2Pressed(&buttonPressed);
   1.206 +    if (buttonPressed) {
   1.207 +      buttons |= WidgetMouseEvent::e5thButtonFlag;
   1.208 +    }
   1.209 +    return buttons;
   1.210 +  }
   1.211 +
   1.212 +  /**
   1.213 +   * This function is for use with mTouches.Enumerate.  It will
   1.214 +   * append each element it encounters to the {@link nsTArray}
   1.215 +   * of {@link mozilla::dom::Touch}es passed in through the third (void*)
   1.216 +   * parameter.
   1.217 +   *
   1.218 +   * NOTE: This function will set the `mChanged` member of each
   1.219 +   * element it encounters to `false`, since this function is only
   1.220 +   * used to populate a touchlist that is about to be dispatched
   1.221 +   * in a gecko touch event.
   1.222 +   *
   1.223 +   * @param aKey the key of the current element being enumerated
   1.224 +   * @param aData the value of the current element being enumerated
   1.225 +   * @param aTouchList the {@link nsTArray} to append to
   1.226 +   */
   1.227 +  PLDHashOperator
   1.228 +  AppendToTouchList(const unsigned int& aKey,
   1.229 +                    nsRefPtr<Touch>& aData,
   1.230 +                    void *aTouchList)
   1.231 +  {
   1.232 +    nsTArray<nsRefPtr<Touch> > *touches =
   1.233 +              static_cast<nsTArray<nsRefPtr<Touch> > *>(aTouchList);
   1.234 +    nsRefPtr<Touch> copy = new Touch(aData->mIdentifier,
   1.235 +               aData->mRefPoint,
   1.236 +               aData->mRadius,
   1.237 +               aData->mRotationAngle,
   1.238 +               aData->mForce);
   1.239 +    copy->tiltX = aData->tiltX;
   1.240 +    copy->tiltY = aData->tiltY;
   1.241 +    touches->AppendElement(copy);
   1.242 +    aData->mChanged = false;
   1.243 +    return PL_DHASH_NEXT;
   1.244 +  }
   1.245 +
   1.246 +  // Helper for making sure event ptrs get freed.
   1.247 +  class AutoDeleteEvent
   1.248 +  {
   1.249 +  public:
   1.250 +    AutoDeleteEvent(WidgetGUIEvent* aPtr) :
   1.251 +      mPtr(aPtr) {}
   1.252 +    ~AutoDeleteEvent() {
   1.253 +      if (mPtr) {
   1.254 +        delete mPtr;
   1.255 +      }
   1.256 +    }
   1.257 +    WidgetGUIEvent* mPtr;
   1.258 +  };
   1.259 +}
   1.260 +
   1.261 +namespace mozilla {
   1.262 +namespace widget {
   1.263 +namespace winrt {
   1.264 +
   1.265 +MetroInput::InputPrecisionLevel MetroInput::sCurrentInputLevel =
   1.266 +  MetroInput::InputPrecisionLevel::LEVEL_IMPRECISE;
   1.267 +
   1.268 +MetroInput::MetroInput(MetroWidget* aWidget,
   1.269 +                       UI::Core::ICoreWindow* aWindow)
   1.270 +              : mWidget(aWidget),
   1.271 +                mNonApzTargetForTouch(false),
   1.272 +                mWindow(aWindow)
   1.273 +{
   1.274 +  LogFunction();
   1.275 +  NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!");
   1.276 +  NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!");
   1.277 +
   1.278 +  Preferences::AddBoolVarCache(&gTouchActionPropertyEnabled, "layout.css.touch_action.enabled", gTouchActionPropertyEnabled);
   1.279 +  mTokenPointerPressed.value = 0;
   1.280 +  mTokenPointerReleased.value = 0;
   1.281 +  mTokenPointerMoved.value = 0;
   1.282 +  mTokenPointerEntered.value = 0;
   1.283 +  mTokenPointerExited.value = 0;
   1.284 +  mTokenEdgeStarted.value = 0;
   1.285 +  mTokenEdgeCanceled.value = 0;
   1.286 +  mTokenEdgeCompleted.value = 0;
   1.287 +  mTokenManipulationCompleted.value = 0;
   1.288 +  mTokenTapped.value = 0;
   1.289 +  mTokenRightTapped.value = 0;
   1.290 +
   1.291 +  // Create our Gesture Recognizer
   1.292 +  ActivateGenericInstance(RuntimeClass_Windows_UI_Input_GestureRecognizer,
   1.293 +                          mGestureRecognizer);
   1.294 +  NS_ASSERTION(mGestureRecognizer, "Failed to create GestureRecognizer!");
   1.295 +
   1.296 +  RegisterInputEvents();
   1.297 +}
   1.298 +
   1.299 +MetroInput::~MetroInput()
   1.300 +{
   1.301 +  LogFunction();
   1.302 +  UnregisterInputEvents();
   1.303 +}
   1.304 +
   1.305 +/* static */
   1.306 +bool MetroInput::IsInputModeImprecise()
   1.307 +{
   1.308 +  return sCurrentInputLevel == LEVEL_IMPRECISE;
   1.309 +}
   1.310 +
   1.311 +/**
   1.312 + * Tracks the current input level (precise/imprecise) and fires an observer
   1.313 + * when the mode changes.
   1.314 + */
   1.315 +void
   1.316 +MetroInput::UpdateInputLevel(InputPrecisionLevel aInputLevel)
   1.317 +{
   1.318 +  // ignore mouse input if we have active touch input.
   1.319 +  if (aInputLevel == LEVEL_PRECISE && mTouches.Count() > 0) {
   1.320 +    return;
   1.321 +  }
   1.322 +  if (sCurrentInputLevel != aInputLevel) {
   1.323 +    sCurrentInputLevel = aInputLevel;
   1.324 +    MetroUtils::FireObserver(sCurrentInputLevel == LEVEL_PRECISE ?
   1.325 +                               "metro_precise_input" : "metro_imprecise_input");
   1.326 +  }
   1.327 +}
   1.328 +
   1.329 +/**
   1.330 + * Processes an IEdgeGestureEventArgs and returns the input source type
   1.331 + * for the event. Also updates input level via UpdateInputLevel.
   1.332 + */
   1.333 +uint16_t
   1.334 +MetroInput::ProcessInputTypeForGesture(UI::Input::IEdgeGestureEventArgs* aArgs)
   1.335 +{
   1.336 +  MOZ_ASSERT(aArgs);
   1.337 +  UI::Input::EdgeGestureKind kind;
   1.338 +  aArgs->get_Kind(&kind);
   1.339 +  switch(kind) {
   1.340 +    case UI::Input::EdgeGestureKind::EdgeGestureKind_Touch:
   1.341 +      UpdateInputLevel(LEVEL_IMPRECISE);
   1.342 +      return nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   1.343 +    break;
   1.344 +    case UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard:
   1.345 +      return nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
   1.346 +    break;
   1.347 +    case UI::Input::EdgeGestureKind::EdgeGestureKind_Mouse:
   1.348 +      UpdateInputLevel(LEVEL_PRECISE);
   1.349 +      return nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
   1.350 +    break;
   1.351 +  }
   1.352 +  return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
   1.353 +}
   1.354 +
   1.355 +/**
   1.356 + * When the user swipes her/his finger in from the top of the screen,
   1.357 + * we receive this event.
   1.358 + *
   1.359 + * @param sender the CoreDispatcher that fired this event
   1.360 + * @param aArgs the event-specific args we use when processing this event
   1.361 + * @returns S_OK
   1.362 + */
   1.363 +HRESULT
   1.364 +MetroInput::OnEdgeGestureStarted(UI::Input::IEdgeGesture* sender,
   1.365 +                                 UI::Input::IEdgeGestureEventArgs* aArgs)
   1.366 +{
   1.367 +#ifdef DEBUG_INPUT
   1.368 +  LogFunction();
   1.369 +#endif
   1.370 +  WidgetSimpleGestureEvent geckoEvent(true,
   1.371 +                                      NS_SIMPLE_GESTURE_EDGE_STARTED,
   1.372 +                                      mWidget.Get());
   1.373 +  mModifierKeyState.Update();
   1.374 +  mModifierKeyState.InitInputEvent(geckoEvent);
   1.375 +  geckoEvent.time = ::GetMessageTime();
   1.376 +  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
   1.377 +
   1.378 +  // Safe
   1.379 +  DispatchEventIgnoreStatus(&geckoEvent);
   1.380 +  return S_OK;
   1.381 +}
   1.382 +
   1.383 +/**
   1.384 + * This event can be received if the user swipes her/his finger back to
   1.385 + * the top of the screen, or continues moving her/his finger such that
   1.386 + * the movement is interpreted as a "grab this window" gesture
   1.387 + *
   1.388 + * @param sender the CoreDispatcher that fired this event
   1.389 + * @param aArgs the event-specific args we use when processing this event
   1.390 + * @returns S_OK
   1.391 + */
   1.392 +HRESULT
   1.393 +MetroInput::OnEdgeGestureCanceled(UI::Input::IEdgeGesture* sender,
   1.394 +                                  UI::Input::IEdgeGestureEventArgs* aArgs)
   1.395 +{
   1.396 +#ifdef DEBUG_INPUT
   1.397 +  LogFunction();
   1.398 +#endif
   1.399 +  WidgetSimpleGestureEvent geckoEvent(true,
   1.400 +                                      NS_SIMPLE_GESTURE_EDGE_CANCELED,
   1.401 +                                      mWidget.Get());
   1.402 +  mModifierKeyState.Update();
   1.403 +  mModifierKeyState.InitInputEvent(geckoEvent);
   1.404 +  geckoEvent.time = ::GetMessageTime();
   1.405 +  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
   1.406 +
   1.407 +  // Safe
   1.408 +  DispatchEventIgnoreStatus(&geckoEvent);
   1.409 +  return S_OK;
   1.410 +}
   1.411 +
   1.412 +/**
   1.413 + * This event is received if the user presses ctrl+Z or lifts her/his
   1.414 + * finger after causing an EdgeGestureStarting event to fire.
   1.415 + *
   1.416 + * @param sender the CoreDispatcher that fired this event
   1.417 + * @param aArgs the event-specific args we use when processing this event
   1.418 + * @returns S_OK
   1.419 + */
   1.420 +HRESULT
   1.421 +MetroInput::OnEdgeGestureCompleted(UI::Input::IEdgeGesture* sender,
   1.422 +                                   UI::Input::IEdgeGestureEventArgs* aArgs)
   1.423 +{
   1.424 +#ifdef DEBUG_INPUT
   1.425 +  LogFunction();
   1.426 +#endif
   1.427 +  WidgetSimpleGestureEvent geckoEvent(true,
   1.428 +                                      NS_SIMPLE_GESTURE_EDGE_COMPLETED,
   1.429 +                                      mWidget.Get());
   1.430 +  mModifierKeyState.Update();
   1.431 +  mModifierKeyState.InitInputEvent(geckoEvent);
   1.432 +  geckoEvent.time = ::GetMessageTime();
   1.433 +  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
   1.434 +
   1.435 +  // Safe
   1.436 +  DispatchEventIgnoreStatus(&geckoEvent);
   1.437 +  return S_OK;
   1.438 +}
   1.439 +
   1.440 +/**
   1.441 + * This helper function is used by our processing of PointerPressed,
   1.442 + * PointerReleased, and PointerMoved events.
   1.443 + * It dispatches a gecko event in response to the input received.  This
   1.444 + * function should only be called for non-touch (i.e. pen or mouse) input
   1.445 + * events.
   1.446 + *
   1.447 + * @param aPoint the PointerPoint for the input event
   1.448 + */
   1.449 +void
   1.450 +MetroInput::OnPointerNonTouch(UI::Input::IPointerPoint* aPoint) {
   1.451 +  WRL::ComPtr<UI::Input::IPointerPointProperties> props;
   1.452 +  UI::Input::PointerUpdateKind pointerUpdateKind;
   1.453 +
   1.454 +  aPoint->get_Properties(props.GetAddressOf());
   1.455 +  props->get_PointerUpdateKind(&pointerUpdateKind);
   1.456 +
   1.457 +  uint32_t message = NS_MOUSE_MOVE;
   1.458 +  int16_t button = 0;
   1.459 +
   1.460 +  switch (pointerUpdateKind) {
   1.461 +    case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonPressed:
   1.462 +      button = WidgetMouseEvent::buttonType::eLeftButton;
   1.463 +      message = NS_MOUSE_BUTTON_DOWN;
   1.464 +      break;
   1.465 +    case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonPressed:
   1.466 +      button = WidgetMouseEvent::buttonType::eMiddleButton;
   1.467 +      message = NS_MOUSE_BUTTON_DOWN;
   1.468 +      break;
   1.469 +    case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonPressed:
   1.470 +      button = WidgetMouseEvent::buttonType::eRightButton;
   1.471 +      message = NS_MOUSE_BUTTON_DOWN;
   1.472 +      break;
   1.473 +    case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonReleased:
   1.474 +      button = WidgetMouseEvent::buttonType::eLeftButton;
   1.475 +      message = NS_MOUSE_BUTTON_UP;
   1.476 +      break;
   1.477 +    case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonReleased:
   1.478 +      button = WidgetMouseEvent::buttonType::eMiddleButton;
   1.479 +      message = NS_MOUSE_BUTTON_UP;
   1.480 +      break;
   1.481 +    case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased:
   1.482 +      button = WidgetMouseEvent::buttonType::eRightButton;
   1.483 +      message = NS_MOUSE_BUTTON_UP;
   1.484 +      break;
   1.485 +  }
   1.486 +
   1.487 +  UpdateInputLevel(LEVEL_PRECISE);
   1.488 +
   1.489 +  WidgetMouseEvent* event =
   1.490 +    new WidgetMouseEvent(true, message, mWidget.Get(),
   1.491 +                         WidgetMouseEvent::eReal,
   1.492 +                         WidgetMouseEvent::eNormal);
   1.493 +  event->button = button;
   1.494 +  aPoint->get_PointerId(&event->pointerId);
   1.495 +  InitGeckoMouseEventFromPointerPoint(event, aPoint);
   1.496 +  DispatchAsyncEventIgnoreStatus(event);
   1.497 +}
   1.498 +
   1.499 +void
   1.500 +MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent)
   1.501 +{
   1.502 +  MOZ_ASSERT(aEvent);
   1.503 +  mTouches.Enumerate(&AppendToTouchList,
   1.504 +                      static_cast<void*>(&aEvent->touches));
   1.505 +}
   1.506 +
   1.507 +bool
   1.508 +MetroInput::ShouldDeliverInputToRecognizer()
   1.509 +{
   1.510 +  return mRecognizerWantsEvents;
   1.511 +}
   1.512 +
   1.513 +void
   1.514 +MetroInput::GetAllowedTouchBehavior(WidgetTouchEvent* aTransformedEvent, nsTArray<TouchBehaviorFlags>& aOutBehaviors)
   1.515 +{
   1.516 +  mWidget->ApzcGetAllowedTouchBehavior(aTransformedEvent, aOutBehaviors);
   1.517 +
   1.518 +  for (uint32_t i = 0; i < aOutBehaviors.Length(); i++) {
   1.519 +    if (aOutBehaviors[i] & AllowedTouchBehavior::UNKNOWN) {
   1.520 +      // performing hit testing fallback: asking content to perform hit testing itself
   1.521 +      // (in spite that this operation has high latency).
   1.522 +      aOutBehaviors[i] = mWidget->ContentGetAllowedTouchBehavior(aTransformedEvent->touches[i]->mRefPoint);
   1.523 +    }
   1.524 +  }
   1.525 +}
   1.526 +
   1.527 +bool
   1.528 +MetroInput::IsTouchBehaviorForbidden(const nsTArray<TouchBehaviorFlags>& aTouchBehaviors)
   1.529 +{
   1.530 +  for (size_t i = 0; i < aTouchBehaviors.Length(); i++) {
   1.531 +    if (aTouchBehaviors[i] == AllowedTouchBehavior::NONE)
   1.532 +      return true;
   1.533 +  }
   1.534 +
   1.535 +  return false;
   1.536 +}
   1.537 +
   1.538 +// This event is raised when the user pushes the left mouse button, presses a
   1.539 +// pen to the surface, or presses a touch screen.
   1.540 +HRESULT
   1.541 +MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender,
   1.542 +                             UI::Core::IPointerEventArgs* aArgs)
   1.543 +{
   1.544 +#ifdef DEBUG_INPUT
   1.545 +  LogFunction();
   1.546 +#endif
   1.547 +
   1.548 +  WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
   1.549 +  WRL::ComPtr<Devices::Input::IPointerDevice> device;
   1.550 +  Devices::Input::PointerDeviceType deviceType;
   1.551 +
   1.552 +  aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
   1.553 +  currentPoint->get_PointerDevice(device.GetAddressOf());
   1.554 +  device->get_PointerDeviceType(&deviceType);
   1.555 +
   1.556 +  // For mouse and pen input, simply call our helper function
   1.557 +  if (deviceType !=
   1.558 +          Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
   1.559 +    OnPointerNonTouch(currentPoint.Get());
   1.560 +    mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
   1.561 +    return S_OK;
   1.562 +  }
   1.563 +
   1.564 +  // This is touch input.
   1.565 +  UpdateInputLevel(LEVEL_IMPRECISE);
   1.566 +
   1.567 +  // Create the new touch point and add it to our event.
   1.568 +  uint32_t pointerId;
   1.569 +  currentPoint->get_PointerId(&pointerId);
   1.570 +  nsRefPtr<Touch> touch = CreateDOMTouch(currentPoint.Get());
   1.571 +  touch->mChanged = true;
   1.572 +  mTouches.Put(pointerId, touch);
   1.573 +
   1.574 +  WidgetTouchEvent* touchEvent =
   1.575 +    new WidgetTouchEvent(true, NS_TOUCH_START, mWidget.Get());
   1.576 +
   1.577 +  if (mTouches.Count() == 1) {
   1.578 +    // If this is the first touchstart of a touch session reset some
   1.579 +    // tracking flags.
   1.580 +    mContentConsumingTouch = false;
   1.581 +    mApzConsumingTouch = false;
   1.582 +    mRecognizerWantsEvents = true;
   1.583 +    mCancelable = true;
   1.584 +    mCanceledIds.Clear();
   1.585 +  } else {
   1.586 +    mCancelable = false;
   1.587 +  }
   1.588 +
   1.589 +  InitTouchEventTouchList(touchEvent);
   1.590 +  DispatchAsyncTouchEvent(touchEvent);
   1.591 +
   1.592 +  if (ShouldDeliverInputToRecognizer()) {
   1.593 +    mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
   1.594 +  }
   1.595 +  return S_OK;
   1.596 +}
   1.597 +
   1.598 +void
   1.599 +MetroInput::AddPointerMoveDataToRecognizer(UI::Core::IPointerEventArgs* aArgs)
   1.600 +{
   1.601 +  if (ShouldDeliverInputToRecognizer()) {
   1.602 +    WRL::ComPtr<Foundation::Collections::IVector<UI::Input::PointerPoint*>>
   1.603 +        pointerPoints;
   1.604 +    aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf());
   1.605 +    mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get());
   1.606 +  }
   1.607 +}
   1.608 +
   1.609 +// This event is raised when the user moves the mouse, moves a pen that is
   1.610 +// in contact with the surface, or moves a finger that is in contact with
   1.611 +// a touch screen.
   1.612 +HRESULT
   1.613 +MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
   1.614 +                           UI::Core::IPointerEventArgs* aArgs)
   1.615 +{
   1.616 +#ifdef DEBUG_INPUT
   1.617 +  LogFunction();
   1.618 +#endif
   1.619 +
   1.620 +  WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
   1.621 +  WRL::ComPtr<Devices::Input::IPointerDevice> device;
   1.622 +  Devices::Input::PointerDeviceType deviceType;
   1.623 +
   1.624 +  aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
   1.625 +  currentPoint->get_PointerDevice(device.GetAddressOf());
   1.626 +  device->get_PointerDeviceType(&deviceType);
   1.627 +
   1.628 +  // For mouse and pen input, simply call our helper function
   1.629 +  if (deviceType !=
   1.630 +          Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
   1.631 +    OnPointerNonTouch(currentPoint.Get());
   1.632 +    AddPointerMoveDataToRecognizer(aArgs);
   1.633 +    return S_OK;
   1.634 +  }
   1.635 +
   1.636 +  // This is touch input.
   1.637 +  UpdateInputLevel(LEVEL_IMPRECISE);
   1.638 +
   1.639 +  // Get the touch associated with this touch point.
   1.640 +  uint32_t pointerId;
   1.641 +  currentPoint->get_PointerId(&pointerId);
   1.642 +  nsRefPtr<Touch> touch = mTouches.Get(pointerId);
   1.643 +
   1.644 +  // Some old drivers cause us to receive a PointerMoved event for a touchId
   1.645 +  // after we've already received a PointerReleased event for that touchId.
   1.646 +  // To work around those busted drivers, we simply ignore TouchMoved events
   1.647 +  // for touchIds that we are not currently tracking.  See bug 819223.
   1.648 +  if (!touch) {
   1.649 +    return S_OK;
   1.650 +  }
   1.651 +
   1.652 +  AddPointerMoveDataToRecognizer(aArgs);
   1.653 +
   1.654 +  // If the point hasn't moved, filter it out per the spec. Pres shell does
   1.655 +  // this as well, but we need to know when our first touchmove is going to
   1.656 +  // get delivered so we can check the result.
   1.657 +  if (!HasPointMoved(touch, currentPoint.Get())) {
   1.658 +    return S_OK;
   1.659 +  }
   1.660 +
   1.661 +  touch = CreateDOMTouch(currentPoint.Get());
   1.662 +  touch->mChanged = true;
   1.663 +  // replacing old touch point in mTouches map
   1.664 +  mTouches.Put(pointerId, touch);
   1.665 +
   1.666 +  WidgetTouchEvent* touchEvent =
   1.667 +    new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
   1.668 +  InitTouchEventTouchList(touchEvent);
   1.669 +  DispatchAsyncTouchEvent(touchEvent);
   1.670 +
   1.671 +  return S_OK;
   1.672 +}
   1.673 +
   1.674 +// This event is raised when the user lifts the left mouse button, lifts a
   1.675 +// pen from the surface, or lifts her/his finger from a touch screen.
   1.676 +HRESULT
   1.677 +MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender,
   1.678 +                              UI::Core::IPointerEventArgs* aArgs)
   1.679 +{
   1.680 +#ifdef DEBUG_INPUT
   1.681 +  LogFunction();
   1.682 +#endif
   1.683 +
   1.684 +  WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
   1.685 +  WRL::ComPtr<Devices::Input::IPointerDevice> device;
   1.686 +  Devices::Input::PointerDeviceType deviceType;
   1.687 +
   1.688 +  aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
   1.689 +  currentPoint->get_PointerDevice(device.GetAddressOf());
   1.690 +  device->get_PointerDeviceType(&deviceType);
   1.691 +
   1.692 +  // For mouse and pen input, simply call our helper function
   1.693 +  if (deviceType !=
   1.694 +          Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
   1.695 +    OnPointerNonTouch(currentPoint.Get());
   1.696 +    mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
   1.697 +    return S_OK;
   1.698 +  }
   1.699 +
   1.700 +  // This is touch input.
   1.701 +  UpdateInputLevel(LEVEL_IMPRECISE);
   1.702 +
   1.703 +  // Get the touch associated with this touch point.
   1.704 +  uint32_t pointerId;
   1.705 +  currentPoint->get_PointerId(&pointerId);
   1.706 +  nsRefPtr<Touch> touch = mTouches.Get(pointerId);
   1.707 +
   1.708 +  // Purge any pending moves for this pointer
   1.709 +  if (touch->mChanged) {
   1.710 +    WidgetTouchEvent* touchEvent =
   1.711 +      new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
   1.712 +    InitTouchEventTouchList(touchEvent);
   1.713 +    DispatchAsyncTouchEvent(touchEvent);
   1.714 +  }
   1.715 +
   1.716 +  // Remove this touch point from our map. Eventually all touch points are
   1.717 +  // removed for this session since we receive released events for every
   1.718 +  // point.
   1.719 +  mTouches.Remove(pointerId);
   1.720 +
   1.721 +  // touchend events only have a single touch; the touch that has been removed
   1.722 +  WidgetTouchEvent* touchEvent =
   1.723 +    new WidgetTouchEvent(true, NS_TOUCH_END, mWidget.Get());
   1.724 +  touchEvent->touches.AppendElement(CreateDOMTouch(currentPoint.Get()));
   1.725 +  DispatchAsyncTouchEvent(touchEvent);
   1.726 +
   1.727 +  if (ShouldDeliverInputToRecognizer()) {
   1.728 +    mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
   1.729 +  }
   1.730 +
   1.731 +  return S_OK;
   1.732 +}
   1.733 +
   1.734 +// Tests for chrome vs. content target so we know whether input coordinates need
   1.735 +// to be transformed through the apz. Eventually this hit testing should move
   1.736 +// into the apz (bug 918288).
   1.737 +bool
   1.738 +MetroInput::HitTestChrome(const LayoutDeviceIntPoint& pt)
   1.739 +{
   1.740 +  // Confirm this event targets content. We pick this up in browser's input.js.
   1.741 +  WidgetMouseEvent hittest(true, NS_MOUSE_MOZHITTEST, mWidget.Get(),
   1.742 +                           WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
   1.743 +  hittest.refPoint = pt;
   1.744 +  nsEventStatus status;
   1.745 +  mWidget->DispatchEvent(&hittest, status);
   1.746 +  return (status == nsEventStatus_eConsumeNoDefault);
   1.747 +}
   1.748 +
   1.749 +/**
   1.750 + * Returns true if the position is in chrome, false otherwise.
   1.751 + */
   1.752 +bool
   1.753 +MetroInput::TransformRefPoint(const Foundation::Point& aPosition, LayoutDeviceIntPoint& aRefPointOut)
   1.754 +{
   1.755 +  // If this event is destined for content we need to transform our ref point through
   1.756 +  // the apz so that zoom can be accounted for.
   1.757 +  aRefPointOut = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition));
   1.758 +  ScreenIntPoint spt;
   1.759 +  spt.x = aRefPointOut.x;
   1.760 +  spt.y = aRefPointOut.y;
   1.761 +  // This is currently a general contained rect hit test, it may produce a false positive for
   1.762 +  // overlay chrome elements.
   1.763 +  bool apzIntersect = mWidget->ApzHitTest(spt);
   1.764 +  if (!apzIntersect) {
   1.765 +    return true;
   1.766 +  }
   1.767 +  if (HitTestChrome(aRefPointOut)) {
   1.768 +    return true;
   1.769 +  }
   1.770 +  mWidget->ApzTransformGeckoCoordinate(spt, &aRefPointOut);
   1.771 +  return false;
   1.772 +}
   1.773 +
   1.774 +void
   1.775 +MetroInput::TransformTouchEvent(WidgetTouchEvent* aEvent)
   1.776 +{
   1.777 +  nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
   1.778 +  for (uint32_t i = 0; i < touches.Length(); ++i) {
   1.779 +    dom::Touch* touch = touches[i];
   1.780 +    if (touch) {
   1.781 +      LayoutDeviceIntPoint lpt;
   1.782 +      ScreenIntPoint spt;
   1.783 +      spt.x = touch->mRefPoint.x;
   1.784 +      spt.y = touch->mRefPoint.y;
   1.785 +      mWidget->ApzTransformGeckoCoordinate(spt, &lpt);
   1.786 +      touch->mRefPoint.x = lpt.x;
   1.787 +      touch->mRefPoint.y = lpt.y;
   1.788 +    }
   1.789 +  }
   1.790 +}
   1.791 +
   1.792 +void
   1.793 +MetroInput::InitGeckoMouseEventFromPointerPoint(
   1.794 +                                  WidgetMouseEvent* aEvent,
   1.795 +                                  UI::Input::IPointerPoint* aPointerPoint)
   1.796 +{
   1.797 +  NS_ASSERTION(aPointerPoint, "InitGeckoMouseEventFromPointerPoint "
   1.798 +                              "called with null PointerPoint!");
   1.799 +
   1.800 +  WRL::ComPtr<UI::Input::IPointerPointProperties> props;
   1.801 +  WRL::ComPtr<Devices::Input::IPointerDevice> device;
   1.802 +  Devices::Input::PointerDeviceType deviceType;
   1.803 +  Foundation::Point position;
   1.804 +  uint64_t timestamp;
   1.805 +  float pressure;
   1.806 +  boolean canBeDoubleTap;
   1.807 +  float tiltX;
   1.808 +  float tiltY;
   1.809 +
   1.810 +  aPointerPoint->get_Position(&position);
   1.811 +  aPointerPoint->get_Timestamp(&timestamp);
   1.812 +  aPointerPoint->get_PointerDevice(device.GetAddressOf());
   1.813 +  device->get_PointerDeviceType(&deviceType);
   1.814 +  aPointerPoint->get_Properties(props.GetAddressOf());
   1.815 +  aPointerPoint->get_PointerId(&aEvent->pointerId);
   1.816 +  props->get_Pressure(&pressure);
   1.817 +  props->get_XTilt(&tiltX);
   1.818 +  props->get_YTilt(&tiltY);
   1.819 +
   1.820 +  mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap);
   1.821 +
   1.822 +  TransformRefPoint(position, aEvent->refPoint);
   1.823 +
   1.824 +  if (!canBeDoubleTap) {
   1.825 +    aEvent->clickCount = 1;
   1.826 +  } else {
   1.827 +    aEvent->clickCount = 2;
   1.828 +  }
   1.829 +  aEvent->pressure = pressure;
   1.830 +  aEvent->tiltX = tiltX;
   1.831 +  aEvent->tiltY = tiltY;
   1.832 +  aEvent->buttons = ButtonsForPointerPoint(aPointerPoint);
   1.833 +
   1.834 +  MozInputSourceFromDeviceType(deviceType, aEvent->inputSource);
   1.835 +}
   1.836 +
   1.837 +// This event is raised when a precise pointer moves into the bounding box of
   1.838 +// our window.  For touch input, this will be raised before the PointerPressed
   1.839 +// event.
   1.840 +HRESULT
   1.841 +MetroInput::OnPointerEntered(UI::Core::ICoreWindow* aSender,
   1.842 +                             UI::Core::IPointerEventArgs* aArgs)
   1.843 +{
   1.844 +#ifdef DEBUG_INPUT
   1.845 +  LogFunction();
   1.846 +#endif
   1.847 +
   1.848 +  WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
   1.849 +  WRL::ComPtr<Devices::Input::IPointerDevice> device;
   1.850 +  Devices::Input::PointerDeviceType deviceType;
   1.851 +
   1.852 +  aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
   1.853 +  currentPoint->get_PointerDevice(device.GetAddressOf());
   1.854 +  device->get_PointerDeviceType(&deviceType);
   1.855 +
   1.856 +  // We only dispatch mouseenter and mouseexit events for mouse and pen input.
   1.857 +  if (deviceType !=
   1.858 +          Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
   1.859 +    WidgetMouseEvent* event =
   1.860 +      new WidgetMouseEvent(true, NS_MOUSE_ENTER, mWidget.Get(),
   1.861 +                           WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
   1.862 +    UpdateInputLevel(LEVEL_PRECISE);
   1.863 +    InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
   1.864 +    DispatchAsyncEventIgnoreStatus(event);
   1.865 +    return S_OK;
   1.866 +  }
   1.867 +  UpdateInputLevel(LEVEL_IMPRECISE);
   1.868 +  return S_OK;
   1.869 +}
   1.870 +
   1.871 +// This event is raised when a precise pointer leaves the bounding box of
   1.872 +// our window.  For touch input, this will be raised before the
   1.873 +// PointerReleased event.
   1.874 +HRESULT
   1.875 +MetroInput::OnPointerExited(UI::Core::ICoreWindow* aSender,
   1.876 +                            UI::Core::IPointerEventArgs* aArgs)
   1.877 +{
   1.878 +#ifdef DEBUG_INPUT
   1.879 +  LogFunction();
   1.880 +#endif
   1.881 +
   1.882 +  WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
   1.883 +  WRL::ComPtr<Devices::Input::IPointerDevice> device;
   1.884 +  Devices::Input::PointerDeviceType deviceType;
   1.885 +
   1.886 +  aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
   1.887 +  currentPoint->get_PointerDevice(device.GetAddressOf());
   1.888 +  device->get_PointerDeviceType(&deviceType);
   1.889 +
   1.890 +  // We only dispatch mouseenter and mouseexit events for mouse and pen input.
   1.891 +  if (deviceType !=
   1.892 +          Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
   1.893 +    WidgetMouseEvent* event =
   1.894 +      new WidgetMouseEvent(true, NS_MOUSE_EXIT, mWidget.Get(),
   1.895 +                           WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
   1.896 +    UpdateInputLevel(LEVEL_PRECISE);
   1.897 +    InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
   1.898 +    DispatchAsyncEventIgnoreStatus(event);
   1.899 +    return S_OK;
   1.900 +  }
   1.901 +  UpdateInputLevel(LEVEL_IMPRECISE);
   1.902 +  return S_OK;
   1.903 +}
   1.904 +
   1.905 +// Gecko expects a "finished" event to be sent that has the cumulative
   1.906 +// changes since the gesture began.  The idea is that consumers could hook
   1.907 +// only this last event and still effectively support magnification and
   1.908 +// rotation. We accomplish sending this "finished" event by calling our
   1.909 +// helper function with a cumulative "delta" value.
   1.910 +//
   1.911 +// After sending the "finished" event, this function detects and sends
   1.912 +// swipe gestures.
   1.913 +HRESULT
   1.914 +MetroInput::OnManipulationCompleted(
   1.915 +                        UI::Input::IGestureRecognizer* aSender,
   1.916 +                        UI::Input::IManipulationCompletedEventArgs* aArgs)
   1.917 +{
   1.918 +#ifdef DEBUG_INPUT
   1.919 +  LogFunction();
   1.920 +#endif
   1.921 +
   1.922 +  Devices::Input::PointerDeviceType deviceType;
   1.923 +  aArgs->get_PointerDeviceType(&deviceType);
   1.924 +  if (deviceType ==
   1.925 +              Devices::Input::PointerDeviceType::PointerDeviceType_Mouse) {
   1.926 +    return S_OK;
   1.927 +  }
   1.928 +
   1.929 +  UI::Input::ManipulationDelta delta;
   1.930 +  Foundation::Point position;
   1.931 +
   1.932 +  aArgs->get_Position(&position);
   1.933 +  aArgs->get_Cumulative(&delta);
   1.934 +
   1.935 +  // We check that the distance the user's finger traveled and the
   1.936 +  // velocity with which it traveled exceed our thresholds for
   1.937 +  // classifying the movement as a swipe.
   1.938 +  UI::Input::ManipulationVelocities velocities;
   1.939 +  aArgs->get_Velocities(&velocities);
   1.940 +
   1.941 +  bool isHorizontalSwipe =
   1.942 +            abs(velocities.Linear.X) >= SWIPE_MIN_VELOCITY
   1.943 +         && abs(delta.Translation.X) >= SWIPE_MIN_DISTANCE;
   1.944 +  bool isVerticalSwipe =
   1.945 +            abs(velocities.Linear.Y) >= SWIPE_MIN_VELOCITY
   1.946 +         && abs(delta.Translation.Y) >= SWIPE_MIN_DISTANCE;
   1.947 +
   1.948 +  // If our thresholds were exceeded for both a vertical and a horizontal
   1.949 +  // swipe, it means the user is flinging her/his finger around and we
   1.950 +  // should just ignore the input.
   1.951 +  if (isHorizontalSwipe && isVerticalSwipe) {
   1.952 +    return S_OK;
   1.953 +  }
   1.954 +
   1.955 +  if (isHorizontalSwipe) {
   1.956 +    WidgetSimpleGestureEvent* swipeEvent =
   1.957 +      new WidgetSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE,
   1.958 +                                   mWidget.Get());
   1.959 +    swipeEvent->direction = delta.Translation.X > 0
   1.960 +                         ? nsIDOMSimpleGestureEvent::DIRECTION_RIGHT
   1.961 +                         : nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
   1.962 +    swipeEvent->delta = delta.Translation.X;
   1.963 +    swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   1.964 +    swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position));
   1.965 +    DispatchAsyncEventIgnoreStatus(swipeEvent);
   1.966 +  }
   1.967 +
   1.968 +  if (isVerticalSwipe) {
   1.969 +    WidgetSimpleGestureEvent* swipeEvent =
   1.970 +      new WidgetSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE,
   1.971 +                                   mWidget.Get());
   1.972 +    swipeEvent->direction = delta.Translation.Y > 0
   1.973 +                         ? nsIDOMSimpleGestureEvent::DIRECTION_DOWN
   1.974 +                         : nsIDOMSimpleGestureEvent::DIRECTION_UP;
   1.975 +    swipeEvent->delta = delta.Translation.Y;
   1.976 +    swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   1.977 +    swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position));
   1.978 +    DispatchAsyncEventIgnoreStatus(swipeEvent);
   1.979 +  }
   1.980 +
   1.981 +  return S_OK;
   1.982 +}
   1.983 +
   1.984 +// This event is raised when a sequence of pointer events has been
   1.985 +// interpreted by the GestureRecognizer as a tap (this could be a mouse
   1.986 +// click, a pen tap, or a tap on a touch surface).
   1.987 +HRESULT
   1.988 +MetroInput::OnTapped(UI::Input::IGestureRecognizer* aSender,
   1.989 +                     UI::Input::ITappedEventArgs* aArgs)
   1.990 +{
   1.991 +#ifdef DEBUG_INPUT
   1.992 +  LogFunction();
   1.993 +#endif
   1.994 +
   1.995 +  Devices::Input::PointerDeviceType deviceType;
   1.996 +  aArgs->get_PointerDeviceType(&deviceType);
   1.997 +
   1.998 +  unsigned int tapCount;
   1.999 +  aArgs->get_TapCount(&tapCount);
  1.1000 +
  1.1001 +  // For mouse and pen input, we send mousedown/mouseup/mousemove
  1.1002 +  // events as soon as we detect the input event.  For touch input, a set of
  1.1003 +  // mousedown/mouseup events will be sent only once a tap has been detected.
  1.1004 +  if (deviceType != Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
  1.1005 +    return S_OK;
  1.1006 +  }
  1.1007 +
  1.1008 +  Foundation::Point position;
  1.1009 +  aArgs->get_Position(&position);
  1.1010 +  HandleTap(position, tapCount);
  1.1011 +  return S_OK;
  1.1012 +}
  1.1013 +
  1.1014 +// This event is raised when a sequence of pointer events has been
  1.1015 +// interpreted by the GestureRecognizer as a right tap.
  1.1016 +// This could be a mouse right-click, a right-click on a pen, or
  1.1017 +// a tap-and-hold on a touch surface.
  1.1018 +HRESULT
  1.1019 +MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender,
  1.1020 +                          UI::Input::IRightTappedEventArgs* aArgs)
  1.1021 +{
  1.1022 +#ifdef DEBUG_INPUT
  1.1023 +  LogFunction();
  1.1024 +#endif
  1.1025 +
  1.1026 +  Devices::Input::PointerDeviceType deviceType;
  1.1027 +  aArgs->get_PointerDeviceType(&deviceType);
  1.1028 +
  1.1029 +  Foundation::Point position;
  1.1030 +  aArgs->get_Position(&position);
  1.1031 +  HandleLongTap(position);
  1.1032 +
  1.1033 +  return S_OK;
  1.1034 +}
  1.1035 +
  1.1036 +void
  1.1037 +MetroInput::HandleTap(const Foundation::Point& aPoint, unsigned int aTapCount)
  1.1038 +{
  1.1039 +#ifdef DEBUG_INPUT
  1.1040 +  LogFunction();
  1.1041 +#endif
  1.1042 +
  1.1043 +  LayoutDeviceIntPoint refPoint;
  1.1044 +  TransformRefPoint(aPoint, refPoint);
  1.1045 +
  1.1046 +  WidgetMouseEvent* mouseEvent =
  1.1047 +    new WidgetMouseEvent(true, NS_MOUSE_MOVE, mWidget.Get(),
  1.1048 +                         WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
  1.1049 +  mouseEvent->refPoint = refPoint;
  1.1050 +  mouseEvent->clickCount = aTapCount;
  1.1051 +  mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
  1.1052 +  DispatchAsyncEventIgnoreStatus(mouseEvent);
  1.1053 +
  1.1054 +  mouseEvent =
  1.1055 +    new WidgetMouseEvent(true, NS_MOUSE_BUTTON_DOWN, mWidget.Get(),
  1.1056 +                         WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
  1.1057 +  mouseEvent->refPoint = refPoint;
  1.1058 +  mouseEvent->clickCount = aTapCount;
  1.1059 +  mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
  1.1060 +  mouseEvent->button = WidgetMouseEvent::buttonType::eLeftButton;
  1.1061 +  DispatchAsyncEventIgnoreStatus(mouseEvent);
  1.1062 +
  1.1063 +  mouseEvent =
  1.1064 +    new WidgetMouseEvent(true, NS_MOUSE_BUTTON_UP, mWidget.Get(),
  1.1065 +                         WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
  1.1066 +  mouseEvent->refPoint = refPoint;
  1.1067 +  mouseEvent->clickCount = aTapCount;
  1.1068 +  mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
  1.1069 +  mouseEvent->button = WidgetMouseEvent::buttonType::eLeftButton;
  1.1070 +  DispatchAsyncEventIgnoreStatus(mouseEvent);
  1.1071 +
  1.1072 +  // Make sure all gecko events are dispatched and the dom is up to date
  1.1073 +  // so that when ui automation comes in looking for focus info it gets
  1.1074 +  // the right information.
  1.1075 +  MetroAppShell::MarkEventQueueForPurge();
  1.1076 +}
  1.1077 +
  1.1078 +void
  1.1079 +MetroInput::HandleLongTap(const Foundation::Point& aPoint)
  1.1080 +{
  1.1081 +#ifdef DEBUG_INPUT
  1.1082 +  LogFunction();
  1.1083 +#endif
  1.1084 +  LayoutDeviceIntPoint refPoint;
  1.1085 +  TransformRefPoint(aPoint, refPoint);
  1.1086 +
  1.1087 +  WidgetMouseEvent* contextEvent =
  1.1088 +    new WidgetMouseEvent(true, NS_CONTEXTMENU, mWidget.Get(),
  1.1089 +                         WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
  1.1090 +  contextEvent->refPoint = refPoint;
  1.1091 +  contextEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
  1.1092 +  DispatchAsyncEventIgnoreStatus(contextEvent);
  1.1093 +}
  1.1094 +
  1.1095 +/**
  1.1096 + * Implementation Details
  1.1097 + */
  1.1098 +nsEventStatus MetroInput::sThrowawayStatus;
  1.1099 +
  1.1100 +void
  1.1101 +MetroInput::DispatchAsyncEventIgnoreStatus(WidgetInputEvent* aEvent)
  1.1102 +{
  1.1103 +  aEvent->time = ::GetMessageTime();
  1.1104 +  mModifierKeyState.Update();
  1.1105 +  mModifierKeyState.InitInputEvent(*aEvent);
  1.1106 +  mInputEventQueue.Push(aEvent);
  1.1107 +  nsCOMPtr<nsIRunnable> runnable =
  1.1108 +    NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedEventIgnoreStatus);
  1.1109 +  NS_DispatchToCurrentThread(runnable);
  1.1110 +}
  1.1111 +
  1.1112 +void
  1.1113 +MetroInput::DeliverNextQueuedEventIgnoreStatus()
  1.1114 +{
  1.1115 +  nsAutoPtr<WidgetGUIEvent> event =
  1.1116 +    static_cast<WidgetGUIEvent*>(mInputEventQueue.PopFront());
  1.1117 +  MOZ_ASSERT(event.get());
  1.1118 +  DispatchEventIgnoreStatus(event.get());
  1.1119 +
  1.1120 +  // Let app shell know we've delivered that last input we wanted purged
  1.1121 +  // via a call to MarkEventQueueForPurge().
  1.1122 +  if (event->message == NS_MOUSE_BUTTON_UP) {
  1.1123 +    MetroAppShell::InputEventsDispatched();
  1.1124 +  }
  1.1125 +
  1.1126 +  // Clear :hover/:active states for mouse events generated by HandleTap
  1.1127 +  WidgetMouseEvent* mouseEvent = event.get()->AsMouseEvent();
  1.1128 +  if (!mouseEvent) {
  1.1129 +    return;
  1.1130 +  }
  1.1131 +  if (mouseEvent->message != NS_MOUSE_BUTTON_UP ||
  1.1132 +      mouseEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
  1.1133 +    return;
  1.1134 +  }
  1.1135 +  nsCOMPtr<nsIPresShell> presShell = mWidget->GetPresShell();
  1.1136 +  if (presShell) {
  1.1137 +    EventStateManager* esm = presShell->GetPresContext()->EventStateManager();
  1.1138 +    if (esm) {
  1.1139 +      esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER);
  1.1140 +    }
  1.1141 +  }
  1.1142 +}
  1.1143 +
  1.1144 +void
  1.1145 +MetroInput::DispatchAsyncTouchEvent(WidgetTouchEvent* aEvent)
  1.1146 +{
  1.1147 +  aEvent->time = ::GetMessageTime();
  1.1148 +  mModifierKeyState.Update();
  1.1149 +  mModifierKeyState.InitInputEvent(*aEvent);
  1.1150 +  mInputEventQueue.Push(aEvent);
  1.1151 +  nsCOMPtr<nsIRunnable> runnable =
  1.1152 +    NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedTouchEvent);
  1.1153 +  NS_DispatchToCurrentThread(runnable);
  1.1154 +}
  1.1155 +
  1.1156 +static void DumpTouchIds(const char* aTarget, WidgetTouchEvent* aEvent)
  1.1157 +{
  1.1158 +  // comment out for touch moves
  1.1159 +  if (aEvent->message == NS_TOUCH_MOVE) {
  1.1160 +    return;
  1.1161 +  }
  1.1162 +  switch(aEvent->message) {
  1.1163 +    case NS_TOUCH_START:
  1.1164 +    WinUtils::Log("DumpTouchIds: NS_TOUCH_START block");
  1.1165 +    break;
  1.1166 +    case NS_TOUCH_MOVE:
  1.1167 +    WinUtils::Log("DumpTouchIds: NS_TOUCH_MOVE block");
  1.1168 +    break;
  1.1169 +    case NS_TOUCH_END:
  1.1170 +    WinUtils::Log("DumpTouchIds: NS_TOUCH_END block");
  1.1171 +    break;
  1.1172 +    case NS_TOUCH_CANCEL:
  1.1173 +    WinUtils::Log("DumpTouchIds: NS_TOUCH_CANCEL block");
  1.1174 +    break;
  1.1175 +  }
  1.1176 +  nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
  1.1177 +  for (uint32_t i = 0; i < touches.Length(); ++i) {
  1.1178 +    dom::Touch* touch = touches[i];
  1.1179 +    if (!touch) {
  1.1180 +      continue;
  1.1181 +    }
  1.1182 +    int32_t id = touch->Identifier();
  1.1183 +    WinUtils::Log("   id=%d target=%s", id, aTarget);
  1.1184 +  }
  1.1185 +}
  1.1186 +
  1.1187 +static void DumpTouchBehavior(nsTArray<uint32_t>& aBehavior)
  1.1188 +{
  1.1189 +  WinUtils::Log("DumpTouchBehavior: Touch behavior flags set for current touch session:");
  1.1190 +  for (uint32_t i = 0; i < aBehavior.Length(); i++) {
  1.1191 +    if (mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN & aBehavior[i]) {
  1.1192 +      WinUtils::Log("VERTICAL_PAN");
  1.1193 +    }
  1.1194 +
  1.1195 +    if (mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN & aBehavior[i]) {
  1.1196 +      WinUtils::Log("HORIZONTAL_PAN");
  1.1197 +    }
  1.1198 +
  1.1199 +    if (mozilla::layers::AllowedTouchBehavior::UNKNOWN & aBehavior[i]) {
  1.1200 +      WinUtils::Log("UNKNOWN");
  1.1201 +    }
  1.1202 +
  1.1203 +    if ((mozilla::layers::AllowedTouchBehavior::NONE & aBehavior[i]) == 0) {
  1.1204 +      WinUtils::Log("NONE");
  1.1205 +    }
  1.1206 +  }
  1.1207 +}
  1.1208 +
  1.1209 +/*
  1.1210 + * nsPreShell's processing of WidgetTouchEvent events:
  1.1211 + *
  1.1212 + * NS_TOUCH_START:
  1.1213 + *  Interprets a single touch point as the first touch point of a block and will reset its
  1.1214 + *  queue when it receives this. For multiple touch points it sets all points in its queue
  1.1215 + *  and marks new points as changed.
  1.1216 + * NS_TOUCH_MOVE:
  1.1217 + *  Uses the equality tests in dom::Touch to test if a touch point has changed (moved).
  1.1218 + *  If a point has moved, keeps this touch point in the event, otherwise it removes
  1.1219 + *  the touch point. Note if no points have changed, it exits without sending a dom event.
  1.1220 + * NS_TOUCH_CANCEL/NS_TOUCH_END
  1.1221 + *  Assumes any point in touchEvent->touches has been removed or canceled.
  1.1222 +*/
  1.1223 +
  1.1224 +//#define DUMP_TOUCH_IDS(aTarget, aEvent) DumpTouchIds(aTarget, aEvent)
  1.1225 +#define DUMP_TOUCH_IDS(...)
  1.1226 +
  1.1227 +//#define DUMP_ALLOWED_TOUCH_BEHAVIOR(aBehavior) DumpTouchBehavior(aBehavior)
  1.1228 +#define DUMP_ALLOWED_TOUCH_BEHAVIOR(...)
  1.1229 +
  1.1230 +void
  1.1231 +MetroInput::HandleFirstTouchStartEvent(WidgetTouchEvent* aEvent)
  1.1232 +{
  1.1233 +  nsEventStatus contentStatus = nsEventStatus_eIgnore;
  1.1234 +
  1.1235 +  WidgetTouchEvent transformedEvent(*aEvent);
  1.1236 +  DUMP_TOUCH_IDS("APZC(1)", aEvent);
  1.1237 +  mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid);
  1.1238 +
  1.1239 +  if (gTouchActionPropertyEnabled) {
  1.1240 +    nsTArray<TouchBehaviorFlags> touchBehaviors;
  1.1241 +    // Retrieving touch behaviors from apzctm and from the content (if needed)
  1.1242 +    // then setting it back to the apzc. The apzc we retrieved touch behaviors
  1.1243 +    // from and we're setting to may changes if there are multiple touches (in that
  1.1244 +    // case apzctm needs to take common ancestor of them).
  1.1245 +    GetAllowedTouchBehavior(&transformedEvent, touchBehaviors);
  1.1246 +    // Setting the touch behaviors to the apzc that will be responsible
  1.1247 +    // for interpreting it. It may be not the same apzc we retrieved touch
  1.1248 +    // action values from. E.g. for zooming we're taking parent apzc of a few ones
  1.1249 +    // that were touched but touch behaviors would be taken from childs.
  1.1250 +    DUMP_ALLOWED_TOUCH_BEHAVIOR(touchBehaviors);
  1.1251 +    mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, touchBehaviors);
  1.1252 +    if (IsTouchBehaviorForbidden(touchBehaviors)) {
  1.1253 +      mContentConsumingTouch = true;
  1.1254 +    }
  1.1255 +  }
  1.1256 +
  1.1257 +  DUMP_TOUCH_IDS("DOM(2)", aEvent);
  1.1258 +  mWidget->DispatchEvent(&transformedEvent, contentStatus);
  1.1259 +  if (nsEventStatus_eConsumeNoDefault == contentStatus) {
  1.1260 +    mContentConsumingTouch = true;
  1.1261 +  }
  1.1262 +
  1.1263 +  if (mContentConsumingTouch) {
  1.1264 +    mCancelable = false;
  1.1265 +    mWidget->ApzContentConsumingTouch(mTargetAPZCGuid);
  1.1266 +    DispatchTouchCancel(aEvent);
  1.1267 +  }
  1.1268 +
  1.1269 +  // Disable gesture based events (taps, swipes, rotation) if
  1.1270 +  // preventDefault is called on touchstart.
  1.1271 +  mRecognizerWantsEvents = !(nsEventStatus_eConsumeNoDefault == contentStatus);
  1.1272 +
  1.1273 +  // If content is consuming touch don't generate any gesture based
  1.1274 +  // input - clear the recognizer state without sending any events.
  1.1275 +  if (!ShouldDeliverInputToRecognizer()) {
  1.1276 +    mGestureRecognizer->CompleteGesture();
  1.1277 +  }
  1.1278 +}
  1.1279 +
  1.1280 +void
  1.1281 +MetroInput::HandleFirstTouchMoveEvent(WidgetTouchEvent* aEvent)
  1.1282 +{
  1.1283 +  mCancelable = false;
  1.1284 +
  1.1285 +  nsEventStatus contentStatus = nsEventStatus_eIgnore;
  1.1286 +  nsEventStatus apzcStatus = nsEventStatus_eIgnore;
  1.1287 +
  1.1288 +  WidgetTouchEvent transformedEvent(*aEvent);
  1.1289 +  DUMP_TOUCH_IDS("APZC(2)", aEvent);
  1.1290 +  apzcStatus = mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid);
  1.1291 +
  1.1292 +  // We need to dispatch here only touch event, not pointer one.
  1.1293 +  // That's because according to the spec pointer events doesn't imply pointermove event
  1.1294 +  // between pointerdown and pointercancel (If default touch behavior is triggered).
  1.1295 +  // But at the same time we need to dispatch at least touchmove event to let content to
  1.1296 +  // consume it (or not consume).
  1.1297 +  // TODO: determine how to dispatch only one kind of events: currently there are two options:
  1.1298 +  // 1) Create two separate instances of the WidgetTouchEvent and WidgetPointerEvent and
  1.1299 +  // dispatch them separately.
  1.1300 +  // 2) Add a boolean flag to the WidgetTouchEvent that states whether this event should produce
  1.1301 +  // both touch and pointer event or only touch one.
  1.1302 +  // Anyway it's worth to add this stuff only after patches from bug 822898 (Pointer events) are
  1.1303 +  // fully commited.
  1.1304 +  DUMP_TOUCH_IDS("DOM(3)", aEvent);
  1.1305 +  mWidget->DispatchEvent(&transformedEvent, contentStatus);
  1.1306 +
  1.1307 +  // Checking content result first since content can override apzc wish and disallow apzc touch
  1.1308 +  // behavior (via preventDefault).
  1.1309 +  if (nsEventStatus_eConsumeNoDefault == contentStatus) {
  1.1310 +    // Touchmove handler consumed touch.
  1.1311 +    mContentConsumingTouch = true;
  1.1312 +  } else if (nsEventStatus_eConsumeNoDefault == apzcStatus) {
  1.1313 +    // Apzc triggered default behavior.
  1.1314 +    mApzConsumingTouch = true;
  1.1315 +  }
  1.1316 +
  1.1317 +  // Let the apz know if content wants to consume touch events, or cancel
  1.1318 +  // the touch block for content.
  1.1319 +  if (mContentConsumingTouch) {
  1.1320 +    mWidget->ApzContentConsumingTouch(mTargetAPZCGuid);
  1.1321 +    DispatchTouchCancel(aEvent);
  1.1322 +  } else {
  1.1323 +    mWidget->ApzContentIgnoringTouch(mTargetAPZCGuid);
  1.1324 +  }
  1.1325 +
  1.1326 +  if (mApzConsumingTouch) {
  1.1327 +    // Dispatching cancel to the content.
  1.1328 +    DispatchTouchCancel(&transformedEvent);
  1.1329 +  }
  1.1330 +}
  1.1331 +
  1.1332 +void
  1.1333 +MetroInput::DeliverNextQueuedTouchEvent()
  1.1334 +{
  1.1335 +  /*
  1.1336 +   * We go through states here and make different decisions in each:
  1.1337 +   *
  1.1338 +   * 1) Hit test for apz on first touchstart
  1.1339 +   *  If non-apzc content/chrome is the target simplify event delivery from
  1.1340 +   *  that point on by directing all input to chrome, bypassing the apz.
  1.1341 +   * 2) Process first touchstart and touchmove events
  1.1342 +   *  If touch behavior value associated with the TouchStart's touches doesn't
  1.1343 +   *  allow zooming or panning we explicitly set mContentConsumingTouch to true.
  1.1344 +   *  Otherwise check the result and set mContentConsumingTouch appropriately.
  1.1345 +   *  Deliver touch events to the apz (ignoring return result) and to content.
  1.1346 +   * 3) If mContentConsumingTouch is true: deliver touch to content after
  1.1347 +   *  transforming through the apz. Also let the apz know content is
  1.1348 +   *  consuming touch and deliver cancel event to apz.
  1.1349 +   * 4) If mContentConsumingTouch is false: check the result from the apz and
  1.1350 +   *  set mApzConsumingTouch appropriately.
  1.1351 +   * 5) If mApzConsumingTouch is true: send a touchcancel to content
  1.1352 +   *  and deliver all events to the apz. If the apz is doing something with
  1.1353 +   *  the events we can save ourselves the overhead of delivering dom events.
  1.1354 +   *
  1.1355 +   * Notes:
  1.1356 +   * - never rely on the contents of mTouches here, since this is a delayed
  1.1357 +   *   callback. mTouches will likely have been modified.
  1.1358 +   */
  1.1359 +  nsEventStatus status = nsEventStatus_eIgnore;
  1.1360 +
  1.1361 +  WidgetTouchEvent* event =
  1.1362 +    static_cast<WidgetTouchEvent*>(mInputEventQueue.PopFront());
  1.1363 +  MOZ_ASSERT(event);
  1.1364 +
  1.1365 +  AutoDeleteEvent wrap(event);
  1.1366 +
  1.1367 +  // Test for non-apz vs. apz target. To do this we only use the first touch
  1.1368 +  // point since that will be the input batch target. Cache this for touch events
  1.1369 +  // since HitTestChrome has to send a dom event.
  1.1370 +  if (mCancelable && event->message == NS_TOUCH_START) {
  1.1371 +    nsRefPtr<Touch> touch = event->touches[0];
  1.1372 +    LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint);
  1.1373 +    // This is currently a general contained rect hit test, it may produce a false
  1.1374 +    // positive for overlay chrome elements. Also, some content pages won't support
  1.1375 +    // apzc, so this may be false for content as well.
  1.1376 +    bool apzIntersect = mWidget->ApzHitTest(mozilla::ScreenIntPoint(pt.x, pt.y));
  1.1377 +    mNonApzTargetForTouch = (!apzIntersect || HitTestChrome(pt));
  1.1378 +  }
  1.1379 +
  1.1380 +  // If this event is destined for dom, deliver it directly there bypassing
  1.1381 +  // the apz.
  1.1382 +  if (mNonApzTargetForTouch) {
  1.1383 +    DUMP_TOUCH_IDS("DOM(1)", event);
  1.1384 +    mWidget->DispatchEvent(event, status);
  1.1385 +    if (mCancelable) {
  1.1386 +      // Disable gesture based events (taps, swipes, rotation) if
  1.1387 +      // preventDefault is called on touchstart.
  1.1388 +      if (nsEventStatus_eConsumeNoDefault == status) {
  1.1389 +        mRecognizerWantsEvents = false;
  1.1390 +        mGestureRecognizer->CompleteGesture();
  1.1391 +      }
  1.1392 +      if (event->message == NS_TOUCH_MOVE) {
  1.1393 +        mCancelable = false;
  1.1394 +      }
  1.1395 +    }
  1.1396 +    return;
  1.1397 +  }
  1.1398 +
  1.1399 +  if (mCancelable && event->message == NS_TOUCH_START) {
  1.1400 +    HandleFirstTouchStartEvent(event);
  1.1401 +    return;
  1.1402 +  } else if (mCancelable && event->message == NS_TOUCH_MOVE) {
  1.1403 +    HandleFirstTouchMoveEvent(event);
  1.1404 +    return;
  1.1405 +  }
  1.1406 +  // Let TouchEnd events go through even if mCancelable is true since we
  1.1407 +  // don't need to check whether it is prevented by content or consumed
  1.1408 +  // by apzc.
  1.1409 +
  1.1410 +  // If content is consuming touch, we may need to transform event coords
  1.1411 +  // through the apzc before sending to the dom. Otherwise send the event
  1.1412 +  // to apzc.
  1.1413 +  if (mContentConsumingTouch) {
  1.1414 +    // Only translate if we're dealing with web content that's transformed
  1.1415 +    // by the apzc.
  1.1416 +    TransformTouchEvent(event);
  1.1417 +    DUMP_TOUCH_IDS("DOM(4)", event);
  1.1418 +    mWidget->DispatchEvent(event, status);
  1.1419 +    return;
  1.1420 +  }
  1.1421 +
  1.1422 +  DUMP_TOUCH_IDS("APZC(3)", event);
  1.1423 +  status = mWidget->ApzReceiveInputEvent(event, nullptr);
  1.1424 +
  1.1425 +  // If we're getting a new touch (touch start) after some touch start/move
  1.1426 +  // events we need to reset touch behavior for touches.
  1.1427 +  if (gTouchActionPropertyEnabled && event->message == NS_TOUCH_START) {
  1.1428 +    nsTArray<TouchBehaviorFlags> touchBehaviors;
  1.1429 +    GetAllowedTouchBehavior(event, touchBehaviors);
  1.1430 +    DUMP_ALLOWED_TOUCH_BEHAVIOR(touchBehaviors);
  1.1431 +    mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, touchBehaviors);
  1.1432 +  }
  1.1433 +
  1.1434 +  // Send the event to content unless APZC is consuming it.
  1.1435 +  if (!mApzConsumingTouch) {
  1.1436 +    if (status == nsEventStatus_eConsumeNoDefault) {
  1.1437 +      mApzConsumingTouch = true;
  1.1438 +      DispatchTouchCancel(event);
  1.1439 +      return;
  1.1440 +    }
  1.1441 +    TransformTouchEvent(event);
  1.1442 +    DUMP_TOUCH_IDS("DOM(5)", event);
  1.1443 +    mWidget->DispatchEvent(event, status);
  1.1444 +  }
  1.1445 +}
  1.1446 +
  1.1447 +void
  1.1448 +MetroInput::DispatchTouchCancel(WidgetTouchEvent* aEvent)
  1.1449 +{
  1.1450 +  MOZ_ASSERT(aEvent);
  1.1451 +  // Send a touchcancel for each pointer id we have a corresponding start
  1.1452 +  // for. Note we can't rely on mTouches here since touchends remove points
  1.1453 +  // from it.
  1.1454 +  WidgetTouchEvent touchEvent(true, NS_TOUCH_CANCEL, mWidget.Get());
  1.1455 +  nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
  1.1456 +  for (uint32_t i = 0; i < touches.Length(); ++i) {
  1.1457 +    dom::Touch* touch = touches[i];
  1.1458 +    if (!touch) {
  1.1459 +      continue;
  1.1460 +    }
  1.1461 +    int32_t id = touch->Identifier();
  1.1462 +    if (mCanceledIds.Contains(id)) {
  1.1463 +      continue;
  1.1464 +    }
  1.1465 +    mCanceledIds.AppendElement(id);
  1.1466 +    touchEvent.touches.AppendElement(touch);
  1.1467 +  }
  1.1468 +  if (!touchEvent.touches.Length()) {
  1.1469 +    return;
  1.1470 +  }
  1.1471 +  if (mContentConsumingTouch) {
  1.1472 +    DUMP_TOUCH_IDS("APZC(4)", &touchEvent);
  1.1473 +    mWidget->ApzReceiveInputEvent(&touchEvent, nullptr);
  1.1474 +  } else {
  1.1475 +    DUMP_TOUCH_IDS("DOM(6)", &touchEvent);
  1.1476 +    mWidget->DispatchEvent(&touchEvent, sThrowawayStatus);
  1.1477 +  }
  1.1478 +}
  1.1479 +
  1.1480 +void
  1.1481 +MetroInput::DispatchEventIgnoreStatus(WidgetGUIEvent *aEvent)
  1.1482 +{
  1.1483 +  mWidget->DispatchEvent(aEvent, sThrowawayStatus);
  1.1484 +}
  1.1485 +
  1.1486 +void
  1.1487 +MetroInput::UnregisterInputEvents() {
  1.1488 +  // Unregister ourselves for the edge swipe event
  1.1489 +  WRL::ComPtr<UI::Input::IEdgeGestureStatics> edgeStatics;
  1.1490 +  if (SUCCEEDED(Foundation::GetActivationFactory(
  1.1491 +        WRL::Wrappers::HStringReference(
  1.1492 +              RuntimeClass_Windows_UI_Input_EdgeGesture).Get(),
  1.1493 +      edgeStatics.GetAddressOf()))) {
  1.1494 +    WRL::ComPtr<UI::Input::IEdgeGesture> edge;
  1.1495 +    if (SUCCEEDED(edgeStatics->GetForCurrentView(edge.GetAddressOf()))) {
  1.1496 +      edge->remove_Starting(mTokenEdgeStarted);
  1.1497 +      edge->remove_Canceled(mTokenEdgeCanceled);
  1.1498 +      edge->remove_Completed(mTokenEdgeCompleted);
  1.1499 +    }
  1.1500 +  }
  1.1501 +  // Unregister ourselves from the window events. This is extremely important;
  1.1502 +  // once this object is destroyed we don't want Windows to try to send events
  1.1503 +  // to it.
  1.1504 +  mWindow->remove_PointerPressed(mTokenPointerPressed);
  1.1505 +  mWindow->remove_PointerReleased(mTokenPointerReleased);
  1.1506 +  mWindow->remove_PointerMoved(mTokenPointerMoved);
  1.1507 +  mWindow->remove_PointerEntered(mTokenPointerEntered);
  1.1508 +  mWindow->remove_PointerExited(mTokenPointerExited);
  1.1509 +
  1.1510 +  // Unregistering from the gesture recognizer events probably isn't as
  1.1511 +  // necessary since we're about to destroy the gesture recognizer, but
  1.1512 +  // it can't hurt.
  1.1513 +  mGestureRecognizer->remove_ManipulationCompleted(
  1.1514 +                                        mTokenManipulationCompleted);
  1.1515 +  mGestureRecognizer->remove_Tapped(mTokenTapped);
  1.1516 +  mGestureRecognizer->remove_RightTapped(mTokenRightTapped);
  1.1517 +}
  1.1518 +
  1.1519 +void
  1.1520 +MetroInput::RegisterInputEvents()
  1.1521 +{
  1.1522 +  NS_ASSERTION(mWindow, "Must have a window to register for input events!");
  1.1523 +  NS_ASSERTION(mGestureRecognizer,
  1.1524 +               "Must have a GestureRecognizer for input events!");
  1.1525 +  // Register for edge swipe
  1.1526 +  WRL::ComPtr<UI::Input::IEdgeGestureStatics> edgeStatics;
  1.1527 +  Foundation::GetActivationFactory(
  1.1528 +            WRL::Wrappers::HStringReference(
  1.1529 +                    RuntimeClass_Windows_UI_Input_EdgeGesture)
  1.1530 +            .Get(),
  1.1531 +            edgeStatics.GetAddressOf());
  1.1532 +  WRL::ComPtr<UI::Input::IEdgeGesture> edge;
  1.1533 +  edgeStatics->GetForCurrentView(edge.GetAddressOf());
  1.1534 +
  1.1535 +  edge->add_Starting(
  1.1536 +      WRL::Callback<EdgeGestureHandler>(
  1.1537 +                                  this,
  1.1538 +                                  &MetroInput::OnEdgeGestureStarted).Get(),
  1.1539 +      &mTokenEdgeStarted);
  1.1540 +
  1.1541 +  edge->add_Canceled(
  1.1542 +      WRL::Callback<EdgeGestureHandler>(
  1.1543 +                                  this,
  1.1544 +                                  &MetroInput::OnEdgeGestureCanceled).Get(),
  1.1545 +      &mTokenEdgeCanceled);
  1.1546 +
  1.1547 +  edge->add_Completed(
  1.1548 +      WRL::Callback<EdgeGestureHandler>(
  1.1549 +                                  this,
  1.1550 +                                  &MetroInput::OnEdgeGestureCompleted).Get(),
  1.1551 +      &mTokenEdgeCompleted);
  1.1552 +
  1.1553 +  // Set up our Gesture Recognizer to raise events for the gestures we
  1.1554 +  // care about
  1.1555 +  mGestureRecognizer->put_GestureSettings(
  1.1556 +            UI::Input::GestureSettings::GestureSettings_Tap
  1.1557 +          | UI::Input::GestureSettings::GestureSettings_DoubleTap
  1.1558 +          | UI::Input::GestureSettings::GestureSettings_RightTap
  1.1559 +          | UI::Input::GestureSettings::GestureSettings_Hold
  1.1560 +          | UI::Input::GestureSettings::GestureSettings_ManipulationTranslateX
  1.1561 +          | UI::Input::GestureSettings::GestureSettings_ManipulationTranslateY);
  1.1562 +
  1.1563 +  // Register for the pointer events on our Window
  1.1564 +  mWindow->add_PointerPressed(
  1.1565 +      WRL::Callback<PointerEventHandler>(
  1.1566 +        this,
  1.1567 +        &MetroInput::OnPointerPressed).Get(),
  1.1568 +      &mTokenPointerPressed);
  1.1569 +
  1.1570 +  mWindow->add_PointerReleased(
  1.1571 +      WRL::Callback<PointerEventHandler>(
  1.1572 +        this,
  1.1573 +        &MetroInput::OnPointerReleased).Get(),
  1.1574 +      &mTokenPointerReleased);
  1.1575 +
  1.1576 +  mWindow->add_PointerMoved(
  1.1577 +      WRL::Callback<PointerEventHandler>(
  1.1578 +        this,
  1.1579 +        &MetroInput::OnPointerMoved).Get(),
  1.1580 +      &mTokenPointerMoved);
  1.1581 +
  1.1582 +  mWindow->add_PointerEntered(
  1.1583 +      WRL::Callback<PointerEventHandler>(
  1.1584 +        this,
  1.1585 +        &MetroInput::OnPointerEntered).Get(),
  1.1586 +      &mTokenPointerEntered);
  1.1587 +
  1.1588 +  mWindow->add_PointerExited(
  1.1589 +      WRL::Callback<PointerEventHandler>(
  1.1590 +        this,
  1.1591 +        &MetroInput::OnPointerExited).Get(),
  1.1592 +      &mTokenPointerExited);
  1.1593 +
  1.1594 +  // Register for the events raised by our Gesture Recognizer
  1.1595 +  mGestureRecognizer->add_Tapped(
  1.1596 +      WRL::Callback<TappedEventHandler>(
  1.1597 +        this,
  1.1598 +        &MetroInput::OnTapped).Get(),
  1.1599 +      &mTokenTapped);
  1.1600 +
  1.1601 +  mGestureRecognizer->add_RightTapped(
  1.1602 +      WRL::Callback<RightTappedEventHandler>(
  1.1603 +        this,
  1.1604 +        &MetroInput::OnRightTapped).Get(),
  1.1605 +      &mTokenRightTapped);
  1.1606 +
  1.1607 +  mGestureRecognizer->add_ManipulationCompleted(
  1.1608 +      WRL::Callback<ManipulationCompletedEventHandler>(
  1.1609 +        this,
  1.1610 +        &MetroInput::OnManipulationCompleted).Get(),
  1.1611 +      &mTokenManipulationCompleted);
  1.1612 +}
  1.1613 +
  1.1614 +} } }

mercurial