Bug 1340904 - Implement telemetry scroll tracking in C++ r=smaug
authorRyan Hunt <rhunt@eqrion.net>
Thu, 02 Mar 2017 00:51:40 -0500
changeset 395003 11b27d2c87e71f1ed379c99c3f9ff5ca72d7e042
parent 395002 8ab76e34613bf4af022e05d14ed5f75529ed951d
child 395004 0b1aaa038970c008089482f3a3862c77489e4713
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1340904
milestone54.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 1340904 - Implement telemetry scroll tracking in C++ r=smaug
dom/ipc/TabChild.cpp
dom/ipc/TelemetryScrollProbe.cpp
dom/ipc/TelemetryScrollProbe.h
dom/ipc/moz.build
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/generic/nsGfxScrollFrame.cpp
toolkit/components/telemetry/Histograms.json
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -15,16 +15,17 @@
 #include "Layers.h"
 #include "ContentChild.h"
 #include "TabParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
+#include "mozilla/dom/TelemetryScrollProbe.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
 #include "mozilla/layers/APZEventState.h"
@@ -3346,16 +3347,18 @@ TabChildGlobal::~TabChildGlobal()
 
 void
 TabChildGlobal::Init()
 {
   NS_ASSERTION(!mMessageManager, "Re-initializing?!?");
   mMessageManager = new nsFrameMessageManager(mTabChild,
                                               nullptr,
                                               MM_CHILD);
+
+  TelemetryScrollProbe::Create(this);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildGlobal)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChildGlobal,
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild);
new file mode 100644
--- /dev/null
+++ b/dom/ipc/TelemetryScrollProbe.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TelemetryScrollProbe.h"
+
+#include "nsIDOMDocument.h"     // for nsIDOMDocument
+#include "nsIDOMEvent.h"        // for nsIDOMEvent
+#include "nsIDOMLocation.h"     // for nsIDOMLocation
+#include "nsIURI.h"             // for nsIURI
+#include "TabChild.h"           // for TabChildGlobal, TabChildBase
+#include "mozilla/Telemetry.h"  // for mozilla::Telemetry
+
+namespace mozilla {
+namespace dom {
+
+/* static */ void
+TelemetryScrollProbe::Create(TabChildGlobal* aWebFrame)
+{
+  nsWeakPtr webNav = do_GetWeakReference(aWebFrame->mTabChild->WebNavigation());
+  RefPtr<TelemetryScrollProbe> probe = new TelemetryScrollProbe(webNav);
+
+  aWebFrame->AddEventListener(NS_LITERAL_STRING("pagehide"), probe, true, false, 0);
+}
+
+already_AddRefed<nsIWebNavigation>
+TelemetryScrollProbe::GetWebNavigation() const
+{
+  nsCOMPtr<nsIWebNavigation> webNav = do_QueryReferent(mWebNav);
+  return webNav.forget();
+}
+
+already_AddRefed<nsIDocument>
+TelemetryScrollProbe::GetDocument() const
+{
+  nsCOMPtr<nsIDocument> result;
+  if (nsCOMPtr<nsIWebNavigation> webNav = GetWebNavigation()) {
+    nsCOMPtr<nsIDOMDocument> domDoc;
+    webNav->GetDocument(getter_AddRefs(domDoc));
+    result = do_QueryInterface(domDoc);
+  }
+  return result.forget();
+}
+
+already_AddRefed<nsIPresShell>
+TelemetryScrollProbe::GetPresShell() const
+{
+  nsCOMPtr<nsIPresShell> result;
+  if (nsCOMPtr<nsIDocument> doc = GetDocument()) {
+    result = doc->GetShell();
+  }
+  return result.forget();
+}
+
+bool
+TelemetryScrollProbe::ShouldIgnore(nsIDOMEvent* aEvent) const
+{
+  nsCOMPtr<nsIDOMEventTarget> target;
+  aEvent->GetTarget(getter_AddRefs(target));
+  nsCOMPtr<nsIDocument> targetDocument = do_QueryInterface(target);
+  RefPtr<nsIDocument> document = GetDocument();
+
+  return !document || targetDocument != document || nsContentUtils::IsSystemPrincipal(document->NodePrincipal());
+}
+
+NS_IMPL_ISUPPORTS(TelemetryScrollProbe, nsIDOMEventListener)
+
+NS_IMETHODIMP
+TelemetryScrollProbe::HandleEvent(nsIDOMEvent* aEvent)
+{
+  RefPtr<nsIPresShell> presShell = GetPresShell();
+
+  if (!presShell || ShouldIgnore(aEvent)) {
+    return NS_OK;
+  }
+
+  RefPtr<nsPresContext> presContext = presShell->GetPresContext();
+
+  nscoord maxAppUnits = presContext->TelemetryScrollMaxY();
+  nscoord totalAppUnits = presContext->TelemetryScrollTotalY();
+
+  float maxCSSPixels = nsPresContext::AppUnitsToFloatCSSPixels(maxAppUnits);
+  float totalCSSPixels = nsPresContext::AppUnitsToFloatCSSPixels(totalAppUnits);
+
+  mozilla::Telemetry::Accumulate(mozilla::Telemetry::TOTAL_SCROLL_Y, totalCSSPixels);
+  mozilla::Telemetry::Accumulate(mozilla::Telemetry::PAGE_MAX_SCROLL_Y, maxCSSPixels);
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/TelemetryScrollProbe.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TelemetryScrollProbe_h
+#define mozilla_dom_TelemetryScrollProbe_h
+
+#include "nsIDOMEventListener.h"
+
+class nsIDocument;
+class nsIDOMEvent;
+class nsIPresShell;
+class nsIWebNavigation;
+
+namespace mozilla {
+namespace dom {
+
+class TabChildGlobal;
+
+/*
+ * A class for implementing a telemetry scroll probe, listens to
+ * pagehide events in a frame script context and records the
+ * max scroll y, and amount of vertical scrolling that ocurred.
+ * for more information see bug 1340904
+ */
+class TelemetryScrollProbe final : public nsIDOMEventListener
+{
+public:
+  static void Create(TabChildGlobal* aWebFrame);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMEVENTLISTENER
+
+private:
+  explicit TelemetryScrollProbe(nsWeakPtr aWebNav)
+      : mWebNav(aWebNav)
+  { }
+  ~TelemetryScrollProbe() {}
+
+  already_AddRefed<nsIWebNavigation> GetWebNavigation() const;
+  already_AddRefed<nsIDocument> GetDocument() const;
+  already_AddRefed<nsIPresShell> GetPresShell() const;
+
+  bool ShouldIgnore(nsIDOMEvent* aEvent) const;
+
+  nsWeakPtr mWebNav;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TelemetryScrollProbe_h
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -31,16 +31,17 @@ EXPORTS.mozilla.dom += [
     'MemoryReportRequest.h',
     'nsIContentChild.h',
     'nsIContentParent.h',
     'PermissionMessageUtils.h',
     'TabChild.h',
     'TabContext.h',
     'TabMessageUtils.h',
     'TabParent.h',
+    'TelemetryScrollProbe.h',
     'URLClassifierChild.h',
     'URLClassifierParent.h',
 ]
 
 EXPORTS.mozilla += [
     'PreallocatedProcessManager.h',
     'ProcessHangMonitor.h',
     'ProcessHangMonitorIPC.h',
@@ -64,16 +65,17 @@ UNIFIED_SOURCES += [
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
     'ScreenManagerParent.cpp',
     'StructuredCloneData.cpp',
     'TabChild.cpp',
     'TabContext.cpp',
     'TabMessageUtils.cpp',
     'TabParent.cpp',
+    'TelemetryScrollProbe.cpp',
     'URLClassifierChild.cpp',
     'URLClassifierParent.cpp',
 ]
 
 # ContentChild.cpp cannot be compiled in unified mode on  linux due to Time conflict
 SOURCES += [
     'ContentChild.cpp',
     'ProcessHangMonitor.cpp',
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -237,16 +237,19 @@ nsPresContext::nsPresContext(nsIDocument
     mExistThrottledUpdates(false),
     // mImageAnimationMode is initialised below, in constructor body
     mImageAnimationModePref(imgIContainer::kNormalAnimMode),
     mInterruptChecksToSkip(0),
     mElementsRestyled(0),
     mFramesConstructed(0),
     mFramesReflowed(0),
     mInteractionTimeEnabled(true),
+    mTelemetryScrollLastY(0),
+    mTelemetryScrollMaxY(0),
+    mTelemetryScrollTotalY(0),
     mHasPendingInterrupt(false),
     mPendingInterruptFromTest(false),
     mInterruptsEnabled(false),
     mUseDocumentFonts(true),
     mUseDocumentColors(true),
     mUnderlineLinks(true),
     mSendAfterPaintToContent(false),
     mUseFocusColors(false),
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -883,16 +883,39 @@ public:
   }
   uint64_t FramesConstructedCount() {
     return mFramesConstructed;
   }
   uint64_t FramesReflowedCount() {
     return mFramesReflowed;
   }
 
+  /*
+   * Helper functions for a telemetry scroll probe
+   * for more information see bug 1340904
+   */
+  void SetTelemetryScrollY(nscoord aScrollY)
+  {
+    nscoord delta = abs(aScrollY - mTelemetryScrollLastY);
+    mTelemetryScrollLastY = aScrollY;
+
+    mTelemetryScrollTotalY += delta;
+    if (aScrollY > mTelemetryScrollMaxY) {
+      mTelemetryScrollMaxY = aScrollY;
+    }
+  }
+  nscoord TelemetryScrollMaxY() const
+  {
+    return mTelemetryScrollMaxY;
+  }
+  nscoord TelemetryScrollTotalY() const
+  {
+    return mTelemetryScrollTotalY;
+  }
+
   static nscoord GetBorderWidthForKeyword(unsigned int aBorderWidthKeyword)
   {
     // This table maps border-width enums 'thin', 'medium', 'thick'
     // to actual nscoord values.
     static const nscoord kBorderWidths[] = {
       CSSPixelsToAppUnits(1),
       CSSPixelsToAppUnits(3),
       CSSPixelsToAppUnits(5)
@@ -1354,16 +1377,20 @@ protected:
   mozilla::TimeStamp    mFirstKeyTime;
   mozilla::TimeStamp    mFirstMouseMoveTime;
   mozilla::TimeStamp    mFirstScrollTime;
   bool                  mInteractionTimeEnabled;
 
   // last time we did a full style flush
   mozilla::TimeStamp    mLastStyleUpdateForAllAnimations;
 
+  nscoord mTelemetryScrollLastY;
+  nscoord mTelemetryScrollMaxY;
+  nscoord mTelemetryScrollTotalY;
+
   unsigned              mHasPendingInterrupt : 1;
   unsigned              mPendingInterruptFromTest : 1;
   unsigned              mInterruptsEnabled : 1;
   unsigned              mUseDocumentFonts : 1;
   unsigned              mUseDocumentColors : 1;
   unsigned              mUnderlineLinks : 1;
   unsigned              mSendAfterPaintToContent : 1;
   unsigned              mUseFocusColors : 1;
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4723,16 +4723,17 @@ ScrollFrameHelper::FireScrollEvent()
   nsIContent* content = mOuter->GetContent();
   nsPresContext* prescontext = mOuter->PresContext();
   // Fire viewport scroll events at the document (where they
   // will bubble to the window)
   mozilla::layers::ScrollLinkedEffectDetector detector(content->GetComposedDoc());
   if (mIsRoot) {
     nsIDocument* doc = content->GetUncomposedDoc();
     if (doc) {
+      prescontext->SetTelemetryScrollY(GetScrollPosition().y);
       EventDispatcher::Dispatch(doc, prescontext, &event, nullptr,  &status);
     }
   } else {
     // scroll events fired at elements don't bubble (although scroll events
     // fired at documents do, to the window)
     event.mFlags.mBubbles = false;
     EventDispatcher::Dispatch(content, prescontext, &event, nullptr, &status);
   }
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -10302,16 +10302,32 @@
   "SCROLL_INPUT_METHODS": {
     "alert_emails": ["botond@mozilla.com"],
     "bug_numbers": [1238137],
     "expires_in_version": "55",
     "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."
   },
+  "TOTAL_SCROLL_Y": {
+    "alert_emails": ["hkirschner@mozilla.com"],
+    "bug_numbers": [1297867],
+    "expires_in_version": "58",
+    "kind": "count",
+    "description": "Count of the total number of CSS pixels scrolled up or down in the vertical axis of the root frame of all the pages visited in a subsession. This doesn't include any scrolling of nested scroll frames such as inputs, iframes, or scrollable divs."
+  },
+  "PAGE_MAX_SCROLL_Y": {
+    "alert_emails": ["hkirschner@mozilla.com"],
+    "bug_numbers": [1312881],
+    "expires_in_version": "58",
+    "kind": "exponential",
+    "high": 100000,
+    "n_buckets": 50,
+    "description": "Maximum distance in CSS pixels a user scrolls down the vertical axis of the root frame of a page. This doesn't include any scrolling of nested scroll frames such as inputs, iframes, or scrollable divs."
+  },
   "WEB_NOTIFICATION_CLICKED": {
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["firefox-dev@mozilla.org"],
     "bug_numbers": [1225336],
     "expires_in_version": "55",
     "kind": "count",
     "description": "Count of times a web notification was clicked"
   },