--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -152,16 +152,18 @@ public:
virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
{}
/**
* Notify content that the repaint requests have been flushed.
*/
virtual void NotifyFlushComplete() = 0;
+ virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) = 0;
+
virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {}
virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) {}
virtual void SetScrollingRootContent(bool isRootContent) {}
GeckoContentController() {}
/**
* Needs to be called on the main thread.
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -48,31 +48,33 @@
namespace mozilla {
namespace layers {
typedef mozilla::gfx::Point Point;
typedef mozilla::gfx::Point4D Point4D;
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
+
float APZCTreeManager::sDPI = 160.0;
struct APZCTreeManager::TreeBuildingState {
- TreeBuildingState(const CompositorBridgeParent::LayerTreeState* const aLayerTreeState,
+ TreeBuildingState(const LayerTreeState* const aLayerTreeState,
bool aIsFirstPaint, uint64_t aOriginatingLayersId,
APZTestData* aTestData, uint32_t aPaintSequence)
: mLayerTreeState(aLayerTreeState)
, mIsFirstPaint(aIsFirstPaint)
, mOriginatingLayersId(aOriginatingLayersId)
, mPaintLogger(aTestData, aPaintSequence)
{
}
// State that doesn't change as we recurse in the tree building
- const CompositorBridgeParent::LayerTreeState* const mLayerTreeState;
+ const LayerTreeState* const mLayerTreeState;
const bool mIsFirstPaint;
const uint64_t mOriginatingLayersId;
const APZPaintLogHelper mPaintLogger;
// State that is updated as we perform the tree build
// A list of nodes that need to be destroyed at the end of the tree building.
// This is initialized with all nodes in the old tree, and nodes are removed
@@ -221,23 +223,23 @@ APZCTreeManager::UpdateHitTestingTree(ui
APZThreadUtils::AssertOnCompositorThread();
MutexAutoLock lock(mTreeLock);
// For testing purposes, we log some data to the APZTestData associated with
// the layers id that originated this update.
APZTestData* testData = nullptr;
if (gfxPrefs::APZTestLoggingEnabled()) {
- if (CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
+ if (LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
testData = &state->mApzTestData;
testData->StartNewPaint(aPaintSequenceNumber);
}
}
- const CompositorBridgeParent::LayerTreeState* treeState =
+ const LayerTreeState* treeState =
CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId);
MOZ_ASSERT(treeState);
TreeBuildingState state(treeState, aIsFirstPaint, aOriginatingLayersId,
testData, aPaintSequenceNumber);
// We do this business with collecting the entire tree into an array because otherwise
// it's very hard to determine which APZC instances need to be destroyed. In the worst
// case, there are two scenarios: (a) a layer with an APZC is removed from the layer
@@ -434,40 +436,49 @@ GetEventRegionsOverride(HitTestingTreeNo
void
APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
const AsyncDragMetrics& aDragMetrics)
{
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (!apzc) {
+ NotifyScrollbarDragRejected(aGuid);
return;
}
uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
}
+void
+APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const
+{
+ const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aGuid.mLayersId);
+ MOZ_ASSERT(state && state->mController);
+ state->mController->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
+}
+
HitTestingTreeNode*
APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
const FrameMetrics& aMetrics,
uint64_t aLayersId,
const gfx::Matrix4x4& aAncestorTransform,
HitTestingTreeNode* aParent,
HitTestingTreeNode* aNextSibling,
TreeBuildingState& aState)
{
mTreeLock.AssertCurrentThreadOwns();
bool needsApzc = true;
if (!aMetrics.IsScrollable()) {
needsApzc = false;
}
- const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
if (!(state && state->mController.get())) {
needsApzc = false;
}
RefPtr<HitTestingTreeNode> node = nullptr;
if (!needsApzc) {
node = RecycleOrCreateNode(aState, nullptr, aLayersId);
AttachNodeToTree(node, aParent, aNextSibling);
@@ -685,17 +696,17 @@ WillHandleInput(const PanGestureOrScroll
void
APZCTreeManager::FlushApzRepaints(uint64_t aLayersId)
{
// Previously, paints were throttled and therefore this method was used to
// ensure any pending paints were flushed. Now, paints are flushed
// immediately, so it is safe to simply send a notification now.
APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId);
- const CompositorBridgeParent::LayerTreeState* state =
+ const LayerTreeState* state =
CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
MOZ_ASSERT(state && state->mController);
state->mController->DispatchToRepaintThread(NewRunnableMethod(
state->mController, &GeckoContentController::NotifyFlushComplete));
}
nsEventStatus
APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -472,16 +472,18 @@ private:
const gfx::Matrix4x4& aAncestorTransform,
HitTestingTreeNode* aParent,
HitTestingTreeNode* aNextSibling,
TreeBuildingState& aState);
void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
const AsyncPanZoomController* apzc);
+ void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const;
+
protected:
/* The input queue where input events are held until we know enough to
* figure out where they're going. Protected so gtests can access it.
*/
RefPtr<InputQueue> mInputQueue;
private:
/* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held.
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -86,16 +86,17 @@ public:
bool IsRepaintThread() {
return NS_IsMainThread();
}
void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) {
NS_DispatchToMainThread(Move(aTask));
}
MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
MOCK_METHOD0(NotifyFlushComplete, void());
+ MOCK_METHOD1(NotifyAsyncScrollbarDragRejected, void(const FrameMetrics::ViewID&));
};
class MockContentControllerDelayed : public MockContentController {
public:
MockContentControllerDelayed()
: mTime(GetStartupTime())
{
}
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -908,16 +908,25 @@ APZCCallbackHelper::IsDisplayportSuppres
APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
{
return aFrame->IsProcessingAsyncScroll()
|| nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
|| aFrame->LastSmoothScrollOrigin();
}
/* static */ void
+APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aScrollId)) {
+ scrollFrame->AsyncScrollbarDragRejected();
+ }
+}
+
+/* static */ void
APZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers,
nsIWidget* aWidget)
{
EventMessage msg;
switch (aType) {
case PinchGestureInput::PINCHGESTURE_START:
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -159,16 +159,18 @@ public:
const SetAllowedTouchBehaviorCallback& aCallback);
/* Notify content of a mouse scroll testing event. */
static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent);
/* Notify content that the repaint flush is complete. */
static void NotifyFlushComplete(nsIPresShell* aShell);
+ static void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId);
+
/* Temporarily ignore the Displayport for better paint performance. If at
* all possible, pass in a presShell if you have one at the call site, we
* use it to trigger a repaint once suppression is disabled. Without that
* the displayport may get left at the suppressed size for an extended
* period of time and result in unnecessary checkerboarding (see bug
* 1255054). */
static void SuppressDisplayport(const bool& aEnabled,
const nsCOMPtr<nsIPresShell>& aShell);
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -269,8 +269,21 @@ ChromeProcessController::NotifyMozMouseS
void
ChromeProcessController::NotifyFlushComplete()
{
MOZ_ASSERT(IsRepaintThread());
APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
}
+
+void
+ChromeProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod<FrameMetrics::ViewID>(this,
+ &ChromeProcessController::NotifyAsyncScrollbarDragRejected,
+ aScrollId));
+ return;
+ }
+
+ APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
+}
--- a/gfx/layers/apz/util/ChromeProcessController.h
+++ b/gfx/layers/apz/util/ChromeProcessController.h
@@ -58,16 +58,17 @@ public:
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers) override;
virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange,
int aArg) override;
virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
const nsString& aEvent) override;
virtual void NotifyFlushComplete() override;
+ virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
private:
nsCOMPtr<nsIWidget> mWidget;
RefPtr<APZEventState> mAPZEventState;
RefPtr<IAPZCTreeManager> mAPZCTreeManager;
MessageLoop* mUILoop;
void InitializeRoot();
nsIPresShell* GetPresShell() const;
--- a/gfx/layers/apz/util/ContentProcessController.cpp
+++ b/gfx/layers/apz/util/ContentProcessController.cpp
@@ -81,16 +81,22 @@ ContentProcessController::NotifyFlushCom
if (nsCOMPtr<nsIDocument> doc = mBrowser->GetDocument()) {
shell = doc->GetShell();
}
APZCCallbackHelper::NotifyFlushComplete(shell.get());
}
}
void
+ContentProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
+{
+ APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
+}
+
+void
ContentProcessController::PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs)
{
MOZ_ASSERT_UNREACHABLE("ContentProcessController should only be used remotely.");
}
bool
ContentProcessController::IsRepaintThread()
{
--- a/gfx/layers/apz/util/ContentProcessController.h
+++ b/gfx/layers/apz/util/ContentProcessController.h
@@ -57,16 +57,18 @@ public:
APZStateChange aChange,
int aArg) override;
void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
const nsString& aEvent) override;
void NotifyFlushComplete() override;
+ void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
+
void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) override;
bool IsRepaintThread() override;
void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
private:
RefPtr<dom::TabChild> mBrowser;
--- a/gfx/layers/ipc/APZChild.cpp
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -81,16 +81,23 @@ APZChild::RecvNotifyFlushComplete()
{
MOZ_ASSERT(mController->IsRepaintThread());
mController->NotifyFlushComplete();
return IPC_OK();
}
mozilla::ipc::IPCResult
+APZChild::RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId)
+{
+ mController->NotifyAsyncScrollbarDragRejected(aScrollId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
APZChild::RecvDestroy()
{
// mController->Destroy will be called in the destructor
PAPZChild::Send__delete__(this);
return IPC_OK();
}
--- a/gfx/layers/ipc/APZChild.h
+++ b/gfx/layers/ipc/APZChild.h
@@ -37,16 +37,18 @@ public:
const nsString& aEvent) override;
mozilla::ipc::IPCResult RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
const APZStateChange& aChange,
const int& aArg) override;
mozilla::ipc::IPCResult RecvNotifyFlushComplete() override;
+ mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId) override;
+
mozilla::ipc::IPCResult RecvDestroy() override;
private:
RefPtr<GeckoContentController> mController;
};
} // namespace layers
--- a/gfx/layers/ipc/PAPZ.ipdl
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -59,13 +59,15 @@ child:
async SetScrollingRootContent(bool aIsRootContent);
async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
async NotifyAPZStateChange(ScrollableLayerGuid aGuid, APZStateChange aChange, int aArg);
async NotifyFlushComplete();
+ async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
+
async Destroy();
};
} // layers
} // mozilla
--- a/gfx/layers/ipc/RemoteContentController.cpp
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -240,16 +240,31 @@ RemoteContentController::NotifyFlushComp
MOZ_ASSERT(IsRepaintThread());
if (mCanSend) {
Unused << SendNotifyFlushComplete();
}
}
void
+RemoteContentController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->PostTask(NewRunnableMethod<FrameMetrics::ViewID>(this,
+ &RemoteContentController::NotifyAsyncScrollbarDragRejected, aScrollId));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAsyncScrollbarDragRejected(aScrollId);
+ }
+}
+
+void
RemoteContentController::ActorDestroy(ActorDestroyReason aWhy)
{
// This controller could possibly be kept alive longer after this
// by a RefPtr, but it is no longer valid to send messages.
mCanSend = false;
}
void
--- a/gfx/layers/ipc/RemoteContentController.h
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -69,16 +69,18 @@ public:
virtual void SetScrollingRootContent(bool aIsRootContent) override;
virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
const nsString& aEvent) override;
virtual void NotifyFlushComplete() override;
+ virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
+
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual void Destroy() override;
private:
MessageLoop* mCompositorThread;
bool mCanSend;
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -58,16 +58,17 @@
#include "StickyScrollContainer.h"
#include "nsIFrameInlines.h"
#include "gfxPlatform.h"
#include "gfxPrefs.h"
#include "AsyncScrollBase.h"
#include "ScrollSnap.h"
#include "UnitTransforms.h"
#include "nsPluginFrame.h"
+#include "nsSliderFrame.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include <mozilla/layers/AxisPhysicsModel.h>
#include <mozilla/layers/AxisPhysicsMSDModel.h>
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/ScrollLinkedEffectDetector.h"
#include "mozilla/Unused.h"
#include "LayersLogging.h" // for Stringify
#include <algorithm>
@@ -6239,8 +6240,35 @@ ScrollFrameHelper::DragScroll(WidgetEven
}
if (offset.x || offset.y) {
ScrollTo(GetScrollPosition() + offset, nsIScrollableFrame::NORMAL);
}
return willScroll;
}
+
+static void
+AsyncScrollbarDragRejected(nsIFrame* aScrollbar)
+{
+ if (!aScrollbar) {
+ return;
+ }
+
+ for (nsIFrame::ChildListIterator childLists(aScrollbar);
+ !childLists.IsDone();
+ childLists.Next()) {
+ for (nsIFrame* frame : childLists.CurrentList()) {
+ if (nsSliderFrame* sliderFrame = do_QueryFrame(frame)) {
+ sliderFrame->AsyncScrollbarDragRejected();
+ }
+ }
+ }
+}
+
+void
+ScrollFrameHelper::AsyncScrollbarDragRejected()
+{
+ // We don't get told which scrollbar requested the async drag,
+ // so we notify both.
+ ::AsyncScrollbarDragRejected(mHScrollbarBox);
+ ::AsyncScrollbarDragRejected(mVScrollbarBox);
+}
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -457,16 +457,18 @@ public:
nsIScrollbarMediator::ScrollSnapMode aSnap
= nsIScrollbarMediator::DISABLE_SNAP);
bool ShouldSuppressScrollbarRepaints() const {
return mSuppressScrollbarRepaints;
}
bool DragScroll(WidgetEvent* aEvent);
+ void AsyncScrollbarDragRejected();
+
// owning references to the nsIAnonymousContentCreator-built content
nsCOMPtr<nsIContent> mHScrollbarContent;
nsCOMPtr<nsIContent> mVScrollbarContent;
nsCOMPtr<nsIContent> mScrollCornerContent;
nsCOMPtr<nsIContent> mResizerContent;
RefPtr<ScrollEvent> mScrollEvent;
nsRevocableEventPtr<AsyncScrollPortEvent> mAsyncScrollPortEvent;
@@ -1040,16 +1042,20 @@ public:
ScrollSnapInfo GetScrollSnapInfo() const override {
return mHelper.GetScrollSnapInfo();
}
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
return mHelper.DragScroll(aEvent);
}
+ virtual void AsyncScrollbarDragRejected() override {
+ return mHelper.AsyncScrollbarDragRejected();
+ }
+
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
@@ -1473,16 +1479,20 @@ public:
ScrollSnapInfo GetScrollSnapInfo() const override {
return mHelper.GetScrollSnapInfo();
}
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
return mHelper.DragScroll(aEvent);
}
+ virtual void AsyncScrollbarDragRejected() override {
+ return mHelper.AsyncScrollbarDragRejected();
+ }
+
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
protected:
nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot,
bool aClipAllDescendants);
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -478,11 +478,13 @@ public:
/**
* Given the drag event aEvent, determine whether the mouse is near the edge
* of the scrollable area, and scroll the view in the direction of that edge
* if so. If scrolling occurred, true is returned. When false is returned, the
* caller should look for an ancestor to scroll.
*/
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0;
+
+ virtual void AsyncScrollbarDragRejected() = 0;
};
#endif
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -984,54 +984,54 @@ ScrollFrameWillBuildScrollInfoLayer(nsIF
if (UsesSVGEffects(current)) {
return true;
}
current = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(current);
}
return false;
}
-bool
+void
nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
{
if (!aEvent->mFlags.mHandledByAPZ) {
- return false;
+ return;
}
if (!gfxPlatform::GetPlatform()->SupportsApzDragInput()) {
- return false;
+ return;
}
nsContainerFrame* scrollFrame = GetScrollbar()->GetParent();
if (!scrollFrame) {
- return false;
+ return;
}
nsIContent* scrollableContent = scrollFrame->GetContent();
if (!scrollableContent) {
- return false;
+ return;
}
nsIScrollableFrame* scrollFrameAsScrollable = do_QueryFrame(scrollFrame);
if (!scrollFrameAsScrollable) {
- return false;
+ return;
}
// APZ dragging requires the scrollbar to be layerized, which doesn't
// happen for scroll info layers.
if (ScrollFrameWillBuildScrollInfoLayer(scrollFrame)) {
- return false;
+ return;
}
mozilla::layers::FrameMetrics::ViewID scrollTargetId;
bool hasID = nsLayoutUtils::FindIDFor(scrollableContent, &scrollTargetId);
bool hasAPZView = hasID && (scrollTargetId != layers::FrameMetrics::NULL_SCROLL_ID);
if (!hasAPZView) {
- return false;
+ return;
}
nsIFrame* scrollbarBox = GetScrollbar();
nsCOMPtr<nsIContent> scrollbar = GetContentOfBox(scrollbarBox);
nsRect sliderTrack;
GetXULClientRect(sliderTrack);
@@ -1046,31 +1046,36 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEve
AsyncDragMetrics dragMetrics(scrollTargetId, presShellId, inputblockId,
NSAppUnitsToFloatPixels(mDragStart,
float(AppUnitsPerCSSPixel())),
sliderTrackCSS,
IsXULHorizontal() ? AsyncDragMetrics::HORIZONTAL :
AsyncDragMetrics::VERTICAL);
if (!nsLayoutUtils::HasDisplayPort(scrollableContent)) {
- return false;
+ return;
}
+ // It's important to set this before calling nsIWidget::StartAsyncScrollbarDrag(),
+ // because in some configurations, that can call AsyncScrollbarDragRejected()
+ // synchronously, which clears the flag (and we want it to stay cleared in
+ // that case).
+ mScrollingWithAPZ = true;
+
// When we start an APZ drag, we wont get mouse events for the drag.
// APZ will consume them all and only notify us of the new scroll position.
bool waitForRefresh = InputAPZContext::HavePendingLayerization();
nsIWidget* widget = this->GetNearestWidget();
if (waitForRefresh) {
waitForRefresh = shell->AddPostRefreshObserver(
new AsyncScrollbarDragStarter(shell, widget, dragMetrics));
}
if (!waitForRefresh) {
widget->StartAsyncScrollbarDrag(dragMetrics);
}
- return true;
}
nsresult
nsSliderFrame::StartDrag(nsIDOMEvent* aEvent)
{
#ifdef DEBUG_SLIDER
printf("Begin dragging\n");
#endif
@@ -1130,44 +1135,39 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aE
if (isHorizontal)
mThumbStart = thumbFrame->GetPosition().x;
else
mThumbStart = thumbFrame->GetPosition().y;
mDragStart = pos - mThumbStart;
- mScrollingWithAPZ = StartAPZDrag(event);
+ mScrollingWithAPZ = false;
+ StartAPZDrag(event); // sets mScrollingWithAPZ=true if appropriate
#ifdef DEBUG_SLIDER
printf("Pressed mDragStart=%d\n",mDragStart);
#endif
- if (!mScrollingWithAPZ && !mSuppressionActive) {
- MOZ_ASSERT(PresContext()->PresShell());
- APZCCallbackHelper::SuppressDisplayport(true, PresContext()->PresShell());
- mSuppressionActive = true;
+ if (!mScrollingWithAPZ) {
+ SuppressDisplayport();
}
return NS_OK;
}
nsresult
nsSliderFrame::StopDrag()
{
AddListener();
DragThumb(false);
mScrollingWithAPZ = false;
- if (mSuppressionActive) {
- MOZ_ASSERT(PresContext()->PresShell());
- APZCCallbackHelper::SuppressDisplayport(false, PresContext()->PresShell());
- mSuppressionActive = false;
- }
+ UnsuppressDisplayport();
#ifdef MOZ_WIDGET_GTK
nsIFrame* thumbFrame = mFrames.FirstChild();
if (thumbFrame) {
nsCOMPtr<nsIContent> thumb = thumbFrame->GetContent();
thumb->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, true);
}
#endif
@@ -1529,10 +1529,41 @@ nsSliderFrame::GetThumbRatio() const
{
// mRatio is in thumb app units per scrolled css pixels. Convert it to a
// ratio of the thumb's CSS pixels per scrolled CSS pixels. (Note the thumb
// is in the scrollframe's parent's space whereas the scrolled CSS pixels
// are in the scrollframe's space).
return mRatio / mozilla::AppUnitsPerCSSPixel();
}
+void
+nsSliderFrame::AsyncScrollbarDragRejected()
+{
+ mScrollingWithAPZ = false;
+ // Only suppress the displayport if we're still dragging the thumb.
+ // Otherwise, no one will unsuppress it.
+ if (isDraggingThumb()) {
+ SuppressDisplayport();
+ }
+}
+
+void
+nsSliderFrame::SuppressDisplayport()
+{
+ if (!mSuppressionActive) {
+ MOZ_ASSERT(PresContext()->PresShell());
+ APZCCallbackHelper::SuppressDisplayport(true, PresContext()->PresShell());
+ mSuppressionActive = true;
+ }
+}
+
+void
+nsSliderFrame::UnsuppressDisplayport()
+{
+ if (mSuppressionActive) {
+ MOZ_ASSERT(PresContext()->PresShell());
+ APZCCallbackHelper::SuppressDisplayport(false, PresContext()->PresShell());
+ mSuppressionActive = false;
+ }
+}
+
NS_IMPL_ISUPPORTS(nsSliderMediator,
nsIDOMEventListener)
--- a/layout/xul/nsSliderFrame.h
+++ b/layout/xul/nsSliderFrame.h
@@ -95,17 +95,17 @@ public:
nsIFrame* aPrevFrame,
nsFrameList& aFrameList) override;
virtual void RemoveFrame(ChildListID aListID,
nsIFrame* aOldFrame) override;
nsresult StartDrag(nsIDOMEvent* aEvent);
nsresult StopDrag();
- bool StartAPZDrag(WidgetGUIEvent* aEvent);
+ void StartAPZDrag(mozilla::WidgetGUIEvent* aEvent);
static int32_t GetCurrentPosition(nsIContent* content);
static int32_t GetMinPosition(nsIContent* content);
static int32_t GetMaxPosition(nsIContent* content);
static int32_t GetIncrement(nsIContent* content);
static int32_t GetPageIncrement(nsIContent* content);
static int32_t GetIntegerAttribute(nsIContent* content, nsIAtom* atom, int32_t defaultValue);
void EnsureOrient();
@@ -132,16 +132,21 @@ public:
NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) override;
// Return the ratio the scrollbar thumb should move in proportion to the
// scrolled frame.
float GetThumbRatio() const;
+ // Notify the slider frame than an async scrollbar drag requested in
+ // StartAPZDrag() was rejected by APZ, and the slider frame should
+ // fall back to main-thread dragging.
+ void AsyncScrollbarDragRejected();
+
private:
bool GetScrollToClick();
nsIFrame* GetScrollbar();
bool ShouldScrollForEvent(mozilla::WidgetGUIEvent* aEvent);
bool ShouldScrollToClickForEvent(mozilla::WidgetGUIEvent* aEvent);
bool IsEventOverThumb(mozilla::WidgetGUIEvent* aEvent);
@@ -153,16 +158,19 @@ private:
bool aIsSmooth);
void CurrentPositionChanged();
void DragThumb(bool aGrabMouseEvents);
void AddListener();
void RemoveListener();
bool isDraggingThumb();
+ void SuppressDisplayport();
+ void UnsuppressDisplayport();
+
void StartRepeat() {
nsRepeatService::GetInstance()->Start(Notify, this);
}
void StopRepeat() {
nsRepeatService::GetInstance()->Stop(Notify, this);
}
void Notify();
static void Notify(void* aData) {