зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1478776 - Part 7: Tune scroll events to only fire when the *relative* offset changes. r=botond
Internally, Gecko stores and updates the *absolute* offset between the visual viewport and the page, however the spec demands that the scroll event be fired whenever the *relative* offset between visual and layout viewport changes. Differential Revision: https://phabricator.services.mozilla.com/D14044 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
6011bbb22c
Коммит
29e0e9331a
|
@ -7,6 +7,7 @@
|
|||
#include "VisualViewport.h"
|
||||
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/ToString.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsPresContext.h"
|
||||
|
@ -175,23 +176,27 @@ void VisualViewport::FireResizeEvent() {
|
|||
|
||||
/* ================= Scroll event handling ================= */
|
||||
|
||||
void VisualViewport::PostScrollEvent() {
|
||||
VVP_LOG("%p: PostScrollEvent\n", this);
|
||||
void VisualViewport::PostScrollEvent(const nsPoint& aPrevRelativeOffset) {
|
||||
VVP_LOG("%p: PostScrollEvent, prevRelativeOffset %s\n", this,
|
||||
ToString(aPrevRelativeOffset).c_str());
|
||||
if (mScrollEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The event constructor will register itself with the refresh driver.
|
||||
if (nsPresContext* presContext = GetPresContext()) {
|
||||
mScrollEvent = new VisualViewportScrollEvent(this, presContext);
|
||||
mScrollEvent =
|
||||
new VisualViewportScrollEvent(this, presContext, aPrevRelativeOffset);
|
||||
VVP_LOG("%p: PostScrollEvent, created new event\n", this);
|
||||
}
|
||||
}
|
||||
|
||||
VisualViewport::VisualViewportScrollEvent::VisualViewportScrollEvent(
|
||||
VisualViewport* aViewport, nsPresContext* aPresContext)
|
||||
VisualViewport* aViewport, nsPresContext* aPresContext,
|
||||
const nsPoint& aPrevRelativeOffset)
|
||||
: Runnable("VisualViewport::VisualViewportScrollEvent"),
|
||||
mViewport(aViewport) {
|
||||
mViewport(aViewport),
|
||||
mPrevRelativeOffset(aPrevRelativeOffset) {
|
||||
aPresContext->RefreshDriver()->PostVisualViewportScrollEvent(this);
|
||||
}
|
||||
|
||||
|
@ -205,12 +210,28 @@ VisualViewport::VisualViewportScrollEvent::Run() {
|
|||
|
||||
void VisualViewport::FireScrollEvent() {
|
||||
MOZ_ASSERT(mScrollEvent);
|
||||
nsPoint prevRelativeOffset = mScrollEvent->PrevRelativeOffset();
|
||||
mScrollEvent->Revoke();
|
||||
mScrollEvent = nullptr;
|
||||
|
||||
VVP_LOG("%p, FireScrollEvent, fire VisualViewport scroll\n", this);
|
||||
WidgetGUIEvent event(true, eScroll, nullptr);
|
||||
event.mFlags.mBubbles = false;
|
||||
event.mFlags.mCancelable = false;
|
||||
EventDispatcher::Dispatch(this, GetPresContext(), &event);
|
||||
nsIPresShell* presShell = GetPresShell();
|
||||
// Check whether the relative visual viewport offset actually changed - maybe
|
||||
// both visual and layout viewport scrolled together and there was no change
|
||||
// after all.
|
||||
if (presShell) {
|
||||
nsPoint curRelativeOffset =
|
||||
presShell->GetVisualViewportOffsetRelativeToLayoutViewport();
|
||||
VVP_LOG(
|
||||
"%p: FireScrollEvent, curRelativeOffset %s, "
|
||||
"prevRelativeOffset %s\n",
|
||||
this, ToString(curRelativeOffset).c_str(),
|
||||
ToString(prevRelativeOffset).c_str());
|
||||
if (curRelativeOffset != prevRelativeOffset) {
|
||||
VVP_LOG("%p, FireScrollEvent, fire VisualViewport scroll\n", this);
|
||||
WidgetGUIEvent event(true, eScroll, nullptr);
|
||||
event.mFlags.mBubbles = false;
|
||||
event.mFlags.mCancelable = false;
|
||||
EventDispatcher::Dispatch(this, GetPresContext(), &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class VisualViewport final : public mozilla::DOMEventTargetHelper {
|
|||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void PostResizeEvent();
|
||||
void PostScrollEvent();
|
||||
void PostScrollEvent(const nsPoint& aPrevRelativeOffset);
|
||||
|
||||
// These two events are modelled after the ScrollEvent class in
|
||||
// nsGfxScrollFrame.h.
|
||||
|
@ -54,11 +54,23 @@ class VisualViewport final : public mozilla::DOMEventTargetHelper {
|
|||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
VisualViewportScrollEvent(VisualViewport* aViewport,
|
||||
nsPresContext* aPresContext);
|
||||
nsPresContext* aPresContext,
|
||||
const nsPoint& aPrevRelativeOffset);
|
||||
void Revoke() { mViewport = nullptr; }
|
||||
nsPoint PrevRelativeOffset() const { return mPrevRelativeOffset; }
|
||||
|
||||
private:
|
||||
VisualViewport* mViewport;
|
||||
// The VisualViewport "scroll" event is supposed to be fired only when the
|
||||
// *relative* offset between visual and layout viewport changes. The two
|
||||
// viewports are updated independently from each other, though, so the only
|
||||
// thing we can do is note the fact that one of the inputs into the relative
|
||||
// visual viewport offset changed and then check the offset again at the
|
||||
// next refresh driver tick, just before the event is going to fire.
|
||||
// Hopefully, at this point both visual and layout viewport positions have
|
||||
// been updated, so that we're able to tell whether the relative offset did
|
||||
// in fact change or not.
|
||||
const nsPoint mPrevRelativeOffset;
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
@ -55,7 +55,7 @@ function* test(testDriver) {
|
|||
// a constant zero and therefore not cause any visual viewport scroll events
|
||||
// to fire.
|
||||
visScrEvt.unregister();
|
||||
todo_is(visScrEvt.count, 0, "Got no visual viewport scroll events");
|
||||
is(visScrEvt.count, 0, "Got no visual viewport scroll events");
|
||||
visScrEvtInternal.unregister();
|
||||
// Our internal visual viewport scroll event on the other hand only cares
|
||||
// about the absolute offset of the visual viewport and should therefore
|
||||
|
|
|
@ -175,7 +175,8 @@ static ScreenMargin ScrollFrame(nsIContent* aContent,
|
|||
if (sf->IsRootScrollFrameOfDocument()) {
|
||||
if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) {
|
||||
shell->SetVisualViewportOffset(
|
||||
CSSPoint::ToAppUnits(aRequest.GetScrollOffset()));
|
||||
CSSPoint::ToAppUnits(aRequest.GetScrollOffset()),
|
||||
shell->GetVisualViewportOffsetRelativeToLayoutViewport());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10042,12 +10042,13 @@ void nsIPresShell::SetVisualViewportSize(nscoord aWidth, nscoord aHeight) {
|
|||
}
|
||||
}
|
||||
|
||||
void nsIPresShell::SetVisualViewportOffset(const nsPoint& aScrollOffset) {
|
||||
void nsIPresShell::SetVisualViewportOffset(const nsPoint& aScrollOffset,
|
||||
const nsPoint& aPrevRelativeOffset) {
|
||||
if (mVisualViewportOffset != aScrollOffset) {
|
||||
mVisualViewportOffset = aScrollOffset;
|
||||
|
||||
if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
|
||||
window->VisualViewport()->PostScrollEvent();
|
||||
window->VisualViewport()->PostScrollEvent(aPrevRelativeOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1647,7 +1647,8 @@ class nsIPresShell : public nsStubDocumentObserver {
|
|||
return mVisualViewportSize;
|
||||
}
|
||||
|
||||
void SetVisualViewportOffset(const nsPoint& aScrollOffset);
|
||||
void SetVisualViewportOffset(const nsPoint& aScrollOffset,
|
||||
const nsPoint& aPrevRelativeOffset);
|
||||
|
||||
nsPoint GetVisualViewportOffset() const { return mVisualViewportOffset; }
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "mozilla/layers/LayerTransactionChild.h"
|
||||
#include "mozilla/layers/ScrollLinkedEffectDetector.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "VisualViewport.h"
|
||||
#include "LayersLogging.h" // for Stringify
|
||||
#include <algorithm>
|
||||
#include <cstdlib> // for std::abs(int/long)
|
||||
|
@ -2675,6 +2676,9 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange,
|
|||
if (dist.x >= horzAllowance || dist.y >= vertAllowance) {
|
||||
needFrameVisibilityUpdate = true;
|
||||
}
|
||||
nsPoint prevVVRelativeOffset =
|
||||
presContext->PresShell()
|
||||
->GetVisualViewportOffsetRelativeToLayoutViewport();
|
||||
|
||||
// notify the listeners.
|
||||
for (uint32_t i = 0; i < mListeners.Length(); i++) {
|
||||
|
@ -2736,7 +2740,7 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange,
|
|||
// offset (e.g. by using nsIDOMWindowUtils.getVisualViewportOffset()
|
||||
// in chrome JS code) before it's updated by the next APZ repaint,
|
||||
// we could get incorrect results.
|
||||
presContext->PresShell()->SetVisualViewportOffset(pt);
|
||||
presContext->PresShell()->SetVisualViewportOffset(pt, prevVVRelativeOffset);
|
||||
}
|
||||
|
||||
ScrollVisual();
|
||||
|
@ -2856,6 +2860,15 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange,
|
|||
nsPresContext::InteractionType::eScrollInteraction, TimeStamp::Now());
|
||||
|
||||
PostScrollEvent();
|
||||
// If this is a viewport scroll, this could affect the relative offset
|
||||
// between layout and visual viewport, so we might have to fire a visual
|
||||
// viewport scroll event as well.
|
||||
if (mIsRoot) {
|
||||
if (auto* window = nsGlobalWindowInner::Cast(
|
||||
mOuter->PresContext()->Document()->GetInnerWindow())) {
|
||||
window->VisualViewport()->PostScrollEvent(prevVVRelativeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// notify the listeners.
|
||||
for (uint32_t i = 0; i < mListeners.Length(); i++) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче