Bug 1643042 - Turn the scroll origin parameter into a strongly-typed enum. r=tnikkel
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 05 Jun 2020 09:37:51 +0000
changeset 534115 aa8fe25a871527045ee41c61f1cbe16c1b318ccc
parent 534114 78301c2498bf22f4f19ae78a4e07ed9861e0931b
child 534116 674cc15b2fba621f48dfce2696399dd99e8884b5
push id37483
push userapavel@mozilla.com
push dateFri, 05 Jun 2020 21:40:11 +0000
treeherdermozilla-central@dadc7312128e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1643042
milestone79.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 1643042 - Turn the scroll origin parameter into a strongly-typed enum. r=tnikkel This patch is a fairly mechanical conversion. The old `nullptr` gets converted to ScrollOrigin::NotSpecified, and all the other possible values get corresponding values in the new ScrollOrigin enum. A few switch statements are introduced to clean up big if statements, but other than that, additional cleanups will happen in later patches. Differential Revision: https://phabricator.services.mozilla.com/D78438
dom/base/Element.cpp
dom/base/nsGlobalWindowInner.cpp
dom/events/EventStateManager.cpp
gfx/layers/apz/util/APZCCallbackHelper.cpp
layout/base/PresShell.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/ScrollAnchorContainer.cpp
layout/generic/ScrollOrigin.h
layout/generic/moz.build
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
xpcom/ds/StaticAtoms.py
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -720,17 +720,18 @@ void Element::ScrollBy(const ScrollToOpt
     if (aOptions.mTop.WasPassed()) {
       scrollDelta.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
     }
 
     ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
                                 ? ScrollMode::SmoothMsd
                                 : ScrollMode::Instant;
 
-    sf->ScrollByCSSPixels(scrollDelta, scrollMode, nsGkAtoms::relative);
+    sf->ScrollByCSSPixels(scrollDelta, scrollMode,
+                          mozilla::ScrollOrigin::Relative);
   }
 }
 
 int32_t Element::ScrollTop() {
   nsIScrollableFrame* sf = GetScrollFrame();
   return sf ? sf->GetScrollPositionCSSPixels().y : 0;
 }
 
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -3661,17 +3661,18 @@ void nsGlobalWindowInner::ScrollBy(const
     if (aOptions.mTop.WasPassed()) {
       scrollDelta.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
     }
 
     ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
                                 ? ScrollMode::SmoothMsd
                                 : ScrollMode::Instant;
 
-    sf->ScrollByCSSPixels(scrollDelta, scrollMode, nsGkAtoms::relative);
+    sf->ScrollByCSSPixels(scrollDelta, scrollMode,
+                          mozilla::ScrollOrigin::Relative);
   }
 }
 
 void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
                                         const ScrollOptions& aOptions) {
   FlushPendingNotifications(FlushType::Layout);
   nsIScrollableFrame* sf = GetScrollFrame();
   if (sf) {
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2729,28 +2729,28 @@ void EventStateManager::DoScrollText(nsI
     actualDevPixelScrollAmount.x = 0;
   }
   if (overflowStyle.mVertical == StyleOverflow::Hidden) {
     actualDevPixelScrollAmount.y = 0;
   }
 
   nsIScrollbarMediator::ScrollSnapMode snapMode =
       nsIScrollbarMediator::DISABLE_SNAP;
-  nsAtom* origin = nullptr;
+  mozilla::ScrollOrigin origin = mozilla::ScrollOrigin::NotSpecified;
   switch (aEvent->mDeltaMode) {
     case WheelEvent_Binding::DOM_DELTA_LINE:
-      origin = nsGkAtoms::mouseWheel;
+      origin = mozilla::ScrollOrigin::MouseWheel;
       snapMode = nsIScrollableFrame::ENABLE_SNAP;
       break;
     case WheelEvent_Binding::DOM_DELTA_PAGE:
-      origin = nsGkAtoms::pages;
+      origin = mozilla::ScrollOrigin::Pages;
       snapMode = nsIScrollableFrame::ENABLE_SNAP;
       break;
     case WheelEvent_Binding::DOM_DELTA_PIXEL:
-      origin = nsGkAtoms::pixels;
+      origin = mozilla::ScrollOrigin::Pixels;
       break;
     default:
       MOZ_CRASH("Invalid deltaMode value comes");
   }
 
   // We shouldn't scroll more one page at once except when over one page scroll
   // is allowed for the event.
   nsSize pageSize = aScrollableFrame->GetPageScrollAmount();
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -132,17 +132,18 @@ static CSSPoint ScrollFrameTo(nsIScrolla
 
   // If the scrollable frame is currently in the middle of an async or smooth
   // scroll then we don't want to interrupt it (see bug 961280).
   // Also if the scrollable frame got a scroll request from a higher priority
   // origin since the last layers update, then we don't want to push our scroll
   // request because we'll clobber that one, which is bad.
   bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
   if (!scrollInProgress) {
-    aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
+    aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition,
+                                         ScrollOrigin::Apz);
     geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
     aSuccessOut = true;
   }
   // Return the final scroll position after setting it so that anything that
   // relies on it can have an accurate value. Note that even if we set it above
   // re-querying it is a good idea because it may have gotten clamped or
   // rounded.
   return geckoScrollPosition;
@@ -812,17 +813,17 @@ void APZCCallbackHelper::NotifyFlushComp
   MOZ_ASSERT(observerService);
   observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr);
 }
 
 /* static */
 bool APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame) {
   return aFrame->IsProcessingAsyncScroll() ||
          nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin()) ||
-         aFrame->LastSmoothScrollOrigin();
+         aFrame->LastSmoothScrollOrigin() != ScrollOrigin::NotSpecified;
 }
 
 /* static */
 void APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(
     uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
     ScrollDirection aDirection) {
   MOZ_ASSERT(NS_IsMainThread());
   if (nsIScrollableFrame* scrollFrame =
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -2332,65 +2332,65 @@ PresShell::PageMove(bool aForward, bool 
       aForward, aExtend, frame, nsFrameSelection::SelectionIntoView::IfChanged);
 }
 
 NS_IMETHODIMP
 PresShell::ScrollPage(bool aForward) {
   nsIScrollableFrame* scrollFrame =
       GetScrollableFrameToScroll(ScrollableDirection::Vertical);
   if (scrollFrame) {
-    scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::PAGES,
-                          ScrollMode::Smooth, nullptr, nullptr,
-                          nsIScrollableFrame::NOT_MOMENTUM,
-                          nsIScrollableFrame::ENABLE_SNAP);
+    scrollFrame->ScrollBy(
+        nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::PAGES, ScrollMode::Smooth,
+        nullptr, mozilla::ScrollOrigin::NotSpecified,
+        nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresShell::ScrollLine(bool aForward) {
   nsIScrollableFrame* scrollFrame =
       GetScrollableFrameToScroll(ScrollableDirection::Vertical);
   if (scrollFrame) {
     int32_t lineCount =
         Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
                             NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
-    scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
-                          ScrollUnit::LINES, ScrollMode::Smooth, nullptr,
-                          nullptr, nsIScrollableFrame::NOT_MOMENTUM,
-                          nsIScrollableFrame::ENABLE_SNAP);
+    scrollFrame->ScrollBy(
+        nsIntPoint(0, aForward ? lineCount : -lineCount), ScrollUnit::LINES,
+        ScrollMode::Smooth, nullptr, mozilla::ScrollOrigin::NotSpecified,
+        nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresShell::ScrollCharacter(bool aRight) {
   nsIScrollableFrame* scrollFrame =
       GetScrollableFrameToScroll(ScrollableDirection::Horizontal);
   if (scrollFrame) {
     int32_t h =
         Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
                             NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
-    scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0), ScrollUnit::LINES,
-                          ScrollMode::Smooth, nullptr, nullptr,
-                          nsIScrollableFrame::NOT_MOMENTUM,
-                          nsIScrollableFrame::ENABLE_SNAP);
+    scrollFrame->ScrollBy(
+        nsIntPoint(aRight ? h : -h, 0), ScrollUnit::LINES, ScrollMode::Smooth,
+        nullptr, mozilla::ScrollOrigin::NotSpecified,
+        nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresShell::CompleteScroll(bool aForward) {
   nsIScrollableFrame* scrollFrame =
       GetScrollableFrameToScroll(ScrollableDirection::Vertical);
   if (scrollFrame) {
-    scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::WHOLE,
-                          ScrollMode::Smooth, nullptr, nullptr,
-                          nsIScrollableFrame::NOT_MOMENTUM,
-                          nsIScrollableFrame::ENABLE_SNAP);
+    scrollFrame->ScrollBy(
+        nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::WHOLE, ScrollMode::Smooth,
+        nullptr, mozilla::ScrollOrigin::NotSpecified,
+        nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresShell::CompleteMove(bool aForward, bool aExtend) {
   // Beware! This may flush notifications via synchronous
   // ScrollSelectionIntoView.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/layers/PAPZ.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PerfStats.h"
 #include "mozilla/PresShell.h"
+#include "mozilla/ScrollOrigin.h"
 #include "mozilla/ServoStyleSetInlines.h"
 #include "mozilla/StaticPrefs_apz.h"
 #include "mozilla/StaticPrefs_dom.h"
 #include "mozilla/StaticPrefs_font.h"
 #include "mozilla/StaticPrefs_gfx.h"
 #include "mozilla/StaticPrefs_image.h"
 #include "mozilla/StaticPrefs_layers.h"
 #include "mozilla/StaticPrefs_layout.h"
@@ -9018,19 +9019,25 @@ void nsLayoutUtils::SetVisualViewportSiz
   // of the screen changes, the scroll position clamping scroll port
   // size also changes, we hook in the needed updates here rather
   // than adding a separate notification just for this change.
   nsPresContext* presContext = aPresShell->GetPresContext();
   MaybeReflowForInflationScreenSizeChange(presContext);
 }
 
 /* static */
-bool nsLayoutUtils::CanScrollOriginClobberApz(nsAtom* aScrollOrigin) {
-  return aScrollOrigin != nullptr && aScrollOrigin != nsGkAtoms::apz &&
-         aScrollOrigin != nsGkAtoms::restore;
+bool nsLayoutUtils::CanScrollOriginClobberApz(ScrollOrigin aScrollOrigin) {
+  switch (aScrollOrigin) {
+    case ScrollOrigin::NotSpecified:
+    case ScrollOrigin::Apz:
+    case ScrollOrigin::Restore:
+      return false;
+    default:
+      return true;
+  }
 }
 
 /* static */
 ScrollMetadata nsLayoutUtils::ComputeScrollMetadata(
     nsIFrame* aForFrame, nsIFrame* aScrollFrame, nsIContent* aContent,
     const nsIFrame* aReferenceFrame, LayerManager* aLayerManager,
     ViewID aScrollParentId, const nsRect& aViewport,
     const Maybe<nsRect>& aClipRect, bool aIsRootContent,
@@ -9158,30 +9165,31 @@ ScrollMetadata nsLayoutUtils::ComputeScr
     nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
     metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
 
     // If the frame was scrolled since the last layers update, and by something
     // that is higher priority than APZ, we want to tell the APZ to update
     // its scroll offset. We want to distinguish the case where the scroll
     // offset was "restored" because in that case the restored scroll position
     // should not overwrite a user-driven scroll.
-    nsAtom* lastOrigin = scrollableFrame->LastScrollOrigin();
-    if (lastOrigin == nsGkAtoms::restore) {
+    ScrollOrigin lastOrigin = scrollableFrame->LastScrollOrigin();
+    if (lastOrigin == ScrollOrigin::Restore) {
       metrics.SetScrollGeneration(scrollableFrame->CurrentScrollGeneration());
       metrics.SetScrollOffsetUpdateType(FrameMetrics::eRestore);
     } else if (CanScrollOriginClobberApz(lastOrigin)) {
-      if (lastOrigin == nsGkAtoms::relative) {
+      if (lastOrigin == ScrollOrigin::Relative) {
         metrics.SetIsRelative(true);
       }
       metrics.SetScrollGeneration(scrollableFrame->CurrentScrollGeneration());
       metrics.SetScrollOffsetUpdateType(FrameMetrics::eMainThread);
     }
 
-    nsAtom* lastSmoothScrollOrigin = scrollableFrame->LastSmoothScrollOrigin();
-    if (lastSmoothScrollOrigin) {
+    ScrollOrigin lastSmoothScrollOrigin =
+        scrollableFrame->LastSmoothScrollOrigin();
+    if (lastSmoothScrollOrigin != ScrollOrigin::NotSpecified) {
       metrics.SetSmoothScrollOffsetUpdated(
           scrollableFrame->CurrentScrollGeneration());
     }
 
     nsSize lineScrollAmount = scrollableFrame->GetLineScrollAmount();
     LayoutDeviceIntSize lineScrollAmountInDevPixels =
         LayoutDeviceIntSize::FromAppUnitsRounded(
             lineScrollAmount, presContext->AppUnitsPerDevPixel());
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -69,16 +69,17 @@ enum class PseudoStyleType : uint8_t;
 class EventListenerManager;
 enum class LayoutFrameType : uint8_t;
 struct IntrinsicSize;
 struct ContainerLayerParameters;
 class WritingMode;
 class DisplayItemClip;
 class EffectSet;
 struct ActiveScrolledRoot;
+enum class ScrollOrigin : uint8_t;
 enum class StyleImageOrientation : uint8_t;
 namespace dom {
 class CanvasRenderingContext2D;
 class DOMRectList;
 class Document;
 class Element;
 class Event;
 class HTMLImageElement;
@@ -144,16 +145,17 @@ class nsLayoutUtils {
   typedef mozilla::LengthPercentage LengthPercentage;
   typedef mozilla::LengthPercentageOrAuto LengthPercentageOrAuto;
   typedef mozilla::dom::DOMRectList DOMRectList;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::StackingContextHelper StackingContextHelper;
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
   typedef mozilla::IntrinsicSize IntrinsicSize;
   typedef mozilla::RelativeTo RelativeTo;
+  typedef mozilla::ScrollOrigin ScrollOrigin;
   typedef mozilla::ViewportType ViewportType;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::sRGBColor sRGBColor;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::ExtendMode ExtendMode;
   typedef mozilla::gfx::SamplingFilter SamplingFilter;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Point Point;
@@ -2865,17 +2867,17 @@ class nsLayoutUtils {
 
   /**
    * Returns true if the given scroll origin is "higher priority" than APZ.
    * In general any content programmatic scrolls (e.g. scrollTo calls) are
    * higher priority, and take precedence over APZ scrolling. This function
    * returns true for those, and returns false for other origins like APZ
    * itself, or scroll position updates from the history restore code.
    */
-  static bool CanScrollOriginClobberApz(nsAtom* aScrollOrigin);
+  static bool CanScrollOriginClobberApz(ScrollOrigin aScrollOrigin);
 
   static ScrollMetadata ComputeScrollMetadata(
       nsIFrame* aForFrame, nsIFrame* aScrollFrame, nsIContent* aContent,
       const nsIFrame* aReferenceFrame,
       mozilla::layers::LayerManager* aLayerManager, ViewID aScrollParentId,
       const nsRect& aViewport, const mozilla::Maybe<nsRect>& aClipRect,
       bool aIsRoot,
       const mozilla::Maybe<ContainerLayerParameters>& aContainerParameters);
--- a/layout/generic/ScrollAnchorContainer.cpp
+++ b/layout/generic/ScrollAnchorContainer.cpp
@@ -477,17 +477,17 @@ void ScrollAnchorContainer::ApplyAdjustm
       break;
     }
   }
 
   MOZ_RELEASE_ASSERT(!mApplyingAnchorAdjustment);
   // We should use AutoRestore here, but that doesn't work with bitfields
   mApplyingAnchorAdjustment = true;
   mScrollFrame->ScrollTo(mScrollFrame->GetScrollPosition() + physicalAdjustment,
-                         ScrollMode::Instant, nsGkAtoms::relative);
+                         ScrollMode::Instant, ScrollOrigin::Relative);
   mApplyingAnchorAdjustment = false;
 
   nsPresContext* pc = Frame()->PresContext();
   if (mScrollFrame->mIsRoot) {
     pc->PresShell()->RootScrollFrameAdjusted(physicalAdjustment.y);
   }
   pc->Document()->UpdateForScrollAnchorAdjustment(logicalAdjustment);
 
new file mode 100644
--- /dev/null
+++ b/layout/generic/ScrollOrigin.h
@@ -0,0 +1,58 @@
+/* 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_ScrollOrigin_h_
+#define mozilla_ScrollOrigin_h_
+
+namespace mozilla {
+
+// A scroll origin is a bit of a combination of "what part of the code caused
+// this scroll" and "what special properties does this scroll have, in the
+// context of what caused it". See specific comments below.
+enum class ScrollOrigin : uint8_t {
+  // This is a default value that we use when we don't know of a more specific
+  // value that we can use. Note that for the "LastSmoothScrollOrigin" property
+  // on a scrollable frame, the NotSpecified value is special, in that it means
+  // there is no smooth scroll in progress.
+  NotSpecified,
+  // The scroll came from APZ code.
+  Apz,
+  // The scroll came from an attempt at restoring a scroll position saved in
+  // the bfcache or history.
+  Restore,
+  // The scroll came from a "relative" scroll method like ScrollBy, where the
+  // scroll destination is indicated by a delta from the current position
+  // instead of an absolute value.
+  Relative,
+
+  // The following scroll origins also are associated with prefs of the form
+  //   general.smoothScroll.<origin>(.*)
+  // e.g. general.smoothScroll.lines indicates whether or not a scroll with
+  // origin Lines will be animated smoothly, and e.g. subprefs like
+  // general.smoothScroll.lines.durationMinMS control some of the animation
+  // timing behavior.
+
+  // The scroll came from some sort of input that's not one of the above or
+  // below values. Generally this means it came from a content-exposed API,
+  // like window.scrollTo, but may also be from other sources that don't need
+  // any particular special handling.
+  Other,
+  // The scroll was originated by pixel-scrolling input device (e.g. precision
+  // mouse wheel).
+  Pixels,
+  // The scroll was originated by a line-scrolling input device (e.g. up/down
+  // keyboard buttons).
+  Lines,
+  // The scroll was originated by a page-scrolling input device (e.g. pgup/
+  // pgdn keyboard buttons).
+  Pages,
+  // The scroll was originated by a mousewheel that scrolls by lines.
+  MouseWheel,
+  // The scroll was originated by moving the scrollbars.
+  Scrollbars,
+};
+
+}  // namespace mozilla
+
+#endif  // mozilla_ScrollOrigin_h_
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -146,16 +146,17 @@ EXPORTS.mozilla += [
     'AspectRatio.h',
     'AutoCopyListener.h',
     'ColumnUtils.h',
     'CSSAlignUtils.h',
     'CSSOrderAwareFrameIterator.h',
     'ReflowInput.h',
     'ReflowOutput.h',
     'ScrollbarPreferences.h',
+    'ScrollOrigin.h',
     'ViewportFrame.h',
     'WritingModes.h',
 ]
 
 EXPORTS.mozilla.layout += [
     'FrameChildList.h',
     'ScrollAnchorContainer.h',
 ]
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1502,17 +1502,17 @@ void ScrollFrameHelper::ScrollByLine(
       // context.
       ScrollByPage(aScrollbar, aDirection);
       return;
     }
   }
 
   nsIntPoint overflow;
   ScrollBy(delta, ScrollUnit::LINES, ScrollMode::Smooth, &overflow,
-           nsGkAtoms::other, nsIScrollableFrame::NOT_MOMENTUM, aSnap);
+           ScrollOrigin::Other, nsIScrollableFrame::NOT_MOMENTUM, aSnap);
 }
 
 void ScrollFrameHelper::RepeatButtonScroll(nsScrollbarFrame* aScrollbar) {
   aScrollbar->MoveToNewPosition();
 }
 
 void ScrollFrameHelper::ThumbMoved(nsScrollbarFrame* aScrollbar,
                                    nscoord aOldPos, nscoord aNewPos) {
@@ -1529,17 +1529,17 @@ void ScrollFrameHelper::ThumbMoved(nsScr
 
   // Don't try to scroll if we're already at an acceptable place.
   // Don't call Contains here since Contains returns false when the point is
   // on the bottom or right edge of the rectangle.
   if (allowedRange.ClampPoint(current) == current) {
     return;
   }
 
-  ScrollTo(dest, ScrollMode::Instant, nsGkAtoms::other, &allowedRange);
+  ScrollTo(dest, ScrollMode::Instant, ScrollOrigin::Other, &allowedRange);
 }
 
 void ScrollFrameHelper::ScrollbarReleased(nsScrollbarFrame* aScrollbar) {
   // Scrollbar scrolling does not result in fling gestures, clear any
   // accumulated velocity
   mVelocityQueue.Reset();
 
   // Perform scroll snapping, if needed.  Scrollbar movement uses the same
@@ -1554,17 +1554,17 @@ void ScrollFrameHelper::ScrollByUnit(
   bool isHorizontal = aScrollbar->IsXULHorizontal();
   nsIntPoint delta;
   if (isHorizontal) {
     delta.x = aDirection;
   } else {
     delta.y = aDirection;
   }
   nsIntPoint overflow;
-  ScrollBy(delta, aUnit, aMode, &overflow, nsGkAtoms::other,
+  ScrollBy(delta, aUnit, aMode, &overflow, ScrollOrigin::Other,
            nsIScrollableFrame::NOT_MOMENTUM, aSnap);
 }
 
 nsresult nsXULScrollFrame::CreateAnonymousContent(
     nsTArray<ContentInfo>& aElements) {
   return mHelper.CreateAnonymousContent(aElements);
 }
 
@@ -1833,32 +1833,33 @@ class ScrollFrameHelper::AsyncSmoothMSDS
 };
 
 // AsyncScroll has ref counting.
 class ScrollFrameHelper::AsyncScroll final : public nsARefreshObserver {
  public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
 
-  explicit AsyncScroll() : mCallee(nullptr) {
+  explicit AsyncScroll()
+      : mOrigin(ScrollOrigin::NotSpecified), mCallee(nullptr) {
     Telemetry::SetHistogramRecordingEnabled(
         Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, true);
   }
 
  private:
   // Private destructor, to discourage deletion outside of Release():
   ~AsyncScroll() {
     RemoveObserver();
     Telemetry::SetHistogramRecordingEnabled(
         Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, false);
   }
 
  public:
   void InitSmoothScroll(TimeStamp aTime, nsPoint aInitialPosition,
-                        nsPoint aDestination, nsAtom* aOrigin,
+                        nsPoint aDestination, ScrollOrigin aOrigin,
                         const nsRect& aRange, const nsSize& aCurrentVelocity);
   void Init(const nsRect& aRange) {
     mAnimationPhysics = nullptr;
     mRange = aRange;
   }
 
   bool IsSmoothScroll() { return mAnimationPhysics != nullptr; }
 
@@ -1873,17 +1874,17 @@ class ScrollFrameHelper::AsyncScroll fin
   }
 
   nsSize VelocityAt(const TimeStamp& aTime) const {
     MOZ_RELEASE_ASSERT(mAnimationPhysics);
     return mAnimationPhysics->VelocityAt(aTime);
   }
 
   // Most recent scroll origin.
-  RefPtr<nsAtom> mOrigin;
+  ScrollOrigin mOrigin;
 
   // Allowed destination positions around mDestination
   nsRect mRange;
 
  private:
   void InitPreferences(TimeStamp aTime, nsAtom* aOrigin);
 
   UniquePtr<ScrollAnimationPhysics> mAnimationPhysics;
@@ -1938,30 +1939,47 @@ class ScrollFrameHelper::AsyncScroll fin
   }
 };
 
 /*
  * Calculate duration, possibly dynamically according to events rate and event
  * origin. (also maintain previous timestamps - which are only used here).
  */
 static ScrollAnimationBezierPhysicsSettings
-ComputeBezierAnimationSettingsForOrigin(nsAtom* aOrigin) {
+ComputeBezierAnimationSettingsForOrigin(ScrollOrigin aOrigin) {
   int32_t minMS = 0;
   int32_t maxMS = 0;
   bool isOriginSmoothnessEnabled = false;
   double intervalRatio = 1;
 
   // Default values for all preferences are defined in all.js
   static const int32_t kDefaultMinMS = 150, kDefaultMaxMS = 150;
   static const bool kDefaultIsSmoothEnabled = true;
 
-  nsAutoCString originName;
-  aOrigin->ToUTF8String(originName);
-  nsAutoCString prefBase =
-      NS_LITERAL_CSTRING("general.smoothScroll.") + originName;
+  nsAutoCString prefBase;
+  switch (aOrigin) {
+    case ScrollOrigin::Pixels:
+      prefBase = NS_LITERAL_CSTRING("general.smoothScroll.pixels");
+      break;
+    case ScrollOrigin::Lines:
+      prefBase = NS_LITERAL_CSTRING("general.smoothScroll.lines");
+      break;
+    case ScrollOrigin::Pages:
+      prefBase = NS_LITERAL_CSTRING("general.smoothScroll.pages");
+      break;
+    case ScrollOrigin::MouseWheel:
+      prefBase = NS_LITERAL_CSTRING("general.smoothScroll.mouseWheel");
+      break;
+    case ScrollOrigin::Scrollbars:
+      prefBase = NS_LITERAL_CSTRING("general.smoothScroll.scrollbars");
+      break;
+    default:
+      prefBase = NS_LITERAL_CSTRING("general.smoothScroll.other");
+      break;
+  }
 
   isOriginSmoothnessEnabled =
       Preferences::GetBool(prefBase.get(), kDefaultIsSmoothEnabled);
   if (isOriginSmoothnessEnabled) {
     nsAutoCString prefMin = prefBase + NS_LITERAL_CSTRING(".durationMinMS");
     nsAutoCString prefMax = prefBase + NS_LITERAL_CSTRING(".durationMaxMS");
     minMS = Preferences::GetInt(prefMin.get(), kDefaultMinMS);
     maxMS = Preferences::GetInt(prefMax.get(), kDefaultMaxMS);
@@ -1985,27 +2003,36 @@ ComputeBezierAnimationSettingsForOrigin(
   // Duration should be at least as long as the intervals -> ratio is at least 1
   intervalRatio = std::max(1.0, intervalRatio);
 
   return ScrollAnimationBezierPhysicsSettings{minMS, maxMS, intervalRatio};
 }
 
 void ScrollFrameHelper::AsyncScroll::InitSmoothScroll(
     TimeStamp aTime, nsPoint aInitialPosition, nsPoint aDestination,
-    nsAtom* aOrigin, const nsRect& aRange, const nsSize& aCurrentVelocity) {
-  if (!aOrigin || aOrigin == nsGkAtoms::restore ||
-      aOrigin == nsGkAtoms::relative) {
-    // We don't have special prefs for "restore", just treat it as "other".
-    // "restore" scrolls are (for now) always instant anyway so unless something
-    // changes we should never have aOrigin == nsGkAtoms::restore here.
-    aOrigin = nsGkAtoms::other;
-  }
-  // Likewise we should never get APZ-triggered scrolls here, and if that
-  // changes something is likely broken somewhere.
-  MOZ_ASSERT(aOrigin != nsGkAtoms::apz);
+    ScrollOrigin aOrigin, const nsRect& aRange,
+    const nsSize& aCurrentVelocity) {
+  switch (aOrigin) {
+    case ScrollOrigin::NotSpecified:
+    case ScrollOrigin::Restore:
+    case ScrollOrigin::Relative:
+      // We don't have special prefs for "restore", just treat it as "other".
+      // "restore" scrolls are (for now) always instant anyway so unless
+      // something changes we should never have aOrigin ==
+      // ScrollOrigin::Restore here.
+      aOrigin = ScrollOrigin::Other;
+      break;
+    case ScrollOrigin::Apz:
+      // Likewise we should never get APZ-triggered scrolls here, and if that
+      // changes something is likely broken somewhere.
+      MOZ_ASSERT(false);
+      break;
+    default:
+      break;
+  };
 
   // Read preferences only on first iteration or for a different event origin.
   if (!mAnimationPhysics || aOrigin != mOrigin) {
     mOrigin = aOrigin;
     if (StaticPrefs::general_smoothScroll_msdPhysics_enabled()) {
       mAnimationPhysics =
           MakeUnique<ScrollAnimationMSDPhysics>(aInitialPosition);
     } else {
@@ -2057,18 +2084,18 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
       mVScrollbarBox(nullptr),
       mScrolledFrame(nullptr),
       mScrollCornerBox(nullptr),
       mResizerBox(nullptr),
       mOuter(aOuter),
       mReferenceFrameDuringPainting(nullptr),
       mAsyncScroll(nullptr),
       mAsyncSmoothMSDScroll(nullptr),
-      mLastScrollOrigin(nsGkAtoms::other),
-      mLastSmoothScrollOrigin(nullptr),
+      mLastScrollOrigin(ScrollOrigin::Other),
+      mLastSmoothScrollOrigin(ScrollOrigin::NotSpecified),
       mScrollGeneration(++sScrollGenerationCounter),
       mDestination(0, 0),
       mRestorePos(-1, -1),
       mLastPos(-1, -1),
       mApzScrollPos(0, 0),
       mScrollPosForLayerPixelAlignment(-1, -1),
       mLastUpdateFramesPos(-1, -1),
       mDisplayPortAtLastFrameUpdate(),
@@ -2189,17 +2216,17 @@ void ScrollFrameHelper::AsyncScrollCallb
       return;
     }
   }
 
   aInstance->CompleteAsyncScroll(range);
 }
 
 void ScrollFrameHelper::CompleteAsyncScroll(const nsRect& aRange,
-                                            nsAtom* aOrigin) {
+                                            ScrollOrigin aOrigin) {
   // Apply desired destination range since this is the last step of scrolling.
   RemoveObservers();
   AutoWeakFrame weakFrame(mOuter);
   ScrollToImpl(mDestination, aRange, aOrigin);
   if (!weakFrame.IsAlive()) {
     return;
   }
   // We are done scrolling, set our destination to wherever we actually ended
@@ -2223,36 +2250,36 @@ bool ScrollFrameHelper::HasPluginFrames(
 }
 
 bool ScrollFrameHelper::HasBgAttachmentLocal() const {
   const nsStyleBackground* bg = mOuter->StyleBackground();
   return bg->HasLocalBackground();
 }
 
 void ScrollFrameHelper::ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
-                                 nsAtom* aOrigin, const nsRect* aRange,
+                                 ScrollOrigin aOrigin, const nsRect* aRange,
                                  nsIScrollbarMediator::ScrollSnapMode aSnap) {
-  if (aOrigin == nullptr) {
-    aOrigin = nsGkAtoms::other;
+  if (aOrigin == ScrollOrigin::NotSpecified) {
+    aOrigin = ScrollOrigin::Other;
   }
   ScrollToWithOrigin(aScrollPosition, aMode, aOrigin, aRange, aSnap);
 }
 
 void ScrollFrameHelper::ScrollToCSSPixels(
     const CSSIntPoint& aScrollPosition, ScrollMode aMode,
-    nsIScrollbarMediator::ScrollSnapMode aSnap, nsAtom* aOrigin) {
+    nsIScrollbarMediator::ScrollSnapMode aSnap, ScrollOrigin aOrigin) {
   nsPoint current = GetScrollPosition();
   CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
   nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
   if (aSnap == nsIScrollableFrame::DEFAULT) {
     aSnap = nsIScrollableFrame::ENABLE_SNAP;
   }
 
-  if (aOrigin == nullptr) {
-    aOrigin = nsGkAtoms::other;
+  if (aOrigin == ScrollOrigin::NotSpecified) {
+    aOrigin = ScrollOrigin::Other;
   }
 
   nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
   nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2 * halfPixel - 1,
                2 * halfPixel - 1);
   // XXX I don't think the following blocks are needed anymore, now that
   // ScrollToImpl simply tries to scroll an integer number of layer
   // pixels from the current position
@@ -2266,17 +2293,17 @@ void ScrollFrameHelper::ScrollToCSSPixel
     range.y = pt.y;
     range.height = 0;
   }
   ScrollTo(pt, aMode, aOrigin, &range, aSnap);
   // 'this' might be destroyed here
 }
 
 void ScrollFrameHelper::ScrollToCSSPixelsApproximate(
-    const CSSPoint& aScrollPosition, nsAtom* aOrigin) {
+    const CSSPoint& aScrollPosition, ScrollOrigin aOrigin) {
   nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
   nscoord halfRange = nsPresContext::CSSPixelsToAppUnits(1000);
   nsRect range(pt.x - halfRange, pt.y - halfRange, 2 * halfRange - 1,
                2 * halfRange - 1);
   ScrollToWithOrigin(pt, ScrollMode::Instant, aOrigin, &range);
   // 'this' might be destroyed here
 }
 
@@ -2284,37 +2311,37 @@ CSSIntPoint ScrollFrameHelper::GetScroll
   return CSSIntPoint::FromAppUnitsRounded(GetScrollPosition());
 }
 
 /*
  * this method wraps calls to ScrollToImpl(), either in one shot or
  * incrementally, based on the setting of the smoothness scroll pref
  */
 void ScrollFrameHelper::ScrollToWithOrigin(
-    nsPoint aScrollPosition, ScrollMode aMode, nsAtom* aOrigin,
+    nsPoint aScrollPosition, ScrollMode aMode, ScrollOrigin aOrigin,
     const nsRect* aRange, nsIScrollbarMediator::ScrollSnapMode aSnap) {
-  if (aOrigin != nsGkAtoms::restore) {
+  if (aOrigin != ScrollOrigin::Restore) {
     // If we're doing a non-restore scroll, we don't want to later
     // override it by restoring our saved scroll position.
     mRestorePos.x = mRestorePos.y = -1;
   }
 
   bool willSnap = false;
   if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
     willSnap = GetSnapPointForDestination(ScrollUnit::DEVICE_PIXELS,
                                           mDestination, aScrollPosition);
   }
 
   nsRect scrollRange = GetLayoutScrollRange();
   mDestination = scrollRange.ClampPoint(aScrollPosition);
-  if (mDestination != aScrollPosition && aOrigin == nsGkAtoms::restore &&
+  if (mDestination != aScrollPosition && aOrigin == ScrollOrigin::Restore &&
       GetPageLoadingState() != LoadingState::Loading) {
     // If we're doing a restore but the scroll position is clamped, promote
     // the origin from one that APZ can clobber to one that it can't clobber.
-    aOrigin = nsGkAtoms::other;
+    aOrigin = ScrollOrigin::Other;
   }
 
   nsRect range =
       aRange && !willSnap ? *aRange : nsRect(aScrollPosition, nsSize(0, 0));
 
   if (aMode != ScrollMode::SmoothMsd) {
     // If we get a non-smooth-scroll, reset the cached APZ scroll destination,
     // so that we know to process the next smooth-scroll destined for APZ.
@@ -2699,33 +2726,34 @@ bool ScrollFrameHelper::GetDisplayPortAt
     nsRect* aDisplayPort) {
   if (mHadDisplayPortAtLastFrameUpdate) {
     *aDisplayPort = mDisplayPortAtLastFrameUpdate;
   }
   return mHadDisplayPortAtLastFrameUpdate;
 }
 
 void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange,
-                                     nsAtom* aOrigin) {
+                                     ScrollOrigin aOrigin) {
   // Figure out the effective origin for this scroll request.
-  if (aOrigin == nullptr) {
+  if (aOrigin == ScrollOrigin::NotSpecified) {
     // If no origin was specified, we still want to set it to something that's
-    // non-null, so that we can use nullness to distinguish if the frame was
+    // non-unknown, so that we can use eUnknown to distinguish if the frame was
     // scrolled at all. Default it to some generic placeholder.
-    aOrigin = nsGkAtoms::other;
+    aOrigin = ScrollOrigin::Other;
   }
 
   // If this scroll is |relative|, but we've already had a user scroll that
   // was not relative, promote this origin to |other|. This ensures that we
   // may only transmit a relative update to APZ if all scrolls since the last
   // transaction or repaint request have been relative.
-  if (aOrigin == nsGkAtoms::relative &&
-      (mLastScrollOrigin && mLastScrollOrigin != nsGkAtoms::relative &&
-       mLastScrollOrigin != nsGkAtoms::apz)) {
-    aOrigin = nsGkAtoms::other;
+  if (aOrigin == ScrollOrigin::Relative &&
+      (mLastScrollOrigin != ScrollOrigin::NotSpecified &&
+       mLastScrollOrigin != ScrollOrigin::Relative &&
+       mLastScrollOrigin != ScrollOrigin::Apz)) {
+    aOrigin = ScrollOrigin::Other;
   }
 
   // If the origin is a downgrade, and downgrades are allowed, process the
   // downgrade even if we're going to early-exit because we're already at
   // the correct scroll position. This ensures that if there wasn't a main-
   // thread scroll update pending before a frame reconstruction (as indicated
   // by mAllowScrollOriginDowngrade=true), then after the frame reconstruction
   // the origin is downgraded to "restore" even if the layout scroll offset to
@@ -2793,18 +2821,18 @@ void ScrollFrameHelper::ScrollToImpl(nsP
   if (IsRootScrollFrameOfDocument() && presContext->IsRootContentDocument()) {
     PresShell* ps = presContext->GetPresShell();
     if (const auto& visualScrollUpdate = ps->GetPendingVisualScrollUpdate()) {
       if (visualScrollUpdate->mVisualScrollOffset != aPt) {
         // Only clobber if the scroll was originated by the main thread.
         // Respect the priority of origins (an "eRestore" layout scroll should
         // not clobber an "eMainThread" visual scroll.)
         bool shouldClobber =
-            aOrigin == nsGkAtoms::other ||
-            (aOrigin == nsGkAtoms::restore &&
+            aOrigin == ScrollOrigin::Other ||
+            (aOrigin == ScrollOrigin::Restore &&
              visualScrollUpdate->mUpdateType == FrameMetrics::eRestore);
         if (shouldClobber) {
           ps->AcknowledgePendingVisualScrollUpdate();
           ps->ClearPendingVisualScrollUpdate();
         }
       }
     }
   }
@@ -2853,19 +2881,19 @@ void ScrollFrameHelper::ScrollToImpl(nsP
   allowScrollOriginChange =
       (mAllowScrollOriginDowngrade || !isScrollOriginDowngrade) &&
       !suppressScrollOriginChange;
 
   if (allowScrollOriginChange) {
     mLastScrollOrigin = aOrigin;
     mAllowScrollOriginDowngrade = false;
   }
-  mLastSmoothScrollOrigin = nullptr;
+  mLastSmoothScrollOrigin = ScrollOrigin::NotSpecified;
   mScrollGeneration = ++sScrollGenerationCounter;
-  if (mLastScrollOrigin == nsGkAtoms::apz) {
+  if (mLastScrollOrigin == ScrollOrigin::Apz) {
     mApzScrollPos = GetScrollPosition();
   }
 
   // If the new scroll offset is going to clobber APZ's scroll offset, for
   // the RCD-RSF this will have the effect of resetting the visual viewport
   // offset to be the same as the new scroll (= layout viewport) offset.
   // The APZ callback transform, which reflects the difference between these
   // offsets, will subsequently be cleared. However, if we wait for APZ to
@@ -2921,17 +2949,17 @@ void ScrollFrameHelper::ScrollToImpl(nsP
     if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) &&
         !HasPerspective() && !HasBgAttachmentLocal() &&
         !mHasOutOfFlowContentInsideFilter) {
       bool haveScrollLinkedEffects =
           content->GetComposedDoc()->HasScrollLinkedEffect();
       bool apzDisabled = haveScrollLinkedEffects &&
                          StaticPrefs::apz_disable_for_scroll_linked_effects();
       if (!apzDisabled && !HasPluginFrames()) {
-        if (LastScrollOrigin() == nsGkAtoms::apz) {
+        if (LastScrollOrigin() == ScrollOrigin::Apz) {
           schedulePaint = false;
           PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
         } else if (mScrollableByAPZ) {
           nsIWidget* widget = presContext->GetNearestWidget();
           LayerManager* manager = widget ? widget->GetLayerManager() : nullptr;
           if (manager) {
             mozilla::layers::ScrollableLayerGuid::ViewID id;
             bool success = nsLayoutUtils::FindIDFor(content, &id);
@@ -2940,17 +2968,17 @@ void ScrollFrameHelper::ScrollToImpl(nsP
             // Schedule an empty transaction to carry over the scroll offset
             // update, instead of a full transaction. This empty transaction
             // might still get squashed into a full transaction if something
             // happens to trigger one.
             success = manager->SetPendingScrollUpdateForNextTransaction(
                 id,
                 {mScrollGeneration, CSSPoint::FromAppUnits(GetScrollPosition()),
                  CSSPoint::FromAppUnits(GetApzScrollPosition()),
-                 mLastScrollOrigin == nsGkAtoms::relative});
+                 mLastScrollOrigin == ScrollOrigin::Relative});
             if (success) {
               schedulePaint = false;
               mOuter->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
               PAINT_SKIP_LOG(
                   "Skipping due to APZ-forwarded main-thread scroll\n");
             } else {
               PAINT_SKIP_LOG(
                   "Failed to set pending scroll update on layer manager\n");
@@ -4243,17 +4271,17 @@ static void CalcRangeForScrollBy(int32_t
   *aLower = aPos - NSToCoordRound(aMultiplier *
                                   (aDelta > 0 ? aNegTolerance : aPosTolerance));
   *aUpper = aPos + NSToCoordRound(aMultiplier *
                                   (aDelta > 0 ? aPosTolerance : aNegTolerance));
 }
 
 void ScrollFrameHelper::ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit,
                                  ScrollMode aMode, nsIntPoint* aOverflow,
-                                 nsAtom* aOrigin,
+                                 ScrollOrigin aOrigin,
                                  nsIScrollableFrame::ScrollMomentum aMomentum,
                                  nsIScrollbarMediator::ScrollSnapMode aSnap) {
   // When a smooth scroll is being processed on a frame, mouse wheel and
   // trackpad momentum scroll event updates must notcancel the SMOOTH or
   // SMOOTH_MSD scroll animations, enabling Javascript that depends on them to
   // be responsive without forcing the user to wait for the fling animations to
   // completely stop.
   switch (aMomentum) {
@@ -4272,56 +4300,56 @@ void ScrollFrameHelper::ScrollBy(nsIntPo
     // the scroll is not completed to avoid non-smooth snapping to the
     // prior smooth scroll's destination.
     mDestination = GetScrollPosition();
   }
 
   nsSize deltaMultiplier;
   float negativeTolerance;
   float positiveTolerance;
-  if (!aOrigin) {
-    aOrigin = nsGkAtoms::other;
-  }
-  bool isGenericOrigin = (aOrigin == nsGkAtoms::other);
+  if (aOrigin == ScrollOrigin::NotSpecified) {
+    aOrigin = ScrollOrigin::Other;
+  }
+  bool isGenericOrigin = (aOrigin == ScrollOrigin::Other);
   switch (aUnit) {
     case ScrollUnit::DEVICE_PIXELS: {
       nscoord appUnitsPerDevPixel =
           mOuter->PresContext()->AppUnitsPerDevPixel();
       deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
       if (isGenericOrigin) {
-        aOrigin = nsGkAtoms::pixels;
+        aOrigin = ScrollOrigin::Pixels;
       }
       negativeTolerance = positiveTolerance = 0.5f;
       break;
     }
     case ScrollUnit::LINES: {
       deltaMultiplier = GetLineScrollAmount();
       if (isGenericOrigin) {
-        aOrigin = nsGkAtoms::lines;
+        aOrigin = ScrollOrigin::Lines;
       }
       negativeTolerance = positiveTolerance = 0.1f;
       break;
     }
     case ScrollUnit::PAGES: {
       deltaMultiplier = GetPageScrollAmount();
       if (isGenericOrigin) {
-        aOrigin = nsGkAtoms::pages;
+        aOrigin = ScrollOrigin::Pages;
       }
       negativeTolerance = 0.05f;
       positiveTolerance = 0;
       break;
     }
     case ScrollUnit::WHOLE: {
       nsPoint pos = GetScrollPosition();
       AdjustForWholeDelta(aDelta.x, &pos.x);
       AdjustForWholeDelta(aDelta.y, &pos.y);
       if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
         GetSnapPointForDestination(aUnit, mDestination, pos);
       }
-      ScrollTo(pos, aMode, nsGkAtoms::other);
+      ScrollTo(pos, aMode, ScrollOrigin::Other);
       // 'this' might be destroyed here
       if (aOverflow) {
         *aOverflow = nsIntPoint(0, 0);
       }
       return;
     }
     default:
       NS_ERROR("Invalid scroll mode");
@@ -4338,17 +4366,17 @@ void ScrollFrameHelper::ScrollBy(nsIntPo
   if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
     if (NeedsScrollSnap()) {
       nscoord appUnitsPerDevPixel =
           mOuter->PresContext()->AppUnitsPerDevPixel();
       deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
       negativeTolerance = 0.1f;
       positiveTolerance = 0;
       ScrollUnit snapUnit = aUnit;
-      if (aOrigin == nsGkAtoms::mouseWheel) {
+      if (aOrigin == ScrollOrigin::MouseWheel) {
         // When using a clicky scroll wheel, snap point selection works the same
         // as keyboard up/down/left/right navigation, but with varying amounts
         // of scroll delta.
         snapUnit = ScrollUnit::LINES;
       }
       GetSnapPointForDestination(snapUnit, mDestination, newPos);
     }
   }
@@ -4379,17 +4407,17 @@ void ScrollFrameHelper::ScrollBy(nsIntPo
       !nsLayoutUtils::AsyncPanZoomEnabled(mOuter)) {
     // When APZ is disabled, we must track the velocity
     // on the main thread; otherwise, the APZC will manage this.
     mVelocityQueue.Sample(GetScrollPosition());
   }
 }
 
 void ScrollFrameHelper::ScrollByCSSPixels(
-    const CSSIntPoint& aDelta, ScrollMode aMode, nsAtom* aOrigin,
+    const CSSIntPoint& aDelta, ScrollMode aMode, ScrollOrigin aOrigin,
     nsIScrollbarMediator::ScrollSnapMode aSnap) {
   nsPoint current = GetScrollPosition();
   // `current` value above might be a value which was aligned to in
   // layer-pixels, so starting from such points will make the difference between
   // the given position in script (e.g. scrollTo) and the aligned position
   // larger, in the worst case the difference can be observed in CSS pixels.
   // To avoid it, we use the current position in CSS pixels as the start
   // position.  Hopefully it exactly matches the position where it was given by
@@ -4398,18 +4426,18 @@ void ScrollFrameHelper::ScrollByCSSPixel
   // cases should be fixed in bug 1556685.
   CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
   nsPoint pt = CSSPoint::ToAppUnits(currentCSSPixels + aDelta);
 
   if (aSnap == nsIScrollableFrame::DEFAULT) {
     aSnap = nsIScrollableFrame::ENABLE_SNAP;
   }
 
-  if (aOrigin == nullptr) {
-    aOrigin = nsGkAtoms::other;
+  if (aOrigin == ScrollOrigin::NotSpecified) {
+    aOrigin = ScrollOrigin::Other;
   }
 
   nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
   nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2 * halfPixel - 1,
                2 * halfPixel - 1);
   // XXX I don't think the following blocks are needed anymore, now that
   // ScrollToImpl simply tries to scroll an integer number of layer
   // pixels from the current position
@@ -4446,17 +4474,17 @@ void ScrollFrameHelper::ScrollSnap(Scrol
 
 void ScrollFrameHelper::ScrollSnap(const nsPoint& aDestination,
                                    ScrollMode aMode) {
   nsRect scrollRange = GetLayoutScrollRange();
   nsPoint pos = GetScrollPosition();
   nsPoint snapDestination = scrollRange.ClampPoint(aDestination);
   if (GetSnapPointForDestination(ScrollUnit::DEVICE_PIXELS, pos,
                                  snapDestination)) {
-    ScrollTo(snapDestination, aMode, nsGkAtoms::other);
+    ScrollTo(snapDestination, aMode, ScrollOrigin::Other);
   }
 }
 
 nsSize ScrollFrameHelper::GetLineScrollAmount() const {
   RefPtr<nsFontMetrics> fm =
       nsLayoutUtils::GetInflatedFontMetricsForFrame(mOuter);
   NS_ASSERTION(fm, "FontMetrics is null, assuming fontHeight == 1 appunit");
   int32_t appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
@@ -4651,20 +4679,20 @@ void ScrollFrameHelper::ScrollToRestored
       if (!IsPhysicalLTR()) {
         // convert from logical to physical scroll position
         visualScrollToPos.x -=
             (GetVisualViewportSize().width - mScrolledFrame->GetRect().width);
         layoutScrollToPos.x -=
             (GetVisualViewportSize().width - mScrolledFrame->GetRect().width);
       }
       AutoWeakFrame weakFrame(mOuter);
-      // It's very important to pass nsGkAtoms::restore here, so
+      // It's very important to pass ScrollOrigin::Restore here, so
       // ScrollToWithOrigin won't clear out mRestorePos.
       ScrollToWithOrigin(layoutScrollToPos, ScrollMode::Instant,
-                         nsGkAtoms::restore, nullptr);
+                         ScrollOrigin::Restore, nullptr);
       if (!weakFrame.IsAlive()) {
         return;
       }
       if (mIsRoot && mOuter->PresContext()->IsRootContentDocument()) {
         mOuter->PresShell()->ScrollToVisual(
             visualScrollToPos, FrameMetrics::eRestore, ScrollMode::Instant);
       }
       if (state == LoadingState::Loading || NS_SUBTREE_DIRTY(mOuter)) {
@@ -5205,17 +5233,17 @@ void ScrollFrameHelper::CurPosAttributeC
     if (!weakFrame.IsAlive()) {
       return;
     }
   }
 
   if (aDoScroll) {
     ScrollToWithOrigin(dest,
                        isSmooth ? ScrollMode::Smooth : ScrollMode::Instant,
-                       nsGkAtoms::scrollbars, &allowedRange);
+                       ScrollOrigin::Scrollbars, &allowedRange);
   }
   // 'this' might be destroyed here
 }
 
 /* ============= Scroll events ========== */
 
 ScrollFrameHelper::ScrollEvent::ScrollEvent(ScrollFrameHelper* aHelper,
                                             bool aDelayed)
@@ -6614,17 +6642,18 @@ UniquePtr<PresState> ScrollFrameHelper::
   nsIScrollbarMediator* mediator = do_QueryFrame(GetScrolledFrame());
   if (mediator) {
     // child handles its own scroll state, so don't bother saving state here
     return nullptr;
   }
 
   // Don't store a scroll state if we never have been scrolled or restored
   // a previous scroll state, and we're not in the middle of a smooth scroll.
-  bool isInSmoothScroll = IsProcessingAsyncScroll() || mLastSmoothScrollOrigin;
+  bool isInSmoothScroll = IsProcessingAsyncScroll() ||
+                          mLastSmoothScrollOrigin != ScrollOrigin::NotSpecified;
   if (!mHasBeenScrolled && !mDidHistoryRestore && !isInSmoothScroll) {
     return nullptr;
   }
 
   UniquePtr<PresState> state = NewPresState();
   bool allowScrollOriginDowngrade =
       !nsLayoutUtils::CanScrollOriginClobberApz(mLastScrollOrigin) ||
       mAllowScrollOriginDowngrade;
@@ -6651,17 +6680,17 @@ UniquePtr<PresState> ScrollFrameHelper::
     // Only save resolution properties for root scroll frames
     state->resolution() = mOuter->PresShell()->GetResolution();
   }
   return state;
 }
 
 void ScrollFrameHelper::RestoreState(PresState* aState) {
   mRestorePos = aState->scrollState();
-  MOZ_ASSERT(mLastScrollOrigin == nsGkAtoms::other);
+  MOZ_ASSERT(mLastScrollOrigin == ScrollOrigin::Other);
   mAllowScrollOriginDowngrade = aState->allowScrollOriginDowngrade();
   mDidHistoryRestore = true;
   mLastPos = mScrolledFrame ? GetLogicalVisualViewportOffset() : nsPoint(0, 0);
 
   // Resolution properties should only exist on root scroll frames.
   MOZ_ASSERT(mIsRoot || aState->resolution() == 1.0);
 
   if (mIsRoot) {
@@ -7115,17 +7144,17 @@ bool ScrollFrameHelper::DragScroll(Widge
       if (scrollPoint.y < rangeRect.height) {
         willScroll = true;
       }
     }
   }
 
   if (offset.x || offset.y) {
     ScrollTo(GetScrollPosition() + offset, ScrollMode::Normal,
-             nsGkAtoms::other);
+             ScrollOrigin::Other);
   }
 
   return willScroll;
 }
 
 static nsSliderFrame* GetSliderFrame(nsIFrame* aScrollbarFrame) {
   if (!aScrollbarFrame) {
     return nullptr;
@@ -7169,17 +7198,17 @@ static void AsyncScrollbarDragRejected(n
 void ScrollFrameHelper::AsyncScrollbarDragRejected() {
   // We don't get told which scrollbar requested the async drag,
   // so we notify both.
   ::AsyncScrollbarDragRejected(mHScrollbarBox);
   ::AsyncScrollbarDragRejected(mVScrollbarBox);
 }
 
 void ScrollFrameHelper::ApzSmoothScrollTo(const nsPoint& aDestination,
-                                          nsAtom* aOrigin) {
+                                          ScrollOrigin aOrigin) {
   if (mApzSmoothScrollDestination == Some(aDestination) &&
       mScrollGeneration == sScrollGenerationCounter) {
     // If we already sent APZ a smooth-scroll request to this
     // destination with this generation (i.e. it was the last request
     // we sent), then don't send another one because it is redundant.
     // This is to avoid a scenario where pages do repeated scrollBy
     // calls, incrementing the generation counter, and blocking APZ from
     // syncing the scroll offset back to the main thread.
@@ -7193,16 +7222,17 @@ void ScrollFrameHelper::ApzSmoothScrollT
     // incremented and this early-exit won't get taken. Bug 1231177 is
     // on file for this.
     return;
   }
 
   // The animation will be handled in the compositor, pass the
   // information needed to start the animation and skip the main-thread
   // animation for this scroll.
+  MOZ_ASSERT(aOrigin != ScrollOrigin::NotSpecified);
   mLastSmoothScrollOrigin = aOrigin;
   mApzSmoothScrollDestination = Some(aDestination);
   mScrollGeneration = ++sScrollGenerationCounter;
 
   if (!nsLayoutUtils::HasDisplayPort(mOuter->GetContent())) {
     // If this frame doesn't have a displayport then there won't be an
     // APZC instance for it and so there won't be anything to process
     // this smooth scroll request. We should set a displayport on this
@@ -7238,18 +7268,18 @@ bool ScrollFrameHelper::SmoothScrollVisu
   //     the layout scroll range
   //   - clamp mDestination to the layout scroll range here
   //   - make sure ComputeScrollMetadata() picks up the former as the
   //     smooth scroll destination to send to APZ.
   mDestination = GetVisualScrollRange().ClampPoint(aVisualViewportOffset);
 
   // Perform the scroll.
   ApzSmoothScrollTo(mDestination, aUpdateType == FrameMetrics::eRestore
-                                      ? nsGkAtoms::restore
-                                      : nsGkAtoms::other);
+                                      ? ScrollOrigin::Restore
+                                      : ScrollOrigin::Other);
   return true;
 }
 
 bool ScrollFrameHelper::IsSmoothScroll(dom::ScrollBehavior aBehavior) const {
   if (aBehavior == dom::ScrollBehavior::Smooth) {
     return true;
   }
 
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -220,52 +220,55 @@ class ScrollFrameHelper : public nsIRefl
                                            mozilla::TimeDuration aDeltaTime);
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    * aRange is the range of allowable scroll positions around the desired
    * aScrollPosition. Null means only aScrollPosition is allowed.
    * This is a closed-ended range --- aRange.XMost()/aRange.YMost() are allowed.
    */
   void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
-                nsAtom* aOrigin = nullptr, const nsRect* aRange = nullptr,
+                ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
+                const nsRect* aRange = nullptr,
                 nsIScrollbarMediator::ScrollSnapMode aSnap =
                     nsIScrollbarMediator::DISABLE_SNAP);
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
                          ScrollMode aMode = ScrollMode::Instant,
                          nsIScrollbarMediator::ScrollSnapMode aSnap =
                              nsIScrollbarMediator::DEFAULT,
-                         nsAtom* aOrigin = nullptr);
+                         ScrollOrigin aOrigin = ScrollOrigin::NotSpecified);
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
-  void ScrollToCSSPixelsApproximate(const mozilla::CSSPoint& aScrollPosition,
-                                    nsAtom* aOrigin = nullptr);
+  void ScrollToCSSPixelsApproximate(
+      const mozilla::CSSPoint& aScrollPosition,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified);
 
   CSSIntPoint GetScrollPositionCSSPixels();
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollToImpl(nsPoint aScrollPosition, const nsRect& aRange,
-                    nsAtom* aOrigin = nullptr);
+                    ScrollOrigin aOrigin = ScrollOrigin::NotSpecified);
   void ScrollVisual();
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollBy(nsIntPoint aDelta, mozilla::ScrollUnit aUnit, ScrollMode aMode,
-                nsIntPoint* aOverflow, nsAtom* aOrigin = nullptr,
+                nsIntPoint* aOverflow,
+                ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
                 nsIScrollableFrame::ScrollMomentum aMomentum =
                     nsIScrollableFrame::NOT_MOMENTUM,
                 nsIScrollbarMediator::ScrollSnapMode aSnap =
                     nsIScrollbarMediator::DISABLE_SNAP);
   void ScrollByCSSPixels(const CSSIntPoint& aDelta,
                          ScrollMode aMode = ScrollMode::Instant,
-                         nsAtom* aOrigin = nullptr,
+                         ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
                          nsIScrollbarMediator::ScrollSnapMode aSnap =
                              nsIScrollbarMediator::DEFAULT);
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollToRestoredPosition();
 
   enum class LoadingState { Loading, Stopped, Loaded };
@@ -450,24 +453,26 @@ class ScrollFrameHelper : public nsIRefl
   void TriggerDisplayPortExpiration();
   void ResetDisplayPortExpiryTimer();
 
   void ScheduleSyntheticMouseMove();
   static void ScrollActivityCallback(nsITimer* aTimer, void* anInstance);
 
   void HandleScrollbarStyleSwitching();
 
-  nsAtom* LastScrollOrigin() const { return mLastScrollOrigin; }
-  nsAtom* LastSmoothScrollOrigin() const { return mLastSmoothScrollOrigin; }
+  ScrollOrigin LastScrollOrigin() const { return mLastScrollOrigin; }
+  ScrollOrigin LastSmoothScrollOrigin() const {
+    return mLastSmoothScrollOrigin;
+  }
   uint32_t CurrentScrollGeneration() const { return mScrollGeneration; }
   nsPoint LastScrollDestination() const { return mDestination; }
   void ResetScrollInfoIfGeneration(uint32_t aGeneration) {
     if (aGeneration == mScrollGeneration) {
-      mLastScrollOrigin = nullptr;
-      mLastSmoothScrollOrigin = nullptr;
+      mLastScrollOrigin = ScrollOrigin::NotSpecified;
+      mLastSmoothScrollOrigin = ScrollOrigin::NotSpecified;
     }
   }
   bool WantAsyncScroll() const;
   Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
       LayerManager* aLayerManager, const nsIFrame* aContainerReferenceFrame,
       const Maybe<ContainerLayerParameters>& aParameters,
       const mozilla::DisplayItemClip* aClip) const;
   void ClipLayerToDisplayPort(
@@ -546,18 +551,18 @@ class ScrollFrameHelper : public nsIRefl
   nsIFrame* mScrollCornerBox;
   nsIFrame* mResizerBox;
   nsContainerFrame* mOuter;
   const nsIFrame* mReferenceFrameDuringPainting;
   RefPtr<AsyncScroll> mAsyncScroll;
   RefPtr<AsyncSmoothMSDScroll> mAsyncSmoothMSDScroll;
   RefPtr<ScrollbarActivity> mScrollbarActivity;
   nsTArray<nsIScrollPositionListener*> mListeners;
-  nsAtom* mLastScrollOrigin;
-  nsAtom* mLastSmoothScrollOrigin;
+  ScrollOrigin mLastScrollOrigin;
+  ScrollOrigin mLastSmoothScrollOrigin;
   Maybe<nsPoint> mApzSmoothScrollDestination;
   uint32_t mScrollGeneration;
   // NOTE: On mobile this value might be factoring into overflow:hidden region
   // in the case of the top level document.
   nsRect mScrollPort;
   nsSize mMinimumScaleSize;
 
   // Stores the ICB size for the root document if this frame is using the
@@ -719,33 +724,33 @@ class ScrollFrameHelper : public nsIRefl
     AutoWeakFrame& mWeakOuter;
     bool mOldSuppressValue;
   };
 
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollToWithOrigin(nsPoint aScrollPosition, ScrollMode aMode,
-                          nsAtom* aOrigin,  // nullptr indicates "other" origin
-                          const nsRect* aRange,
+                          ScrollOrigin aOrigin, const nsRect* aRange,
                           nsIScrollbarMediator::ScrollSnapMode aSnap =
                               nsIScrollbarMediator::DISABLE_SNAP);
 
-  void CompleteAsyncScroll(const nsRect& aRange, nsAtom* aOrigin = nullptr);
+  void CompleteAsyncScroll(const nsRect& aRange,
+                           ScrollOrigin aOrigin = ScrollOrigin::NotSpecified);
 
   bool HasPluginFrames();
   bool HasPerspective() const { return mOuter->ChildrenHavePerspective(); }
   bool HasBgAttachmentLocal() const;
   mozilla::StyleDirection GetScrolledFrameDir() const;
 
   // Ask APZ to smooth scroll to |aDestination|.
   // This method does not clamp the destination; callers should clamp it to
   // either the layout or the visual scroll range (APZ will happily smooth
   // scroll to either).
-  void ApzSmoothScrollTo(const nsPoint& aDestination, nsAtom* aOrigin);
+  void ApzSmoothScrollTo(const nsPoint& aDestination, ScrollOrigin aOrigin);
 
   // Removes any RefreshDriver observers we might have registered.
   void RemoveObservers();
 };
 
 }  // namespace mozilla
 
 /**
@@ -925,53 +930,57 @@ class nsHTMLScrollFrame : public nsConta
   nsMargin GetScrollPadding() const final { return mHelper.GetScrollPadding(); }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
                 const nsRect* aRange = nullptr,
                 nsIScrollbarMediator::ScrollSnapMode aSnap =
                     nsIScrollbarMediator::DISABLE_SNAP) final {
-    mHelper.ScrollTo(aScrollPosition, aMode, nsGkAtoms::other, aRange, aSnap);
+    mHelper.ScrollTo(aScrollPosition, aMode, ScrollOrigin::Other, aRange,
+                     aSnap);
   }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
-  void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
-                         ScrollMode aMode = ScrollMode::Instant,
-                         nsIScrollbarMediator::ScrollSnapMode aSnap =
-                             nsIScrollbarMediator::DEFAULT,
-                         nsAtom* aOrigin = nullptr) final {
+  void ScrollToCSSPixels(
+      const CSSIntPoint& aScrollPosition,
+      ScrollMode aMode = ScrollMode::Instant,
+      nsIScrollbarMediator::ScrollSnapMode aSnap =
+          nsIScrollbarMediator::DEFAULT,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified) final {
     mHelper.ScrollToCSSPixels(aScrollPosition, aMode, aSnap, aOrigin);
   }
-  void ScrollToCSSPixelsApproximate(const mozilla::CSSPoint& aScrollPosition,
-                                    nsAtom* aOrigin = nullptr) final {
+  void ScrollToCSSPixelsApproximate(
+      const mozilla::CSSPoint& aScrollPosition,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified) final {
     mHelper.ScrollToCSSPixelsApproximate(aScrollPosition, aOrigin);
   }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   CSSIntPoint GetScrollPositionCSSPixels() final {
     return mHelper.GetScrollPositionCSSPixels();
   }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollBy(nsIntPoint aDelta, mozilla::ScrollUnit aUnit, ScrollMode aMode,
-                nsIntPoint* aOverflow, nsAtom* aOrigin = nullptr,
+                nsIntPoint* aOverflow,
+                ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
                 nsIScrollableFrame::ScrollMomentum aMomentum =
                     nsIScrollableFrame::NOT_MOMENTUM,
                 nsIScrollbarMediator::ScrollSnapMode aSnap =
                     nsIScrollbarMediator::DISABLE_SNAP) final {
     mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum,
                      aSnap);
   }
   void ScrollByCSSPixels(const CSSIntPoint& aDelta,
                          ScrollMode aMode = ScrollMode::Instant,
-                         nsAtom* aOrigin = nullptr,
+                         ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
                          nsIScrollbarMediator::ScrollSnapMode aSnap =
                              nsIScrollbarMediator::DEFAULT) final {
     mHelper.ScrollByCSSPixels(aDelta, aMode, aOrigin, aSnap);
   }
   void ScrollSnap() final { mHelper.ScrollSnap(); }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
@@ -1012,18 +1021,18 @@ class nsHTMLScrollFrame : public nsConta
   void ClearDidHistoryRestore() final { mHelper.mDidHistoryRestore = false; }
   void MarkEverScrolled() final { mHelper.MarkEverScrolled(); }
   bool IsRectNearlyVisible(const nsRect& aRect) final {
     return mHelper.IsRectNearlyVisible(aRect);
   }
   nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const final {
     return mHelper.ExpandRectToNearlyVisible(aRect);
   }
-  nsAtom* LastScrollOrigin() final { return mHelper.LastScrollOrigin(); }
-  nsAtom* LastSmoothScrollOrigin() final {
+  ScrollOrigin LastScrollOrigin() final { return mHelper.LastScrollOrigin(); }
+  ScrollOrigin LastSmoothScrollOrigin() final {
     return mHelper.LastSmoothScrollOrigin();
   }
   uint32_t CurrentScrollGeneration() final {
     return mHelper.CurrentScrollGeneration();
   }
   nsPoint LastScrollDestination() final {
     return mHelper.LastScrollDestination();
   }
@@ -1395,50 +1404,54 @@ class nsXULScrollFrame final : public ns
   }
   nsMargin GetScrollPadding() const final { return mHelper.GetScrollPadding(); }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollTo(
       nsPoint aScrollPosition, ScrollMode aMode, const nsRect* aRange = nullptr,
       ScrollSnapMode aSnap = nsIScrollbarMediator::DISABLE_SNAP) final {
-    mHelper.ScrollTo(aScrollPosition, aMode, nsGkAtoms::other, aRange, aSnap);
+    mHelper.ScrollTo(aScrollPosition, aMode, ScrollOrigin::Other, aRange,
+                     aSnap);
   }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
-  void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
-                         ScrollMode aMode = ScrollMode::Instant,
-                         nsIScrollbarMediator::ScrollSnapMode aSnap =
-                             nsIScrollbarMediator::DISABLE_SNAP,
-                         nsAtom* aOrigin = nullptr) final {
+  void ScrollToCSSPixels(
+      const CSSIntPoint& aScrollPosition,
+      ScrollMode aMode = ScrollMode::Instant,
+      nsIScrollbarMediator::ScrollSnapMode aSnap =
+          nsIScrollbarMediator::DISABLE_SNAP,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified) final {
     mHelper.ScrollToCSSPixels(aScrollPosition, aMode, aSnap, aOrigin);
   }
-  void ScrollToCSSPixelsApproximate(const mozilla::CSSPoint& aScrollPosition,
-                                    nsAtom* aOrigin = nullptr) final {
+  void ScrollToCSSPixelsApproximate(
+      const mozilla::CSSPoint& aScrollPosition,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified) final {
     mHelper.ScrollToCSSPixelsApproximate(aScrollPosition, aOrigin);
   }
   CSSIntPoint GetScrollPositionCSSPixels() final {
     return mHelper.GetScrollPositionCSSPixels();
   }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void ScrollBy(nsIntPoint aDelta, mozilla::ScrollUnit aUnit, ScrollMode aMode,
-                nsIntPoint* aOverflow, nsAtom* aOrigin = nullptr,
+                nsIntPoint* aOverflow,
+                ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
                 nsIScrollableFrame::ScrollMomentum aMomentum =
                     nsIScrollableFrame::NOT_MOMENTUM,
                 nsIScrollbarMediator::ScrollSnapMode aSnap =
                     nsIScrollbarMediator::DISABLE_SNAP) final {
     mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum,
                      aSnap);
   }
   void ScrollByCSSPixels(const CSSIntPoint& aDelta,
                          ScrollMode aMode = ScrollMode::Instant,
-                         nsAtom* aOrigin = nullptr,
+                         ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
                          nsIScrollbarMediator::ScrollSnapMode aSnap =
                              nsIScrollbarMediator::DEFAULT) final {
     mHelper.ScrollByCSSPixels(aDelta, aMode, aOrigin, aSnap);
   }
   void ScrollSnap() final { mHelper.ScrollSnap(); }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
@@ -1479,18 +1492,18 @@ class nsXULScrollFrame final : public ns
   void ClearDidHistoryRestore() final { mHelper.mDidHistoryRestore = false; }
   void MarkEverScrolled() final { mHelper.MarkEverScrolled(); }
   bool IsRectNearlyVisible(const nsRect& aRect) final {
     return mHelper.IsRectNearlyVisible(aRect);
   }
   nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const final {
     return mHelper.ExpandRectToNearlyVisible(aRect);
   }
-  nsAtom* LastScrollOrigin() final { return mHelper.LastScrollOrigin(); }
-  nsAtom* LastSmoothScrollOrigin() final {
+  ScrollOrigin LastScrollOrigin() final { return mHelper.LastScrollOrigin(); }
+  ScrollOrigin LastSmoothScrollOrigin() final {
     return mHelper.LastSmoothScrollOrigin();
   }
   uint32_t CurrentScrollGeneration() final {
     return mHelper.CurrentScrollGeneration();
   }
   nsPoint LastScrollDestination() final {
     return mHelper.LastScrollDestination();
   }
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -8,33 +8,33 @@
  * interface that provides scroll APIs implemented by scrollable frames
  */
 
 #ifndef nsIScrollFrame_h___
 #define nsIScrollFrame_h___
 
 #include "nsCoord.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/ScrollOrigin.h"
 #include "mozilla/ScrollStyles.h"
 #include "mozilla/ScrollTypes.h"
 #include "mozilla/gfx/Point.h"
 #include "nsIScrollbarMediator.h"
 #include "Units.h"
 #include "FrameMetrics.h"
 
 #define NS_DEFAULT_VERTICAL_SCROLL_DISTANCE 3
 #define NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE 5
 
 class gfxContext;
 class nsBoxLayoutState;
 class nsIScrollPositionListener;
 class nsIFrame;
 class nsPresContext;
 class nsIContent;
-class nsAtom;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 struct ContainerLayerParameters;
 class DisplayItemClip;
 namespace layers {
 struct ScrollMetadata;
 class Layer;
@@ -52,16 +52,17 @@ class ScrollAnchorContainer;
  */
 class nsIScrollableFrame : public nsIScrollbarMediator {
  public:
   typedef mozilla::CSSIntPoint CSSIntPoint;
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
   typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
   typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer;
   typedef mozilla::ScrollMode ScrollMode;
+  typedef mozilla::ScrollOrigin ScrollOrigin;
 
   NS_DECL_QUERYFRAME_TARGET(nsIScrollableFrame)
 
   /**
    * Get the frame for the content that we are scrolling within
    * this scrollable frame.
    */
   virtual nsIFrame* GetScrolledFrame() const = 0;
@@ -262,31 +263,33 @@ class nsIScrollableFrame : public nsIScr
    * When aMode is SMOOTH_MSD, intermediate animation frames may be outside the
    * range and / or moving in any direction; GetScrollPositionCSSPixels will be
    * exactly aScrollPosition at the end of the scroll animation unless the
    * SMOOTH_MSD animation is interrupted.
    *
    * FIXME: Drop |aSnap| argument once after we finished the migration to the
    * Scroll Snap Module v1. We should alway use ENABLE_SNAP.
    */
-  virtual void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
-                                 ScrollMode aMode = ScrollMode::Instant,
-                                 nsIScrollbarMediator::ScrollSnapMode aSnap =
-                                     nsIScrollbarMediator::DEFAULT,
-                                 nsAtom* aOrigin = nullptr) = 0;
+  virtual void ScrollToCSSPixels(
+      const CSSIntPoint& aScrollPosition,
+      ScrollMode aMode = ScrollMode::Instant,
+      nsIScrollbarMediator::ScrollSnapMode aSnap =
+          nsIScrollbarMediator::DEFAULT,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified) = 0;
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    * Scrolls to a particular position in float CSS pixels.
    * This does not guarantee that GetScrollPositionCSSPixels equals
    * aScrollPosition afterward. It tries to scroll as close to
    * aScrollPosition as possible while scrolling by an integer
    * number of layer pixels (so the operation is fast and looks clean).
    */
   virtual void ScrollToCSSPixelsApproximate(
-      const mozilla::CSSPoint& aScrollPosition, nsAtom* aOrigin = nullptr) = 0;
+      const mozilla::CSSPoint& aScrollPosition,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified) = 0;
 
   /**
    * Returns the scroll position in integer CSS pixels, rounded to the nearest
    * pixel.
    */
   virtual CSSIntPoint GetScrollPositionCSSPixels() = 0;
   /**
    * @note This method might destroy the frame, pres shell and other objects.
@@ -295,30 +298,30 @@ class nsIScrollableFrame : public nsIScr
    * content is scrolled all the way in the direction(s) given by aDelta.
    * @param aOverflow if non-null, returns the amount that scrolling
    * was clamped by in each direction (how far we moved the scroll position
    * to bring it back into the legal range). This is never negative. The
    * values are in device pixels.
    */
   virtual void ScrollBy(nsIntPoint aDelta, mozilla::ScrollUnit aUnit,
                         ScrollMode aMode, nsIntPoint* aOverflow = nullptr,
-                        nsAtom* aOrigin = nullptr,
+                        ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
                         ScrollMomentum aMomentum = NOT_MOMENTUM,
                         nsIScrollbarMediator::ScrollSnapMode aSnap =
                             nsIScrollbarMediator::DISABLE_SNAP) = 0;
 
   /**
    * FIXME: Drop |aSnap| argument once after we finished the migration to the
    * Scroll Snap Module v1. We should alway use ENABLE_SNAP.
    */
-  virtual void ScrollByCSSPixels(const CSSIntPoint& aDelta,
-                                 ScrollMode aMode = ScrollMode::Instant,
-                                 nsAtom* aOrigin = nullptr,
-                                 nsIScrollbarMediator::ScrollSnapMode aSnap =
-                                     nsIScrollbarMediator::DEFAULT) = 0;
+  virtual void ScrollByCSSPixels(
+      const CSSIntPoint& aDelta, ScrollMode aMode = ScrollMode::Instant,
+      ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
+      nsIScrollbarMediator::ScrollSnapMode aSnap =
+          nsIScrollbarMediator::DEFAULT) = 0;
 
   /**
    * Perform scroll snapping, possibly resulting in a smooth scroll to
    * maintain the scroll snap position constraints.  Velocity sampled from
    * main thread scrolling is used to determine best matching snap point
    * when called after a fling gesture on a trackpad or mouse wheel.
    */
   virtual void ScrollSnap() = 0;
@@ -408,35 +411,35 @@ class nsIScrollableFrame : public nsIScr
   virtual bool IsRectNearlyVisible(const nsRect& aRect) = 0;
   /**
    * Expand the given rect taking into account which directions we can scroll
    * and how far we want to expand for frame visibility purposes.
    */
   virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const = 0;
   /**
    * Returns the origin that triggered the last instant scroll. Will equal
-   * nsGkAtoms::apz when the compositor's replica frame metrics includes the
+   * ScrollOrigin::Apz when the compositor's replica frame metrics includes the
    * latest instant scroll.
    */
-  virtual nsAtom* LastScrollOrigin() = 0;
+  virtual ScrollOrigin LastScrollOrigin() = 0;
   /**
    * Returns the origin that triggered the last smooth scroll.
-   * Will equal nsGkAtoms::apz when the compositor's replica frame
+   * Will equal ScrollOrigin::Apz when the compositor's replica frame
    * metrics includes the latest smooth scroll.  The compositor will always
    * perform an instant scroll prior to instantiating any smooth scrolls
    * if LastScrollOrigin and LastSmoothScrollOrigin indicate that
    * an instant scroll and a smooth scroll have occurred since the last
    * replication of the frame metrics.
    *
    * This is set to nullptr to when the compositor thread acknowledges that
    * the smooth scroll has been started.  If the smooth scroll has been stomped
    * by an instant scroll before the smooth scroll could be started by the
    * compositor, this is set to nullptr to clear the smooth scroll.
    */
-  virtual nsAtom* LastSmoothScrollOrigin() = 0;
+  virtual ScrollOrigin LastSmoothScrollOrigin() = 0;
   /**
    * Returns the current generation counter for the scroll. This counter
    * increments every time the scroll position is set.
    */
   virtual uint32_t CurrentScrollGeneration() = 0;
   /**
    * LastScrollDestination returns the destination of the most recently
    * requested smooth scroll animation.
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -2271,32 +2271,16 @@ STATIC_ATOMS = [
     Atom("Save", "Save"),
     Atom("Find", "Find"),
     Atom("Help", "Help"),
     Atom("Print", "Print"),
     Atom("SendMail", "SendMail"),
     Atom("ForwardMail", "ForwardMail"),
     Atom("ReplyToMail", "ReplyToMail"),
 
-    # Scroll origins (these are used in various scrolling functions in
-    # nsIScrollableFrame and ScrollFrameHelper). These are divided into two lists
-    # - origins in the first one have smooth-scrolling prefs associated with them,
-    # under the "general.smoothScroll.<origin>.*" pref branch. Origins in the
-    # second one do not.
-    Atom("mouseWheel", "mouseWheel"),  # For discrete wheel events (e.g. not OSX magic mouse)
-    Atom("pixels",     "pixels"),
-    Atom("lines",      "lines"),
-    Atom("pages",      "pages"),
-    Atom("scrollbars", "scrollbars"),
-    # Atom("other",      "other"),  # "other" is present above
-    # Scroll origins without smooth-scrolling prefs
-    Atom("apz",        "apz"),
-    Atom("restore",    "restore"),
-    Atom("relative",    "relative"),
-
     Atom("alert", "alert"),
     Atom("alertdialog", "alertdialog"),
     Atom("application", "application"),
     Atom("aria_colcount", "aria-colcount"),
     Atom("aria_colindex", "aria-colindex"),
     Atom("aria_colindextext", "aria-colindextext"),
     Atom("aria_colspan", "aria-colspan"),
     Atom("aria_details", "aria-details"),