michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set sw=2 ts=8 et tw=80 : */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_layers_AsyncPanZoomController_h michael@0: #define mozilla_layers_AsyncPanZoomController_h michael@0: michael@0: #include "CrossProcessMutex.h" michael@0: #include "mozilla/layers/GeckoContentController.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/EventForwards.h" michael@0: #include "mozilla/Monitor.h" michael@0: #include "mozilla/ReentrantMonitor.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "InputData.h" michael@0: #include "Axis.h" michael@0: #include "TaskThrottler.h" michael@0: #include "gfx3DMatrix.h" michael@0: michael@0: #include "base/message_loop.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace ipc { michael@0: michael@0: class SharedMemoryBasic; michael@0: michael@0: } michael@0: michael@0: namespace layers { michael@0: michael@0: struct ScrollableLayerGuid; michael@0: class CompositorParent; michael@0: class GestureEventListener; michael@0: class ContainerLayer; michael@0: class PCompositorParent; michael@0: class ViewTransform; michael@0: class APZCTreeManager; michael@0: class AsyncPanZoomAnimation; michael@0: class FlingAnimation; michael@0: michael@0: /** michael@0: * Controller for all panning and zooming logic. Any time a user input is michael@0: * detected and it must be processed in some way to affect what the user sees, michael@0: * it goes through here. Listens for any input event from InputData and can michael@0: * optionally handle WidgetGUIEvent-derived touch events, but this must be done michael@0: * on the main thread. Note that this class completely cross-platform. michael@0: * michael@0: * Input events originate on the UI thread of the platform that this runs on, michael@0: * and are then sent to this class. This class processes the event in some way; michael@0: * for example, a touch move will usually lead to a panning of content (though michael@0: * of course there are exceptions, such as if content preventDefaults the event, michael@0: * or if the target frame is not scrollable). The compositor interacts with this michael@0: * class by locking it and querying it for the current transform matrix based on michael@0: * the panning and zooming logic that was invoked on the UI thread. michael@0: * michael@0: * Currently, each outer DOM window (i.e. a website in a tab, but not any michael@0: * subframes) has its own AsyncPanZoomController. In the future, to support michael@0: * asynchronously scrolled subframes, we want to have one AsyncPanZoomController michael@0: * per frame. michael@0: */ michael@0: class AsyncPanZoomController { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController) michael@0: michael@0: typedef mozilla::MonitorAutoLock MonitorAutoLock; michael@0: typedef uint32_t TouchBehaviorFlags; michael@0: michael@0: public: michael@0: enum GestureBehavior { michael@0: // The platform code is responsible for forwarding gesture events here. We michael@0: // will not attempt to generate gesture events from MultiTouchInputs. michael@0: DEFAULT_GESTURES, michael@0: // An instance of GestureEventListener is used to detect gestures. This is michael@0: // handled completely internally within this class. michael@0: USE_GESTURE_DETECTOR michael@0: }; michael@0: michael@0: /** michael@0: * Constant describing the tolerance in distance we use, multiplied by the michael@0: * device DPI, before we start panning the screen. This is to prevent us from michael@0: * accidentally processing taps as touch moves, and from very short/accidental michael@0: * touches moving the screen. michael@0: */ michael@0: static float GetTouchStartTolerance(); michael@0: michael@0: AsyncPanZoomController(uint64_t aLayersId, michael@0: APZCTreeManager* aTreeManager, michael@0: GeckoContentController* aController, michael@0: GestureBehavior aGestures = DEFAULT_GESTURES); michael@0: michael@0: // -------------------------------------------------------------------------- michael@0: // These methods must only be called on the gecko thread. michael@0: // michael@0: michael@0: /** michael@0: * Read the various prefs and do any global initialization for all APZC instances. michael@0: * This must be run on the gecko thread before any APZC instances are actually michael@0: * used for anything meaningful. michael@0: */ michael@0: static void InitializeGlobalState(); michael@0: michael@0: // -------------------------------------------------------------------------- michael@0: // These methods must only be called on the controller/UI thread. michael@0: // michael@0: michael@0: /** michael@0: * General handler for incoming input events. Manipulates the frame metrics michael@0: * based on what type of input it is. For example, a PinchGestureEvent will michael@0: * cause scaling. This should only be called externally to this class. michael@0: * HandleInputEvent() should be used internally. michael@0: */ michael@0: nsEventStatus ReceiveInputEvent(const InputData& aEvent); michael@0: michael@0: /** michael@0: * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom michael@0: * in. The actual animation is done on the compositor thread after being set michael@0: * up. michael@0: */ michael@0: void ZoomToRect(CSSRect aRect); michael@0: michael@0: /** michael@0: * If we have touch listeners, this should always be called when we know michael@0: * definitively whether or not content has preventDefaulted any touch events michael@0: * that have come in. If |aPreventDefault| is true, any touch events in the michael@0: * queue will be discarded. michael@0: */ michael@0: void ContentReceivedTouch(bool aPreventDefault); michael@0: michael@0: /** michael@0: * Updates any zoom constraints contained in the tag. michael@0: */ michael@0: void UpdateZoomConstraints(const ZoomConstraints& aConstraints); michael@0: michael@0: /** michael@0: * Return the zoom constraints last set for this APZC (in the constructor michael@0: * or in UpdateZoomConstraints()). michael@0: */ michael@0: ZoomConstraints GetZoomConstraints() const; michael@0: michael@0: /** michael@0: * Schedules a runnable to run on the controller/UI thread at some time michael@0: * in the future. michael@0: */ michael@0: void PostDelayedTask(Task* aTask, int aDelayMs); michael@0: michael@0: // -------------------------------------------------------------------------- michael@0: // These methods must only be called on the compositor thread. michael@0: // michael@0: michael@0: bool UpdateAnimation(const TimeStamp& aSampleTime); michael@0: michael@0: /** michael@0: * The compositor calls this when it's about to draw pannable/zoomable content michael@0: * and is setting up transforms for compositing the layer tree. This is not michael@0: * idempotent. For example, a fling transform can be applied each time this is michael@0: * called (though not necessarily). |aSampleTime| is the time that this is michael@0: * sampled at; this is used for interpolating animations. Calling this sets a michael@0: * new transform in |aNewTransform| which should be multiplied to the transform michael@0: * in the shadow layer corresponding to this APZC. michael@0: * michael@0: * Return value indicates whether or not any currently running animation michael@0: * should continue. That is, if true, the compositor should schedule another michael@0: * composite. michael@0: */ michael@0: bool SampleContentTransformForFrame(const TimeStamp& aSampleTime, michael@0: ViewTransform* aNewTransform, michael@0: ScreenPoint& aScrollOffset); michael@0: michael@0: /** michael@0: * A shadow layer update has arrived. |aLayerMetrics| is the new FrameMetrics michael@0: * for the container layer corresponding to this APZC. michael@0: * |aIsFirstPaint| is a flag passed from the shadow michael@0: * layers code indicating that the frame metrics being sent with this call are michael@0: * the initial metrics and the initial paint of the frame has just happened. michael@0: */ michael@0: void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint); michael@0: michael@0: /** michael@0: * The platform implementation must set the compositor parent so that we can michael@0: * request composites. michael@0: */ michael@0: void SetCompositorParent(CompositorParent* aCompositorParent); michael@0: michael@0: /** michael@0: * The platform implementation must set the cross process compositor if michael@0: * there is one associated with the layer tree. The cross process compositor michael@0: * allows the APZC to share its FrameMetrics with the content process. michael@0: * The shared FrameMetrics is used in progressive paint updates. michael@0: */ michael@0: void SetCrossProcessCompositorParent(PCompositorParent* aCrossProcessCompositorParent); michael@0: michael@0: // -------------------------------------------------------------------------- michael@0: // These methods can be called from any thread. michael@0: // michael@0: michael@0: /** michael@0: * Shut down the controller/UI thread state and prepare to be michael@0: * deleted (which may happen from any thread). michael@0: */ michael@0: void Destroy(); michael@0: michael@0: /** michael@0: * Returns true if Destroy() has already been called on this APZC instance. michael@0: */ michael@0: bool IsDestroyed(); michael@0: michael@0: /** michael@0: * Returns the incremental transformation corresponding to the async pan/zoom michael@0: * in progress. That is, when this transform is multiplied with the layer's michael@0: * existing transform, it will make the layer appear with the desired pan/zoom michael@0: * amount. michael@0: */ michael@0: ViewTransform GetCurrentAsyncTransform(); michael@0: michael@0: /** michael@0: * Returns the part of the async transform that will remain once Gecko does a michael@0: * repaint at the desired metrics. That is, in the steady state: michael@0: * gfx3DMatrix(GetCurrentAsyncTransform()) === GetNontransientAsyncTransform() michael@0: */ michael@0: gfx3DMatrix GetNontransientAsyncTransform(); michael@0: michael@0: /** michael@0: * Returns the transform to take something from the coordinate space of the michael@0: * last thing we know gecko painted, to the coordinate space of the last thing michael@0: * we asked gecko to paint. In cases where that last request has not yet been michael@0: * processed, this is needed to transform input events properly into a space michael@0: * gecko will understand. michael@0: */ michael@0: gfx3DMatrix GetTransformToLastDispatchedPaint(); michael@0: michael@0: /** michael@0: * Recalculates the displayport. Ideally, this should paint an area bigger michael@0: * than the composite-to dimensions so that when you scroll down, you don't michael@0: * checkerboard immediately. This includes a bunch of logic, including michael@0: * algorithms to bias painting in the direction of the velocity. michael@0: */ michael@0: static const LayerMargin CalculatePendingDisplayPort( michael@0: const FrameMetrics& aFrameMetrics, michael@0: const ScreenPoint& aVelocity, michael@0: double aEstimatedPaintDuration); michael@0: michael@0: /** michael@0: * Send an mozbrowserasyncscroll event. michael@0: * *** The monitor must be held while calling this. michael@0: */ michael@0: void SendAsyncScrollEvent(); michael@0: michael@0: /** michael@0: * Handler for events which should not be intercepted by the touch listener. michael@0: * Does the work for ReceiveInputEvent(). michael@0: */ michael@0: nsEventStatus HandleInputEvent(const InputData& aEvent); michael@0: michael@0: /** michael@0: * Handler for gesture events. michael@0: * Currently some gestures are detected in GestureEventListener that calls michael@0: * APZC back through this handler in order to avoid recursive calls to michael@0: * APZC::HandleInputEvent() which is supposed to do the work for michael@0: * ReceiveInputEvent(). michael@0: */ michael@0: nsEventStatus HandleGestureEvent(const InputData& aEvent); michael@0: michael@0: /** michael@0: * Populates the provided object (if non-null) with the scrollable guid of this apzc. michael@0: */ michael@0: void GetGuid(ScrollableLayerGuid* aGuidOut); michael@0: michael@0: /** michael@0: * Returns the scrollable guid of this apzc. michael@0: */ michael@0: ScrollableLayerGuid GetGuid(); michael@0: michael@0: /** michael@0: * Returns true if this APZC instance is for the layer identified by the guid. michael@0: */ michael@0: bool Matches(const ScrollableLayerGuid& aGuid); michael@0: michael@0: /** michael@0: * Sync panning and zooming animation using a fixed frame time. michael@0: * This will ensure that we animate the APZC correctly with other external michael@0: * animations to the same timestamp. michael@0: */ michael@0: static void SetFrameTime(const TimeStamp& aMilliseconds); michael@0: michael@0: void StartAnimation(AsyncPanZoomAnimation* aAnimation); michael@0: michael@0: /** michael@0: * Cancels any currently running animation. Note that all this does is set the michael@0: * state of the AsyncPanZoomController back to NOTHING, but it is the michael@0: * animation's responsibility to check this before advancing. michael@0: */ michael@0: void CancelAnimation(); michael@0: michael@0: /** michael@0: * Take over a fling with the given velocity from another APZC. Used for michael@0: * during overscroll handoff for a fling. michael@0: */ michael@0: void TakeOverFling(ScreenPoint aVelocity); michael@0: michael@0: /** michael@0: * Returns allowed touch behavior for the given point on the scrollable layer. michael@0: * Internally performs a kind of hit testing based on the regions constructed michael@0: * on the main thread and attached to the current scrollable layer. Each of such regions michael@0: * contains info about allowed touch behavior. If regions info isn't enough it returns michael@0: * UNKNOWN value and we should switch to the fallback approach - asking content. michael@0: * TODO: for now it's only a stub and returns hardcoded magic value. As soon as bug 928833 michael@0: * is done we should integrate its logic here. michael@0: */ michael@0: TouchBehaviorFlags GetAllowedTouchBehavior(ScreenIntPoint& aPoint); michael@0: michael@0: /** michael@0: * Sets allowed touch behavior for current touch session. michael@0: * This method is invoked by the APZCTreeManager which in its turn invoked by michael@0: * the widget after performing touch-action values retrieving. michael@0: * Must be called after receiving the TOUCH_START even that started the michael@0: * touch session. michael@0: */ michael@0: void SetAllowedTouchBehavior(const nsTArray& aBehaviors); michael@0: michael@0: /** michael@0: * Returns whether this APZC is for an element marked with the 'scrollgrab' michael@0: * attribute. michael@0: */ michael@0: bool HasScrollgrab() const { return mFrameMetrics.mHasScrollgrab; } michael@0: michael@0: /** michael@0: * Set an extra offset for testing async scrolling. michael@0: */ michael@0: void SetTestAsyncScrollOffset(const CSSPoint& aPoint) michael@0: { michael@0: mTestAsyncScrollOffset = aPoint; michael@0: } michael@0: michael@0: /** michael@0: * Returns whether this APZC has room to be panned (in any direction). michael@0: */ michael@0: bool IsPannable() const; michael@0: michael@0: protected: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: ~AsyncPanZoomController(); michael@0: michael@0: /** michael@0: * Helper method for touches beginning. Sets everything up for panning and any michael@0: * multitouch gestures. michael@0: */ michael@0: nsEventStatus OnTouchStart(const MultiTouchInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for touches moving. Does any transforms needed when panning. michael@0: */ michael@0: nsEventStatus OnTouchMove(const MultiTouchInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for touches ending. Redraws the screen if necessary and does michael@0: * any cleanup after a touch has ended. michael@0: */ michael@0: nsEventStatus OnTouchEnd(const MultiTouchInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for touches being cancelled. Treated roughly the same as a michael@0: * touch ending (OnTouchEnd()). michael@0: */ michael@0: nsEventStatus OnTouchCancel(const MultiTouchInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for scales beginning. Distinct from the OnTouch* handlers in michael@0: * that this implies some outside implementation has determined that the user michael@0: * is pinching. michael@0: */ michael@0: nsEventStatus OnScaleBegin(const PinchGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for scaling. As the user moves their fingers when pinching, michael@0: * this changes the scale of the page. michael@0: */ michael@0: nsEventStatus OnScale(const PinchGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for scales ending. Redraws the screen if necessary and does michael@0: * any cleanup after a scale has ended. michael@0: */ michael@0: nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Helper methods for long press gestures. michael@0: */ michael@0: nsEventStatus OnLongPress(const TapGestureInput& aEvent); michael@0: nsEventStatus OnLongPressUp(const TapGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for single tap gestures. michael@0: */ michael@0: nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for a single tap confirmed. michael@0: */ michael@0: nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method for double taps. michael@0: */ michael@0: nsEventStatus OnDoubleTap(const TapGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Helper method to cancel any gesture currently going to Gecko. Used michael@0: * primarily when a user taps the screen over some clickable content but then michael@0: * pans down instead of letting go (i.e. to cancel a previous touch so that a michael@0: * new one can properly take effect. michael@0: */ michael@0: nsEventStatus OnCancelTap(const TapGestureInput& aEvent); michael@0: michael@0: /** michael@0: * Scrolls the viewport by an X,Y offset. michael@0: */ michael@0: void ScrollBy(const CSSPoint& aOffset); michael@0: michael@0: /** michael@0: * Scales the viewport by an amount (note that it multiplies this scale in to michael@0: * the current scale, it doesn't set it to |aScale|). Also considers a focus michael@0: * point so that the page zooms inward/outward from that point. michael@0: */ michael@0: void ScaleWithFocus(float aScale, michael@0: const CSSPoint& aFocus); michael@0: michael@0: /** michael@0: * Schedules a composite on the compositor thread. Wrapper for michael@0: * CompositorParent::ScheduleRenderOnCompositorThread(). michael@0: */ michael@0: void ScheduleComposite(); michael@0: michael@0: /** michael@0: * Gets the displacement of the current touch since it began. That is, it is michael@0: * the distance between the current position and the initial position of the michael@0: * current touch (this only makes sense if a touch is currently happening and michael@0: * OnTouchMove() is being invoked). michael@0: */ michael@0: float PanDistance(); michael@0: michael@0: /** michael@0: * Gets a vector of the velocities of each axis. michael@0: */ michael@0: const ScreenPoint GetVelocityVector(); michael@0: michael@0: /** michael@0: * Gets a reference to the first touch point from a MultiTouchInput. This michael@0: * gets only the first one and assumes the rest are either missing or not michael@0: * relevant. michael@0: */ michael@0: ScreenIntPoint& GetFirstTouchScreenPoint(const MultiTouchInput& aEvent); michael@0: michael@0: /** michael@0: * Sets the panning state basing on the pan direction angle and current touch-action value. michael@0: */ michael@0: void HandlePanningWithTouchAction(double angle, TouchBehaviorFlags value); michael@0: michael@0: /** michael@0: * Sets the panning state ignoring the touch action value. michael@0: */ michael@0: void HandlePanning(double angle); michael@0: michael@0: /** michael@0: * Sets up anything needed for panning. This takes us out of the "TOUCHING" michael@0: * state and starts actually panning us. michael@0: */ michael@0: nsEventStatus StartPanning(const MultiTouchInput& aStartPoint); michael@0: michael@0: /** michael@0: * Wrapper for Axis::UpdateWithTouchAtDevicePoint(). Calls this function for michael@0: * both axes and factors in the time delta from the last update. michael@0: */ michael@0: void UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent); michael@0: michael@0: /** michael@0: * Does any panning required due to a new touch event. michael@0: */ michael@0: void TrackTouch(const MultiTouchInput& aEvent); michael@0: michael@0: /** michael@0: * Utility function to send updated FrameMetrics to Gecko so that it can paint michael@0: * the displayport area. Calls into GeckoContentController to do the actual michael@0: * work. Note that only one paint request can be active at a time. If a paint michael@0: * request is made while a paint is currently happening, it gets queued up. If michael@0: * a new paint request arrives before a paint is completed, the old request michael@0: * gets discarded. michael@0: */ michael@0: void RequestContentRepaint(); michael@0: michael@0: /** michael@0: * Tell the paint throttler to request a content repaint with the given michael@0: * metrics. (Helper function used by RequestContentRepaint.) michael@0: */ michael@0: void RequestContentRepaint(FrameMetrics& aFrameMetrics); michael@0: michael@0: /** michael@0: * Actually send the next pending paint request to gecko. michael@0: */ michael@0: void DispatchRepaintRequest(const FrameMetrics& aFrameMetrics); michael@0: michael@0: /** michael@0: * Advances a fling by an interpolated amount based on the passed in |aDelta|. michael@0: * This should be called whenever sampling the content transform for this michael@0: * frame. Returns true if the fling animation should be advanced by one frame, michael@0: * or false if there is no fling or the fling has ended. michael@0: */ michael@0: bool DoFling(const TimeDuration& aDelta); michael@0: michael@0: /** michael@0: * Gets the current frame metrics. This is *not* the Gecko copy stored in the michael@0: * layers code. michael@0: */ michael@0: const FrameMetrics& GetFrameMetrics(); michael@0: michael@0: /** michael@0: * Sets the timer for content response to a series of touch events, if it michael@0: * hasn't been already. This is to prevent us from batching up touch events michael@0: * indefinitely in the case that content doesn't respond with whether or not michael@0: * it wants to preventDefault. When the timer is fired, the touch event queue michael@0: * will be flushed. michael@0: */ michael@0: void SetContentResponseTimer(); michael@0: michael@0: /** michael@0: * Timeout function for content response. This should be called on a timer michael@0: * after we get our first touch event in a batch, under the condition that we michael@0: * waiting for response from content. If a notification comes indicating whether or not michael@0: * content preventDefaulted a series of touch events and touch behavior values are michael@0: * set before the timeout, the timeout should be cancelled. michael@0: */ michael@0: void TimeoutContentResponse(); michael@0: michael@0: /** michael@0: * Timeout function for mozbrowserasyncscroll event. Because we throttle michael@0: * mozbrowserasyncscroll events in some conditions, this function ensures michael@0: * that the last mozbrowserasyncscroll event will be fired after a period of michael@0: * time. michael@0: */ michael@0: void FireAsyncScrollOnTimeout(); michael@0: michael@0: private: michael@0: enum PanZoomState { michael@0: NOTHING, /* no touch-start events received */ michael@0: FLING, /* all touches removed, but we're still scrolling page */ michael@0: TOUCHING, /* one touch-start event received */ michael@0: michael@0: PANNING, /* panning the frame */ michael@0: PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */ michael@0: PANNING_LOCKED_Y, /* as above for Y axis */ michael@0: michael@0: CROSS_SLIDING_X, /* Panning disabled while user does a horizontal gesture michael@0: on a vertically-scrollable view. This used for the michael@0: Windows Metro "cross-slide" gesture. */ michael@0: CROSS_SLIDING_Y, /* as above for Y axis */ michael@0: michael@0: PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */ michael@0: ANIMATING_ZOOM, /* animated zoom to a new rect */ michael@0: WAITING_CONTENT_RESPONSE, /* a state halfway between NOTHING and TOUCHING - the user has michael@0: put a finger down, but we don't yet know if a touch listener has michael@0: prevented the default actions yet and the allowed touch behavior michael@0: was not set yet. we still need to abort animations. */ michael@0: }; michael@0: michael@0: // State related to a single touch block. Does not persist across touch blocks. michael@0: struct TouchBlockState { michael@0: michael@0: TouchBlockState() michael@0: : mAllowedTouchBehaviorSet(false), michael@0: mPreventDefault(false), michael@0: mPreventDefaultSet(false), michael@0: mSingleTapOccurred(false) michael@0: {} michael@0: michael@0: // Values of allowed touch behavior for touch points of this touch block. michael@0: // Since there are maybe a few current active touch points per time (multitouch case) michael@0: // and each touch point should have its own value of allowed touch behavior- we're michael@0: // keeping an array of allowed touch behavior values, not the single value. michael@0: nsTArray mAllowedTouchBehaviors; michael@0: michael@0: // Specifies whether mAllowedTouchBehaviors is set for this touch events block. michael@0: bool mAllowedTouchBehaviorSet; michael@0: michael@0: // Flag used to specify that content prevented the default behavior of this michael@0: // touch events block. michael@0: bool mPreventDefault; michael@0: michael@0: // Specifies whether mPreventDefault property is set for this touch events block. michael@0: bool mPreventDefaultSet; michael@0: michael@0: // Specifies whether a single tap event was generated during this touch block. michael@0: bool mSingleTapOccurred; michael@0: }; michael@0: michael@0: /* michael@0: * Returns whether current touch behavior values allow pinch-zooming. michael@0: */ michael@0: bool TouchActionAllowPinchZoom(); michael@0: michael@0: /* michael@0: * Returns whether current touch behavior values allow double-tap-zooming. michael@0: */ michael@0: bool TouchActionAllowDoubleTapZoom(); michael@0: michael@0: /* michael@0: * Returns allowed touch behavior from the mAllowedTouchBehavior array. michael@0: * In case apzc didn't receive touch behavior values within the timeout michael@0: * it returns default value. michael@0: */ michael@0: TouchBehaviorFlags GetTouchBehavior(uint32_t touchIndex); michael@0: michael@0: /** michael@0: * To move from the WAITING_CONTENT_RESPONSE state to TOUCHING one we need two michael@0: * conditions set: get content listeners response (whether they called preventDefault) michael@0: * and get allowed touch behaviors. michael@0: * This method checks both conditions and changes (or not changes) state michael@0: * appropriately. michael@0: */ michael@0: void CheckContentResponse(); michael@0: michael@0: /** michael@0: * Helper to set the current state. Holds the monitor before actually setting michael@0: * it and fires content controller events based on state changes. Always set michael@0: * the state using this call, do not set it directly. michael@0: */ michael@0: void SetState(PanZoomState aState); michael@0: michael@0: /** michael@0: * Convert ScreenPoint relative to this APZC to CSSPoint relative michael@0: * to the parent document. This excludes the transient compositor transform. michael@0: * NOTE: This must be converted to CSSPoint relative to the child michael@0: * document before sending over IPC. michael@0: */ michael@0: bool ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut); michael@0: michael@0: /** michael@0: * Internal helpers for checking general state of this apzc. michael@0: */ michael@0: bool IsTransformingState(PanZoomState aState); michael@0: bool IsPanningState(PanZoomState mState); michael@0: michael@0: enum AxisLockMode { michael@0: FREE, /* No locking at all */ michael@0: STANDARD, /* Default axis locking mode that remains locked until pan ends*/ michael@0: STICKY, /* Allow lock to be broken, with hysteresis */ michael@0: }; michael@0: michael@0: static AxisLockMode GetAxisLockMode(); michael@0: michael@0: // Convert a point from local screen coordinates to parent layer coordinates. michael@0: // This is a common operation as inputs from the tree manager are in screen michael@0: // coordinates but the composition bounds is in parent layer coordinates. michael@0: ParentLayerPoint ToParentLayerCoords(const ScreenPoint& aPoint); michael@0: michael@0: // Update mFrameMetrics.mTransformScale. This should be called whenever michael@0: // our CSS transform or the non-transient part of our async transform michael@0: // changes, as it corresponds to the scale portion of those transforms. michael@0: void UpdateTransformScale(); michael@0: michael@0: // Helper function for OnSingleTapUp() and OnSingleTapConfirmed(). michael@0: nsEventStatus GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers); michael@0: michael@0: // Common processing at the end of a touch block. michael@0: void OnTouchEndOrCancel(); michael@0: michael@0: uint64_t mLayersId; michael@0: nsRefPtr mCompositorParent; michael@0: PCompositorParent* mCrossProcessCompositorParent; michael@0: TaskThrottler mPaintThrottler; michael@0: michael@0: /* Access to the following two fields is protected by the mRefPtrMonitor, michael@0: since they are accessed on the UI thread but can be cleared on the michael@0: compositor thread. */ michael@0: nsRefPtr mGeckoContentController; michael@0: nsRefPtr mGestureEventListener; michael@0: Monitor mRefPtrMonitor; michael@0: michael@0: /* Utility functions that return a addrefed pointer to the corresponding fields. */ michael@0: already_AddRefed GetGeckoContentController(); michael@0: already_AddRefed GetGestureEventListener(); michael@0: michael@0: protected: michael@0: // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the michael@0: // monitor. Do not read from or modify either of them without locking. michael@0: FrameMetrics mFrameMetrics; michael@0: michael@0: // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|. michael@0: // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the michael@0: // monitor should be held. When setting |mState|, either the SetState() michael@0: // function can be used, or the monitor can be held and then |mState| updated. michael@0: // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h. michael@0: // This is mutable to allow entering it from 'const' methods; doing otherwise michael@0: // would significantly limit what methods could be 'const'. michael@0: mutable ReentrantMonitor mMonitor; michael@0: michael@0: // Specifies whether we should use touch-action css property. Initialized from michael@0: // the preferences. This property (in comparison with the global one) simplifies michael@0: // testing apzc with (and without) touch-action property enabled concurrently michael@0: // (e.g. with the gtest framework). michael@0: bool mTouchActionPropertyEnabled; michael@0: michael@0: private: michael@0: // Metrics of the container layer corresponding to this APZC. This is michael@0: // stored here so that it is accessible from the UI/controller thread. michael@0: // These are the metrics at last content paint, the most recent michael@0: // values we were notified of in NotifyLayersUpdate(). Since it represents michael@0: // the Gecko state, it should be used as a basis for untransformation when michael@0: // sending messages back to Gecko. michael@0: FrameMetrics mLastContentPaintMetrics; michael@0: // The last metrics that we requested a paint for. These are used to make sure michael@0: // that we're not requesting a paint of the same thing that's already drawn. michael@0: // If we don't do this check, we don't get a ShadowLayersUpdated back. michael@0: FrameMetrics mLastPaintRequestMetrics; michael@0: // The last metrics that we actually sent to Gecko. This allows us to transform michael@0: // inputs into a coordinate space that Gecko knows about. This assumes the pipe michael@0: // through which input events and repaint requests are sent to Gecko operates michael@0: // in a FIFO manner. michael@0: FrameMetrics mLastDispatchedPaintMetrics; michael@0: michael@0: nsTArray mTouchQueue; michael@0: michael@0: CancelableTask* mContentResponseTimeoutTask; michael@0: michael@0: AxisX mX; michael@0: AxisY mY; michael@0: michael@0: // This flag is set to true when we are in a axis-locked pan as a result of michael@0: // the touch-action CSS property. michael@0: bool mPanDirRestricted; michael@0: michael@0: // Most up-to-date constraints on zooming. These should always be reasonable michael@0: // values; for example, allowing a min zoom of 0.0 can cause very bad things michael@0: // to happen. michael@0: ZoomConstraints mZoomConstraints; michael@0: michael@0: // The last time the compositor has sampled the content transform for this michael@0: // frame. michael@0: TimeStamp mLastSampleTime; michael@0: // The last time a touch event came through on the UI thread. michael@0: uint32_t mLastEventTime; michael@0: michael@0: // Stores the previous focus point if there is a pinch gesture happening. Used michael@0: // to allow panning by moving multiple fingers (thus moving the focus point). michael@0: ParentLayerPoint mLastZoomFocus; michael@0: michael@0: // Stores the state of panning and zooming this frame. This is protected by michael@0: // |mMonitor|; that is, it should be held whenever this is updated. michael@0: PanZoomState mState; michael@0: michael@0: // The last time and offset we fire the mozbrowserasyncscroll event when michael@0: // compositor has sampled the content transform for this frame. michael@0: TimeStamp mLastAsyncScrollTime; michael@0: CSSPoint mLastAsyncScrollOffset; michael@0: michael@0: // The current offset drawn on the screen, it may not be sent since we have michael@0: // throttling policy for mozbrowserasyncscroll event. michael@0: CSSPoint mCurrentAsyncScrollOffset; michael@0: michael@0: // The delay task triggered by the throttling mozbrowserasyncscroll event michael@0: // ensures the last mozbrowserasyncscroll event is always been fired. michael@0: CancelableTask* mAsyncScrollTimeoutTask; michael@0: michael@0: // Flag used to determine whether or not we should try to enter the michael@0: // WAITING_LISTENERS state. This is used in the case that we are processing a michael@0: // queued up event block. If set, this means that we are handling this queue michael@0: // and we don't want to queue the events back up again. michael@0: bool mHandlingTouchQueue; michael@0: michael@0: // Stores information about the current touch block. michael@0: TouchBlockState mTouchBlockState; michael@0: michael@0: // Extra offset to add in SampleContentTransformForFrame for testing michael@0: CSSPoint mTestAsyncScrollOffset; michael@0: michael@0: RefPtr mAnimation; michael@0: michael@0: friend class Axis; michael@0: friend class FlingAnimation; michael@0: michael@0: michael@0: /* =================================================================== michael@0: * The functions and members in this section are used to build a tree michael@0: * structure out of APZC instances. This tree can only be walked or michael@0: * manipulated while holding the lock in the associated APZCTreeManager michael@0: * instance. michael@0: */ michael@0: public: michael@0: void SetLastChild(AsyncPanZoomController* child) { michael@0: mLastChild = child; michael@0: if (child) { michael@0: child->mParent = this; michael@0: } michael@0: } michael@0: michael@0: void SetPrevSibling(AsyncPanZoomController* sibling) { michael@0: mPrevSibling = sibling; michael@0: if (sibling) { michael@0: sibling->mParent = mParent; michael@0: } michael@0: } michael@0: michael@0: AsyncPanZoomController* GetLastChild() const { return mLastChild; } michael@0: AsyncPanZoomController* GetPrevSibling() const { return mPrevSibling; } michael@0: AsyncPanZoomController* GetParent() const { return mParent; } michael@0: michael@0: /* Returns true if there is no APZC higher in the tree with the same michael@0: * layers id. michael@0: */ michael@0: bool IsRootForLayersId() const { michael@0: return !mParent || (mParent->mLayersId != mLayersId); michael@0: } michael@0: michael@0: bool IsRootForLayersId(const uint64_t& aLayersId) const { michael@0: return (mLayersId == aLayersId) && IsRootForLayersId(); michael@0: } michael@0: michael@0: private: michael@0: // This is a raw pointer to avoid introducing a reference cycle between michael@0: // AsyncPanZoomController and APZCTreeManager. Since these objects don't michael@0: // live on the main thread, we can't use the cycle collector with them. michael@0: // The APZCTreeManager owns the lifetime of the APZCs, so nulling this michael@0: // pointer out in Destroy() will prevent accessing deleted memory. michael@0: Atomic mTreeManager; michael@0: michael@0: nsRefPtr mLastChild; michael@0: nsRefPtr mPrevSibling; michael@0: nsRefPtr mParent; michael@0: michael@0: michael@0: /* =================================================================== michael@0: * The functions and members in this section are used in building the michael@0: * scroll handoff chain, so that we can have seamless scrolling continue michael@0: * across APZC instances. michael@0: */ michael@0: public: michael@0: void SetScrollHandoffParentId(FrameMetrics::ViewID aScrollParentId) { michael@0: mScrollParentId = aScrollParentId; michael@0: } michael@0: michael@0: FrameMetrics::ViewID GetScrollHandoffParentId() const { michael@0: return mScrollParentId; michael@0: } michael@0: michael@0: /** michael@0: * Attempt to scroll in response to a touch-move from |aStartPoint| to michael@0: * |aEndPoint|, which are in our (transformed) screen coordinates. michael@0: * Due to overscroll handling, there may not actually have been a touch-move michael@0: * at these points, but this function will scroll as if there had been. michael@0: * If this attempt causes overscroll (i.e. the layer cannot be scrolled michael@0: * by the entire amount requested), the overscroll is passed back to the michael@0: * tree manager via APZCTreeManager::DispatchScroll(). michael@0: * |aOverscrollHandoffChainIndex| is used by the tree manager to keep track michael@0: * of which APZC to hand off the overscroll to; this function increments it michael@0: * and passes it on to APZCTreeManager::DispatchScroll() in the event of michael@0: * overscroll. michael@0: */ michael@0: void AttemptScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint, michael@0: uint32_t aOverscrollHandoffChainIndex = 0); michael@0: michael@0: void FlushRepaintForOverscrollHandoff(); michael@0: michael@0: private: michael@0: FrameMetrics::ViewID mScrollParentId; michael@0: michael@0: /** michael@0: * A helper function for calling APZCTreeManager::DispatchScroll(). michael@0: * Guards against the case where the APZC is being concurrently destroyed michael@0: * (and thus mTreeManager is being nulled out). michael@0: */ michael@0: void CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint, michael@0: uint32_t aOverscrollHandoffChainIndex); michael@0: michael@0: michael@0: /* =================================================================== michael@0: * The functions and members in this section are used to maintain the michael@0: * area that this APZC instance is responsible for. This is used when michael@0: * hit-testing to see which APZC instance should handle touch events. michael@0: */ michael@0: public: michael@0: void SetLayerHitTestData(const ParentLayerRect& aRect, const gfx3DMatrix& aTransformToLayer, michael@0: const gfx3DMatrix& aTransformForLayer) { michael@0: mVisibleRect = aRect; michael@0: mAncestorTransform = aTransformToLayer; michael@0: mCSSTransform = aTransformForLayer; michael@0: UpdateTransformScale(); michael@0: } michael@0: michael@0: gfx3DMatrix GetAncestorTransform() const { michael@0: return mAncestorTransform; michael@0: } michael@0: michael@0: gfx3DMatrix GetCSSTransform() const { michael@0: return mCSSTransform; michael@0: } michael@0: michael@0: bool VisibleRegionContains(const ParentLayerPoint& aPoint) const { michael@0: return mVisibleRect.Contains(aPoint); michael@0: } michael@0: michael@0: private: michael@0: /* This is the visible region of the layer that this APZC corresponds to, in michael@0: * that layer's screen pixels (the same coordinate system in which this APZC michael@0: * receives events in ReceiveInputEvent()). */ michael@0: ParentLayerRect mVisibleRect; michael@0: /* This is the cumulative CSS transform for all the layers between the parent michael@0: * APZC and this one (not inclusive) */ michael@0: gfx3DMatrix mAncestorTransform; michael@0: /* This is the CSS transform for this APZC's layer. */ michael@0: gfx3DMatrix mCSSTransform; michael@0: michael@0: michael@0: /* =================================================================== michael@0: * The functions and members in this section are used for sharing the michael@0: * FrameMetrics across processes for the progressive tiling code. michael@0: */ michael@0: private: michael@0: /* Unique id assigned to each APZC. Used with ViewID to uniquely identify michael@0: * shared FrameMeterics used in progressive tile painting. */ michael@0: const uint32_t mAPZCId; michael@0: michael@0: ipc::SharedMemoryBasic* mSharedFrameMetricsBuffer; michael@0: CrossProcessMutex* mSharedLock; michael@0: /** michael@0: * Called when ever mFrameMetrics is updated so that if it is being michael@0: * shared with the content process the shared FrameMetrics may be updated. michael@0: */ michael@0: void UpdateSharedCompositorFrameMetrics(); michael@0: /** michael@0: * Create a shared memory buffer for containing the FrameMetrics and michael@0: * a CrossProcessMutex that may be shared with the content process michael@0: * for use in progressive tiled update calculations. michael@0: */ michael@0: void ShareCompositorFrameMetrics(); michael@0: }; michael@0: michael@0: class AsyncPanZoomAnimation { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation) michael@0: michael@0: public: michael@0: AsyncPanZoomAnimation(const TimeDuration& aRepaintInterval = michael@0: TimeDuration::Forever()) michael@0: : mRepaintInterval(aRepaintInterval) michael@0: { } michael@0: michael@0: virtual bool Sample(FrameMetrics& aFrameMetrics, michael@0: const TimeDuration& aDelta) = 0; michael@0: michael@0: /** michael@0: * Get the deferred tasks in |mDeferredTasks|. See |mDeferredTasks| michael@0: * for more information. michael@0: * Clears |mDeferredTasks|. michael@0: */ michael@0: Vector TakeDeferredTasks() { michael@0: Vector result; michael@0: mDeferredTasks.swap(result); michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * Specifies how frequently (at most) we want to do repaints during the michael@0: * animation sequence. TimeDuration::Forever() will cause it to only repaint michael@0: * at the end of the animation. michael@0: */ michael@0: TimeDuration mRepaintInterval; michael@0: michael@0: protected: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~AsyncPanZoomAnimation() michael@0: { } michael@0: michael@0: /** michael@0: * Tasks scheduled for execution after the APZC's mMonitor is released. michael@0: * Derived classes can add tasks here in Sample(), and the APZC can call michael@0: * ExecuteDeferredTasks() to execute them. michael@0: */ michael@0: Vector mDeferredTasks; michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif // mozilla_layers_PanZoomController_h