Bug 1423017 - Add a telemetry for out-of-reach overflowing on root. r=botond
☠☠ backed out by e9a0eeeed39a ☠ ☠
authorXidorn Quan <me@upsuper.org>
Fri, 01 Jun 2018 15:35:32 +1000
changeset 475947 ad8f78a9b78cbc2620526c99e1a2892ebeb845f0
parent 475946 19607304a6b77cee2d44674281230ccd41763bf7
child 475948 6df1a71c5fe283c185dab23d0e5235f66f5958b9
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1423017
milestone62.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 1423017 - Add a telemetry for out-of-reach overflowing on root. r=botond MozReview-Commit-ID: 2CyZTVBFP59
dom/base/nsDocument.cpp
dom/base/nsIDocument.h
layout/generic/nsGfxScrollFrame.cpp
toolkit/components/telemetry/Histograms.json
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1493,16 +1493,17 @@ nsIDocument::nsIDocument()
     mUseCounters(0),
     mChildDocumentUseCounters(0),
     mNotifiedPageForUseCounter(0),
     mUserHasInteracted(false),
     mUserHasActivatedInteraction(false),
     mStackRefCnt(0),
     mUpdateNestLevel(0),
     mViewportType(Unknown),
+    mViewportOverflowType(ViewportOverflowType::NoOverflow),
     mSubDocuments(nullptr),
     mHeaderData(nullptr),
     mFlashClassification(FlashClassification::Unclassified),
     mBoxObjectTable(nullptr),
     mCurrentOrientationAngle(0),
     mCurrentOrientationType(OrientationType::Portrait_primary),
     mServoRestyleRootDirtyBits(0),
     mThrowOnDynamicMarkupInsertionCounter(0),
@@ -3744,16 +3745,17 @@ nsIDocument::SetHeaderData(nsAtom* aHead
       aHeaderField == nsGkAtoms::handheldFriendly ||
       aHeaderField == nsGkAtoms::viewport_minimum_scale ||
       aHeaderField == nsGkAtoms::viewport_maximum_scale ||
       aHeaderField == nsGkAtoms::viewport_initial_scale ||
       aHeaderField == nsGkAtoms::viewport_height ||
       aHeaderField == nsGkAtoms::viewport_width ||
       aHeaderField ==  nsGkAtoms::viewport_user_scalable) {
     mViewportType = Unknown;
+    mViewportOverflowType = ViewportOverflowType::NoOverflow;
   }
 
   // Referrer policy spec says to ignore any empty referrer policies.
   if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
      enum mozilla::net::ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
     // If policy is not the empty string, then set element's node document's
     // referrer policy to policy
     if (policy != mozilla::net::RP_Unset) {
@@ -7291,16 +7293,17 @@ nsIDocument::GetViewportInfo(const Scree
     }
 
     mScaleStrEmpty = scaleStr.IsEmpty();
     mWidthStrEmpty = widthStr.IsEmpty();
     mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
     mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
 
     mViewportType = Specified;
+    mViewportOverflowType = ViewportOverflowType::NoOverflow;
     MOZ_FALLTHROUGH;
   }
   case Specified:
   default:
     LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat;
     LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat;
     bool effectiveValidMaxScale = mValidMaxScale;
     bool effectiveAllowZoom = mAllowZoom;
@@ -7372,16 +7375,64 @@ nsIDocument::GetViewportInfo(const Scree
       size.height = std::max(size.height, displaySize.height);
     }
 
     return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
                           mAutoSize, effectiveAllowZoom);
   }
 }
 
+void
+nsIDocument::UpdateViewportOverflowType(nscoord aScrolledWidth,
+                                        nscoord aScrollportWidth)
+{
+#ifdef DEBUG
+  MOZ_ASSERT(mPresShell);
+  nsPresContext* pc = GetPresContext();
+  MOZ_ASSERT(pc->GetViewportScrollbarStylesOverride().mHorizontal ==
+             NS_STYLE_OVERFLOW_HIDDEN,
+             "Should only be called when viewport has overflow-x: hidden");
+  MOZ_ASSERT(aScrolledWidth > aScrollportWidth,
+             "Should only be called when viewport is overflowed");
+  MOZ_ASSERT(IsTopLevelContentDocument(),
+             "Should only be called for top-level content document");
+#endif // DEBUG
+
+  if (!gfxPrefs::MetaViewportEnabled() ||
+      (GetWindow() && GetWindow()->IsDesktopModeViewport())) {
+    mViewportOverflowType = ViewportOverflowType::Desktop;
+    return;
+  }
+
+  static const LayoutDeviceToScreenScale
+    kBlinkDefaultMinScale = LayoutDeviceToScreenScale(0.25f);
+  LayoutDeviceToScreenScale minScale;
+  if (mViewportType == DisplayWidthHeight) {
+    minScale = kBlinkDefaultMinScale;
+  } else {
+    MOZ_ASSERT(mViewportType == Specified,
+               "Viewport information should have been initialized");
+    if (mScaleMinFloat == kViewportMinScale) {
+      minScale = kBlinkDefaultMinScale;
+    } else {
+      minScale = mScaleMinFloat;
+    }
+  }
+
+  // If the content has overflowed with minimum scale applied, don't
+  // change it, otherwise update the overflow type.
+  if (mViewportOverflowType != ViewportOverflowType::MinScaleSize) {
+    if (aScrolledWidth * minScale.scale < aScrollportWidth) {
+      mViewportOverflowType = ViewportOverflowType::ButNotMinScaleSize;
+    } else {
+      mViewportOverflowType = ViewportOverflowType::MinScaleSize;
+    }
+  }
+}
+
 EventListenerManager*
 nsDocument::GetOrCreateListenerManager()
 {
   if (!mListenerManager) {
     mListenerManager =
       new EventListenerManager(static_cast<EventTarget*>(this));
     SetFlags(NODE_HAS_LISTENERMANAGER);
   }
@@ -12186,16 +12237,33 @@ nsIDocument::ReportUseCounters(UseCounte
             printf(": %d\n", value);
           }
 
           Telemetry::Accumulate(id, 1);
         }
       }
     }
   }
+
+  if (IsTopLevelContentDocument() && !IsResourceDoc()) {
+    using mozilla::Telemetry::LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE;
+    LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE label;
+    switch (mViewportOverflowType) {
+#define CASE_OVERFLOW_TYPE(t_)                            \
+      case ViewportOverflowType::t_:                      \
+        label = LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE::t_; \
+        break;
+      CASE_OVERFLOW_TYPE(NoOverflow)
+      CASE_OVERFLOW_TYPE(Desktop)
+      CASE_OVERFLOW_TYPE(ButNotMinScaleSize)
+      CASE_OVERFLOW_TYPE(MinScaleSize)
+#undef CASE_OVERFLOW_TYPE
+    }
+    Telemetry::AccumulateCategorical(label);
+  }
 }
 
 void
 nsIDocument::UpdateIntersectionObservations()
 {
   if (mIntersectionObservers.IsEmpty()) {
     return;
   }
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -1196,16 +1196,27 @@ public:
    * document, in device pixels.
    *
    * NOTE: If the site is optimized for mobile (via the doctype), this
    * will return viewport information that specifies default information.
    */
   nsViewportInfo GetViewportInfo(const mozilla::ScreenIntSize& aDisplaySize);
 
   /**
+   * It updates the viewport overflow type with the given two widths
+   * and the viewport setting of the document.
+   * This should only be called when there is out-of-reach overflow
+   * happens on the viewport, i.e. the viewport should be using
+   * `overflow: hidden`. And it should only be called on a top level
+   * content document.
+   */
+  void UpdateViewportOverflowType(nscoord aScrolledWidth,
+                                  nscoord aScrollportWidth);
+
+  /**
    * True iff this doc will ignore manual character encoding overrides.
    */
   virtual bool WillIgnoreCharsetOverride() {
     return true;
   }
 
   /**
    * Return whether the document was created by a srcdoc iframe.
@@ -4272,24 +4283,48 @@ protected:
   // Weak reference to our sink for in case we no longer have a parser.  This
   // will allow us to flush out any pending stuff from the sink even if
   // EndLoad() has already happened.
   nsWeakPtr mWeakSink;
 
   // Our update nesting level
   uint32_t mUpdateNestLevel;
 
-  enum ViewportType {
+  enum ViewportType : uint8_t {
     DisplayWidthHeight,
     Specified,
     Unknown
   };
 
   ViewportType mViewportType;
 
+  // Enum for how content in this document overflows viewport causing
+  // out-of-reach issue. Currently it only takes horizontal overflow
+  // into consideration. This enum and the corresponding field is only
+  // set and read on a top level content document.
+  enum class ViewportOverflowType : uint8_t {
+    // Viewport doesn't have out-of-reach overflow content, either
+    // because the content doesn't overflow, or the viewport doesn't
+    // have "overflow: hidden".
+    NoOverflow,
+
+    // All following items indicates that the content overflows the
+    // scroll port which causing out-of-reach content.
+
+    // Meta viewport is disabled or the document is in desktop mode.
+    Desktop,
+    // The content does not overflow the minimum-scale size. When there
+    // is no minimum scale specified, the default value used by Blink,
+    // 0.25, is used for this matter.
+    ButNotMinScaleSize,
+    // The content overflows the minimum-scale size.
+    MinScaleSize,
+  };
+  ViewportOverflowType mViewportOverflowType;
+
   PLDHashTable* mSubDocuments;
 
   nsDocHeaderData* mHeaderData;
 
   RefPtr<PrincipalFlashClassifier> mPrincipalFlashClassifier;
   mozilla::dom::FlashClassification mFlashClassification;
   // Do not use this value directly. Call the |IsThirdParty()| method, which
   // caches its result here.
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -390,22 +390,22 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
     nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(this, false);
     float resolution = presShell->GetResolution();
     compositionSize.width /= resolution;
     compositionSize.height /= resolution;
     visualScrollPortSize = nsSize(std::max(0, compositionSize.width - vScrollbarDesiredWidth),
                                   std::max(0, compositionSize.height - hScrollbarDesiredHeight));
   }
 
+  nsRect scrolledRect =
+    mHelper.GetUnsnappedScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
+                                             scrollPortSize);
+  nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
+
   if (!aForce) {
-    nsRect scrolledRect =
-      mHelper.GetUnsnappedScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
-                                               scrollPortSize);
-    nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
-
     // If the style is HIDDEN then we already know that aAssumeHScroll is false
     if (aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
       bool wantHScrollbar =
         aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL ||
         scrolledRect.XMost() >= visualScrollPortSize.width + oneDevPixel ||
         scrolledRect.x <= -oneDevPixel;
       if (scrollPortSize.width < hScrollbarMinSize.width)
         wantHScrollbar = false;
@@ -421,16 +421,27 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
         scrolledRect.y <= -oneDevPixel;
       if (scrollPortSize.height < vScrollbarMinSize.height)
         wantVScrollbar = false;
       if (wantVScrollbar != aAssumeVScroll)
         return false;
     }
   }
 
+  if (mHelper.mIsRoot &&
+      aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
+    nscoord scrolledWidth = scrolledRect.width + oneDevPixel;
+    if (scrolledWidth > scrollPortSize.width) {
+      nsIDocument* doc = PresContext()->Document();
+      if (doc->IsTopLevelContentDocument()) {
+        doc->UpdateViewportOverflowType(scrolledWidth, scrollPortSize.width);
+      }
+    }
+  }
+
   nscoord vScrollbarActualWidth = aState->mInsideBorderSize.width - scrollPortSize.width;
 
   aState->mShowHScrollbar = aAssumeHScroll;
   aState->mShowVScrollbar = aAssumeVScroll;
   nsPoint scrollPortOrigin(aState->mComputedBorder.left,
                            aState->mComputedBorder.top);
   if (!IsScrollbarOnRight()) {
     scrollPortOrigin.x += vScrollbarActualWidth;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -13799,10 +13799,19 @@
     "record_in_processes": ["content"],
     "alert_emails": ["farre@mozilla.com"],
     "bug_numbers": [1441972],
     "expires_in_version": "67",
     "kind": "exponential",
     "high": 50,
     "n_buckets": 20,
     "description": "Total number of doc groups per tab group, including docgroups fully in bfcache. Collected at the point when the top level document of the tab group is unloaded."
+  },
+  "HIDDEN_VIEWPORT_OVERFLOW_TYPE": {
+    "record_in_processes": ["content"],
+    "alert_emails": ["xquan@mozilla.com", "botond@mozilla.com"],
+    "bug_numbers": [1423013, 1423017],
+    "expires_in_version": "65",
+    "kind": "categorical",
+    "labels": ["NoOverflow", "Desktop", "ButNotMinScaleSize", "MinScaleSize"],
+    "description": "How common are different types of out-of-reach viewport overflow?"
   }
 }