tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ScrollbarActivity.cpp (5315B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ScrollbarActivity.h"
      8 
      9 #include "PresShell.h"
     10 #include "mozilla/LookAndFeel.h"
     11 #include "mozilla/ScrollContainerFrame.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/dom/Element.h"
     14 #include "mozilla/dom/Event.h"
     15 #include "nsContentUtils.h"
     16 #include "nsIContent.h"
     17 #include "nsIFrame.h"
     18 #include "nsIScrollbarMediator.h"
     19 #include "nsITimer.h"
     20 #include "nsLayoutUtils.h"
     21 #include "nsQueryFrame.h"
     22 #include "nsRefreshDriver.h"
     23 #include "nsScrollbarFrame.h"
     24 
     25 namespace mozilla::layout {
     26 
     27 using mozilla::dom::Element;
     28 
     29 NS_IMPL_ISUPPORTS(ScrollbarActivity, nsIDOMEventListener)
     30 
     31 static bool DisplayOnMouseMove() {
     32  return LookAndFeel::GetInt(LookAndFeel::IntID::ScrollbarDisplayOnMouseMove);
     33 }
     34 
     35 void ScrollbarActivity::Destroy() {
     36  StopListeningForScrollAreaEvents();
     37  CancelFadeTimer();
     38 }
     39 
     40 void ScrollbarActivity::ActivityOccurred() {
     41  ActivityStarted();
     42  ActivityStopped();
     43 }
     44 
     45 static void SetScrollbarActive(Element* aScrollbar, bool aIsActive) {
     46  if (!aScrollbar) {
     47    return;
     48  }
     49  if (aIsActive) {
     50    if (nsScrollbarFrame* sf = do_QueryFrame(aScrollbar->GetPrimaryFrame())) {
     51      sf->WillBecomeActive();
     52    }
     53  }
     54  aScrollbar->SetBoolAttr(nsGkAtoms::active, aIsActive);
     55 }
     56 
     57 void ScrollbarActivity::ActivityStarted() {
     58  const bool wasActive = IsActive();
     59  mNestedActivityCounter++;
     60  if (wasActive) {
     61    return;
     62  }
     63  CancelFadeTimer();
     64  if (mScrollbarEffectivelyVisible) {
     65    return;
     66  }
     67  StartListeningForScrollAreaEvents();
     68  SetScrollbarActive(GetHorizontalScrollbar(), true);
     69  SetScrollbarActive(GetVerticalScrollbar(), true);
     70  mScrollbarEffectivelyVisible = true;
     71 }
     72 
     73 void ScrollbarActivity::ActivityStopped() {
     74  if (!IsActive()) {
     75    // This can happen if there was a frame reconstruction while the activity
     76    // was ongoing. In this case we just do nothing. We should probably handle
     77    // this case better.
     78    return;
     79  }
     80  mNestedActivityCounter--;
     81  if (IsActive()) {
     82    return;
     83  }
     84  StartFadeTimer();
     85 }
     86 
     87 NS_IMETHODIMP
     88 ScrollbarActivity::HandleEvent(dom::Event* aEvent) {
     89  if (!mScrollbarEffectivelyVisible && !DisplayOnMouseMove()) {
     90    return NS_OK;
     91  }
     92 
     93  nsAutoString type;
     94  aEvent->GetType(type);
     95 
     96  auto* targetContent =
     97      nsIContent::FromEventTargetOrNull(aEvent->GetOriginalTarget());
     98  if (type.EqualsLiteral("mousemove")) {
     99    // Mouse motions anywhere in the scrollable frame should keep the
    100    // scrollbars visible, but we have to be careful as content descendants of
    101    // our scrollable content aren't necessarily scrolled by our scroll frame
    102    // (if they are out of flow and their containing block is not a descendant
    103    // of our scroll frame) and we don't want those to activate us.
    104    nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame);
    105    MOZ_ASSERT(scrollFrame);
    106    ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(scrollFrame);
    107    nsIFrame* targetFrame =
    108        targetContent ? targetContent->GetPrimaryFrame() : nullptr;
    109    if ((scrollContainerFrame &&
    110         scrollContainerFrame->IsRootScrollFrameOfDocument()) ||
    111        !targetFrame ||
    112        nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
    113            scrollFrame, targetFrame,
    114            scrollFrame->PresShell()->GetRootFrame())) {
    115      ActivityOccurred();
    116    }
    117    return NS_OK;
    118  }
    119 
    120  return NS_OK;
    121 }
    122 
    123 void ScrollbarActivity::StartListeningForScrollAreaEvents() {
    124  if (mListeningForScrollAreaEvents) {
    125    return;
    126  }
    127  nsIFrame* scrollArea = do_QueryFrame(mScrollableFrame);
    128  scrollArea->GetContent()->AddEventListener(u"mousemove"_ns, this, true);
    129  mListeningForScrollAreaEvents = true;
    130 }
    131 
    132 void ScrollbarActivity::StopListeningForScrollAreaEvents() {
    133  if (!mListeningForScrollAreaEvents) {
    134    return;
    135  }
    136  nsIFrame* scrollArea = do_QueryFrame(mScrollableFrame);
    137  scrollArea->GetContent()->RemoveEventListener(u"mousemove"_ns, this, true);
    138  mListeningForScrollAreaEvents = false;
    139 }
    140 
    141 void ScrollbarActivity::CancelFadeTimer() {
    142  if (mFadeTimer) {
    143    mFadeTimer->Cancel();
    144  }
    145 }
    146 
    147 void ScrollbarActivity::StartFadeTimer() {
    148  CancelFadeTimer();
    149  if (StaticPrefs::layout_testing_overlay_scrollbars_always_visible()) {
    150    return;
    151  }
    152  if (!mFadeTimer) {
    153    mFadeTimer = NS_NewTimer();
    154  }
    155  mFadeTimer->InitWithNamedFuncCallback(
    156      [](nsITimer*, void* aClosure) {
    157        RefPtr<ScrollbarActivity> activity =
    158            static_cast<ScrollbarActivity*>(aClosure);
    159        activity->BeginFade();
    160      },
    161      this, LookAndFeel::GetInt(LookAndFeel::IntID::ScrollbarFadeBeginDelay),
    162      nsITimer::TYPE_ONE_SHOT, "ScrollbarActivity::FadeBeginTimerFired"_ns);
    163 }
    164 
    165 void ScrollbarActivity::BeginFade() {
    166  MOZ_ASSERT(!IsActive());
    167  mScrollbarEffectivelyVisible = false;
    168  SetScrollbarActive(GetHorizontalScrollbar(), false);
    169  SetScrollbarActive(GetVerticalScrollbar(), false);
    170 }
    171 
    172 Element* ScrollbarActivity::GetScrollbarContent(bool aVertical) {
    173  nsIFrame* box = mScrollableFrame->GetScrollbarBox(aVertical);
    174  return box ? box->GetContent()->AsElement() : nullptr;
    175 }
    176 
    177 }  // namespace mozilla::layout