diff options
Diffstat (limited to 'gfx/layers')
83 files changed, 1131 insertions, 365 deletions
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 <string.h> +#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<ShmemAndHandle> 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<Helpers> 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<CanvasDrawEventRecorder>&& 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<CanvasDrawEventRecorder>&& aRecorder) + : dom::WorkerRunnable(aWorkerPrivate), + mRecorder(std::move(aRecorder)) {} + + bool WorkerRun(JSContext*, dom::WorkerPrivate*) override { + RefPtr<CanvasDrawEventRecorder> recorder = std::move(mRecorder); + recorder->ProcessPendingDeletions(); + return true; + } + + private: + RefPtr<CanvasDrawEventRecorder> mRecorder; + }; + + auto task = MakeRefPtr<ProcessPendingRunnable>(mWorkerRef->Private(), + std::move(aRecorder)); + if (NS_WARN_IF(!task->Dispatch())) { + MOZ_CRASH("ProcessPendingRunnable leaked!"); + } +} + +void CanvasDrawEventRecorder::QueueProcessPendingDeletions( + RefPtr<CanvasDrawEventRecorder>&& 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<void()>&& 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<CanvasDrawEventRecorder> 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<Handle>&& aBufferHandles, @@ -89,8 +95,8 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, virtual bool RestartReader() = 0; }; - bool Init(TextureType aTextureType, gfx::BackendType aBackendType, - UniquePtr<Helpers> aHelpers); + bool Init(TextureType aTextureType, TextureType aWebglTextureType, + gfx::BackendType aBackendType, UniquePtr<Helpers> 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<void()>&& 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<CanvasDrawEventRecorder>&& aRecorder); + void QueueProcessPendingDeletionsLocked( + RefPtr<CanvasDrawEventRecorder>&& aRecorder); + size_t mDefaultBufferSize; size_t mMaxDefaultBuffers; uint32_t mMaxSpinCount; @@ -173,6 +188,9 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, UniquePtr<CrossProcessSemaphore> mWriterSemaphore; UniquePtr<CrossProcessSemaphore> mReaderSemaphore; + + RefPtr<dom::ThreadSafeWorkerRef> 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<bool, CSSPoint> 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<bool, CSSPoint> 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<bool, CSSPoint> 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<ScrollPositionUpdate>& 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<MacIOSurfaceRecycleAllocator> 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<mozilla::layers::LayersBackend, - mozilla::layers::LayersBackend::LAYERS_LAST, - UniquePtr<ImageBackendData>> + UniquePtr<ImageBackendData>, + size_t(mozilla::layers::LayersBackend::LAYERS_LAST)> mBackendData; void* mImplData; @@ -530,7 +530,7 @@ class ImageContainer final : public SupportsThreadSafeWeakPtr<ImageContainer> { KnowsCompositor* aKnowsCompositor); #endif -#ifdef XP_MACOSX +#ifdef XP_DARWIN already_AddRefed<MacIOSurfaceRecycleAllocator> GetMacIOSurfaceRecycleAllocator(); #endif @@ -618,7 +618,7 @@ class ImageContainer final : public SupportsThreadSafeWeakPtr<ImageContainer> { RefPtr<D3D11YCbCrRecycleAllocator> mD3D11YCbCrRecycleAllocator MOZ_GUARDED_BY(mRecursiveMutex); #endif -#ifdef XP_MACOSX +#ifdef XP_DARWIN RefPtr<MacIOSurfaceRecycleAllocator> 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 <IOSurface/IOSurface.h> +#include <IOSurface/IOSurfaceRef.h> #include <deque> #include <unordered_map> @@ -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<RefPtr<NativeLayerCA>>& aSublayers); + const nsTArray<RefPtr<NativeLayerCA>>& 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<RefPtr<NativeLayerCA>> 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<NativeLayerRootSnapshotterCA> Create( @@ -217,6 +225,7 @@ class NativeLayerRootSnapshotterCA final : public NativeLayerRootSnapshotter { RefPtr<RenderSourceNLRS> 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<SurfaceWithInvalidRegion> 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<CGColorRef> 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 <AppKit/NSAnimationContext.h> -#import <AppKit/NSColor.h> +#ifdef XP_MACOSX +# import <AppKit/NSAnimationContext.h> +# import <AppKit/NSColor.h> +# import <OpenGL/gl.h> +#endif #import <AVFoundation/AVFoundation.h> -#import <OpenGL/gl.h> #import <QuartzCore/QuartzCore.h> #include <algorithm> @@ -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<Telemetry::LABELS_GFX_MACOS_VIDEO_LOW_POWER> 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<NativeLayerRootCA> @@ -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<NativeLayerCA> 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<NativeLayerRootSnapshotter> NativeLayerRootCA::CreateSnapshotter() { +#ifdef XP_MACOSX MutexAutoLock lock(mMutex); MOZ_RELEASE_ASSERT(!mWeakSnapshotter, "No NativeLayerRootSnapshotter for this NativeLayerRoot " @@ -358,18 +379,24 @@ UniquePtr<NativeLayerRootSnapshotter> 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 <typename F> @@ -394,7 +421,8 @@ NativeLayerRootCA::Representation::~Representation() { void NativeLayerRootCA::Representation::Commit( WhichRepresentation aRepresentation, - const nsTArray<RefPtr<NativeLayerCA>>& aSublayers) { + const nsTArray<RefPtr<NativeLayerCA>>& 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> 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<GLContext>&& aGL, CALayer* aRootCALayer) @@ -779,6 +817,7 @@ NativeLayerRootSnapshotterCA::CreateAsyncReadbackBuffer(const IntSize& aSize) { LOCAL_GL_STREAM_READ); return MakeAndAddRef<AsyncReadbackBufferNLRS>(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<TextureDataHolder> 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<void(const RemoteTextureInfo&)>&& aReadyCallback, - bool aWaitForRemoteTextureOwner) { + std::function<void(const RemoteTextureInfo&)>&& 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<RenderingReadyCallbackHolder>( 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<void(const RemoteTextureInfo&)>&& aReadyCallback, - bool aWaitForRemoteTextureOwner = false); + std::function<void(const RemoteTextureInfo&)>&& aReadyCallback); bool WaitForTxn(const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, RemoteTextureTxnType aTxnType, @@ -403,7 +404,8 @@ class RemoteTextureMap { std::deque<UniquePtr<RenderingReadyCallbackHolder>> 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<SurfacePool> 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 <IOSurface/IOSurface.h> +#include <IOSurface/IOSurfaceRef.h> #include <deque> #include <unordered_map> 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 <CoreVideo/CVPixelBuffer.h> +#include <IOSurface/IOSurfaceTypes.h> #include <algorithm> #include <unordered_set> @@ -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> SurfacePool::Create(size_t aPoolSizeLimit) { return new SurfacePoolCA(aPoolSizeLimit); @@ -306,8 +316,13 @@ Maybe<GLuint> SurfacePoolCA::LockedPool::GetFramebufferForSurface( "Framebuffer creation", GRAPHICS_TileAllocation, nsPrintfCString("%dx%d", entry.mSize.width, entry.mSize.height)); +#ifdef XP_MACOSX RefPtr<GLContextCGL> cgl = GLContextCGL::Cast(aGL); MOZ_RELEASE_ASSERT(cgl, "Unexpected GLContext type"); +#else + RefPtr<GLContextEAGL> 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<GLuint> 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<EventRegionsOverride> 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<TouchBehaviorFlags>& aValues) { if (!APZThreadUtils::IsControllerThread()) { APZThreadUtils::RunOnControllerThread( - NewRunnableMethod<uint64_t, - StoreCopyPassByLRef<nsTArray<TouchBehaviorFlags>>>( + NewRunnableMethod<uint64_t, nsTArray<TouchBehaviorFlags>>( "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<APZTestData> ptr = MakeUnique<APZTestData>(); 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<VsyncId>& 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<TouchBehaviorFlags> 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 <algorithm> // for max +#include <algorithm> // for max +#include <ostream> #include <math.h> // for fabsf #include <stddef.h> // 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 <iosfwd> #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 @@ <script type="application/javascript"> async function test() { - document.getElementById("overlay").addEventListener("touchstart", function(e) { + document.getElementById("overlay").addEventListener("touchstart", function() { // no need to do anything here. Just having a non-passive touchstart // listener will force APZ to wait for the main thread to handle the // touch event. The bug is that the touch-action:none property on the @@ -34,7 +34,7 @@ async function test() { // This promise will resolve after the main thread has processed // all the synthesized touch events. let promiseTouchEnd = new Promise(resolve => { - var waitForTouchEnd = function(e) { + var waitForTouchEnd = function() { dump("touchend listener hit\n"); resolve(); }; diff --git a/gfx/layers/apz/test/mochitest/helper_bug1695598.html b/gfx/layers/apz/test/mochitest/helper_bug1695598.html index fb6102e33d..7961d7ced4 100644 --- a/gfx/layers/apz/test/mochitest/helper_bug1695598.html +++ b/gfx/layers/apz/test/mochitest/helper_bug1695598.html @@ -19,7 +19,7 @@ let utils = SpecialPowers.getDOMWindowUtils(window); let timeStamp = document.timeline.currentTime; - async function sendScrollEvent(aRafTimestamp) { + async function sendScrollEvent() { if (i < scrollEvents) { if (timeStamp == document.timeline.currentTime) { // If we are in a rAF callback at the same time stamp we've already diff --git a/gfx/layers/apz/test/mochitest/helper_content_response_timeout.html b/gfx/layers/apz/test/mochitest/helper_content_response_timeout.html index 41a0319699..c0481d01a4 100644 --- a/gfx/layers/apz/test/mochitest/helper_content_response_timeout.html +++ b/gfx/layers/apz/test/mochitest/helper_content_response_timeout.html @@ -11,7 +11,7 @@ div { height: 1000vh; } </style> <div id='target'></div> <script> -window.addEventListener('wheel', (e) => { +window.addEventListener('wheel', () => { const timeAtStart = window.performance.now(); while (window.performance.now() - timeAtStart < 200) { // Make a 200ms busy state. diff --git a/gfx/layers/apz/test/mochitest/helper_displayport_expiry.html b/gfx/layers/apz/test/mochitest/helper_displayport_expiry.html index 023786a270..b463e4f39c 100644 --- a/gfx/layers/apz/test/mochitest/helper_displayport_expiry.html +++ b/gfx/layers/apz/test/mochitest/helper_displayport_expiry.html @@ -53,7 +53,7 @@ async function test() { await promiseFrame(); let paintCount = 0; - function countPaints(e) { + function countPaints() { paintCount += 1; } diff --git a/gfx/layers/apz/test/mochitest/helper_fission_event_region_override.html b/gfx/layers/apz/test/mochitest/helper_fission_event_region_override.html index 82f529bebd..8ebfc492f2 100644 --- a/gfx/layers/apz/test/mochitest/helper_fission_event_region_override.html +++ b/gfx/layers/apz/test/mochitest/helper_fission_event_region_override.html @@ -29,7 +29,7 @@ let code_for_oopif_to_run = function() { let result = { x: e.clientX, y: e.clientY }; FissionTestHelper.fireEventInEmbedder("OOPIF:WheelData", result); }, { passive: true }); - document.addEventListener("scroll", function(e) { + document.addEventListener("scroll", function() { dump(`OOPIF got scroll to ${window.scrollX},${window.scrollY}\n`); let result = { x: window.scrollX, y: window.scrollY }; FissionTestHelper.fireEventInEmbedder("OOPIF:Scrolled", result); diff --git a/gfx/layers/apz/test/mochitest/helper_fission_scroll_oopif.html b/gfx/layers/apz/test/mochitest/helper_fission_scroll_oopif.html index 2911b1eaf0..5a192c9f9e 100644 --- a/gfx/layers/apz/test/mochitest/helper_fission_scroll_oopif.html +++ b/gfx/layers/apz/test/mochitest/helper_fission_scroll_oopif.html @@ -66,7 +66,7 @@ let make_oopif_scrollable = function() { FissionTestHelper.fireEventInEmbedder("OOPIF:Scrollable", result); }); // Also register a scroll listener for when it actually gets scrolled. - window.addEventListener("scroll", function(e) { + window.addEventListener("scroll", function() { dump(`OOPIF got scroll event, now at ${window.scrollY}\n`); let result = { y: window.scrollY }; FissionTestHelper.fireEventInEmbedder("OOPIF:Scrolled", result); diff --git a/gfx/layers/apz/test/mochitest/helper_fullscreen.html b/gfx/layers/apz/test/mochitest/helper_fullscreen.html index 32de4979f2..7ac443a1cc 100644 --- a/gfx/layers/apz/test/mochitest/helper_fullscreen.html +++ b/gfx/layers/apz/test/mochitest/helper_fullscreen.html @@ -27,7 +27,7 @@ }); } - async function test(testDriver) { + async function test() { target.requestFullscreen(); await waitForFullscreenChange(); diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_deep_scene_stack.html b/gfx/layers/apz/test/mochitest/helper_hittest_deep_scene_stack.html index a04b1d3e83..30a9740d24 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_deep_scene_stack.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_deep_scene_stack.html @@ -38,7 +38,7 @@ for (var i = 3; i < 1000; i++) { } document.body.appendChild(div); -async function test(testDriver) { +async function test() { var config = getHitTestConfig(); var utils = config.utils; diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_fixed-2.html b/gfx/layers/apz/test/mochitest/helper_hittest_fixed-2.html index 0f20719d46..b83b864aa2 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_fixed-2.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_fixed-2.html @@ -45,7 +45,7 @@ async function test() { e.stopPropagation(); // do not propagate event to |fixed| ancestor resolve(); }); - fixed.addEventListener("click", e => { + fixed.addEventListener("click", () => { // Since target's listener calls stopPropagation(), if we get here // then the coordinates of the click event did not correspond to // |target|, but somewhere else on |fixed|. diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_fixed-3.html b/gfx/layers/apz/test/mochitest/helper_hittest_fixed-3.html index 2004ea9ae4..092c5b0dfa 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_fixed-3.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_fixed-3.html @@ -72,7 +72,7 @@ async function test() { e.stopPropagation(); // do not propagate event to ancestors resolve(); }); - fixed.addEventListener("click", e => { + fixed.addEventListener("click", () => { // Since target's listener calls stopPropagation(), if we get here // then the coordinates of the click event did not correspond to // |target|, but somewhere else on |fixed|. @@ -82,7 +82,7 @@ async function test() { todo(false, "Fixed ancestor should not be hit"); resolve(); }); - window.addEventListener("click", e => { + window.addEventListener("click", () => { // Similarly, the root content document's window should not be hit. ok(false, "Root document should not be hit"); resolve(); diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_fixed.html b/gfx/layers/apz/test/mochitest/helper_hittest_fixed.html index 530c53fd7a..3185d83bf5 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_fixed.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_fixed.html @@ -53,7 +53,7 @@ async function test() { e.stopPropagation(); // do not propagate event to |fixed| ancestor resolve(); }); - fixed.addEventListener("click", e => { + fixed.addEventListener("click", () => { // Since target's listener calls stopPropagation(), if we get here // then the coordinates of the click event did not correspond to // |target|, but somewhere else on |fixed|. diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_hidden_inactive_scrollframe.html b/gfx/layers/apz/test/mochitest/helper_hittest_hidden_inactive_scrollframe.html index 0abed82156..6d9ea7bc53 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_hidden_inactive_scrollframe.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_hidden_inactive_scrollframe.html @@ -26,7 +26,7 @@ </body> <script type="application/javascript"> -function test(testDriver) { +function test() { var config = getHitTestConfig(); var utils = config.utils; diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-2.html b/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-2.html index 9838a02aa9..b6128aaa87 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-2.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-2.html @@ -44,7 +44,7 @@ async function test() { } }); - window.addEventListener("mousedown", event => { + window.addEventListener("mousedown", () => { ok(true, "Parent document should have received mouse-down"); resolve(); }); diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_overscroll.html b/gfx/layers/apz/test/mochitest/helper_hittest_overscroll.html index c245258b68..c33dff3f28 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_overscroll.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_overscroll.html @@ -63,7 +63,7 @@ function startListeningForClickEventsInChrome() { topWin = Services.wm.getMostRecentWindow("navigator:geckoview"); } let chromeReceivedClick = false; - function chromeListener(e) { + function chromeListener() { chromeReceivedClick = true; } topWin.addEventListener("click", chromeListener); diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_contextmenu.html b/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_contextmenu.html index 8aff3103dd..0bb3972c8a 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_contextmenu.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_contextmenu.html @@ -43,7 +43,7 @@ function startListeningForContextmenuEventsInChrome() { topWin = Services.wm.getMostRecentWindow("navigator:geckoview"); } let chromeReceivedContextmenu = false; - function chromeListener(e) { + function chromeListener() { chromeReceivedContextmenu = true; } topWin.addEventListener("contextmenu", chromeListener); @@ -95,7 +95,7 @@ async function test() { let midGutter = 4 / deviceScale; // gutter is 8 *screen* pixels startListeningForContextmenuEventsInChrome(); let contentReceivedContextmenu = false; - let contentListener = function(e) { + let contentListener = function() { contentReceivedContextmenu = true; }; document.addEventListener("contextmenu", contentListener); diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html b/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html index 36918b3682..ff97c34bd5 100644 --- a/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html +++ b/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html @@ -84,7 +84,7 @@ async function test() { // This makes sure we catch the case where the overscroll transform causes // the event to incorrectly target the document. let receivedClick = false; - let listener = function(e) { + let listener = function() { receivedClick = true; }; document.addEventListener("click", listener); diff --git a/gfx/layers/apz/test/mochitest/helper_key_scroll.html b/gfx/layers/apz/test/mochitest/helper_key_scroll.html index 021e2803b7..4f574abd10 100644 --- a/gfx/layers/apz/test/mochitest/helper_key_scroll.html +++ b/gfx/layers/apz/test/mochitest/helper_key_scroll.html @@ -32,7 +32,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1383365 // page. This scroll is done synchronously because APZ doesn't have // current focus state at page load. let scrollBottomPromise = new Promise(resolve => { - let checkBottom = function(e) { + let checkBottom = function() { if (window.scrollY < window.scrollMaxY) { return; } @@ -63,7 +63,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1383365 // This scroll should be asynchronous now that the focus state is up to date. let scrollTopPromise = new Promise(resolve => { - let checkTop = function(e) { + let checkTop = function() { if (window.scrollY > 0) { return; } diff --git a/gfx/layers/apz/test/mochitest/helper_main_thread_smooth_scroll_scrollend.html b/gfx/layers/apz/test/mochitest/helper_main_thread_smooth_scroll_scrollend.html index 4f07db516e..c4a98ec7fa 100644 --- a/gfx/layers/apz/test/mochitest/helper_main_thread_smooth_scroll_scrollend.html +++ b/gfx/layers/apz/test/mochitest/helper_main_thread_smooth_scroll_scrollend.html @@ -25,7 +25,7 @@ async function test() { let scrollendCount = 0; - window.addEventListener("scrollend", e => { + window.addEventListener("scrollend", () => { scrollendCount += 1; }); diff --git a/gfx/layers/apz/test/mochitest/helper_minimum_scale_1_0.html b/gfx/layers/apz/test/mochitest/helper_minimum_scale_1_0.html index 17ccb3a54d..9c98ecf727 100644 --- a/gfx/layers/apz/test/mochitest/helper_minimum_scale_1_0.html +++ b/gfx/layers/apz/test/mochitest/helper_minimum_scale_1_0.html @@ -22,7 +22,7 @@ <script type="application/javascript"> const utils = SpecialPowers.getDOMWindowUtils(window); - async function test(testDriver) { + async function test() { utils.scrollToVisual(100, 0, utils.UPDATE_TYPE_MAIN_THREAD, utils.SCROLL_MODE_INSTANT); diff --git a/gfx/layers/apz/test/mochitest/helper_no_scalable_with_initial_scale.html b/gfx/layers/apz/test/mochitest/helper_no_scalable_with_initial_scale.html index 7280a26006..1c26fafdb7 100644 --- a/gfx/layers/apz/test/mochitest/helper_no_scalable_with_initial_scale.html +++ b/gfx/layers/apz/test/mochitest/helper_no_scalable_with_initial_scale.html @@ -22,7 +22,7 @@ <script type="application/javascript"> const utils = SpecialPowers.getDOMWindowUtils(window); - async function test(testDriver) { + async function test() { utils.scrollToVisual(100, 0, utils.UPDATE_TYPE_MAIN_THREAD, utils.SCROLL_MODE_INSTANT); diff --git a/gfx/layers/apz/test/mochitest/helper_programmatic_scroll_behavior.html b/gfx/layers/apz/test/mochitest/helper_programmatic_scroll_behavior.html index 721ce7e538..755c4115e2 100644 --- a/gfx/layers/apz/test/mochitest/helper_programmatic_scroll_behavior.html +++ b/gfx/layers/apz/test/mochitest/helper_programmatic_scroll_behavior.html @@ -35,7 +35,7 @@ async function test() { // trigger one scroll event, so a scroll event count of 1 indicates that a // instant scroll was conducted. let scrollCount = 0; - window.addEventListener("scroll", (e) => { + window.addEventListener("scroll", () => { scrollCount += 1; }); diff --git a/gfx/layers/apz/test/mochitest/helper_touch_action_regions.html b/gfx/layers/apz/test/mochitest/helper_touch_action_regions.html index 6a8a09e55a..14e800a561 100644 --- a/gfx/layers/apz/test/mochitest/helper_touch_action_regions.html +++ b/gfx/layers/apz/test/mochitest/helper_touch_action_regions.html @@ -293,7 +293,7 @@ function* test(testDriver) { // waitUntilApzStable().then(runContinuation(myTest)); function runContinuation(testFunction) { return function() { - return new Promise(function(resolve, reject) { + return new Promise(function(resolve) { var testContinuation = null; function driveTest() { diff --git a/gfx/layers/apz/test/mochitest/helper_zoomed_pan.html b/gfx/layers/apz/test/mochitest/helper_zoomed_pan.html index 98547fb73f..0692c2f588 100644 --- a/gfx/layers/apz/test/mochitest/helper_zoomed_pan.html +++ b/gfx/layers/apz/test/mochitest/helper_zoomed_pan.html @@ -42,7 +42,7 @@ x: 0, y: 0, dx: (width) => -computeDelta(width), - dy: (height) => 0, + dy: () => 0, expected: { x: [OFFSET_CSS_PX, "x-offset was adjusted"], y: [0, "y-offset was not affected"], @@ -51,7 +51,7 @@ { x: OFFSET_CSS_PX, y: 0, - dx: (width) => 0, + dx: () => 0, dy: (height) => -computeDelta(height), expected: { x: [OFFSET_CSS_PX, "x-offset was not affected"], diff --git a/gfx/layers/apz/test/mochitest/test_smoothness.html b/gfx/layers/apz/test/mochitest/test_smoothness.html index 64cb8bcefa..0f2ca3219e 100644 --- a/gfx/layers/apz/test/mochitest/test_smoothness.html +++ b/gfx/layers/apz/test/mochitest/test_smoothness.html @@ -23,7 +23,7 @@ SimpleTest.waitForExplicitFinish(); var utils = SpecialPowers.getDOMWindowUtils(window); - async function sendScrollEvent(aRafTimestamp) { + async function sendScrollEvent() { var scrollDiv = document.getElementById("content"); if (i < scrollEvents) { diff --git a/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html b/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html index 71147d5238..0598f1a201 100644 --- a/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html +++ b/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html @@ -27,7 +27,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1203140 const kResponseTimeoutMs = 2 * 60 * 1000; // 2 minutes -function takeSnapshots(e) { +function takeSnapshots() { // Grab some snapshots, and make sure some of them are different (i.e. check // the page is scrolling in the compositor, concurrently with this wheel // listener running). @@ -69,7 +69,7 @@ async function test() { // Ensure the div is layerized by scrolling it await promiseMoveMouseAndScrollWheelOver(box, 10, 10); - box.addEventListener("touchstart", function(e) { + box.addEventListener("touchstart", function() { ok(false, "This should never be run"); }); box.addEventListener("wheel", takeSnapshots, { capture: false, passive: true }); diff --git a/gfx/layers/apz/test/reftest/reftest.list b/gfx/layers/apz/test/reftest/reftest.list index 77bfff3e6e..9af67384a8 100644 --- a/gfx/layers/apz/test/reftest/reftest.list +++ b/gfx/layers/apz/test/reftest/reftest.list @@ -1,14 +1,14 @@ # The following tests test the async positioning of the scrollbars. # Basic root-frame scrollbar with async scrolling # First make sure that we are actually drawing scrollbars -skip-if(!asyncPan) pref(apz.allow_zooming,true) != async-scrollbar-1-v.html about:blank -skip-if(!asyncPan) pref(apz.allow_zooming,true) != async-scrollbar-1-v-ref.html about:blank -fuzzy-if(Android,0-5,0-6) fuzzy-if(gtkWidget,1-8,8-32) fuzzy-if(cocoaWidget,16-22,20-44) skip-if(!asyncPan) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html -fuzzy-if(Android,0-13,0-10) fuzzy-if(gtkWidget,1-30,4-32) fuzzy-if(cocoaWidget,14-22,20-44) skip-if(!asyncPan) pref(apz.allow_zooming,true) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html -fuzzy-if(Android,0-13,0-21) fuzzy-if(gtkWidget,1-4,4-24) fuzzy-if(cocoaWidget,11-18,44-88) skip-if(!asyncPan) pref(apz.allow_zooming,true) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html -fuzzy-if(Android,0-5,0-6) fuzzy-if(gtkWidget,1-8,8-32) fuzzy-if(cocoaWidget,16-22,20-44) skip-if(!asyncPan) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html -fuzzy-if(Android,0-14,0-10) fuzzy-if(gtkWidget,1-30,12-32) fuzzy-if(cocoaWidget,14-22,20-44) skip-if(!asyncPan) pref(apz.allow_zooming,true) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html -fuzzy-if(Android,0-43,0-26) fuzzy-if(gtkWidget,0-14,12-32) fuzzy-if(cocoaWidget,11-18,26-76) skip-if(!asyncPan) pref(apz.allow_zooming,true) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html +skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) != async-scrollbar-1-v.html about:blank +skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) != async-scrollbar-1-v-ref.html about:blank +fuzzy-if(Android,0-5,0-6) fuzzy-if(gtkWidget,1-8,8-32) fuzzy-if(cocoaWidget,16-22,20-44) skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html +fuzzy-if(Android,0-13,0-10) fuzzy-if(gtkWidget,1-30,4-32) fuzzy-if(cocoaWidget,14-22,20-44) skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html +fuzzy-if(Android,0-13,0-21) fuzzy-if(gtkWidget,1-4,4-24) fuzzy-if(cocoaWidget,11-18,44-88) skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html +fuzzy-if(Android,0-5,0-6) fuzzy-if(gtkWidget,1-8,8-32) fuzzy-if(cocoaWidget,16-22,20-44) skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html +fuzzy-if(Android,0-14,0-10) fuzzy-if(gtkWidget,1-30,12-32) fuzzy-if(cocoaWidget,14-22,20-44) skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html +fuzzy-if(Android,0-43,0-26) fuzzy-if(gtkWidget,0-14,12-32) fuzzy-if(cocoaWidget,11-18,26-76) skip-if(useDrawSnapshot) pref(apz.allow_zooming,true) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html # Different async zoom levels. Since the scrollthumb gets async-scaled in the # compositor, the border-radius ends of the scrollthumb are going to be a little @@ -37,7 +37,7 @@ fuzzy-if(Android,0-28,0-23) fuzzy-if(!Android,0-107,0-34) pref(apz.allow_zooming # Meta-viewport tag support skip-if(!Android) pref(apz.allow_zooming,true) == initial-scale-1.html initial-scale-1-ref.html -skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping-ref.html +skip-if(useDrawSnapshot) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping-ref.html # Test that position:fixed and position:sticky elements are attached to the # layout viewport. diff --git a/gfx/layers/apz/testutil/APZTestData.h b/gfx/layers/apz/testutil/APZTestData.h index e4a73c80cc..d180bbcf1d 100644 --- a/gfx/layers/apz/testutil/APZTestData.h +++ b/gfx/layers/apz/testutil/APZTestData.h @@ -144,10 +144,10 @@ class APZTestData { // A helper class for logging data for a paint. class APZPaintLogHelper { public: - APZPaintLogHelper(APZTestData* aTestData, SequenceNumber aPaintSequenceNumber) + APZPaintLogHelper(APZTestData* aTestData, SequenceNumber aPaintSequenceNumber, + bool aIsTestLoggingEnabled) : mTestData(aTestData), mPaintSequenceNumber(aPaintSequenceNumber) { - MOZ_ASSERT(!aTestData || StaticPrefs::apz_test_logging_enabled(), - "don't call me"); + MOZ_ASSERT(!aTestData || aIsTestLoggingEnabled, "don't call me"); } template <typename Value> diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index 9ad65ce981..508cd8e67f 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -518,7 +518,8 @@ nsEventStatus APZCCallbackHelper::DispatchSynthesizedMouseEvent( WidgetMouseEvent::eNormal); event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y); event.mButton = MouseButton::ePrimary; - event.mButtons |= MouseButtonsFlag::ePrimaryFlag; + event.mButtons = aMsg == eMouseDown ? MouseButtonsFlag::ePrimaryFlag + : MouseButtonsFlag::eNoButtons; event.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH; if (aMsg == eMouseLongTap) { event.mFlags.mOnlyChromeDispatch = true; diff --git a/gfx/layers/apz/util/APZEventState.cpp b/gfx/layers/apz/util/APZEventState.cpp index 7384296f45..5db6a08429 100644 --- a/gfx/layers/apz/util/APZEventState.cpp +++ b/gfx/layers/apz/util/APZEventState.cpp @@ -240,7 +240,7 @@ void APZEventState::ProcessLongTap(PresShell* aPresShell, false; #elif defined(MOZ_WIDGET_ANDROID) // On Android, GeckoView calls preventDefault() in a JSActor - // (ContentDelegateChild.jsm) when opening context menu so that we can + // (ContentDelegateChild.sys.mjs) when opening context menu so that we can // tell whether contextmenu opens in response to the contextmenu event by // checking where preventDefault() got called. preventDefaultResult == PreventDefaultResult::ByChrome; diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index bfc20b9b08..4187e48955 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -27,6 +27,7 @@ #include "mozilla/gfx/gfxVars.h" #include "mozilla/ipc/CrossProcessSemaphore.h" #include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/layers/CanvasRenderer.h" #include "mozilla/layers/CompositableForwarder.h" #include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/layers/ImageBridgeChild.h" @@ -375,7 +376,8 @@ TextureData* TextureData::Create(TextureForwarder* aAllocator, RefPtr<CanvasChild> canvasChild = aAllocator->GetCanvasChild(); if (canvasChild) { return new RecordedTextureData(canvasChild.forget(), aSize, aFormat, - textureType); + textureType, + layers::TexTypeForWebgl(aKnowsCompositor)); } // If we must be remote, but there is no canvas child, then falling back // is not possible. @@ -746,12 +748,10 @@ void TextureClient::ReadUnlock() { } bool TextureClient::Lock(OpenMode aMode) { - MOZ_ASSERT(IsValid()); - MOZ_ASSERT(!mIsLocked); - if (!IsValid()) { + if (NS_WARN_IF(!IsValid())) { return false; } - if (mIsLocked) { + if (NS_WARN_IF(mIsLocked)) { return mOpenMode == aMode; } diff --git a/gfx/layers/client/TextureRecorded.cpp b/gfx/layers/client/TextureRecorded.cpp index 7e7ee2da99..da4ca4f8f3 100644 --- a/gfx/layers/client/TextureRecorded.cpp +++ b/gfx/layers/client/TextureRecorded.cpp @@ -23,9 +23,10 @@ static int64_t sNextRecordedTextureId = 0; RecordedTextureData::RecordedTextureData( already_AddRefed<CanvasChild> aCanvasChild, gfx::IntSize aSize, - gfx::SurfaceFormat aFormat, TextureType aTextureType) + gfx::SurfaceFormat aFormat, TextureType aTextureType, + TextureType aWebglTextureType) : mCanvasChild(aCanvasChild), mSize(aSize), mFormat(aFormat) { - mCanvasChild->EnsureRecorder(aSize, aFormat, aTextureType); + mCanvasChild->EnsureRecorder(aSize, aFormat, aTextureType, aWebglTextureType); } RecordedTextureData::~RecordedTextureData() { diff --git a/gfx/layers/client/TextureRecorded.h b/gfx/layers/client/TextureRecorded.h index d846c1fac1..56e504fb54 100644 --- a/gfx/layers/client/TextureRecorded.h +++ b/gfx/layers/client/TextureRecorded.h @@ -18,7 +18,8 @@ class RecordedTextureData final : public TextureData { public: RecordedTextureData(already_AddRefed<CanvasChild> aCanvasChild, gfx::IntSize aSize, gfx::SurfaceFormat aFormat, - TextureType aTextureType); + TextureType aTextureType, + TextureType aWebglTextureType = TextureType::Unknown); void FillInfo(TextureData::Info& aInfo) const final; diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp index 553217d82b..515463cd8e 100644 --- a/gfx/layers/ipc/CanvasChild.cpp +++ b/gfx/layers/ipc/CanvasChild.cpp @@ -7,6 +7,9 @@ #include "CanvasChild.h" #include "MainThreadUtils.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/WorkerRunnable.h" #include "mozilla/gfx/CanvasManagerChild.h" #include "mozilla/gfx/DrawTargetRecording.h" #include "mozilla/gfx/Tools.h" @@ -18,6 +21,7 @@ #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/SourceSurfaceSharedData.h" #include "mozilla/Maybe.h" +#include "mozilla/Mutex.h" #include "nsIObserverService.h" #include "RecordedCanvasEventImpl.h" @@ -33,9 +37,9 @@ class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers { ~RecorderHelpers() override = default; - bool InitTranslator(TextureType aTextureType, gfx::BackendType aBackendType, - Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles, - uint64_t aBufferSize, + bool InitTranslator(TextureType aTextureType, TextureType aWebglTextureType, + gfx::BackendType aBackendType, Handle&& aReadHandle, + nsTArray<Handle>&& aBufferHandles, uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem, CrossProcessSemaphoreHandle&& aWriterSem) override { NS_ASSERT_OWNINGTHREAD(RecorderHelpers); @@ -43,7 +47,7 @@ class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers { return false; } return mCanvasChild->SendInitTranslator( - aTextureType, aBackendType, std::move(aReadHandle), + aTextureType, aWebglTextureType, aBackendType, std::move(aReadHandle), std::move(aBufferHandles), aBufferSize, std::move(aReaderSem), std::move(aWriterSem)); } @@ -165,9 +169,104 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface { bool mDetached = false; }; -CanvasChild::CanvasChild() = default; +class CanvasDataShmemHolder { + public: + CanvasDataShmemHolder(ipc::SharedMemoryBasic* aShmem, + CanvasChild* aCanvasChild) + : mMutex("CanvasChild::DataShmemHolder::mMutex"), + mShmem(aShmem), + mCanvasChild(aCanvasChild) {} + + bool Init(dom::ThreadSafeWorkerRef* aWorkerRef) { + if (!aWorkerRef) { + return true; + } -CanvasChild::~CanvasChild() = default; + RefPtr<dom::StrongWorkerRef> workerRef = dom::StrongWorkerRef::Create( + aWorkerRef->Private(), "CanvasChild::DataShmemHolder", + [this]() { DestroyWorker(); }); + if (NS_WARN_IF(!workerRef)) { + return false; + } + + MutexAutoLock lock(mMutex); + mWorkerRef = new dom::ThreadSafeWorkerRef(workerRef); + return true; + } + + void Destroy() { + class DestroyRunnable final : public dom::WorkerRunnable { + public: + DestroyRunnable(dom::WorkerPrivate* aWorkerPrivate, + CanvasDataShmemHolder* aShmemHolder) + : dom::WorkerRunnable(aWorkerPrivate, + "CanvasDataShmemHolder::Destroy", + dom::WorkerRunnable::WorkerThread), + mShmemHolder(aShmemHolder) {} + + bool WorkerRun(JSContext* aCx, + dom::WorkerPrivate* aWorkerPrivate) override { + mShmemHolder->Destroy(); + return true; + } + + void PostRun(JSContext* aCx, dom::WorkerPrivate* aWorkerPrivate, + bool aRunResult) override {} + + bool PreDispatch(dom::WorkerPrivate* aWorkerPrivate) override { + return true; + } + + void PostDispatch(dom::WorkerPrivate* aWorkerPrivate, + bool aDispatchResult) override {} + + private: + CanvasDataShmemHolder* mShmemHolder; + }; + + mMutex.Lock(); + + if (mCanvasChild) { + if (mWorkerRef) { + if (!mWorkerRef->Private()->IsOnCurrentThread()) { + auto task = MakeRefPtr<DestroyRunnable>(mWorkerRef->Private(), this); + mMutex.Unlock(); + task->Dispatch(); + return; + } + } else if (!NS_IsMainThread()) { + mMutex.Unlock(); + NS_DispatchToMainThread(NS_NewRunnableFunction( + "CanvasDataShmemHolder::Destroy", [this]() { Destroy(); })); + return; + } + + mCanvasChild->ReturnDataSurfaceShmem(mShmem.forget()); + mCanvasChild = nullptr; + mWorkerRef = nullptr; + } + + mMutex.Unlock(); + delete this; + } + + void DestroyWorker() { + MutexAutoLock lock(mMutex); + mCanvasChild = nullptr; + mWorkerRef = nullptr; + } + + private: + Mutex mMutex; + RefPtr<ipc::SharedMemoryBasic> mShmem; + RefPtr<CanvasChild> mCanvasChild MOZ_GUARDED_BY(mMutex); + RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef MOZ_GUARDED_BY(mMutex); +}; + +CanvasChild::CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef) + : mWorkerRef(aWorkerRef) {} + +CanvasChild::~CanvasChild() { MOZ_ASSERT(!mWorkerRef); } static void NotifyCanvasDeviceReset() { nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); @@ -207,14 +306,15 @@ ipc::IPCResult CanvasChild::RecvBlockCanvas() { } void CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, - TextureType aTextureType) { + TextureType aTextureType, + TextureType aWebglTextureType) { NS_ASSERT_OWNINGTHREAD(CanvasChild); if (!mRecorder) { gfx::BackendType backendType = gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(); - auto recorder = MakeRefPtr<CanvasDrawEventRecorder>(); - if (!recorder->Init(aTextureType, backendType, + auto recorder = MakeRefPtr<CanvasDrawEventRecorder>(mWorkerRef); + if (!recorder->Init(aTextureType, aWebglTextureType, backendType, MakeUnique<RecorderHelpers>(this))) { return; } @@ -242,6 +342,8 @@ void CanvasChild::Destroy() { if (CanSend()) { Send__delete__(this); } + + mWorkerRef = nullptr; } bool CanvasChild::EnsureBeginTransaction() { @@ -397,6 +499,10 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface( return nullptr; } + gfx::IntSize ssSize = aSurface->GetSize(); + gfx::SurfaceFormat ssFormat = aSurface->GetFormat(); + auto stride = ImageDataSerializer::ComputeRGBStride(ssFormat, ssSize.width); + // Shmem is only valid if the surface is the latest snapshot (not detached). if (!aDetached) { // If there is a shmem associated with this snapshot id, then we want to try @@ -411,20 +517,22 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface( if (NS_WARN_IF(!mRecorder->WaitForCheckpoint(checkpoint))) { return nullptr; } - gfx::IntSize size = aSurface->GetSize(); - gfx::SurfaceFormat format = aSurface->GetFormat(); - auto stride = ImageDataSerializer::ComputeRGBStride(format, size.width); + auto* closure = + new CanvasDataShmemHolder(it->second.mSnapshotShmem, this); + if (NS_WARN_IF(!closure->Init(mWorkerRef))) { + delete closure; + return nullptr; + } RefPtr<gfx::DataSourceSurface> dataSurface = - gfx::Factory::CreateWrappingDataSourceSurface(shmemPtr, stride, size, - format); + gfx::Factory::CreateWrappingDataSourceSurface( + shmemPtr, stride, ssSize, ssFormat, ReleaseDataShmemHolder, + closure); return dataSurface.forget(); } } RecordEvent(RecordedPrepareDataForSurface(aSurface)); - gfx::IntSize ssSize = aSurface->GetSize(); - gfx::SurfaceFormat ssFormat = aSurface->GetFormat(); if (!EnsureDataSurfaceShmem(ssSize, ssFormat)) { return nullptr; } @@ -435,15 +543,15 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface( return nullptr; } + auto* closure = new CanvasDataShmemHolder(mDataSurfaceShmem, this); + if (NS_WARN_IF(!closure->Init(mWorkerRef))) { + delete closure; + return nullptr; + } + mDataSurfaceShmemAvailable = false; - struct DataShmemHolder { - RefPtr<ipc::SharedMemoryBasic> shmem; - RefPtr<CanvasChild> canvasChild; - }; auto* data = static_cast<uint8_t*>(mDataSurfaceShmem->memory()); - auto* closure = new DataShmemHolder{do_AddRef(mDataSurfaceShmem), this}; - auto stride = ImageDataSerializer::ComputeRGBStride(ssFormat, ssSize.width); RefPtr<gfx::DataSourceSurface> dataSurface = gfx::Factory::CreateWrappingDataSourceSurface( @@ -452,16 +560,8 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface( } /* static */ void CanvasChild::ReleaseDataShmemHolder(void* aClosure) { - if (!NS_IsMainThread()) { - NS_DispatchToMainThread(NS_NewRunnableFunction( - "CanvasChild::ReleaseDataShmemHolder", - [aClosure]() { ReleaseDataShmemHolder(aClosure); })); - return; - } - - auto* shmemHolder = static_cast<DataShmemHolder*>(aClosure); - shmemHolder->canvasChild->ReturnDataSurfaceShmem(shmemHolder->shmem.forget()); - delete shmemHolder; + auto* shmemHolder = static_cast<CanvasDataShmemHolder*>(aClosure); + shmemHolder->Destroy(); } already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface( diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h index c99fe50bfb..e22109f406 100644 --- a/gfx/layers/ipc/CanvasChild.h +++ b/gfx/layers/ipc/CanvasChild.h @@ -15,6 +15,10 @@ namespace mozilla { +namespace dom { +class ThreadSafeWorkerRef; +} + namespace gfx { class DrawTargetRecording; class SourceSurface; @@ -28,7 +32,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { public: NS_INLINE_DECL_REFCOUNTING(CanvasChild) - CanvasChild(); + explicit CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef); /** * @returns true if remote canvas has been deactivated due to failure. @@ -58,7 +62,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { * @params aTextureType the TextureType to create in the CanvasTranslator. */ void EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, - TextureType aTextureType); + TextureType aTextureType, TextureType aWebglTextureType); /** * Clean up IPDL actor. @@ -147,6 +151,9 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { void CleanupTexture(int64_t aTextureId); + void ReturnDataSurfaceShmem( + already_AddRefed<ipc::SharedMemoryBasic> aDataSurfaceShmem); + protected: void ActorDestroy(ActorDestroyReason aWhy) final; @@ -157,14 +164,6 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { bool EnsureDataSurfaceShmem(gfx::IntSize aSize, gfx::SurfaceFormat aFormat); - void ReturnDataSurfaceShmem( - already_AddRefed<ipc::SharedMemoryBasic> aDataSurfaceShmem); - - struct DataShmemHolder { - RefPtr<ipc::SharedMemoryBasic> shmem; - RefPtr<CanvasChild> canvasChild; - }; - static void ReleaseDataShmemHolder(void* aClosure); void DropFreeBuffersWhenDormant(); @@ -173,6 +172,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { static bool mDeactivated; + RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef; RefPtr<CanvasDrawEventRecorder> mRecorder; RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem; diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp index 08150d6952..4a184f48d8 100644 --- a/gfx/layers/ipc/CanvasTranslator.cpp +++ b/gfx/layers/ipc/CanvasTranslator.cpp @@ -13,6 +13,7 @@ #include "mozilla/gfx/DrawTargetWebgl.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/gfx/Logging.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/layers/BufferTexture.h" @@ -131,15 +132,17 @@ bool CanvasTranslator::EnsureSharedContextWebgl() { } mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator( - TextureType aTextureType, gfx::BackendType aBackendType, - Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles, - uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem, + TextureType aTextureType, TextureType aWebglTextureType, + gfx::BackendType aBackendType, Handle&& aReadHandle, + nsTArray<Handle>&& aBufferHandles, uint64_t aBufferSize, + CrossProcessSemaphoreHandle&& aReaderSem, CrossProcessSemaphoreHandle&& aWriterSem) { if (mHeaderShmem) { return IPC_FAIL(this, "RecvInitTranslator called twice."); } mTextureType = aTextureType; + mWebglTextureType = aWebglTextureType; mBackendType = aBackendType; mOtherPid = OtherPid(); @@ -637,7 +640,11 @@ bool CanvasTranslator::HandleExtensionEvent(int32_t aType) { } } -void CanvasTranslator::BeginTransaction() { mIsInTransaction = true; } +void CanvasTranslator::BeginTransaction() { + PROFILER_MARKER_TEXT("CanvasTranslator", GRAPHICS, {}, + "CanvasTranslator::BeginTransaction"_ns); + mIsInTransaction = true; +} void CanvasTranslator::Flush() { #if defined(XP_WIN) @@ -728,9 +735,15 @@ bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) { NotifyDeviceChanged(); } - RefPtr<Runnable> runnable = NS_NewRunnableFunction( - "CanvasTranslator NotifyDeviceReset", - []() { gfx::GPUParent::GetSingleton()->NotifyDeviceReset(); }); + RefPtr<Runnable> runnable = + NS_NewRunnableFunction("CanvasTranslator NotifyDeviceReset", []() { + if (XRE_IsGPUProcess()) { + gfx::GPUParent::GetSingleton()->NotifyDeviceReset(); + } else { + gfx::GPUProcessManager::Get()->OnInProcessDeviceReset( + /* aTrackThreshold */ false); + } + }); // It is safe to wait here because only the Compositor thread waits on us and // the main thread doesn't wait on the compositor thread in the GPU process. @@ -1025,6 +1038,8 @@ bool CanvasTranslator::UnlockTexture(int64_t aTextureId) { } bool CanvasTranslator::PresentTexture(int64_t aTextureId, RemoteTextureId aId) { + AUTO_PROFILER_MARKER_TEXT("CanvasTranslator", GRAPHICS, {}, + "CanvasTranslator::PresentTexture"_ns); auto result = mTextureInfo.find(aTextureId); if (result == mTextureInfo.end()) { return false; @@ -1033,7 +1048,8 @@ bool CanvasTranslator::PresentTexture(int64_t aTextureId, RemoteTextureId aId) { RemoteTextureOwnerId ownerId = info.mRemoteTextureOwnerId; if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) { EnsureRemoteTextureOwner(ownerId); - if (webgl->CopyToSwapChain(aId, ownerId, mRemoteTextureOwner)) { + if (webgl->CopyToSwapChain(mWebglTextureType, aId, ownerId, + mRemoteTextureOwner)) { return true; } if (mSharedContext && mSharedContext->IsContextLost()) { diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h index cf87b7ab53..5258e0c529 100644 --- a/gfx/layers/ipc/CanvasTranslator.h +++ b/gfx/layers/ipc/CanvasTranslator.h @@ -69,6 +69,7 @@ class CanvasTranslator final : public gfx::InlineTranslator, * CanvasEventRingBuffer. * * @param aTextureType the TextureType the translator will create + * @param aWebglTextureType the TextureType of any WebGL buffers * @param aBackendType the BackendType for texture data * @param aHeaderHandle handle for the control header * @param aBufferHandles handles for the initial buffers for translation @@ -77,6 +78,7 @@ class CanvasTranslator final : public gfx::InlineTranslator, * @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer */ ipc::IPCResult RecvInitTranslator(TextureType aTextureType, + TextureType aWebglTextureType, gfx::BackendType aBackendType, Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles, @@ -358,6 +360,7 @@ class CanvasTranslator final : public gfx::InlineTranslator, UniquePtr<CrossProcessSemaphore> mWriterSemaphore; UniquePtr<CrossProcessSemaphore> mReaderSemaphore; TextureType mTextureType = TextureType::Unknown; + TextureType mWebglTextureType = TextureType::Unknown; UniquePtr<TextureData> mReferenceTextureData; dom::ContentParentId mContentId; uint32_t mManagerId; diff --git a/gfx/layers/ipc/CompositorManagerChild.cpp b/gfx/layers/ipc/CompositorManagerChild.cpp index 0e553745d3..8d89339373 100644 --- a/gfx/layers/ipc/CompositorManagerChild.cpp +++ b/gfx/layers/ipc/CompositorManagerChild.cpp @@ -10,6 +10,7 @@ #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/CompositorManagerParent.h" #include "mozilla/layers/CompositorThread.h" +#include "mozilla/gfx/CanvasShutdownManager.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/dom/ContentChild.h" // for ContentChild @@ -67,7 +68,15 @@ bool CompositorManagerChild::Init(Endpoint<PCompositorManagerChild>&& aEndpoint, sInstance = new CompositorManagerChild(std::move(aEndpoint), aProcessToken, aNamespace); sOtherPid = sInstance->OtherPid(); - return sInstance->CanSend(); + if (!sInstance->CanSend()) { + return false; + } + + // If there are any canvases waiting on the recreation of the GPUProcess or + // CompositorManagerChild, then we need to notify them so that they can + // restore their contexts. + gfx::CanvasShutdownManager::OnCompositorManagerRestored(); + return true; } /* static */ diff --git a/gfx/layers/ipc/LayersMessageUtils.h b/gfx/layers/ipc/LayersMessageUtils.h index d869d4ed83..a4e9557ac3 100644 --- a/gfx/layers/ipc/LayersMessageUtils.h +++ b/gfx/layers/ipc/LayersMessageUtils.h @@ -520,7 +520,7 @@ template <> struct ParamTraits<mozilla::ScrollUpdateType> : public ContiguousEnumSerializerInclusive< mozilla::ScrollUpdateType, mozilla::ScrollUpdateType::Absolute, - mozilla::ScrollUpdateType::MergeableAbsolute> {}; + mozilla::ScrollUpdateType::PureRelative> {}; template <> struct ParamTraits<mozilla::ScrollMode> diff --git a/gfx/layers/ipc/PCanvas.ipdl b/gfx/layers/ipc/PCanvas.ipdl index 5187e61d23..59896b72cf 100644 --- a/gfx/layers/ipc/PCanvas.ipdl +++ b/gfx/layers/ipc/PCanvas.ipdl @@ -32,9 +32,9 @@ parent: * each aBufferHandles' memory and the default size. aReaderSem and aWriterSem * are handles for the semaphores to handle waiting on either side. */ - async InitTranslator(TextureType aTextureType, BackendType aBackendType, - Handle aHeaderHandle, Handle[] aBufferHandles, - uint64_t aBufferSize, + async InitTranslator(TextureType aTextureType, TextureType aWebglTextureType, + BackendType aBackendType, Handle aHeaderHandle, + Handle[] aBufferHandles, uint64_t aBufferSize, CrossProcessSemaphoreHandle aReaderSem, CrossProcessSemaphoreHandle aWriterSem); diff --git a/gfx/layers/ipc/VideoBridgeParent.cpp b/gfx/layers/ipc/VideoBridgeParent.cpp index 6413c27bc1..50f0549e50 100644 --- a/gfx/layers/ipc/VideoBridgeParent.cpp +++ b/gfx/layers/ipc/VideoBridgeParent.cpp @@ -17,9 +17,8 @@ namespace mozilla::layers { using namespace mozilla::ipc; using namespace mozilla::gfx; -using VideoBridgeTable = - EnumeratedArray<VideoBridgeSource, VideoBridgeSource::_Count, - VideoBridgeParent*>; +using VideoBridgeTable = EnumeratedArray<VideoBridgeSource, VideoBridgeParent*, + size_t(VideoBridgeSource::_Count)>; static StaticDataMutex<VideoBridgeTable> sVideoBridgeFromProcess( "VideoBridges"); diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index 1bf2e83804..384611b68b 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -260,7 +260,7 @@ if CONFIG["MOZ_WAYLAND"]: "SurfacePoolWayland.cpp", ] -if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": +if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("cocoa", "uikit"): EXPORTS.mozilla.layers += [ "NativeLayerCA.h", "SurfacePoolCA.h", @@ -458,7 +458,7 @@ DEFINES["GOOGLE_PROTOBUF_NO_RTTI"] = True DEFINES["GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER"] = True DEFINES["MOZ_APP_VERSION"] = CONFIG["MOZ_APP_VERSION"] -if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": +if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("cocoa", "uikit"): SOURCES += [ "opengl/MacIOSurfaceTextureClientOGL.cpp", "opengl/MacIOSurfaceTextureHostOGL.cpp", diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp index ee61045599..112ed7711c 100644 --- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp +++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp @@ -10,7 +10,6 @@ #include "mozilla/webrender/RenderMacIOSurfaceTextureHost.h" #include "mozilla/webrender/RenderThread.h" #include "mozilla/webrender/WebRenderAPI.h" -#include "GLContextCGL.h" namespace mozilla { namespace layers { diff --git a/gfx/layers/wr/AsyncImagePipelineManager.cpp b/gfx/layers/wr/AsyncImagePipelineManager.cpp index 464b8fb69f..8a49ffef69 100644 --- a/gfx/layers/wr/AsyncImagePipelineManager.cpp +++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp @@ -229,8 +229,7 @@ Maybe<TextureHost::ResourceUpdateOp> AsyncImagePipelineManager::UpdateImageKeys( auto* wrapper = aTexture ? aTexture->AsRemoteTextureHostWrapper() : nullptr; if (wrapper && !aPipeline->mImageHost->GetAsyncRef()) { std::function<void(const RemoteTextureInfo&)> function; - RemoteTextureMap::Get()->GetRemoteTexture( - wrapper, std::move(function), /* aWaitForRemoteTextureOwner */ false); + RemoteTextureMap::Get()->GetRemoteTexture(wrapper, std::move(function)); } if (!aTexture || aTexture->NumSubTextures() == 0) { diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 789476cb6b..83139b6af6 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -141,7 +141,7 @@ void gfx_wr_set_crash_annotation(mozilla::wr::CrashAnnotation aAnnotation, return; } - CrashReporter::AnnotateCrashReport(annotation, nsDependentCString(aValue)); + CrashReporter::RecordAnnotationCString(annotation, aValue); } void gfx_wr_clear_crash_annotation(mozilla::wr::CrashAnnotation aAnnotation) { @@ -150,7 +150,7 @@ void gfx_wr_clear_crash_annotation(mozilla::wr::CrashAnnotation aAnnotation) { return; } - CrashReporter::RemoveCrashReportAnnotation(annotation); + CrashReporter::UnrecordAnnotation(annotation); } } @@ -1188,7 +1188,8 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetDisplayList( IsRootWebRenderBridgeParent()); if (!IsRootWebRenderBridgeParent()) { - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aTxnURL); + CrashReporter::RecordAnnotationNSCString(CrashReporter::Annotation::URL, + aTxnURL); } CompositorBridgeParent* cbp = GetRootCompositorBridgeParent(); @@ -1330,7 +1331,8 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEmptyTransaction( IsRootWebRenderBridgeParent()); if (!IsRootWebRenderBridgeParent()) { - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aTxnURL); + CrashReporter::RecordAnnotationNSCString(CrashReporter::Annotation::URL, + aTxnURL); } AUTO_PROFILER_TRACING_MARKER("Paint", "EmptyTransaction", GRAPHICS); diff --git a/gfx/layers/wr/WebRenderImageHost.cpp b/gfx/layers/wr/WebRenderImageHost.cpp index e3bbd5d50f..5016bc30f8 100644 --- a/gfx/layers/wr/WebRenderImageHost.cpp +++ b/gfx/layers/wr/WebRenderImageHost.cpp @@ -198,8 +198,11 @@ void WebRenderImageHost::UseRemoteTexture() { while (!mPendingRemoteTextureWrappers.empty()) { auto* wrapper = mPendingRemoteTextureWrappers.front()->AsRemoteTextureHostWrapper(); - mWaitingReadyCallback = RemoteTextureMap::Get()->GetRemoteTexture( - wrapper, readyCallback, mWaitForRemoteTextureOwner); + if (mWaitForRemoteTextureOwner) { + RemoteTextureMap::Get()->WaitForRemoteTextureOwner(wrapper); + } + mWaitingReadyCallback = + RemoteTextureMap::Get()->GetRemoteTexture(wrapper, readyCallback); MOZ_ASSERT_IF(mWaitingReadyCallback, !wrapper->IsReadyForRendering()); if (!wrapper->IsReadyForRendering()) { break; @@ -213,9 +216,9 @@ void WebRenderImageHost::UseRemoteTexture() { mPendingRemoteTextureWrappers.pop_front(); MOZ_ASSERT(mPendingRemoteTextureWrappers.empty()); - std::function<void(const RemoteTextureInfo&)> function; - RemoteTextureMap::Get()->GetRemoteTexture(wrapper, std::move(function), - mWaitForRemoteTextureOwner); + if (mWaitForRemoteTextureOwner) { + RemoteTextureMap::Get()->WaitForRemoteTextureOwner(wrapper); + } mWaitForRemoteTextureOwner = false; } diff --git a/gfx/layers/wr/WebRenderScrollData.cpp b/gfx/layers/wr/WebRenderScrollData.cpp index 538df8bdef..905b7e1de0 100644 --- a/gfx/layers/wr/WebRenderScrollData.cpp +++ b/gfx/layers/wr/WebRenderScrollData.cpp @@ -370,6 +370,22 @@ void WebRenderScrollData::ApplyUpdates(ScrollUpdatesMap&& aUpdates, mPaintSequenceNumber = aPaintSequenceNumber; } +void WebRenderScrollData::PrependUpdates( + const WebRenderScrollData& aPreviousData) { + for (auto previousMetadata : aPreviousData.mScrollMetadatas) { + const nsTArray<ScrollPositionUpdate>& previousUpdates = + previousMetadata.GetScrollUpdates(); + if (previousUpdates.IsEmpty()) { + continue; + } + + if (Maybe<size_t> index = + HasMetadataFor(previousMetadata.GetMetrics().GetScrollId())) { + mScrollMetadatas[*index].PrependUpdates(previousUpdates); + } + } +} + void WebRenderScrollData::DumpSubtree(std::ostream& aOut, size_t aIndex, const std::string& aIndent) const { aOut << aIndent; diff --git a/gfx/layers/wr/WebRenderScrollData.h b/gfx/layers/wr/WebRenderScrollData.h index c575d4ca21..fd82ac93fc 100644 --- a/gfx/layers/wr/WebRenderScrollData.h +++ b/gfx/layers/wr/WebRenderScrollData.h @@ -276,6 +276,13 @@ class WebRenderScrollData { void ApplyUpdates(ScrollUpdatesMap&& aUpdates, uint32_t aPaintSequenceNumber); + // Prepend the scroll position updates in the previous data to this data so + // that we can handle all scroll position updates in the proper order. + void PrependUpdates(const WebRenderScrollData& aPreviousData); + + void SetWasUpdateSkipped() { mWasUpdateSkipped = true; } + bool GetWasUpdateSkipped() const { return mWasUpdateSkipped; } + friend struct IPC::ParamTraits<WebRenderScrollData>; friend std::ostream& operator<<(std::ostream& aOut, @@ -328,6 +335,12 @@ class WebRenderScrollData { bool mIsFirstPaint; uint32_t mPaintSequenceNumber; + + // Wether this data was skipped to updated because the parent process hasn't + // yet gotten the referent LayersId for this data. + // + // Note this variable is not copied over IPC. + bool mWasUpdateSkipped = false; }; } // namespace layers |