Bug 1326290 - Correct sequencing of layer transaction and StartAsyncScrollbarDrag messages. r=kats

In cases where a mouse click that starts a scrollbar drag is also what
layerizes the scroll frame, the StartAsyncScrollbarDrag message needs to
arrive after the layer transaction. This patch ensures it does.

MozReview-Commit-ID: A02qRb6yWxg

--HG--
extra : rebase_source : 3517e8c8a578a0bd257a80bb8cb81303d171bb6c
This commit is contained in:
Botond Ballo 2017-01-04 13:42:36 -05:00
parent 70f69c9c9a
commit e8cc185f9d
6 changed files with 87 additions and 9 deletions

View File

@ -1595,14 +1595,18 @@ TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
// actually go through the APZ code and so their mHandledByAPZ flag is false.
// Since thos events didn't go through APZ, we don't need to send notifications
// for them.
bool pendingLayerization = false;
if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
nsCOMPtr<nsIDocument> document(GetDocument());
APZCCallbackHelper::SendSetTargetAPZCNotification(
pendingLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
mPuppetWidget, document, aEvent, aGuid, aInputBlockId);
}
nsEventStatus unused;
InputAPZContext context(aGuid, aInputBlockId, unused);
if (pendingLayerization) {
context.SetPendingLayerization();
}
WidgetMouseEvent localEvent(aEvent);
localEvent.mWidget = mPuppetWidget;

View File

@ -746,7 +746,7 @@ SendSetTargetAPZCNotificationHelper(nsIWidget* aWidget,
}
}
void
bool
APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
nsIDocument* aDocument,
const WidgetGUIEvent& aEvent,
@ -754,7 +754,7 @@ APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
uint64_t aInputBlockId)
{
if (!aWidget || !aDocument) {
return;
return false;
}
if (aInputBlockId == sLastTargetAPZCNotificationInputBlock) {
// We have already confirmed the target APZC for a previous event of this
@ -763,7 +763,7 @@ APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
// race the original confirmation (which needs to go through a layers
// transaction).
APZCCH_LOG("Not resending target APZC confirmation for input block %" PRIu64 "\n", aInputBlockId);
return;
return false;
}
sLastTargetAPZCNotificationInputBlock = aInputBlockId;
if (nsIPresShell* shell = aDocument->GetShell()) {
@ -795,8 +795,11 @@ APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
Move(targets),
waitForRefresh);
}
return waitForRefresh;
}
}
return false;
}
void

View File

@ -139,8 +139,12 @@ public:
* sent to the compositor, which will then post a message back to APZ's
* controller thread. Otherwise, the provided widget's SetConfirmedTargetAPZC
* method is invoked immediately.
*
* Returns true if any displayports need to be set. (A caller may be
* interested to know this, because they may need to delay certain actions
* until after the displayport comes into effect.)
*/
static void SendSetTargetAPZCNotification(nsIWidget* aWidget,
static bool SendSetTargetAPZCNotification(nsIWidget* aWidget,
nsIDocument* aDocument,
const WidgetGUIEvent& aEvent,
const ScrollableLayerGuid& aGuid,

View File

@ -12,6 +12,7 @@ ScrollableLayerGuid InputAPZContext::sGuid;
uint64_t InputAPZContext::sBlockId = 0;
nsEventStatus InputAPZContext::sApzResponse = nsEventStatus_eIgnore;
bool InputAPZContext::sRoutedToChildProcess = false;
bool InputAPZContext::sPendingLayerization = false;
/*static*/ ScrollableLayerGuid
InputAPZContext::GetTargetLayerGuid()
@ -37,6 +38,12 @@ InputAPZContext::SetRoutedToChildProcess()
sRoutedToChildProcess = true;
}
/*static*/ void
InputAPZContext::SetPendingLayerization()
{
sPendingLayerization = true;
}
InputAPZContext::InputAPZContext(const ScrollableLayerGuid& aGuid,
const uint64_t& aBlockId,
const nsEventStatus& aApzResponse)
@ -44,11 +51,13 @@ InputAPZContext::InputAPZContext(const ScrollableLayerGuid& aGuid,
, mOldBlockId(sBlockId)
, mOldApzResponse(sApzResponse)
, mOldRoutedToChildProcess(sRoutedToChildProcess)
, mOldPendingLayerization(sPendingLayerization)
{
sGuid = aGuid;
sBlockId = aBlockId;
sApzResponse = aApzResponse;
sRoutedToChildProcess = false;
sPendingLayerization = false;
}
InputAPZContext::~InputAPZContext()
@ -57,13 +66,20 @@ InputAPZContext::~InputAPZContext()
sBlockId = mOldBlockId;
sApzResponse = mOldApzResponse;
sRoutedToChildProcess = mOldRoutedToChildProcess;
sPendingLayerization = mOldPendingLayerization;
}
bool
/*static*/ bool
InputAPZContext::WasRoutedToChildProcess()
{
return sRoutedToChildProcess;
}
/*static*/ bool
InputAPZContext::HavePendingLayerization()
{
return sPendingLayerization;
}
} // namespace layers
} // namespace mozilla

View File

@ -23,25 +23,29 @@ private:
static uint64_t sBlockId;
static nsEventStatus sApzResponse;
static bool sRoutedToChildProcess;
static bool sPendingLayerization;
public:
static ScrollableLayerGuid GetTargetLayerGuid();
static uint64_t GetInputBlockId();
static nsEventStatus GetApzResponse();
static void SetRoutedToChildProcess();
static void SetPendingLayerization();
InputAPZContext(const ScrollableLayerGuid& aGuid,
const uint64_t& aBlockId,
const nsEventStatus& aApzResponse);
~InputAPZContext();
bool WasRoutedToChildProcess();
static bool WasRoutedToChildProcess();
static bool HavePendingLayerization();
private:
ScrollableLayerGuid mOldGuid;
uint64_t mOldBlockId;
nsEventStatus mOldApzResponse;
bool mOldRoutedToChildProcess;
bool mOldPendingLayerization;
};
} // namespace layers

View File

@ -37,6 +37,7 @@
#include "nsContentUtils.h"
#include "nsLayoutUtils.h"
#include "nsDisplayList.h"
#include "nsRefreshDriver.h" // for nsAPostRefreshObserver
#include "mozilla/Assertions.h" // for MOZ_ASSERT
#include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h"
@ -929,6 +930,43 @@ nsSliderMediator::HandleEvent(nsIDOMEvent* aEvent)
return NS_OK;
}
class AsyncScrollbarDragStarter : public nsAPostRefreshObserver {
public:
AsyncScrollbarDragStarter(nsIPresShell* aPresShell,
nsIWidget* aWidget,
const AsyncDragMetrics& aDragMetrics)
: mPresShell(aPresShell)
, mWidget(aWidget)
, mDragMetrics(aDragMetrics)
{
}
virtual ~AsyncScrollbarDragStarter() {}
void DidRefresh() override {
if (!mPresShell) {
MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
return;
}
mWidget->StartAsyncScrollbarDrag(mDragMetrics);
if (!mPresShell->RemovePostRefreshObserver(this)) {
MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
// Graceful handling, just in case...
mPresShell = nullptr;
mWidget = nullptr;
return;
}
delete this;
}
private:
RefPtr<nsIPresShell> mPresShell;
RefPtr<nsIWidget> mWidget;
AsyncDragMetrics mDragMetrics;
};
bool
nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
{
@ -974,8 +1012,9 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
scrollFrameAsScrollable->GetScrollPortRect().TopLeft();
CSSRect sliderTrackCSS = CSSRect::FromAppUnits(sliderTrack);
nsIPresShell* shell = PresContext()->PresShell();
uint64_t inputblockId = InputAPZContext::GetInputBlockId();
uint32_t presShellId = PresContext()->PresShell()->GetPresShellId();
uint32_t presShellId = shell->GetPresShellId();
AsyncDragMetrics dragMetrics(scrollTargetId, presShellId, inputblockId,
NSAppUnitsToFloatPixels(mDragStart,
float(AppUnitsPerCSSPixel())),
@ -989,7 +1028,15 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
// 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.
this->GetNearestWidget()->StartAsyncScrollbarDrag(dragMetrics);
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;
}