1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 mozilla_IntrinsicISizesCache_h
#define mozilla_IntrinsicISizesCache_h
#include "nsIFrame.h"
namespace mozilla {
// Some frame classes keep a cache of intrinsic inline sizes. This class
// encapsulates the logic for caching them depending on the IntrinsicSizeInput.
//
// The cache is intended to take as little space as possible
// (max(sizeof(nscoord) * 2, sizeof(void*))), when there are no percentage-size
// dependencies.
struct IntrinsicISizesCache final {
IntrinsicISizesCache() {
new (&mInline) InlineCache();
MOZ_ASSERT(IsInline());
}
~IntrinsicISizesCache() { delete GetOutOfLine(); }
template <typename Compute>
nscoord GetOrSet(nsIFrame& aFrame, IntrinsicISizeType aType,
const IntrinsicSizeInput& aInput, Compute aCompute) {
bool dependentOnPercentBSize = aFrame.HasAnyStateBits(
NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
nscoord value = Get(dependentOnPercentBSize, aType, aInput);
if (value != kNotFound) {
return value;
}
value = aCompute();
// Inside of aCompute(), we might have newly discovered that we do have a
// descendant whose intrinsic isize depends on our bsize; so we check that
// state bit again before updating the cache.
dependentOnPercentBSize = aFrame.HasAnyStateBits(
NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
Set(dependentOnPercentBSize, aType, aInput, value);
return value;
}
void Clear() {
if (auto* ool = GetOutOfLine()) {
ool->mCacheWithPercentageBasis.Clear();
ool->mCacheWithoutPercentageBasis.Clear();
ool->mLastPercentageBasis.reset();
} else {
mInline.Clear();
}
}
private:
// We use nscoord_MAX rather than NS_INTRINSIC_ISIZE_UNKNOWN as our sentinel
// value so that our high bit is always free.
static constexpr nscoord kNotFound = nscoord_MAX;
nscoord Get(bool aDependentOnPercentBSize, IntrinsicISizeType aType,
const IntrinsicSizeInput& aInput) const {
const bool usePercentageAwareCache =
aDependentOnPercentBSize && aInput.HasSomePercentageBasisForChildren();
if (!usePercentageAwareCache) {
if (auto* ool = GetOutOfLine()) {
return ool->mCacheWithoutPercentageBasis.Get(aType);
}
return mInline.Get(aType);
}
if (auto* ool = GetOutOfLine()) {
if (ool->mLastPercentageBasis == aInput.mPercentageBasisForChildren) {
return ool->mCacheWithPercentageBasis.Get(aType);
}
}
return kNotFound;
}
void Set(bool aDependentOnPercentBSize, IntrinsicISizeType aType,
const IntrinsicSizeInput& aInput, nscoord aValue) {
// Intrinsic sizes should be nonnegative, so this std::max clamping should
// rarely be necessary except in cases of integer overflow. We have to be
// strict about it, though, because of how we (ab)use the high bit
// (see kHighBit)
aValue = std::max(aValue, 0);
const bool usePercentageAwareCache =
aDependentOnPercentBSize && aInput.HasSomePercentageBasisForChildren();
if (usePercentageAwareCache) {
auto* ool = EnsureOutOfLine();
if (ool->mLastPercentageBasis != aInput.mPercentageBasisForChildren) {
ool->mLastPercentageBasis = aInput.mPercentageBasisForChildren;
ool->mCacheWithPercentageBasis.Clear();
}
ool->mCacheWithPercentageBasis.Set(aType, aValue);
} else if (auto* ool = GetOutOfLine()) {
ool->mCacheWithoutPercentageBasis.Set(aType, aValue);
} else {
mInline.Set(aType, aValue);
// No inline value should be able to cause us to misinterpret our
// representation as out-of-line, because intrinsic isizes should always
// be non-negative.
MOZ_DIAGNOSTIC_ASSERT(IsInline());
}
}
struct InlineCache {
nscoord mCachedMinISize = kNotFound;
nscoord mCachedPrefISize = kNotFound;
nscoord Get(IntrinsicISizeType aType) const {
return aType == IntrinsicISizeType::MinISize ? mCachedMinISize
: mCachedPrefISize;
}
void Set(IntrinsicISizeType aType, nscoord aValue) {
MOZ_ASSERT(aValue >= 0);
if (aType == IntrinsicISizeType::MinISize) {
mCachedMinISize = aValue;
} else {
mCachedPrefISize = aValue;
}
}
void Clear() { *this = {}; }
};
struct OutOfLineCache {
InlineCache mCacheWithoutPercentageBasis;
InlineCache mCacheWithPercentageBasis;
Maybe<LogicalSize> mLastPercentageBasis;
};
// If the high bit of mOutOfLine is 1, then it points to an OutOfLineCache.
union {
InlineCache mInline;
struct {
#ifndef HAVE_64BIT_BUILD
uintptr_t mPadding = 0;
#endif
uintptr_t mOutOfLine = 0;
};
};
static constexpr uintptr_t kHighBit = uintptr_t(1)
<< (sizeof(void*) * CHAR_BIT - 1);
bool IsOutOfLine() const {
#ifdef HAVE_64BIT_BUILD
return mOutOfLine & kHighBit;
#else
return mPadding & kHighBit;
#endif
}
bool IsInline() const { return !IsOutOfLine(); }
OutOfLineCache* EnsureOutOfLine() {
if (auto* ool = GetOutOfLine()) {
return ool;
}
auto inlineCache = mInline;
auto* ool = new OutOfLineCache();
ool->mCacheWithoutPercentageBasis = inlineCache;
#ifdef HAVE_64BIT_BUILD
MOZ_ASSERT((reinterpret_cast<uintptr_t>(ool) & kHighBit) == 0);
mOutOfLine = reinterpret_cast<uintptr_t>(ool) | kHighBit;
#else
mOutOfLine = reinterpret_cast<uintptr_t>(ool);
mPadding = kHighBit;
#endif
MOZ_ASSERT(IsOutOfLine());
return ool;
}
OutOfLineCache* GetOutOfLine() const {
if (!IsOutOfLine()) {
return nullptr;
}
#ifdef HAVE_64BIT_BUILD
return reinterpret_cast<OutOfLineCache*>(mOutOfLine & ~kHighBit);
#else
return reinterpret_cast<OutOfLineCache*>(mOutOfLine);
#endif
}
};
static_assert(sizeof(IntrinsicISizesCache) == 8, "Unexpected cache size");
} // namespace mozilla
#endif
|