diff options
Diffstat (limited to 'gfx/layers/Layers.h')
-rw-r--r-- | gfx/layers/Layers.h | 2619 |
1 files changed, 2619 insertions, 0 deletions
diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h new file mode 100644 index 000000000..805d41d48 --- /dev/null +++ b/gfx/layers/Layers.h @@ -0,0 +1,2619 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_LAYERS_H +#define GFX_LAYERS_H + +#include <map> +#include <stdint.h> // for uint32_t, uint64_t, uint8_t +#include <stdio.h> // for FILE +#include <sys/types.h> // for int32_t, int64_t +#include "FrameMetrics.h" // for FrameMetrics +#include "Units.h" // for LayerMargin, LayerPoint, ParentLayerIntRect +#include "gfxContext.h" +#include "gfxTypes.h" +#include "gfxPoint.h" // for gfxPoint +#include "gfxRect.h" // for gfxRect +#include "gfx2DGlue.h" +#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2, etc +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/EventForwards.h" // for nsPaintEvent +#include "mozilla/Maybe.h" // for Maybe +#include "mozilla/Poison.h" +#include "mozilla/RefPtr.h" // for already_AddRefed +#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc +#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration +#include "mozilla/UniquePtr.h" // for UniquePtr +#include "mozilla/gfx/BaseMargin.h" // for BaseMargin +#include "mozilla/gfx/BasePoint.h" // for BasePoint +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/TiledRegion.h" // for TiledIntRegion +#include "mozilla/gfx/Types.h" // for SurfaceFormat +#include "mozilla/gfx/UserData.h" // for UserData, etc +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "nsAutoPtr.h" // for nsAutoPtr, nsRefPtr, etc +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsCSSPropertyID.h" // for nsCSSPropertyID +#include "nsDebug.h" // for NS_ASSERTION +#include "nsISupportsImpl.h" // for Layer::Release, etc +#include "nsRect.h" // for mozilla::gfx::IntRect +#include "nsRegion.h" // for nsIntRegion +#include "nsString.h" // for nsCString +#include "nsTArray.h" // for nsTArray +#include "nsTArrayForwardDeclare.h" // for InfallibleTArray +#include "nscore.h" // for nsACString, nsAString +#include "mozilla/Logging.h" // for PRLogModuleInfo +#include "nsIWidget.h" // For plugin window configuration information structs +#include "ImageContainer.h" + +class gfxContext; + +extern uint8_t gLayerManagerLayerBuilder; + +namespace mozilla { + +class ComputedTimingFunction; +class FrameLayerBuilder; +class StyleAnimationValue; + +namespace gl { +class GLContext; +} // namespace gl + +namespace gfx { +class DrawTarget; +} // namespace gfx + +namespace dom { +class OverfillCallback; +} // namespace dom + +namespace layers { + +class Animation; +class AnimationData; +class AsyncCanvasRenderer; +class AsyncPanZoomController; +class BasicLayerManager; +class ClientLayerManager; +class Layer; +class LayerMetricsWrapper; +class PaintedLayer; +class ContainerLayer; +class ImageLayer; +class ColorLayer; +class CanvasLayer; +class ReadbackLayer; +class ReadbackProcessor; +class RefLayer; +class LayerComposite; +class ShadowableLayer; +class ShadowLayerForwarder; +class LayerManagerComposite; +class SpecificLayerAttributes; +class Compositor; +class FrameUniformityData; +class PersistentBufferProvider; + +namespace layerscope { +class LayersPacket; +} // namespace layerscope + +#define MOZ_LAYER_DECL_NAME(n, e) \ + virtual const char* Name() const override { return n; } \ + virtual LayerType GetType() const override { return e; } + +// Defined in LayerUserData.h; please include that file instead. +class LayerUserData; + +/* + * Motivation: For truly smooth animation and video playback, we need to + * be able to compose frames and render them on a dedicated thread (i.e. + * off the main thread where DOM manipulation, script execution and layout + * induce difficult-to-bound latency). This requires Gecko to construct + * some kind of persistent scene structure (graph or tree) that can be + * safely transmitted across threads. We have other scenarios (e.g. mobile + * browsing) where retaining some rendered data between paints is desired + * for performance, so again we need a retained scene structure. + * + * Our retained scene structure is a layer tree. Each layer represents + * content which can be composited onto a destination surface; the root + * layer is usually composited into a window, and non-root layers are + * composited into their parent layers. Layers have attributes (e.g. + * opacity and clipping) that influence their compositing. + * + * We want to support a variety of layer implementations, including + * a simple "immediate mode" implementation that doesn't retain any + * rendered data between paints (i.e. uses cairo in just the way that + * Gecko used it before layers were introduced). But we also don't want + * to have bifurcated "layers"/"non-layers" rendering paths in Gecko. + * Therefore the layers API is carefully designed to permit maximally + * efficient implementation in an "immediate mode" style. See the + * BasicLayerManager for such an implementation. + */ + +/** + * A LayerManager controls a tree of layers. All layers in the tree + * must use the same LayerManager. + * + * All modifications to a layer tree must happen inside a transaction. + * Only the state of the layer tree at the end of a transaction is + * rendered. Transactions cannot be nested + * + * Each transaction has two phases: + * 1) Construction: layers are created, inserted, removed and have + * properties set on them in this phase. + * BeginTransaction and BeginTransactionWithTarget start a transaction in + * the Construction phase. + * 2) Drawing: PaintedLayers are rendered into in this phase, in tree + * order. When the client has finished drawing into the PaintedLayers, it should + * call EndTransaction to complete the transaction. + * + * All layer API calls happen on the main thread. + * + * Layers are refcounted. The layer manager holds a reference to the + * root layer, and each container layer holds a reference to its children. + */ +class LayerManager { + NS_INLINE_DECL_REFCOUNTING(LayerManager) + +protected: + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::IntSize IntSize; + typedef mozilla::gfx::SurfaceFormat SurfaceFormat; + +public: + LayerManager() + : mDestroyed(false) + , mSnapEffectiveTransforms(true) + , mId(0) + , mInTransaction(false) + , mPaintedPixelCount(0) + {} + + /** + * Release layers and resources held by this layer manager, and mark + * it as destroyed. Should do any cleanup necessary in preparation + * for its widget going away. After this call, only user data calls + * are valid on the layer manager. + */ + virtual void Destroy() + { + mDestroyed = true; + mUserData.Destroy(); + mRoot = nullptr; + } + bool IsDestroyed() { return mDestroyed; } + + virtual ShadowLayerForwarder* AsShadowForwarder() + { return nullptr; } + + virtual LayerManagerComposite* AsLayerManagerComposite() + { return nullptr; } + + virtual ClientLayerManager* AsClientLayerManager() + { return nullptr; } + + virtual BasicLayerManager* AsBasicLayerManager() + { return nullptr; } + + /** + * Returns true if this LayerManager is owned by an nsIWidget, + * and is used for drawing into the widget. + */ + virtual bool IsWidgetLayerManager() { return true; } + virtual bool IsInactiveLayerManager() { return false; } + + /** + * Start a new transaction. Nested transactions are not allowed so + * there must be no transaction currently in progress. + * This transaction will update the state of the window from which + * this LayerManager was obtained. + */ + virtual bool BeginTransaction() = 0; + /** + * Start a new transaction. Nested transactions are not allowed so + * there must be no transaction currently in progress. + * This transaction will render the contents of the layer tree to + * the given target context. The rendering will be complete when + * EndTransaction returns. + */ + virtual bool BeginTransactionWithTarget(gfxContext* aTarget) = 0; + + enum EndTransactionFlags { + END_DEFAULT = 0, + END_NO_IMMEDIATE_REDRAW = 1 << 0, // Do not perform the drawing phase + END_NO_COMPOSITE = 1 << 1, // Do not composite after drawing painted layer contents. + END_NO_REMOTE_COMPOSITE = 1 << 2 // Do not schedule a composition with a remote Compositor, if one exists. + }; + + FrameLayerBuilder* GetLayerBuilder() { + return reinterpret_cast<FrameLayerBuilder*>(GetUserData(&gLayerManagerLayerBuilder)); + } + + /** + * Attempts to end an "empty transaction". There must have been no + * changes to the layer tree since the BeginTransaction(). + * It's possible for this to fail; PaintedLayers may need to be updated + * due to VRAM data being lost, for example. In such cases this method + * returns false, and the caller must proceed with a normal layer tree + * update and EndTransaction. + */ + virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) = 0; + + /** + * Function called to draw the contents of each PaintedLayer. + * aRegionToDraw contains the region that needs to be drawn. + * This would normally be a subregion of the visible region. + * The callee must draw all of aRegionToDraw. Drawing outside + * aRegionToDraw will be clipped out or ignored. + * The callee must draw all of aRegionToDraw. + * This region is relative to 0,0 in the PaintedLayer. + * + * aDirtyRegion should contain the total region that is be due to be painted + * during the transaction, even though only aRegionToDraw should be drawn + * during this call. aRegionToDraw must be entirely contained within + * aDirtyRegion. If the total dirty region is unknown it is okay to pass a + * subregion of the total dirty region, e.g. just aRegionToDraw, though it + * may not be as efficient. + * + * aRegionToInvalidate contains a region whose contents have been + * changed by the layer manager and which must therefore be invalidated. + * For example, this could be non-empty if a retained layer internally + * switches from RGBA to RGB or back ... we might want to repaint it to + * consistently use subpixel-AA or not. + * This region is relative to 0,0 in the PaintedLayer. + * aRegionToInvalidate may contain areas that are outside + * aRegionToDraw; the callee must ensure that these areas are repainted + * in the current layer manager transaction or in a later layer + * manager transaction. + * + * aContext must not be used after the call has returned. + * We guarantee that buffered contents in the visible + * region are valid once drawing is complete. + * + * The origin of aContext is 0,0 in the PaintedLayer. + */ + typedef void (* DrawPaintedLayerCallback)(PaintedLayer* aLayer, + gfxContext* aContext, + const nsIntRegion& aRegionToDraw, + const nsIntRegion& aDirtyRegion, + DrawRegionClip aClip, + const nsIntRegion& aRegionToInvalidate, + void* aCallbackData); + + /** + * Finish the construction phase of the transaction, perform the + * drawing phase, and end the transaction. + * During the drawing phase, all PaintedLayers in the tree are + * drawn in tree order, exactly once each, except for those layers + * where it is known that the visible region is empty. + */ + virtual void EndTransaction(DrawPaintedLayerCallback aCallback, + void* aCallbackData, + EndTransactionFlags aFlags = END_DEFAULT) = 0; + + /** + * Schedule a composition with the remote Compositor, if one exists + * for this LayerManager. Useful in conjunction with the END_NO_REMOTE_COMPOSITE + * flag to EndTransaction. + */ + virtual void Composite() {} + + virtual bool HasShadowManagerInternal() const { return false; } + bool HasShadowManager() const { return HasShadowManagerInternal(); } + virtual void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>& aConfigurations) {} + bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; } + + + /** + * Returns true if the layer manager can't render component alpha + * layers, and layer building should do it's best to avoid + * creating them. + */ + virtual bool ShouldAvoidComponentAlphaLayers() { return false; } + + /** + * Returns true if this LayerManager can properly support layers with + * SurfaceMode::SURFACE_COMPONENT_ALPHA. LayerManagers that can't will use + * transparent surfaces (and lose subpixel-AA for text). + */ + virtual bool AreComponentAlphaLayersEnabled(); + + /** + * CONSTRUCTION PHASE ONLY + * Set the root layer. The root layer is initially null. If there is + * no root layer, EndTransaction won't draw anything. + */ + virtual void SetRoot(Layer* aLayer) = 0; + /** + * Can be called anytime + */ + Layer* GetRoot() { return mRoot; } + + /** + * Does a breadth-first search from the root layer to find the first + * scrollable layer, and returns its ViewID. Note that there may be + * other layers in the tree which share the same ViewID. + * Can be called any time. + */ + FrameMetrics::ViewID GetRootScrollableLayerId(); + + /** + * Returns a LayerMetricsWrapper containing the Root + * Content Documents layer. + */ + LayerMetricsWrapper GetRootContentLayer(); + + /** + * CONSTRUCTION PHASE ONLY + * Called when a managee has mutated. + * Subclasses overriding this method must first call their + * superclass's impl + */ +#ifdef DEBUG + // In debug builds, we check some properties of |aLayer|. + virtual void Mutated(Layer* aLayer); +#else + virtual void Mutated(Layer* aLayer) { } +#endif + + /** + * Hints that can be used during PaintedLayer creation to influence the type + * or properties of the layer created. + * + * NONE: No hint. + * SCROLLABLE: This layer may represent scrollable content. + */ + enum PaintedLayerCreationHint { + NONE, SCROLLABLE + }; + + /** + * CONSTRUCTION PHASE ONLY + * Create a PaintedLayer for this manager's layer tree. + */ + virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a PaintedLayer for this manager's layer tree, with a creation hint + * parameter to help optimise the type of layer created. + */ + virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint) { + return CreatePaintedLayer(); + } + /** + * CONSTRUCTION PHASE ONLY + * Create a ContainerLayer for this manager's layer tree. + */ + virtual already_AddRefed<ContainerLayer> CreateContainerLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create an ImageLayer for this manager's layer tree. + */ + virtual already_AddRefed<ImageLayer> CreateImageLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a ColorLayer for this manager's layer tree. + */ + virtual already_AddRefed<ColorLayer> CreateColorLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a CanvasLayer for this manager's layer tree. + */ + virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a ReadbackLayer for this manager's layer tree. + */ + virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() { return nullptr; } + /** + * CONSTRUCTION PHASE ONLY + * Create a RefLayer for this manager's layer tree. + */ + virtual already_AddRefed<RefLayer> CreateRefLayer() { return nullptr; } + + + /** + * Can be called anytime, from any thread. + * + * Creates an Image container which forwards its images to the compositor within + * layer transactions on the main thread or asynchronously using the ImageBridge IPDL protocol. + * In the case of asynchronous, If the protocol is not available, the returned ImageContainer + * will forward images within layer transactions. + */ + static already_AddRefed<ImageContainer> CreateImageContainer(ImageContainer::Mode flag + = ImageContainer::SYNCHRONOUS); + + /** + * Type of layer manager his is. This is to be used sparsely in order to + * avoid a lot of Layers backend specific code. It should be used only when + * Layers backend specific functionality is necessary. + */ + virtual LayersBackend GetBackendType() = 0; + + /** + * Type of layers backend that will be used to composite this layer tree. + * When compositing is done remotely, then this returns the layers type + * of the compositor. + */ + virtual LayersBackend GetCompositorBackendType() { return GetBackendType(); } + + /** + * Creates a DrawTarget which is optimized for inter-operating with this + * layer manager. + */ + virtual already_AddRefed<DrawTarget> + CreateOptimalDrawTarget(const IntSize &aSize, + SurfaceFormat imageFormat); + + /** + * Creates a DrawTarget for alpha masks which is optimized for inter- + * operating with this layer manager. In contrast to CreateOptimalDrawTarget, + * this surface is optimised for drawing alpha only and we assume that + * drawing the mask is fairly simple. + */ + virtual already_AddRefed<DrawTarget> + CreateOptimalMaskDrawTarget(const IntSize &aSize); + + /** + * Creates a DrawTarget for use with canvas which is optimized for + * inter-operating with this layermanager. + */ + virtual already_AddRefed<mozilla::gfx::DrawTarget> + CreateDrawTarget(const mozilla::gfx::IntSize &aSize, + mozilla::gfx::SurfaceFormat aFormat); + + /** + * Creates a PersistentBufferProvider for use with canvas which is optimized for + * inter-operating with this layermanager. + */ + virtual already_AddRefed<PersistentBufferProvider> + CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize, + mozilla::gfx::SurfaceFormat aFormat); + + virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) { return true; } + + /** + * returns the maximum texture size on this layer backend, or INT32_MAX + * if there is no maximum + */ + virtual int32_t GetMaxTextureSize() const = 0; + + /** + * Return the name of the layer manager's backend. + */ + virtual void GetBackendName(nsAString& aName) = 0; + + /** + * This setter can be used anytime. The user data for all keys is + * initially null. Ownership pases to the layer manager. + */ + void SetUserData(void* aKey, LayerUserData* aData) + { + mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerUserDataDestroy); + } + /** + * This can be used anytime. Ownership passes to the caller! + */ + UniquePtr<LayerUserData> RemoveUserData(void* aKey); + + /** + * This getter can be used anytime. + */ + bool HasUserData(void* aKey) + { + return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey)); + } + /** + * This getter can be used anytime. Ownership is retained by the layer + * manager. + */ + LayerUserData* GetUserData(void* aKey) const + { + return static_cast<LayerUserData*>(mUserData.Get(static_cast<gfx::UserDataKey*>(aKey))); + } + + /** + * Must be called outside of a layers transaction. + * + * For the subtree rooted at |aSubtree|, this attempts to free up + * any free-able resources like retained buffers, but may do nothing + * at all. After this call, the layer tree is left in an undefined + * state; the layers in |aSubtree|'s subtree may no longer have + * buffers with valid content and may no longer be able to draw + * their visible and valid regions. + * + * In general, a painting or forwarding transaction on |this| must + * complete on the tree before it returns to a valid state. + * + * Resource freeing begins from |aSubtree| or |mRoot| if |aSubtree| + * is null. |aSubtree|'s manager must be this. + */ + virtual void ClearCachedResources(Layer* aSubtree = nullptr) {} + + /** + * Flag the next paint as the first for a document. + */ + virtual void SetIsFirstPaint() {} + + /** + * Make sure that the previous transaction has been entirely + * completed. + * + * Note: This may sychronously wait on a remote compositor + * to complete rendering. + */ + virtual void FlushRendering() { } + + /** + * Checks if we need to invalidate the OS widget to trigger + * painting when updating this layer manager. + */ + virtual bool NeedsWidgetInvalidation() { return true; } + + virtual const char* Name() const { return "???"; } + + /** + * Dump information about this layer manager and its managed tree to + * aStream. + */ + void Dump(std::stringstream& aStream, const char* aPrefix="", + bool aDumpHtml=false, bool aSorted=false); + /** + * Dump information about just this layer manager itself to aStream + */ + void DumpSelf(std::stringstream& aStream, const char* aPrefix="", bool aSorted=false); + void Dump(bool aSorted=false); + + /** + * Dump information about this layer manager and its managed tree to + * layerscope packet. + */ + void Dump(layerscope::LayersPacket* aPacket); + + /** + * Log information about this layer manager and its managed tree to + * the NSPR log (if enabled for "Layers"). + */ + void Log(const char* aPrefix=""); + /** + * Log information about just this layer manager itself to the NSPR + * log (if enabled for "Layers"). + */ + void LogSelf(const char* aPrefix=""); + + /** + * Record (and return) frame-intervals and paint-times for frames which were presented + * between calling StartFrameTimeRecording and StopFrameTimeRecording. + * + * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late + * (elements were overwritten since Start), result is considered invalid and hence empty. + * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent). + * Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize. + * - Note: the first frame-interval may be longer than expected because last frame + * might have been presented some time before calling StartFrameTimeRecording. + */ + + /** + * Returns a handle which represents current recording start position. + */ + virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize); + + /** + * Clears, then populates aFrameIntervals with the recorded frame timing + * data. The array will be empty if data was overwritten since + * aStartIndex was obtained. + */ + virtual void StopFrameTimeRecording(uint32_t aStartIndex, + nsTArray<float>& aFrameIntervals); + + void RecordFrame(); + void PostPresent(); + + void BeginTabSwitch(); + + static bool IsLogEnabled(); + static mozilla::LogModule* GetLog(); + + bool IsCompositingCheap(LayersBackend aBackend) + { + // LayersBackend::LAYERS_NONE is an error state, but in that case we should try to + // avoid loading the compositor! + return LayersBackend::LAYERS_BASIC != aBackend && LayersBackend::LAYERS_NONE != aBackend; + } + + virtual bool IsCompositingCheap() { return true; } + + bool IsInTransaction() const { return mInTransaction; } + virtual void GetFrameUniformity(FrameUniformityData* aOutData) { } + virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) { return true; } + virtual void RunOverfillCallback(const uint32_t aOverfill) { } + + virtual void SetRegionToClear(const nsIntRegion& aRegion) + { + mRegionToClear = aRegion; + } + + virtual float RequestProperty(const nsAString& property) { return -1; } + + const TimeStamp& GetAnimationReadyTime() const { + return mAnimationReadyTime; + } + + virtual bool AsyncPanZoomEnabled() const { + return false; + } + + static void LayerUserDataDestroy(void* data); + + void AddPaintedPixelCount(int32_t aCount) { + mPaintedPixelCount += aCount; + } + + uint32_t GetAndClearPaintedPixelCount() { + uint32_t count = mPaintedPixelCount; + mPaintedPixelCount = 0; + return count; + } + +protected: + RefPtr<Layer> mRoot; + gfx::UserData mUserData; + bool mDestroyed; + bool mSnapEffectiveTransforms; + + nsIntRegion mRegionToClear; + + // Protected destructor, to discourage deletion outside of Release(): + virtual ~LayerManager() {} + + // Print interesting information about this into aStreamo. Internally + // used to implement Dump*() and Log*(). + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix); + + // Print interesting information about this into layerscope packet. + // Internally used to implement Dump(). + virtual void DumpPacket(layerscope::LayersPacket* aPacket); + + uint64_t mId; + bool mInTransaction; + // The time when painting most recently finished. This is recorded so that + // we can time any play-pending animations from this point. + TimeStamp mAnimationReadyTime; + // The count of pixels that were painted in the current transaction. + uint32_t mPaintedPixelCount; +private: + struct FramesTimingRecording + { + // Stores state and data for frame intervals and paint times recording. + // see LayerManager::StartFrameTimeRecording() at Layers.cpp for more details. + FramesTimingRecording() + : mNextIndex(0) + , mLatestStartIndex(0) + , mCurrentRunStartIndex(0) + , mIsPaused(true) + {} + nsTArray<float> mIntervals; + TimeStamp mLastFrameTime; + uint32_t mNextIndex; + uint32_t mLatestStartIndex; + uint32_t mCurrentRunStartIndex; + bool mIsPaused; + }; + FramesTimingRecording mRecording; + + TimeStamp mTabSwitchStart; + +public: + /* + * Methods to store/get/clear a "pending scroll info update" object on a + * per-scrollid basis. This is used for empty transactions that push over + * scroll position updates to the APZ code. + */ + void SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId, + const ScrollUpdateInfo& aUpdateInfo); + Maybe<ScrollUpdateInfo> GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId); + void ClearPendingScrollInfoUpdate(); +private: + std::map<FrameMetrics::ViewID,ScrollUpdateInfo> mPendingScrollUpdates; +}; + +typedef InfallibleTArray<Animation> AnimationArray; + +struct AnimData { + InfallibleTArray<mozilla::StyleAnimationValue> mStartValues; + InfallibleTArray<mozilla::StyleAnimationValue> mEndValues; + InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions; +}; + +/** + * A Layer represents anything that can be rendered onto a destination + * surface. + */ +class Layer { + NS_INLINE_DECL_REFCOUNTING(Layer) + +public: + // Keep these in alphabetical order + enum LayerType { + TYPE_CANVAS, + TYPE_COLOR, + TYPE_CONTAINER, + TYPE_IMAGE, + TYPE_READBACK, + TYPE_REF, + TYPE_SHADOW, + TYPE_PAINTED + }; + + /** + * Returns the LayerManager this Layer belongs to. Note that the layer + * manager might be in a destroyed state, at which point it's only + * valid to set/get user data from it. + */ + LayerManager* Manager() { return mManager; } + + enum { + /** + * If this is set, the caller is promising that by the end of this + * transaction the entire visible region (as specified by + * SetVisibleRegion) will be filled with opaque content. + */ + CONTENT_OPAQUE = 0x01, + /** + * If this is set, the caller is notifying that the contents of this layer + * require per-component alpha for optimal fidelity. However, there is no + * guarantee that component alpha will be supported for this layer at + * paint time. + * This should never be set at the same time as CONTENT_OPAQUE. + */ + CONTENT_COMPONENT_ALPHA = 0x02, + + /** + * If this is set then one of the descendant layers of this one has + * CONTENT_COMPONENT_ALPHA set. + */ + CONTENT_COMPONENT_ALPHA_DESCENDANT = 0x04, + + /** + * If this is set then this layer is part of a preserve-3d group, and should + * be sorted with sibling layers that are also part of the same group. + */ + CONTENT_EXTEND_3D_CONTEXT = 0x08, + /** + * This indicates that the transform may be changed on during an empty + * transaction where there is no possibility of redrawing the content, so the + * implementation should be ready for that. + */ + CONTENT_MAY_CHANGE_TRANSFORM = 0x10, + + /** + * Disable subpixel AA for this layer. This is used if the display isn't suited + * for subpixel AA like hidpi or rotated content. + */ + CONTENT_DISABLE_SUBPIXEL_AA = 0x20, + + /** + * If this is set then the layer contains content that may look objectionable + * if not handled as an active layer (such as text with an animated transform). + * This is for internal layout/FrameLayerBuilder usage only until flattening + * code is obsoleted. See bug 633097 + */ + CONTENT_DISABLE_FLATTENING = 0x40, + + /** + * This layer is hidden if the backface of the layer is visible + * to user. + */ + CONTENT_BACKFACE_HIDDEN = 0x80 + }; + /** + * CONSTRUCTION PHASE ONLY + * This lets layout make some promises about what will be drawn into the + * visible region of the PaintedLayer. This enables internal quality + * and performance optimizations. + */ + void SetContentFlags(uint32_t aFlags) + { + NS_ASSERTION((aFlags & (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA)) != + (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA), + "Can't be opaque and require component alpha"); + if (mContentFlags != aFlags) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ContentFlags", this)); + mContentFlags = aFlags; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * The union of the bounds of all the display item that got flattened + * into this layer. This is intended to be an approximation to the + * size of the layer if the nearest scrollable ancestor had an infinitely + * large displayport. Computing this more exactly is too expensive, + * but this approximation is sufficient for what we need to use it for. + */ + virtual void SetLayerBounds(const gfx::IntRect& aLayerBounds) + { + if (!mLayerBounds.IsEqualEdges(aLayerBounds)) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) LayerBounds", this)); + mLayerBounds = aLayerBounds; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Tell this layer which region will be visible. The visible region + * is a region which contains all the contents of the layer that can + * actually affect the rendering of the window. It can exclude areas + * that are covered by opaque contents of other layers, and it can + * exclude areas where this layer simply contains no content at all. + * (This can be an overapproximation to the "true" visible region.) + * + * There is no general guarantee that drawing outside the bounds of the + * visible region will be ignored. So if a layer draws outside the bounds + * of its visible region, it needs to ensure that what it draws is valid. + */ + virtual void SetVisibleRegion(const LayerIntRegion& aRegion) + { + // IsEmpty is required otherwise we get invalidation glitches. + // See bug 1288464 for investigating why. + if (!mVisibleRegion.IsEqual(aRegion) || aRegion.IsEmpty()) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) VisibleRegion was %s is %s", this, + mVisibleRegion.ToString().get(), aRegion.ToString().get())); + mVisibleRegion = aRegion; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Set the (sub)document metrics used to render the Layer subtree + * rooted at this. Note that a layer may have multiple FrameMetrics + * objects; calling this function will remove all of them and replace + * them with the provided FrameMetrics. See the documentation for + * SetFrameMetrics(const nsTArray<FrameMetrics>&) for more details. + */ + void SetScrollMetadata(const ScrollMetadata& aScrollMetadata) + { + Manager()->ClearPendingScrollInfoUpdate(); + if (mScrollMetadata.Length() != 1 || mScrollMetadata[0] != aScrollMetadata) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this)); + mScrollMetadata.ReplaceElementsAt(0, mScrollMetadata.Length(), aScrollMetadata); + ScrollMetadataChanged(); + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Set the (sub)document metrics used to render the Layer subtree + * rooted at this. There might be multiple metrics on this layer + * because the layer may, for example, be contained inside multiple + * nested scrolling subdocuments. In general a Layer having multiple + * ScrollMetadata objects is conceptually equivalent to having a stack + * of ContainerLayers that have been flattened into this Layer. + * See the documentation in LayerMetricsWrapper.h for a more detailed + * explanation of this conceptual equivalence. + * + * Note also that there is actually a many-to-many relationship between + * Layers and ScrollMetadata, because multiple Layers may have identical + * ScrollMetadata objects. This happens when those layers belong to the + * same scrolling subdocument and therefore end up with the same async + * transform when they are scrolled by the APZ code. + */ + void SetScrollMetadata(const nsTArray<ScrollMetadata>& aMetadataArray) + { + Manager()->ClearPendingScrollInfoUpdate(); + if (mScrollMetadata != aMetadataArray) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this)); + mScrollMetadata = aMetadataArray; + ScrollMetadataChanged(); + Mutated(); + } + } + + /* + * Compositor event handling + * ========================= + * When a touch-start event (or similar) is sent to the AsyncPanZoomController, + * it needs to decide whether the event should be sent to the main thread. + * Each layer has a list of event handling regions. When the compositor needs + * to determine how to handle a touch event, it scans the layer tree from top + * to bottom in z-order (traversing children before their parents). Points + * outside the clip region for a layer cause that layer (and its subtree) + * to be ignored. If a layer has a mask layer, and that mask layer's alpha + * value is zero at the event point, then the layer and its subtree should + * be ignored. + * For each layer, if the point is outside its hit region, we ignore the layer + * and move onto the next. If the point is inside its hit region but + * outside the dispatch-to-content region, we can initiate a gesture without + * consulting the content thread. Otherwise we must dispatch the event to + * content. + * Note that if a layer or any ancestor layer has a ForceEmptyHitRegion + * override in GetEventRegionsOverride() then the hit-region must be treated + * as empty. Similarly, if there is a ForceDispatchToContent override then + * the dispatch-to-content region must be treated as encompassing the entire + * hit region, and therefore we must consult the content thread before + * initiating a gesture. (If both flags are set, ForceEmptyHitRegion takes + * priority.) + */ + /** + * CONSTRUCTION PHASE ONLY + * Set the event handling region. + */ + void SetEventRegions(const EventRegions& aRegions) + { + if (mEventRegions != aRegions) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) eventregions were %s, now %s", this, + mEventRegions.ToString().get(), aRegions.ToString().get())); + mEventRegions = aRegions; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Set the opacity which will be applied to this layer as it + * is composited to the destination. + */ + void SetOpacity(float aOpacity) + { + if (mOpacity != aOpacity) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Opacity", this)); + mOpacity = aOpacity; + Mutated(); + } + } + + void SetMixBlendMode(gfx::CompositionOp aMixBlendMode) + { + if (mMixBlendMode != aMixBlendMode) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MixBlendMode", this)); + mMixBlendMode = aMixBlendMode; + Mutated(); + } + } + + void SetForceIsolatedGroup(bool aForceIsolatedGroup) + { + if(mForceIsolatedGroup != aForceIsolatedGroup) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceIsolatedGroup", this)); + mForceIsolatedGroup = aForceIsolatedGroup; + Mutated(); + } + } + + bool GetForceIsolatedGroup() const + { + return mForceIsolatedGroup; + } + + /** + * CONSTRUCTION PHASE ONLY + * Set a clip rect which will be applied to this layer as it is + * composited to the destination. The coordinates are relative to + * the parent layer (i.e. the contents of this layer + * are transformed before this clip rect is applied). + * For the root layer, the coordinates are relative to the widget, + * in device pixels. + * If aRect is null no clipping will be performed. + */ + void SetClipRect(const Maybe<ParentLayerIntRect>& aRect) + { + if (mClipRect) { + if (!aRect) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is <none>", this, + mClipRect->x, mClipRect->y, mClipRect->width, mClipRect->height)); + mClipRect.reset(); + Mutated(); + } else { + if (!aRect->IsEqualEdges(*mClipRect)) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is %d,%d,%d,%d", this, + mClipRect->x, mClipRect->y, mClipRect->width, mClipRect->height, + aRect->x, aRect->y, aRect->width, aRect->height)); + mClipRect = aRect; + Mutated(); + } + } + } else { + if (aRect) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was <none> is %d,%d,%d,%d", this, + aRect->x, aRect->y, aRect->width, aRect->height)); + mClipRect = aRect; + Mutated(); + } + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Set an optional scrolled clip on the layer. + * The scrolled clip, if present, consists of a clip rect and an optional mask. + * This scrolled clip is always scrolled by all scroll frames associated with + * this layer. (By contrast, the scroll clips stored in ScrollMetadata are + * only scrolled by scroll frames above that ScrollMetadata, and the layer's + * mClipRect is always fixed to the layer contents (which may or may not be + * scrolled by some of the scroll frames associated with the layer, depending + * on whether the layer is fixed).) + */ + void SetScrolledClip(const Maybe<LayerClip>& aScrolledClip) + { + if (mScrolledClip != aScrolledClip) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrolledClip", this)); + mScrolledClip = aScrolledClip; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Set a layer to mask this layer. + * + * The mask layer should be applied using its effective transform (after it + * is calculated by ComputeEffectiveTransformForMaskLayer), this should use + * this layer's parent's transform and the mask layer's transform, but not + * this layer's. That is, the mask layer is specified relative to this layer's + * position in it's parent layer's coord space. + * Currently, only 2D translations are supported for the mask layer transform. + * + * Ownership of aMaskLayer passes to this. + * Typical use would be an ImageLayer with an alpha image used for masking. + * See also ContainerState::BuildMaskLayer in FrameLayerBuilder.cpp. + */ + void SetMaskLayer(Layer* aMaskLayer) + { +#ifdef DEBUG + if (aMaskLayer) { + bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D(); + NS_ASSERTION(maskIs2D, "Mask layer has invalid transform."); + } +#endif + + if (mMaskLayer != aMaskLayer) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MaskLayer", this)); + mMaskLayer = aMaskLayer; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Add mask layers associated with LayerClips. + */ + void SetAncestorMaskLayers(const nsTArray<RefPtr<Layer>>& aLayers) { + if (aLayers != mAncestorMaskLayers) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AncestorMaskLayers", this)); + mAncestorMaskLayers = aLayers; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * Add a mask layer associated with a LayerClip. + */ + void AddAncestorMaskLayer(const RefPtr<Layer>& aLayer) { + mAncestorMaskLayers.AppendElement(aLayer); + Mutated(); + } + + /** + * CONSTRUCTION PHASE ONLY + * Tell this layer what its transform should be. The transformation + * is applied when compositing the layer into its parent container. + */ + void SetBaseTransform(const gfx::Matrix4x4& aMatrix) + { + NS_ASSERTION(!aMatrix.IsSingular(), + "Shouldn't be trying to draw with a singular matrix!"); + mPendingTransform = nullptr; + if (mTransform == aMatrix) { + return; + } + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) BaseTransform", this)); + mTransform = aMatrix; + Mutated(); + } + + /** + * Can be called at any time. + * + * Like SetBaseTransform(), but can be called before the next + * transform (i.e. outside an open transaction). Semantically, this + * method enqueues a new transform value to be set immediately after + * the next transaction is opened. + */ + void SetBaseTransformForNextTransaction(const gfx::Matrix4x4& aMatrix) + { + mPendingTransform = new gfx::Matrix4x4(aMatrix); + } + + void SetPostScale(float aXScale, float aYScale) + { + if (mPostXScale == aXScale && mPostYScale == aYScale) { + return; + } + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PostScale", this)); + mPostXScale = aXScale; + mPostYScale = aYScale; + Mutated(); + } + + /** + * CONSTRUCTION PHASE ONLY + * A layer is "fixed position" when it draws content from a content + * (not chrome) document, the topmost content document has a root scrollframe + * with a displayport, but the layer does not move when that displayport scrolls. + */ + void SetIsFixedPosition(bool aFixedPosition) + { + if (mIsFixedPosition != aFixedPosition) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) IsFixedPosition", this)); + mIsFixedPosition = aFixedPosition; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * This flag is true when the transform on the layer is a perspective + * transform. The compositor treats perspective transforms specially + * for async scrolling purposes. + */ + void SetTransformIsPerspective(bool aTransformIsPerspective) + { + if (mTransformIsPerspective != aTransformIsPerspective) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) TransformIsPerspective", this)); + mTransformIsPerspective = aTransformIsPerspective; + Mutated(); + } + } + + // Call AddAnimation to add a new animation to this layer from layout code. + // Caller must fill in all the properties of the returned animation. + // A later animation overrides an earlier one. + Animation* AddAnimation(); + // ClearAnimations clears animations on this layer. + void ClearAnimations(); + // This is only called when the layer tree is updated. Do not call this from + // layout code. To add an animation to this layer, use AddAnimation. + void SetAnimations(const AnimationArray& aAnimations); + // Go through all animations in this layer and its children and, for + // any animations with a null start time, update their start time such + // that at |aReadyTime| the animation's current time corresponds to its + // 'initial current time' value. + void StartPendingAnimations(const TimeStamp& aReadyTime); + + // These are a parallel to AddAnimation and clearAnimations, except + // they add pending animations that apply only when the next + // transaction is begun. (See also + // SetBaseTransformForNextTransaction.) + Animation* AddAnimationForNextTransaction(); + void ClearAnimationsForNextTransaction(); + + /** + * CONSTRUCTION PHASE ONLY + * If a layer represents a fixed position element, this data is stored on the + * layer for use by the compositor. + * + * - |aScrollId| identifies the scroll frame that this element is fixed + * with respect to. + * + * - |aAnchor| is the point on the layer that is considered the "anchor" + * point, that is, the point which remains in the same position when + * compositing the layer tree with a transformation (such as when + * asynchronously scrolling and zooming). + * + * - |aSides| is the set of sides to which the element is fixed relative to. + * This is used if the viewport size is changed in the compositor and + * fixed position items need to shift accordingly. This value is made up + * combining appropriate values from mozilla::SideBits. + */ + void SetFixedPositionData(FrameMetrics::ViewID aScrollId, + const LayerPoint& aAnchor, + int32_t aSides) + { + if (!mFixedPositionData || + mFixedPositionData->mScrollId != aScrollId || + mFixedPositionData->mAnchor != aAnchor || + mFixedPositionData->mSides != aSides) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FixedPositionData", this)); + if (!mFixedPositionData) { + mFixedPositionData = MakeUnique<FixedPositionData>(); + } + mFixedPositionData->mScrollId = aScrollId; + mFixedPositionData->mAnchor = aAnchor; + mFixedPositionData->mSides = aSides; + Mutated(); + } + } + + /** + * CONSTRUCTION PHASE ONLY + * If a layer is "sticky position", |aScrollId| holds the scroll identifier + * of the scrollable content that contains it. The difference between the two + * rectangles |aOuter| and |aInner| is treated as two intervals in each + * dimension, with the current scroll position at the origin. For each + * dimension, while that component of the scroll position lies within either + * interval, the layer should not move relative to its scrolling container. + */ + void SetStickyPositionData(FrameMetrics::ViewID aScrollId, LayerRect aOuter, + LayerRect aInner) + { + if (!mStickyPositionData || + !mStickyPositionData->mOuter.IsEqualEdges(aOuter) || + !mStickyPositionData->mInner.IsEqualEdges(aInner)) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) StickyPositionData", this)); + if (!mStickyPositionData) { + mStickyPositionData = new StickyPositionData; + } + mStickyPositionData->mScrollId = aScrollId; + mStickyPositionData->mOuter = aOuter; + mStickyPositionData->mInner = aInner; + Mutated(); + } + } + + enum ScrollDirection { + NONE, + VERTICAL, + HORIZONTAL + }; + + /** + * CONSTRUCTION PHASE ONLY + * If a layer is a scrollbar layer, |aScrollId| holds the scroll identifier + * of the scrollable content that the scrollbar is for. + */ + void SetScrollbarData(FrameMetrics::ViewID aScrollId, ScrollDirection aDir, float aThumbRatio) + { + if (mScrollbarTargetId != aScrollId || + mScrollbarDirection != aDir || + mScrollbarThumbRatio != aThumbRatio) + { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrollbarData", this)); + mScrollbarTargetId = aScrollId; + mScrollbarDirection = aDir; + mScrollbarThumbRatio = aThumbRatio; + Mutated(); + } + } + + // Set during construction for the container layer of scrollbar components. + void SetIsScrollbarContainer() + { + if (!mIsScrollbarContainer) { + mIsScrollbarContainer = true; + Mutated(); + } + } + + // These getters can be used anytime. + float GetOpacity() { return mOpacity; } + gfx::CompositionOp GetMixBlendMode() const { return mMixBlendMode; } + const Maybe<ParentLayerIntRect>& GetClipRect() const { return mClipRect; } + const Maybe<LayerClip>& GetScrolledClip() const { return mScrolledClip; } + Maybe<ParentLayerIntRect> GetScrolledClipRect() const; + uint32_t GetContentFlags() { return mContentFlags; } + const gfx::IntRect& GetLayerBounds() const { return mLayerBounds; } + const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; } + const ScrollMetadata& GetScrollMetadata(uint32_t aIndex) const; + const FrameMetrics& GetFrameMetrics(uint32_t aIndex) const; + uint32_t GetScrollMetadataCount() const { return mScrollMetadata.Length(); } + const nsTArray<ScrollMetadata>& GetAllScrollMetadata() { return mScrollMetadata; } + bool HasScrollableFrameMetrics() const; + bool IsScrollInfoLayer() const; + const EventRegions& GetEventRegions() const { return mEventRegions; } + ContainerLayer* GetParent() { return mParent; } + Layer* GetNextSibling() { + if (mNextSibling) { + mNextSibling->CheckCanary(); + } + return mNextSibling; + } + const Layer* GetNextSibling() const { + if (mNextSibling) { + mNextSibling->CheckCanary(); + } + return mNextSibling; + } + Layer* GetPrevSibling() { return mPrevSibling; } + const Layer* GetPrevSibling() const { return mPrevSibling; } + virtual Layer* GetFirstChild() const { return nullptr; } + virtual Layer* GetLastChild() const { return nullptr; } + gfx::Matrix4x4 GetTransform() const; + // Same as GetTransform(), but returns the transform as a strongly-typed + // matrix. Eventually this will replace GetTransform(). + const CSSTransformMatrix GetTransformTyped() const; + const gfx::Matrix4x4& GetBaseTransform() const { return mTransform; } + // Note: these are virtual because ContainerLayerComposite overrides them. + virtual float GetPostXScale() const { return mPostXScale; } + virtual float GetPostYScale() const { return mPostYScale; } + bool GetIsFixedPosition() { return mIsFixedPosition; } + bool GetTransformIsPerspective() const { return mTransformIsPerspective; } + bool GetIsStickyPosition() { return mStickyPositionData; } + FrameMetrics::ViewID GetFixedPositionScrollContainerId() { return mFixedPositionData ? mFixedPositionData->mScrollId : FrameMetrics::NULL_SCROLL_ID; } + LayerPoint GetFixedPositionAnchor() { return mFixedPositionData ? mFixedPositionData->mAnchor : LayerPoint(); } + int32_t GetFixedPositionSides() { return mFixedPositionData ? mFixedPositionData->mSides : eSideBitsNone; } + FrameMetrics::ViewID GetStickyScrollContainerId() { return mStickyPositionData->mScrollId; } + const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; } + const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; } + FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; } + ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; } + float GetScrollbarThumbRatio() { return mScrollbarThumbRatio; } + bool IsScrollbarContainer() { return mIsScrollbarContainer; } + Layer* GetMaskLayer() const { return mMaskLayer; } + void CheckCanary() const { mCanary.Check(); } + + // Ancestor mask layers are associated with FrameMetrics, but for simplicity + // in maintaining the layer tree structure we attach them to the layer. + size_t GetAncestorMaskLayerCount() const { + return mAncestorMaskLayers.Length(); + } + Layer* GetAncestorMaskLayerAt(size_t aIndex) const { + return mAncestorMaskLayers.ElementAt(aIndex); + } + const nsTArray<RefPtr<Layer>>& GetAllAncestorMaskLayers() const { + return mAncestorMaskLayers; + } + + bool HasMaskLayers() const { + return GetMaskLayer() || mAncestorMaskLayers.Length() > 0; + } + + /* + * Get the combined clip rect of the Layer clip and all clips on FrameMetrics. + * This is intended for use in Layout. The compositor needs to apply async + * transforms to find the combined clip. + */ + Maybe<ParentLayerIntRect> GetCombinedClipRect() const; + + /** + * Retrieve the root level visible region for |this| taking into account + * clipping applied to parent layers of |this| as well as subtracting + * visible regions of higher siblings of this layer and each ancestor. + * + * Note translation values for offsets of visible regions and accumulated + * aLayerOffset are integer rounded using IntPoint::Round. + * + * @param aResult - the resulting visible region of this layer. + * @param aLayerOffset - this layer's total offset from the root layer. + * @return - false if during layer tree traversal a parent or sibling + * transform is found to be non-translational. This method returns early + * in this case, results will not be valid. Returns true on successful + * traversal. + */ + bool GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult, + nsIntPoint* aLayerOffset); + + // Note that all lengths in animation data are either in CSS pixels or app + // units and must be converted to device pixels by the compositor. + AnimationArray& GetAnimations() { return mAnimations; } + InfallibleTArray<AnimData>& GetAnimationData() { return mAnimationData; } + + uint64_t GetAnimationGeneration() { return mAnimationGeneration; } + void SetAnimationGeneration(uint64_t aCount) { mAnimationGeneration = aCount; } + + bool HasTransformAnimation() const; + + /** + * Returns the local transform for this layer: either mTransform or, + * for shadow layers, GetShadowBaseTransform(), in either case with the + * pre- and post-scales applied. + */ + gfx::Matrix4x4 GetLocalTransform(); + + /** + * Same as GetLocalTransform(), but returns a strongly-typed matrix. + * Eventually, this will replace GetLocalTransform(). + */ + const LayerToParentLayerMatrix4x4 GetLocalTransformTyped(); + + /** + * Returns the local opacity for this layer: either mOpacity or, + * for shadow layers, GetShadowOpacity() + */ + float GetLocalOpacity(); + + /** + * DRAWING PHASE ONLY + * + * Apply pending changes to layers before drawing them, if those + * pending changes haven't been overridden by later changes. + */ + void ApplyPendingUpdatesToSubtree(); + + /** + * DRAWING PHASE ONLY + * + * Write layer-subtype-specific attributes into aAttrs. Used to + * synchronize layer attributes to their shadows'. + */ + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { } + + // Returns true if it's OK to save the contents of aLayer in an + // opaque surface (a surface without an alpha channel). + // If we can use a surface without an alpha channel, we should, because + // it will often make painting of antialiased text faster and higher + // quality. + bool CanUseOpaqueSurface(); + + SurfaceMode GetSurfaceMode() + { + if (CanUseOpaqueSurface()) + return SurfaceMode::SURFACE_OPAQUE; + if (mContentFlags & CONTENT_COMPONENT_ALPHA) + return SurfaceMode::SURFACE_COMPONENT_ALPHA; + return SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; + } + + // Returns true if this layer can be treated as opaque for visibility + // computation. A layer may be non-opaque for visibility even if it + // is not transparent, for example, if it has a mix-blend-mode. + bool IsOpaqueForVisibility(); + + /** + * This setter can be used anytime. The user data for all keys is + * initially null. Ownership pases to the layer manager. + */ + void SetUserData(void* aKey, LayerUserData* aData) + { + mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerManager::LayerUserDataDestroy); + } + /** + * This can be used anytime. Ownership passes to the caller! + */ + UniquePtr<LayerUserData> RemoveUserData(void* aKey); + /** + * This getter can be used anytime. + */ + bool HasUserData(void* aKey) + { + return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey)); + } + /** + * This getter can be used anytime. Ownership is retained by the layer + * manager. + */ + LayerUserData* GetUserData(void* aKey) const + { + return static_cast<LayerUserData*>(mUserData.Get(static_cast<gfx::UserDataKey*>(aKey))); + } + + /** + * |Disconnect()| is used by layers hooked up over IPC. It may be + * called at any time, and may not be called at all. Using an + * IPC-enabled layer after Destroy() (drawing etc.) results in a + * safe no-op; no crashy or uaf etc. + * + * XXX: this interface is essentially LayerManager::Destroy, but at + * Layer granularity. It might be beneficial to unify them. + */ + virtual void Disconnect() {} + + /** + * Dynamic downcast to a PaintedLayer. Returns null if this is not + * a PaintedLayer. + */ + virtual PaintedLayer* AsPaintedLayer() { return nullptr; } + + /** + * Dynamic cast to a ContainerLayer. Returns null if this is not + * a ContainerLayer. + */ + virtual ContainerLayer* AsContainerLayer() { return nullptr; } + virtual const ContainerLayer* AsContainerLayer() const { return nullptr; } + + /** + * Dynamic cast to a RefLayer. Returns null if this is not a + * RefLayer. + */ + virtual RefLayer* AsRefLayer() { return nullptr; } + + /** + * Dynamic cast to a Color. Returns null if this is not a + * ColorLayer. + */ + virtual ColorLayer* AsColorLayer() { return nullptr; } + + /** + * Dynamic cast to a LayerComposite. Return null if this is not a + * LayerComposite. Can be used anytime. + */ + virtual LayerComposite* AsLayerComposite() { return nullptr; } + + /** + * Dynamic cast to a ShadowableLayer. Return null if this is not a + * ShadowableLayer. Can be used anytime. + */ + virtual ShadowableLayer* AsShadowableLayer() { return nullptr; } + + // These getters can be used anytime. They return the effective + // values that should be used when drawing this layer to screen, + // accounting for this layer possibly being a shadow. + const Maybe<ParentLayerIntRect>& GetLocalClipRect(); + const LayerIntRegion& GetLocalVisibleRegion(); + + bool Extend3DContext() { + return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT; + } + bool Combines3DTransformWithAncestors() { + return GetParent() && + reinterpret_cast<Layer*>(GetParent())->Extend3DContext(); + } + bool Is3DContextLeaf() { + return !Extend3DContext() && Combines3DTransformWithAncestors(); + } + /** + * It is true if the user can see the back of the layer and the + * backface is hidden. The compositor should skip the layer if the + * result is true. + */ + bool IsBackfaceHidden(); + bool IsVisible() { + // For containers extending 3D context, visible region + // is meaningless, since they are just intermediate result of + // content. + return !GetLocalVisibleRegion().IsEmpty() || Extend3DContext(); + } + + /** + * Return true if current layer content is opaque. + * It does not guarantee that layer content is always opaque. + */ + virtual bool IsOpaque() { return GetContentFlags() & CONTENT_OPAQUE; } + + /** + * Returns the product of the opacities of this layer and all ancestors up + * to and excluding the nearest ancestor that has UseIntermediateSurface() set. + */ + float GetEffectiveOpacity(); + + /** + * Returns the blendmode of this layer. + */ + gfx::CompositionOp GetEffectiveMixBlendMode(); + + /** + * This returns the effective transform computed by + * ComputeEffectiveTransforms. Typically this is a transform that transforms + * this layer all the way to some intermediate surface or destination + * surface. For non-BasicLayers this will be a transform to the nearest + * ancestor with UseIntermediateSurface() (or to the root, if there is no + * such ancestor), but for BasicLayers it's different. + */ + const gfx::Matrix4x4& GetEffectiveTransform() const { return mEffectiveTransform; } + + /** + * This returns the effective transform for Layer's buffer computed by + * ComputeEffectiveTransforms. Typically this is a transform that transforms + * this layer's buffer all the way to some intermediate surface or destination + * surface. For non-BasicLayers this will be a transform to the nearest + * ancestor with UseIntermediateSurface() (or to the root, if there is no + * such ancestor), but for BasicLayers it's different. + * + * By default, its value is same to GetEffectiveTransform(). + * When ImageLayer is rendered with ScaleMode::STRETCH, + * it becomes different from GetEffectiveTransform(). + */ + virtual const gfx::Matrix4x4& GetEffectiveTransformForBuffer() const + { + return mEffectiveTransform; + } + + /** + * @param aTransformToSurface the composition of the transforms + * from the parent layer (if any) to the destination pixel grid. + * + * Computes mEffectiveTransform for this layer and all its descendants. + * mEffectiveTransform transforms this layer up to the destination + * pixel grid (whatever aTransformToSurface is relative to). + * + * We promise that when this is called on a layer, all ancestor layers + * have already had ComputeEffectiveTransforms called. + */ + virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) = 0; + + /** + * Computes the effective transform for mask layers, if this layer has any. + */ + void ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface); + static void ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer, + const gfx::Matrix4x4& aTransformToSurface); + + /** + * Calculate the scissor rect required when rendering this layer. + * Returns a rectangle relative to the intermediate surface belonging to the + * nearest ancestor that has an intermediate surface, or relative to the root + * viewport if no ancestor has an intermediate surface, corresponding to the + * clip rect for this layer intersected with aCurrentScissorRect. + */ + RenderTargetIntRect CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect); + + virtual const char* Name() const =0; + virtual LayerType GetType() const =0; + + /** + * Only the implementation should call this. This is per-implementation + * private data. Normally, all layers with a given layer manager + * use the same type of ImplData. + */ + void* ImplData() { return mImplData; } + + /** + * Only the implementation should use these methods. + */ + void SetParent(ContainerLayer* aParent) { mParent = aParent; } + void SetNextSibling(Layer* aSibling) { mNextSibling = aSibling; } + void SetPrevSibling(Layer* aSibling) { mPrevSibling = aSibling; } + + /** + * Dump information about this layer manager and its managed tree to + * aStream. + */ + void Dump(std::stringstream& aStream, const char* aPrefix="", + bool aDumpHtml=false, bool aSorted=false); + /** + * Dump information about just this layer manager itself to aStream. + */ + void DumpSelf(std::stringstream& aStream, const char* aPrefix=""); + + /** + * Dump information about this layer and its child & sibling layers to + * layerscope packet. + */ + void Dump(layerscope::LayersPacket* aPacket, const void* aParent); + + /** + * Log information about this layer manager and its managed tree to + * the NSPR log (if enabled for "Layers"). + */ + void Log(const char* aPrefix=""); + /** + * Log information about just this layer manager itself to the NSPR + * log (if enabled for "Layers"). + */ + void LogSelf(const char* aPrefix=""); + + // Print interesting information about this into aStream. Internally + // used to implement Dump*() and Log*(). If subclasses have + // additional interesting properties, they should override this with + // an implementation that first calls the base implementation then + // appends additional info to aTo. + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix); + + // Just like PrintInfo, but this function dump information into layerscope packet, + // instead of a StringStream. It is also internally used to implement Dump(); + virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent); + + /** + * Store display list log. + */ + void SetDisplayListLog(const char *log); + + /** + * Return display list log. + */ + void GetDisplayListLog(nsCString& log); + + static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); } + + /** + * Returns the current area of the layer (in layer-space coordinates) + * marked as needed to be recomposited. + */ + const virtual gfx::TiledIntRegion& GetInvalidRegion() { return mInvalidRegion; } + void AddInvalidRegion(const nsIntRegion& aRegion) { + mInvalidRegion.Add(aRegion); + } + + /** + * Mark the entirety of the layer's visible region as being invalid. + */ + void SetInvalidRectToVisibleRegion() + { + mInvalidRegion.SetEmpty(); + mInvalidRegion.Add(GetVisibleRegion().ToUnknownRegion()); + } + + /** + * Adds to the current invalid rect. + */ + void AddInvalidRect(const gfx::IntRect& aRect) { mInvalidRegion.Add(aRect); } + + /** + * Clear the invalid rect, marking the layer as being identical to what is currently + * composited. + */ + void ClearInvalidRect() { mInvalidRegion.SetEmpty(); } + + // These functions allow attaching an AsyncPanZoomController to this layer, + // and can be used anytime. + // A layer has an APZC at index aIndex only-if GetFrameMetrics(aIndex).IsScrollable(); + // attempting to get an APZC for a non-scrollable metrics will return null. + // The aIndex for these functions must be less than GetScrollMetadataCount(). + void SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller); + AsyncPanZoomController* GetAsyncPanZoomController(uint32_t aIndex) const; + // The ScrollMetadataChanged function is used internally to ensure the APZC array length + // matches the frame metrics array length. +private: + void ScrollMetadataChanged(); +public: + + void ApplyPendingUpdatesForThisTransaction(); + +#ifdef DEBUG + void SetDebugColorIndex(uint32_t aIndex) { mDebugColorIndex = aIndex; } + uint32_t GetDebugColorIndex() { return mDebugColorIndex; } +#endif + + virtual LayerRenderState GetRenderState() { return LayerRenderState(); } + + void Mutated() + { + mManager->Mutated(this); + } + + virtual int32_t GetMaxLayerSize() { return Manager()->GetMaxTextureSize(); } + + /** + * Returns true if this layer's effective transform is not just + * a translation by integers, or if this layer or some ancestor layer + * is marked as having a transform that may change without a full layer + * transaction. + */ + bool MayResample(); + + RenderTargetRect TransformRectToRenderTarget(const LayerIntRect& aRect); + + /** + * Add debugging information to the layer dump. + */ + void AddExtraDumpInfo(const nsACString& aStr) + { +#ifdef MOZ_DUMP_PAINTING + mExtraDumpInfo.AppendElement(aStr); +#endif + } + + /** + * Clear debugging information. Useful for recycling. + */ + void ClearExtraDumpInfo() + { +#ifdef MOZ_DUMP_PAINTING + mExtraDumpInfo.Clear(); +#endif + } + +protected: + Layer(LayerManager* aManager, void* aImplData); + + // Protected destructor, to discourage deletion outside of Release(): + virtual ~Layer(); + + /** + * We can snap layer transforms for two reasons: + * 1) To avoid unnecessary resampling when a transform is a translation + * by a non-integer number of pixels. + * Snapping the translation to an integer number of pixels avoids + * blurring the layer and can be faster to composite. + * 2) When a layer is used to render a rectangular object, we need to + * emulate the rendering of rectangular inactive content and snap the + * edges of the rectangle to pixel boundaries. This is both to ensure + * layer rendering is consistent with inactive content rendering, and to + * avoid seams. + * This function implements type 1 snapping. If aTransform is a 2D + * translation, and this layer's layer manager has enabled snapping + * (which is the default), return aTransform with the translation snapped + * to nearest pixels. Otherwise just return aTransform. Call this when the + * layer does not correspond to a single rectangular content object. + * This function does not try to snap if aTransform has a scale, because in + * that case resampling is inevitable and there's no point in trying to + * avoid it. In fact snapping can cause problems because pixel edges in the + * layer's content can be rendered unpredictably (jiggling) as the scale + * interacts with the snapping of the translation, especially with animated + * transforms. + * @param aResidualTransform a transform to apply before the result transform + * in order to get the results to completely match aTransform. + */ + gfx::Matrix4x4 SnapTransformTranslation(const gfx::Matrix4x4& aTransform, + gfx::Matrix* aResidualTransform); + gfx::Matrix4x4 SnapTransformTranslation3D(const gfx::Matrix4x4& aTransform, + gfx::Matrix* aResidualTransform); + /** + * See comment for SnapTransformTranslation. + * This function implements type 2 snapping. If aTransform is a translation + * and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries, + * and return the transform that maps aSnapRect to that rect. Otherwise + * just return aTransform. + * @param aSnapRect a rectangle whose edges should be snapped to pixel + * boundaries in the destination surface. + * @param aResidualTransform a transform to apply before the result transform + * in order to get the results to completely match aTransform. + */ + gfx::Matrix4x4 SnapTransform(const gfx::Matrix4x4& aTransform, + const gfxRect& aSnapRect, + gfx::Matrix* aResidualTransform); + + LayerManager* mManager; + ContainerLayer* mParent; + Layer* mNextSibling; + Layer* mPrevSibling; + void* mImplData; + RefPtr<Layer> mMaskLayer; + nsTArray<RefPtr<Layer>> mAncestorMaskLayers; + // Look for out-of-bound in the middle of the structure + mozilla::CorruptionCanary mCanary; + gfx::UserData mUserData; + gfx::IntRect mLayerBounds; + LayerIntRegion mVisibleRegion; + nsTArray<ScrollMetadata> mScrollMetadata; + EventRegions mEventRegions; + gfx::Matrix4x4 mTransform; + // A mutation of |mTransform| that we've queued to be applied at the + // end of the next transaction (if nothing else overrides it in the + // meantime). + nsAutoPtr<gfx::Matrix4x4> mPendingTransform; + float mPostXScale; + float mPostYScale; + gfx::Matrix4x4 mEffectiveTransform; + AnimationArray mAnimations; + // See mPendingTransform above. + nsAutoPtr<AnimationArray> mPendingAnimations; + InfallibleTArray<AnimData> mAnimationData; + float mOpacity; + gfx::CompositionOp mMixBlendMode; + bool mForceIsolatedGroup; + Maybe<ParentLayerIntRect> mClipRect; + Maybe<LayerClip> mScrolledClip; + gfx::IntRect mTileSourceRect; + gfx::TiledIntRegion mInvalidRegion; + nsTArray<RefPtr<AsyncPanZoomController> > mApzcs; + uint32_t mContentFlags; + bool mUseTileSourceRect; + bool mIsFixedPosition; + bool mTransformIsPerspective; + struct FixedPositionData { + FrameMetrics::ViewID mScrollId; + LayerPoint mAnchor; + int32_t mSides; + }; + UniquePtr<FixedPositionData> mFixedPositionData; + struct StickyPositionData { + FrameMetrics::ViewID mScrollId; + LayerRect mOuter; + LayerRect mInner; + }; + nsAutoPtr<StickyPositionData> mStickyPositionData; + FrameMetrics::ViewID mScrollbarTargetId; + ScrollDirection mScrollbarDirection; + // The scrollbar thumb ratio is the ratio of the thumb position (in the CSS + // pixels of the scrollframe's parent's space) to the scroll position (in the + // CSS pixels of the scrollframe's space). + float mScrollbarThumbRatio; + bool mIsScrollbarContainer; +#ifdef DEBUG + uint32_t mDebugColorIndex; +#endif + // If this layer is used for OMTA, then this counter is used to ensure we + // stay in sync with the animation manager + uint64_t mAnimationGeneration; +#ifdef MOZ_DUMP_PAINTING + nsTArray<nsCString> mExtraDumpInfo; +#endif + // Store display list log. + nsCString mDisplayListLog; +}; + +/** + * A Layer which we can paint into. It is a conceptually + * infinite surface, but each PaintedLayer has an associated "valid region" + * of contents that it is currently storing, which is finite. PaintedLayer + * implementations can store content between paints. + * + * PaintedLayers are rendered into during the drawing phase of a transaction. + * + * Currently the contents of a PaintedLayer are in the device output color + * space. + */ +class PaintedLayer : public Layer { +public: + /** + * CONSTRUCTION PHASE ONLY + * Tell this layer that the content in some region has changed and + * will need to be repainted. This area is removed from the valid + * region. + */ + virtual void InvalidateRegion(const nsIntRegion& aRegion) = 0; + /** + * CONSTRUCTION PHASE ONLY + * Set whether ComputeEffectiveTransforms should compute the + * "residual translation" --- the translation that should be applied *before* + * mEffectiveTransform to get the ideal transform for this PaintedLayer. + * When this is true, ComputeEffectiveTransforms will compute the residual + * and ensure that the layer is invalidated whenever the residual changes. + * When it's false, a change in the residual will not trigger invalidation + * and GetResidualTranslation will return 0,0. + * So when the residual is to be ignored, set this to false for better + * performance. + */ + void SetAllowResidualTranslation(bool aAllow) { mAllowResidualTranslation = aAllow; } + + /** + * Can be used anytime + */ + const nsIntRegion& GetValidRegion() const { return mValidRegion; } + + virtual PaintedLayer* AsPaintedLayer() override { return this; } + + MOZ_LAYER_DECL_NAME("PaintedLayer", TYPE_PAINTED) + + virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override + { + gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface; + gfx::Matrix residual; + mEffectiveTransform = SnapTransformTranslation(idealTransform, + mAllowResidualTranslation ? &residual : nullptr); + // The residual can only be a translation because SnapTransformTranslation + // only changes the transform if it's a translation + NS_ASSERTION(residual.IsTranslation(), + "Residual transform can only be a translation"); + if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) { + mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation()); + DebugOnly<mozilla::gfx::Point> transformedOrig = + idealTransform.TransformPoint(mozilla::gfx::Point()); +#ifdef DEBUG + DebugOnly<mozilla::gfx::Point> transformed = idealTransform.TransformPoint( + mozilla::gfx::Point(mResidualTranslation.x, mResidualTranslation.y) + ) - *&transformedOrig; +#endif + NS_ASSERTION(-0.5 <= (&transformed)->x && (&transformed)->x < 0.5 && + -0.5 <= (&transformed)->y && (&transformed)->y < 0.5, + "Residual translation out of range"); + mValidRegion.SetEmpty(); + } + ComputeEffectiveTransformForMaskLayers(aTransformToSurface); + } + + LayerManager::PaintedLayerCreationHint GetCreationHint() const { return mCreationHint; } + + bool UsedForReadback() { return mUsedForReadback; } + void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; } + + /** + * Returns true if aLayer is optimized for the given PaintedLayerCreationHint. + */ + virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint) + { return true; } + + /** + * Returns the residual translation. Apply this translation when drawing + * into the PaintedLayer so that when mEffectiveTransform is applied afterwards + * by layer compositing, the results exactly match the "ideal transform" + * (the product of the transform of this layer and its ancestors). + * Returns 0,0 unless SetAllowResidualTranslation(true) has been called. + * The residual translation components are always in the range [-0.5, 0.5). + */ + gfxPoint GetResidualTranslation() const { return mResidualTranslation; } + +protected: + PaintedLayer(LayerManager* aManager, void* aImplData, + LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE) + : Layer(aManager, aImplData) + , mValidRegion() + , mCreationHint(aCreationHint) + , mUsedForReadback(false) + , mAllowResidualTranslation(false) + { + mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT + } + + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; + + virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override; + + /** + * ComputeEffectiveTransforms snaps the ideal transform to get mEffectiveTransform. + * mResidualTranslation is the translation that should be applied *before* + * mEffectiveTransform to get the ideal transform. + */ + gfxPoint mResidualTranslation; + nsIntRegion mValidRegion; + /** + * The creation hint that was used when constructing this layer. + */ + const LayerManager::PaintedLayerCreationHint mCreationHint; + /** + * Set when this PaintedLayer is participating in readback, i.e. some + * ReadbackLayer (may) be getting its background from this layer. + */ + bool mUsedForReadback; + /** + * True when + */ + bool mAllowResidualTranslation; +}; + +/** + * A Layer which other layers render into. It holds references to its + * children. + */ +class ContainerLayer : public Layer { +public: + + ~ContainerLayer(); + + /** + * CONSTRUCTION PHASE ONLY + * Insert aChild into the child list of this container. aChild must + * not be currently in any child list or the root for the layer manager. + * If aAfter is non-null, it must be a child of this container and + * we insert after that layer. If it's null we insert at the start. + */ + virtual bool InsertAfter(Layer* aChild, Layer* aAfter); + /** + * CONSTRUCTION PHASE ONLY + * Remove aChild from the child list of this container. aChild must + * be a child of this container. + */ + virtual bool RemoveChild(Layer* aChild); + /** + * CONSTRUCTION PHASE ONLY + * Reposition aChild from the child list of this container. aChild must + * be a child of this container. + * If aAfter is non-null, it must be a child of this container and we + * reposition after that layer. If it's null, we reposition at the start. + */ + virtual bool RepositionChild(Layer* aChild, Layer* aAfter); + + void SetPreScale(float aXScale, float aYScale) + { + if (mPreXScale == aXScale && mPreYScale == aYScale) { + return; + } + + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PreScale", this)); + mPreXScale = aXScale; + mPreYScale = aYScale; + Mutated(); + } + + void SetInheritedScale(float aXScale, float aYScale) + { + if (mInheritedXScale == aXScale && mInheritedYScale == aYScale) { + return; + } + + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) InheritedScale", this)); + mInheritedXScale = aXScale; + mInheritedYScale = aYScale; + Mutated(); + } + + void SetScaleToResolution(bool aScaleToResolution, float aResolution) + { + if (mScaleToResolution == aScaleToResolution && mPresShellResolution == aResolution) { + return; + } + + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScaleToResolution", this)); + mScaleToResolution = aScaleToResolution; + mPresShellResolution = aResolution; + Mutated(); + } + + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override; + + void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray); + + // These getters can be used anytime. + + virtual ContainerLayer* AsContainerLayer() override { return this; } + virtual const ContainerLayer* AsContainerLayer() const override { return this; } + + virtual Layer* GetFirstChild() const override { return mFirstChild; } + virtual Layer* GetLastChild() const override { return mLastChild; } + float GetPreXScale() const { return mPreXScale; } + float GetPreYScale() const { return mPreYScale; } + float GetInheritedXScale() const { return mInheritedXScale; } + float GetInheritedYScale() const { return mInheritedYScale; } + float GetPresShellResolution() const { return mPresShellResolution; } + bool ScaleToResolution() const { return mScaleToResolution; } + + MOZ_LAYER_DECL_NAME("ContainerLayer", TYPE_CONTAINER) + + /** + * ContainerLayer backends need to override ComputeEffectiveTransforms + * since the decision about whether to use a temporary surface for the + * container is backend-specific. ComputeEffectiveTransforms must also set + * mUseIntermediateSurface. + */ + virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override = 0; + + /** + * Call this only after ComputeEffectiveTransforms has been invoked + * on this layer. + * Returns true if this will use an intermediate surface. This is largely + * backend-dependent, but it affects the operation of GetEffectiveOpacity(). + */ + bool UseIntermediateSurface() { return mUseIntermediateSurface; } + + /** + * Returns the rectangle covered by the intermediate surface, + * in this layer's coordinate system. + * + * NOTE: Since this layer has an intermediate surface it follows + * that LayerPixel == RenderTargetPixel + */ + RenderTargetIntRect GetIntermediateSurfaceRect() + { + NS_ASSERTION(mUseIntermediateSurface, "Must have intermediate surface"); + return RenderTargetIntRect::FromUnknownRect(GetLocalVisibleRegion().ToUnknownRegion().GetBounds()); + } + + /** + * Returns true if this container has more than one non-empty child + */ + bool HasMultipleChildren(); + + /** + * Returns true if this container supports children with component alpha. + * Should only be called while painting a child of this layer. + */ + bool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; } + + /** + * Returns true if aLayer or any layer in its parent chain has the opaque + * content flag set. + */ + static bool HasOpaqueAncestorLayer(Layer* aLayer); + + void SetChildrenChanged(bool aVal) { + mChildrenChanged = aVal; + } + + void SetEventRegionsOverride(EventRegionsOverride aVal) { + if (mEventRegionsOverride == aVal) { + return; + } + + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) EventRegionsOverride", this)); + mEventRegionsOverride = aVal; + Mutated(); + } + + EventRegionsOverride GetEventRegionsOverride() const { + return mEventRegionsOverride; + } + +protected: + friend class ReadbackProcessor; + + void DidInsertChild(Layer* aLayer); + void DidRemoveChild(Layer* aLayer); + + void Collect3DContextLeaves(nsTArray<Layer*>& aToSort); + + ContainerLayer(LayerManager* aManager, void* aImplData); + + /** + * A default implementation of ComputeEffectiveTransforms for use by OpenGL + * and D3D. + */ + void DefaultComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface); + + /** + * A default implementation to compute and set the value for SupportsComponentAlphaChildren(). + * + * If aNeedsSurfaceCopy is provided, then it is set to true if the caller needs to copy the background + * up into the intermediate surface created, false otherwise. + */ + void DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy = nullptr); + + /** + * Loops over the children calling ComputeEffectiveTransforms on them. + */ + void ComputeEffectiveTransformsForChildren(const gfx::Matrix4x4& aTransformToSurface); + + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; + + virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override; + + /** + * True for if the container start a new 3D context extended by one + * or more children. + */ + bool Creates3DContextWithExtendingChildren(); + + Layer* mFirstChild; + Layer* mLastChild; + float mPreXScale; + float mPreYScale; + // The resolution scale inherited from the parent layer. This will already + // be part of mTransform. + float mInheritedXScale; + float mInheritedYScale; + // For layers corresponding to an nsDisplayResolution, the resolution of the + // associated pres shell; for other layers, 1.0. + float mPresShellResolution; + // Whether the compositor should scale to mPresShellResolution. + bool mScaleToResolution; + bool mUseIntermediateSurface; + bool mSupportsComponentAlphaChildren; + bool mMayHaveReadbackChild; + // This is updated by ComputeDifferences. This will be true if we need to invalidate + // the intermediate surface. + bool mChildrenChanged; + EventRegionsOverride mEventRegionsOverride; +}; + +/** + * A Layer which just renders a solid color in its visible region. It actually + * can fill any area that contains the visible region, so if you need to + * restrict the area filled, set a clip region on this layer. + */ +class ColorLayer : public Layer { +public: + virtual ColorLayer* AsColorLayer() override { return this; } + + /** + * CONSTRUCTION PHASE ONLY + * Set the color of the layer. + */ + virtual void SetColor(const gfx::Color& aColor) + { + if (mColor != aColor) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Color", this)); + mColor = aColor; + Mutated(); + } + } + + void SetBounds(const gfx::IntRect& aBounds) + { + if (!mBounds.IsEqualEdges(aBounds)) { + mBounds = aBounds; + Mutated(); + } + } + + const gfx::IntRect& GetBounds() + { + return mBounds; + } + + // This getter can be used anytime. + virtual const gfx::Color& GetColor() { return mColor; } + + MOZ_LAYER_DECL_NAME("ColorLayer", TYPE_COLOR) + + virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override + { + gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface; + mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr); + ComputeEffectiveTransformForMaskLayers(aTransformToSurface); + } + +protected: + ColorLayer(LayerManager* aManager, void* aImplData) + : Layer(aManager, aImplData) + , mColor() + {} + + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; + + virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override; + + gfx::IntRect mBounds; + gfx::Color mColor; +}; + +/** + * A Layer for HTML Canvas elements. It's backed by either a + * gfxASurface or a GLContext (for WebGL layers), and has some control + * for intelligent updating from the source if necessary (for example, + * if hardware compositing is not available, for reading from the GL + * buffer into an image surface that we can layer composite.) + * + * After Initialize is called, the underlying canvas Surface/GLContext + * must not be modified during a layer transaction. + */ +class CanvasLayer : public Layer { +public: + struct Data { + Data() + : mBufferProvider(nullptr) + , mGLContext(nullptr) + , mRenderer(nullptr) + , mFrontbufferGLTex(0) + , mSize(0,0) + , mHasAlpha(false) + , mIsGLAlphaPremult(true) + , mIsMirror(false) + { } + + // One of these three must be specified for Canvas2D, but never more than one + PersistentBufferProvider* mBufferProvider; // A BufferProvider for the Canvas contents + mozilla::gl::GLContext* mGLContext; // or this, for GL. + AsyncCanvasRenderer* mRenderer; // or this, for OffscreenCanvas + + // Frontbuffer override + uint32_t mFrontbufferGLTex; + + // The size of the canvas content + gfx::IntSize mSize; + + // Whether the canvas drawingbuffer has an alpha channel. + bool mHasAlpha; + + // Whether mGLContext contains data that is alpha-premultiplied. + bool mIsGLAlphaPremult; + + // Whether the canvas front buffer is already being rendered somewhere else. + // When true, do not swap buffers or Morph() to another factory on mGLContext + bool mIsMirror; + }; + + /** + * CONSTRUCTION PHASE ONLY + * Initialize this CanvasLayer with the given data. The data must + * have either mSurface or mGLContext initialized (but not both), as + * well as mSize. + * + * This must only be called once. + */ + virtual void Initialize(const Data& aData) = 0; + + /** + * Check the data is owned by this layer is still valid for rendering + */ + virtual bool IsDataValid(const Data& aData) { return true; } + + /** + * Notify this CanvasLayer that the canvas surface contents have + * changed (or will change) before the next transaction. + */ + void Updated() { mDirty = true; SetInvalidRectToVisibleRegion(); } + + /** + * Notify this CanvasLayer that the canvas surface contents have + * been painted since the last change. + */ + void Painted() { mDirty = false; } + + /** + * Returns true if the canvas surface contents have changed since the + * last paint. + */ + bool IsDirty() + { + // We can only tell if we are dirty if we're part of the + // widget's retained layer tree. + if (!mManager || !mManager->IsWidgetLayerManager()) { + return true; + } + return mDirty; + } + + /** + * Register a callback to be called at the start of each transaction. + */ + typedef void PreTransactionCallback(void* closureData); + void SetPreTransactionCallback(PreTransactionCallback* callback, void* closureData) + { + mPreTransCallback = callback; + mPreTransCallbackData = closureData; + } + + const nsIntRect& GetBounds() const { return mBounds; } + +protected: + void FirePreTransactionCallback() + { + if (mPreTransCallback) { + mPreTransCallback(mPreTransCallbackData); + } + } + +public: + /** + * Register a callback to be called at the end of each transaction. + */ + typedef void (* DidTransactionCallback)(void* aClosureData); + void SetDidTransactionCallback(DidTransactionCallback aCallback, void* aClosureData) + { + mPostTransCallback = aCallback; + mPostTransCallbackData = aClosureData; + } + + /** + * CONSTRUCTION PHASE ONLY + * Set the filter used to resample this image (if necessary). + */ + void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter) + { + if (mSamplingFilter != aSamplingFilter) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this)); + mSamplingFilter = aSamplingFilter; + Mutated(); + } + } + gfx::SamplingFilter GetSamplingFilter() const { return mSamplingFilter; } + + MOZ_LAYER_DECL_NAME("CanvasLayer", TYPE_CANVAS) + + virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override + { + // Snap our local transform first, and snap the inherited transform as well. + // This makes our snapping equivalent to what would happen if our content + // was drawn into a PaintedLayer (gfxContext would snap using the local + // transform, then we'd snap again when compositing the PaintedLayer). + mEffectiveTransform = + SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height), + nullptr)* + SnapTransformTranslation(aTransformToSurface, nullptr); + ComputeEffectiveTransformForMaskLayers(aTransformToSurface); + } + + bool GetIsAsyncRenderer() const + { + return !!mAsyncRenderer; + } + +protected: + CanvasLayer(LayerManager* aManager, void* aImplData); + virtual ~CanvasLayer(); + + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; + + virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override; + + void FireDidTransactionCallback() + { + if (mPostTransCallback) { + mPostTransCallback(mPostTransCallbackData); + } + } + + /** + * 0, 0, canvaswidth, canvasheight + */ + gfx::IntRect mBounds; + PreTransactionCallback* mPreTransCallback; + void* mPreTransCallbackData; + DidTransactionCallback mPostTransCallback; + void* mPostTransCallbackData; + gfx::SamplingFilter mSamplingFilter; + RefPtr<AsyncCanvasRenderer> mAsyncRenderer; + +private: + /** + * Set to true in Updated(), cleared during a transaction. + */ + bool mDirty; +}; + +/** + * ContainerLayer that refers to a "foreign" layer tree, through an + * ID. Usage of RefLayer looks like + * + * Construction phase: + * allocate ID for layer subtree + * create RefLayer, SetReferentId(ID) + * + * Composition: + * look up subtree for GetReferentId() + * ConnectReferentLayer(subtree) + * compose + * ClearReferentLayer() + * + * Clients will usually want to Connect/Clear() on each transaction to + * avoid difficulties managing memory across multiple layer subtrees. + */ +class RefLayer : public ContainerLayer { + friend class LayerManager; + +private: + virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override + { MOZ_CRASH("GFX: RefLayer"); return false; } + + virtual bool RemoveChild(Layer* aChild) override + { MOZ_CRASH("GFX: RefLayer"); return false; } + + virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override + { MOZ_CRASH("GFX: RefLayer"); return false; } + +public: + /** + * CONSTRUCTION PHASE ONLY + * Set the ID of the layer's referent. + */ + void SetReferentId(uint64_t aId) + { + MOZ_ASSERT(aId != 0); + if (mId != aId) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ReferentId", this)); + mId = aId; + Mutated(); + } + } + /** + * CONSTRUCTION PHASE ONLY + * Connect this ref layer to its referent, temporarily. + * ClearReferentLayer() must be called after composition. + */ + void ConnectReferentLayer(Layer* aLayer) + { + MOZ_ASSERT(!mFirstChild && !mLastChild); + MOZ_ASSERT(!aLayer->GetParent()); + if (aLayer->Manager() != Manager()) { + // This can happen when e.g. rendering while dragging tabs + // between windows - aLayer's manager may be the manager for the + // old window's tab. In that case, it will be changed before the + // next render (see SetLayerManager). It is simply easier to + // ignore the rendering here than it is to pause it. + NS_WARNING("ConnectReferentLayer failed - Incorrect LayerManager"); + return; + } + + mFirstChild = mLastChild = aLayer; + aLayer->SetParent(this); + } + + /** + * DRAWING PHASE ONLY + * |aLayer| is the same as the argument to ConnectReferentLayer(). + */ + void DetachReferentLayer(Layer* aLayer) + { + mFirstChild = mLastChild = nullptr; + aLayer->SetParent(nullptr); + } + + // These getters can be used anytime. + virtual RefLayer* AsRefLayer() override { return this; } + + virtual int64_t GetReferentId() { return mId; } + + /** + * DRAWING PHASE ONLY + */ + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override; + + MOZ_LAYER_DECL_NAME("RefLayer", TYPE_REF) + +protected: + RefLayer(LayerManager* aManager, void* aImplData) + : ContainerLayer(aManager, aImplData) , mId(0) + {} + + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; + + virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override; + + // 0 is a special value that means "no ID". + uint64_t mId; +}; + +void SetAntialiasingFlags(Layer* aLayer, gfx::DrawTarget* aTarget); + +#ifdef MOZ_DUMP_PAINTING +void WriteSnapshotToDumpFile(Layer* aLayer, gfx::DataSourceSurface* aSurf); +void WriteSnapshotToDumpFile(LayerManager* aManager, gfx::DataSourceSurface* aSurf); +void WriteSnapshotToDumpFile(Compositor* aCompositor, gfx::DrawTarget* aTarget); +#endif + +// A utility function used by different LayerManager implementations. +gfx::IntRect ToOutsideIntRect(const gfxRect &aRect); + +} // namespace layers +} // namespace mozilla + +#endif /* GFX_LAYERS_H */ |