Line data Source code
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/. */
5 :
6 : #include "nsTemplateRule.h"
7 : #include "nsTemplateMatch.h"
8 : #include "nsXULContentUtils.h"
9 : #include "nsUnicharUtils.h"
10 : #include "nsReadableUtils.h"
11 : #include "nsICollation.h"
12 :
13 0 : nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
14 : const nsAString& aRelation,
15 : nsIAtom* aTargetVariable,
16 : bool aIgnoreCase,
17 0 : bool aNegate)
18 : : mSourceVariable(aSourceVariable),
19 : mTargetVariable(aTargetVariable),
20 : mIgnoreCase(aIgnoreCase),
21 : mNegate(aNegate),
22 0 : mNext(nullptr)
23 : {
24 0 : SetRelation(aRelation);
25 :
26 0 : MOZ_COUNT_CTOR(nsTemplateCondition);
27 0 : }
28 :
29 0 : nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
30 : const nsAString& aRelation,
31 : const nsAString& aTargets,
32 : bool aIgnoreCase,
33 : bool aNegate,
34 0 : bool aIsMultiple)
35 : : mSourceVariable(aSourceVariable),
36 : mIgnoreCase(aIgnoreCase),
37 : mNegate(aNegate),
38 0 : mNext(nullptr)
39 : {
40 0 : SetRelation(aRelation);
41 :
42 0 : if (aIsMultiple) {
43 0 : int32_t start = 0, end = 0;
44 0 : while ((end = aTargets.FindChar(',',start)) >= 0) {
45 0 : if (end > start) {
46 0 : mTargetList.AppendElement(Substring(aTargets, start, end - start));
47 : }
48 0 : start = end + 1;
49 : }
50 0 : if (start < int32_t(aTargets.Length())) {
51 0 : mTargetList.AppendElement(Substring(aTargets, start));
52 : }
53 : }
54 : else {
55 0 : mTargetList.AppendElement(aTargets);
56 : }
57 :
58 0 : MOZ_COUNT_CTOR(nsTemplateCondition);
59 0 : }
60 :
61 0 : nsTemplateCondition::nsTemplateCondition(const nsAString& aSource,
62 : const nsAString& aRelation,
63 : nsIAtom* aTargetVariable,
64 : bool aIgnoreCase,
65 0 : bool aNegate)
66 : : mSource(aSource),
67 : mTargetVariable(aTargetVariable),
68 : mIgnoreCase(aIgnoreCase),
69 : mNegate(aNegate),
70 0 : mNext(nullptr)
71 : {
72 0 : SetRelation(aRelation);
73 :
74 0 : MOZ_COUNT_CTOR(nsTemplateCondition);
75 0 : }
76 :
77 : void
78 0 : nsTemplateCondition::SetRelation(const nsAString& aRelation)
79 : {
80 0 : if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty())
81 0 : mRelation = eEquals;
82 0 : else if (aRelation.EqualsLiteral("less"))
83 0 : mRelation = eLess;
84 0 : else if (aRelation.EqualsLiteral("greater"))
85 0 : mRelation = eGreater;
86 0 : else if (aRelation.EqualsLiteral("before"))
87 0 : mRelation = eBefore;
88 0 : else if (aRelation.EqualsLiteral("after"))
89 0 : mRelation = eAfter;
90 0 : else if (aRelation.EqualsLiteral("startswith"))
91 0 : mRelation = eStartswith;
92 0 : else if (aRelation.EqualsLiteral("endswith"))
93 0 : mRelation = eEndswith;
94 0 : else if (aRelation.EqualsLiteral("contains"))
95 0 : mRelation = eContains;
96 : else
97 0 : mRelation = eUnknown;
98 0 : }
99 :
100 : bool
101 0 : nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult)
102 : {
103 0 : bool match = false;
104 :
105 0 : nsAutoString leftString;
106 0 : if (mSourceVariable)
107 0 : aResult->GetBindingFor(mSourceVariable, leftString);
108 : else
109 0 : leftString.Assign(mSource);
110 :
111 0 : if (mTargetVariable) {
112 0 : nsAutoString rightString;
113 0 : aResult->GetBindingFor(mTargetVariable, rightString);
114 :
115 0 : match = CheckMatchStrings(leftString, rightString);
116 : }
117 : else {
118 : // iterate over the strings in the target and determine
119 : // whether there is a match.
120 0 : uint32_t length = mTargetList.Length();
121 0 : for (uint32_t t = 0; t < length; t++) {
122 0 : match = CheckMatchStrings(leftString, mTargetList[t]);
123 :
124 : // stop once a match is found. In negate mode, stop once a
125 : // target does not match.
126 0 : if (match != mNegate) break;
127 : }
128 : }
129 :
130 0 : return match;
131 : }
132 :
133 :
134 : bool
135 0 : nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString,
136 : const nsAString& aRightString)
137 : {
138 0 : bool match = false;
139 :
140 0 : if (aRightString.IsEmpty()) {
141 0 : if ((mRelation == eEquals) && aLeftString.IsEmpty())
142 0 : match = true;
143 : }
144 : else {
145 0 : switch (mRelation) {
146 : case eEquals:
147 0 : if (mIgnoreCase)
148 0 : match = aLeftString.Equals(aRightString,
149 0 : nsCaseInsensitiveStringComparator());
150 : else
151 0 : match = aLeftString.Equals(aRightString);
152 0 : break;
153 :
154 : case eLess:
155 : case eGreater:
156 : {
157 : // non-numbers always compare false
158 : nsresult err;
159 0 : int32_t leftint = PromiseFlatString(aLeftString).ToInteger(&err);
160 0 : if (NS_SUCCEEDED(err)) {
161 0 : int32_t rightint = PromiseFlatString(aRightString).ToInteger(&err);
162 0 : if (NS_SUCCEEDED(err)) {
163 0 : match = (mRelation == eLess) ? (leftint < rightint) :
164 : (leftint > rightint);
165 : }
166 : }
167 :
168 0 : break;
169 : }
170 :
171 : case eBefore:
172 : {
173 0 : nsICollation* collation = nsXULContentUtils::GetCollation();
174 0 : if (collation) {
175 : int32_t sortOrder;
176 0 : collation->CompareString((mIgnoreCase ?
177 : static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) :
178 : static_cast<int32_t>(nsICollation::kCollationCaseSensitive)),
179 : aLeftString,
180 : aRightString,
181 0 : &sortOrder);
182 0 : match = (sortOrder < 0);
183 : }
184 0 : else if (mIgnoreCase) {
185 0 : match = (Compare(aLeftString, aRightString,
186 0 : nsCaseInsensitiveStringComparator()) < 0);
187 : }
188 : else {
189 0 : match = (Compare(aLeftString, aRightString) < 0);
190 : }
191 0 : break;
192 : }
193 :
194 : case eAfter:
195 : {
196 0 : nsICollation* collation = nsXULContentUtils::GetCollation();
197 0 : if (collation) {
198 : int32_t sortOrder;
199 0 : collation->CompareString((mIgnoreCase ?
200 : static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) :
201 : static_cast<int32_t>(nsICollation::kCollationCaseSensitive)),
202 : aLeftString,
203 : aRightString,
204 0 : &sortOrder);
205 0 : match = (sortOrder > 0);
206 : }
207 0 : else if (mIgnoreCase) {
208 0 : match = (Compare(aLeftString, aRightString,
209 0 : nsCaseInsensitiveStringComparator()) > 0);
210 : }
211 : else {
212 0 : match = (Compare(aLeftString, aRightString) > 0);
213 : }
214 0 : break;
215 : }
216 :
217 : case eStartswith:
218 0 : if (mIgnoreCase)
219 : match = (StringBeginsWith(aLeftString, aRightString,
220 0 : nsCaseInsensitiveStringComparator()));
221 : else
222 0 : match = (StringBeginsWith(aLeftString, aRightString));
223 0 : break;
224 :
225 : case eEndswith:
226 0 : if (mIgnoreCase)
227 : match = (StringEndsWith(aLeftString, aRightString,
228 0 : nsCaseInsensitiveStringComparator()));
229 : else
230 0 : match = (StringEndsWith(aLeftString, aRightString));
231 0 : break;
232 :
233 : case eContains:
234 : {
235 0 : nsAString::const_iterator start, end;
236 0 : aLeftString.BeginReading(start);
237 0 : aLeftString.EndReading(end);
238 0 : if (mIgnoreCase)
239 0 : match = CaseInsensitiveFindInReadable(aRightString, start, end);
240 : else
241 0 : match = FindInReadable(aRightString, start, end);
242 0 : break;
243 : }
244 :
245 : default:
246 0 : break;
247 : }
248 : }
249 :
250 0 : if (mNegate) match = !match;
251 :
252 0 : return match;
253 : }
254 :
255 0 : nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode,
256 : nsIContent* aAction,
257 0 : nsTemplateQuerySet* aQuerySet)
258 : : mQuerySet(aQuerySet),
259 : mAction(aAction),
260 : mBindings(nullptr),
261 0 : mConditions(nullptr)
262 : {
263 0 : MOZ_COUNT_CTOR(nsTemplateRule);
264 0 : mRuleNode = do_QueryInterface(aRuleNode);
265 0 : }
266 :
267 0 : nsTemplateRule::nsTemplateRule(const nsTemplateRule& aOtherRule)
268 0 : : mQuerySet(aOtherRule.mQuerySet),
269 : mRuleNode(aOtherRule.mRuleNode),
270 : mAction(aOtherRule.mAction),
271 : mBindings(nullptr),
272 0 : mConditions(nullptr)
273 : {
274 0 : MOZ_COUNT_CTOR(nsTemplateRule);
275 0 : }
276 :
277 0 : nsTemplateRule::~nsTemplateRule()
278 : {
279 0 : MOZ_COUNT_DTOR(nsTemplateRule);
280 :
281 0 : while (mBindings) {
282 0 : Binding* doomed = mBindings;
283 0 : mBindings = mBindings->mNext;
284 0 : delete doomed;
285 : }
286 :
287 0 : while (mConditions) {
288 0 : nsTemplateCondition* cdel = mConditions;
289 0 : mConditions = mConditions->GetNext();
290 0 : delete cdel;
291 : }
292 0 : }
293 :
294 : nsresult
295 0 : nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const
296 : {
297 0 : *aRuleNode = mRuleNode;
298 0 : NS_IF_ADDREF(*aRuleNode);
299 0 : return NS_OK;
300 : }
301 :
302 0 : void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition)
303 : {
304 0 : while (mConditions) {
305 0 : nsTemplateCondition* cdel = mConditions;
306 0 : mConditions = mConditions->GetNext();
307 0 : delete cdel;
308 : }
309 :
310 0 : mConditions = aCondition;
311 0 : }
312 :
313 : bool
314 0 : nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const
315 : {
316 : // check the conditions in the rule first
317 0 : nsTemplateCondition* condition = mConditions;
318 0 : while (condition) {
319 0 : if (!condition->CheckMatch(aResult))
320 0 : return false;
321 :
322 0 : condition = condition->GetNext();
323 : }
324 :
325 0 : if (mRuleFilter) {
326 : // if a rule filter was set, check it for a match. If an error occurs,
327 : // assume that the match was acceptable
328 : bool match;
329 0 : nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match);
330 0 : return NS_FAILED(rv) || match;
331 : }
332 :
333 0 : return true;
334 : }
335 :
336 : bool
337 0 : nsTemplateRule::HasBinding(nsIAtom* aSourceVariable,
338 : nsAString& aExpr,
339 : nsIAtom* aTargetVariable) const
340 : {
341 0 : for (Binding* binding = mBindings; binding != nullptr; binding = binding->mNext) {
342 0 : if ((binding->mSourceVariable == aSourceVariable) &&
343 0 : (binding->mExpr.Equals(aExpr)) &&
344 0 : (binding->mTargetVariable == aTargetVariable))
345 0 : return true;
346 : }
347 :
348 0 : return false;
349 : }
350 :
351 : nsresult
352 0 : nsTemplateRule::AddBinding(nsIAtom* aSourceVariable,
353 : nsAString& aExpr,
354 : nsIAtom* aTargetVariable)
355 : {
356 0 : NS_PRECONDITION(aSourceVariable != 0, "no source variable!");
357 0 : if (! aSourceVariable)
358 0 : return NS_ERROR_INVALID_ARG;
359 :
360 0 : NS_PRECONDITION(aTargetVariable != 0, "no target variable!");
361 0 : if (! aTargetVariable)
362 0 : return NS_ERROR_INVALID_ARG;
363 :
364 0 : NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable),
365 : "binding added twice");
366 :
367 0 : Binding* newbinding = new Binding;
368 0 : if (! newbinding)
369 0 : return NS_ERROR_OUT_OF_MEMORY;
370 :
371 0 : newbinding->mSourceVariable = aSourceVariable;
372 0 : newbinding->mTargetVariable = aTargetVariable;
373 0 : newbinding->mParent = nullptr;
374 :
375 0 : newbinding->mExpr.Assign(aExpr);
376 :
377 0 : Binding* binding = mBindings;
378 0 : Binding** link = &mBindings;
379 :
380 : // Insert it at the end, unless we detect that an existing
381 : // binding's source is dependent on the newbinding's target.
382 : //
383 : // XXXwaterson this isn't enough to make sure that we get all of
384 : // the dependencies worked out right, but it'll do for now. For
385 : // example, if you have (ab, bc, cd), and insert them in the order
386 : // (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the
387 : // person uses a natural ordering when writing the XUL, it'll all
388 : // work out ok.
389 0 : while (binding) {
390 0 : if (binding->mSourceVariable == newbinding->mTargetVariable) {
391 0 : binding->mParent = newbinding;
392 0 : break;
393 : }
394 0 : else if (binding->mTargetVariable == newbinding->mSourceVariable) {
395 0 : newbinding->mParent = binding;
396 : }
397 :
398 0 : link = &binding->mNext;
399 0 : binding = binding->mNext;
400 : }
401 :
402 : // Insert the newbinding
403 0 : *link = newbinding;
404 0 : newbinding->mNext = binding;
405 0 : return NS_OK;
406 : }
407 :
408 : nsresult
409 0 : nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor)
410 : {
411 0 : Binding* binding = mBindings;
412 :
413 0 : while (binding) {
414 0 : nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable,
415 0 : binding->mSourceVariable, binding->mExpr);
416 0 : if (NS_FAILED(rv)) return rv;
417 :
418 0 : binding = binding->mNext;
419 : }
420 :
421 0 : return NS_OK;
422 : }
|