Bug 1340415 - Ensure main-thread and async autoscrolling do not happen at the same time. r=kats

The two happening at the same time can lead to the APZ autoscroll being
cancelled due to APZ receiving a main-thread scroll offset update.

To achieve this:

  - The content process assumes APZ is handling the autoscroll until
    told otherwise.

  - If the parent process knows APZ won't handle an autoscroll, it
    tells the content process via its response to the Autoscroll:Start
    message. This covers all cases where APZ doesn't handle the
    autoscroll, except the case where APZCTreeManager itself rejects
    the autoscroll and it lives in the compositor process rather than
    the parent process.

  - If APZCTreeManager rejects an autoscroll and it lives in the
    compositor process, it sends an 'autoscroll-rejected-by-apz' message
    to the content process.

MozReview-Commit-ID: L62v4COai6W
This commit is contained in:
Botond Ballo
2017-10-18 18:18:13 -04:00
parent 624d0b96d4
commit 473f8337a1
26 changed files with 102 additions and 69 deletions

View File

@@ -83,9 +83,13 @@ interface nsITabParent : nsISupports
* (aAnchorX, aAnchorY) are the coordinates of the autoscroll anchor,
* in CSS coordinates relative to the screen. aScrollId and
* aPresShellId identify the scroll frame that content chose to scroll.
* Returns whether we were successfully able to notify APZ.
* If this function returns true, APZ (which may live in another process)
* may still reject the autoscroll, but it's then APZ's reponsibility
* to notify content via an "autoscroll-rejected-by-apz" message.
*/
void startApzAutoscroll(in float aAnchorX, in float aAnchorY,
in nsViewID aScrollId, in uint32_t aPresShellId);
boolean startApzAutoscroll(in float aAnchorX, in float aAnchorY,
in nsViewID aScrollId, in uint32_t aPresShellId);
/**
* Notify APZ to stop autoscrolling.

View File

@@ -3468,12 +3468,15 @@ TabParent::StartPersistence(uint64_t aOuterWindowID,
NS_IMETHODIMP
TabParent::StartApzAutoscroll(float aAnchorX, float aAnchorY,
nsViewID aScrollId, uint32_t aPresShellId)
nsViewID aScrollId, uint32_t aPresShellId,
bool* aOutRetval)
{
if (!AsyncPanZoomEnabled()) {
*aOutRetval = false;
return NS_OK;
}
bool success = false;
if (RenderFrameParent* renderFrame = GetRenderFrame()) {
uint64_t layersId = renderFrame->GetLayersId();
if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
@@ -3486,11 +3489,12 @@ TabParent::StartApzAutoscroll(float aAnchorX, float aAnchorY,
LayoutDeviceIntPoint anchor = RoundedToInt(anchorCss * widget->GetDefaultScale());
anchor -= widget->WidgetToScreenOffset();
widget->StartAsyncAutoscroll(
success = widget->StartAsyncAutoscroll(
ViewAs<ScreenPixel>(anchor, PixelCastJustification::LayoutDeviceIsScreenForBounds),
guid);
}
}
*aOutRetval = success;
return NS_OK;
}

View File

@@ -156,8 +156,8 @@ public:
virtual void NotifyFlushComplete() = 0;
virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) = 0;
virtual void NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId) = 0;
virtual void NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId) = 0;
virtual void CancelAutoscroll(const ScrollableLayerGuid& aGuid) = 0;
virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {}

View File

@@ -176,7 +176,7 @@ public:
const ScrollableLayerGuid& aGuid,
const AsyncDragMetrics& aDragMetrics) = 0;
virtual void StartAutoscroll(
virtual bool StartAutoscroll(
const ScrollableLayerGuid& aGuid,
const ScreenPoint& aAnchorLocation) = 0;

View File

@@ -660,7 +660,6 @@ void
APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
const AsyncDragMetrics& aDragMetrics)
{
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (!apzc) {
NotifyScrollbarDragRejected(aGuid);
@@ -671,13 +670,24 @@ APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
}
void
bool
APZCTreeManager::StartAutoscroll(const ScrollableLayerGuid& aGuid,
const ScreenPoint& aAnchorLocation)
{
if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) {
apzc->StartAutoscroll(aAnchorLocation);
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (!apzc) {
if (XRE_IsGPUProcess()) {
// If we're in the compositor process, the "return false" will be
// ignored because the query comes over the PAPZCTreeManager protocol
// via an async message. In this case, send an explicit rejection
// message to content.
NotifyAutoscrollRejected(aGuid);
}
return false;
}
apzc->StartAutoscroll(aAnchorLocation);
return true;
}
void
@@ -696,6 +706,14 @@ APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) c
state->mController->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
}
void
APZCTreeManager::NotifyAutoscrollRejected(const ScrollableLayerGuid& aGuid) const
{
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aGuid.mLayersId);
MOZ_ASSERT(state && state->mController);
state->mController->NotifyAsyncAutoscrollRejected(aGuid.mScrollId);
}
template<class ScrollNode> HitTestingTreeNode*
APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
const FrameMetrics& aMetrics,

View File

@@ -433,7 +433,7 @@ public:
const ScrollableLayerGuid& aGuid,
const AsyncDragMetrics& aDragMetrics) override;
void StartAutoscroll(const ScrollableLayerGuid& aGuid,
bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
const ScreenPoint& aAnchorLocation) override;
void StopAutoscroll(const ScrollableLayerGuid& aGuid) override;
@@ -605,6 +605,7 @@ private:
const AsyncPanZoomController* apzc);
void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const;
void NotifyAutoscrollRejected(const ScrollableLayerGuid& aGuid) const;
// Requires the caller to hold mTreeLock.
LayerToParentLayerMatrix4x4 ComputeTransformForNode(const HitTestingTreeNode* aNode) const;

View File

@@ -1120,11 +1120,6 @@ void AsyncPanZoomController::StartAutoscroll(const ScreenPoint& aPoint)
SetState(AUTOSCROLL);
StartAnimation(new AutoscrollAnimation(*this, aPoint));
// Notify content that we are handlng the autoscroll.
if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
controller->NotifyAutoscrollHandledByAPZ(mFrameMetrics.GetScrollId());
}
}
void AsyncPanZoomController::StopAutoscroll()

View File

@@ -93,7 +93,7 @@ public:
MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
MOCK_METHOD0(NotifyFlushComplete, void());
MOCK_METHOD1(NotifyAsyncScrollbarDragRejected, void(const FrameMetrics::ViewID&));
MOCK_METHOD1(NotifyAutoscrollHandledByAPZ, void(const FrameMetrics::ViewID&));
MOCK_METHOD1(NotifyAsyncAutoscrollRejected, void(const FrameMetrics::ViewID&));
MOCK_METHOD1(CancelAutoscroll, void(const ScrollableLayerGuid&));
};

View File

@@ -965,7 +965,7 @@ APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID&
}
/* static */ void
APZCCallbackHelper::NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId)
APZCCallbackHelper::NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
@@ -973,7 +973,7 @@ APZCCallbackHelper::NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aSc
nsAutoString data;
data.AppendInt(aScrollId);
observerService->NotifyObservers(nullptr, "autoscroll-handled-by-apz", data.get());
observerService->NotifyObservers(nullptr, "autoscroll-rejected-by-apz", data.get());
}
/* static */ void

View File

@@ -165,8 +165,7 @@ public:
static void NotifyFlushComplete(nsIPresShell* aShell);
static void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId);
static void NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId);
static void NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId);
static void CancelAutoscroll(const FrameMetrics::ViewID& aScrollId);

View File

@@ -310,18 +310,18 @@ ChromeProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::Vi
}
void
ChromeProcessController::NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId)
ChromeProcessController::NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId)
{
if (MessageLoop::current() != mUILoop) {
mUILoop->PostTask(NewRunnableMethod<FrameMetrics::ViewID>(
"layers::ChromeProcessController::NotifyAutoscrollHandledByAPZ",
"layers::ChromeProcessController::NotifyAsyncAutoscrollRejected",
this,
&ChromeProcessController::NotifyAutoscrollHandledByAPZ,
&ChromeProcessController::NotifyAsyncAutoscrollRejected,
aScrollId));
return;
}
APZCCallbackHelper::NotifyAutoscrollHandledByAPZ(aScrollId);
APZCCallbackHelper::NotifyAsyncAutoscrollRejected(aScrollId);
}
void

View File

@@ -64,7 +64,7 @@ public:
const nsString& aEvent) override;
virtual void NotifyFlushComplete() override;
virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
virtual void NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId) override;
virtual void NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId) override;
virtual void CancelAutoscroll(const ScrollableLayerGuid& aGuid) override;
private:
nsCOMPtr<nsIWidget> mWidget;

View File

@@ -93,9 +93,9 @@ ContentProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::V
}
void
ContentProcessController::NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId)
ContentProcessController::NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId)
{
APZCCallbackHelper::NotifyAutoscrollHandledByAPZ(aScrollId);
APZCCallbackHelper::NotifyAsyncAutoscrollRejected(aScrollId);
}
void

View File

@@ -64,7 +64,7 @@ public:
void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
void NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId) override;
void NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId) override;
void CancelAutoscroll(const ScrollableLayerGuid& aGuid) override;

View File

@@ -201,12 +201,12 @@ APZCTreeManagerChild::StartScrollbarDrag(
SendStartScrollbarDrag(aGuid, aDragMetrics);
}
void
bool
APZCTreeManagerChild::StartAutoscroll(
const ScrollableLayerGuid& aGuid,
const ScreenPoint& aAnchorLocation)
{
SendStartAutoscroll(aGuid, aAnchorLocation);
return SendStartAutoscroll(aGuid, aAnchorLocation);
}
void

View File

@@ -67,7 +67,7 @@ public:
const ScrollableLayerGuid& aGuid,
const AsyncDragMetrics& aDragMetrics) override;
void
bool
StartAutoscroll(
const ScrollableLayerGuid& aGuid,
const ScreenPoint& aAnchorLocation) override;

View File

@@ -86,9 +86,9 @@ APZChild::RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId)
}
mozilla::ipc::IPCResult
APZChild::RecvNotifyAutoscrollHandledByAPZ(const ViewID& aScrollId)
APZChild::RecvNotifyAsyncAutoscrollRejected(const ViewID& aScrollId)
{
mController->NotifyAutoscrollHandledByAPZ(aScrollId);
mController->NotifyAsyncAutoscrollRejected(aScrollId);
return IPC_OK();
}

View File

@@ -42,7 +42,7 @@ public:
mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId) override;
mozilla::ipc::IPCResult RecvNotifyAutoscrollHandledByAPZ(const ViewID& aScrollId) override;
mozilla::ipc::IPCResult RecvNotifyAsyncAutoscrollRejected(const ViewID& aScrollId) override;
mozilla::ipc::IPCResult RecvDestroy() override;

View File

@@ -65,7 +65,7 @@ child:
async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
async NotifyAutoscrollHandledByAPZ(ViewID aScrollId);
async NotifyAsyncAutoscrollRejected(ViewID aScrollId);
async Destroy();
};

View File

@@ -273,20 +273,20 @@ RemoteContentController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::Vi
}
void
RemoteContentController::NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId)
RemoteContentController::NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId)
{
if (MessageLoop::current() != mCompositorThread) {
// We have to send messages from the compositor thread
mCompositorThread->PostTask(NewRunnableMethod<FrameMetrics::ViewID>(
"layers::RemoteContentController::NotifyAutoscrollHandledByAPZ",
"layers::RemoteContentController::NotifyAsyncAutoscrollRejected",
this,
&RemoteContentController::NotifyAutoscrollHandledByAPZ,
&RemoteContentController::NotifyAsyncAutoscrollRejected,
aScrollId));
return;
}
if (mCanSend) {
Unused << SendNotifyAutoscrollHandledByAPZ(aScrollId);
Unused << SendNotifyAsyncAutoscrollRejected(aScrollId);
}
}

View File

@@ -74,7 +74,7 @@ public:
virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
virtual void NotifyAutoscrollHandledByAPZ(const FrameMetrics::ViewID& aScrollId) override;
virtual void NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId) override;
virtual void CancelAutoscroll(const ScrollableLayerGuid& aScrollId) override;

View File

@@ -159,20 +159,19 @@ var ClickEventHandler = {
// No view ID - leave this._scrollId as null. Receiving side will check.
}
let presShellId = domUtils.getPresShellId();
let [enabled] = sendSyncMessage("Autoscroll:Start",
{scrolldir: this._scrolldir,
screenX: event.screenX,
screenY: event.screenY,
scrollId: this._scrollId,
presShellId});
if (!enabled) {
let [result] = sendSyncMessage("Autoscroll:Start",
{scrolldir: this._scrolldir,
screenX: event.screenX,
screenY: event.screenY,
scrollId: this._scrollId,
presShellId});
if (!result.autoscrollEnabled) {
this._scrollable = null;
return;
}
Services.els.addSystemEventListener(global, "mousemove", this, true);
addEventListener("pagehide", this, true);
Services.obs.addObserver(this, "autoscroll-handled-by-apz");
this._ignoreMouseEvents = true;
this._startX = event.screenX;
@@ -181,9 +180,22 @@ var ClickEventHandler = {
this._screenY = event.screenY;
this._scrollErrorX = 0;
this._scrollErrorY = 0;
this._autoscrollHandledByApz = false;
this._lastFrame = content.performance.now();
this._autoscrollHandledByApz = result.usingApz;
if (!result.usingApz) {
// If the browser didn't hand the autoscroll off to APZ,
// scroll here in the main thread.
this.startMainThreadScroll();
} else {
// Even if the browser did hand the autoscroll to APZ,
// APZ might reject it in which case it will notify us
// and we need to take over.
Services.obs.addObserver(this, "autoscroll-rejected-by-apz");
}
},
startMainThreadScroll() {
this._lastFrame = content.performance.now();
content.requestAnimationFrame(this.autoscrollLoop);
},
@@ -194,7 +206,9 @@ var ClickEventHandler = {
Services.els.removeSystemEventListener(global, "mousemove", this, true);
removeEventListener("pagehide", this, true);
Services.obs.removeObserver(this, "autoscroll-handled-by-apz");
if (this._autoscrollHandledByApz) {
Services.obs.removeObserver(this, "autoscroll-rejected-by-apz");
}
}
},
@@ -221,12 +235,6 @@ var ClickEventHandler = {
return;
}
if (this._autoscrollHandledByApz) {
// APZ is handling the autoscroll, so we don't need to keep running
// this callback.
return;
}
// avoid long jumps when the browser hangs for more than
// |maxTimeDelta| ms
const maxTimeDelta = 100;
@@ -297,10 +305,12 @@ var ClickEventHandler = {
},
observe(subject, topic, data) {
if (topic === "autoscroll-handled-by-apz") {
if (topic === "autoscroll-rejected-by-apz") {
// The caller passes in the scroll id via 'data'.
if (data == this._scrollId) {
this._autoscrollHandledByApz = true;
this._autoscrollHandledByApz = false;
this.startMainThreadScroll();
Services.obs.removeObserver(this, "autoscroll-rejected-by-apz");
}
}
},
@@ -1898,4 +1908,3 @@ let ExtFind = {
};
ExtFind.init();

View File

@@ -1101,9 +1101,10 @@
}
case "Autoscroll:Start": {
if (!this.autoscrollEnabled) {
return false;
return {autoscrollEnabled: false, usingApz: false};
}
this.startScroll(data.scrolldir, data.screenX, data.screenY);
let usingApz = false;
if (this.isRemoteBrowser && data.scrollId != null &&
this.mPrefs.getBoolPref("apz.autoscroll.enabled", false)) {
let { tabParent } = this.frameLoader;
@@ -1115,14 +1116,15 @@
.getService(Components.interfaces.nsIObserverService);
os.addObserver(this, "apz:cancel-autoscroll", true);
tabParent.startApzAutoscroll(data.screenX, data.screenY,
data.scrollId, data.presShellId);
usingApz = tabParent.startApzAutoscroll(
data.screenX, data.screenY,
data.scrollId, data.presShellId);
}
// Save the IDs for later
this._autoScrollScrollId = data.scrollId;
this._autoScrollPresShellId = data.presShellId;
}
return true;
return {autoscrollEnabled: true, usingApz};
}
case "Autoscroll:Cancel":
this._autoScrollPopup.hidePopup();

View File

@@ -1930,13 +1930,13 @@ nsBaseWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
aDragMetrics));
}
void
bool
nsBaseWidget::StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
const ScrollableLayerGuid& aGuid)
{
MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
mAPZC->StartAutoscroll(aGuid, aAnchorLocation);
return mAPZC->StartAutoscroll(aGuid, aAnchorLocation);
}
void

View File

@@ -367,7 +367,7 @@ public:
virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override;
virtual void StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
virtual bool StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
const ScrollableLayerGuid& aGuid) override;
virtual void StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) override;

View File

@@ -1692,8 +1692,9 @@ class nsIWidget : public nsISupports
* Notify APZ to start autoscrolling.
* @param aAnchorLocation the location of the autoscroll anchor
* @param aGuid identifies the scroll frame to be autoscrolled
* @return true if APZ has been successfully notified
*/
virtual void StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
virtual bool StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
const ScrollableLayerGuid& aGuid) = 0;
/**