1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/xul/templates/src/nsTemplateRule.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,422 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsTemplateRule.h" 1.10 +#include "nsTemplateMatch.h" 1.11 +#include "nsXULContentUtils.h" 1.12 +#include "nsUnicharUtils.h" 1.13 +#include "nsReadableUtils.h" 1.14 +#include "nsICollation.h" 1.15 + 1.16 +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, 1.17 + const nsAString& aRelation, 1.18 + nsIAtom* aTargetVariable, 1.19 + bool aIgnoreCase, 1.20 + bool aNegate) 1.21 + : mSourceVariable(aSourceVariable), 1.22 + mTargetVariable(aTargetVariable), 1.23 + mIgnoreCase(aIgnoreCase), 1.24 + mNegate(aNegate), 1.25 + mNext(nullptr) 1.26 +{ 1.27 + SetRelation(aRelation); 1.28 + 1.29 + MOZ_COUNT_CTOR(nsTemplateCondition); 1.30 +} 1.31 + 1.32 +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, 1.33 + const nsAString& aRelation, 1.34 + const nsAString& aTargets, 1.35 + bool aIgnoreCase, 1.36 + bool aNegate, 1.37 + bool aIsMultiple) 1.38 + : mSourceVariable(aSourceVariable), 1.39 + mIgnoreCase(aIgnoreCase), 1.40 + mNegate(aNegate), 1.41 + mNext(nullptr) 1.42 +{ 1.43 + SetRelation(aRelation); 1.44 + 1.45 + if (aIsMultiple) { 1.46 + int32_t start = 0, end = 0; 1.47 + while ((end = aTargets.FindChar(',',start)) >= 0) { 1.48 + if (end > start) { 1.49 + mTargetList.AppendElement(Substring(aTargets, start, end - start)); 1.50 + } 1.51 + start = end + 1; 1.52 + } 1.53 + if (start < int32_t(aTargets.Length())) { 1.54 + mTargetList.AppendElement(Substring(aTargets, start)); 1.55 + } 1.56 + } 1.57 + else { 1.58 + mTargetList.AppendElement(aTargets); 1.59 + } 1.60 + 1.61 + MOZ_COUNT_CTOR(nsTemplateCondition); 1.62 +} 1.63 + 1.64 +nsTemplateCondition::nsTemplateCondition(const nsAString& aSource, 1.65 + const nsAString& aRelation, 1.66 + nsIAtom* aTargetVariable, 1.67 + bool aIgnoreCase, 1.68 + bool aNegate) 1.69 + : mSource(aSource), 1.70 + mTargetVariable(aTargetVariable), 1.71 + mIgnoreCase(aIgnoreCase), 1.72 + mNegate(aNegate), 1.73 + mNext(nullptr) 1.74 +{ 1.75 + SetRelation(aRelation); 1.76 + 1.77 + MOZ_COUNT_CTOR(nsTemplateCondition); 1.78 +} 1.79 + 1.80 +void 1.81 +nsTemplateCondition::SetRelation(const nsAString& aRelation) 1.82 +{ 1.83 + if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty()) 1.84 + mRelation = eEquals; 1.85 + else if (aRelation.EqualsLiteral("less")) 1.86 + mRelation = eLess; 1.87 + else if (aRelation.EqualsLiteral("greater")) 1.88 + mRelation = eGreater; 1.89 + else if (aRelation.EqualsLiteral("before")) 1.90 + mRelation = eBefore; 1.91 + else if (aRelation.EqualsLiteral("after")) 1.92 + mRelation = eAfter; 1.93 + else if (aRelation.EqualsLiteral("startswith")) 1.94 + mRelation = eStartswith; 1.95 + else if (aRelation.EqualsLiteral("endswith")) 1.96 + mRelation = eEndswith; 1.97 + else if (aRelation.EqualsLiteral("contains")) 1.98 + mRelation = eContains; 1.99 + else 1.100 + mRelation = eUnknown; 1.101 +} 1.102 + 1.103 +bool 1.104 +nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult) 1.105 +{ 1.106 + bool match = false; 1.107 + 1.108 + nsAutoString leftString; 1.109 + if (mSourceVariable) 1.110 + aResult->GetBindingFor(mSourceVariable, leftString); 1.111 + else 1.112 + leftString.Assign(mSource); 1.113 + 1.114 + if (mTargetVariable) { 1.115 + nsAutoString rightString; 1.116 + aResult->GetBindingFor(mTargetVariable, rightString); 1.117 + 1.118 + match = CheckMatchStrings(leftString, rightString); 1.119 + } 1.120 + else { 1.121 + // iterate over the strings in the target and determine 1.122 + // whether there is a match. 1.123 + uint32_t length = mTargetList.Length(); 1.124 + for (uint32_t t = 0; t < length; t++) { 1.125 + match = CheckMatchStrings(leftString, mTargetList[t]); 1.126 + 1.127 + // stop once a match is found. In negate mode, stop once a 1.128 + // target does not match. 1.129 + if (match != mNegate) break; 1.130 + } 1.131 + } 1.132 + 1.133 + return match; 1.134 +} 1.135 + 1.136 + 1.137 +bool 1.138 +nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString, 1.139 + const nsAString& aRightString) 1.140 +{ 1.141 + bool match = false; 1.142 + 1.143 + if (aRightString.IsEmpty()) { 1.144 + if ((mRelation == eEquals) && aLeftString.IsEmpty()) 1.145 + match = true; 1.146 + } 1.147 + else { 1.148 + switch (mRelation) { 1.149 + case eEquals: 1.150 + if (mIgnoreCase) 1.151 + match = aLeftString.Equals(aRightString, 1.152 + nsCaseInsensitiveStringComparator()); 1.153 + else 1.154 + match = aLeftString.Equals(aRightString); 1.155 + break; 1.156 + 1.157 + case eLess: 1.158 + case eGreater: 1.159 + { 1.160 + // non-numbers always compare false 1.161 + nsresult err; 1.162 + int32_t leftint = PromiseFlatString(aLeftString).ToInteger(&err); 1.163 + if (NS_SUCCEEDED(err)) { 1.164 + int32_t rightint = PromiseFlatString(aRightString).ToInteger(&err); 1.165 + if (NS_SUCCEEDED(err)) { 1.166 + match = (mRelation == eLess) ? (leftint < rightint) : 1.167 + (leftint > rightint); 1.168 + } 1.169 + } 1.170 + 1.171 + break; 1.172 + } 1.173 + 1.174 + case eBefore: 1.175 + { 1.176 + nsICollation* collation = nsXULContentUtils::GetCollation(); 1.177 + if (collation) { 1.178 + int32_t sortOrder; 1.179 + collation->CompareString((mIgnoreCase ? 1.180 + static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : 1.181 + static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), 1.182 + aLeftString, 1.183 + aRightString, 1.184 + &sortOrder); 1.185 + match = (sortOrder < 0); 1.186 + } 1.187 + else if (mIgnoreCase) { 1.188 + match = (Compare(aLeftString, aRightString, 1.189 + nsCaseInsensitiveStringComparator()) < 0); 1.190 + } 1.191 + else { 1.192 + match = (Compare(aLeftString, aRightString) < 0); 1.193 + } 1.194 + break; 1.195 + } 1.196 + 1.197 + case eAfter: 1.198 + { 1.199 + nsICollation* collation = nsXULContentUtils::GetCollation(); 1.200 + if (collation) { 1.201 + int32_t sortOrder; 1.202 + collation->CompareString((mIgnoreCase ? 1.203 + static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : 1.204 + static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), 1.205 + aLeftString, 1.206 + aRightString, 1.207 + &sortOrder); 1.208 + match = (sortOrder > 0); 1.209 + } 1.210 + else if (mIgnoreCase) { 1.211 + match = (Compare(aLeftString, aRightString, 1.212 + nsCaseInsensitiveStringComparator()) > 0); 1.213 + } 1.214 + else { 1.215 + match = (Compare(aLeftString, aRightString) > 0); 1.216 + } 1.217 + break; 1.218 + } 1.219 + 1.220 + case eStartswith: 1.221 + if (mIgnoreCase) 1.222 + match = (StringBeginsWith(aLeftString, aRightString, 1.223 + nsCaseInsensitiveStringComparator())); 1.224 + else 1.225 + match = (StringBeginsWith(aLeftString, aRightString)); 1.226 + break; 1.227 + 1.228 + case eEndswith: 1.229 + if (mIgnoreCase) 1.230 + match = (StringEndsWith(aLeftString, aRightString, 1.231 + nsCaseInsensitiveStringComparator())); 1.232 + else 1.233 + match = (StringEndsWith(aLeftString, aRightString)); 1.234 + break; 1.235 + 1.236 + case eContains: 1.237 + { 1.238 + nsAString::const_iterator start, end; 1.239 + aLeftString.BeginReading(start); 1.240 + aLeftString.EndReading(end); 1.241 + if (mIgnoreCase) 1.242 + match = CaseInsensitiveFindInReadable(aRightString, start, end); 1.243 + else 1.244 + match = FindInReadable(aRightString, start, end); 1.245 + break; 1.246 + } 1.247 + 1.248 + default: 1.249 + break; 1.250 + } 1.251 + } 1.252 + 1.253 + if (mNegate) match = !match; 1.254 + 1.255 + return match; 1.256 +} 1.257 + 1.258 +nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode, 1.259 + nsIContent* aAction, 1.260 + nsTemplateQuerySet* aQuerySet) 1.261 + : mQuerySet(aQuerySet), 1.262 + mAction(aAction), 1.263 + mBindings(nullptr), 1.264 + mConditions(nullptr) 1.265 +{ 1.266 + MOZ_COUNT_CTOR(nsTemplateRule); 1.267 + mRuleNode = do_QueryInterface(aRuleNode); 1.268 +} 1.269 + 1.270 +nsTemplateRule::nsTemplateRule(const nsTemplateRule& aOtherRule) 1.271 + : mQuerySet(aOtherRule.mQuerySet), 1.272 + mRuleNode(aOtherRule.mRuleNode), 1.273 + mAction(aOtherRule.mAction), 1.274 + mBindings(nullptr), 1.275 + mConditions(nullptr) 1.276 +{ 1.277 + MOZ_COUNT_CTOR(nsTemplateRule); 1.278 +} 1.279 + 1.280 +nsTemplateRule::~nsTemplateRule() 1.281 +{ 1.282 + MOZ_COUNT_DTOR(nsTemplateRule); 1.283 + 1.284 + while (mBindings) { 1.285 + Binding* doomed = mBindings; 1.286 + mBindings = mBindings->mNext; 1.287 + delete doomed; 1.288 + } 1.289 + 1.290 + while (mConditions) { 1.291 + nsTemplateCondition* cdel = mConditions; 1.292 + mConditions = mConditions->GetNext(); 1.293 + delete cdel; 1.294 + } 1.295 +} 1.296 + 1.297 +nsresult 1.298 +nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const 1.299 +{ 1.300 + *aRuleNode = mRuleNode; 1.301 + NS_IF_ADDREF(*aRuleNode); 1.302 + return NS_OK; 1.303 +} 1.304 + 1.305 +void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition) 1.306 +{ 1.307 + while (mConditions) { 1.308 + nsTemplateCondition* cdel = mConditions; 1.309 + mConditions = mConditions->GetNext(); 1.310 + delete cdel; 1.311 + } 1.312 + 1.313 + mConditions = aCondition; 1.314 +} 1.315 + 1.316 +bool 1.317 +nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const 1.318 +{ 1.319 + // check the conditions in the rule first 1.320 + nsTemplateCondition* condition = mConditions; 1.321 + while (condition) { 1.322 + if (!condition->CheckMatch(aResult)) 1.323 + return false; 1.324 + 1.325 + condition = condition->GetNext(); 1.326 + } 1.327 + 1.328 + if (mRuleFilter) { 1.329 + // if a rule filter was set, check it for a match. If an error occurs, 1.330 + // assume that the match was acceptable 1.331 + bool match; 1.332 + nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match); 1.333 + return NS_FAILED(rv) || match; 1.334 + } 1.335 + 1.336 + return true; 1.337 +} 1.338 + 1.339 +bool 1.340 +nsTemplateRule::HasBinding(nsIAtom* aSourceVariable, 1.341 + nsAString& aExpr, 1.342 + nsIAtom* aTargetVariable) const 1.343 +{ 1.344 + for (Binding* binding = mBindings; binding != nullptr; binding = binding->mNext) { 1.345 + if ((binding->mSourceVariable == aSourceVariable) && 1.346 + (binding->mExpr.Equals(aExpr)) && 1.347 + (binding->mTargetVariable == aTargetVariable)) 1.348 + return true; 1.349 + } 1.350 + 1.351 + return false; 1.352 +} 1.353 + 1.354 +nsresult 1.355 +nsTemplateRule::AddBinding(nsIAtom* aSourceVariable, 1.356 + nsAString& aExpr, 1.357 + nsIAtom* aTargetVariable) 1.358 +{ 1.359 + NS_PRECONDITION(aSourceVariable != 0, "no source variable!"); 1.360 + if (! aSourceVariable) 1.361 + return NS_ERROR_INVALID_ARG; 1.362 + 1.363 + NS_PRECONDITION(aTargetVariable != 0, "no target variable!"); 1.364 + if (! aTargetVariable) 1.365 + return NS_ERROR_INVALID_ARG; 1.366 + 1.367 + NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable), 1.368 + "binding added twice"); 1.369 + 1.370 + Binding* newbinding = new Binding; 1.371 + if (! newbinding) 1.372 + return NS_ERROR_OUT_OF_MEMORY; 1.373 + 1.374 + newbinding->mSourceVariable = aSourceVariable; 1.375 + newbinding->mTargetVariable = aTargetVariable; 1.376 + newbinding->mParent = nullptr; 1.377 + 1.378 + newbinding->mExpr.Assign(aExpr); 1.379 + 1.380 + Binding* binding = mBindings; 1.381 + Binding** link = &mBindings; 1.382 + 1.383 + // Insert it at the end, unless we detect that an existing 1.384 + // binding's source is dependent on the newbinding's target. 1.385 + // 1.386 + // XXXwaterson this isn't enough to make sure that we get all of 1.387 + // the dependencies worked out right, but it'll do for now. For 1.388 + // example, if you have (ab, bc, cd), and insert them in the order 1.389 + // (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the 1.390 + // person uses a natural ordering when writing the XUL, it'll all 1.391 + // work out ok. 1.392 + while (binding) { 1.393 + if (binding->mSourceVariable == newbinding->mTargetVariable) { 1.394 + binding->mParent = newbinding; 1.395 + break; 1.396 + } 1.397 + else if (binding->mTargetVariable == newbinding->mSourceVariable) { 1.398 + newbinding->mParent = binding; 1.399 + } 1.400 + 1.401 + link = &binding->mNext; 1.402 + binding = binding->mNext; 1.403 + } 1.404 + 1.405 + // Insert the newbinding 1.406 + *link = newbinding; 1.407 + newbinding->mNext = binding; 1.408 + return NS_OK; 1.409 +} 1.410 + 1.411 +nsresult 1.412 +nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor) 1.413 +{ 1.414 + Binding* binding = mBindings; 1.415 + 1.416 + while (binding) { 1.417 + nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable, 1.418 + binding->mSourceVariable, binding->mExpr); 1.419 + if (NS_FAILED(rv)) return rv; 1.420 + 1.421 + binding = binding->mNext; 1.422 + } 1.423 + 1.424 + return NS_OK; 1.425 +}