From fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:14:29 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- gfx/layers/BuildConstants.h | 7 + gfx/layers/CanvasDrawEventRecorder.cpp | 114 +++++++++- gfx/layers/CanvasDrawEventRecorder.h | 24 ++- gfx/layers/FrameMetrics.cpp | 10 +- gfx/layers/FrameMetrics.h | 12 +- gfx/layers/ImageContainer.cpp | 4 +- gfx/layers/ImageContainer.h | 14 +- gfx/layers/NativeLayerCA.h | 22 +- gfx/layers/NativeLayerCA.mm | 212 ++++++++++++------ gfx/layers/PersistentBufferProvider.cpp | 7 +- gfx/layers/RemoteTextureMap.cpp | 100 +++++---- gfx/layers/RemoteTextureMap.h | 8 +- gfx/layers/SurfacePool.h | 2 +- gfx/layers/SurfacePoolCA.h | 2 +- gfx/layers/SurfacePoolCA.mm | 21 +- gfx/layers/apz/src/APZCTreeManager.cpp | 56 ++++- gfx/layers/apz/src/APZCTreeManager.h | 9 +- gfx/layers/apz/src/APZUpdater.cpp | 30 ++- gfx/layers/apz/src/AsyncPanZoomController.cpp | 46 ++-- gfx/layers/apz/src/AsyncPanZoomController.h | 2 + gfx/layers/apz/src/AutoscrollAnimation.cpp | 2 +- gfx/layers/apz/src/GenericScrollAnimation.cpp | 9 + gfx/layers/apz/src/GestureEventListener.cpp | 87 +++++--- gfx/layers/apz/src/GestureEventListener.h | 3 + gfx/layers/apz/test/gtest/TestGestureDetector.cpp | 236 ++++++++++++++++++++- .../test/mochitest/apz_test_native_event_utils.js | 14 +- gfx/layers/apz/test/mochitest/apz_test_utils.js | 4 +- gfx/layers/apz/test/mochitest/browser.toml | 8 +- .../browser_test_autoscrolling_in_oop_frame.js | 2 +- .../browser_test_content_response_timeout.js | 1 + ...ser_test_scrolling_in_extension_popup_window.js | 2 +- .../apz/test/mochitest/helper_basic_scrollend.html | 2 +- .../test/mochitest/helper_browser_test_utils.js | 2 +- .../apz/test/mochitest/helper_bug1346632.html | 2 +- .../apz/test/mochitest/helper_bug1414336.html | 6 +- .../helper_bug1502010_unconsumed_pan.html | 2 +- ...per_bug1506497_touch_action_fixed_on_fixed.html | 4 +- .../apz/test/mochitest/helper_bug1695598.html | 2 +- .../mochitest/helper_content_response_timeout.html | 2 +- .../test/mochitest/helper_displayport_expiry.html | 2 +- .../helper_fission_event_region_override.html | 2 +- .../mochitest/helper_fission_scroll_oopif.html | 2 +- .../apz/test/mochitest/helper_fullscreen.html | 2 +- .../mochitest/helper_hittest_deep_scene_stack.html | 2 +- .../apz/test/mochitest/helper_hittest_fixed-2.html | 2 +- .../apz/test/mochitest/helper_hittest_fixed-3.html | 4 +- .../apz/test/mochitest/helper_hittest_fixed.html | 2 +- ...helper_hittest_hidden_inactive_scrollframe.html | 2 +- .../helper_hittest_iframe_perspective-2.html | 2 +- .../test/mochitest/helper_hittest_overscroll.html | 2 +- .../helper_hittest_overscroll_contextmenu.html | 4 +- .../helper_hittest_overscroll_subframe.html | 2 +- .../apz/test/mochitest/helper_key_scroll.html | 4 +- ...helper_main_thread_smooth_scroll_scrollend.html | 2 +- .../test/mochitest/helper_minimum_scale_1_0.html | 2 +- .../helper_no_scalable_with_initial_scale.html | 2 +- .../helper_programmatic_scroll_behavior.html | 2 +- .../mochitest/helper_touch_action_regions.html | 2 +- .../apz/test/mochitest/helper_zoomed_pan.html | 4 +- gfx/layers/apz/test/mochitest/test_smoothness.html | 2 +- .../test_touch_listeners_impacting_wheel.html | 4 +- gfx/layers/apz/test/reftest/reftest.list | 18 +- gfx/layers/apz/testutil/APZTestData.h | 6 +- gfx/layers/apz/util/APZCCallbackHelper.cpp | 3 +- gfx/layers/apz/util/APZEventState.cpp | 2 +- gfx/layers/client/TextureClient.cpp | 10 +- gfx/layers/client/TextureRecorded.cpp | 5 +- gfx/layers/client/TextureRecorded.h | 3 +- gfx/layers/ipc/CanvasChild.cpp | 164 +++++++++++--- gfx/layers/ipc/CanvasChild.h | 20 +- gfx/layers/ipc/CanvasTranslator.cpp | 32 ++- gfx/layers/ipc/CanvasTranslator.h | 3 + gfx/layers/ipc/CompositorManagerChild.cpp | 11 +- gfx/layers/ipc/LayersMessageUtils.h | 2 +- gfx/layers/ipc/PCanvas.ipdl | 6 +- gfx/layers/ipc/VideoBridgeParent.cpp | 5 +- gfx/layers/moz.build | 4 +- gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp | 1 - gfx/layers/wr/AsyncImagePipelineManager.cpp | 3 +- gfx/layers/wr/WebRenderBridgeParent.cpp | 10 +- gfx/layers/wr/WebRenderImageHost.cpp | 13 +- gfx/layers/wr/WebRenderScrollData.cpp | 16 ++ gfx/layers/wr/WebRenderScrollData.h | 13 ++ 83 files changed, 1131 insertions(+), 365 deletions(-) (limited to 'gfx/layers') diff --git a/gfx/layers/BuildConstants.h b/gfx/layers/BuildConstants.h index 6db150d136..cebdd35371 100644 --- a/gfx/layers/BuildConstants.h +++ b/gfx/layers/BuildConstants.h @@ -52,6 +52,13 @@ constexpr bool kIsAndroid = false; #endif +constexpr bool kIsDmd = +#ifdef MOZ_DMD + true; +#else + false; +#endif + } // namespace mozilla #endif // BUILD_CONSTANTS_H_ diff --git a/gfx/layers/CanvasDrawEventRecorder.cpp b/gfx/layers/CanvasDrawEventRecorder.cpp index 1bf928b816..af143bf5cb 100644 --- a/gfx/layers/CanvasDrawEventRecorder.cpp +++ b/gfx/layers/CanvasDrawEventRecorder.cpp @@ -8,6 +8,11 @@ #include +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/WorkerRunnable.h" +#include "mozilla/layers/TextureRecorded.h" #include "mozilla/layers/SharedSurfacesChild.h" #include "mozilla/StaticPrefs_gfx.h" #include "RecordedCanvasEventImpl.h" @@ -34,7 +39,9 @@ static Maybe CreateAndMapShmem(size_t aSize) { return Some(ShmemAndHandle{shmem.forget(), std::move(shmemHandle)}); } -CanvasDrawEventRecorder::CanvasDrawEventRecorder() { +CanvasDrawEventRecorder::CanvasDrawEventRecorder( + dom::ThreadSafeWorkerRef* aWorkerRef) + : mWorkerRef(aWorkerRef), mIsOnWorker(!!aWorkerRef) { mDefaultBufferSize = ipc::SharedMemory::PageAlignedSize( StaticPrefs::gfx_canvas_remote_default_buffer_size()); mMaxDefaultBuffers = StaticPrefs::gfx_canvas_remote_max_default_buffers(); @@ -43,7 +50,10 @@ CanvasDrawEventRecorder::CanvasDrawEventRecorder() { mDropBufferOnZero = mDropBufferLimit; } +CanvasDrawEventRecorder::~CanvasDrawEventRecorder() { MOZ_ASSERT(!mWorkerRef); } + bool CanvasDrawEventRecorder::Init(TextureType aTextureType, + TextureType aWebglTextureType, gfx::BackendType aBackendType, UniquePtr aHelpers) { NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder); @@ -95,7 +105,7 @@ bool CanvasDrawEventRecorder::Init(TextureType aTextureType, return false; } - if (!mHelpers->InitTranslator(aTextureType, aBackendType, + if (!mHelpers->InitTranslator(aTextureType, aWebglTextureType, aBackendType, std::move(header->handle), std::move(bufferHandles), mDefaultBufferSize, std::move(readerSem), std::move(writerSem))) { @@ -317,6 +327,106 @@ void CanvasDrawEventRecorder::CheckAndSignalReader() { } while (true); } +void CanvasDrawEventRecorder::DetachResources() { + NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder); + + DrawEventRecorderPrivate::DetachResources(); + + { + auto lockedPendingDeletions = mPendingDeletions.Lock(); + mWorkerRef = nullptr; + } +} + +void CanvasDrawEventRecorder::QueueProcessPendingDeletionsLocked( + RefPtr&& aRecorder) { + if (!mWorkerRef) { + MOZ_RELEASE_ASSERT( + !mIsOnWorker, + "QueueProcessPendingDeletionsLocked called after worker shutdown!"); + + NS_DispatchToMainThread(NS_NewRunnableFunction( + "CanvasDrawEventRecorder::QueueProcessPendingDeletionsLocked", + [self = std::move(aRecorder)]() { self->ProcessPendingDeletions(); })); + return; + } + + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "CanvasDrawEventRecorder::QueueProcessPendingDeletionsLocked", + [self = std::move(aRecorder)]() mutable { + self->QueueProcessPendingDeletions(std::move(self)); + })); + return; + } + + class ProcessPendingRunnable final : public dom::WorkerRunnable { + public: + ProcessPendingRunnable(dom::WorkerPrivate* aWorkerPrivate, + RefPtr&& aRecorder) + : dom::WorkerRunnable(aWorkerPrivate), + mRecorder(std::move(aRecorder)) {} + + bool WorkerRun(JSContext*, dom::WorkerPrivate*) override { + RefPtr recorder = std::move(mRecorder); + recorder->ProcessPendingDeletions(); + return true; + } + + private: + RefPtr mRecorder; + }; + + auto task = MakeRefPtr(mWorkerRef->Private(), + std::move(aRecorder)); + if (NS_WARN_IF(!task->Dispatch())) { + MOZ_CRASH("ProcessPendingRunnable leaked!"); + } +} + +void CanvasDrawEventRecorder::QueueProcessPendingDeletions( + RefPtr&& aRecorder) { + auto lockedPendingDeletions = mPendingDeletions.Lock(); + if (lockedPendingDeletions->empty()) { + // We raced to handle the deletions, and something got there first. + return; + } + + QueueProcessPendingDeletionsLocked(std::move(aRecorder)); +} + +void CanvasDrawEventRecorder::AddPendingDeletion( + std::function&& aPendingDeletion) { + PendingDeletionsVector pendingDeletions; + + { + auto lockedPendingDeletions = mPendingDeletions.Lock(); + bool wasEmpty = lockedPendingDeletions->empty(); + lockedPendingDeletions->emplace_back(std::move(aPendingDeletion)); + + MOZ_RELEASE_ASSERT(!mIsOnWorker || mWorkerRef, + "AddPendingDeletion called after worker shutdown!"); + + // If we are not on the owning thread, we must queue an event to run the + // deletions, if we transitioned from empty to non-empty. + if ((mWorkerRef && !mWorkerRef->Private()->IsOnCurrentThread()) || + (!mWorkerRef && !NS_IsMainThread())) { + if (wasEmpty) { + RefPtr self(this); + QueueProcessPendingDeletionsLocked(std::move(self)); + } + return; + } + + // Otherwise, we can just run all of them right now. + pendingDeletions.swap(*lockedPendingDeletions); + } + + for (const auto& pendingDeletion : pendingDeletions) { + pendingDeletion(); + } +} + void CanvasDrawEventRecorder::StoreSourceSurfaceRecording( gfx::SourceSurface* aSurface, const char* aReason) { NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder); diff --git a/gfx/layers/CanvasDrawEventRecorder.h b/gfx/layers/CanvasDrawEventRecorder.h index a4b1261cfb..c9eacf27ac 100644 --- a/gfx/layers/CanvasDrawEventRecorder.h +++ b/gfx/layers/CanvasDrawEventRecorder.h @@ -21,6 +21,10 @@ namespace mozilla { using EventType = gfx::RecordedEvent::EventType; +namespace dom { +class ThreadSafeWorkerRef; +} + namespace layers { typedef mozilla::ipc::SharedMemoryBasic::Handle Handle; @@ -31,7 +35,8 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CanvasDrawEventRecorder, final) - CanvasDrawEventRecorder(); + explicit CanvasDrawEventRecorder(dom::ThreadSafeWorkerRef* aWorkerRef); + ~CanvasDrawEventRecorder() override; enum class State : uint32_t { Processing, @@ -68,6 +73,7 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, virtual ~Helpers() = default; virtual bool InitTranslator(TextureType aTextureType, + TextureType aWebglTextureType, gfx::BackendType aBackendType, Handle&& aReadHandle, nsTArray&& aBufferHandles, @@ -89,8 +95,8 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, virtual bool RestartReader() = 0; }; - bool Init(TextureType aTextureType, gfx::BackendType aBackendType, - UniquePtr aHelpers); + bool Init(TextureType aTextureType, TextureType aWebglTextureType, + gfx::BackendType aBackendType, UniquePtr aHelpers); /** * Record an event for processing by the CanvasParent's CanvasTranslator. @@ -98,6 +104,10 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, */ void RecordEvent(const gfx::RecordedEvent& aEvent) final; + void DetachResources() final; + + void AddPendingDeletion(std::function&& aPendingDeletion) override; + void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface, const char* aReason) final; @@ -134,6 +144,11 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, void CheckAndSignalReader(); + void QueueProcessPendingDeletions( + RefPtr&& aRecorder); + void QueueProcessPendingDeletionsLocked( + RefPtr&& aRecorder); + size_t mDefaultBufferSize; size_t mMaxDefaultBuffers; uint32_t mMaxSpinCount; @@ -173,6 +188,9 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, UniquePtr mWriterSemaphore; UniquePtr mReaderSemaphore; + + RefPtr mWorkerRef; + bool mIsOnWorker = false; }; } // namespace layers diff --git a/gfx/layers/FrameMetrics.cpp b/gfx/layers/FrameMetrics.cpp index 5d82b7f295..86fdd5ffd6 100644 --- a/gfx/layers/FrameMetrics.cpp +++ b/gfx/layers/FrameMetrics.cpp @@ -147,12 +147,10 @@ CSSSize FrameMetrics::CalculateCompositedSizeInCssPixels( return aCompositionBounds.Size() / aZoom; } -std::pair FrameMetrics::ApplyAbsoluteScrollUpdateFrom( - const ScrollPositionUpdate& aUpdate) { - CSSPoint oldVisualOffset = GetVisualScrollOffset(); +bool FrameMetrics::ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate) { // In applying a main-thread scroll update, try to preserve the relative // offset between the visual and layout viewports. - CSSPoint relativeOffset = oldVisualOffset - GetLayoutScrollOffset(); + CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset(); MOZ_ASSERT(IsRootContent() || relativeOffset == CSSPoint()); // We need to set the two offsets together, otherwise a subsequent // RecalculateLayoutViewportOffset() could see divergent layout and @@ -160,7 +158,7 @@ std::pair FrameMetrics::ApplyAbsoluteScrollUpdateFrom( bool offsetChanged = SetLayoutScrollOffset(aUpdate.GetDestination()); offsetChanged |= ClampAndSetVisualScrollOffset(aUpdate.GetDestination() + relativeOffset); - return {offsetChanged, GetVisualScrollOffset() - oldVisualOffset}; + return offsetChanged; } CSSPoint FrameMetrics::ApplyRelativeScrollUpdateFrom( @@ -168,7 +166,7 @@ CSSPoint FrameMetrics::ApplyRelativeScrollUpdateFrom( MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::Relative); CSSPoint origin = GetVisualScrollOffset(); CSSPoint delta = (aUpdate.GetDestination() - aUpdate.GetSource()); - ClampAndSetVisualScrollOffset(origin + delta); + SetVisualScrollOffset(origin + delta); return GetVisualScrollOffset() - origin; } diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 6b7a41bf25..5d13d36703 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -243,11 +243,9 @@ struct FrameMetrics { } /* - * Returns true if the layout scroll offset or visual scroll offset changed - * and returns the visual scroll offset change delta. + * Returns true if the layout scroll offset or visual scroll offset changed. */ - std::pair ApplyAbsoluteScrollUpdateFrom( - const ScrollPositionUpdate& aUpdate); + bool ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate); /** * Applies the relative scroll offset update contained in aOther to the @@ -893,6 +891,12 @@ struct ScrollMetadata { mScrollUpdates.AppendElements(std::move(aUpdates)); } + void PrependUpdates(const nsTArray& aUpdates) { + MOZ_ASSERT(!aUpdates.IsEmpty()); + + mScrollUpdates.InsertElementsAt(0, aUpdates); + } + private: FrameMetrics mMetrics; diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index 8a4c03f5a2..bdedccf56d 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -34,7 +34,7 @@ #include "nsProxyRelease.h" #include "nsISupportsUtils.h" // for NS_IF_ADDREF -#ifdef XP_MACOSX +#ifdef XP_DARWIN # include "MacIOSurfaceImage.h" #endif @@ -563,7 +563,7 @@ ImageContainer::GetD3D11YCbCrRecycleAllocator( } #endif -#ifdef XP_MACOSX +#ifdef XP_DARWIN already_AddRefed ImageContainer::GetMacIOSurfaceRecycleAllocator() { RecursiveMutexAutoLock lock(mRecursiveMutex); diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index 253ba417eb..6fa12cf195 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -63,7 +63,7 @@ class MemoryOrShmem; class D3D11RecycleAllocator; class D3D11YCbCrRecycleAllocator; #endif -#ifdef XP_MACOSX +#ifdef XP_DARWIN class MacIOSurfaceRecycleAllocator; #endif class SurfaceDescriptorBuffer; @@ -80,7 +80,7 @@ class GLImage; class SharedRGBImage; #ifdef MOZ_WIDGET_ANDROID class SurfaceTextureImage; -#elif defined(XP_MACOSX) +#elif defined(XP_DARWIN) class MacIOSurfaceImage; #elif MOZ_WIDGET_GTK class DMABUFSurfaceImage; @@ -159,7 +159,7 @@ class Image { #ifdef MOZ_WIDGET_ANDROID virtual SurfaceTextureImage* AsSurfaceTextureImage() { return nullptr; } #endif -#ifdef XP_MACOSX +#ifdef XP_DARWIN virtual MacIOSurfaceImage* AsMacIOSurfaceImage() { return nullptr; } #endif virtual PlanarYCbCrImage* AsPlanarYCbCrImage() { return nullptr; } @@ -191,8 +191,8 @@ class Image { virtual ~Image() = default; mozilla::EnumeratedArray> + UniquePtr, + size_t(mozilla::layers::LayersBackend::LAYERS_LAST)> mBackendData; void* mImplData; @@ -530,7 +530,7 @@ class ImageContainer final : public SupportsThreadSafeWeakPtr { KnowsCompositor* aKnowsCompositor); #endif -#ifdef XP_MACOSX +#ifdef XP_DARWIN already_AddRefed GetMacIOSurfaceRecycleAllocator(); #endif @@ -618,7 +618,7 @@ class ImageContainer final : public SupportsThreadSafeWeakPtr { RefPtr mD3D11YCbCrRecycleAllocator MOZ_GUARDED_BY(mRecursiveMutex); #endif -#ifdef XP_MACOSX +#ifdef XP_DARWIN RefPtr mMacIOSurfaceRecycleAllocator MOZ_GUARDED_BY(mRecursiveMutex); #endif diff --git a/gfx/layers/NativeLayerCA.h b/gfx/layers/NativeLayerCA.h index 1424c1251f..b41ac36c23 100644 --- a/gfx/layers/NativeLayerCA.h +++ b/gfx/layers/NativeLayerCA.h @@ -6,7 +6,7 @@ #ifndef mozilla_layers_NativeLayerCA_h #define mozilla_layers_NativeLayerCA_h -#include +#include #include #include @@ -39,7 +39,9 @@ class RenderMacIOSurfaceTextureHost; namespace layers { +#ifdef XP_MACOSX class NativeLayerRootSnapshotterCA; +#endif class SurfacePoolHandleCA; enum class VideoLowPowerType { @@ -102,8 +104,10 @@ class NativeLayerRootCA : public NativeLayerRoot { bool CommitToScreen() override; void CommitOffscreen(); +#ifdef XP_MACOSX void OnNativeLayerRootSnapshotterDestroyed( NativeLayerRootSnapshotterCA* aNativeLayerRootSnapshotter); +#endif // Enters a mode during which CommitToScreen(), when called on a non-main // thread, will not apply any updates to the CALayer tree. @@ -139,7 +143,7 @@ class NativeLayerRootCA : public NativeLayerRoot { void SetWindowIsFullscreen(bool aFullscreen); - VideoLowPowerType CheckVideoLowPower(const MutexAutoLock& aProofOfLock); + VideoLowPowerType CheckVideoLowPower(); protected: explicit NativeLayerRootCA(CALayer* aLayer); @@ -149,7 +153,8 @@ class NativeLayerRootCA : public NativeLayerRoot { explicit Representation(CALayer* aRootCALayer); ~Representation(); void Commit(WhichRepresentation aRepresentation, - const nsTArray>& aSublayers); + const nsTArray>& aSublayers, + bool aWindowIsFullscreen); CALayer* mRootCALayer = nullptr; // strong bool mMutatedLayerStructure = false; }; @@ -160,7 +165,9 @@ class NativeLayerRootCA : public NativeLayerRoot { Mutex mMutex MOZ_UNANNOTATED; // protects all other fields Representation mOnscreenRepresentation; Representation mOffscreenRepresentation; +#ifdef XP_MACOSX NativeLayerRootSnapshotterCA* mWeakSnapshotter = nullptr; +#endif nsTArray> mSublayers; // in z-order float mBackingScale = 1.0f; bool mMutated = false; @@ -188,6 +195,7 @@ class NativeLayerRootCA : public NativeLayerRoot { class RenderSourceNLRS; +#ifdef XP_MACOSX class NativeLayerRootSnapshotterCA final : public NativeLayerRootSnapshotter { public: static UniquePtr Create( @@ -217,6 +225,7 @@ class NativeLayerRootSnapshotterCA final : public NativeLayerRootSnapshotter { RefPtr mSnapshot; CARenderer* mRenderer = nullptr; // strong }; +#endif // NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only // fully-drawn frames make their way to the screen, by maintaining a swap chain @@ -260,6 +269,8 @@ class NativeLayerCA : public NativeLayer { void AttachExternalImage(wr::RenderTextureHost* aExternalImage) override; + void SetRootWindowIsFullscreen(bool aFullscreen); + protected: friend class NativeLayerRootCA; @@ -324,7 +335,8 @@ class NativeLayerCA : public NativeLayer { Maybe GetUnusedSurfaceAndCleanUp( const MutexAutoLock& aProofOfLock); - bool IsVideo(const MutexAutoLock& aProofOfLock); + bool IsVideo(); + bool IsVideoAndLocked(const MutexAutoLock& aProofOfLock); bool ShouldSpecializeVideo(const MutexAutoLock& aProofOfLock); bool HasExtent() const { return mHasExtent; } void SetHasExtent(bool aHasExtent) { mHasExtent = aHasExtent; } @@ -468,10 +480,10 @@ class NativeLayerCA : public NativeLayer { bool mSurfaceIsFlipped = false; CFTypeRefPtr mColor; const bool mIsOpaque = false; + bool mRootWindowIsFullscreen = false; bool mSpecializeVideo = false; bool mHasExtent = false; bool mIsDRM = false; - bool mIsTextureHostVideo = false; #ifdef NIGHTLY_BUILD // Track the consistency of our caller's API usage. Layers that are drawn diff --git a/gfx/layers/NativeLayerCA.mm b/gfx/layers/NativeLayerCA.mm index bd0b50e91b..42a889184e 100644 --- a/gfx/layers/NativeLayerCA.mm +++ b/gfx/layers/NativeLayerCA.mm @@ -5,10 +5,12 @@ #include "mozilla/layers/NativeLayerCA.h" -#import -#import +#ifdef XP_MACOSX +# import +# import +# import +#endif #import -#import #import #include @@ -19,7 +21,11 @@ #include "gfxUtils.h" #include "GLBlitHelper.h" -#include "GLContextCGL.h" +#ifdef XP_MACOSX +# include "GLContextCGL.h" +#else +# include "GLContextEAGL.h" +#endif #include "GLContextProvider.h" #include "MozFramebuffer.h" #include "mozilla/gfx/Swizzle.h" @@ -45,7 +51,9 @@ using gfx::IntSize; using gfx::Matrix4x4; using gfx::SurfaceFormat; using gl::GLContext; +#ifdef XP_MACOSX using gl::GLContextCGL; +#endif static Maybe VideoLowPowerTypeToTelemetryType(VideoLowPowerType aVideoLowPower) { @@ -150,13 +158,23 @@ class AsyncReadbackBufferNLRS // protection, see bug 1585523. struct MOZ_STACK_CLASS AutoCATransaction final { AutoCATransaction() { +#ifdef XP_MACOSX [NSAnimationContext beginGrouping]; +#else + [CATransaction begin]; +#endif // By default, mutating a CALayer property triggers an animation which // smoothly transitions the property to the new value. We don't need these // animations, and this call turns them off: [CATransaction setDisableActions:YES]; } - ~AutoCATransaction() { [NSAnimationContext endGrouping]; } + ~AutoCATransaction() { +#ifdef XP_MACOSX + [NSAnimationContext endGrouping]; +#else + [CATransaction commit]; +#endif + } }; /* static */ already_AddRefed @@ -178,9 +196,9 @@ static CALayer* MakeOffscreenRootCALayer() { // down). AutoCATransaction transaction; CALayer* layer = [CALayer layer]; - layer.position = NSZeroPoint; - layer.bounds = NSZeroRect; - layer.anchorPoint = NSZeroPoint; + layer.position = CGPointZero; + layer.bounds = CGRectZero; + layer.anchorPoint = CGPointZero; layer.contentsGravity = kCAGravityTopLeft; layer.masksToBounds = YES; layer.geometryFlipped = YES; @@ -226,6 +244,7 @@ void NativeLayerRootCA::AppendLayer(NativeLayer* aLayer) { mSublayers.AppendElement(layerCA); layerCA->SetBackingScale(mBackingScale); + layerCA->SetRootWindowIsFullscreen(mWindowIsFullscreen); ForAllRepresentations( [&](Representation& r) { r.mMutatedLayerStructure = true; }); } @@ -257,6 +276,7 @@ void NativeLayerRootCA::SetLayers( RefPtr layerCA = layer->AsNativeLayerCA(); MOZ_RELEASE_ASSERT(layerCA); layerCA->SetBackingScale(mBackingScale); + layerCA->SetRootWindowIsFullscreen(mWindowIsFullscreen); layersCA.AppendElement(std::move(layerCA)); } @@ -306,47 +326,48 @@ bool NativeLayerRootCA::CommitToScreen() { return false; } - mOnscreenRepresentation.Commit(WhichRepresentation::ONSCREEN, mSublayers); + mOnscreenRepresentation.Commit(WhichRepresentation::ONSCREEN, mSublayers, + mWindowIsFullscreen); mCommitPending = false; + } - if (StaticPrefs::gfx_webrender_debug_dump_native_layer_tree_to_file()) { - static uint32_t sFrameID = 0; - uint32_t frameID = sFrameID++; - - NSString* dirPath = - [NSString stringWithFormat:@"%@/Desktop/nativelayerdumps-%d", - NSHomeDirectory(), getpid()]; - if ([NSFileManager.defaultManager createDirectoryAtPath:dirPath - withIntermediateDirectories:YES - attributes:nil - error:nullptr]) { - NSString* filename = - [NSString stringWithFormat:@"frame-%d.html", frameID]; - NSString* filePath = [dirPath stringByAppendingPathComponent:filename]; - DumpLayerTreeToFile([filePath UTF8String]); - } else { - NSLog(@"Failed to create directory %@", dirPath); - } + if (StaticPrefs::gfx_webrender_debug_dump_native_layer_tree_to_file()) { + static uint32_t sFrameID = 0; + uint32_t frameID = sFrameID++; + + NSString* dirPath = + [NSString stringWithFormat:@"%@/Desktop/nativelayerdumps-%d", + NSHomeDirectory(), getpid()]; + if ([NSFileManager.defaultManager createDirectoryAtPath:dirPath + withIntermediateDirectories:YES + attributes:nil + error:nullptr]) { + NSString* filename = + [NSString stringWithFormat:@"frame-%d.html", frameID]; + NSString* filePath = [dirPath stringByAppendingPathComponent:filename]; + DumpLayerTreeToFile([filePath UTF8String]); + } else { + NSLog(@"Failed to create directory %@", dirPath); } + } - // Decide if we are going to emit telemetry about video low power on this - // commit. - static const int32_t TELEMETRY_COMMIT_PERIOD = - StaticPrefs::gfx_core_animation_low_power_telemetry_frames_AtStartup(); - mTelemetryCommitCount = - (mTelemetryCommitCount + 1) % TELEMETRY_COMMIT_PERIOD; - if (mTelemetryCommitCount == 0) { - // Figure out if we are hitting video low power mode. - VideoLowPowerType videoLowPower = CheckVideoLowPower(lock); - EmitTelemetryForVideoLowPower(videoLowPower); - } + // Decide if we are going to emit telemetry about video low power on this + // commit. + static const int32_t TELEMETRY_COMMIT_PERIOD = + StaticPrefs::gfx_core_animation_low_power_telemetry_frames_AtStartup(); + mTelemetryCommitCount = (mTelemetryCommitCount + 1) % TELEMETRY_COMMIT_PERIOD; + if (mTelemetryCommitCount == 0) { + // Figure out if we are hitting video low power mode. + VideoLowPowerType videoLowPower = CheckVideoLowPower(); + EmitTelemetryForVideoLowPower(videoLowPower); } return true; } UniquePtr NativeLayerRootCA::CreateSnapshotter() { +#ifdef XP_MACOSX MutexAutoLock lock(mMutex); MOZ_RELEASE_ASSERT(!mWeakSnapshotter, "No NativeLayerRootSnapshotter for this NativeLayerRoot " @@ -358,18 +379,24 @@ UniquePtr NativeLayerRootCA::CreateSnapshotter() { mWeakSnapshotter = cr.get(); } return cr; +#else + return nullptr; +#endif } +#ifdef XP_MACOSX void NativeLayerRootCA::OnNativeLayerRootSnapshotterDestroyed( NativeLayerRootSnapshotterCA* aNativeLayerRootSnapshotter) { MutexAutoLock lock(mMutex); MOZ_RELEASE_ASSERT(mWeakSnapshotter == aNativeLayerRootSnapshotter); mWeakSnapshotter = nullptr; } +#endif void NativeLayerRootCA::CommitOffscreen() { MutexAutoLock lock(mMutex); - mOffscreenRepresentation.Commit(WhichRepresentation::OFFSCREEN, mSublayers); + mOffscreenRepresentation.Commit(WhichRepresentation::OFFSCREEN, mSublayers, + mWindowIsFullscreen); } template @@ -394,7 +421,8 @@ NativeLayerRootCA::Representation::~Representation() { void NativeLayerRootCA::Representation::Commit( WhichRepresentation aRepresentation, - const nsTArray>& aSublayers) { + const nsTArray>& aSublayers, + bool aWindowIsFullscreen) { bool mustRebuild = mMutatedLayerStructure; if (!mustRebuild) { // Check which type of update we need to do, if any. @@ -439,7 +467,7 @@ void NativeLayerRootCA::Representation::Commit( mustRebuild |= layer->WillUpdateAffectLayers(aRepresentation); layer->ApplyChanges(aRepresentation, NativeLayerCA::UpdateType::All); CALayer* caLayer = layer->UnderlyingCALayer(aRepresentation); - if (!caLayer.masksToBounds || !NSIsEmptyRect(caLayer.bounds)) { + if (!caLayer.masksToBounds || !CGRectIsEmpty(caLayer.bounds)) { // This layer has an extent. If it didn't before, we need to rebuild. mustRebuild |= !layer->HasExtent(); layer->SetHasExtent(true); @@ -472,6 +500,7 @@ void NativeLayerRootCA::Representation::Commit( mMutatedLayerStructure = false; } +#ifdef XP_MACOSX /* static */ UniquePtr NativeLayerRootSnapshotterCA::Create(NativeLayerRootCA* aLayerRoot, CALayer* aRootCALayer) { @@ -498,6 +527,7 @@ NativeLayerRootSnapshotterCA::Create(NativeLayerRootCA* aLayerRoot, new NativeLayerRootSnapshotterCA(aLayerRoot, std::move(gl), aRootCALayer)); } +#endif void NativeLayerRootCA::DumpLayerTreeToFile(const char* aPath) { MutexAutoLock lock(mMutex); @@ -519,7 +549,15 @@ void NativeLayerRootCA::DumpLayerTreeToFile(const char* aPath) { } void NativeLayerRootCA::SetWindowIsFullscreen(bool aFullscreen) { - mWindowIsFullscreen = aFullscreen; + MutexAutoLock lock(mMutex); + + if (mWindowIsFullscreen != aFullscreen) { + mWindowIsFullscreen = aFullscreen; + + for (auto layer : mSublayers) { + layer->SetRootWindowIsFullscreen(mWindowIsFullscreen); + } + } } /* static */ bool IsCGColorOpaqueBlack(CGColorRef aColor) { @@ -541,8 +579,7 @@ void NativeLayerRootCA::SetWindowIsFullscreen(bool aFullscreen) { return components[componentCount - 1] >= 1.0f; } -VideoLowPowerType NativeLayerRootCA::CheckVideoLowPower( - const MutexAutoLock& aProofOfLock) { +VideoLowPowerType NativeLayerRootCA::CheckVideoLowPower() { // This deteremines whether the current layer contents qualify for the // macOS Core Animation video low power mode. Those requirements are // summarized at @@ -572,7 +609,7 @@ VideoLowPowerType NativeLayerRootCA::CheckVideoLowPower( secondCALayer = topCALayer; topCALayer = topLayer->UnderlyingCALayer(WhichRepresentation::ONSCREEN); - topLayerIsVideo = topLayer->IsVideo(aProofOfLock); + topLayerIsVideo = topLayer->IsVideo(); if (topLayerIsVideo) { ++videoLayerCount; } @@ -641,6 +678,7 @@ VideoLowPowerType NativeLayerRootCA::CheckVideoLowPower( return VideoLowPowerType::LowPower; } +#ifdef XP_MACOSX NativeLayerRootSnapshotterCA::NativeLayerRootSnapshotterCA( NativeLayerRootCA* aLayerRoot, RefPtr&& aGL, CALayer* aRootCALayer) @@ -779,6 +817,7 @@ NativeLayerRootSnapshotterCA::CreateAsyncReadbackBuffer(const IntSize& aSize) { LOCAL_GL_STREAM_READ); return MakeAndAddRef(mGL, aSize, bufferHandle); } +#endif NativeLayerCA::NativeLayerCA(const IntSize& aSize, bool aIsOpaque, SurfacePoolHandleCA* aSurfacePoolHandle) @@ -796,8 +835,9 @@ NativeLayerCA::NativeLayerCA(bool aIsOpaque) mIsOpaque(aIsOpaque) { #ifdef NIGHTLY_BUILD if (StaticPrefs::gfx_core_animation_specialize_video_log()) { - NSLog(@"VIDEO_LOG: NativeLayerCA: %p is being created to host an external " - @"image, which may force a video layer rebuild.", + NSLog(@"VIDEO_LOG: NativeLayerCA: %p is being created to host video, which " + @"will force a video " + @"layer rebuild.", this); } #endif @@ -824,7 +864,7 @@ NativeLayerCA::~NativeLayerCA() { if (mHasEverAttachExternalImage && StaticPrefs::gfx_core_animation_specialize_video_log()) { NSLog(@"VIDEO_LOG: ~NativeLayerCA: %p is being destroyed after hosting " - @"an external image.", + @"video.", this); } #endif @@ -862,9 +902,6 @@ void NativeLayerCA::AttachExternalImage(wr::RenderTextureHost* aExternalImage) { return; } - // Determine if TextureHost is a video surface. - mIsTextureHostVideo = gfx::Info(mTextureHost->GetFormat())->isYuv; - gfx::IntSize oldSize = mSize; mSize = texture->GetSize(0); bool changedSizeAndDisplayRect = (mSize != oldSize); @@ -896,15 +933,18 @@ void NativeLayerCA::AttachExternalImage(wr::RenderTextureHost* aExternalImage) { }); } -bool NativeLayerCA::IsVideo(const MutexAutoLock& aProofOfLock) { - // If we have a texture host, we've checked to see if it's providing video. - // And if we don't have a texture host, it isn't video, so we just check - // the value we've computed. - return mIsTextureHostVideo; +bool NativeLayerCA::IsVideo() { + // Anything with a texture host is considered a video source. + return mTextureHost; +} + +bool NativeLayerCA::IsVideoAndLocked(const MutexAutoLock& aProofOfLock) { + // Anything with a texture host is considered a video source. + return mTextureHost; } bool NativeLayerCA::ShouldSpecializeVideo(const MutexAutoLock& aProofOfLock) { - if (!IsVideo(aProofOfLock)) { + if (!IsVideoAndLocked(aProofOfLock)) { // Only videos are eligible. return false; } @@ -934,7 +974,49 @@ bool NativeLayerCA::ShouldSpecializeVideo(const MutexAutoLock& aProofOfLock) { return true; } - return StaticPrefs::gfx_core_animation_specialize_video(); + // Beyond this point, we return true if-and-only-if we think we can achieve + // the power-saving "detached mode" of the macOS compositor. + + if (!StaticPrefs::gfx_core_animation_specialize_video()) { + // Pref must be set. + return false; + } + + if (pixelFormat != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange && + pixelFormat != kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) { + // The video is not in one of the formats that qualifies for detachment. + return false; + } + + // It will only detach if we're fullscreen. + return mRootWindowIsFullscreen; +} + +void NativeLayerCA::SetRootWindowIsFullscreen(bool aFullscreen) { + if (mRootWindowIsFullscreen == aFullscreen) { + return; + } + + MutexAutoLock lock(mMutex); + + mRootWindowIsFullscreen = aFullscreen; + + bool oldSpecializeVideo = mSpecializeVideo; + mSpecializeVideo = ShouldSpecializeVideo(lock); + bool changedSpecializeVideo = (mSpecializeVideo != oldSpecializeVideo); + + if (changedSpecializeVideo) { +#ifdef NIGHTLY_BUILD + if (StaticPrefs::gfx_core_animation_specialize_video_log()) { + NSLog(@"VIDEO_LOG: SetRootWindowIsFullscreen: %p is forcing a video " + @"layer rebuild.", + this); + } +#endif + + ForAllRepresentations( + [&](Representation& r) { r.mMutatedSpecializeVideo = true; }); + } } void NativeLayerCA::SetSurfaceIsFlipped(bool aIsFlipped) { @@ -1328,8 +1410,6 @@ void NativeLayerCA::NotifySurfaceReady() { mInProgressSurface, "NotifySurfaceReady called without preceding call to NextSurface"); - mIsTextureHostVideo = false; - if (mInProgressLockedIOSurface) { mInProgressLockedIOSurface->Unlock(false); mInProgressLockedIOSurface = nullptr; @@ -1385,7 +1465,7 @@ void NativeLayerCA::ForAllRepresentations(F aFn) { NativeLayerCA::UpdateType NativeLayerCA::HasUpdate( WhichRepresentation aRepresentation) { MutexAutoLock lock(mMutex); - return GetRepresentation(aRepresentation).HasUpdate(IsVideo(lock)); + return GetRepresentation(aRepresentation).HasUpdate(IsVideoAndLocked(lock)); } /* static */ @@ -1430,7 +1510,7 @@ bool NativeLayerCA::ApplyChanges(WhichRepresentation aRepresentation, .ApplyChanges(aUpdate, mSize, mIsOpaque, mPosition, mTransform, mDisplayRect, mClipRect, mBackingScale, mSurfaceIsFlipped, mSamplingFilter, mSpecializeVideo, surface, mColor, mIsDRM, - IsVideo(lock)); + IsVideo()); } CALayer* NativeLayerCA::UnderlyingCALayer(WhichRepresentation aRepresentation) { @@ -1457,8 +1537,10 @@ static NSString* NSStringForOSType(OSType type) { CFRelease(surfaceValues); if (aBuffer) { +#ifdef XP_MACOSX CGColorSpaceRef colorSpace = CVImageBufferGetColorSpace(aBuffer); NSLog(@"ColorSpace is %@.\n", colorSpace); +#endif CFDictionaryRef bufferAttachments = CVBufferGetAttachments(aBuffer, kCVAttachmentMode_ShouldPropagate); @@ -1512,7 +1594,7 @@ bool NativeLayerCA::Representation::EnqueueSurface(IOSurfaceRef aSurfaceRef) { return false; } -#ifdef NIGHTLY_BUILD +#if defined(NIGHTLY_BUILD) && defined(XP_MACOSX) if (StaticPrefs::gfx_core_animation_specialize_video_check_color_space()) { // Ensure the resulting pixel buffer has a color space. If it doesn't, then // modify the surface and create the buffer again. @@ -1764,10 +1846,10 @@ bool NativeLayerCA::Representation::ApplyChanges( mOpaquenessTintLayer.contentsGravity = kCAGravityTopLeft; if (aIsOpaque) { mOpaquenessTintLayer.backgroundColor = - [[[NSColor greenColor] colorWithAlphaComponent:0.5] CGColor]; + CGColorCreateGenericRGB(0, 1, 0, 0.5); } else { mOpaquenessTintLayer.backgroundColor = - [[[NSColor redColor] colorWithAlphaComponent:0.5] CGColor]; + CGColorCreateGenericRGB(1, 0, 0, 0.5); } [mWrappingCALayer addSublayer:mOpaquenessTintLayer]; } else if (!shouldTintOpaqueness && mOpaquenessTintLayer) { diff --git a/gfx/layers/PersistentBufferProvider.cpp b/gfx/layers/PersistentBufferProvider.cpp index 0e02501a4a..d79c8cdca1 100644 --- a/gfx/layers/PersistentBufferProvider.cpp +++ b/gfx/layers/PersistentBufferProvider.cpp @@ -663,14 +663,15 @@ PersistentBufferProviderShared::BorrowSnapshot(gfx::DrawTarget* aTarget) { if (mDrawTarget) { auto back = GetTexture(mBack); - MOZ_ASSERT(back && back->IsLocked()); + if (NS_WARN_IF(!back) || NS_WARN_IF(!back->IsLocked())) { + return nullptr; + } mSnapshot = back->BorrowSnapshot(); return do_AddRef(mSnapshot); } auto front = GetTexture(mFront); - if (!front || front->IsLocked()) { - MOZ_ASSERT(false); + if (NS_WARN_IF(!front) || NS_WARN_IF(front->IsLocked())) { return nullptr; } diff --git a/gfx/layers/RemoteTextureMap.cpp b/gfx/layers/RemoteTextureMap.cpp index fa0cfd33f5..3fe3b13deb 100644 --- a/gfx/layers/RemoteTextureMap.cpp +++ b/gfx/layers/RemoteTextureMap.cpp @@ -369,7 +369,11 @@ void RemoteTextureMap::PushTexture( aTextureId, aTextureHost, std::move(aTextureData), std::move(aResourceWrapper)); - MOZ_ASSERT(owner->mLatestTextureId < aTextureId); + MOZ_ASSERT(owner->mLatestPushedTextureId < aTextureId); + if (owner->mLatestPushedTextureId < aTextureId) { + owner->mLatestPushedTextureId = aTextureId; + } + MOZ_ASSERT(owner->mLatestUsingTextureId < aTextureId); owner->mWaitingTextureDataHolders.push_back(std::move(textureData)); @@ -898,9 +902,9 @@ void RemoteTextureMap::UpdateTexture(const MonitorAutoLock& aProofOfLock, const RemoteTextureId aTextureId) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(aOwner); - MOZ_ASSERT(aTextureId >= aOwner->mLatestTextureId); + MOZ_ASSERT(aTextureId >= aOwner->mLatestUsingTextureId); - if (aTextureId == aOwner->mLatestTextureId) { + if (aTextureId == aOwner->mLatestUsingTextureId) { // No need to update texture. return; } @@ -913,7 +917,7 @@ void RemoteTextureMap::UpdateTexture(const MonitorAutoLock& aProofOfLock, } MOZ_RELEASE_ASSERT(front->mTextureHost); aOwner->mLatestTextureHost = front->mTextureHost; - aOwner->mLatestTextureId = front->mTextureId; + aOwner->mLatestUsingTextureId = front->mTextureId; UniquePtr holder = std::move(front); aOwner->mWaitingTextureDataHolders.pop_front(); @@ -962,10 +966,42 @@ void RemoteTextureMap::GetAllRenderingReadyCallbacks( MOZ_ASSERT(aOwner->mRenderingReadyCallbackHolders.empty()); } +bool RemoteTextureMap::WaitForRemoteTextureOwner( + RemoteTextureHostWrapper* aTextureHostWrapper) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(aTextureHostWrapper); + + const auto& ownerId = aTextureHostWrapper->mOwnerId; + const auto& forPid = aTextureHostWrapper->mForPid; + + MonitorAutoLock lock(mMonitor); + + auto* owner = GetTextureOwner(lock, ownerId, forPid); + // If there is no texture owner yet, then we might need to wait for one to + // be created, if allowed. If so, we must also wait for an initial texture + // host to be created so we can use it. + if (!owner || (!owner->mLatestTextureHost && + owner->mWaitingTextureDataHolders.empty())) { + const TimeDuration timeout = TimeDuration::FromMilliseconds(10000); + while (!owner || (!owner->mLatestTextureHost && + owner->mWaitingTextureDataHolders.empty())) { + if (owner && (owner->mIsContextLost || owner->mDeferUnregister)) { + // If the context was lost, no further updates are expected. + return false; + } + CVStatus status = mMonitor.Wait(timeout); + if (status == CVStatus::Timeout) { + return false; + } + owner = GetTextureOwner(lock, ownerId, forPid); + } + } + return true; +} + bool RemoteTextureMap::GetRemoteTexture( RemoteTextureHostWrapper* aTextureHostWrapper, - std::function&& aReadyCallback, - bool aWaitForRemoteTextureOwner) { + std::function&& aReadyCallback) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(aTextureHostWrapper); @@ -983,27 +1019,8 @@ bool RemoteTextureMap::GetRemoteTexture( MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, ownerId, forPid); - // If there is no texture owner yet, then we might need to wait for one to - // be created, if allowed. If so, we must also wait for an initial texture - // host to be created so we can use it. - if (!owner || (aWaitForRemoteTextureOwner && !owner->mLatestTextureHost && - owner->mWaitingTextureDataHolders.empty())) { - if (!aWaitForRemoteTextureOwner) { - return false; - } - const TimeDuration timeout = TimeDuration::FromMilliseconds(10000); - while (!owner || (!owner->mLatestTextureHost && - owner->mWaitingTextureDataHolders.empty())) { - if (owner && (owner->mIsContextLost || owner->mDeferUnregister)) { - // If the context was lost, no further updates are expected. - return false; - } - CVStatus status = mMonitor.Wait(timeout); - if (status == CVStatus::Timeout) { - return false; - } - owner = GetTextureOwner(lock, ownerId, forPid); - } + if (!owner) { + return false; } UpdateTexture(lock, owner, textureId); @@ -1014,7 +1031,7 @@ bool RemoteTextureMap::GetRemoteTexture( return false; } - if (textureId == owner->mLatestTextureId) { + if (textureId == owner->mLatestUsingTextureId) { MOZ_ASSERT(owner->mLatestTextureHost); MOZ_ASSERT(owner->mLatestTextureHost->GetSize() == size); if (owner->mLatestTextureHost->GetSize() != size) { @@ -1034,7 +1051,7 @@ bool RemoteTextureMap::GetRemoteTexture( } // Update mRemoteTextureHost - if (textureId == owner->mLatestTextureId) { + if (textureId == owner->mLatestUsingTextureId) { const auto key = std::pair(forPid, textureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it != mRemoteTextureHostWrapperHolders.end() && @@ -1195,16 +1212,9 @@ bool RemoteTextureMap::CheckRemoteTextureReady( return true; } - if (it->second->mRemoteTextureHost) { + if (owner->mLatestPushedTextureId >= aInfo.mTextureId) { return true; } - MOZ_ASSERT(!it->second->mRemoteTextureHost); - - // Check if RemoteTextureId is as expected. - if (!owner->mRenderingReadyCallbackHolders.empty()) { - auto& front = owner->mRenderingReadyCallbackHolders.front(); - MOZ_RELEASE_ASSERT(aInfo.mTextureId >= front->mTextureId); - } auto callbackHolder = MakeUnique( aInfo.mTextureId, std::move(aCallback)); @@ -1234,9 +1244,8 @@ bool RemoteTextureMap::WaitRemoteTextureReady(const RemoteTextureInfo& aInfo) { } const TimeDuration timeout = TimeDuration::FromMilliseconds(1000); - TextureHost* remoteTexture = it->second->mRemoteTextureHost; - while (!remoteTexture) { + while (owner->mLatestPushedTextureId < aInfo.mTextureId) { CVStatus status = mMonitor.Wait(timeout); if (status == CVStatus::Timeout) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); @@ -1251,14 +1260,11 @@ bool RemoteTextureMap::WaitRemoteTextureReady(const RemoteTextureInfo& aInfo) { return false; } - remoteTexture = it->second->mRemoteTextureHost; - if (!remoteTexture) { - auto* owner = GetTextureOwner(lock, aInfo.mOwnerId, aInfo.mForPid); - // When owner is alreay unregistered, remote texture will not be pushed. - if (!owner || owner->mIsContextLost) { - // This could happen with IPC abnormal shutdown - return false; - } + auto* owner = GetTextureOwner(lock, aInfo.mOwnerId, aInfo.mForPid); + // When owner is alreay unregistered, remote texture will not be pushed. + if (!owner || owner->mIsContextLost) { + // This could happen with IPC abnormal shutdown + return false; } } diff --git a/gfx/layers/RemoteTextureMap.h b/gfx/layers/RemoteTextureMap.h index a60bfd4c3c..8aee1c6f7d 100644 --- a/gfx/layers/RemoteTextureMap.h +++ b/gfx/layers/RemoteTextureMap.h @@ -304,13 +304,14 @@ class RemoteTextureMap { void NotifyContextRestored(const RemoteTextureOwnerIdSet& aOwnerIds, const base::ProcessId aForPid); + bool WaitForRemoteTextureOwner(RemoteTextureHostWrapper* aTextureHostWrapper); + // Get remote texture's TextureHost for RemoteTextureHostWrapper. // // return true when aReadyCallback will be called. bool GetRemoteTexture( RemoteTextureHostWrapper* aTextureHostWrapper, - std::function&& aReadyCallback, - bool aWaitForRemoteTextureOwner = false); + std::function&& aReadyCallback); bool WaitForTxn(const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, RemoteTextureTxnType aTxnType, @@ -403,7 +404,8 @@ class RemoteTextureMap { std::deque> mRenderingReadyCallbackHolders; - RemoteTextureId mLatestTextureId = {0}; + RemoteTextureId mLatestPushedTextureId = {0}; + RemoteTextureId mLatestUsingTextureId = {0}; CompositableTextureHostRef mLatestTextureHost; CompositableTextureHostRef mLatestRenderedTextureHost; // Holds compositable refs to TextureHosts of RenderTextureHosts that are diff --git a/gfx/layers/SurfacePool.h b/gfx/layers/SurfacePool.h index eecb398d85..6be3718af5 100644 --- a/gfx/layers/SurfacePool.h +++ b/gfx/layers/SurfacePool.h @@ -31,7 +31,7 @@ class SurfacePool { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SurfacePool); -#if defined(XP_MACOSX) || defined(MOZ_WAYLAND) +#if defined(XP_DARWIN) || defined(MOZ_WAYLAND) static RefPtr Create(size_t aPoolSizeLimit); #endif diff --git a/gfx/layers/SurfacePoolCA.h b/gfx/layers/SurfacePoolCA.h index 97344f7d06..265782df9e 100644 --- a/gfx/layers/SurfacePoolCA.h +++ b/gfx/layers/SurfacePoolCA.h @@ -6,7 +6,7 @@ #ifndef mozilla_layers_SurfacePoolCA_h #define mozilla_layers_SurfacePoolCA_h -#include +#include #include #include diff --git a/gfx/layers/SurfacePoolCA.mm b/gfx/layers/SurfacePoolCA.mm index 47b2663ed7..4e012350f0 100644 --- a/gfx/layers/SurfacePoolCA.mm +++ b/gfx/layers/SurfacePoolCA.mm @@ -6,6 +6,7 @@ #include "mozilla/layers/SurfacePoolCA.h" #import +#include #include #include @@ -16,7 +17,12 @@ #include "mozilla/StaticMutex.h" #include "mozilla/StaticPrefs_gfx.h" -#include "GLContextCGL.h" +#ifdef XP_MACOSX +# include "GLContextCGL.h" +#else +# include "GLContextEAGL.h" +#endif + #include "MozFramebuffer.h" #include "ScopedGLHelpers.h" @@ -28,7 +34,11 @@ using gfx::IntRect; using gfx::IntRegion; using gfx::IntSize; using gl::GLContext; +#ifdef XP_MACOSX using gl::GLContextCGL; +#else +using gl::GLContextEAGL; +#endif /* static */ RefPtr SurfacePool::Create(size_t aPoolSizeLimit) { return new SurfacePoolCA(aPoolSizeLimit); @@ -306,8 +316,13 @@ Maybe SurfacePoolCA::LockedPool::GetFramebufferForSurface( "Framebuffer creation", GRAPHICS_TileAllocation, nsPrintfCString("%dx%d", entry.mSize.width, entry.mSize.height)); +#ifdef XP_MACOSX RefPtr cgl = GLContextCGL::Cast(aGL); MOZ_RELEASE_ASSERT(cgl, "Unexpected GLContext type"); +#else + RefPtr eagl = GLContextEAGL::Cast(aGL); + MOZ_RELEASE_ASSERT(eagl, "Unexpected GLContext type"); +#endif if (!aGL->MakeCurrent()) { // Context may have been destroyed. @@ -318,10 +333,14 @@ Maybe SurfacePoolCA::LockedPool::GetFramebufferForSurface( { const gl::ScopedBindTexture bindTex(aGL, tex, LOCAL_GL_TEXTURE_RECTANGLE_ARB); +#ifdef XP_MACOSX CGLTexImageIOSurface2D(cgl->GetCGLContext(), LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_RGBA, entry.mSize.width, entry.mSize.height, LOCAL_GL_BGRA, LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, entry.mIOSurface.get(), 0); +#else + MOZ_CRASH("unimplemented"); +#endif } auto fb = diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index d28392b716..ef3cde3596 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -62,6 +62,8 @@ mozilla::LazyLogModule mozilla::layers::APZCTreeManager::sLog("apz.manager"); #define APZCTM_LOG(...) \ MOZ_LOG(APZCTreeManager::sLog, LogLevel::Debug, (__VA_ARGS__)) +#define APZCTM_LOGV(...) \ + MOZ_LOG(APZCTreeManager::sLog, LogLevel::Verbose, (__VA_ARGS__)) static mozilla::LazyLogModule sApzKeyLog("apz.key"); #define APZ_KEY_LOG(...) MOZ_LOG(sApzKeyLog, LogLevel::Debug, (__VA_ARGS__)) @@ -83,10 +85,10 @@ typedef CompositorBridgeParent::LayerTreeState LayerTreeState; struct APZCTreeManager::TreeBuildingState { TreeBuildingState(LayersId aRootLayersId, bool aIsFirstPaint, LayersId aOriginatingLayersId, APZTestData* aTestData, - uint32_t aPaintSequence) + uint32_t aPaintSequence, bool aIsTestLoggingEnabled) : mIsFirstPaint(aIsFirstPaint), mOriginatingLayersId(aOriginatingLayersId), - mPaintLogger(aTestData, aPaintSequence) { + mPaintLogger(aTestData, aPaintSequence, aIsTestLoggingEnabled) { CompositorBridgeParent::CallWithIndirectShadowTree( aRootLayersId, [this](LayerTreeState& aState) -> void { mCompositorController = aState.GetCompositorController(); @@ -158,6 +160,9 @@ struct APZCTreeManager::TreeBuildingState { // cumulative EventRegionsOverride flags from the reflayers, and is used to // apply them to descendant layers. std::stack mOverrideFlags; + + // Wether the APZC correspoinding to the originating LayersId was updated. + bool mOriginatingLayersIdUpdated = false; }; class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver { @@ -391,8 +396,7 @@ void APZCTreeManager::SetAllowedTouchBehavior( uint64_t aInputBlockId, const nsTArray& aValues) { if (!APZThreadUtils::IsControllerThread()) { APZThreadUtils::RunOnControllerThread( - NewRunnableMethod>>( + NewRunnableMethod>( "layers::APZCTreeManager::SetAllowedTouchBehavior", this, &APZCTreeManager::SetAllowedTouchBehavior, aInputBlockId, aValues.Clone())); @@ -420,9 +424,11 @@ void APZCTreeManager::SetBrowserGestureResponse( mInputQueue->SetBrowserGestureResponse(aInputBlockId, aResponse); } -void APZCTreeManager::UpdateHitTestingTree( - const WebRenderScrollDataWrapper& aRoot, bool aIsFirstPaint, - LayersId aOriginatingLayersId, uint32_t aPaintSequenceNumber) { +APZCTreeManager::OriginatingLayersIdUpdated +APZCTreeManager::UpdateHitTestingTree(const WebRenderScrollDataWrapper& aRoot, + bool aIsFirstPaint, + LayersId aOriginatingLayersId, + uint32_t aPaintSequenceNumber) { AssertOnUpdaterThread(); RecursiveMutexAutoLock lock(mTreeLock); @@ -430,7 +436,8 @@ void APZCTreeManager::UpdateHitTestingTree( // For testing purposes, we log some data to the APZTestData associated with // the layers id that originated this update. APZTestData* testData = nullptr; - if (StaticPrefs::apz_test_logging_enabled()) { + const bool testLoggingEnabled = StaticPrefs::apz_test_logging_enabled(); + if (testLoggingEnabled) { MutexAutoLock lock(mTestDataLock); UniquePtr ptr = MakeUnique(); auto result = @@ -440,7 +447,7 @@ void APZCTreeManager::UpdateHitTestingTree( } TreeBuildingState state(mRootLayersId, aIsFirstPaint, aOriginatingLayersId, - testData, aPaintSequenceNumber); + testData, aPaintSequenceNumber, testLoggingEnabled); // We do this business with collecting the entire tree into an array because // otherwise it's very hard to determine which APZC instances need to be @@ -730,6 +737,8 @@ void APZCTreeManager::UpdateHitTestingTree( mRootNode->Dump(" "); } SendSubtreeTransformsToChromeMainThread(nullptr); + + return OriginatingLayersIdUpdated{state.mOriginatingLayersIdUpdated}; } void APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId, @@ -763,7 +772,7 @@ void APZCTreeManager::SampleForWebRender(const Maybe& aVsyncId, controller->ScheduleRenderOnCompositorThread( wr::RenderReasons::ANIMATED_PROPERTY); } - APZCTM_LOG( + APZCTM_LOGV( "APZCTreeManager(%p)::SampleForWebRender, want more composites: %d\n", this, (activeAnimations && controller)); @@ -1231,6 +1240,10 @@ HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer( "Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n", apzc.get(), aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId); + if (aLayersId == aState.mOriginatingLayersId) { + aState.mOriginatingLayersIdUpdated = true; + } + // If we haven't encountered a layer already with the same metrics, then we // need to do the full reuse-or-make-an-APZC algorithm, which is contained // inside the block below. @@ -1522,6 +1535,11 @@ APZEventResult APZCTreeManager::ReceiveInputEvent( } case MOUSE_INPUT: { MouseInput& mouseInput = aEvent.AsMouseInput(); + MOZ_LOG(APZCTreeManager::sLog, + mouseInput.mType == MouseInput::MOUSE_MOVE ? LogLevel::Verbose + : LogLevel::Debug, + ("Received mouse input type %d at %s\n", (int)mouseInput.mType, + ToString(mouseInput.mOrigin).c_str())); mouseInput.mHandledByAPZ = true; SetCurrentMousePosition(mouseInput.mOrigin); @@ -1613,6 +1631,9 @@ APZEventResult APZCTreeManager::ReceiveInputEvent( // Do this before early return for Fission hit testing. ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput(); + APZCTM_LOG("Received wheel input at %s with delta (%f, %f)\n", + ToString(wheelInput.mOrigin).c_str(), wheelInput.mDeltaX, + wheelInput.mDeltaY); state.mHit = GetTargetAPZC(wheelInput.mOrigin); wheelInput.mHandledByAPZ = WillHandleInput(wheelInput); @@ -1674,6 +1695,9 @@ APZEventResult APZCTreeManager::ReceiveInputEvent( // Do this before early return for Fission hit testing. PanGestureInput& panInput = aEvent.AsPanGestureInput(); + APZCTM_LOG("Received pan gesture input type %d at %s with delta %s\n", + (int)panInput.mType, ToString(panInput.mPanStartPoint).c_str(), + ToString(panInput.mPanDisplacement).c_str()); state.mHit = GetTargetAPZC(panInput.mPanStartPoint); panInput.mHandledByAPZ = WillHandleInput(panInput); @@ -2021,6 +2045,18 @@ APZEventResult APZCTreeManager::InputHandlingState::Finish( void APZCTreeManager::ProcessTouchInput(InputHandlingState& aState, MultiTouchInput& aInput) { + APZCTM_LOG("Received touch input type %d with touch points [%s]\n", + (int)aInput.mType, + [&] { + nsCString result; + for (const auto& touch : aInput.mTouches) { + result.AppendPrintf("%s", + ToString(touch.mScreenPoint).c_str()); + } + return result; + }() + .get()); + aInput.mHandledByAPZ = true; nsTArray touchBehaviors; HitTestingTreeNodeAutoLock hitScrollbarNode; diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h index ff9bba51ca..71d35fd5a6 100644 --- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -190,10 +190,13 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge { * this layer update. Note that every child * process' layer subtree has its own sequence * numbers. + * @return OriginatingLayersIdUpdated whether the given + * |aOriginatingLayersId|'s data was processed. */ - void UpdateHitTestingTree(const WebRenderScrollDataWrapper& aRoot, - bool aIsFirstPaint, LayersId aOriginatingLayersId, - uint32_t aPaintSequenceNumber); + enum class OriginatingLayersIdUpdated : bool { No, Yes }; + OriginatingLayersIdUpdated UpdateHitTestingTree( + const WebRenderScrollDataWrapper& aRoot, bool aIsFirstPaint, + LayersId aOriginatingLayersId, uint32_t aPaintSequenceNumber); /** * Called when webrender is enabled, from the sampler thread. This function diff --git a/gfx/layers/apz/src/APZUpdater.cpp b/gfx/layers/apz/src/APZUpdater.cpp index 2bbad6e1a7..2f1b551f3a 100644 --- a/gfx/layers/apz/src/APZUpdater.cpp +++ b/gfx/layers/apz/src/APZUpdater.cpp @@ -191,14 +191,38 @@ void APZUpdater::UpdateScrollDataAndTreeState( auto isFirstPaint = aScrollData.IsFirstPaint(); auto paintSequenceNumber = aScrollData.GetPaintSequenceNumber(); + auto previous = self->mScrollData.find(aOriginatingLayersId); + // If there's the previous scroll data which hasn't yet been + // processed, we need to merge the previous scroll position updates + // into the latest one. + if (previous != self->mScrollData.end()) { + WebRenderScrollData& previousData = previous->second; + if (previousData.GetWasUpdateSkipped()) { + MOZ_ASSERT(previousData.IsFirstPaint()); + aScrollData.PrependUpdates(previousData); + } + } + self->mScrollData[aOriginatingLayersId] = std::move(aScrollData); auto root = self->mScrollData.find(aRootLayerTreeId); if (root == self->mScrollData.end()) { return; } - self->mApz->UpdateHitTestingTree( - WebRenderScrollDataWrapper(*self, &(root->second)), - isFirstPaint, aOriginatingLayersId, paintSequenceNumber); + if ((self->mApz->UpdateHitTestingTree( + WebRenderScrollDataWrapper(*self, &(root->second)), + isFirstPaint, aOriginatingLayersId, paintSequenceNumber) == + APZCTreeManager::OriginatingLayersIdUpdated::No) && + isFirstPaint) { + // If the given |aOriginatingLayersId| data wasn't used for + // updating, it's likly that the parent process hasn't yet + // received the LayersId as "ReferentId", thus we need to process + // it in a subsequent update where we got the "ReferentId". + // + // NOTE: We restrict the above previous scroll data prepending to + // the first paint case, otherwise the cumulative scroll data may + // be exploded if we have never received the "ReferenceId". + self->mScrollData[aOriginatingLayersId].SetWasUpdateSkipped(); + } })); } diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 342375c019..edbd2ecffa 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -106,6 +106,10 @@ static mozilla::LazyLogModule sApzCtlLog("apz.controller"); APZC_LOG("%p(%s scrollId=%" PRIu64 "): " fmt, (apzc), \ (apzc)->IsRootContent() ? "root" : "subframe", \ (apzc)->GetScrollId(), ##__VA_ARGS__) +#define APZC_LOGV_DETAIL(fmt, apzc, ...) \ + APZC_LOGV("%p(%s scrollId=%" PRIu64 "): " fmt, (apzc), \ + (apzc)->IsRootContent() ? "root" : "subframe", \ + (apzc)->GetScrollId(), ##__VA_ARGS__) #define APZC_LOG_FM_COMMON(fm, prefix, level, ...) \ if (MOZ_LOG_TEST(sApzCtlLog, level)) { \ @@ -2248,6 +2252,11 @@ CSSRect AsyncPanZoomController::GetCurrentScrollRangeInCssPixels() const { return Metrics().CalculateScrollRange(); } +bool AsyncPanZoomController::AllowOneTouchPinch() const { + return StaticPrefs::apz_one_touch_pinch_enabled() && + ZoomConstraintsAllowZoom(); +} + // Return whether or not the underlying layer can be scrolled on either axis. bool AsyncPanZoomController::CanScroll(const InputData& aEvent) const { ParentLayerPoint delta = GetDeltaForEvent(aEvent); @@ -4734,7 +4743,7 @@ bool AsyncPanZoomController::UpdateAnimation( // Even if there's no animation, if we have a scroll offset change pending due // to the frame delay, we need to keep compositing. if (mLastSampleTime == aSampleTime) { - APZC_LOG_DETAIL( + APZC_LOGV_DETAIL( "UpdateAnimation short-circuit, animation=%p, pending frame-delayed " "offset=%d\n", this, mAnimation.get(), HavePendingFrameDelayedOffset()); @@ -4754,8 +4763,8 @@ bool AsyncPanZoomController::UpdateAnimation( // so that e.g. a main-thread animation can stay in sync with user-driven // scrolling or a compositor animation. bool needComposite = SampleCompositedAsyncTransform(aProofOfLock); - APZC_LOG_DETAIL("UpdateAnimation needComposite=%d mAnimation=%p\n", this, - needComposite, mAnimation.get()); + APZC_LOGV_DETAIL("UpdateAnimation needComposite=%d mAnimation=%p\n", this, + needComposite, mAnimation.get()); TimeDuration sampleTimeDelta = aSampleTime - mLastSampleTime; mLastSampleTime = aSampleTime; @@ -5581,17 +5590,6 @@ void AsyncPanZoomController::NotifyLayersUpdated( aScrollMetadata.GetOverscrollBehavior()); } - if (needToReclampScroll) { - // Whenever scrollable rect or composition bounds has changed, we need to - // re-clamp the scroll offset since it may be out of bounds. Also note that - // we need to re-clamp before updating new scroll offsets from content since - // we will use the last scroll offset to reflect the new offsets. - ClampAndSetVisualScrollOffset(Metrics().GetVisualScrollOffset()); - for (auto& sampledState : mSampledState) { - sampledState.ClampVisualScrollOffset(Metrics()); - } - } - bool instantScrollMayTriggerTransform = false; bool scrollOffsetUpdated = false; bool smoothScrollRequested = false; @@ -5738,20 +5736,11 @@ void AsyncPanZoomController::NotifyLayersUpdated( relativeDelta = Some(Metrics().ApplyPureRelativeScrollUpdateFrom(scrollUpdate)); Metrics().RecalculateLayoutViewportOffset(); - } else if (scrollUpdate.GetType() == ScrollUpdateType::MergeableAbsolute) { - APZC_LOG("%p mergeable updating scroll offset from %s to %s\n", this, - ToString(Metrics().GetVisualScrollOffset()).c_str(), - ToString(scrollUpdate.GetDestination()).c_str()); - relativeDelta = - Some(Metrics().ApplyAbsoluteScrollUpdateFrom(scrollUpdate).second); - Metrics().RecalculateLayoutViewportOffset(); - scrollOffsetUpdated = true; } else { APZC_LOG("%p updating scroll offset from %s to %s\n", this, ToString(Metrics().GetVisualScrollOffset()).c_str(), ToString(scrollUpdate.GetDestination()).c_str()); - auto [offsetChanged, _] = - Metrics().ApplyAbsoluteScrollUpdateFrom(scrollUpdate); + bool offsetChanged = Metrics().ApplyScrollUpdateFrom(scrollUpdate); Metrics().RecalculateLayoutViewportOffset(); if (offsetChanged || scrollUpdate.GetMode() != ScrollMode::Instant || @@ -5788,6 +5777,15 @@ void AsyncPanZoomController::NotifyLayersUpdated( } } + if (aIsFirstPaint || needToReclampScroll) { + // The scrollable rect or composition bounds may have changed in a way that + // makes our local scroll offset out of bounds, so clamp it. + ClampAndSetVisualScrollOffset(Metrics().GetVisualScrollOffset()); + for (auto& sampledState : mSampledState) { + sampledState.ClampVisualScrollOffset(Metrics()); + } + } + if (scrollOffsetUpdated) { for (auto& sampledState : mSampledState) { if (!didCancelAnimation && cumulativeRelativeDelta.isSome()) { diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index fdc72b1971..d0c4537a66 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -1305,6 +1305,8 @@ class AsyncPanZoomController { */ CSSRect GetCurrentScrollRangeInCssPixels() const; + bool AllowOneTouchPinch() const; + private: /** * Advances to the next sample, if there is one, the list of sampled states diff --git a/gfx/layers/apz/src/AutoscrollAnimation.cpp b/gfx/layers/apz/src/AutoscrollAnimation.cpp index 8d4b8fca10..45063ef2b4 100644 --- a/gfx/layers/apz/src/AutoscrollAnimation.cpp +++ b/gfx/layers/apz/src/AutoscrollAnimation.cpp @@ -17,7 +17,7 @@ namespace mozilla { namespace layers { // Helper function for AutoscrollAnimation::DoSample(). -// Basically copied as-is from toolkit/actors/AutoScrollChild.jsm. +// Basically copied as-is from toolkit/actors/AutoScrollChild.sys.mjs. static float Accelerate(ScreenCoord curr, ScreenCoord start) { static const int speed = 12; float val = (curr - start) / speed; diff --git a/gfx/layers/apz/src/GenericScrollAnimation.cpp b/gfx/layers/apz/src/GenericScrollAnimation.cpp index 04ca8c4697..62f19fab35 100644 --- a/gfx/layers/apz/src/GenericScrollAnimation.cpp +++ b/gfx/layers/apz/src/GenericScrollAnimation.cpp @@ -15,6 +15,9 @@ #include "ScrollAnimationMSDPhysics.h" #include "mozilla/StaticPrefs_general.h" +static mozilla::LazyLogModule sApzScrollAnimLog("apz.scrollanimation"); +#define GSA_LOG(...) MOZ_LOG(sApzScrollAnimLog, LogLevel::Debug, (__VA_ARGS__)) + namespace mozilla { namespace layers { @@ -101,6 +104,12 @@ bool GenericScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, // then end the animation early. Note that the initial displacement could be 0 // if the compositor ran very quickly (<1ms) after the animation was created. // When that happens we want to make sure the animation continues. + GSA_LOG( + "Sampling GenericScrollAnimation: time %f finished %d sampledDest %s " + "adjustedOffset %s overscroll %s\n", + (now - TimeStamp::ProcessCreation()).ToMilliseconds(), finished, + ToString(CSSPoint::FromAppUnits(sampledDest)).c_str(), + ToString(adjustedOffset).c_str(), ToString(overscroll).c_str()); if (!IsZero(displacement / zoom) && IsZero(adjustedOffset / zoom)) { // Nothing more to do - end the animation. return false; diff --git a/gfx/layers/apz/src/GestureEventListener.cpp b/gfx/layers/apz/src/GestureEventListener.cpp index b54674b593..4300e9ba41 100644 --- a/gfx/layers/apz/src/GestureEventListener.cpp +++ b/gfx/layers/apz/src/GestureEventListener.cpp @@ -5,13 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GestureEventListener.h" -#include // for max +#include // for max +#include #include // for fabsf #include // for size_t #include "AsyncPanZoomController.h" // for AsyncPanZoomController #include "InputBlockState.h" // for TouchBlockState #include "base/task.h" // for CancelableTask, etc #include "InputBlockState.h" // for TouchBlockState +#include "mozilla/Assertions.h" +#include "mozilla/EventForwards.h" #include "mozilla/StaticPrefs_apz.h" #include "mozilla/StaticPrefs_ui.h" #include "nsDebug.h" // for NS_WARNING @@ -83,8 +86,8 @@ GestureEventListener::~GestureEventListener() = default; nsEventStatus GestureEventListener::HandleInputEvent( const MultiTouchInput& aEvent) { - GEL_LOG("Receiving event type %d with %zu touches in state %d\n", - aEvent.mType, aEvent.mTouches.Length(), mState); + GEL_LOG("Receiving event type %d with %zu touches in state %s\n", + aEvent.mType, aEvent.mTouches.Length(), ToString(mState).c_str()); nsEventStatus rv = nsEventStatus_eIgnore; @@ -175,16 +178,15 @@ nsEventStatus GestureEventListener::HandleInputTouchSingleStart() { EnterFirstSingleTouchDown(); break; case GESTURE_FIRST_SINGLE_TOUCH_UP: + // Bail out of any gesture that includes the first tap. + CancelLongTapTimeoutTask(); + CancelMaxTapTimeoutTask(); if (SecondTapIsFar()) { - // If the second tap goes down far away from the first, then bail out - // of any gesture that includes the first tap. - CancelLongTapTimeoutTask(); - CancelMaxTapTimeoutTask(); - mSingleTapSent = Nothing(); - - // But still allow the second tap to participate in a gesture + // If the second tap goes down far away from the first, + // allow the second tap to participate in a gesture // (e.g. lead to a single tap, or a double tap if an additional // tap occurs near the same location). + mSingleTapSent = Nothing(); EnterFirstSingleTouchDown(); } else { // Otherwise, reset the touch start position so that, if this turns into @@ -225,9 +227,10 @@ nsEventStatus GestureEventListener::HandleInputTouchMultiStart() { rv = nsEventStatus_eConsumeNoDefault; break; case GESTURE_FIRST_SINGLE_TOUCH_UP: - case GESTURE_SECOND_SINGLE_TOUCH_DOWN: // Cancel wait for double tap CancelMaxTapTimeoutTask(); + [[fallthrough]]; + case GESTURE_SECOND_SINGLE_TOUCH_DOWN: MOZ_ASSERT(mSingleTapSent.isSome()); if (!mSingleTapSent.value()) { TriggerSingleTapConfirmedEvent(); @@ -307,12 +310,8 @@ nsEventStatus GestureEventListener::HandleInputTouchMove() { // If touch has moved noticeably (within StaticPrefs::apz_max_tap_time()), // change state. if (MoveDistanceIsLarge()) { - CancelLongTapTimeoutTask(); - CancelMaxTapTimeoutTask(); mSingleTapSent = Nothing(); - if (!StaticPrefs::apz_one_touch_pinch_enabled()) { - // If the one-touch-pinch feature is disabled, bail out of the double- - // tap gesture instead. + if (!mAsyncPanZoomController->AllowOneTouchPinch()) { SetState(GESTURE_NONE); break; } @@ -451,7 +450,6 @@ nsEventStatus GestureEventListener::HandleInputTouchEnd() { } case GESTURE_SECOND_SINGLE_TOUCH_DOWN: { - CancelMaxTapTimeoutTask(); MOZ_ASSERT(mSingleTapSent.isSome()); mAsyncPanZoomController->HandleGestureEvent(CreateTapEvent( mLastTouchInput, mSingleTapSent.value() @@ -535,7 +533,9 @@ nsEventStatus GestureEventListener::HandleInputTouchCancel() { } void GestureEventListener::HandleInputTimeoutLongTap() { - GEL_LOG("Running long-tap timeout task in state %d\n", mState); + MOZ_ASSERT(mState != GESTURE_SECOND_SINGLE_TOUCH_DOWN); + GEL_LOG("Running long-tap timeout task in state %s\n", + ToString(mState).c_str()); mLongTapTimeoutTask = nullptr; @@ -559,14 +559,15 @@ void GestureEventListener::HandleInputTimeoutLongTap() { } void GestureEventListener::HandleInputTimeoutMaxTap(bool aDuringFastFling) { - GEL_LOG("Running max-tap timeout task in state %d\n", mState); + MOZ_ASSERT(mState != GESTURE_SECOND_SINGLE_TOUCH_DOWN); + GEL_LOG("Running max-tap timeout task in state %s\n", + ToString(mState).c_str()); mMaxTapTimeoutTask = nullptr; if (mState == GESTURE_FIRST_SINGLE_TOUCH_DOWN) { SetState(GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN); - } else if (mState == GESTURE_FIRST_SINGLE_TOUCH_UP || - mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) { + } else if (mState == GESTURE_FIRST_SINGLE_TOUCH_UP) { MOZ_ASSERT(mSingleTapSent.isSome()); if (!aDuringFastFling && !mSingleTapSent.value()) { TriggerSingleTapConfirmedEvent(); @@ -585,6 +586,8 @@ void GestureEventListener::TriggerSingleTapConfirmedEvent() { } void GestureEventListener::SetState(GestureState aState) { + GEL_LOG("State change from %s to %s", ToString(mState).c_str(), + ToString(aState).c_str()); mState = aState; if (mState == GESTURE_NONE) { @@ -598,10 +601,7 @@ void GestureEventListener::SetState(GestureState aState) { } void GestureEventListener::CancelLongTapTimeoutTask() { - if (mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) { - // being in this state means the task has been canceled already - return; - } + MOZ_ASSERT(mState != GESTURE_SECOND_SINGLE_TOUCH_DOWN); if (mLongTapTimeoutTask) { mLongTapTimeoutTask->Cancel(); @@ -628,6 +628,8 @@ void GestureEventListener::CreateLongTapTimeoutTask() { } void GestureEventListener::CancelMaxTapTimeoutTask() { + MOZ_ASSERT(mState != GESTURE_SECOND_SINGLE_TOUCH_DOWN); + if (mState == GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN) { // being in this state means the timer has just been triggered return; @@ -659,5 +661,40 @@ void GestureEventListener::CreateMaxTapTimeoutTask() { std::max(0L, remainingDelay)); } +std::ostream& operator<<(std::ostream& os, + GestureEventListener::GestureState aState) { + switch (aState) { + case GestureEventListener::GESTURE_NONE: + os << "GESTURE_NONE"; + break; + case GestureEventListener::GESTURE_FIRST_SINGLE_TOUCH_DOWN: + os << "GESTURE_FIRST_SINGLE_TOUCH_DOWN"; + break; + case GestureEventListener::GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: + os << "GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN"; + break; + case GestureEventListener::GESTURE_FIRST_SINGLE_TOUCH_UP: + os << "GESTURE_FIRST_SINGLE_TOUCH_UP"; + break; + case GestureEventListener::GESTURE_SECOND_SINGLE_TOUCH_DOWN: + os << "GESTURE_SECOND_SINGLE_TOUCH_DOWN"; + break; + case GestureEventListener::GESTURE_LONG_TOUCH_DOWN: + os << "GESTURE_LONG_TOUCH_DOWN"; + break; + case GestureEventListener::GESTURE_MULTI_TOUCH_DOWN: + os << "GESTURE_MULTI_TOUCH_DOWN"; + break; + case GestureEventListener::GESTURE_PINCH: + os << "GESTURE_PINCH"; + break; + case GestureEventListener::GESTURE_ONE_TOUCH_PINCH: + os << "GESTURE_ONE_TOUCH_PINCH"; + break; + } + + return os; +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/apz/src/GestureEventListener.h b/gfx/layers/apz/src/GestureEventListener.h index aa51889fdd..bf50d4f40a 100644 --- a/gfx/layers/apz/src/GestureEventListener.h +++ b/gfx/layers/apz/src/GestureEventListener.h @@ -7,6 +7,7 @@ #ifndef mozilla_layers_GestureEventListener_h #define mozilla_layers_GestureEventListener_h +#include #include "InputData.h" // for MultiTouchInput, etc #include "Units.h" #include "mozilla/EventForwards.h" // for nsEventStatus @@ -135,6 +136,8 @@ class GestureEventListener final { GESTURE_ONE_TOUCH_PINCH }; + friend std::ostream& operator<<(std::ostream& os, GestureState aState); + /** * These HandleInput* functions comprise input alphabet of the GEL * finite-state machine triggering state transitions. diff --git a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp index f244ca4dc7..8256667d6b 100644 --- a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp +++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp @@ -21,12 +21,12 @@ class APZCGestureDetectorTester : public APZCBasicTester { : APZCBasicTester(AsyncPanZoomController::USE_GESTURE_DETECTOR) {} protected: - FrameMetrics GetPinchableFrameMetrics() { + FrameMetrics GetPinchableFrameMetrics(float aZoom = 2.0f) { FrameMetrics fm; fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200)); fm.SetScrollableRect(CSSRect(0, 0, 980, 1000)); fm.SetVisualScrollOffset(CSSPoint(300, 300)); - fm.SetZoom(CSSToParentLayerScale(2.0)); + fm.SetZoom(CSSToParentLayerScale(aZoom)); // APZC only allows zooming on the root scrollable frame. fm.SetIsRootContent(true); // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100 @@ -843,3 +843,235 @@ TEST_F(APZCGestureDetectorTester, LongPressWithInputQueueDelay3) { mcc->AdvanceByMillis(1); check.Call("post long-tap dispatch"); } + +TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureShort) { + // Take less than StaticPrefs::apz_max_tap_time() until second touch down, + // hold second touch down for a very short time, then move + // and expect a successful one touch pinch gesture + SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); + + MakeApzcZoomable(); + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; + + const auto tapResult = + Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); + apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, + {kDefaultTouchBehavior}); + + mcc->AdvanceByMillis(10); + const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); + apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, + {kDefaultTouchBehavior}); + + // We should be able to hold down the second touch as long as we like + // before beginning to move + mcc->AdvanceByMillis(10); + TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; + EXPECT_NE(newZoom, oldZoom); +} + +TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureLong) { + // Take less than StaticPrefs::apz_max_tap_time() until second touch down, + // hold second touch down for a long time, then move + // and expect a successful one touch pinch gesture + SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); + + MakeApzcZoomable(); + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; + + const auto tapResult = + Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); + apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, + {kDefaultTouchBehavior}); + + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); + const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); + apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, + {kDefaultTouchBehavior}); + + // We should be able to hold down the second touch as long as we like + // before beginning to move + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); + TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; + EXPECT_NE(newZoom, oldZoom); +} + +TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureNoMoveTriggersDoubleTap) { + // Take less than StaticPrefs::apz_max_tap_time() until second touch down, + // then wait longer than StaticPrefs::apz_max_tap_time(), lift finger up + // and expect a successful double tap. No zooming should be performed + // by the one-touch pinch codepath. + SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; + + MakeApzcZoomable(); + + EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) + .Times(0); + EXPECT_CALL(*mcc, + HandleTap(TapType::eDoubleTap, _, 0, apzc->GetGuid(), _, _)); + + const auto tapResult = + Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); + apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, + {kDefaultTouchBehavior}); + + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); + const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); + apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, + {kDefaultTouchBehavior}); + + // We should be able to hold down the second touch as long as we like + // before lifting the finger + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); + TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time()); + + const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; + EXPECT_EQ(newZoom, oldZoom); +} + +TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureNonZoomablePage) { + // Use a non-zoomable page. Perform a tap and a touch-drag + // which on a zoomable page trigger a one touch pinch gesture, + // and expect a single tap followed by a touch-scroll + SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics(1.0f)); + const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; + const auto oldScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); + MakeApzcUnzoomable(); + + EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) + .Times(1); + EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, _, 0, apzc->GetGuid(), _, _)) + .Times(0); + + const auto tapResult = + Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); + apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, + {kDefaultTouchBehavior}); + + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); + const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); + apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, + {kDefaultTouchBehavior}); + + // We should be able to hold down the second touch as long as we like + // before beginning to move + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); + TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchMove(apzc, ScreenIntPoint(10, 100), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchUp(apzc, ScreenIntPoint(10, 100), mcc->Time()); + + const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; + EXPECT_EQ(newZoom, oldZoom); + + const auto newScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); + EXPECT_NE(newScrollOffset, oldScrollOffset); +} + +TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureTimeout) { + // Take longer than StaticPrefs::apz_max_tap_time() until second touch down + // and expect no one touch pinch gesture being performed + SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); + + MakeApzcZoomable(); + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; + + EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) + .Times(1); + + const auto tapResult = + Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); + apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, + {kDefaultTouchBehavior}); + + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time()); + const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); + apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, + {kDefaultTouchBehavior}); + + mcc->AdvanceByMillis(10); + TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; + EXPECT_EQ(newZoom, oldZoom); +} + +TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureDisabled) { + // With apz.one_touch_pinch disabled, + // perform one touch pinch gesture within the time threshold, + // and expect no zooming. + SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", false); + + MakeApzcZoomable(); + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; + const auto oldScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); + + // todo: enable following EXPECT_CALLs when fixing bug 1881794 + // EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, + // _)) + // .Times(1); + // EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, _, 0, apzc->GetGuid(), _, + // _)) + // .Times(0); + + const auto tapResult = + Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); + apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, + {kDefaultTouchBehavior}); + + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); + const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); + apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, + {kDefaultTouchBehavior}); + + // We should be able to hold down the second touch as long as we like + // before beginning to move + mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); + TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + mcc->AdvanceByMillis(10); + TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); + + const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; + EXPECT_EQ(newZoom, oldZoom); + + const auto newScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); + EXPECT_NE(newScrollOffset, oldScrollOffset); +} diff --git a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js index c290965845..1642bc147a 100644 --- a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js +++ b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js @@ -498,7 +498,7 @@ async function promiseNativeTouchpadPanEventAndWaitForObserver( return new Promise(resolve => { var observer = { - observe(aSubject, aTopic, aData) { + observe(aSubject, aTopic) { if (aTopic == "touchpadpanevent") { resolve(); } @@ -557,7 +557,7 @@ function promiseNativePanGestureEventAndWaitForObserver( ) { return new Promise(resolve => { var observer = { - observe(aSubject, aTopic, aData) { + observe(aSubject, aTopic) { if (aTopic == "mousescrollevent") { resolve(); } @@ -588,7 +588,7 @@ function promiseNativeWheelAndWaitForObserver( ) { return new Promise(resolve => { var observer = { - observe(aSubject, aTopic, aData) { + observe(aSubject, aTopic) { if (aTopic == "mousescrollevent") { resolve(); } @@ -614,7 +614,7 @@ function promiseNativeWheelAndWaitForWheelEvent( var targetWindow = windowForTarget(aTarget); targetWindow.addEventListener( "wheel", - function (e) { + function () { setTimeout(resolve, 0); }, { once: true } @@ -1566,7 +1566,7 @@ function promiseScrollend(aTarget = window) { function promiseTouchEnd(element, count = 1) { return new Promise(resolve => { var eventCount = 0; - var counterFunction = function (e) { + var counterFunction = function () { eventCount++; if (eventCount == count) { element.removeEventListener("touchend", counterFunction, { @@ -1816,7 +1816,7 @@ async function panRightToLeftUpdate(aElement, aX, aY, aMultiplier) { ); } -async function panRightToLeftEnd(aElement, aX, aY, aMultiplier) { +async function panRightToLeftEnd(aElement, aX, aY) { await NativePanHandler.promiseNativePanEvent( aElement, aX, @@ -1869,7 +1869,7 @@ async function panLeftToRightUpdate(aElement, aX, aY, aMultiplier) { ); } -async function panLeftToRightEnd(aElement, aX, aY, aMultiplier) { +async function panLeftToRightEnd(aElement, aX, aY) { await NativePanHandler.promiseNativePanEvent( aElement, aX, diff --git a/gfx/layers/apz/test/mochitest/apz_test_utils.js b/gfx/layers/apz/test/mochitest/apz_test_utils.js index 821c66103d..cdae7824b2 100644 --- a/gfx/layers/apz/test/mochitest/apz_test_utils.js +++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js @@ -309,7 +309,7 @@ function promiseAfterPaint() { // occurred by the the returned promise resolves. If you want to wait // for those repaints, consider using promiseApzFlushedRepaints instead. function promiseOnlyApzControllerFlushedWithoutSetTimeout(aWindow = window) { - return new Promise(function (resolve, reject) { + return new Promise(function (resolve) { var repaintDone = function () { dump("PromiseApzRepaintsFlushed: APZ flush done\n"); SpecialPowers.Services.obs.removeObserver( @@ -518,7 +518,7 @@ function runSubtestsSeriallyInFreshWindows(aSubtests) { if (test.onload) { w.addEventListener( "load", - function (e) { + function () { test.onload(w); }, { once: true } diff --git a/gfx/layers/apz/test/mochitest/browser.toml b/gfx/layers/apz/test/mochitest/browser.toml index 5432aa0ae1..67428eee23 100644 --- a/gfx/layers/apz/test/mochitest/browser.toml +++ b/gfx/layers/apz/test/mochitest/browser.toml @@ -19,7 +19,7 @@ support-files = ["helper_test_autoscrolling_in_oop_frame.html"] support-files = ["helper_background_tab_load_scroll.html"] ["browser_test_background_tab_scroll.js"] -skip-if = ["toolkit == 'android'"] # wheel events not supported on mobile +skip-if = ["os == 'android'"] # wheel events not supported on mobile support-files = ["helper_background_tab_scroll.html"] ["browser_test_content_response_timeout.js"] @@ -28,7 +28,7 @@ support-files = ["helper_content_response_timeout.html"] ["browser_test_group_fission.js"] skip-if = [ "win11_2009 && bits == 32", # intermittent failures on on win11/32 - "os == 'linux' && bits == 64", # Bug 1773830 + "os == 'linux' && os_version == '18.04' && bits == 64", # Bug 1773830 ] support-files = [ "FissionTestHelperParent.sys.mjs", @@ -49,7 +49,7 @@ support-files = ["helper_scroll_thumb_dragging.html"] ["browser_test_scrollbar_in_extension_popup_window.js"] skip-if = [ "verify", - "os == 'linux'" # Bug 1713052 + "os == 'linux' && os_version == '18.04'" # Bug 1713052 ] ["browser_test_scrolling_in_extension_popup_window.js"] @@ -70,7 +70,7 @@ skip-if = ["os == 'win'"] # bug 1495580 support-files = ["helper_test_select_zoom.html"] ["browser_test_tab_drag_event_counts.js"] -skip-if = ["os == 'linux'"] # No native key event support on Linux at this time (bug 1770143) +skip-if = ["os == 'linux' && os_version == '18.04'"] # No native key event support on Linux at this time (bug 1770143) support-files = [ "helper_test_tab_drag_event_counts.html" ] diff --git a/gfx/layers/apz/test/mochitest/browser_test_autoscrolling_in_oop_frame.js b/gfx/layers/apz/test/mochitest/browser_test_autoscrolling_in_oop_frame.js index 26d0ff6109..f7f83575f2 100644 --- a/gfx/layers/apz/test/mochitest/browser_test_autoscrolling_in_oop_frame.js +++ b/gfx/layers/apz/test/mochitest/browser_test_autoscrolling_in_oop_frame.js @@ -74,7 +74,7 @@ async function doTest() { return new Promise(resolve => { content.addEventListener( "scroll", - event => { + () => { dump("Got a scroll event in the iframe\n"); resolve(); }, diff --git a/gfx/layers/apz/test/mochitest/browser_test_content_response_timeout.js b/gfx/layers/apz/test/mochitest/browser_test_content_response_timeout.js index a80fd77c17..98f6ea6c29 100644 --- a/gfx/layers/apz/test/mochitest/browser_test_content_response_timeout.js +++ b/gfx/layers/apz/test/mochitest/browser_test_content_response_timeout.js @@ -60,6 +60,7 @@ add_task(async () => { ); await new Promise(resolve => { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout setTimeout(resolve, 200); }); diff --git a/gfx/layers/apz/test/mochitest/browser_test_scrolling_in_extension_popup_window.js b/gfx/layers/apz/test/mochitest/browser_test_scrolling_in_extension_popup_window.js index 6da3f3311b..9896e628e3 100644 --- a/gfx/layers/apz/test/mochitest/browser_test_scrolling_in_extension_popup_window.js +++ b/gfx/layers/apz/test/mochitest/browser_test_scrolling_in_extension_popup_window.js @@ -98,7 +98,7 @@ add_task(async () => { return new Promise(resolve => { content.window.addEventListener( "scroll", - event => { + () => { dump("Got a scroll event in the popup content document\n"); resolve(); }, diff --git a/gfx/layers/apz/test/mochitest/helper_basic_scrollend.html b/gfx/layers/apz/test/mochitest/helper_basic_scrollend.html index 9d71fe6251..6c31859690 100644 --- a/gfx/layers/apz/test/mochitest/helper_basic_scrollend.html +++ b/gfx/layers/apz/test/mochitest/helper_basic_scrollend.html @@ -19,7 +19,7 @@ const searchParams = new URLSearchParams(location.search); async function test() { var scrollendCount = 0; - function onScrollend(e) { + function onScrollend() { scrollendCount += 1; } diff --git a/gfx/layers/apz/test/mochitest/helper_browser_test_utils.js b/gfx/layers/apz/test/mochitest/helper_browser_test_utils.js index ac68c9b1d4..22a072e48d 100644 --- a/gfx/layers/apz/test/mochitest/helper_browser_test_utils.js +++ b/gfx/layers/apz/test/mochitest/helper_browser_test_utils.js @@ -4,7 +4,7 @@ Services.scriptloader.loadSubScript( this ); -function openSelectPopup(selector = "select", win = window) { +function openSelectPopup(selector, win = window) { let popupShownPromise = BrowserTestUtils.waitForSelectPopupShown(win); EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }, win); return popupShownPromise; diff --git a/gfx/layers/apz/test/mochitest/helper_bug1346632.html b/gfx/layers/apz/test/mochitest/helper_bug1346632.html index f91f8159b5..c7f92c3810 100644 --- a/gfx/layers/apz/test/mochitest/helper_bug1346632.html +++ b/gfx/layers/apz/test/mochitest/helper_bug1346632.html @@ -28,7 +28,7 @@ async function test() { var root = document.scrollingElement; var scrollPos = root.scrollTop; - var scrollPromise = new Promise((resolve, reject) => { + var scrollPromise = new Promise((resolve) => { document.addEventListener("scroll", () => { ok(root.scrollTop > scrollPos, "document scrolled after dragging scrollbar"); resolve(); diff --git a/gfx/layers/apz/test/mochitest/helper_bug1414336.html b/gfx/layers/apz/test/mochitest/helper_bug1414336.html index 636328b7e4..c0d35db401 100644 --- a/gfx/layers/apz/test/mochitest/helper_bug1414336.html +++ b/gfx/layers/apz/test/mochitest/helper_bug1414336.html @@ -62,14 +62,14 @@ waitUntilApzStable().then(async () => { let target0 = window.document.getElementById("target0"); let target0_events = ["pointerdown", "pointermove"]; - target0_events.forEach((elem, index, arr) => { + target0_events.forEach((elem) => { target0.addEventListener(elem, (event) => { is(event.type, target0_events[0], "receive " + event.type + " on target0"); target0_events.shift(); }, { once: true }); }); - target0.addEventListener("pointercancel", (event) => { + target0.addEventListener("pointercancel", () => { ok(false, "Shouldn't receive pointercancel when content prevents default on touchstart"); // Wait until the event is done processing before we end the subtest, // otherwise on Android the pointer events pref is flipped back to false @@ -81,7 +81,7 @@ waitUntilApzStable().then(async () => { event.preventDefault(); }, { once: true }); - target0.addEventListener("pointerup", (event) => { + target0.addEventListener("pointerup", () => { ok(!target0_events.length, " should receive " + target0_events + " on target0"); // Wait until the event is done processing before we end the subtest, // otherwise on Android the pointer events pref is flipped back to false diff --git a/gfx/layers/apz/test/mochitest/helper_bug1502010_unconsumed_pan.html b/gfx/layers/apz/test/mochitest/helper_bug1502010_unconsumed_pan.html index 73badf4bc7..8534e8886b 100644 --- a/gfx/layers/apz/test/mochitest/helper_bug1502010_unconsumed_pan.html +++ b/gfx/layers/apz/test/mochitest/helper_bug1502010_unconsumed_pan.html @@ -38,7 +38,7 @@ async function test() { var target = document.getElementById("carousel"); - target.addEventListener("pointercancel", (event) => { + target.addEventListener("pointercancel", () => { ok(false, "Received pointercancel, uh-oh!"); endEventReceived = true; setTimeout(checkForTestEnd, 0); diff --git a/gfx/layers/apz/test/mochitest/helper_bug1506497_touch_action_fixed_on_fixed.html b/gfx/layers/apz/test/mochitest/helper_bug1506497_touch_action_fixed_on_fixed.html index cc73fe99ea..b853bde04d 100644 --- a/gfx/layers/apz/test/mochitest/helper_bug1506497_touch_action_fixed_on_fixed.html +++ b/gfx/layers/apz/test/mochitest/helper_bug1506497_touch_action_fixed_on_fixed.html @@ -11,7 +11,7 @@