Bug 1357880 - Add a telemetry probe for non-passive keyboard event listeners r?smaug draft
authorRyan Hunt <rhunt@eqrion.net>
Thu, 27 Apr 2017 18:32:08 -0400
changeset 570312 e8ca5396e4e6519d713eb20f3d5b0224dd3f3678
parent 569700 ffdedb9c5aadf033a6a230bbc0338bc0e0760db0
child 570313 f60bfa648a75126cba2866173f0bcaacdbf54f03
child 570317 2079cb87101972dbce8b4d4ea2c70625fa599c34
push id56455
push userbmo:rhunt@eqrion.net
push dateFri, 28 Apr 2017 18:34:51 +0000
reviewerssmaug
bugs1357880
milestone55.0a1
Bug 1357880 - Add a telemetry probe for non-passive keyboard event listeners r?smaug This commit adds a telemetry probe to track the percentage of pages that ever have a non-passive 'keydown' or 'keypress' event that could preventDefault() APZ key scrolling of the root of a page. A flag is added to each EventListenerManager to track whether it ever had a qualifying event listener, and then in nsGlobalWindow::FreeInnerObjects() the event targets that could preventDefault() a scroll are checked for this flag. This check is done at nsGlobalWindow::FreeInnerObjects() so that the DOM is still alive. MozReview-Commit-ID: EkK3vxehZA5
dom/base/nsGlobalWindow.cpp
dom/events/EventListenerManager.cpp
dom/events/EventListenerManager.h
dom/events/EventTarget.cpp
dom/events/EventTarget.h
toolkit/components/telemetry/Histograms.json
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -98,16 +98,17 @@
 #include "nsIWidget.h"
 #include "nsIWidgetListener.h"
 #include "nsIBaseWindow.h"
 #include "nsIDeviceSensors.h"
 #include "nsIContent.h"
 #include "nsIDocShell.h"
 #include "nsIDocCharset.h"
 #include "nsIDocument.h"
+#include "nsIDocumentInlines.h"
 #include "Crypto.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow.h"
 #include "nsThreadUtils.h"
@@ -2028,16 +2029,30 @@ nsGlobalWindow::ClearControllers()
   }
 }
 
 void
 nsGlobalWindow::FreeInnerObjects()
 {
   NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
 
+  if (mDoc && !nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) {
+    EventTarget* win = this;
+    EventTarget* html = mDoc->GetHtmlElement();
+    EventTarget* body = mDoc->GetBodyElement();
+
+    bool keyboardAware = win->MayHaveAPZAwareKeyEventListener() ||
+                         mDoc->MayHaveAPZAwareKeyEventListener() ||
+                         (html && html->MayHaveAPZAwareKeyEventListener()) ||
+                         (body && body->MayHaveAPZAwareKeyEventListener());
+
+    Telemetry::Accumulate(Telemetry::APZ_AWARE_KEY_LISTENERS,
+                          keyboardAware ? 1 : 0);
+  }
+
   // Make sure that this is called before we null out the document and
   // other members that the window destroyed observers could
   // re-create.
   NotifyDOMWindowDestroyed(this);
   if (auto* reporter = nsWindowMemoryReporter::Get()) {
     reporter->ObserveDOMWindowDetached(this);
   }
 
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -124,16 +124,17 @@ EventListenerManagerBase::EventListenerM
   : mNoListenerForEvent(eVoidEvent)
   , mMayHavePaintEventListener(false)
   , mMayHaveMutationListeners(false)
   , mMayHaveCapturingListeners(false)
   , mMayHaveSystemGroupListeners(false)
   , mMayHaveTouchEventListener(false)
   , mMayHaveMouseEnterLeaveEventListener(false)
   , mMayHavePointerEnterLeaveEventListener(false)
+  , mMayHaveAPZAwareKeyEventListener(false)
   , mMayHaveKeyEventListener(false)
   , mMayHaveInputOrCompositionEventListener(false)
   , mClearingListeners(false)
   , mIsMainThreadELM(NS_IsMainThread())
 {
   static_assert(sizeof(EventListenerManagerBase) == sizeof(uint32_t),
                 "Keep the size of EventListenerManagerBase size compact!");
 }
@@ -406,16 +407,19 @@ EventListenerManager::AddEventListenerIn
       window->SetHasGamepadEventListener();
     }
   } else if (aTypeAtom == nsGkAtoms::onkeydown ||
              aTypeAtom == nsGkAtoms::onkeypress ||
              aTypeAtom == nsGkAtoms::onkeyup) {
     if (!aFlags.mInSystemGroup) {
       mMayHaveKeyEventListener = true;
     }
+    if (!aFlags.mPassive && aTypeAtom != nsGkAtoms::onkeyup) {
+      mMayHaveAPZAwareKeyEventListener = true;
+    }
   } else if (aTypeAtom == nsGkAtoms::oncompositionend ||
              aTypeAtom == nsGkAtoms::oncompositionstart ||
              aTypeAtom == nsGkAtoms::oncompositionupdate ||
              aTypeAtom == nsGkAtoms::oninput) {
     if (!aFlags.mInSystemGroup) {
       mMayHaveInputOrCompositionEventListener = true;
     }
   }
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -158,21 +158,22 @@ protected:
   EventMessage mNoListenerForEvent;
   uint16_t mMayHavePaintEventListener : 1;
   uint16_t mMayHaveMutationListeners : 1;
   uint16_t mMayHaveCapturingListeners : 1;
   uint16_t mMayHaveSystemGroupListeners : 1;
   uint16_t mMayHaveTouchEventListener : 1;
   uint16_t mMayHaveMouseEnterLeaveEventListener : 1;
   uint16_t mMayHavePointerEnterLeaveEventListener : 1;
+  uint16_t mMayHaveAPZAwareKeyEventListener : 1;
   uint16_t mMayHaveKeyEventListener : 1;
   uint16_t mMayHaveInputOrCompositionEventListener : 1;
   uint16_t mClearingListeners : 1;
   uint16_t mIsMainThreadELM : 1;
-  // uint16_t mUnused : 5;
+  // uint16_t mUnused : 4;
 };
 
 /*
  * Event listener manager
  */
 
 class EventListenerManager final : public EventListenerManagerBase
 {
@@ -437,16 +438,18 @@ public:
    * Returns true if there may be a touch event listener registered,
    * false if there definitely isn't.
    */
   bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener; }
 
   bool MayHaveMouseEnterLeaveEventListener() { return mMayHaveMouseEnterLeaveEventListener; }
   bool MayHavePointerEnterLeaveEventListener() { return mMayHavePointerEnterLeaveEventListener; }
 
+  bool MayHaveAPZAwareKeyEventListener() const { return mMayHaveAPZAwareKeyEventListener; }
+
   /**
    * Returns true if there may be a key event listener (keydown, keypress,
    * or keyup) registered, or false if there definitely isn't.
    */
   bool MayHaveKeyEventListener() { return mMayHaveKeyEventListener; }
 
   /**
    * Returns true if there may be an advanced input event listener (input,
--- a/dom/events/EventTarget.cpp
+++ b/dom/events/EventTarget.cpp
@@ -60,16 +60,23 @@ EventTarget::SetEventHandler(nsIAtom* aT
 bool
 EventTarget::IsApzAware() const
 {
   EventListenerManager* elm = GetExistingListenerManager();
   return elm && elm->HasApzAwareListeners();
 }
 
 bool
+EventTarget::MayHaveAPZAwareKeyEventListener() const
+{
+  EventListenerManager* elm = GetExistingListenerManager();
+  return elm && elm->MayHaveAPZAwareKeyEventListener();
+}
+
+bool
 EventTarget::DispatchEvent(Event& aEvent,
                            CallerType aCallerType,
                            ErrorResult& aRv)
 {
   bool result = false;
   aRv = DispatchEvent(&aEvent, &result);
   return !aEvent.DefaultPrevented(aCallerType);
 }
--- a/dom/events/EventTarget.h
+++ b/dom/events/EventTarget.h
@@ -95,16 +95,17 @@ public:
    * exist.
    */
   virtual EventListenerManager* GetExistingListenerManager() const = 0;
 
   // Called from AsyncEventDispatcher to notify it is running.
   virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) {}
 
   virtual bool IsApzAware() const;
+  bool MayHaveAPZAwareKeyEventListener() const;
 
 protected:
   EventHandlerNonNull* GetEventHandler(nsIAtom* aType,
                                        const nsAString& aTypeString);
   void SetEventHandler(nsIAtom* aType, const nsAString& aTypeString,
                        EventHandlerNonNull* aHandler);
 };
 
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -10423,16 +10423,23 @@
   },
   "GFX_CRASH": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 100,
     "releaseChannelCollection": "opt-out",
     "description": "Graphics Crash Reason (...)"
   },
+  "APZ_AWARE_KEY_LISTENERS": {
+    "alert_emails": ["rhunt@mozilla.com"],
+    "bug_numbers": [1352654],
+    "expires_in_version": "58",
+    "kind": "boolean",
+    "description": "The percentage of pages with a non-passive key event on the path to the root scrolling element that would disable APZ key scrolling."
+  },
   "SCROLL_INPUT_METHODS": {
     "alert_emails": ["botond@mozilla.com"],
     "bug_numbers": [1238137],
     "expires_in_version": "60",
     "kind": "enumerated",
     "n_values": 64,
     "description": "Count of scroll actions triggered by different input methods. See gfx/layers/apz/util/ScrollInputMethods.h for a list of possible values and their meanings."
   },