mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-02 17:28:50 +02:00
Bug 1958965 - Change scroll event setup to match the spec better. r=smaug
This matches the spec (plus resolution at https://github.com/w3c/csswg-drafts/issues/11164) better, by running the events per document. Same comments as D244655 regarding the delayed event stuff that's going away. Differential Revision: https://phabricator.services.mozilla.com/D244666
This commit is contained in:
parent
e0f0653a49
commit
abf15a9165
9 changed files with 57 additions and 106 deletions
|
|
@ -1424,7 +1424,6 @@ Document::Document(const char* aContentType)
|
|||
mParserAborted(false),
|
||||
mReportedDocumentUseCounters(false),
|
||||
mHasReportedShadowDOMUsage(false),
|
||||
mHasDelayedRefreshEvent(false),
|
||||
mLoadEventFiring(false),
|
||||
mSkipLoadEventAfterClose(false),
|
||||
mDisableCookieAccess(false),
|
||||
|
|
@ -13219,19 +13218,6 @@ void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) {
|
|||
for (net::ChannelEventQueue* queue : queues) {
|
||||
queue->Resume();
|
||||
}
|
||||
|
||||
// If there have been any events driven by the refresh driver which were
|
||||
// delayed due to events being suppressed in this document, make sure
|
||||
// there is a refresh scheduled soon so the events will run.
|
||||
if (doc->mHasDelayedRefreshEvent) {
|
||||
doc->mHasDelayedRefreshEvent = false;
|
||||
|
||||
if (doc->mPresShell) {
|
||||
nsRefreshDriver* rd =
|
||||
doc->mPresShell->GetPresContext()->RefreshDriver();
|
||||
rd->RunDelayedEventsSoon();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2810,8 +2810,6 @@ class Document : public nsINode,
|
|||
*/
|
||||
void FireOrClearPostMessageEvents(bool aFireEvents);
|
||||
|
||||
void SetHasDelayedRefreshEvent() { mHasDelayedRefreshEvent = true; }
|
||||
|
||||
/**
|
||||
* Flag whether we're about to fire the window's load event for this document.
|
||||
*/
|
||||
|
|
@ -4920,10 +4918,6 @@ class Document : public nsINode,
|
|||
|
||||
bool mHasReportedShadowDOMUsage : 1;
|
||||
|
||||
// Whether an event triggered by the refresh driver was delayed because this
|
||||
// document has suppressed events.
|
||||
bool mHasDelayedRefreshEvent : 1;
|
||||
|
||||
// The HTML spec has a "iframe load in progress" flag, but that doesn't seem
|
||||
// to have the right semantics. See
|
||||
// <https://github.com/whatwg/html/issues/4292>. What we have instead is a
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ VisualViewport::VisualViewportScrollEvent::VisualViewportScrollEvent(
|
|||
mPrevLayoutOffset(aPrevLayoutOffset) {
|
||||
VVP_LOG("%p: Registering PostScroll on %p %p\n", aViewport, aPresContext,
|
||||
aPresContext->RefreshDriver());
|
||||
aPresContext->RefreshDriver()->PostScrollEvent(this);
|
||||
aPresContext->PresShell()->PostScrollEvent(this);
|
||||
}
|
||||
|
||||
bool VisualViewport::VisualViewportScrollEvent::HasPresContext(
|
||||
|
|
|
|||
|
|
@ -1997,6 +1997,16 @@ bool PresShell::CanHandleUserInputEvents(WidgetGUIEvent* aGUIEvent) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void PresShell::PostScrollEvent(Runnable* aEvent) {
|
||||
MOZ_ASSERT(aEvent);
|
||||
const bool hadEvents = !mPendingScrollEvents.IsEmpty();
|
||||
mPendingScrollEvents.AppendElement(aEvent);
|
||||
if (!hadEvents) {
|
||||
mPresContext->RefreshDriver()->ScheduleRenderingPhase(
|
||||
RenderingPhase::ScrollSteps);
|
||||
}
|
||||
}
|
||||
|
||||
void PresShell::ScheduleResizeEventIfNeeded(ResizeEventKind aKind) {
|
||||
if (mIsDestroying) {
|
||||
return;
|
||||
|
|
@ -2011,7 +2021,7 @@ void PresShell::ScheduleResizeEventIfNeeded(ResizeEventKind aKind) {
|
|||
mVisualViewportResizeEventPending = true;
|
||||
}
|
||||
mPresContext->RefreshDriver()->ScheduleRenderingPhase(
|
||||
mozilla::RenderingPhase::ResizeSteps);
|
||||
RenderingPhase::ResizeSteps);
|
||||
}
|
||||
|
||||
bool PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
|
||||
|
|
@ -2165,6 +2175,19 @@ void PresShell::RunResizeSteps() {
|
|||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom-view/#document-run-the-scroll-steps
|
||||
// But note: https://github.com/w3c/csswg-drafts/issues/11164
|
||||
void PresShell::RunScrollSteps() {
|
||||
// Scroll events are one-shot, so after running them we can drop them.
|
||||
// However, dispatching a scroll event can potentially cause more scroll
|
||||
// events to be posted, so we move the initial set into a temporary array
|
||||
// first. (Newly posted scroll events will be dispatched on the next tick.)
|
||||
auto events = std::move(mPendingScrollEvents);
|
||||
for (auto& event : events) {
|
||||
event->Run();
|
||||
}
|
||||
}
|
||||
|
||||
static nsIContent* GetNativeAnonymousSubtreeRoot(nsIContent* aContent) {
|
||||
if (!aContent) {
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -361,6 +361,8 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
enum class ResizeEventKind : uint8_t { Regular, Visual };
|
||||
void ScheduleResizeEventIfNeeded(ResizeEventKind = ResizeEventKind::Regular);
|
||||
|
||||
void PostScrollEvent(mozilla::Runnable*);
|
||||
|
||||
/**
|
||||
* Returns true if the document hosted by this presShell is in a devtools
|
||||
* Responsive Design Mode browsing context.
|
||||
|
|
@ -1175,6 +1177,7 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
bool HasHandledUserInput() const { return mHasHandledUserInput; }
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void RunResizeSteps();
|
||||
MOZ_CAN_RUN_SCRIPT void RunScrollSteps();
|
||||
|
||||
void NativeAnonymousContentWillBeRemoved(nsIContent* aAnonContent);
|
||||
|
||||
|
|
@ -3101,6 +3104,8 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
nsTHashSet<ScrollContainerFrame*> mPendingScrollAnchorSelection;
|
||||
nsTHashSet<ScrollContainerFrame*> mPendingScrollAnchorAdjustment;
|
||||
nsTHashSet<ScrollContainerFrame*> mPendingScrollResnap;
|
||||
// Pending list of scroll/scrollend/etc events.
|
||||
nsTArray<RefPtr<Runnable>> mPendingScrollEvents;
|
||||
|
||||
nsTHashSet<nsIContent*> mHiddenContentInForcedLayout;
|
||||
|
||||
|
|
|
|||
|
|
@ -1556,27 +1556,6 @@ bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
|
|||
return true;
|
||||
}
|
||||
|
||||
void nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent,
|
||||
bool aDelayed) {
|
||||
if (aDelayed) {
|
||||
mDelayedScrollEvents.AppendElement(aScrollEvent);
|
||||
} else {
|
||||
mScrollEvents.AppendElement(aScrollEvent);
|
||||
ScheduleRenderingPhase(RenderingPhase::ScrollSteps);
|
||||
}
|
||||
}
|
||||
|
||||
void nsRefreshDriver::DispatchScrollEvents() {
|
||||
// Scroll events are one-shot, so after running them we can drop them.
|
||||
// However, dispatching a scroll event can potentially cause more scroll
|
||||
// events to be posted, so we move the initial set into a temporary array
|
||||
// first. (Newly posted scroll events will be dispatched on the next tick.)
|
||||
ScrollEventArray events = std::move(mScrollEvents);
|
||||
for (auto& event : events) {
|
||||
event->Run();
|
||||
}
|
||||
}
|
||||
|
||||
void nsRefreshDriver::AddPostRefreshObserver(
|
||||
nsAPostRefreshObserver* aObserver) {
|
||||
MOZ_ASSERT(!mPostRefreshObservers.Contains(aObserver));
|
||||
|
|
@ -1654,17 +1633,6 @@ void nsRefreshDriver::FlushForceNotifyContentfulPaintPresContext() {
|
|||
}
|
||||
}
|
||||
|
||||
void nsRefreshDriver::RunDelayedEventsSoon() {
|
||||
// Place entries for delayed events into their corresponding normal list,
|
||||
// and schedule a refresh. When these delayed events run, if their document
|
||||
// still has events suppressed then they will be readded to the delayed
|
||||
// events list.
|
||||
|
||||
mScrollEvents.AppendElements(mDelayedScrollEvents);
|
||||
mDelayedScrollEvents.Clear();
|
||||
ScheduleRenderingPhase(RenderingPhase::ScrollSteps);
|
||||
}
|
||||
|
||||
bool nsRefreshDriver::CanDoCatchUpTick() {
|
||||
if (mTestControllingRefreshes || !mActiveTimer) {
|
||||
return false;
|
||||
|
|
@ -2584,9 +2552,12 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
|
|||
});
|
||||
|
||||
// Step 9. For each doc of docs, run the scroll steps for doc.
|
||||
RunRenderingPhaseLegacy(
|
||||
RenderingPhase::ScrollSteps,
|
||||
[&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { DispatchScrollEvents(); });
|
||||
RunRenderingPhase(RenderingPhase::ScrollSteps,
|
||||
[](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
|
||||
if (RefPtr<PresShell> ps = aDoc.GetPresShell()) {
|
||||
ps->RunScrollSteps();
|
||||
}
|
||||
});
|
||||
|
||||
// Step 10. For each doc of docs, evaluate media queries and report changes
|
||||
// for doc.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/dom/VisualViewport.h"
|
||||
#include "mozilla/layers/TransactionIdAllocator.h"
|
||||
#include "LayersTypes.h"
|
||||
|
||||
|
|
@ -46,14 +45,17 @@ class PresShell;
|
|||
class RefreshDriverTimer;
|
||||
class Runnable;
|
||||
class Task;
|
||||
|
||||
namespace dom {
|
||||
class Document;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||
public nsARefreshObserver {
|
||||
using Document = mozilla::dom::Document;
|
||||
using TransactionId = mozilla::layers::TransactionId;
|
||||
using VVPScrollEvent =
|
||||
mozilla::dom::VisualViewport::VisualViewportScrollEvent;
|
||||
using LogPresShellObserver = mozilla::LogPresShellObserver;
|
||||
|
||||
public:
|
||||
|
|
@ -105,8 +107,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
mozilla::FlushType aFlushType);
|
||||
|
||||
|
||||
void PostScrollEvent(mozilla::Runnable* aScrollEvent, bool aDelayed = false);
|
||||
void DispatchScrollEvents();
|
||||
MOZ_CAN_RUN_SCRIPT void FlushLayoutOnPendingDocsAndFixUpFocus();
|
||||
|
||||
/**
|
||||
|
|
@ -341,9 +341,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
static void DispatchIdleTaskAfterTickUnlessExists(mozilla::Task* aTask);
|
||||
static void CancelIdleTask(mozilla::Task* aTask);
|
||||
|
||||
// Schedule a refresh so that any delayed events will run soon.
|
||||
void RunDelayedEventsSoon();
|
||||
|
||||
void InitializeTimer() {
|
||||
MOZ_ASSERT(!mActiveTimer);
|
||||
EnsureTimerStarted();
|
||||
|
|
@ -420,7 +417,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
void FinishedVsyncTick() { mAttemptedExtraTickSinceLastVsync = false; }
|
||||
|
||||
private:
|
||||
using ScrollEventArray = nsTArray<RefPtr<mozilla::Runnable>>;
|
||||
using RequestTable = nsTHashSet<RefPtr<imgIRequest>>;
|
||||
struct ImageStartData {
|
||||
ImageStartData() = default;
|
||||
|
|
@ -450,10 +446,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
MOZ_CAN_RUN_SCRIPT
|
||||
void RunVideoAndFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void RunVideoFrameCallbacks(const nsTArray<RefPtr<mozilla::dom::Document>>&,
|
||||
void RunVideoFrameCallbacks(const nsTArray<RefPtr<Document>>&,
|
||||
mozilla::TimeStamp aNowTime);
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void RunFrameRequestCallbacks(const nsTArray<RefPtr<mozilla::dom::Document>>&,
|
||||
void RunFrameRequestCallbacks(const nsTArray<RefPtr<Document>>&,
|
||||
mozilla::TimeStamp aNowTime);
|
||||
void UpdateRemoteFrameEffects();
|
||||
void UpdateRelevancyOfContentVisibilityAutoFrames();
|
||||
|
|
@ -645,9 +641,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||
RequestTable mRequests;
|
||||
ImageStartTable mStartTable;
|
||||
AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
|
||||
ScrollEventArray mScrollEvents;
|
||||
// Scroll events on documents that might have events suppressed.
|
||||
ScrollEventArray mDelayedScrollEvents;
|
||||
AutoTArray<mozilla::PresShell*, 16> mStyleFlushObservers;
|
||||
nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
|
||||
nsTArray<mozilla::UniquePtr<mozilla::PendingFullscreenEvent>>
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ static ScrollDirections GetOverflowChange(const nsRect& aCurScrolledRect,
|
|||
class ScrollContainerFrame::ScrollEvent : public Runnable {
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
explicit ScrollEvent(ScrollContainerFrame* aHelper, bool aDelayed);
|
||||
explicit ScrollEvent(ScrollContainerFrame* aHelper);
|
||||
void Revoke() { mHelper = nullptr; }
|
||||
|
||||
private:
|
||||
|
|
@ -169,7 +169,7 @@ class ScrollContainerFrame::ScrollEvent : public Runnable {
|
|||
class ScrollContainerFrame::ScrollEndEvent : public Runnable {
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
explicit ScrollEndEvent(ScrollContainerFrame* aHelper, bool aDelayed);
|
||||
explicit ScrollEndEvent(ScrollContainerFrame* aHelper);
|
||||
void Revoke() { mHelper = nullptr; }
|
||||
|
||||
private:
|
||||
|
|
@ -5382,30 +5382,20 @@ nsresult ScrollContainerFrame::FireScrollPortEvent() {
|
|||
return EventDispatcher::Dispatch(content, presContext, &event);
|
||||
}
|
||||
|
||||
void ScrollContainerFrame::PostScrollEndEvent(bool aDelayed) {
|
||||
void ScrollContainerFrame::PostScrollEndEvent() {
|
||||
if (mScrollEndEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The ScrollEndEvent constructor registers itself with the refresh driver.
|
||||
mScrollEndEvent = new ScrollEndEvent(this, aDelayed);
|
||||
// The ScrollEndEvent constructor registers itself.
|
||||
mScrollEndEvent = new ScrollEndEvent(this);
|
||||
}
|
||||
|
||||
void ScrollContainerFrame::FireScrollEndEvent() {
|
||||
RefPtr<nsIContent> content = GetContent();
|
||||
MOZ_ASSERT(content);
|
||||
|
||||
MOZ_ASSERT(mScrollEndEvent);
|
||||
mScrollEndEvent->Revoke();
|
||||
mScrollEndEvent = nullptr;
|
||||
|
||||
if (content->GetComposedDoc() &&
|
||||
content->GetComposedDoc()->EventHandlingSuppressed()) {
|
||||
content->GetComposedDoc()->SetHasDelayedRefreshEvent();
|
||||
PostScrollEndEvent(/* aDelayed = */ true);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<nsPresContext> presContext = PresContext();
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
WidgetGUIEvent event(true, eScrollend, nullptr);
|
||||
|
|
@ -5825,10 +5815,9 @@ void ScrollContainerFrame::CurPosAttributeChangedInternal(nsIContent* aContent,
|
|||
|
||||
/* ============= Scroll events ========== */
|
||||
|
||||
ScrollContainerFrame::ScrollEvent::ScrollEvent(ScrollContainerFrame* aHelper,
|
||||
bool aDelayed)
|
||||
ScrollContainerFrame::ScrollEvent::ScrollEvent(ScrollContainerFrame* aHelper)
|
||||
: Runnable("ScrollContainerFrame::ScrollEvent"), mHelper(aHelper) {
|
||||
mHelper->PresContext()->RefreshDriver()->PostScrollEvent(this, aDelayed);
|
||||
mHelper->PresShell()->PostScrollEvent(this);
|
||||
}
|
||||
|
||||
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
|
||||
|
|
@ -5841,9 +5830,9 @@ ScrollContainerFrame::ScrollEvent::Run() {
|
|||
}
|
||||
|
||||
ScrollContainerFrame::ScrollEndEvent::ScrollEndEvent(
|
||||
ScrollContainerFrame* aHelper, bool aDelayed)
|
||||
ScrollContainerFrame* aHelper)
|
||||
: Runnable("ScrollContainerFrame::ScrollEndEvent"), mHelper(aHelper) {
|
||||
mHelper->PresContext()->RefreshDriver()->PostScrollEvent(this, aDelayed);
|
||||
mHelper->PresShell()->PostScrollEvent(this);
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
|
||||
|
|
@ -5864,16 +5853,6 @@ void ScrollContainerFrame::FireScrollEvent() {
|
|||
mScrollEvent->Revoke();
|
||||
mScrollEvent = nullptr;
|
||||
|
||||
// If event handling is suppressed, keep posting the scroll event to the
|
||||
// refresh driver until it is unsuppressed. The event is marked as delayed so
|
||||
// that the refresh driver does not continue ticking.
|
||||
if (content->GetComposedDoc() &&
|
||||
content->GetComposedDoc()->EventHandlingSuppressed()) {
|
||||
content->GetComposedDoc()->SetHasDelayedRefreshEvent();
|
||||
PostScrollEvent(/* aDelayed = */ true);
|
||||
return;
|
||||
}
|
||||
|
||||
bool oldProcessing = mProcessingScrollEvent;
|
||||
AutoWeakFrame weakFrame(this);
|
||||
auto RestoreProcessingScrollEvent = mozilla::MakeScopeExit([&] {
|
||||
|
|
@ -5903,13 +5882,13 @@ void ScrollContainerFrame::FireScrollEvent() {
|
|||
}
|
||||
}
|
||||
|
||||
void ScrollContainerFrame::PostScrollEvent(bool aDelayed) {
|
||||
void ScrollContainerFrame::PostScrollEvent() {
|
||||
if (mScrollEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The ScrollEvent constructor registers itself with the refresh driver.
|
||||
mScrollEvent = new ScrollEvent(this, aDelayed);
|
||||
// The ScrollEvent constructor registers itself.
|
||||
mScrollEvent = new ScrollEvent(this);
|
||||
}
|
||||
|
||||
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
|
||||
|
|
|
|||
|
|
@ -1083,7 +1083,7 @@ class ScrollContainerFrame : public nsContainerFrame,
|
|||
PhysicalAxes GetOverflowAxes() const;
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT nsresult FireScrollPortEvent();
|
||||
void PostScrollEndEvent(bool aDelayed = false);
|
||||
void PostScrollEndEvent();
|
||||
MOZ_CAN_RUN_SCRIPT void FireScrollEndEvent();
|
||||
void PostOverflowEvent();
|
||||
|
||||
|
|
@ -1111,7 +1111,7 @@ class ScrollContainerFrame : public nsContainerFrame,
|
|||
*/
|
||||
void CurPosAttributeChangedInternal(nsIContent*, bool aDoScroll = true);
|
||||
|
||||
void PostScrollEvent(bool aDelayed = false);
|
||||
void PostScrollEvent();
|
||||
MOZ_CAN_RUN_SCRIPT void FireScrollEvent();
|
||||
void PostScrolledAreaEvent();
|
||||
MOZ_CAN_RUN_SCRIPT void FireScrolledAreaEvent();
|
||||
|
|
|
|||
Loading…
Reference in a new issue