Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : /* A global table that tracks images referenced by CSS variables. */
7 :
8 : #ifndef mozilla_CSSVariableImageTable_h
9 : #define mozilla_CSSVariableImageTable_h
10 :
11 : #include "nsClassHashtable.h"
12 : #include "nsCSSPropertyID.h"
13 : #include "nsCSSValue.h"
14 : #include "nsStyleContext.h"
15 : #include "nsTArray.h"
16 :
17 : /**
18 : * CSSVariableImageTable maintains a global mapping
19 : * (nsStyleContext, nsCSSPropertyID) -> nsTArray<ImageValue>
20 : * which allows us to track the relationship between CSS property values
21 : * involving variables and any images they may reference.
22 : *
23 : * When properties like background-image contain a normal url(), the
24 : * Declaration's data block will hold a reference to the ImageValue. When a
25 : * token stream is used, the Declaration only holds on to an
26 : * nsCSSValueTokenStream object, and the ImageValue would only exist for the
27 : * duration of nsRuleNode::WalkRuleTree, in the AutoCSSValueArray. So instead
28 : * when we re-parse a token stream and get an ImageValue, we record it in the
29 : * CSSVariableImageTable to keep the ImageValue alive. Such ImageValues are
30 : * eventually freed the next time the token stream is re-parsed, or when the
31 : * associated style context is destroyed.
32 : *
33 : * To add ImageValues to the CSSVariableImageTable, callers should pass a lambda
34 : * to CSSVariableImageTable::ReplaceAll() that calls
35 : * CSSVariableImageTable::Add() for each ImageValue that needs to be added to
36 : * the table. When callers are sure that the ImageValues for a given
37 : * nsStyleContext won't be needed anymore, they can call
38 : * CSSVariableImageTable::RemoveAll() to release them.
39 : */
40 :
41 : namespace mozilla {
42 : namespace CSSVariableImageTable {
43 :
44 : namespace detail {
45 :
46 : typedef nsTArray<RefPtr<css::ImageValue>> ImageValueArray;
47 : typedef nsClassHashtable<nsGenericHashKey<nsCSSPropertyID>, ImageValueArray>
48 : PerPropertyImageHashtable;
49 : typedef nsClassHashtable<nsPtrHashKey<nsStyleContext>, PerPropertyImageHashtable>
50 : CSSVariableImageHashtable;
51 :
52 2501 : inline CSSVariableImageHashtable& GetTable()
53 : {
54 2501 : static CSSVariableImageHashtable imageTable;
55 2501 : return imageTable;
56 : }
57 :
58 : #ifdef DEBUG
59 2649 : inline bool& IsReplacing()
60 : {
61 : static bool isReplacing = false;
62 2649 : return isReplacing;
63 : }
64 : #endif
65 :
66 : } // namespace detail
67 :
68 : /**
69 : * ReplaceAll() allows callers to replace the ImageValues associated with a
70 : * (nsStyleContext, nsCSSPropertyID) pair. The memory used by the previous list of
71 : * ImageValues is automatically released.
72 : *
73 : * @param aContext The style context the ImageValues are associated with.
74 : * @param aProp The CSS property the ImageValues are associated with.
75 : * @param aFunc A lambda that calls CSSVariableImageTable::Add() to add new
76 : * ImageValues which will replace the old ones.
77 : */
78 : template <typename Lambda>
79 883 : inline void ReplaceAll(nsStyleContext* aContext,
80 : nsCSSPropertyID aProp,
81 : Lambda aFunc)
82 : {
83 883 : MOZ_ASSERT(aContext);
84 :
85 883 : auto& imageTable = detail::GetTable();
86 :
87 : // Clear the existing image array, if any, for this property.
88 : {
89 883 : auto* perPropertyImageTable = imageTable.Get(aContext);
90 : auto* imageList = perPropertyImageTable ? perPropertyImageTable->Get(aProp)
91 883 : : nullptr;
92 883 : if (imageList) {
93 0 : imageList->ClearAndRetainStorage();
94 : }
95 : }
96 :
97 : #ifdef DEBUG
98 883 : MOZ_ASSERT(!detail::IsReplacing());
99 883 : detail::IsReplacing() = true;
100 : #endif
101 :
102 883 : aFunc();
103 :
104 : #ifdef DEBUG
105 883 : detail::IsReplacing() = false;
106 : #endif
107 :
108 : // Clean up.
109 883 : auto* perPropertyImageTable = imageTable.Get(aContext);
110 : auto* imageList = perPropertyImageTable ? perPropertyImageTable->Get(aProp)
111 883 : : nullptr;
112 883 : if (imageList) {
113 0 : if (imageList->IsEmpty()) {
114 : // We used to have an image array for this property, but now we don't.
115 : // Remove the entry in the per-property image table for this property.
116 : // That may then allow us to remove the entire per-property image table.
117 0 : perPropertyImageTable->Remove(aProp);
118 0 : if (perPropertyImageTable->Count() == 0) {
119 0 : imageTable.Remove(aContext);
120 : }
121 : } else {
122 : // We still have a non-empty image array for this property. Compact the
123 : // storage it's using if possible.
124 0 : imageList->Compact();
125 : }
126 : }
127 883 : }
128 :
129 : /**
130 : * Adds a new ImageValue @aValue to the CSSVariableImageTable, which will be
131 : * associated with @aContext and @aProp.
132 : *
133 : * It's illegal to call this function outside of a lambda passed to
134 : * CSSVariableImageTable::ReplaceAll().
135 : */
136 : inline void
137 0 : Add(nsStyleContext* aContext, nsCSSPropertyID aProp, css::ImageValue* aValue)
138 : {
139 0 : MOZ_ASSERT(aValue);
140 0 : MOZ_ASSERT(aContext);
141 0 : MOZ_ASSERT(detail::IsReplacing());
142 :
143 0 : auto& imageTable = detail::GetTable();
144 :
145 : // Ensure there's a per-property image table for this style context.
146 0 : auto* perPropertyImageTable = imageTable.Get(aContext);
147 0 : if (!perPropertyImageTable) {
148 0 : perPropertyImageTable = new detail::PerPropertyImageHashtable();
149 0 : imageTable.Put(aContext, perPropertyImageTable);
150 : }
151 :
152 : // Ensure there's an image array for this property.
153 0 : auto* imageList = perPropertyImageTable->Get(aProp);
154 0 : if (!imageList) {
155 0 : imageList = new detail::ImageValueArray();
156 0 : perPropertyImageTable->Put(aProp, imageList);
157 : }
158 :
159 : // Append the provided ImageValue to the list.
160 0 : imageList->AppendElement(aValue);
161 0 : }
162 :
163 : /**
164 : * Removes all ImageValues stored in the CSSVariableImageTable for the provided
165 : * @aContext.
166 : */
167 : inline void
168 1618 : RemoveAll(nsStyleContext* aContext)
169 : {
170 : // Move all ImageValue references into removedImageList so that we can
171 : // release them outside of any hashtable methods. (If we just call
172 : // Remove(aContext) on the table then we can end up calling back
173 : // re-entrantly into hashtable methods, as other style contexts
174 : // are released.)
175 3236 : detail::ImageValueArray removedImages;
176 1618 : auto& imageTable = detail::GetTable();
177 1618 : auto* perPropertyImageTable = imageTable.Get(aContext);
178 1618 : if (perPropertyImageTable) {
179 0 : for (auto it = perPropertyImageTable->Iter(); !it.Done(); it.Next()) {
180 0 : auto* imageList = it.UserData();
181 0 : removedImages.AppendElements(Move(*imageList));
182 : }
183 : }
184 1618 : imageTable.Remove(aContext);
185 1618 : }
186 :
187 : } // namespace CSSVariableImageTable
188 : } // namespace mozilla
189 :
190 : #endif // mozilla_CSSVariableImageTable_h
|