Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / ScrollbarActivity.cpp
blob01296cec9ab5bcffa3217b57d770d95a0345ae5a
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"
12 #include "nsIFrame.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"
20 namespace mozilla {
21 namespace layout {
23 NS_IMPL_ISUPPORTS(ScrollbarActivity, nsIDOMEventListener)
25 static bool
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;
38 void
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.
48 mDisplayOnMouseMove =
49 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollbarDisplayOnMouseMove);
52 void
53 ScrollbarActivity::Destroy()
55 StopListeningForScrollbarEvents();
56 StopListeningForScrollAreaEvents();
57 UnregisterFromRefreshDriver();
58 CancelFadeBeginTimer();
61 void
62 ScrollbarActivity::ActivityOccurred()
64 ActivityStarted();
65 ActivityStopped();
68 void
69 ScrollbarActivity::ActivityStarted()
71 mNestedActivityCounter++;
72 CancelFadeBeginTimer();
73 if (!SetIsFading(false)) {
74 return;
76 UnregisterFromRefreshDriver();
77 StartListeningForScrollbarEvents();
78 StartListeningForScrollAreaEvents();
79 SetIsActive(true);
81 NS_ASSERTION(mIsActive, "need to be active during activity");
82 NS_ASSERTION(!mIsFading, "must not be fading during activity");
85 void
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");
102 NS_IMETHODIMP
103 ScrollbarActivity::HandleEvent(nsIDOMEvent* aEvent)
105 if (!mDisplayOnMouseMove && !mIsActive)
106 return NS_OK;
108 nsAutoString type;
109 aEvent->GetType(type);
111 if (type.EqualsLiteral("mousemove")) {
112 // Mouse motions anywhere in the scrollable frame should keep the
113 // scrollbars visible.
114 ActivityOccurred();
115 return NS_OK;
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);
127 return NS_OK;
130 void
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)) {
138 return;
141 if (!IsStillFading(aTime)) {
142 EndFade();
146 bool
147 ScrollbarActivity::IsStillFading(TimeStamp aTime)
149 return !mFadeBeginTime.IsNull() && (aTime - mFadeBeginTime < FadeDuration());
152 void
153 ScrollbarActivity::HandleEventForScrollbar(const nsAString& aType,
154 nsIContent* aTarget,
155 nsIContent* aScrollbar,
156 bool* aStoredHoverState)
158 if (!aTarget || !aScrollbar ||
159 !nsContentUtils::ContentIsDescendantOf(aTarget, aScrollbar))
160 return;
162 if (aType.EqualsLiteral("mousedown")) {
163 ActivityStarted();
164 } else if (aType.EqualsLiteral("mouseup")) {
165 ActivityStopped();
166 } else if (aType.EqualsLiteral("mouseover") ||
167 aType.EqualsLiteral("mouseout")) {
168 bool newHoveredState = aType.EqualsLiteral("mouseover");
169 if (newHoveredState && !*aStoredHoverState) {
170 ActivityStarted();
171 HoveredScrollbar(aScrollbar);
172 } else if (*aStoredHoverState && !newHoveredState) {
173 ActivityStopped();
174 // Don't call HoveredScrollbar(nullptr) here because we want the hover
175 // attribute to stick until the scrollbars are hidden.
177 *aStoredHoverState = newHoveredState;
181 void
182 ScrollbarActivity::StartListeningForScrollbarEvents()
184 if (mListeningForScrollbarEvents)
185 return;
187 mHorizontalScrollbar = do_QueryInterface(GetHorizontalScrollbar());
188 mVerticalScrollbar = do_QueryInterface(GetVerticalScrollbar());
190 AddScrollbarEventListeners(mHorizontalScrollbar);
191 AddScrollbarEventListeners(mVerticalScrollbar);
193 mListeningForScrollbarEvents = true;
196 void
197 ScrollbarActivity::StopListeningForScrollbarEvents()
199 if (!mListeningForScrollbarEvents)
200 return;
202 RemoveScrollbarEventListeners(mHorizontalScrollbar);
203 RemoveScrollbarEventListeners(mVerticalScrollbar);
205 mHorizontalScrollbar = nullptr;
206 mVerticalScrollbar = nullptr;
207 mListeningForScrollbarEvents = false;
210 void
211 ScrollbarActivity::StartListeningForScrollAreaEvents()
213 if (mListeningForScrollAreaEvents)
214 return;
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,
221 true);
223 mListeningForScrollAreaEvents = true;
226 void
227 ScrollbarActivity::StopListeningForScrollAreaEvents()
229 if (!mListeningForScrollAreaEvents)
230 return;
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;
240 void
241 ScrollbarActivity::AddScrollbarEventListeners(nsIDOMEventTarget* aScrollbar)
243 if (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);
251 void
252 ScrollbarActivity::RemoveScrollbarEventListeners(nsIDOMEventTarget* aScrollbar)
254 if (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);
262 void
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)) {
272 return;
274 RegisterWithRefreshDriver();
276 NS_ASSERTION(mIsActive, "only fade while scrollbars are visible");
277 NS_ASSERTION(mIsFading, "should be fading now");
280 void
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)) {
287 return;
289 SetIsActive(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");
300 void
301 ScrollbarActivity::RegisterWithRefreshDriver()
303 nsRefreshDriver* refreshDriver = GetRefreshDriver();
304 if (refreshDriver) {
305 refreshDriver->AddRefreshObserver(this, Flush_Style);
309 void
310 ScrollbarActivity::UnregisterFromRefreshDriver()
312 nsRefreshDriver* refreshDriver = GetRefreshDriver();
313 if (refreshDriver) {
314 refreshDriver->RemoveRefreshObserver(this, Flush_Style);
318 static void
319 SetBooleanAttribute(nsIContent* aContent, nsIAtom* aAttribute, bool aValue)
321 if (aContent) {
322 if (aValue) {
323 aContent->SetAttr(kNameSpaceID_None, aAttribute,
324 NS_LITERAL_STRING("true"), true);
325 } else {
326 aContent->UnsetAttr(kNameSpaceID_None, aAttribute, true);
331 void
332 ScrollbarActivity::SetIsActive(bool aNewActive)
334 if (mIsActive == aNewActive)
335 return;
337 mIsActive = aNewActive;
338 if (!mIsActive) {
339 // Clear sticky scrollbar hover status.
340 HoveredScrollbar(nullptr);
343 SetBooleanAttribute(GetHorizontalScrollbar(), nsGkAtoms::active, mIsActive);
344 SetBooleanAttribute(GetVerticalScrollbar(), nsGkAtoms::active, mIsActive);
347 static void
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));
355 if (decl) {
356 nsAutoString str;
357 str.AppendFloat(aOpacity);
358 decl->SetProperty(NS_LITERAL_STRING("opacity"), str, EmptyString());
363 bool
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()) {
373 return false;
375 SetOpacityOnElement(GetVerticalScrollbar(), opacity);
376 if (!weakFrame.IsAlive()) {
377 return false;
379 return true;
382 static void
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));
390 if (decl) {
391 nsAutoString dummy;
392 decl->RemoveProperty(NS_LITERAL_STRING("opacity"), dummy);
397 bool
398 ScrollbarActivity::SetIsFading(bool aNewFading)
400 if (mIsFading == aNewFading)
401 return true;
403 mIsFading = aNewFading;
404 if (!mIsFading) {
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()) {
410 return false;
412 UnsetOpacityOnElement(GetVerticalScrollbar());
413 if (!weakFrame.IsAlive()) {
414 return false;
417 return true;
420 void
421 ScrollbarActivity::StartFadeBeginTimer()
423 if (GetForceAlwaysVisiblePref()) {
424 return;
426 if (!mFadeBeginTimer) {
427 mFadeBeginTimer = do_CreateInstance("@mozilla.org/timer;1");
429 mFadeBeginTimer->InitWithFuncCallback(FadeBeginTimerFired, this,
430 mScrollbarFadeBeginDelay,
431 nsITimer::TYPE_ONE_SHOT);
434 void
435 ScrollbarActivity::CancelFadeBeginTimer()
437 if (mFadeBeginTimer) {
438 mFadeBeginTimer->Cancel();
442 void
443 ScrollbarActivity::HoveredScrollbar(nsIContent* aScrollbar)
445 SetBooleanAttribute(GetHorizontalScrollbar(), nsGkAtoms::hover, false);
446 SetBooleanAttribute(GetVerticalScrollbar(), nsGkAtoms::hover, false);
447 SetBooleanAttribute(aScrollbar, nsGkAtoms::hover, true);
450 nsRefreshDriver*
451 ScrollbarActivity::GetRefreshDriver()
453 nsIFrame* scrollableFrame = do_QueryFrame(mScrollableFrame);
454 return scrollableFrame->PresContext()->RefreshDriver();
457 nsIContent*
458 ScrollbarActivity::GetScrollbarContent(bool aVertical)
460 nsIFrame* box = mScrollableFrame->GetScrollbarBox(aVertical);
461 return box ? box->GetContent() : nullptr;
464 } // namespace layout
465 } // namespace mozilla