1 /* -*- Mode: C++; tab-width: 20; 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 #include "FramePropertyTable.h"
7 
8 #include "mozilla/MemoryReporting.h"
9 
10 namespace mozilla {
11 
12 void
13 FramePropertyTable::SetInternal(
14   const nsIFrame* aFrame, UntypedDescriptor aProperty, void* aValue)
15 {
16   NS_ASSERTION(aFrame, "Null frame?");
17   NS_ASSERTION(aProperty, "Null property?");
18 
19   if (mLastFrame != aFrame || !mLastEntry) {
20     mLastFrame = aFrame;
21     mLastEntry = mEntries.PutEntry(aFrame);
22   }
23   Entry* entry = mLastEntry;
24 
25   if (!entry->mProp.IsArray()) {
26     if (!entry->mProp.mProperty) {
27       // Empty entry, so we can just store our property in the empty slot
28       entry->mProp.mProperty = aProperty;
29       entry->mProp.mValue = aValue;
30       return;
31     }
32     if (entry->mProp.mProperty == aProperty) {
33       // Just overwrite the current value
34       entry->mProp.DestroyValueFor(aFrame);
35       entry->mProp.mValue = aValue;
36       return;
37     }
38 
39     // We need to expand the single current entry to an array
40     PropertyValue current = entry->mProp;
41     entry->mProp.mProperty = nullptr;
42     static_assert(sizeof(nsTArray<PropertyValue>) <= sizeof(void *),
43                   "Property array must fit entirely within entry->mProp.mValue");
44     new (&entry->mProp.mValue) nsTArray<PropertyValue>(4);
45     entry->mProp.ToArray()->AppendElement(current);
46   }
47 
48   nsTArray<PropertyValue>* array = entry->mProp.ToArray();
49   nsTArray<PropertyValue>::index_type index =
50     array->IndexOf(aProperty, 0, PropertyComparator());
51   if (index != nsTArray<PropertyValue>::NoIndex) {
52     PropertyValue* pv = &array->ElementAt(index);
53     pv->DestroyValueFor(aFrame);
54     pv->mValue = aValue;
55     return;
56   }
57 
58   array->AppendElement(PropertyValue(aProperty, aValue));
59 }
60 
61 void*
62 FramePropertyTable::GetInternal(
63   const nsIFrame* aFrame, UntypedDescriptor aProperty, bool* aFoundResult)
64 {
65   NS_ASSERTION(aFrame, "Null frame?");
66   NS_ASSERTION(aProperty, "Null property?");
67 
68   if (aFoundResult) {
69     *aFoundResult = false;
70   }
71 
72   if (mLastFrame != aFrame) {
73     mLastFrame = aFrame;
74     mLastEntry = mEntries.GetEntry(mLastFrame);
75   }
76   Entry* entry = mLastEntry;
77   if (!entry)
78     return nullptr;
79 
80   if (entry->mProp.mProperty == aProperty) {
81     if (aFoundResult) {
82       *aFoundResult = true;
83     }
84     return entry->mProp.mValue;
85   }
86   if (!entry->mProp.IsArray()) {
87     // There's just one property and it's not the one we want, bail
88     return nullptr;
89   }
90 
91   nsTArray<PropertyValue>* array = entry->mProp.ToArray();
92   nsTArray<PropertyValue>::index_type index =
93     array->IndexOf(aProperty, 0, PropertyComparator());
94   if (index == nsTArray<PropertyValue>::NoIndex)
95     return nullptr;
96 
97   if (aFoundResult) {
98     *aFoundResult = true;
99   }
100 
101   return array->ElementAt(index).mValue;
102 }
103 
104 void*
105 FramePropertyTable::RemoveInternal(
106   const nsIFrame* aFrame, UntypedDescriptor aProperty, bool* aFoundResult)
107 {
108   NS_ASSERTION(aFrame, "Null frame?");
109   NS_ASSERTION(aProperty, "Null property?");
110 
111   if (aFoundResult) {
112     *aFoundResult = false;
113   }
114 
115   if (mLastFrame != aFrame) {
116     mLastFrame = aFrame;
117     mLastEntry = mEntries.GetEntry(aFrame);
118   }
119   Entry* entry = mLastEntry;
120   if (!entry)
121     return nullptr;
122 
123   if (entry->mProp.mProperty == aProperty) {
124     // There's only one entry and it's the one we want
125     void* value = entry->mProp.mValue;
126 
127     // Here it's ok to use RemoveEntry() -- which may resize mEntries --
128     // because we null mLastEntry at the same time.
129     mEntries.RemoveEntry(entry);
130     mLastEntry = nullptr;
131     if (aFoundResult) {
132       *aFoundResult = true;
133     }
134     return value;
135   }
136   if (!entry->mProp.IsArray()) {
137     // There's just one property and it's not the one we want, bail
138     return nullptr;
139   }
140 
141   nsTArray<PropertyValue>* array = entry->mProp.ToArray();
142   nsTArray<PropertyValue>::index_type index =
143     array->IndexOf(aProperty, 0, PropertyComparator());
144   if (index == nsTArray<PropertyValue>::NoIndex) {
145     // No such property, bail
146     return nullptr;
147   }
148 
149   if (aFoundResult) {
150     *aFoundResult = true;
151   }
152 
153   void* result = array->ElementAt(index).mValue;
154 
155   uint32_t last = array->Length() - 1;
156   array->ElementAt(index) = array->ElementAt(last);
157   array->RemoveElementAt(last);
158 
159   if (last == 1) {
160     PropertyValue pv = array->ElementAt(0);
161     array->~nsTArray<PropertyValue>();
162     entry->mProp = pv;
163   }
164 
165   return result;
166 }
167 
168 void
169 FramePropertyTable::DeleteInternal(
170   const nsIFrame* aFrame, UntypedDescriptor aProperty)
171 {
172   NS_ASSERTION(aFrame, "Null frame?");
173   NS_ASSERTION(aProperty, "Null property?");
174 
175   bool found;
176   void* v = RemoveInternal(aFrame, aProperty, &found);
177   if (found) {
178     PropertyValue pv(aProperty, v);
179     pv.DestroyValueFor(aFrame);
180   }
181 }
182 
183 /* static */ void
184 FramePropertyTable::DeleteAllForEntry(Entry* aEntry)
185 {
186   if (!aEntry->mProp.IsArray()) {
187     aEntry->mProp.DestroyValueFor(aEntry->GetKey());
188     return;
189   }
190 
191   nsTArray<PropertyValue>* array = aEntry->mProp.ToArray();
192   for (uint32_t i = 0; i < array->Length(); ++i) {
193     array->ElementAt(i).DestroyValueFor(aEntry->GetKey());
194   }
195   array->~nsTArray<PropertyValue>();
196 }
197 
198 void
199 FramePropertyTable::DeleteAllFor(const nsIFrame* aFrame)
200 {
201   NS_ASSERTION(aFrame, "Null frame?");
202 
203   Entry* entry = mEntries.GetEntry(aFrame);
204   if (!entry)
205     return;
206 
207   if (mLastFrame == aFrame) {
208     // Flush cache. We assume DeleteAllForEntry will be called before
209     // a frame is destroyed.
210     mLastFrame = nullptr;
211     mLastEntry = nullptr;
212   }
213 
214   DeleteAllForEntry(entry);
215 
216   // mLastEntry points into mEntries, so we use RawRemoveEntry() which will not
217   // resize mEntries.
218   mEntries.RawRemoveEntry(entry);
219 }
220 
221 void
222 FramePropertyTable::DeleteAll()
223 {
224   mLastFrame = nullptr;
225   mLastEntry = nullptr;
226 
227   for (auto iter = mEntries.Iter(); !iter.Done(); iter.Next()) {
228     DeleteAllForEntry(iter.Get());
229   }
230   mEntries.Clear();
231 }
232 
233 size_t
234 FramePropertyTable::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
235 {
236   return mEntries.SizeOfExcludingThis(aMallocSizeOf);
237 }
238 
239 } // namespace mozilla
240