gfx/layers/apz/src/AsyncPanZoomController.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set sw=2 ts=8 et tw=80 : */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef mozilla_layers_AsyncPanZoomController_h
michael@0 8 #define mozilla_layers_AsyncPanZoomController_h
michael@0 9
michael@0 10 #include "CrossProcessMutex.h"
michael@0 11 #include "mozilla/layers/GeckoContentController.h"
michael@0 12 #include "mozilla/Attributes.h"
michael@0 13 #include "mozilla/EventForwards.h"
michael@0 14 #include "mozilla/Monitor.h"
michael@0 15 #include "mozilla/ReentrantMonitor.h"
michael@0 16 #include "mozilla/RefPtr.h"
michael@0 17 #include "mozilla/Atomics.h"
michael@0 18 #include "InputData.h"
michael@0 19 #include "Axis.h"
michael@0 20 #include "TaskThrottler.h"
michael@0 21 #include "gfx3DMatrix.h"
michael@0 22
michael@0 23 #include "base/message_loop.h"
michael@0 24
michael@0 25 namespace mozilla {
michael@0 26
michael@0 27 namespace ipc {
michael@0 28
michael@0 29 class SharedMemoryBasic;
michael@0 30
michael@0 31 }
michael@0 32
michael@0 33 namespace layers {
michael@0 34
michael@0 35 struct ScrollableLayerGuid;
michael@0 36 class CompositorParent;
michael@0 37 class GestureEventListener;
michael@0 38 class ContainerLayer;
michael@0 39 class PCompositorParent;
michael@0 40 class ViewTransform;
michael@0 41 class APZCTreeManager;
michael@0 42 class AsyncPanZoomAnimation;
michael@0 43 class FlingAnimation;
michael@0 44
michael@0 45 /**
michael@0 46 * Controller for all panning and zooming logic. Any time a user input is
michael@0 47 * detected and it must be processed in some way to affect what the user sees,
michael@0 48 * it goes through here. Listens for any input event from InputData and can
michael@0 49 * optionally handle WidgetGUIEvent-derived touch events, but this must be done
michael@0 50 * on the main thread. Note that this class completely cross-platform.
michael@0 51 *
michael@0 52 * Input events originate on the UI thread of the platform that this runs on,
michael@0 53 * and are then sent to this class. This class processes the event in some way;
michael@0 54 * for example, a touch move will usually lead to a panning of content (though
michael@0 55 * of course there are exceptions, such as if content preventDefaults the event,
michael@0 56 * or if the target frame is not scrollable). The compositor interacts with this
michael@0 57 * class by locking it and querying it for the current transform matrix based on
michael@0 58 * the panning and zooming logic that was invoked on the UI thread.
michael@0 59 *
michael@0 60 * Currently, each outer DOM window (i.e. a website in a tab, but not any
michael@0 61 * subframes) has its own AsyncPanZoomController. In the future, to support
michael@0 62 * asynchronously scrolled subframes, we want to have one AsyncPanZoomController
michael@0 63 * per frame.
michael@0 64 */
michael@0 65 class AsyncPanZoomController {
michael@0 66 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController)
michael@0 67
michael@0 68 typedef mozilla::MonitorAutoLock MonitorAutoLock;
michael@0 69 typedef uint32_t TouchBehaviorFlags;
michael@0 70
michael@0 71 public:
michael@0 72 enum GestureBehavior {
michael@0 73 // The platform code is responsible for forwarding gesture events here. We
michael@0 74 // will not attempt to generate gesture events from MultiTouchInputs.
michael@0 75 DEFAULT_GESTURES,
michael@0 76 // An instance of GestureEventListener is used to detect gestures. This is
michael@0 77 // handled completely internally within this class.
michael@0 78 USE_GESTURE_DETECTOR
michael@0 79 };
michael@0 80
michael@0 81 /**
michael@0 82 * Constant describing the tolerance in distance we use, multiplied by the
michael@0 83 * device DPI, before we start panning the screen. This is to prevent us from
michael@0 84 * accidentally processing taps as touch moves, and from very short/accidental
michael@0 85 * touches moving the screen.
michael@0 86 */
michael@0 87 static float GetTouchStartTolerance();
michael@0 88
michael@0 89 AsyncPanZoomController(uint64_t aLayersId,
michael@0 90 APZCTreeManager* aTreeManager,
michael@0 91 GeckoContentController* aController,
michael@0 92 GestureBehavior aGestures = DEFAULT_GESTURES);
michael@0 93
michael@0 94 // --------------------------------------------------------------------------
michael@0 95 // These methods must only be called on the gecko thread.
michael@0 96 //
michael@0 97
michael@0 98 /**
michael@0 99 * Read the various prefs and do any global initialization for all APZC instances.
michael@0 100 * This must be run on the gecko thread before any APZC instances are actually
michael@0 101 * used for anything meaningful.
michael@0 102 */
michael@0 103 static void InitializeGlobalState();
michael@0 104
michael@0 105 // --------------------------------------------------------------------------
michael@0 106 // These methods must only be called on the controller/UI thread.
michael@0 107 //
michael@0 108
michael@0 109 /**
michael@0 110 * General handler for incoming input events. Manipulates the frame metrics
michael@0 111 * based on what type of input it is. For example, a PinchGestureEvent will
michael@0 112 * cause scaling. This should only be called externally to this class.
michael@0 113 * HandleInputEvent() should be used internally.
michael@0 114 */
michael@0 115 nsEventStatus ReceiveInputEvent(const InputData& aEvent);
michael@0 116
michael@0 117 /**
michael@0 118 * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
michael@0 119 * in. The actual animation is done on the compositor thread after being set
michael@0 120 * up.
michael@0 121 */
michael@0 122 void ZoomToRect(CSSRect aRect);
michael@0 123
michael@0 124 /**
michael@0 125 * If we have touch listeners, this should always be called when we know
michael@0 126 * definitively whether or not content has preventDefaulted any touch events
michael@0 127 * that have come in. If |aPreventDefault| is true, any touch events in the
michael@0 128 * queue will be discarded.
michael@0 129 */
michael@0 130 void ContentReceivedTouch(bool aPreventDefault);
michael@0 131
michael@0 132 /**
michael@0 133 * Updates any zoom constraints contained in the <meta name="viewport"> tag.
michael@0 134 */
michael@0 135 void UpdateZoomConstraints(const ZoomConstraints& aConstraints);
michael@0 136
michael@0 137 /**
michael@0 138 * Return the zoom constraints last set for this APZC (in the constructor
michael@0 139 * or in UpdateZoomConstraints()).
michael@0 140 */
michael@0 141 ZoomConstraints GetZoomConstraints() const;
michael@0 142
michael@0 143 /**
michael@0 144 * Schedules a runnable to run on the controller/UI thread at some time
michael@0 145 * in the future.
michael@0 146 */
michael@0 147 void PostDelayedTask(Task* aTask, int aDelayMs);
michael@0 148
michael@0 149 // --------------------------------------------------------------------------
michael@0 150 // These methods must only be called on the compositor thread.
michael@0 151 //
michael@0 152
michael@0 153 bool UpdateAnimation(const TimeStamp& aSampleTime);
michael@0 154
michael@0 155 /**
michael@0 156 * The compositor calls this when it's about to draw pannable/zoomable content
michael@0 157 * and is setting up transforms for compositing the layer tree. This is not
michael@0 158 * idempotent. For example, a fling transform can be applied each time this is
michael@0 159 * called (though not necessarily). |aSampleTime| is the time that this is
michael@0 160 * sampled at; this is used for interpolating animations. Calling this sets a
michael@0 161 * new transform in |aNewTransform| which should be multiplied to the transform
michael@0 162 * in the shadow layer corresponding to this APZC.
michael@0 163 *
michael@0 164 * Return value indicates whether or not any currently running animation
michael@0 165 * should continue. That is, if true, the compositor should schedule another
michael@0 166 * composite.
michael@0 167 */
michael@0 168 bool SampleContentTransformForFrame(const TimeStamp& aSampleTime,
michael@0 169 ViewTransform* aNewTransform,
michael@0 170 ScreenPoint& aScrollOffset);
michael@0 171
michael@0 172 /**
michael@0 173 * A shadow layer update has arrived. |aLayerMetrics| is the new FrameMetrics
michael@0 174 * for the container layer corresponding to this APZC.
michael@0 175 * |aIsFirstPaint| is a flag passed from the shadow
michael@0 176 * layers code indicating that the frame metrics being sent with this call are
michael@0 177 * the initial metrics and the initial paint of the frame has just happened.
michael@0 178 */
michael@0 179 void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint);
michael@0 180
michael@0 181 /**
michael@0 182 * The platform implementation must set the compositor parent so that we can
michael@0 183 * request composites.
michael@0 184 */
michael@0 185 void SetCompositorParent(CompositorParent* aCompositorParent);
michael@0 186
michael@0 187 /**
michael@0 188 * The platform implementation must set the cross process compositor if
michael@0 189 * there is one associated with the layer tree. The cross process compositor
michael@0 190 * allows the APZC to share its FrameMetrics with the content process.
michael@0 191 * The shared FrameMetrics is used in progressive paint updates.
michael@0 192 */
michael@0 193 void SetCrossProcessCompositorParent(PCompositorParent* aCrossProcessCompositorParent);
michael@0 194
michael@0 195 // --------------------------------------------------------------------------
michael@0 196 // These methods can be called from any thread.
michael@0 197 //
michael@0 198
michael@0 199 /**
michael@0 200 * Shut down the controller/UI thread state and prepare to be
michael@0 201 * deleted (which may happen from any thread).
michael@0 202 */
michael@0 203 void Destroy();
michael@0 204
michael@0 205 /**
michael@0 206 * Returns true if Destroy() has already been called on this APZC instance.
michael@0 207 */
michael@0 208 bool IsDestroyed();
michael@0 209
michael@0 210 /**
michael@0 211 * Returns the incremental transformation corresponding to the async pan/zoom
michael@0 212 * in progress. That is, when this transform is multiplied with the layer's
michael@0 213 * existing transform, it will make the layer appear with the desired pan/zoom
michael@0 214 * amount.
michael@0 215 */
michael@0 216 ViewTransform GetCurrentAsyncTransform();
michael@0 217
michael@0 218 /**
michael@0 219 * Returns the part of the async transform that will remain once Gecko does a
michael@0 220 * repaint at the desired metrics. That is, in the steady state:
michael@0 221 * gfx3DMatrix(GetCurrentAsyncTransform()) === GetNontransientAsyncTransform()
michael@0 222 */
michael@0 223 gfx3DMatrix GetNontransientAsyncTransform();
michael@0 224
michael@0 225 /**
michael@0 226 * Returns the transform to take something from the coordinate space of the
michael@0 227 * last thing we know gecko painted, to the coordinate space of the last thing
michael@0 228 * we asked gecko to paint. In cases where that last request has not yet been
michael@0 229 * processed, this is needed to transform input events properly into a space
michael@0 230 * gecko will understand.
michael@0 231 */
michael@0 232 gfx3DMatrix GetTransformToLastDispatchedPaint();
michael@0 233
michael@0 234 /**
michael@0 235 * Recalculates the displayport. Ideally, this should paint an area bigger
michael@0 236 * than the composite-to dimensions so that when you scroll down, you don't
michael@0 237 * checkerboard immediately. This includes a bunch of logic, including
michael@0 238 * algorithms to bias painting in the direction of the velocity.
michael@0 239 */
michael@0 240 static const LayerMargin CalculatePendingDisplayPort(
michael@0 241 const FrameMetrics& aFrameMetrics,
michael@0 242 const ScreenPoint& aVelocity,
michael@0 243 double aEstimatedPaintDuration);
michael@0 244
michael@0 245 /**
michael@0 246 * Send an mozbrowserasyncscroll event.
michael@0 247 * *** The monitor must be held while calling this.
michael@0 248 */
michael@0 249 void SendAsyncScrollEvent();
michael@0 250
michael@0 251 /**
michael@0 252 * Handler for events which should not be intercepted by the touch listener.
michael@0 253 * Does the work for ReceiveInputEvent().
michael@0 254 */
michael@0 255 nsEventStatus HandleInputEvent(const InputData& aEvent);
michael@0 256
michael@0 257 /**
michael@0 258 * Handler for gesture events.
michael@0 259 * Currently some gestures are detected in GestureEventListener that calls
michael@0 260 * APZC back through this handler in order to avoid recursive calls to
michael@0 261 * APZC::HandleInputEvent() which is supposed to do the work for
michael@0 262 * ReceiveInputEvent().
michael@0 263 */
michael@0 264 nsEventStatus HandleGestureEvent(const InputData& aEvent);
michael@0 265
michael@0 266 /**
michael@0 267 * Populates the provided object (if non-null) with the scrollable guid of this apzc.
michael@0 268 */
michael@0 269 void GetGuid(ScrollableLayerGuid* aGuidOut);
michael@0 270
michael@0 271 /**
michael@0 272 * Returns the scrollable guid of this apzc.
michael@0 273 */
michael@0 274 ScrollableLayerGuid GetGuid();
michael@0 275
michael@0 276 /**
michael@0 277 * Returns true if this APZC instance is for the layer identified by the guid.
michael@0 278 */
michael@0 279 bool Matches(const ScrollableLayerGuid& aGuid);
michael@0 280
michael@0 281 /**
michael@0 282 * Sync panning and zooming animation using a fixed frame time.
michael@0 283 * This will ensure that we animate the APZC correctly with other external
michael@0 284 * animations to the same timestamp.
michael@0 285 */
michael@0 286 static void SetFrameTime(const TimeStamp& aMilliseconds);
michael@0 287
michael@0 288 void StartAnimation(AsyncPanZoomAnimation* aAnimation);
michael@0 289
michael@0 290 /**
michael@0 291 * Cancels any currently running animation. Note that all this does is set the
michael@0 292 * state of the AsyncPanZoomController back to NOTHING, but it is the
michael@0 293 * animation's responsibility to check this before advancing.
michael@0 294 */
michael@0 295 void CancelAnimation();
michael@0 296
michael@0 297 /**
michael@0 298 * Take over a fling with the given velocity from another APZC. Used for
michael@0 299 * during overscroll handoff for a fling.
michael@0 300 */
michael@0 301 void TakeOverFling(ScreenPoint aVelocity);
michael@0 302
michael@0 303 /**
michael@0 304 * Returns allowed touch behavior for the given point on the scrollable layer.
michael@0 305 * Internally performs a kind of hit testing based on the regions constructed
michael@0 306 * on the main thread and attached to the current scrollable layer. Each of such regions
michael@0 307 * contains info about allowed touch behavior. If regions info isn't enough it returns
michael@0 308 * UNKNOWN value and we should switch to the fallback approach - asking content.
michael@0 309 * TODO: for now it's only a stub and returns hardcoded magic value. As soon as bug 928833
michael@0 310 * is done we should integrate its logic here.
michael@0 311 */
michael@0 312 TouchBehaviorFlags GetAllowedTouchBehavior(ScreenIntPoint& aPoint);
michael@0 313
michael@0 314 /**
michael@0 315 * Sets allowed touch behavior for current touch session.
michael@0 316 * This method is invoked by the APZCTreeManager which in its turn invoked by
michael@0 317 * the widget after performing touch-action values retrieving.
michael@0 318 * Must be called after receiving the TOUCH_START even that started the
michael@0 319 * touch session.
michael@0 320 */
michael@0 321 void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
michael@0 322
michael@0 323 /**
michael@0 324 * Returns whether this APZC is for an element marked with the 'scrollgrab'
michael@0 325 * attribute.
michael@0 326 */
michael@0 327 bool HasScrollgrab() const { return mFrameMetrics.mHasScrollgrab; }
michael@0 328
michael@0 329 /**
michael@0 330 * Set an extra offset for testing async scrolling.
michael@0 331 */
michael@0 332 void SetTestAsyncScrollOffset(const CSSPoint& aPoint)
michael@0 333 {
michael@0 334 mTestAsyncScrollOffset = aPoint;
michael@0 335 }
michael@0 336
michael@0 337 /**
michael@0 338 * Returns whether this APZC has room to be panned (in any direction).
michael@0 339 */
michael@0 340 bool IsPannable() const;
michael@0 341
michael@0 342 protected:
michael@0 343 // Protected destructor, to discourage deletion outside of Release():
michael@0 344 ~AsyncPanZoomController();
michael@0 345
michael@0 346 /**
michael@0 347 * Helper method for touches beginning. Sets everything up for panning and any
michael@0 348 * multitouch gestures.
michael@0 349 */
michael@0 350 nsEventStatus OnTouchStart(const MultiTouchInput& aEvent);
michael@0 351
michael@0 352 /**
michael@0 353 * Helper method for touches moving. Does any transforms needed when panning.
michael@0 354 */
michael@0 355 nsEventStatus OnTouchMove(const MultiTouchInput& aEvent);
michael@0 356
michael@0 357 /**
michael@0 358 * Helper method for touches ending. Redraws the screen if necessary and does
michael@0 359 * any cleanup after a touch has ended.
michael@0 360 */
michael@0 361 nsEventStatus OnTouchEnd(const MultiTouchInput& aEvent);
michael@0 362
michael@0 363 /**
michael@0 364 * Helper method for touches being cancelled. Treated roughly the same as a
michael@0 365 * touch ending (OnTouchEnd()).
michael@0 366 */
michael@0 367 nsEventStatus OnTouchCancel(const MultiTouchInput& aEvent);
michael@0 368
michael@0 369 /**
michael@0 370 * Helper method for scales beginning. Distinct from the OnTouch* handlers in
michael@0 371 * that this implies some outside implementation has determined that the user
michael@0 372 * is pinching.
michael@0 373 */
michael@0 374 nsEventStatus OnScaleBegin(const PinchGestureInput& aEvent);
michael@0 375
michael@0 376 /**
michael@0 377 * Helper method for scaling. As the user moves their fingers when pinching,
michael@0 378 * this changes the scale of the page.
michael@0 379 */
michael@0 380 nsEventStatus OnScale(const PinchGestureInput& aEvent);
michael@0 381
michael@0 382 /**
michael@0 383 * Helper method for scales ending. Redraws the screen if necessary and does
michael@0 384 * any cleanup after a scale has ended.
michael@0 385 */
michael@0 386 nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent);
michael@0 387
michael@0 388 /**
michael@0 389 * Helper methods for long press gestures.
michael@0 390 */
michael@0 391 nsEventStatus OnLongPress(const TapGestureInput& aEvent);
michael@0 392 nsEventStatus OnLongPressUp(const TapGestureInput& aEvent);
michael@0 393
michael@0 394 /**
michael@0 395 * Helper method for single tap gestures.
michael@0 396 */
michael@0 397 nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent);
michael@0 398
michael@0 399 /**
michael@0 400 * Helper method for a single tap confirmed.
michael@0 401 */
michael@0 402 nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent);
michael@0 403
michael@0 404 /**
michael@0 405 * Helper method for double taps.
michael@0 406 */
michael@0 407 nsEventStatus OnDoubleTap(const TapGestureInput& aEvent);
michael@0 408
michael@0 409 /**
michael@0 410 * Helper method to cancel any gesture currently going to Gecko. Used
michael@0 411 * primarily when a user taps the screen over some clickable content but then
michael@0 412 * pans down instead of letting go (i.e. to cancel a previous touch so that a
michael@0 413 * new one can properly take effect.
michael@0 414 */
michael@0 415 nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
michael@0 416
michael@0 417 /**
michael@0 418 * Scrolls the viewport by an X,Y offset.
michael@0 419 */
michael@0 420 void ScrollBy(const CSSPoint& aOffset);
michael@0 421
michael@0 422 /**
michael@0 423 * Scales the viewport by an amount (note that it multiplies this scale in to
michael@0 424 * the current scale, it doesn't set it to |aScale|). Also considers a focus
michael@0 425 * point so that the page zooms inward/outward from that point.
michael@0 426 */
michael@0 427 void ScaleWithFocus(float aScale,
michael@0 428 const CSSPoint& aFocus);
michael@0 429
michael@0 430 /**
michael@0 431 * Schedules a composite on the compositor thread. Wrapper for
michael@0 432 * CompositorParent::ScheduleRenderOnCompositorThread().
michael@0 433 */
michael@0 434 void ScheduleComposite();
michael@0 435
michael@0 436 /**
michael@0 437 * Gets the displacement of the current touch since it began. That is, it is
michael@0 438 * the distance between the current position and the initial position of the
michael@0 439 * current touch (this only makes sense if a touch is currently happening and
michael@0 440 * OnTouchMove() is being invoked).
michael@0 441 */
michael@0 442 float PanDistance();
michael@0 443
michael@0 444 /**
michael@0 445 * Gets a vector of the velocities of each axis.
michael@0 446 */
michael@0 447 const ScreenPoint GetVelocityVector();
michael@0 448
michael@0 449 /**
michael@0 450 * Gets a reference to the first touch point from a MultiTouchInput. This
michael@0 451 * gets only the first one and assumes the rest are either missing or not
michael@0 452 * relevant.
michael@0 453 */
michael@0 454 ScreenIntPoint& GetFirstTouchScreenPoint(const MultiTouchInput& aEvent);
michael@0 455
michael@0 456 /**
michael@0 457 * Sets the panning state basing on the pan direction angle and current touch-action value.
michael@0 458 */
michael@0 459 void HandlePanningWithTouchAction(double angle, TouchBehaviorFlags value);
michael@0 460
michael@0 461 /**
michael@0 462 * Sets the panning state ignoring the touch action value.
michael@0 463 */
michael@0 464 void HandlePanning(double angle);
michael@0 465
michael@0 466 /**
michael@0 467 * Sets up anything needed for panning. This takes us out of the "TOUCHING"
michael@0 468 * state and starts actually panning us.
michael@0 469 */
michael@0 470 nsEventStatus StartPanning(const MultiTouchInput& aStartPoint);
michael@0 471
michael@0 472 /**
michael@0 473 * Wrapper for Axis::UpdateWithTouchAtDevicePoint(). Calls this function for
michael@0 474 * both axes and factors in the time delta from the last update.
michael@0 475 */
michael@0 476 void UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent);
michael@0 477
michael@0 478 /**
michael@0 479 * Does any panning required due to a new touch event.
michael@0 480 */
michael@0 481 void TrackTouch(const MultiTouchInput& aEvent);
michael@0 482
michael@0 483 /**
michael@0 484 * Utility function to send updated FrameMetrics to Gecko so that it can paint
michael@0 485 * the displayport area. Calls into GeckoContentController to do the actual
michael@0 486 * work. Note that only one paint request can be active at a time. If a paint
michael@0 487 * request is made while a paint is currently happening, it gets queued up. If
michael@0 488 * a new paint request arrives before a paint is completed, the old request
michael@0 489 * gets discarded.
michael@0 490 */
michael@0 491 void RequestContentRepaint();
michael@0 492
michael@0 493 /**
michael@0 494 * Tell the paint throttler to request a content repaint with the given
michael@0 495 * metrics. (Helper function used by RequestContentRepaint.)
michael@0 496 */
michael@0 497 void RequestContentRepaint(FrameMetrics& aFrameMetrics);
michael@0 498
michael@0 499 /**
michael@0 500 * Actually send the next pending paint request to gecko.
michael@0 501 */
michael@0 502 void DispatchRepaintRequest(const FrameMetrics& aFrameMetrics);
michael@0 503
michael@0 504 /**
michael@0 505 * Advances a fling by an interpolated amount based on the passed in |aDelta|.
michael@0 506 * This should be called whenever sampling the content transform for this
michael@0 507 * frame. Returns true if the fling animation should be advanced by one frame,
michael@0 508 * or false if there is no fling or the fling has ended.
michael@0 509 */
michael@0 510 bool DoFling(const TimeDuration& aDelta);
michael@0 511
michael@0 512 /**
michael@0 513 * Gets the current frame metrics. This is *not* the Gecko copy stored in the
michael@0 514 * layers code.
michael@0 515 */
michael@0 516 const FrameMetrics& GetFrameMetrics();
michael@0 517
michael@0 518 /**
michael@0 519 * Sets the timer for content response to a series of touch events, if it
michael@0 520 * hasn't been already. This is to prevent us from batching up touch events
michael@0 521 * indefinitely in the case that content doesn't respond with whether or not
michael@0 522 * it wants to preventDefault. When the timer is fired, the touch event queue
michael@0 523 * will be flushed.
michael@0 524 */
michael@0 525 void SetContentResponseTimer();
michael@0 526
michael@0 527 /**
michael@0 528 * Timeout function for content response. This should be called on a timer
michael@0 529 * after we get our first touch event in a batch, under the condition that we
michael@0 530 * waiting for response from content. If a notification comes indicating whether or not
michael@0 531 * content preventDefaulted a series of touch events and touch behavior values are
michael@0 532 * set before the timeout, the timeout should be cancelled.
michael@0 533 */
michael@0 534 void TimeoutContentResponse();
michael@0 535
michael@0 536 /**
michael@0 537 * Timeout function for mozbrowserasyncscroll event. Because we throttle
michael@0 538 * mozbrowserasyncscroll events in some conditions, this function ensures
michael@0 539 * that the last mozbrowserasyncscroll event will be fired after a period of
michael@0 540 * time.
michael@0 541 */
michael@0 542 void FireAsyncScrollOnTimeout();
michael@0 543
michael@0 544 private:
michael@0 545 enum PanZoomState {
michael@0 546 NOTHING, /* no touch-start events received */
michael@0 547 FLING, /* all touches removed, but we're still scrolling page */
michael@0 548 TOUCHING, /* one touch-start event received */
michael@0 549
michael@0 550 PANNING, /* panning the frame */
michael@0 551 PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */
michael@0 552 PANNING_LOCKED_Y, /* as above for Y axis */
michael@0 553
michael@0 554 CROSS_SLIDING_X, /* Panning disabled while user does a horizontal gesture
michael@0 555 on a vertically-scrollable view. This used for the
michael@0 556 Windows Metro "cross-slide" gesture. */
michael@0 557 CROSS_SLIDING_Y, /* as above for Y axis */
michael@0 558
michael@0 559 PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
michael@0 560 ANIMATING_ZOOM, /* animated zoom to a new rect */
michael@0 561 WAITING_CONTENT_RESPONSE, /* a state halfway between NOTHING and TOUCHING - the user has
michael@0 562 put a finger down, but we don't yet know if a touch listener has
michael@0 563 prevented the default actions yet and the allowed touch behavior
michael@0 564 was not set yet. we still need to abort animations. */
michael@0 565 };
michael@0 566
michael@0 567 // State related to a single touch block. Does not persist across touch blocks.
michael@0 568 struct TouchBlockState {
michael@0 569
michael@0 570 TouchBlockState()
michael@0 571 : mAllowedTouchBehaviorSet(false),
michael@0 572 mPreventDefault(false),
michael@0 573 mPreventDefaultSet(false),
michael@0 574 mSingleTapOccurred(false)
michael@0 575 {}
michael@0 576
michael@0 577 // Values of allowed touch behavior for touch points of this touch block.
michael@0 578 // Since there are maybe a few current active touch points per time (multitouch case)
michael@0 579 // and each touch point should have its own value of allowed touch behavior- we're
michael@0 580 // keeping an array of allowed touch behavior values, not the single value.
michael@0 581 nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
michael@0 582
michael@0 583 // Specifies whether mAllowedTouchBehaviors is set for this touch events block.
michael@0 584 bool mAllowedTouchBehaviorSet;
michael@0 585
michael@0 586 // Flag used to specify that content prevented the default behavior of this
michael@0 587 // touch events block.
michael@0 588 bool mPreventDefault;
michael@0 589
michael@0 590 // Specifies whether mPreventDefault property is set for this touch events block.
michael@0 591 bool mPreventDefaultSet;
michael@0 592
michael@0 593 // Specifies whether a single tap event was generated during this touch block.
michael@0 594 bool mSingleTapOccurred;
michael@0 595 };
michael@0 596
michael@0 597 /*
michael@0 598 * Returns whether current touch behavior values allow pinch-zooming.
michael@0 599 */
michael@0 600 bool TouchActionAllowPinchZoom();
michael@0 601
michael@0 602 /*
michael@0 603 * Returns whether current touch behavior values allow double-tap-zooming.
michael@0 604 */
michael@0 605 bool TouchActionAllowDoubleTapZoom();
michael@0 606
michael@0 607 /*
michael@0 608 * Returns allowed touch behavior from the mAllowedTouchBehavior array.
michael@0 609 * In case apzc didn't receive touch behavior values within the timeout
michael@0 610 * it returns default value.
michael@0 611 */
michael@0 612 TouchBehaviorFlags GetTouchBehavior(uint32_t touchIndex);
michael@0 613
michael@0 614 /**
michael@0 615 * To move from the WAITING_CONTENT_RESPONSE state to TOUCHING one we need two
michael@0 616 * conditions set: get content listeners response (whether they called preventDefault)
michael@0 617 * and get allowed touch behaviors.
michael@0 618 * This method checks both conditions and changes (or not changes) state
michael@0 619 * appropriately.
michael@0 620 */
michael@0 621 void CheckContentResponse();
michael@0 622
michael@0 623 /**
michael@0 624 * Helper to set the current state. Holds the monitor before actually setting
michael@0 625 * it and fires content controller events based on state changes. Always set
michael@0 626 * the state using this call, do not set it directly.
michael@0 627 */
michael@0 628 void SetState(PanZoomState aState);
michael@0 629
michael@0 630 /**
michael@0 631 * Convert ScreenPoint relative to this APZC to CSSPoint relative
michael@0 632 * to the parent document. This excludes the transient compositor transform.
michael@0 633 * NOTE: This must be converted to CSSPoint relative to the child
michael@0 634 * document before sending over IPC.
michael@0 635 */
michael@0 636 bool ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut);
michael@0 637
michael@0 638 /**
michael@0 639 * Internal helpers for checking general state of this apzc.
michael@0 640 */
michael@0 641 bool IsTransformingState(PanZoomState aState);
michael@0 642 bool IsPanningState(PanZoomState mState);
michael@0 643
michael@0 644 enum AxisLockMode {
michael@0 645 FREE, /* No locking at all */
michael@0 646 STANDARD, /* Default axis locking mode that remains locked until pan ends*/
michael@0 647 STICKY, /* Allow lock to be broken, with hysteresis */
michael@0 648 };
michael@0 649
michael@0 650 static AxisLockMode GetAxisLockMode();
michael@0 651
michael@0 652 // Convert a point from local screen coordinates to parent layer coordinates.
michael@0 653 // This is a common operation as inputs from the tree manager are in screen
michael@0 654 // coordinates but the composition bounds is in parent layer coordinates.
michael@0 655 ParentLayerPoint ToParentLayerCoords(const ScreenPoint& aPoint);
michael@0 656
michael@0 657 // Update mFrameMetrics.mTransformScale. This should be called whenever
michael@0 658 // our CSS transform or the non-transient part of our async transform
michael@0 659 // changes, as it corresponds to the scale portion of those transforms.
michael@0 660 void UpdateTransformScale();
michael@0 661
michael@0 662 // Helper function for OnSingleTapUp() and OnSingleTapConfirmed().
michael@0 663 nsEventStatus GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers);
michael@0 664
michael@0 665 // Common processing at the end of a touch block.
michael@0 666 void OnTouchEndOrCancel();
michael@0 667
michael@0 668 uint64_t mLayersId;
michael@0 669 nsRefPtr<CompositorParent> mCompositorParent;
michael@0 670 PCompositorParent* mCrossProcessCompositorParent;
michael@0 671 TaskThrottler mPaintThrottler;
michael@0 672
michael@0 673 /* Access to the following two fields is protected by the mRefPtrMonitor,
michael@0 674 since they are accessed on the UI thread but can be cleared on the
michael@0 675 compositor thread. */
michael@0 676 nsRefPtr<GeckoContentController> mGeckoContentController;
michael@0 677 nsRefPtr<GestureEventListener> mGestureEventListener;
michael@0 678 Monitor mRefPtrMonitor;
michael@0 679
michael@0 680 /* Utility functions that return a addrefed pointer to the corresponding fields. */
michael@0 681 already_AddRefed<GeckoContentController> GetGeckoContentController();
michael@0 682 already_AddRefed<GestureEventListener> GetGestureEventListener();
michael@0 683
michael@0 684 protected:
michael@0 685 // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the
michael@0 686 // monitor. Do not read from or modify either of them without locking.
michael@0 687 FrameMetrics mFrameMetrics;
michael@0 688
michael@0 689 // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|.
michael@0 690 // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the
michael@0 691 // monitor should be held. When setting |mState|, either the SetState()
michael@0 692 // function can be used, or the monitor can be held and then |mState| updated.
michael@0 693 // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h.
michael@0 694 // This is mutable to allow entering it from 'const' methods; doing otherwise
michael@0 695 // would significantly limit what methods could be 'const'.
michael@0 696 mutable ReentrantMonitor mMonitor;
michael@0 697
michael@0 698 // Specifies whether we should use touch-action css property. Initialized from
michael@0 699 // the preferences. This property (in comparison with the global one) simplifies
michael@0 700 // testing apzc with (and without) touch-action property enabled concurrently
michael@0 701 // (e.g. with the gtest framework).
michael@0 702 bool mTouchActionPropertyEnabled;
michael@0 703
michael@0 704 private:
michael@0 705 // Metrics of the container layer corresponding to this APZC. This is
michael@0 706 // stored here so that it is accessible from the UI/controller thread.
michael@0 707 // These are the metrics at last content paint, the most recent
michael@0 708 // values we were notified of in NotifyLayersUpdate(). Since it represents
michael@0 709 // the Gecko state, it should be used as a basis for untransformation when
michael@0 710 // sending messages back to Gecko.
michael@0 711 FrameMetrics mLastContentPaintMetrics;
michael@0 712 // The last metrics that we requested a paint for. These are used to make sure
michael@0 713 // that we're not requesting a paint of the same thing that's already drawn.
michael@0 714 // If we don't do this check, we don't get a ShadowLayersUpdated back.
michael@0 715 FrameMetrics mLastPaintRequestMetrics;
michael@0 716 // The last metrics that we actually sent to Gecko. This allows us to transform
michael@0 717 // inputs into a coordinate space that Gecko knows about. This assumes the pipe
michael@0 718 // through which input events and repaint requests are sent to Gecko operates
michael@0 719 // in a FIFO manner.
michael@0 720 FrameMetrics mLastDispatchedPaintMetrics;
michael@0 721
michael@0 722 nsTArray<MultiTouchInput> mTouchQueue;
michael@0 723
michael@0 724 CancelableTask* mContentResponseTimeoutTask;
michael@0 725
michael@0 726 AxisX mX;
michael@0 727 AxisY mY;
michael@0 728
michael@0 729 // This flag is set to true when we are in a axis-locked pan as a result of
michael@0 730 // the touch-action CSS property.
michael@0 731 bool mPanDirRestricted;
michael@0 732
michael@0 733 // Most up-to-date constraints on zooming. These should always be reasonable
michael@0 734 // values; for example, allowing a min zoom of 0.0 can cause very bad things
michael@0 735 // to happen.
michael@0 736 ZoomConstraints mZoomConstraints;
michael@0 737
michael@0 738 // The last time the compositor has sampled the content transform for this
michael@0 739 // frame.
michael@0 740 TimeStamp mLastSampleTime;
michael@0 741 // The last time a touch event came through on the UI thread.
michael@0 742 uint32_t mLastEventTime;
michael@0 743
michael@0 744 // Stores the previous focus point if there is a pinch gesture happening. Used
michael@0 745 // to allow panning by moving multiple fingers (thus moving the focus point).
michael@0 746 ParentLayerPoint mLastZoomFocus;
michael@0 747
michael@0 748 // Stores the state of panning and zooming this frame. This is protected by
michael@0 749 // |mMonitor|; that is, it should be held whenever this is updated.
michael@0 750 PanZoomState mState;
michael@0 751
michael@0 752 // The last time and offset we fire the mozbrowserasyncscroll event when
michael@0 753 // compositor has sampled the content transform for this frame.
michael@0 754 TimeStamp mLastAsyncScrollTime;
michael@0 755 CSSPoint mLastAsyncScrollOffset;
michael@0 756
michael@0 757 // The current offset drawn on the screen, it may not be sent since we have
michael@0 758 // throttling policy for mozbrowserasyncscroll event.
michael@0 759 CSSPoint mCurrentAsyncScrollOffset;
michael@0 760
michael@0 761 // The delay task triggered by the throttling mozbrowserasyncscroll event
michael@0 762 // ensures the last mozbrowserasyncscroll event is always been fired.
michael@0 763 CancelableTask* mAsyncScrollTimeoutTask;
michael@0 764
michael@0 765 // Flag used to determine whether or not we should try to enter the
michael@0 766 // WAITING_LISTENERS state. This is used in the case that we are processing a
michael@0 767 // queued up event block. If set, this means that we are handling this queue
michael@0 768 // and we don't want to queue the events back up again.
michael@0 769 bool mHandlingTouchQueue;
michael@0 770
michael@0 771 // Stores information about the current touch block.
michael@0 772 TouchBlockState mTouchBlockState;
michael@0 773
michael@0 774 // Extra offset to add in SampleContentTransformForFrame for testing
michael@0 775 CSSPoint mTestAsyncScrollOffset;
michael@0 776
michael@0 777 RefPtr<AsyncPanZoomAnimation> mAnimation;
michael@0 778
michael@0 779 friend class Axis;
michael@0 780 friend class FlingAnimation;
michael@0 781
michael@0 782
michael@0 783 /* ===================================================================
michael@0 784 * The functions and members in this section are used to build a tree
michael@0 785 * structure out of APZC instances. This tree can only be walked or
michael@0 786 * manipulated while holding the lock in the associated APZCTreeManager
michael@0 787 * instance.
michael@0 788 */
michael@0 789 public:
michael@0 790 void SetLastChild(AsyncPanZoomController* child) {
michael@0 791 mLastChild = child;
michael@0 792 if (child) {
michael@0 793 child->mParent = this;
michael@0 794 }
michael@0 795 }
michael@0 796
michael@0 797 void SetPrevSibling(AsyncPanZoomController* sibling) {
michael@0 798 mPrevSibling = sibling;
michael@0 799 if (sibling) {
michael@0 800 sibling->mParent = mParent;
michael@0 801 }
michael@0 802 }
michael@0 803
michael@0 804 AsyncPanZoomController* GetLastChild() const { return mLastChild; }
michael@0 805 AsyncPanZoomController* GetPrevSibling() const { return mPrevSibling; }
michael@0 806 AsyncPanZoomController* GetParent() const { return mParent; }
michael@0 807
michael@0 808 /* Returns true if there is no APZC higher in the tree with the same
michael@0 809 * layers id.
michael@0 810 */
michael@0 811 bool IsRootForLayersId() const {
michael@0 812 return !mParent || (mParent->mLayersId != mLayersId);
michael@0 813 }
michael@0 814
michael@0 815 bool IsRootForLayersId(const uint64_t& aLayersId) const {
michael@0 816 return (mLayersId == aLayersId) && IsRootForLayersId();
michael@0 817 }
michael@0 818
michael@0 819 private:
michael@0 820 // This is a raw pointer to avoid introducing a reference cycle between
michael@0 821 // AsyncPanZoomController and APZCTreeManager. Since these objects don't
michael@0 822 // live on the main thread, we can't use the cycle collector with them.
michael@0 823 // The APZCTreeManager owns the lifetime of the APZCs, so nulling this
michael@0 824 // pointer out in Destroy() will prevent accessing deleted memory.
michael@0 825 Atomic<APZCTreeManager*> mTreeManager;
michael@0 826
michael@0 827 nsRefPtr<AsyncPanZoomController> mLastChild;
michael@0 828 nsRefPtr<AsyncPanZoomController> mPrevSibling;
michael@0 829 nsRefPtr<AsyncPanZoomController> mParent;
michael@0 830
michael@0 831
michael@0 832 /* ===================================================================
michael@0 833 * The functions and members in this section are used in building the
michael@0 834 * scroll handoff chain, so that we can have seamless scrolling continue
michael@0 835 * across APZC instances.
michael@0 836 */
michael@0 837 public:
michael@0 838 void SetScrollHandoffParentId(FrameMetrics::ViewID aScrollParentId) {
michael@0 839 mScrollParentId = aScrollParentId;
michael@0 840 }
michael@0 841
michael@0 842 FrameMetrics::ViewID GetScrollHandoffParentId() const {
michael@0 843 return mScrollParentId;
michael@0 844 }
michael@0 845
michael@0 846 /**
michael@0 847 * Attempt to scroll in response to a touch-move from |aStartPoint| to
michael@0 848 * |aEndPoint|, which are in our (transformed) screen coordinates.
michael@0 849 * Due to overscroll handling, there may not actually have been a touch-move
michael@0 850 * at these points, but this function will scroll as if there had been.
michael@0 851 * If this attempt causes overscroll (i.e. the layer cannot be scrolled
michael@0 852 * by the entire amount requested), the overscroll is passed back to the
michael@0 853 * tree manager via APZCTreeManager::DispatchScroll().
michael@0 854 * |aOverscrollHandoffChainIndex| is used by the tree manager to keep track
michael@0 855 * of which APZC to hand off the overscroll to; this function increments it
michael@0 856 * and passes it on to APZCTreeManager::DispatchScroll() in the event of
michael@0 857 * overscroll.
michael@0 858 */
michael@0 859 void AttemptScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
michael@0 860 uint32_t aOverscrollHandoffChainIndex = 0);
michael@0 861
michael@0 862 void FlushRepaintForOverscrollHandoff();
michael@0 863
michael@0 864 private:
michael@0 865 FrameMetrics::ViewID mScrollParentId;
michael@0 866
michael@0 867 /**
michael@0 868 * A helper function for calling APZCTreeManager::DispatchScroll().
michael@0 869 * Guards against the case where the APZC is being concurrently destroyed
michael@0 870 * (and thus mTreeManager is being nulled out).
michael@0 871 */
michael@0 872 void CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
michael@0 873 uint32_t aOverscrollHandoffChainIndex);
michael@0 874
michael@0 875
michael@0 876 /* ===================================================================
michael@0 877 * The functions and members in this section are used to maintain the
michael@0 878 * area that this APZC instance is responsible for. This is used when
michael@0 879 * hit-testing to see which APZC instance should handle touch events.
michael@0 880 */
michael@0 881 public:
michael@0 882 void SetLayerHitTestData(const ParentLayerRect& aRect, const gfx3DMatrix& aTransformToLayer,
michael@0 883 const gfx3DMatrix& aTransformForLayer) {
michael@0 884 mVisibleRect = aRect;
michael@0 885 mAncestorTransform = aTransformToLayer;
michael@0 886 mCSSTransform = aTransformForLayer;
michael@0 887 UpdateTransformScale();
michael@0 888 }
michael@0 889
michael@0 890 gfx3DMatrix GetAncestorTransform() const {
michael@0 891 return mAncestorTransform;
michael@0 892 }
michael@0 893
michael@0 894 gfx3DMatrix GetCSSTransform() const {
michael@0 895 return mCSSTransform;
michael@0 896 }
michael@0 897
michael@0 898 bool VisibleRegionContains(const ParentLayerPoint& aPoint) const {
michael@0 899 return mVisibleRect.Contains(aPoint);
michael@0 900 }
michael@0 901
michael@0 902 private:
michael@0 903 /* This is the visible region of the layer that this APZC corresponds to, in
michael@0 904 * that layer's screen pixels (the same coordinate system in which this APZC
michael@0 905 * receives events in ReceiveInputEvent()). */
michael@0 906 ParentLayerRect mVisibleRect;
michael@0 907 /* This is the cumulative CSS transform for all the layers between the parent
michael@0 908 * APZC and this one (not inclusive) */
michael@0 909 gfx3DMatrix mAncestorTransform;
michael@0 910 /* This is the CSS transform for this APZC's layer. */
michael@0 911 gfx3DMatrix mCSSTransform;
michael@0 912
michael@0 913
michael@0 914 /* ===================================================================
michael@0 915 * The functions and members in this section are used for sharing the
michael@0 916 * FrameMetrics across processes for the progressive tiling code.
michael@0 917 */
michael@0 918 private:
michael@0 919 /* Unique id assigned to each APZC. Used with ViewID to uniquely identify
michael@0 920 * shared FrameMeterics used in progressive tile painting. */
michael@0 921 const uint32_t mAPZCId;
michael@0 922
michael@0 923 ipc::SharedMemoryBasic* mSharedFrameMetricsBuffer;
michael@0 924 CrossProcessMutex* mSharedLock;
michael@0 925 /**
michael@0 926 * Called when ever mFrameMetrics is updated so that if it is being
michael@0 927 * shared with the content process the shared FrameMetrics may be updated.
michael@0 928 */
michael@0 929 void UpdateSharedCompositorFrameMetrics();
michael@0 930 /**
michael@0 931 * Create a shared memory buffer for containing the FrameMetrics and
michael@0 932 * a CrossProcessMutex that may be shared with the content process
michael@0 933 * for use in progressive tiled update calculations.
michael@0 934 */
michael@0 935 void ShareCompositorFrameMetrics();
michael@0 936 };
michael@0 937
michael@0 938 class AsyncPanZoomAnimation {
michael@0 939 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation)
michael@0 940
michael@0 941 public:
michael@0 942 AsyncPanZoomAnimation(const TimeDuration& aRepaintInterval =
michael@0 943 TimeDuration::Forever())
michael@0 944 : mRepaintInterval(aRepaintInterval)
michael@0 945 { }
michael@0 946
michael@0 947 virtual bool Sample(FrameMetrics& aFrameMetrics,
michael@0 948 const TimeDuration& aDelta) = 0;
michael@0 949
michael@0 950 /**
michael@0 951 * Get the deferred tasks in |mDeferredTasks|. See |mDeferredTasks|
michael@0 952 * for more information.
michael@0 953 * Clears |mDeferredTasks|.
michael@0 954 */
michael@0 955 Vector<Task*> TakeDeferredTasks() {
michael@0 956 Vector<Task*> result;
michael@0 957 mDeferredTasks.swap(result);
michael@0 958 return result;
michael@0 959 }
michael@0 960
michael@0 961 /**
michael@0 962 * Specifies how frequently (at most) we want to do repaints during the
michael@0 963 * animation sequence. TimeDuration::Forever() will cause it to only repaint
michael@0 964 * at the end of the animation.
michael@0 965 */
michael@0 966 TimeDuration mRepaintInterval;
michael@0 967
michael@0 968 protected:
michael@0 969 // Protected destructor, to discourage deletion outside of Release():
michael@0 970 virtual ~AsyncPanZoomAnimation()
michael@0 971 { }
michael@0 972
michael@0 973 /**
michael@0 974 * Tasks scheduled for execution after the APZC's mMonitor is released.
michael@0 975 * Derived classes can add tasks here in Sample(), and the APZC can call
michael@0 976 * ExecuteDeferredTasks() to execute them.
michael@0 977 */
michael@0 978 Vector<Task*> mDeferredTasks;
michael@0 979 };
michael@0 980
michael@0 981 }
michael@0 982 }
michael@0 983
michael@0 984 #endif // mozilla_layers_PanZoomController_h

mercurial