Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/. */
6 /*
8 Implementations for the rule network classes.
10 To Do.
12 - Constrain() & Propagate() still feel like they are poorly named.
13 - As do Instantiation and InstantiationSet.
14 - Make InstantiationSet share and do copy-on-write.
15 - Make things iterative, instead of recursive.
17 */
19 #include "nscore.h"
20 #include "nsCOMPtr.h"
21 #include "plhash.h"
23 #include "prlog.h"
24 #ifdef PR_LOGGING
25 extern PRLogModuleInfo* gXULTemplateLog;
27 #include "nsString.h"
28 #include "nsUnicharUtils.h"
29 #include "nsXULContentUtils.h"
31 #endif
33 #include "nsRuleNetwork.h"
34 #include "nsXULTemplateResultSetRDF.h"
35 #include "nsRDFConMemberTestNode.h"
36 #include "nsRDFPropertyTestNode.h"
38 using namespace mozilla;
40 //----------------------------------------------------------------------
41 //
42 // nsRuleNetwork
43 //
45 nsresult
46 MemoryElementSet::Add(MemoryElement* aElement)
47 {
48 for (ConstIterator element = First(); element != Last(); ++element) {
49 if (*element == *aElement) {
50 // We've already got this element covered. Since Add()
51 // assumes ownership, and we aren't going to need this,
52 // just nuke it.
53 delete aElement;
54 return NS_OK;
55 }
56 }
58 List* list = new List;
59 if (! list)
60 return NS_ERROR_OUT_OF_MEMORY;
62 list->mElement = aElement;
63 list->mRefCnt = 1;
64 list->mNext = mElements;
66 mElements = list;
68 return NS_OK;
69 }
72 //----------------------------------------------------------------------
74 nsresult
75 nsAssignmentSet::Add(const nsAssignment& aAssignment)
76 {
77 NS_PRECONDITION(! HasAssignmentFor(aAssignment.mVariable), "variable already bound");
79 // XXXndeakin should this just silently fail?
80 if (HasAssignmentFor(aAssignment.mVariable))
81 return NS_ERROR_UNEXPECTED;
83 List* list = new List(aAssignment);
84 if (! list)
85 return NS_ERROR_OUT_OF_MEMORY;
87 list->mRefCnt = 1;
88 list->mNext = mAssignments;
90 mAssignments = list;
92 return NS_OK;
93 }
95 int32_t
96 nsAssignmentSet::Count() const
97 {
98 int32_t count = 0;
99 for (ConstIterator assignment = First(); assignment != Last(); ++assignment)
100 ++count;
102 return count;
103 }
105 bool
106 nsAssignmentSet::HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const
107 {
108 for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
109 if (assignment->mVariable == aVariable && assignment->mValue == aValue)
110 return true;
111 }
113 return false;
114 }
116 bool
117 nsAssignmentSet::HasAssignmentFor(nsIAtom* aVariable) const
118 {
119 for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
120 if (assignment->mVariable == aVariable)
121 return true;
122 }
124 return false;
125 }
127 bool
128 nsAssignmentSet::GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const
129 {
130 for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
131 if (assignment->mVariable == aVariable) {
132 *aValue = assignment->mValue;
133 NS_IF_ADDREF(*aValue);
134 return true;
135 }
136 }
138 *aValue = nullptr;
139 return false;
140 }
142 bool
143 nsAssignmentSet::Equals(const nsAssignmentSet& aSet) const
144 {
145 if (aSet.mAssignments == mAssignments)
146 return true;
148 // If they have a different number of assignments, then they're different.
149 if (Count() != aSet.Count())
150 return false;
152 // XXX O(n^2)! Ugh!
153 nsCOMPtr<nsIRDFNode> value;
154 for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
155 if (! aSet.GetAssignmentFor(assignment->mVariable, getter_AddRefs(value)))
156 return false;
158 if (assignment->mValue != value)
159 return false;
160 }
162 return true;
163 }
165 //----------------------------------------------------------------------
167 PLHashNumber
168 Instantiation::Hash(const void* aKey)
169 {
170 const Instantiation* inst = static_cast<const Instantiation*>(aKey);
172 PLHashNumber result = 0;
174 nsAssignmentSet::ConstIterator last = inst->mAssignments.Last();
175 for (nsAssignmentSet::ConstIterator assignment = inst->mAssignments.First();
176 assignment != last; ++assignment)
177 result ^= assignment->Hash();
179 return result;
180 }
183 int
184 Instantiation::Compare(const void* aLeft, const void* aRight)
185 {
186 const Instantiation* left = static_cast<const Instantiation*>(aLeft);
187 const Instantiation* right = static_cast<const Instantiation*>(aRight);
189 return *left == *right;
190 }
193 //----------------------------------------------------------------------
194 //
195 // InstantiationSet
196 //
198 InstantiationSet::InstantiationSet()
199 {
200 mHead.mPrev = mHead.mNext = &mHead;
201 MOZ_COUNT_CTOR(InstantiationSet);
202 }
205 InstantiationSet::InstantiationSet(const InstantiationSet& aInstantiationSet)
206 {
207 mHead.mPrev = mHead.mNext = &mHead;
209 // XXX replace with copy-on-write foo
210 ConstIterator last = aInstantiationSet.Last();
211 for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
212 Append(*inst);
214 MOZ_COUNT_CTOR(InstantiationSet);
215 }
217 InstantiationSet&
218 InstantiationSet::operator=(const InstantiationSet& aInstantiationSet)
219 {
220 // XXX replace with copy-on-write foo
221 Clear();
223 ConstIterator last = aInstantiationSet.Last();
224 for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
225 Append(*inst);
227 return *this;
228 }
231 void
232 InstantiationSet::Clear()
233 {
234 Iterator inst = First();
235 while (inst != Last())
236 Erase(inst++);
237 }
240 InstantiationSet::Iterator
241 InstantiationSet::Insert(Iterator aIterator, const Instantiation& aInstantiation)
242 {
243 List* newelement = new List();
244 if (newelement) {
245 newelement->mInstantiation = aInstantiation;
247 aIterator.mCurrent->mPrev->mNext = newelement;
249 newelement->mNext = aIterator.mCurrent;
250 newelement->mPrev = aIterator.mCurrent->mPrev;
252 aIterator.mCurrent->mPrev = newelement;
253 }
254 return aIterator;
255 }
257 InstantiationSet::Iterator
258 InstantiationSet::Erase(Iterator aIterator)
259 {
260 Iterator result = aIterator;
261 ++result;
262 aIterator.mCurrent->mNext->mPrev = aIterator.mCurrent->mPrev;
263 aIterator.mCurrent->mPrev->mNext = aIterator.mCurrent->mNext;
264 delete aIterator.mCurrent;
265 return result;
266 }
269 bool
270 InstantiationSet::HasAssignmentFor(nsIAtom* aVariable) const
271 {
272 return !Empty() ? First()->mAssignments.HasAssignmentFor(aVariable) : false;
273 }
275 //----------------------------------------------------------------------
276 //
277 // ReteNode
278 //
279 // The basic node in the network.
280 //
282 //----------------------------------------------------------------------
283 //
284 // TestNode
285 //
286 // to do:
287 // - FilterInstantiations() is poorly named
288 //
291 TestNode::TestNode(TestNode* aParent)
292 : mParent(aParent)
293 {
294 }
296 nsresult
297 TestNode::Propagate(InstantiationSet& aInstantiations,
298 bool aIsUpdate, bool& aTakenInstantiations)
299 {
300 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
301 ("TestNode[%p]: Propagate() begin", this));
303 aTakenInstantiations = false;
305 nsresult rv = FilterInstantiations(aInstantiations, nullptr);
306 if (NS_FAILED(rv))
307 return rv;
309 // if there is more than one child, each will need to be supplied with the
310 // original set of instantiations from this node, so create a copy in this
311 // case. If there is only one child, optimize and just pass the
312 // instantiations along to the child without copying
313 bool shouldCopy = (mKids.Count() > 1);
315 // See the header file for details about how instantiation ownership works.
316 if (! aInstantiations.Empty()) {
317 ReteNodeSet::Iterator last = mKids.Last();
318 for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid) {
319 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
320 ("TestNode[%p]: Propagate() passing to child %p", this, kid.operator->()));
322 // create a copy of the instantiations
323 if (shouldCopy) {
324 bool owned = false;
325 InstantiationSet* instantiations =
326 new InstantiationSet(aInstantiations);
327 if (!instantiations)
328 return NS_ERROR_OUT_OF_MEMORY;
329 rv = kid->Propagate(*instantiations, aIsUpdate, owned);
330 if (!owned)
331 delete instantiations;
332 if (NS_FAILED(rv))
333 return rv;
334 }
335 else {
336 rv = kid->Propagate(aInstantiations, aIsUpdate, aTakenInstantiations);
337 if (NS_FAILED(rv))
338 return rv;
339 }
340 }
341 }
343 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
344 ("TestNode[%p]: Propagate() end", this));
346 return NS_OK;
347 }
350 nsresult
351 TestNode::Constrain(InstantiationSet& aInstantiations)
352 {
353 nsresult rv;
355 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
356 ("TestNode[%p]: Constrain() begin", this));
358 // if the cantHandleYet flag is set by FilterInstantiations,
359 // there isn't enough information yet available to fill in.
360 // For this, continue the constrain all the way to the top
361 // and then call FilterInstantiations again afterwards. This
362 // should fill in any missing information.
363 bool cantHandleYet = false;
364 rv = FilterInstantiations(aInstantiations, &cantHandleYet);
365 if (NS_FAILED(rv)) return rv;
367 if (mParent && (!aInstantiations.Empty() || cantHandleYet)) {
368 // if we still have instantiations, or if the instantiations
369 // could not be filled in yet, then ride 'em on up to the
370 // parent to narrow them.
372 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
373 ("TestNode[%p]: Constrain() passing to parent %p", this, mParent));
375 rv = mParent->Constrain(aInstantiations);
377 if (NS_SUCCEEDED(rv) && cantHandleYet)
378 rv = FilterInstantiations(aInstantiations, nullptr);
379 }
380 else {
381 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
382 ("TestNode[%p]: Constrain() failed", this));
384 rv = NS_OK;
385 }
387 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
388 ("TestNode[%p]: Constrain() end", this));
390 return rv;
391 }
394 //----------------------------------------------------------------------
396 ReteNodeSet::ReteNodeSet()
397 : mNodes(nullptr), mCount(0), mCapacity(0)
398 {
399 }
401 ReteNodeSet::~ReteNodeSet()
402 {
403 Clear();
404 }
406 nsresult
407 ReteNodeSet::Add(ReteNode* aNode)
408 {
409 NS_PRECONDITION(aNode != nullptr, "null ptr");
410 if (! aNode)
411 return NS_ERROR_NULL_POINTER;
413 if (mCount >= mCapacity) {
414 int32_t capacity = mCapacity + 4;
415 ReteNode** nodes = new ReteNode*[capacity];
416 if (! nodes)
417 return NS_ERROR_OUT_OF_MEMORY;
419 for (int32_t i = mCount - 1; i >= 0; --i)
420 nodes[i] = mNodes[i];
422 delete[] mNodes;
424 mNodes = nodes;
425 mCapacity = capacity;
426 }
428 mNodes[mCount++] = aNode;
429 return NS_OK;
430 }
432 nsresult
433 ReteNodeSet::Clear()
434 {
435 delete[] mNodes;
436 mNodes = nullptr;
437 mCount = mCapacity = 0;
438 return NS_OK;
439 }