blob: 8b30934f08666cc213feb96694f739a601755e43 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DisplayItem_h
#define DisplayItem_h
#include "platform/PlatformExport.h"
#include "platform/graphics/ContiguousContainer.h"
#include "platform/graphics/paint/DisplayItemClient.h"
#include "wtf/Allocator.h"
#include "wtf/Assertions.h"
#include "wtf/Noncopyable.h"
#ifndef NDEBUG
#include "wtf/text/StringBuilder.h"
#include "wtf/text/WTFString.h"
#endif
class SkPictureGpuAnalyzer;
namespace blink {
class GraphicsContext;
class IntRect;
class WebDisplayItemList;
class PLATFORM_EXPORT DisplayItem {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
enum {
// Must be kept in sync with core/paint/PaintPhase.h.
kPaintPhaseMax = 11,
};
// A display item type uniquely identifies a display item of a client.
// Some display item types can be categorized using the following directives:
// - In enum Type:
// - enum value <Category>First;
// - enum values of the category, first of which should equal <Category>First;
// (for ease of maintenance, the values should be in alphabetic order)
// - enum value <Category>Last which should be equal to the last of the enum values of the category
// - DEFINE_CATEGORY_METHODS(<Category>) to define is<Category>Type(Type) and is<Category>() methods.
//
// A category or subset of a category can contain types each of which corresponds to a PaintPhase:
// - In enum Type:
// - enum value <Category>[<Subset>]PaintPhaseFirst;
// - enum value <Category>[<Subset>]PaintPhaseLast = <Category>[<Subset>]PaintPhaseFirst + PaintPhaseMax;
// - DEFINE_PAINT_PHASE_CONVERSION_METHOD(<Category>[<Subset>]) to define
// paintPhaseTo<Category>[<Subset>]Type(PaintPhase) method.
//
// A category can be derived from another category, containing types each of which corresponds to a
// value of the latter category:
// - In enum Type:
// - enum value <Category>First;
// - enum value <Category>Last = <Category>First + <BaseCategory>Last - <BaseCategory>First;
// - DEFINE_CONVERSION_METHODS(<Category>, <category>, <BaseCategory>, <baseCategory>) to define methods to
// convert types between the categories;
enum Type {
kDrawingFirst,
kDrawingPaintPhaseFirst = kDrawingFirst,
kDrawingPaintPhaseLast = kDrawingFirst + kPaintPhaseMax,
kBoxDecorationBackground,
kCaret,
kColumnRules,
kDebugDrawing,
kDocumentBackground,
kDragImage,
kDragCaret,
kSVGImage,
kLinkHighlight,
kImageAreaFocusRing,
kPageOverlay,
kPageWidgetDelegateBackgroundFallback,
kPopupContainerBorder,
kPopupListBoxBackground,
kPopupListBoxRow,
kPrintedContentBackground,
kPrintedContentDestinationLocations,
kPrintedContentLineBoundary,
kPrintedContentPDFURLRect,
kResizer,
kSVGClip,
kSVGFilter,
kSVGMask,
kScrollbarBackButtonEnd,
kScrollbarBackButtonStart,
kScrollbarBackground,
kScrollbarBackTrack,
kScrollbarCorner,
kScrollbarForwardButtonEnd,
kScrollbarForwardButtonStart,
kScrollbarForwardTrack,
kScrollbarThumb,
kScrollbarTickmarks,
kScrollbarTrackBackground,
kScrollbarCompositedScrollbar,
kSelectionTint,
kTableCellBackgroundFromColumnGroup,
kTableCellBackgroundFromColumn,
kTableCellBackgroundFromSection,
kTableCellBackgroundFromRow,
// Table collapsed borders can be painted together (e.g., left & top) but there are at most 4 phases of collapsed
// border painting for a single cell. To disambiguate these phases of collapsed border painting, a mask is used.
// TableCollapsedBorderBase can be larger than TableCollapsedBorderUnalignedBase to ensure the base lower bits are 0's.
kTableCollapsedBorderUnalignedBase,
kTableCollapsedBorderBase = (((kTableCollapsedBorderUnalignedBase - 1) >> 4) + 1) << 4,
kTableCollapsedBorderLast = kTableCollapsedBorderBase + 0x0f,
kTableSectionBoxShadowInset,
kTableSectionBoxShadowNormal,
kTableRowBoxShadowInset,
kTableRowBoxShadowNormal,
kVideoBitmap,
kWebPlugin,
kWebFont,
kReflectionMask,
kDrawingLast = kReflectionMask,
kForeignLayerFirst,
kForeignLayerCanvas = kForeignLayerFirst,
kForeignLayerPlugin,
kForeignLayerVideo,
kForeignLayerLast = kForeignLayerVideo,
kClipFirst,
kClipBoxPaintPhaseFirst = kClipFirst,
kClipBoxPaintPhaseLast = kClipBoxPaintPhaseFirst + kPaintPhaseMax,
kClipColumnBoundsPaintPhaseFirst,
kClipColumnBoundsPaintPhaseLast = kClipColumnBoundsPaintPhaseFirst + kPaintPhaseMax,
kClipLayerFragmentPaintPhaseFirst,
kClipLayerFragmentPaintPhaseLast = kClipLayerFragmentPaintPhaseFirst + kPaintPhaseMax,
kClipFileUploadControlRect,
kClipFrameToVisibleContentRect,
kClipFrameScrollbars,
kClipLayerBackground,
kClipLayerColumnBounds,
kClipLayerFilter,
kClipLayerForeground,
kClipLayerParent,
kClipLayerOverflowControls,
kClipNodeImage,
kClipPopupListBoxFrame,
kClipScrollbarsToBoxBounds,
kClipSelectionImage,
kPageWidgetDelegateClip,
kClipPrintedPage,
kClipLast = kClipPrintedPage,
kEndClipFirst,
kEndClipLast = kEndClipFirst + kClipLast - kClipFirst,
kFloatClipFirst,
kFloatClipPaintPhaseFirst = kFloatClipFirst,
kFloatClipPaintPhaseLast = kFloatClipFirst + kPaintPhaseMax,
kFloatClipLast = kFloatClipPaintPhaseLast,
kEndFloatClipFirst,
kEndFloatClipLast = kEndFloatClipFirst + kFloatClipLast - kFloatClipFirst,
kScrollFirst,
kScrollPaintPhaseFirst = kScrollFirst,
kScrollPaintPhaseLast = kScrollPaintPhaseFirst + kPaintPhaseMax,
kScrollOverflowControls,
kScrollLast = kScrollOverflowControls,
kEndScrollFirst,
kEndScrollLast = kEndScrollFirst + kScrollLast - kScrollFirst,
kTransform3DFirst,
kTransform3DElementTransform = kTransform3DFirst,
kTransform3DLast = kTransform3DElementTransform,
kEndTransform3DFirst,
kEndTransform3DLast = kEndTransform3DFirst + kTransform3DLast - kTransform3DFirst,
kBeginFilter,
kEndFilter,
kBeginCompositing,
kEndCompositing,
kBeginTransform,
kEndTransform,
kBeginClipPath,
kEndClipPath,
kSubsequence,
kEndSubsequence,
kUninitializedType,
kTypeLast = kUninitializedType
};
static_assert(kTableCollapsedBorderBase >= kTableCollapsedBorderUnalignedBase, "TableCollapsedBorder types overlap with other types");
static_assert((kTableCollapsedBorderBase & 0xf) == 0, "The lowest 4 bits of TableCollapsedBorderBase should be zero");
// Bits or'ed onto TableCollapsedBorderBase to generate a real table collapsed border type.
enum TableCollapsedBorderSides {
TableCollapsedBorderTop = 1 << 0,
TableCollapsedBorderRight = 1 << 1,
TableCollapsedBorderBottom = 1 << 2,
TableCollapsedBorderLeft = 1 << 3,
};
DisplayItem(const DisplayItemClient& client, Type type, size_t derivedSize)
: m_client(&client)
, m_type(type)
, m_derivedSize(derivedSize)
, m_skippedCache(false)
#ifndef NDEBUG
, m_clientDebugString(client.debugName())
#endif
{
// derivedSize must fit in m_derivedSize.
// If it doesn't, enlarge m_derivedSize and fix this assert.
ASSERT_WITH_SECURITY_IMPLICATION(derivedSize < (1 << 8));
ASSERT_WITH_SECURITY_IMPLICATION(derivedSize >= sizeof(*this));
}
virtual ~DisplayItem() { }
// Ids are for matching new DisplayItems with existing DisplayItems.
struct Id {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
Id(const DisplayItemClient& client, const Type type)
: client(client)
, type(type) { }
const DisplayItemClient& client;
const Type type;
};
Id getId() const { return Id(*m_client, m_type); }
virtual void replay(GraphicsContext&) const { }
const DisplayItemClient& client() const { ASSERT(m_client); return *m_client; }
Type getType() const { return m_type; }
// Size of this object in memory, used to move it with memcpy.
// This is not sizeof(*this), because it needs to account for the size of
// the derived class (i.e. runtime type). Derived classes are expected to
// supply this to the DisplayItem constructor.
size_t derivedSize() const { return m_derivedSize; }
// For PaintController only. Painters should use DisplayItemCacheSkipper instead.
void setSkippedCache() { m_skippedCache = true; }
bool skippedCache() const { return m_skippedCache; }
// TODO(wkorman): Only DrawingDisplayItem needs the visual rect argument.
// Consider refactoring class hierarchy to make this more explicit.
virtual void appendToWebDisplayItemList(const IntRect&, WebDisplayItemList*) const { }
// See comments of enum Type for usage of the following macros.
#define DEFINE_CATEGORY_METHODS(Category) \
static bool is##Category##Type(Type type) { return type >= k##Category##First && type <= k##Category##Last; } \
bool is##Category() const { return is##Category##Type(getType()); }
#define DEFINE_CONVERSION_METHODS(Category1, category1, Category2, category2) \
static Type category1##TypeTo##Category2##Type(Type type) \
{ \
static_assert(k##Category1##Last - k##Category1##First == k##Category2##Last - k##Category2##First, \
"Categories " #Category1 " and " #Category2 " should have same number of enum values. See comments of DisplayItem::Type"); \
ASSERT(is##Category1##Type(type)); \
return static_cast<Type>(type - k##Category1##First + k##Category2##First); \
} \
static Type category2##TypeTo##Category1##Type(Type type) \
{ \
ASSERT(is##Category2##Type(type)); \
return static_cast<Type>(type - k##Category2##First + k##Category1##First); \
}
#define DEFINE_PAIRED_CATEGORY_METHODS(Category, category) \
DEFINE_CATEGORY_METHODS(Category) \
DEFINE_CATEGORY_METHODS(End##Category) \
DEFINE_CONVERSION_METHODS(Category, category, End##Category, end##Category)
#define DEFINE_PAINT_PHASE_CONVERSION_METHOD(Category) \
static Type paintPhaseTo##Category##Type(int paintPhase) \
{ \
static_assert(k##Category##PaintPhaseLast - k##Category##PaintPhaseFirst == k##PaintPhaseMax, \
"Invalid paint-phase-based category " #Category ". See comments of DisplayItem::Type"); \
return static_cast<Type>(paintPhase + k##Category##PaintPhaseFirst); \
}
DEFINE_CATEGORY_METHODS(Drawing)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(Drawing)
DEFINE_CATEGORY_METHODS(ForeignLayer)
DEFINE_PAIRED_CATEGORY_METHODS(Clip, clip)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(ClipLayerFragment)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(ClipBox)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(ClipColumnBounds)
DEFINE_PAIRED_CATEGORY_METHODS(FloatClip, floatClip)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(FloatClip)
DEFINE_PAIRED_CATEGORY_METHODS(Scroll, scroll)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(Scroll)
DEFINE_PAIRED_CATEGORY_METHODS(Transform3D, transform3D)
static bool isCacheableType(Type type) { return isDrawingType(type) || type == kSubsequence; }
bool isCacheable() const { return !skippedCache() && isCacheableType(m_type); }
virtual bool isBegin() const { return false; }
virtual bool isEnd() const { return false; }
#if DCHECK_IS_ON()
virtual bool isEndAndPairedWith(DisplayItem::Type otherType) const { return false; }
#endif
virtual bool equals(const DisplayItem& other) const
{
return m_client == other.m_client
&& m_type == other.m_type
&& m_derivedSize == other.m_derivedSize
&& m_skippedCache == other.m_skippedCache;
}
// True if the client is non-null. Because m_client is const, this should
// never be false except when we explicitly create a tombstone/"dead display
// item" as part of moving an item from one list to another (see:
// DisplayItemList::appendByMoving).
bool hasValidClient() const { return m_client; }
virtual bool drawsContent() const { return false; }
// Override to implement specific analysis strategies.
virtual void analyzeForGpuRasterization(SkPictureGpuAnalyzer&) const { }
#ifndef NDEBUG
static WTF::String typeAsDebugString(DisplayItem::Type);
const WTF::String clientDebugString() const { return m_clientDebugString; }
void setClientDebugString(const WTF::String& s) { m_clientDebugString = s; }
WTF::String asDebugString() const;
virtual void dumpPropertiesAsDebugString(WTF::StringBuilder&) const;
#endif
private:
// The default DisplayItem constructor is only used by
// ContiguousContainer::appendByMoving where an invalid DisplaItem is
// constructed at the source location.
template <typename T, unsigned alignment> friend class ContiguousContainer;
DisplayItem()
: m_client(nullptr)
, m_type(kUninitializedType)
, m_derivedSize(sizeof(*this))
, m_skippedCache(false)
{ }
const DisplayItemClient* m_client;
static_assert(kTypeLast < (1 << 16), "DisplayItem::Type should fit in 16 bits");
const Type m_type : 16;
const unsigned m_derivedSize : 8; // size of the actual derived class
unsigned m_skippedCache : 1;
#ifndef NDEBUG
WTF::String m_clientDebugString;
#endif
};
inline bool operator==(const DisplayItem::Id& a, const DisplayItem::Id& b)
{
return a.client == b.client && a.type == b.type;
}
inline bool operator!=(const DisplayItem::Id& a, const DisplayItem::Id& b)
{
return !(a == b);
}
class PLATFORM_EXPORT PairedBeginDisplayItem : public DisplayItem {
protected:
PairedBeginDisplayItem(const DisplayItemClient& client, Type type, size_t derivedSize) : DisplayItem(client, type, derivedSize) { }
private:
bool isBegin() const final { return true; }
};
class PLATFORM_EXPORT PairedEndDisplayItem : public DisplayItem {
protected:
PairedEndDisplayItem(const DisplayItemClient& client, Type type, size_t derivedSize) : DisplayItem(client, type, derivedSize) { }
#if ENABLE(ASSERT)
bool isEndAndPairedWith(DisplayItem::Type otherType) const override = 0;
#endif
private:
bool isEnd() const final { return true; }
};
} // namespace blink
#endif // DisplayItem_h