widget/windows/winrt/MetroInput.h
author Rodrigo Silveira <rsilveira@mozilla.com>
Wed, 26 Feb 2014 21:42:49 -0800
changeset 191344 c6fc27e68f455521abf328b28555c1ed97556160
parent 180647 077f8070700455254604f4c52ae57f17afe3bbb8
child 191353 48680af02f8fcafaa10050b6568770b0a1361188
permissions -rw-r--r--
Bug 953012 - Opening a new tab using the overlay while a website is loading creates two about:start tabs r=jimm

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

#pragma once

// Moz headers (alphabetical)
#include "keyboardlayout.h"   // mModifierKeyState
#include "nsBaseHashtable.h"  // mTouches
#include "nsDeque.h"
#include "nsHashKeys.h"       // type of key for mTouches
#include "mozilla/EventForwards.h"
#include "mozilla/layers/APZCTreeManager.h"
#include "mozilla/TouchEvents.h"
#include "mozwrlbase.h"

// System headers (alphabetical)
#include <EventToken.h>     // EventRegistrationToken
#include <stdint.h>         // uint32_t
#include <windows.ui.input.h> // ABI::Window::UI::Input namespace
#include <wrl\client.h>     // Microsoft::WRL::ComPtr class
#include <wrl\implements.h> // Microsoft::WRL::InspectableClass macro

// Moz forward declarations
class MetroWidget;
struct nsIntPoint;

namespace mozilla {
namespace dom {
class Touch;
}
}

// Windows forward declarations
namespace ABI {
  namespace Windows {
    namespace Devices {
      namespace Input {
        enum PointerDeviceType;
      }
    };
    namespace Foundation {
      struct Point;
    };
    namespace UI {
      namespace Core {
        struct ICoreWindow;
        struct IAcceleratorKeyEventArgs;
        struct IKeyEventArgs;
        struct IPointerEventArgs;
      };
      namespace Input {
        struct IEdgeGesture;
        struct IEdgeGestureEventArgs;
        struct IGestureRecognizer;
        struct IManipulationCompletedEventArgs;
        struct IManipulationStartedEventArgs;
        struct IManipulationUpdatedEventArgs;
        struct IPointerPoint;
        struct IRightTappedEventArgs;
        struct ITappedEventArgs;
        struct ManipulationDelta;
      };
    };
  };
};

namespace mozilla {
namespace widget {
namespace winrt {

class TouchEventQueueEntry {
private:
  typedef ABI::Windows::UI::Input::IPointerPoint IPointerPoint;
  typedef ABI::Windows::UI::Input::PointerPoint PointerPoint;
  typedef ABI::Windows::Foundation::Collections::IVector<PointerPoint*> PointerPointVector;

  Microsoft::WRL::ComPtr<PointerPointVector> mPointerPoints;
  Microsoft::WRL::ComPtr<IPointerPoint> mPointerPoint;
  nsAutoPtr<WidgetTouchEvent> mTouchEvent;

public:
  TouchEventQueueEntry(WidgetTouchEvent* aTouchEvent, IPointerPoint* aPointerPoint)
    : mTouchEvent(aTouchEvent), mPointerPoint(aPointerPoint)
  {
  }

  TouchEventQueueEntry(WidgetTouchEvent* aTouchEvent, PointerPointVector* aPointerPoints)
    : mTouchEvent(aTouchEvent), mPointerPoints(aPointerPoints)
  {
  }

  IPointerPoint* GetPointerPoint() {
    return mPointerPoint.Get();
  }

  PointerPointVector* GetPointerPoints() {
    return mPointerPoints.Get();
  }

  WidgetTouchEvent* GetEvent() {
    return mTouchEvent;
  }
};


class MetroInput : public Microsoft::WRL::RuntimeClass<IInspectable>
{
  InspectableClass(L"MetroInput", BaseTrust);

private:
  typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
  typedef uint32_t TouchBehaviorFlags;

  // Devices
  typedef ABI::Windows::Devices::Input::PointerDeviceType PointerDeviceType;

  // UI::Core
  typedef ABI::Windows::UI::Core::ICoreWindow ICoreWindow;
  typedef ABI::Windows::UI::Core::IAcceleratorKeyEventArgs \
                                  IAcceleratorKeyEventArgs;
  typedef ABI::Windows::UI::Core::IKeyEventArgs IKeyEventArgs;
  typedef ABI::Windows::UI::Core::IPointerEventArgs IPointerEventArgs;

  // UI::Input
  typedef ABI::Windows::UI::Input::IEdgeGesture IEdgeGesture;
  typedef ABI::Windows::UI::Input::IEdgeGestureEventArgs IEdgeGestureEventArgs;
  typedef ABI::Windows::UI::Input::IGestureRecognizer IGestureRecognizer;
  typedef ABI::Windows::UI::Input::IManipulationCompletedEventArgs \
                                   IManipulationCompletedEventArgs;
  typedef ABI::Windows::UI::Input::IManipulationStartedEventArgs \
                                   IManipulationStartedEventArgs;
  typedef ABI::Windows::UI::Input::IManipulationUpdatedEventArgs \
                                   IManipulationUpdatedEventArgs;
  typedef ABI::Windows::UI::Input::IPointerPoint IPointerPoint;
  typedef ABI::Windows::UI::Input::IRightTappedEventArgs IRightTappedEventArgs;
  typedef ABI::Windows::UI::Input::ITappedEventArgs ITappedEventArgs;
  typedef ABI::Windows::UI::Input::ManipulationDelta ManipulationDelta;
  typedef ABI::Windows::UI::Input::PointerPoint PointerPoint;

  // Foundation
  typedef ABI::Windows::Foundation::Point Point;
  typedef ABI::Windows::Foundation::Collections::IVector<PointerPoint*> PointerPointVector;

  typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;

public:
  MetroInput(MetroWidget* aWidget,
             ICoreWindow* aWindow);
  virtual ~MetroInput();

  // These input events are received from our window. These are basic
  // pointer and keyboard press events. MetroInput responds to them
  // by sending gecko events and forwarding these input events to its
  // GestureRecognizer to be processed into more complex input events
  // (tap, rightTap, rotate, etc)
  HRESULT OnPointerPressed(ICoreWindow* aSender,
                           IPointerEventArgs* aArgs);
  HRESULT OnPointerReleased(ICoreWindow* aSender,
                            IPointerEventArgs* aArgs);
  HRESULT OnPointerMoved(ICoreWindow* aSender,
                         IPointerEventArgs* aArgs);
  HRESULT OnPointerEntered(ICoreWindow* aSender,
                           IPointerEventArgs* aArgs);
  HRESULT OnPointerExited(ICoreWindow* aSender,
                          IPointerEventArgs* aArgs);

  // The Edge gesture event is special.  It does not come from our window
  // or from our GestureRecognizer.
  HRESULT OnEdgeGestureStarted(IEdgeGesture* aSender,
                               IEdgeGestureEventArgs* aArgs);
  HRESULT OnEdgeGestureCanceled(IEdgeGesture* aSender,
                                IEdgeGestureEventArgs* aArgs);
  HRESULT OnEdgeGestureCompleted(IEdgeGesture* aSender,
                                 IEdgeGestureEventArgs* aArgs);

  // Swipe gesture callback from the GestureRecognizer.
  HRESULT OnManipulationCompleted(IGestureRecognizer* aSender,
                                  IManipulationCompletedEventArgs* aArgs);

  // Tap gesture callback from the GestureRecognizer.
  HRESULT OnTapped(IGestureRecognizer* aSender, ITappedEventArgs* aArgs);
  HRESULT OnRightTapped(IGestureRecognizer* aSender,
                        IRightTappedEventArgs* aArgs);

  void HandleTap(const Point& aPoint, unsigned int aTapCount);
  void HandleLongTap(const Point& aPoint);

  static bool IsInputModeImprecise();

private:
  Microsoft::WRL::ComPtr<ICoreWindow> mWindow;
  Microsoft::WRL::ComPtr<MetroWidget> mWidget;
  Microsoft::WRL::ComPtr<IGestureRecognizer> mGestureRecognizer;

  ModifierKeyState mModifierKeyState;

  // Tracking input level
  enum InputPrecisionLevel {
    LEVEL_PRECISE,
    LEVEL_IMPRECISE
  };
  static InputPrecisionLevel sCurrentInputLevel;
  void UpdateInputLevel(InputPrecisionLevel aInputLevel);

  // Initialization/Uninitialization helpers
  void RegisterInputEvents();
  void UnregisterInputEvents();

  // Hit testing for apz content
  bool mNonApzTargetForTouch;
  bool HitTestChrome(const LayoutDeviceIntPoint& pt);

  // Event processing helpers.  See function definitions for more info.
  bool TransformRefPoint(const Point& aPosition,
                         LayoutDeviceIntPoint& aRefPointOut);
  void TransformTouchEvent(WidgetTouchEvent* aEvent);
  void OnPointerNonTouch(IPointerPoint* aPoint);
  void AddPointerMoveDataToRecognizer(IPointerEventArgs* aArgs);
  void InitGeckoMouseEventFromPointerPoint(WidgetMouseEvent* aEvent,
                                           IPointerPoint* aPoint);
  void ProcessManipulationDelta(ManipulationDelta const& aDelta,
                                Point const& aPosition,
                                uint32_t aMagEventType,
                                uint32_t aRotEventType);
  uint16_t ProcessInputTypeForGesture(IEdgeGestureEventArgs* aArgs);
  bool ShouldDeliverInputToRecognizer();

  // Returns array of allowed touch behaviors  for touch points of given TouchEvent.
  // Note: event argument should be transformed via apzc before supplying to this method.
  void GetAllowedTouchBehavior(WidgetTouchEvent* aTransformedEvent, nsTArray<TouchBehaviorFlags>& aOutBehaviors);

  // Checks whether any touch behavior is allowed.
  bool IsTouchBehaviorForbidden(const nsTArray<TouchBehaviorFlags>& aTouchBehaviors);

  // The W3C spec states that "whether preventDefault has been called" should
  // be tracked on a per-touchpoint basis, but it also states that touchstart
  // and touchmove events can contain multiple changed points.  At the time of
  // this writing, W3C touch events are in the process of being abandoned in
  // favor of W3C pointer events, so it is unlikely that this ambiguity will
  // be resolved.  Additionally, nsPresShell tracks "whether preventDefault
  // has been called" on a per-touch-session basis.  We will follow a similar
  // approach here.
  //
  // Specifically:
  //   If preventDefault is called on the FIRST touchstart event of a touch
  //   session, then no default actions associated with any touchstart,
  //   touchmove, or touchend events will be taken.  This means that no
  //   mousedowns, mousemoves, mouseups, clicks, swipes, rotations,
  //   magnifications, etc will be dispatched during this touch session;
  //   only touchstart, touchmove, and touchend.
  //
  //   If preventDefault is called on the FIRST touchmove event of a touch
  //   session, then no default actions associated with the _touchmove_ events
  //   will be dispatched.  However, it is still possible that additional
  //   events will be generated based on the touchstart and touchend events.
  //   For example, a set of mousemove, mousedown, and mouseup events might
  //   be sent if a tap is detected.
  bool mContentConsumingTouch;
  bool mApzConsumingTouch;
  bool mCancelable;
  bool mRecognizerWantsEvents;

  nsTArray<uint32_t> mCanceledIds;

  // In the old Win32 way of doing things, we would receive a WM_TOUCH event
  // that told us the state of every touchpoint on the touch surface.  If
  // multiple touchpoints had moved since the last update we would learn
  // about all their movement simultaneously.
  //
  // In the new WinRT way of doing things, we receive a separate
  // PointerPressed/PointerMoved/PointerReleased event for each touchpoint
  // that has changed.
  //
  // When we learn of touch input, we dispatch gecko events in response.
  // With the new WinRT way of doing things, we would end up sending many
  // more gecko events than we would using the Win32 mechanism.  E.g.,
  // for 5 active touchpoints, we would be sending 5 times as many gecko
  // events.  This caused performance to visibly degrade on modestly-powered
  // machines.  In response, we no longer send touch events immediately
  // upon receiving PointerPressed or PointerMoved.  Instead, we store
  // the updated touchpoint info and record the fact that the touchpoint
  // has changed.  If ever we try to update a touchpoint has already
  // changed, we dispatch a touch event containing all the changed touches.
  void InitTouchEventTouchList(WidgetTouchEvent* aEvent);
  nsBaseHashtable<nsUint32HashKey,
                  nsRefPtr<mozilla::dom::Touch>,
                  nsRefPtr<mozilla::dom::Touch> > mTouches;

  // These registration tokens are set when we register ourselves to receive
  // events from our window.  We must hold on to them for the entire duration
  // that we want to receive these events.  When we are done, we must
  // unregister ourself with the window using these tokens.
  EventRegistrationToken mTokenPointerPressed;
  EventRegistrationToken mTokenPointerReleased;
  EventRegistrationToken mTokenPointerMoved;
  EventRegistrationToken mTokenPointerEntered;
  EventRegistrationToken mTokenPointerExited;

  // When we register ourselves to handle edge gestures, we receive a
  // token. To we unregister ourselves, we must use the token we received.
  EventRegistrationToken mTokenEdgeStarted;
  EventRegistrationToken mTokenEdgeCanceled;
  EventRegistrationToken mTokenEdgeCompleted;

  // These registration tokens are set when we register ourselves to receive
  // events from our GestureRecognizer.  It's probably not a huge deal if we
  // don't unregister ourselves with our GestureRecognizer before destroying
  // the GestureRecognizer, but it can't hurt.
  EventRegistrationToken mTokenManipulationCompleted;
  EventRegistrationToken mTokenTapped;
  EventRegistrationToken mTokenRightTapped;

  // Due to a limitation added in 8.1 the ui thread can't re-enter the main
  // native event dispatcher in MetroAppShell. So all events delivered to us
  // on the ui thread via a native event dispatch call get bounced through
  // the gecko thread event queue using runnables. Most events can be sent
  // async without the need to see the status result. Those that do have
  // specialty callbacks. Note any event that arrives to us on the ui thread
  // that originates from another thread is safe to send sync.

  // Async event dispatching
  void DispatchAsyncEventIgnoreStatus(WidgetInputEvent* aEvent);
  void DispatchAsyncTouchEvent(TouchEventQueueEntry* queueEntry);

  // Async event callbacks
  void DeliverNextQueuedEventIgnoreStatus();
  void DeliverNextQueuedTouchEvent();
  void DeliverNextQueuedNoMoveTouch();

  void HandleFirstTouchStartEvent(WidgetTouchEvent* aEvent);
  void HandleFirstTouchMoveEvent(WidgetTouchEvent* aEvent);

  // Sync event dispatching
  void DispatchEventIgnoreStatus(WidgetGUIEvent* aEvent);
  void DispatchTouchCancel(WidgetTouchEvent* aEvent);

  nsDeque mInputEventQueue;
  mozilla::layers::ScrollableLayerGuid mTargetAPZCGuid;
  static nsEventStatus sThrowawayStatus;
};

} } }