1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ScrollbarActivity.h"
7 #include "nsIScrollbarMediator.h"
8 #include "nsIContent.h"
9 #include "nsIDOMEvent.h"
10 #include "nsIDOMElementCSSInlineStyle.h"
11 #include "nsIDOMCSSStyleDeclaration.h"
13 #include "nsContentUtils.h"
14 #include "nsAString.h"
15 #include "nsQueryFrame.h"
16 #include "nsComponentManagerUtils.h"
17 #include "mozilla/LookAndFeel.h"
18 #include "mozilla/Preferences.h"
23 NS_IMPL_ISUPPORTS(ScrollbarActivity
, nsIDOMEventListener
)
26 GetForceAlwaysVisiblePref()
28 static bool sForceAlwaysVisible
;
29 static bool sForceAlwaysVisiblePrefCached
= false;
30 if (!sForceAlwaysVisiblePrefCached
) {
31 Preferences::AddBoolVarCache(&sForceAlwaysVisible
,
32 "layout.testing.overlay-scrollbars.always-visible");
33 sForceAlwaysVisiblePrefCached
= true;
35 return sForceAlwaysVisible
;
39 ScrollbarActivity::QueryLookAndFeelVals()
41 // Fade animation constants
42 mScrollbarFadeBeginDelay
=
43 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollbarFadeBeginDelay
);
44 mScrollbarFadeDuration
=
45 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollbarFadeDuration
);
46 // Controls whether we keep the mouse move listener so we can display the
47 // scrollbars whenever the user moves the mouse within the scroll area.
49 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollbarDisplayOnMouseMove
);
53 ScrollbarActivity::Destroy()
55 StopListeningForScrollbarEvents();
56 StopListeningForScrollAreaEvents();
57 UnregisterFromRefreshDriver();
58 CancelFadeBeginTimer();
62 ScrollbarActivity::ActivityOccurred()
69 ScrollbarActivity::ActivityStarted()
71 mNestedActivityCounter
++;
72 CancelFadeBeginTimer();
73 if (!SetIsFading(false)) {
76 UnregisterFromRefreshDriver();
77 StartListeningForScrollbarEvents();
78 StartListeningForScrollAreaEvents();
81 NS_ASSERTION(mIsActive
, "need to be active during activity");
82 NS_ASSERTION(!mIsFading
, "must not be fading during activity");
86 ScrollbarActivity::ActivityStopped()
88 NS_ASSERTION(IsActivityOngoing(), "activity stopped while none was going on");
89 NS_ASSERTION(mIsActive
, "need to be active during activity");
90 NS_ASSERTION(!mIsFading
, "must not be fading during ongoing activity");
92 mNestedActivityCounter
--;
94 if (!IsActivityOngoing()) {
95 StartFadeBeginTimer();
97 NS_ASSERTION(mIsActive
, "need to be active right after activity");
98 NS_ASSERTION(!mIsFading
, "must not be fading right after activity");
103 ScrollbarActivity::HandleEvent(nsIDOMEvent
* aEvent
)
105 if (!mDisplayOnMouseMove
&& !mIsActive
)
109 aEvent
->GetType(type
);
111 if (type
.EqualsLiteral("mousemove")) {
112 // Mouse motions anywhere in the scrollable frame should keep the
113 // scrollbars visible.
118 nsCOMPtr
<nsIDOMEventTarget
> target
;
119 aEvent
->GetOriginalTarget(getter_AddRefs(target
));
120 nsCOMPtr
<nsIContent
> targetContent
= do_QueryInterface(target
);
122 HandleEventForScrollbar(type
, targetContent
, GetHorizontalScrollbar(),
123 &mHScrollbarHovered
);
124 HandleEventForScrollbar(type
, targetContent
, GetVerticalScrollbar(),
125 &mVScrollbarHovered
);
131 ScrollbarActivity::WillRefresh(TimeStamp aTime
)
133 NS_ASSERTION(mIsActive
, "should only fade while scrollbars are visible");
134 NS_ASSERTION(!IsActivityOngoing(), "why weren't we unregistered from the refresh driver when scrollbar activity started?");
135 NS_ASSERTION(mIsFading
, "should only animate fading during fade");
137 if (!UpdateOpacity(aTime
)) {
141 if (!IsStillFading(aTime
)) {
147 ScrollbarActivity::IsStillFading(TimeStamp aTime
)
149 return !mFadeBeginTime
.IsNull() && (aTime
- mFadeBeginTime
< FadeDuration());
153 ScrollbarActivity::HandleEventForScrollbar(const nsAString
& aType
,
155 nsIContent
* aScrollbar
,
156 bool* aStoredHoverState
)
158 if (!aTarget
|| !aScrollbar
||
159 !nsContentUtils::ContentIsDescendantOf(aTarget
, aScrollbar
))
162 if (aType
.EqualsLiteral("mousedown")) {
164 } else if (aType
.EqualsLiteral("mouseup")) {
166 } else if (aType
.EqualsLiteral("mouseover") ||
167 aType
.EqualsLiteral("mouseout")) {
168 bool newHoveredState
= aType
.EqualsLiteral("mouseover");
169 if (newHoveredState
&& !*aStoredHoverState
) {
171 HoveredScrollbar(aScrollbar
);
172 } else if (*aStoredHoverState
&& !newHoveredState
) {
174 // Don't call HoveredScrollbar(nullptr) here because we want the hover
175 // attribute to stick until the scrollbars are hidden.
177 *aStoredHoverState
= newHoveredState
;
182 ScrollbarActivity::StartListeningForScrollbarEvents()
184 if (mListeningForScrollbarEvents
)
187 mHorizontalScrollbar
= do_QueryInterface(GetHorizontalScrollbar());
188 mVerticalScrollbar
= do_QueryInterface(GetVerticalScrollbar());
190 AddScrollbarEventListeners(mHorizontalScrollbar
);
191 AddScrollbarEventListeners(mVerticalScrollbar
);
193 mListeningForScrollbarEvents
= true;
197 ScrollbarActivity::StopListeningForScrollbarEvents()
199 if (!mListeningForScrollbarEvents
)
202 RemoveScrollbarEventListeners(mHorizontalScrollbar
);
203 RemoveScrollbarEventListeners(mVerticalScrollbar
);
205 mHorizontalScrollbar
= nullptr;
206 mVerticalScrollbar
= nullptr;
207 mListeningForScrollbarEvents
= false;
211 ScrollbarActivity::StartListeningForScrollAreaEvents()
213 if (mListeningForScrollAreaEvents
)
216 nsIFrame
* scrollArea
= do_QueryFrame(mScrollableFrame
);
217 nsCOMPtr
<nsIDOMEventTarget
> scrollAreaTarget
218 = do_QueryInterface(scrollArea
->GetContent());
219 if (scrollAreaTarget
) {
220 scrollAreaTarget
->AddEventListener(NS_LITERAL_STRING("mousemove"), this,
223 mListeningForScrollAreaEvents
= true;
227 ScrollbarActivity::StopListeningForScrollAreaEvents()
229 if (!mListeningForScrollAreaEvents
)
232 nsIFrame
* scrollArea
= do_QueryFrame(mScrollableFrame
);
233 nsCOMPtr
<nsIDOMEventTarget
> scrollAreaTarget
= do_QueryInterface(scrollArea
->GetContent());
234 if (scrollAreaTarget
) {
235 scrollAreaTarget
->RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, true);
237 mListeningForScrollAreaEvents
= false;
241 ScrollbarActivity::AddScrollbarEventListeners(nsIDOMEventTarget
* aScrollbar
)
244 aScrollbar
->AddEventListener(NS_LITERAL_STRING("mousedown"), this, true);
245 aScrollbar
->AddEventListener(NS_LITERAL_STRING("mouseup"), this, true);
246 aScrollbar
->AddEventListener(NS_LITERAL_STRING("mouseover"), this, true);
247 aScrollbar
->AddEventListener(NS_LITERAL_STRING("mouseout"), this, true);
252 ScrollbarActivity::RemoveScrollbarEventListeners(nsIDOMEventTarget
* aScrollbar
)
255 aScrollbar
->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true);
256 aScrollbar
->RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, true);
257 aScrollbar
->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, true);
258 aScrollbar
->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, true);
263 ScrollbarActivity::BeginFade()
265 NS_ASSERTION(mIsActive
, "can't begin fade when we're already inactive");
266 NS_ASSERTION(!IsActivityOngoing(), "why wasn't the fade begin timer cancelled when scrollbar activity started?");
267 NS_ASSERTION(!mIsFading
, "shouldn't be fading just yet");
269 CancelFadeBeginTimer();
270 mFadeBeginTime
= TimeStamp::Now();
271 if (!SetIsFading(true)) {
274 RegisterWithRefreshDriver();
276 NS_ASSERTION(mIsActive
, "only fade while scrollbars are visible");
277 NS_ASSERTION(mIsFading
, "should be fading now");
281 ScrollbarActivity::EndFade()
283 NS_ASSERTION(mIsActive
, "still need to be active at this point");
284 NS_ASSERTION(!IsActivityOngoing(), "why wasn't the fade end timer cancelled when scrollbar activity started?");
286 if (!SetIsFading(false)) {
290 UnregisterFromRefreshDriver();
291 StopListeningForScrollbarEvents();
292 if (!mDisplayOnMouseMove
) {
293 StopListeningForScrollAreaEvents();
296 NS_ASSERTION(!mIsActive
, "should have gone inactive after fade end");
297 NS_ASSERTION(!mIsFading
, "shouldn't be fading anymore");
301 ScrollbarActivity::RegisterWithRefreshDriver()
303 nsRefreshDriver
* refreshDriver
= GetRefreshDriver();
305 refreshDriver
->AddRefreshObserver(this, Flush_Style
);
310 ScrollbarActivity::UnregisterFromRefreshDriver()
312 nsRefreshDriver
* refreshDriver
= GetRefreshDriver();
314 refreshDriver
->RemoveRefreshObserver(this, Flush_Style
);
319 SetBooleanAttribute(nsIContent
* aContent
, nsIAtom
* aAttribute
, bool aValue
)
323 aContent
->SetAttr(kNameSpaceID_None
, aAttribute
,
324 NS_LITERAL_STRING("true"), true);
326 aContent
->UnsetAttr(kNameSpaceID_None
, aAttribute
, true);
332 ScrollbarActivity::SetIsActive(bool aNewActive
)
334 if (mIsActive
== aNewActive
)
337 mIsActive
= aNewActive
;
339 // Clear sticky scrollbar hover status.
340 HoveredScrollbar(nullptr);
343 SetBooleanAttribute(GetHorizontalScrollbar(), nsGkAtoms::active
, mIsActive
);
344 SetBooleanAttribute(GetVerticalScrollbar(), nsGkAtoms::active
, mIsActive
);
348 SetOpacityOnElement(nsIContent
* aContent
, double aOpacity
)
350 nsCOMPtr
<nsIDOMElementCSSInlineStyle
> inlineStyleContent
=
351 do_QueryInterface(aContent
);
352 if (inlineStyleContent
) {
353 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> decl
;
354 inlineStyleContent
->GetStyle(getter_AddRefs(decl
));
357 str
.AppendFloat(aOpacity
);
358 decl
->SetProperty(NS_LITERAL_STRING("opacity"), str
, EmptyString());
364 ScrollbarActivity::UpdateOpacity(TimeStamp aTime
)
366 double progress
= (aTime
- mFadeBeginTime
) / FadeDuration();
367 double opacity
= 1.0 - std::max(0.0, std::min(1.0, progress
));
369 // 'this' may be getting destroyed during SetOpacityOnElement calls.
370 nsWeakFrame
weakFrame((do_QueryFrame(mScrollableFrame
)));
371 SetOpacityOnElement(GetHorizontalScrollbar(), opacity
);
372 if (!weakFrame
.IsAlive()) {
375 SetOpacityOnElement(GetVerticalScrollbar(), opacity
);
376 if (!weakFrame
.IsAlive()) {
383 UnsetOpacityOnElement(nsIContent
* aContent
)
385 nsCOMPtr
<nsIDOMElementCSSInlineStyle
> inlineStyleContent
=
386 do_QueryInterface(aContent
);
387 if (inlineStyleContent
) {
388 nsCOMPtr
<nsIDOMCSSStyleDeclaration
> decl
;
389 inlineStyleContent
->GetStyle(getter_AddRefs(decl
));
392 decl
->RemoveProperty(NS_LITERAL_STRING("opacity"), dummy
);
398 ScrollbarActivity::SetIsFading(bool aNewFading
)
400 if (mIsFading
== aNewFading
)
403 mIsFading
= aNewFading
;
405 mFadeBeginTime
= TimeStamp();
406 // 'this' may be getting destroyed during UnsetOpacityOnElement calls.
407 nsWeakFrame
weakFrame((do_QueryFrame(mScrollableFrame
)));
408 UnsetOpacityOnElement(GetHorizontalScrollbar());
409 if (!weakFrame
.IsAlive()) {
412 UnsetOpacityOnElement(GetVerticalScrollbar());
413 if (!weakFrame
.IsAlive()) {
421 ScrollbarActivity::StartFadeBeginTimer()
423 if (GetForceAlwaysVisiblePref()) {
426 if (!mFadeBeginTimer
) {
427 mFadeBeginTimer
= do_CreateInstance("@mozilla.org/timer;1");
429 mFadeBeginTimer
->InitWithFuncCallback(FadeBeginTimerFired
, this,
430 mScrollbarFadeBeginDelay
,
431 nsITimer::TYPE_ONE_SHOT
);
435 ScrollbarActivity::CancelFadeBeginTimer()
437 if (mFadeBeginTimer
) {
438 mFadeBeginTimer
->Cancel();
443 ScrollbarActivity::HoveredScrollbar(nsIContent
* aScrollbar
)
445 SetBooleanAttribute(GetHorizontalScrollbar(), nsGkAtoms::hover
, false);
446 SetBooleanAttribute(GetVerticalScrollbar(), nsGkAtoms::hover
, false);
447 SetBooleanAttribute(aScrollbar
, nsGkAtoms::hover
, true);
451 ScrollbarActivity::GetRefreshDriver()
453 nsIFrame
* scrollableFrame
= do_QueryFrame(mScrollableFrame
);
454 return scrollableFrame
->PresContext()->RefreshDriver();
458 ScrollbarActivity::GetScrollbarContent(bool aVertical
)
460 nsIFrame
* box
= mScrollableFrame
->GetScrollbarBox(aVertical
);
461 return box
? box
->GetContent() : nullptr;
464 } // namespace layout
465 } // namespace mozilla