blob: cb68913d571b96cca01edcb3f00b5ec0f384fb16 [file] [log] [blame]
/*
* Copyright (C) 2014 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_INVALIDATION_INVALIDATION_SET_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_INVALIDATION_INVALIDATION_SET_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/invalidation/invalidation_flags.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
namespace blink {
class Element;
class TracedValue;
enum InvalidationType { kInvalidateDescendants, kInvalidateSiblings };
class InvalidationSet;
struct CORE_EXPORT InvalidationSetDeleter {
static void Destruct(const InvalidationSet*);
};
// Tracks data to determine which descendants in a DOM subtree, or
// siblings and their descendants, need to have style recalculated.
//
// Some example invalidation sets:
//
// .z {}
// For class z we will have a DescendantInvalidationSet with invalidatesSelf
// (the element itself is invalidated).
//
// .y .z {}
// For class y we will have a DescendantInvalidationSet containing class z.
//
// .x ~ .z {}
// For class x we will have a SiblingInvalidationSet containing class z, with
// invalidatesSelf (the sibling itself is invalidated).
//
// .w ~ .y .z {}
// For class w we will have a SiblingInvalidationSet containing class y, with
// the SiblingInvalidationSet havings siblingDescendants containing class z.
//
// .v * {}
// For class v we will have a DescendantInvalidationSet with
// wholeSubtreeInvalid.
//
// .u ~ * {}
// For class u we will have a SiblingInvalidationSet with wholeSubtreeInvalid
// and invalidatesSelf (for all siblings, the sibling itself is invalidated).
//
// .t .v, .t ~ .z {}
// For class t we will have a SiblingInvalidationSet containing class z, with
// the SiblingInvalidationSet also holding descendants containing class v.
//
// We avoid virtual functions to minimize space consumption.
class CORE_EXPORT InvalidationSet
: public WTF::RefCounted<InvalidationSet, InvalidationSetDeleter> {
USING_FAST_MALLOC_WITH_TYPE_NAME(blink::InvalidationSet);
public:
InvalidationType GetType() const {
return static_cast<InvalidationType>(type_);
}
bool IsDescendantInvalidationSet() const {
return GetType() == kInvalidateDescendants;
}
bool IsSiblingInvalidationSet() const {
return GetType() == kInvalidateSiblings;
}
static void CacheTracingFlag();
bool InvalidatesElement(Element&) const;
bool InvalidatesTagName(Element&) const;
void AddClass(const AtomicString& class_name);
void AddId(const AtomicString& id);
void AddTagName(const AtomicString& tag_name);
void AddAttribute(const AtomicString& attribute_local_name);
void SetInvalidationFlags(InvalidationFlags flags) {
invalidation_flags_ = flags;
};
void SetWholeSubtreeInvalid();
bool WholeSubtreeInvalid() const {
return invalidation_flags_.WholeSubtreeInvalid();
}
void SetInvalidatesSelf() { invalidates_self_ = true; }
bool InvalidatesSelf() const { return invalidates_self_; }
void SetTreeBoundaryCrossing() {
invalidation_flags_.SetTreeBoundaryCrossing(true);
}
bool TreeBoundaryCrossing() const {
return invalidation_flags_.TreeBoundaryCrossing();
}
void SetInsertionPointCrossing() {
invalidation_flags_.SetInsertionPointCrossing(true);
}
bool InsertionPointCrossing() const {
return invalidation_flags_.InsertionPointCrossing();
}
void SetCustomPseudoInvalid() {
invalidation_flags_.SetInvalidateCustomPseudo(true);
}
bool CustomPseudoInvalid() const {
return invalidation_flags_.InvalidateCustomPseudo();
}
void SetInvalidatesSlotted() {
invalidation_flags_.SetInvalidatesSlotted(true);
}
bool InvalidatesSlotted() const {
return invalidation_flags_.InvalidatesSlotted();
}
const InvalidationFlags GetInvalidationFlags() const {
return invalidation_flags_;
};
void SetInvalidatesParts() { invalidation_flags_.SetInvalidatesParts(true); }
bool InvalidatesParts() const {
return invalidation_flags_.InvalidatesParts();
}
bool IsEmpty() const {
return !classes_ && !ids_ && !tag_names_ && !attributes_ &&
!invalidation_flags_.InvalidateCustomPseudo() &&
!invalidation_flags_.InsertionPointCrossing() &&
!invalidation_flags_.InvalidatesSlotted() &&
!invalidation_flags_.InvalidatesParts();
}
bool IsAlive() const { return is_alive_; }
void ToTracedValue(TracedValue*) const;
#ifndef NDEBUG
void Show() const;
#endif
const HashSet<AtomicString>& ClassSetForTesting() const {
DCHECK(classes_);
return *classes_;
}
const HashSet<AtomicString>& IdSetForTesting() const {
DCHECK(ids_);
return *ids_;
}
const HashSet<AtomicString>& TagNameSetForTesting() const {
DCHECK(tag_names_);
return *tag_names_;
}
const HashSet<AtomicString>& AttributeSetForTesting() const {
DCHECK(attributes_);
return *attributes_;
}
void Combine(const InvalidationSet& other);
// Returns a singleton DescendantInvalidationSet which only has
// InvalidatesSelf() set and is otherwise empty. As this is a common
// invalidation set for features only found in rightmost compounds,
// sharing this singleton between such features saves a lot of memory on
// sites with a big number of style rules.
static InvalidationSet* SelfInvalidationSet();
bool IsSelfInvalidationSet() const { return this == SelfInvalidationSet(); }
protected:
explicit InvalidationSet(InvalidationType);
~InvalidationSet() {
CHECK(is_alive_);
is_alive_ = false;
}
private:
friend struct InvalidationSetDeleter;
void Destroy() const;
HashSet<AtomicString>& EnsureClassSet();
HashSet<AtomicString>& EnsureIdSet();
HashSet<AtomicString>& EnsureTagNameSet();
HashSet<AtomicString>& EnsureAttributeSet();
// FIXME: optimize this if it becomes a memory issue.
std::unique_ptr<HashSet<AtomicString>> classes_;
std::unique_ptr<HashSet<AtomicString>> ids_;
std::unique_ptr<HashSet<AtomicString>> tag_names_;
std::unique_ptr<HashSet<AtomicString>> attributes_;
InvalidationFlags invalidation_flags_;
unsigned type_ : 1;
// If true, the element or sibling itself is invalid.
unsigned invalidates_self_ : 1;
// If true, the instance is alive and can be used.
unsigned is_alive_ : 1;
DISALLOW_COPY_AND_ASSIGN(InvalidationSet);
};
class CORE_EXPORT DescendantInvalidationSet final : public InvalidationSet {
public:
static scoped_refptr<DescendantInvalidationSet> Create() {
return base::AdoptRef(new DescendantInvalidationSet);
}
private:
DescendantInvalidationSet() : InvalidationSet(kInvalidateDescendants) {}
};
class CORE_EXPORT SiblingInvalidationSet final : public InvalidationSet {
public:
static scoped_refptr<SiblingInvalidationSet> Create(
scoped_refptr<DescendantInvalidationSet> descendants) {
return base::AdoptRef(new SiblingInvalidationSet(std::move(descendants)));
}
unsigned MaxDirectAdjacentSelectors() const {
return max_direct_adjacent_selectors_;
}
void UpdateMaxDirectAdjacentSelectors(unsigned value) {
max_direct_adjacent_selectors_ =
std::max(value, max_direct_adjacent_selectors_);
}
DescendantInvalidationSet* SiblingDescendants() const {
return sibling_descendant_invalidation_set_.get();
}
DescendantInvalidationSet& EnsureSiblingDescendants();
DescendantInvalidationSet* Descendants() const {
return descendant_invalidation_set_.get();
}
DescendantInvalidationSet& EnsureDescendants();
private:
explicit SiblingInvalidationSet(
scoped_refptr<DescendantInvalidationSet> descendants);
// Indicates the maximum possible number of siblings affected.
unsigned max_direct_adjacent_selectors_;
// Indicates the descendants of siblings.
scoped_refptr<DescendantInvalidationSet> sibling_descendant_invalidation_set_;
// Null if a given feature (class, attribute, id, pseudo-class) has only
// a SiblingInvalidationSet and not also a DescendantInvalidationSet.
scoped_refptr<DescendantInvalidationSet> descendant_invalidation_set_;
};
using InvalidationSetVector = Vector<scoped_refptr<InvalidationSet>>;
struct InvalidationLists {
InvalidationSetVector descendants;
InvalidationSetVector siblings;
};
DEFINE_TYPE_CASTS(DescendantInvalidationSet,
InvalidationSet,
value,
value->IsDescendantInvalidationSet(),
value.IsDescendantInvalidationSet());
DEFINE_TYPE_CASTS(SiblingInvalidationSet,
InvalidationSet,
value,
value->IsSiblingInvalidationSet(),
value.IsSiblingInvalidationSet());
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_INVALIDATION_INVALIDATION_SET_H_