Bug 1385071 - Allow keyboard APZ with passive listeners, behind a pref. r=kats
authorRyan Hunt <rhunt@eqrion.net>
Thu, 27 Jul 2017 19:53:57 -0400
changeset 373038 91bd0a08cabe
parent 373037 aafd06fa1990
child 373039 be1bd1f2cb08
push id32288
push userarchaeopteryx@coole-files.de
push dateSat, 05 Aug 2017 09:55:48 +0000
treeherdermozilla-central@933a04a91ce3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1385071
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1385071 - Allow keyboard APZ with passive listeners, behind a pref. r=kats This commit adds the pref, 'apz.keyboard.passive-listeners', to allow web content to have passive key event listeners and use keyboard APZ. When we are allowing passive listeners, we need to dispatch the input to content and can no longer consume the event. So we use mHandledByAPZ in nsXBLWindowKeyHandler to determine whether we still need to do the default action, or whether it has been done by APZ. MozReview-Commit-ID: 2HAC6DjDyPZ
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/FocusTarget.cpp
gfx/layers/apz/src/InputQueue.cpp
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1354,17 +1354,18 @@ APZCTreeManager::ReceiveInputEvent(Input
       // Dispatch the event to the input queue.
       result = mInputQueue->ReceiveInputEvent(
           targetApzc,
           /* aTargetConfirmed = */ true,
           keyInput, aOutInputBlockId);
 
       // Any keyboard event that is dispatched to the input queue at this point
       // should have been consumed
-      MOZ_ASSERT(result == nsEventStatus_eConsumeNoDefault);
+      MOZ_ASSERT(result == nsEventStatus_eConsumeDoDefault ||
+                 result == nsEventStatus_eConsumeNoDefault);
 
       keyInput.mHandledByAPZ = true;
       focusSetter.MarkAsNonFocusChanging();
 
       break;
     }
   }
   return result;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -280,16 +280,22 @@ typedef GenericFlingAnimation FlingAnima
  *
  * \li\b apz.frame_delay.enabled
  * If this is set to true, changes to the async scroll offset and async zoom
  * will not be immediately reflected in GetCurrentAsyncTransform() when called
  * with |AsyncTransformConsumer::eForCompositing|. Rather, the transform will
  * reflect the value of the async scroll offset and async zoom at the last time
  * SampleCompositedAsyncTransform() was called.
  *
+ * \li\b apz.keyboard.passive-listeners
+ * When enabled, APZ will interpret the passive event listener flag to mean
+ * that the event listener won't change the focused element or selection of
+ * the page. With this, web content can use passive key listeners and not have
+ * keyboard APZ disabled.
+ *
  * \li\b apz.max_velocity_inches_per_ms
  * Maximum velocity.  Velocity will be capped at this value if a faster fling
  * occurs.  Negative values indicate unlimited velocity.\n
  * Units: (real-world, i.e. screen) inches per millisecond
  *
  * \li\b apz.max_velocity_queue_size
  * Maximum size of velocity queue. The queue contains last N velocity records.
  * On touch end we calculate the average velocity in order to compensate
@@ -1745,17 +1751,17 @@ AsyncPanZoomController::OnKeyboard(const
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(destination, scrollUnit)) {
     // If we're scroll snapping, use a smooth scroll animation to get
     // the desired physics. Note that SmoothScrollTo() will re-use an
     // existing smooth scroll animation if there is one.
     APZC_LOG("%p keyboard scrolling to snap point %s\n", this, Stringify(*snapPoint).c_str());
     SmoothScrollTo(*snapPoint);
-    return nsEventStatus_eConsumeNoDefault;
+    return nsEventStatus_eConsumeDoDefault;
   }
 
   // Use a keyboard scroll animation to scroll, reusing an existing one if it exists
   if (mState != KEYBOARD_SCROLL) {
     CancelAnimation();
     SetState(KEYBOARD_SCROLL);
 
     nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
@@ -1770,17 +1776,17 @@ AsyncPanZoomController::OnKeyboard(const
 
   KeyboardScrollAnimation* animation = mAnimation->AsKeyboardScrollAnimation();
   MOZ_ASSERT(animation);
 
   animation->UpdateDestination(aEvent.mTimeStamp,
                                CSSPixel::ToAppUnits(destination),
                                nsSize(velocity.x, velocity.y));
 
-  return nsEventStatus_eConsumeNoDefault;
+  return nsEventStatus_eConsumeDoDefault;
 }
 
 CSSPoint
 AsyncPanZoomController::GetKeyboardDestination(const KeyboardScrollAction& aAction) const
 {
   CSSSize lineScrollSize;
   CSSSize pageScrollSize;
   CSSPoint scrollOffset;
--- a/gfx/layers/apz/src/FocusTarget.cpp
+++ b/gfx/layers/apz/src/FocusTarget.cpp
@@ -67,16 +67,36 @@ HasListenersForKeyEvents(nsIContent* aCo
     if (targets[i]->HasNonSystemGroupListenersForUntrustedKeyEvents()) {
       return true;
     }
   }
   return false;
 }
 
 static bool
+HasListenersForNonPassiveKeyEvents(nsIContent* aContent)
+{
+  if (!aContent) {
+    return false;
+  }
+
+  WidgetEvent event(true, eVoidEvent);
+  nsTArray<EventTarget*> targets;
+  nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
+      nullptr, nullptr, &targets);
+  NS_ENSURE_SUCCESS(rv, false);
+  for (size_t i = 0; i < targets.Length(); i++) {
+    if (targets[i]->HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool
 IsEditableNode(nsINode* aNode)
 {
   return aNode && aNode->IsEditable();
 }
 
 FocusTarget::FocusTarget()
   : mSequenceNumber(0)
   , mFocusHasKeyEventListeners(false)
@@ -111,37 +131,46 @@ FocusTarget::FocusTarget(nsIPresShell* a
     mType = FocusTarget::eNone;
     return;
   }
 
   // Find the focused content and use it to determine whether there are key event
   // listeners or whether key events will be targeted at a different process
   // through a remote browser.
   nsCOMPtr<nsIContent> focusedContent = presShell->GetFocusedContentInOurWindow();
+  nsCOMPtr<nsIContent> keyEventTarget = focusedContent;
+
+  // If there is no focused element then event dispatch goes to the body of
+  // the page if it exists or the root element.
+  if (!keyEventTarget) {
+    keyEventTarget = document->GetUnfocusedKeyEventTarget();
+  }
 
   // Check if there are key event listeners that could prevent default or change
   // the focus or selection of the page.
-  mFocusHasKeyEventListeners =
-    HasListenersForKeyEvents(focusedContent ? focusedContent.get()
-                                            : document->GetUnfocusedKeyEventTarget());
+  if (gfxPrefs::APZKeyboardPassiveListeners()) {
+    mFocusHasKeyEventListeners = HasListenersForNonPassiveKeyEvents(keyEventTarget.get());
+  } else {
+    mFocusHasKeyEventListeners = HasListenersForKeyEvents(keyEventTarget.get());
+  }
 
-  // Check if the focused element is content editable or if the document
+  // Check if the key event target is content editable or if the document
   // is in design mode.
-  if (IsEditableNode(focusedContent) ||
+  if (IsEditableNode(keyEventTarget) ||
       IsEditableNode(document)) {
     FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (disabling for editable node)\n",
            aFocusSequenceNumber,
            static_cast<int>(mFocusHasKeyEventListeners));
 
     mType = FocusTarget::eNone;
     return;
   }
 
-  // Check if the focused element is a remote browser
-  if (TabParent* browserParent = TabParent::GetFrom(focusedContent)) {
+  // Check if the key event target is a remote browser
+  if (TabParent* browserParent = TabParent::GetFrom(keyEventTarget)) {
     RenderFrameParent* rfp = browserParent->GetRenderFrame();
 
     // The globally focused element for scrolling is in a remote layer tree
     if (rfp) {
       FT_LOG("Creating reflayer target with seq=%" PRIu64 ", kl=%d, lt=%" PRIu64 "\n",
              aFocusSequenceNumber,
              mFocusHasKeyEventListeners,
              rfp->GetLayersId());
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -301,17 +301,20 @@ InputQueue::ReceiveKeyboardInput(const R
   if (aOutInputBlockId) {
     *aOutInputBlockId = block->GetBlockId();
   }
 
   mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
 
   ProcessQueue();
 
-  return nsEventStatus_eConsumeNoDefault;
+  // If APZ is allowing passive listeners then we must dispatch the event to
+  // content, otherwise we can consume the event.
+  return gfxPrefs::APZKeyboardPassiveListeners() ? nsEventStatus_eConsumeDoDefault
+                                                 : nsEventStatus_eConsumeNoDefault;
 }
 
 static bool
 CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
                             PanGestureBlockState* aBlock)
 {
   PanGestureInput horizontalComponent = aInitialEvent;
   horizontalComponent.mPanDisplacement.y = 0;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -309,16 +309,17 @@ private:
   DECL_GFX_PREF(Once, "apz.fling_curve_function_y2",           APZCurveFunctionY2, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.fling_curve_threshold_inches_per_ms", APZCurveThreshold, float, -1.0f);
   DECL_GFX_PREF(Live, "apz.fling_friction",                    APZFlingFriction, float, 0.002f);
   DECL_GFX_PREF(Live, "apz.fling_min_velocity_threshold",      APZFlingMinVelocityThreshold, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.fling_stop_on_tap_threshold",       APZFlingStopOnTapThreshold, float, 0.05f);
   DECL_GFX_PREF(Live, "apz.fling_stopped_threshold",           APZFlingStoppedThreshold, float, 0.01f);
   DECL_GFX_PREF(Live, "apz.frame_delay.enabled",               APZFrameDelayEnabled, bool, false);
   DECL_GFX_PREF(Once, "apz.keyboard.enabled",                  APZKeyboardEnabled, bool, false);
+  DECL_GFX_PREF(Live, "apz.keyboard.passive-listeners",        APZKeyboardPassiveListeners, bool, false);
   DECL_GFX_PREF(Live, "apz.max_velocity_inches_per_ms",        APZMaxVelocity, float, -1.0f);
   DECL_GFX_PREF(Once, "apz.max_velocity_queue_size",           APZMaxVelocityQueueSize, uint32_t, 5);
   DECL_GFX_PREF(Live, "apz.min_skate_speed",                   APZMinSkateSpeed, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.minimap.enabled",                   APZMinimap, bool, false);
   DECL_GFX_PREF(Live, "apz.minimap.visibility.enabled",        APZMinimapVisibilityEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.one_touch_pinch.enabled",           APZOneTouchPinchEnabled, bool, true);
   DECL_GFX_PREF(Live, "apz.overscroll.enabled",                APZOverscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -715,16 +715,17 @@ pref("apz.frame_delay.enabled", true);
 #else
 pref("apz.frame_delay.enabled", false);
 #endif
 #if defined(NIGHTLY_BUILD) && !defined(MOZ_WIDGET_ANDROID)
 pref("apz.keyboard.enabled", true);
 #else
 pref("apz.keyboard.enabled", false);
 #endif
+pref("apz.keyboard.passive-listeners", false);
 pref("apz.max_velocity_inches_per_ms", "-1.0");
 pref("apz.max_velocity_queue_size", 5);
 pref("apz.min_skate_speed", "1.0");
 pref("apz.minimap.enabled", false);
 pref("apz.minimap.visibility.enabled", false);
 pref("apz.one_touch_pinch.enabled", true);
 pref("apz.overscroll.enabled", false);
 pref("apz.overscroll.min_pan_distance_ratio", "1.0");