blob: b47bb0cc6554af54c827dd44376197d54adb841c [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 InvalidationSet_h
#define InvalidationSet_h
#include "core/CoreExport.h"
#include "wtf/Allocator.h"
#include "wtf/Assertions.h"
#include "wtf/Forward.h"
#include "wtf/HashSet.h"
#include "wtf/RefPtr.h"
#include "wtf/text/AtomicStringHash.h"
#include "wtf/text/StringHash.h"
#include <memory>
namespace blink {
class Element;
class TracedValue;
enum InvalidationType { InvalidateDescendants, InvalidateSiblings };
// 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 {
WTF_MAKE_NONCOPYABLE(InvalidationSet);
USING_FAST_MALLOC_WITH_TYPE_NAME(blink::InvalidationSet);
public:
InvalidationType type() const {
return static_cast<InvalidationType>(m_type);
}
bool isDescendantInvalidationSet() const {
return type() == InvalidateDescendants;
}
bool isSiblingInvalidationSet() const { return type() == InvalidateSiblings; }
static void cacheTracingFlag();
bool invalidatesElement(Element&) const;
void addClass(const AtomicString& className);
void addId(const AtomicString& id);
void addTagName(const AtomicString& tagName);
void addAttribute(const AtomicString& attributeLocalName);
void setWholeSubtreeInvalid();
bool wholeSubtreeInvalid() const { return m_allDescendantsMightBeInvalid; }
void setInvalidatesSelf() { m_invalidatesSelf = true; }
bool invalidatesSelf() const { return m_invalidatesSelf; }
void setTreeBoundaryCrossing() { m_treeBoundaryCrossing = true; }
bool treeBoundaryCrossing() const { return m_treeBoundaryCrossing; }
void setInsertionPointCrossing() { m_insertionPointCrossing = true; }
bool insertionPointCrossing() const { return m_insertionPointCrossing; }
void setCustomPseudoInvalid() { m_customPseudoInvalid = true; }
bool customPseudoInvalid() const { return m_customPseudoInvalid; }
void setInvalidatesSlotted() { m_invalidatesSlotted = true; }
bool invalidatesSlotted() const { return m_invalidatesSlotted; }
bool isEmpty() const {
return !m_classes && !m_ids && !m_tagNames && !m_attributes &&
!m_customPseudoInvalid && !m_insertionPointCrossing &&
!m_invalidatesSlotted;
}
bool isAlive() const { return m_isAlive; }
void toTracedValue(TracedValue*) const;
#ifndef NDEBUG
void show() const;
#endif
const HashSet<AtomicString>& classSetForTesting() const {
DCHECK(m_classes);
return *m_classes;
}
const HashSet<AtomicString>& idSetForTesting() const {
DCHECK(m_ids);
return *m_ids;
}
const HashSet<AtomicString>& tagNameSetForTesting() const {
DCHECK(m_tagNames);
return *m_tagNames;
}
const HashSet<AtomicString>& attributeSetForTesting() const {
DCHECK(m_attributes);
return *m_attributes;
}
void ref() { ++m_refCount; }
void deref() {
DCHECK_GT(m_refCount, 0);
--m_refCount;
if (!m_refCount)
destroy();
}
void combine(const InvalidationSet& other);
protected:
explicit InvalidationSet(InvalidationType);
~InvalidationSet() {
RELEASE_ASSERT(m_isAlive);
m_isAlive = false;
}
private:
void destroy();
HashSet<AtomicString>& ensureClassSet();
HashSet<AtomicString>& ensureIdSet();
HashSet<AtomicString>& ensureTagNameSet();
HashSet<AtomicString>& ensureAttributeSet();
// Implement reference counting manually so we can call a derived
// class destructor when the reference count decreases to 0.
// If we use RefCounted instead, at least one of our compilers
// requires the ability for RefCounted<InvalidationSet>::deref()
// to call ~InvalidationSet(), but this is not a virtual call.
int m_refCount;
// FIXME: optimize this if it becomes a memory issue.
std::unique_ptr<HashSet<AtomicString>> m_classes;
std::unique_ptr<HashSet<AtomicString>> m_ids;
std::unique_ptr<HashSet<AtomicString>> m_tagNames;
std::unique_ptr<HashSet<AtomicString>> m_attributes;
unsigned m_type : 1;
// If true, all descendants might be invalidated, so a full subtree recalc is
// required.
unsigned m_allDescendantsMightBeInvalid : 1;
// If true, the element or sibling itself is invalid.
unsigned m_invalidatesSelf : 1;
// If true, all descendants which are custom pseudo elements must be
// invalidated.
unsigned m_customPseudoInvalid : 1;
// If true, the invalidation must traverse into ShadowRoots with this set.
unsigned m_treeBoundaryCrossing : 1;
// If true, insertion point descendants must be invalidated.
unsigned m_insertionPointCrossing : 1;
// If true, distributed nodes of <slot> elements need to be invalidated.
unsigned m_invalidatesSlotted : 1;
// If true, the instance is alive and can be used.
unsigned m_isAlive : 1;
};
class CORE_EXPORT DescendantInvalidationSet final : public InvalidationSet {
public:
static PassRefPtr<DescendantInvalidationSet> create() {
return adoptRef(new DescendantInvalidationSet);
}
private:
DescendantInvalidationSet() : InvalidationSet(InvalidateDescendants) {}
};
class CORE_EXPORT SiblingInvalidationSet final : public InvalidationSet {
public:
static PassRefPtr<SiblingInvalidationSet> create(
PassRefPtr<DescendantInvalidationSet> descendants) {
return adoptRef(new SiblingInvalidationSet(std::move(descendants)));
}
unsigned maxDirectAdjacentSelectors() const {
return m_maxDirectAdjacentSelectors;
}
void updateMaxDirectAdjacentSelectors(unsigned value) {
m_maxDirectAdjacentSelectors =
std::max(value, m_maxDirectAdjacentSelectors);
}
DescendantInvalidationSet* siblingDescendants() const {
return m_siblingDescendantInvalidationSet.get();
}
DescendantInvalidationSet& ensureSiblingDescendants();
DescendantInvalidationSet* descendants() const {
return m_descendantInvalidationSet.get();
}
DescendantInvalidationSet& ensureDescendants();
private:
explicit SiblingInvalidationSet(
PassRefPtr<DescendantInvalidationSet> descendants);
// Indicates the maximum possible number of siblings affected.
unsigned m_maxDirectAdjacentSelectors;
// Indicates the descendants of siblings.
RefPtr<DescendantInvalidationSet> m_siblingDescendantInvalidationSet;
// Null if a given feature (class, attribute, id, pseudo-class) has only
// a SiblingInvalidationSet and not also a DescendantInvalidationSet.
RefPtr<DescendantInvalidationSet> m_descendantInvalidationSet;
};
using InvalidationSetVector = Vector<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 // InvalidationSet_h