blob: 01ee2d9ff6f7f05c0a5d2d18d4c923c47d427185 [file] [log] [blame]
// Copyright 2016 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 PropertyTreeState_h
#define PropertyTreeState_h
#include "platform/graphics/paint/ClipPaintPropertyNode.h"
#include "platform/graphics/paint/EffectPaintPropertyNode.h"
#include "platform/graphics/paint/TransformPaintPropertyNode.h"
#include "platform/wtf/HashFunctions.h"
#include "platform/wtf/HashTraits.h"
#include "platform/wtf/text/StringBuilder.h"
namespace blink {
// A complete set of paint properties including those that are inherited from
// other objects. RefPtrs are used to guard against use-after-free bugs and
// DCHECKs ensure PropertyTreeState never retains the last reference to a
// property tree node.
class PLATFORM_EXPORT PropertyTreeState {
USING_FAST_MALLOC(PropertyTreeState);
public:
PropertyTreeState(const TransformPaintPropertyNode* transform,
const ClipPaintPropertyNode* clip,
const EffectPaintPropertyNode* effect)
: transform_(transform), clip_(clip), effect_(effect) {
DCHECK(!transform_ || !transform_->HasOneRef());
DCHECK(!clip_ || !clip_->HasOneRef());
DCHECK(!effect_ || !effect_->HasOneRef());
}
bool HasDirectCompositingReasons() const;
const TransformPaintPropertyNode* Transform() const {
DCHECK(!transform_ || !transform_->HasOneRef());
return transform_.Get();
}
void SetTransform(RefPtr<const TransformPaintPropertyNode> node) {
transform_ = std::move(node);
}
const ClipPaintPropertyNode* Clip() const {
DCHECK(!clip_ || !clip_->HasOneRef());
return clip_.Get();
}
void SetClip(RefPtr<const ClipPaintPropertyNode> node) {
clip_ = std::move(node);
}
const EffectPaintPropertyNode* Effect() const {
DCHECK(!effect_ || !effect_->HasOneRef());
return effect_.Get();
}
void SetEffect(RefPtr<const EffectPaintPropertyNode> node) {
effect_ = std::move(node);
}
static const PropertyTreeState& Root();
// Returns the compositor element id, if any, for this property state. If
// neither the effect nor transform nodes have a compositor element id then a
// default instance is returned.
const CompositorElementId GetCompositorElementId(
const CompositorElementIdSet& element_ids) const;
enum InnermostNode {
kNone, // None means that all nodes are their root values
kTransform,
kClip,
kEffect,
};
// There is always a well-defined order in which the transform, clip
// and effects of a PropertyTreeState apply. This method returns which
// of them applies first to content drawn with this PropertyTreeState.
// Note that it may be the case that multiple nodes from the same tree apply
// before any from another tree. This can happen, for example, if multiple
// effects or clips apply to a descendant transform state from the transform
// node.
//
// This method is meant to be used in concert with
// |PropertyTreeStateIterator|, which allows one to iterate over the nodes in
// the order in which they apply.
//
// Example:
//
// Transform tree Clip tree Effect tree
// ~~~~~~~~~~~~~~ ~~~~~~~~~ ~~~~~~~~~~~
// Root Root Root
// | | |
// T C E
//
// Suppose that PropertyTreeState(T, C, E).innerMostNode() is E, and
// PropertytreeState(T, C, Root).innermostNode() is C. Then a PaintChunk
// that has propertyTreeState = PropertyTreeState(T, C, E) can be painted
// with the following display list structure:
//
// [BeginTransform] [BeginClip] [BeginEffect] PaintChunk drawings
// [EndEffect] [EndClip] [EndTransform]
//
// The PropertyTreeStateIterator will behave like this:
//
// PropertyTreeStateIterator iterator(PropertyTreeState(T, C, E));
// DCHECK(iterator.innermostNode() == Effect);
// DCHECK(iterator.next()->innermostNode() == Clip);
// DCHECK(iterator.next()->innermostNode() == Transform);
// DCHECK(iterator.next()->innermostNode() == None);
InnermostNode GetInnermostNode() const;
#if DCHECK_IS_ON()
// Dumps the tree from this state up to the root as a string.
String ToTreeString() const;
#endif
private:
RefPtr<const TransformPaintPropertyNode> transform_;
RefPtr<const ClipPaintPropertyNode> clip_;
RefPtr<const EffectPaintPropertyNode> effect_;
};
inline bool operator==(const PropertyTreeState& a, const PropertyTreeState& b) {
return a.Transform() == b.Transform() && a.Clip() == b.Clip() &&
a.Effect() == b.Effect();
}
// Iterates over the sequence transforms, clips and effects for a
// PropertyTreeState between that state and the "root" state (all nodes equal
// to *::Root()), in the order that they apply.
//
// See also PropertyTreeState::innermostNode for a more detailed example.
class PLATFORM_EXPORT PropertyTreeStateIterator {
public:
PropertyTreeStateIterator(const PropertyTreeState& properties)
: properties_(properties) {}
const PropertyTreeState* Next();
private:
PropertyTreeState properties_;
};
#if DCHECK_IS_ON()
template <typename PropertyTreeNode>
class PropertyTreeStatePrinter {
public:
String PathAsString(const PropertyTreeNode* last_node) {
const PropertyTreeNode* node = last_node;
while (!node->IsRoot()) {
AddPropertyNode(node, "");
node = node->Parent();
}
AddPropertyNode(node, "root");
StringBuilder string_builder;
AddAllPropertyNodes(string_builder, node);
return string_builder.ToString();
}
void AddPropertyNode(const PropertyTreeNode* node, String debug_info) {
node_to_debug_string_.Set(node, debug_info);
}
void AddAllPropertyNodes(StringBuilder& string_builder,
const PropertyTreeNode* node,
unsigned indent = 0) {
DCHECK(node);
for (unsigned i = 0; i < indent; i++)
string_builder.Append(' ');
if (node_to_debug_string_.Contains(node))
string_builder.Append(node_to_debug_string_.at(node));
string_builder.Append(String::Format(" %p ", node));
string_builder.Append(node->ToString());
string_builder.Append("\n");
for (const auto* child_node : node_to_debug_string_.Keys()) {
if (child_node->Parent() == node)
AddAllPropertyNodes(string_builder, child_node, indent + 2);
}
}
HashMap<const PropertyTreeNode*, String> node_to_debug_string_;
};
#endif
} // namespace blink
#endif // PropertyTreeState_h