Bug 1328658 - Notify main thread of a failed attempt to start an APZ scrollbar drag. r=kats,tnikkel

MozReview-Commit-ID: ERm1sVNfoKL

--HG--
extra : rebase_source : 0b57987a2a15533fad577dd2d363ec386f6bfeae
This commit is contained in:
Botond Ballo 2017-01-27 18:02:22 -05:00
Родитель d034427bd2
Коммит a98321305e
20 изменённых файлов: 183 добавлений и 27 удалений

Просмотреть файл

@ -157,6 +157,8 @@ public:
*/
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) {}

Просмотреть файл

@ -53,10 +53,12 @@ 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)
@ -67,7 +69,7 @@ struct APZCTreeManager::TreeBuildingState {
}
// 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;
@ -226,13 +228,13 @@ APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
// 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,
@ -439,6 +441,7 @@ APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (!apzc) {
NotifyScrollbarDragRejected(aGuid);
return;
}
@ -446,6 +449,14 @@ APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
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,
@ -462,7 +473,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
needsApzc = false;
}
const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
if (!(state && state->mController.get())) {
needsApzc = false;
}
@ -690,7 +701,7 @@ APZCTreeManager::FlushApzRepaints(uint64_t aLayersId)
// 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(

Просмотреть файл

@ -477,6 +477,8 @@ private:
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.

Просмотреть файл

@ -91,6 +91,7 @@ public:
}
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 {

Просмотреть файл

@ -912,6 +912,15 @@ APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
|| 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,

Просмотреть файл

@ -164,6 +164,8 @@ public:
/* 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

Просмотреть файл

@ -274,3 +274,16 @@ ChromeProcessController::NotifyFlushComplete()
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);
}

Просмотреть файл

@ -63,6 +63,7 @@ public:
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;

Просмотреть файл

@ -85,6 +85,12 @@ ContentProcessController::NotifyFlushComplete()
}
}
void
ContentProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
{
APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
}
void
ContentProcessController::PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs)
{

Просмотреть файл

@ -62,6 +62,8 @@ public:
void NotifyFlushComplete() override;
void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) override;
bool IsRepaintThread() override;

Просмотреть файл

@ -85,6 +85,13 @@ APZChild::RecvNotifyFlushComplete()
return IPC_OK();
}
mozilla::ipc::IPCResult
APZChild::RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId)
{
mController->NotifyAsyncScrollbarDragRejected(aScrollId);
return IPC_OK();
}
mozilla::ipc::IPCResult
APZChild::RecvDestroy()
{

Просмотреть файл

@ -42,6 +42,8 @@ public:
mozilla::ipc::IPCResult RecvNotifyFlushComplete() override;
mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId) override;
mozilla::ipc::IPCResult RecvDestroy() override;
private:

Просмотреть файл

@ -64,6 +64,8 @@ child:
async NotifyFlushComplete();
async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
async Destroy();
};

Просмотреть файл

@ -244,6 +244,21 @@ RemoteContentController::NotifyFlushComplete()
}
}
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)
{

Просмотреть файл

@ -74,6 +74,8 @@ public:
virtual void NotifyFlushComplete() override;
virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual void Destroy() override;

Просмотреть файл

@ -62,6 +62,7 @@
#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>
@ -6215,3 +6216,30 @@ ScrollFrameHelper::DragScroll(WidgetEvent* aEvent)
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);
}

Просмотреть файл

@ -461,6 +461,8 @@ public:
bool DragScroll(WidgetEvent* aEvent);
void AsyncScrollbarDragRejected();
// owning references to the nsIAnonymousContentCreator-built content
nsCOMPtr<nsIContent> mHScrollbarContent;
nsCOMPtr<nsIContent> mVScrollbarContent;
@ -1039,6 +1041,10 @@ public:
return mHelper.DragScroll(aEvent);
}
virtual void AsyncScrollbarDragRejected() override {
return mHelper.AsyncScrollbarDragRejected();
}
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
@ -1469,6 +1475,10 @@ public:
return mHelper.DragScroll(aEvent);
}
virtual void AsyncScrollbarDragRejected() override {
return mHelper.AsyncScrollbarDragRejected();
}
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif

Просмотреть файл

@ -481,6 +481,8 @@ public:
* caller should look for an ancestor to scroll.
*/
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0;
virtual void AsyncScrollbarDragRejected() = 0;
};
#endif

Просмотреть файл

@ -994,36 +994,36 @@ ScrollFrameWillBuildScrollInfoLayer(nsIFrame* aScrollFrame)
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;
@ -1031,7 +1031,7 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
bool hasAPZView = hasID && (scrollTargetId != layers::FrameMetrics::NULL_SCROLL_ID);
if (!hasAPZView) {
return false;
return;
}
nsIFrame* scrollbarBox = GetScrollbar();
@ -1056,9 +1056,15 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
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();
@ -1070,7 +1076,6 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
if (!waitForRefresh) {
widget->StartAsyncScrollbarDrag(dragMetrics);
}
return true;
}
nsresult
@ -1140,16 +1145,15 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aEvent)
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;
@ -1163,11 +1167,7 @@ nsSliderFrame::StopDrag()
mScrollingWithAPZ = false;
if (mSuppressionActive) {
MOZ_ASSERT(PresContext()->PresShell());
APZCCallbackHelper::SuppressDisplayport(false, PresContext()->PresShell());
mSuppressionActive = false;
}
UnsuppressDisplayport();
#ifdef MOZ_WIDGET_GTK
nsIFrame* thumbFrame = mFrames.FirstChild();
@ -1539,5 +1539,36 @@ nsSliderFrame::GetThumbRatio() const
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)

Просмотреть файл

@ -100,7 +100,7 @@ public:
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);
@ -137,6 +137,11 @@ public:
// 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();
@ -158,6 +163,9 @@ private:
void RemoveListener();
bool isDraggingThumb();
void SuppressDisplayport();
void UnsuppressDisplayport();
void StartRepeat() {
nsRepeatService::GetInstance()->Start(Notify, this);
}