diff options
Diffstat (limited to 'dom/xul/templates/nsTemplateRule.cpp')
-rw-r--r-- | dom/xul/templates/nsTemplateRule.cpp | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/dom/xul/templates/nsTemplateRule.cpp b/dom/xul/templates/nsTemplateRule.cpp new file mode 100644 index 000000000..6d82740e3 --- /dev/null +++ b/dom/xul/templates/nsTemplateRule.cpp @@ -0,0 +1,422 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsTemplateRule.h" +#include "nsTemplateMatch.h" +#include "nsXULContentUtils.h" +#include "nsUnicharUtils.h" +#include "nsReadableUtils.h" +#include "nsICollation.h" + +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, + const nsAString& aRelation, + nsIAtom* aTargetVariable, + bool aIgnoreCase, + bool aNegate) + : mSourceVariable(aSourceVariable), + mTargetVariable(aTargetVariable), + mIgnoreCase(aIgnoreCase), + mNegate(aNegate), + mNext(nullptr) +{ + SetRelation(aRelation); + + MOZ_COUNT_CTOR(nsTemplateCondition); +} + +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, + const nsAString& aRelation, + const nsAString& aTargets, + bool aIgnoreCase, + bool aNegate, + bool aIsMultiple) + : mSourceVariable(aSourceVariable), + mIgnoreCase(aIgnoreCase), + mNegate(aNegate), + mNext(nullptr) +{ + SetRelation(aRelation); + + if (aIsMultiple) { + int32_t start = 0, end = 0; + while ((end = aTargets.FindChar(',',start)) >= 0) { + if (end > start) { + mTargetList.AppendElement(Substring(aTargets, start, end - start)); + } + start = end + 1; + } + if (start < int32_t(aTargets.Length())) { + mTargetList.AppendElement(Substring(aTargets, start)); + } + } + else { + mTargetList.AppendElement(aTargets); + } + + MOZ_COUNT_CTOR(nsTemplateCondition); +} + +nsTemplateCondition::nsTemplateCondition(const nsAString& aSource, + const nsAString& aRelation, + nsIAtom* aTargetVariable, + bool aIgnoreCase, + bool aNegate) + : mSource(aSource), + mTargetVariable(aTargetVariable), + mIgnoreCase(aIgnoreCase), + mNegate(aNegate), + mNext(nullptr) +{ + SetRelation(aRelation); + + MOZ_COUNT_CTOR(nsTemplateCondition); +} + +void +nsTemplateCondition::SetRelation(const nsAString& aRelation) +{ + if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty()) + mRelation = eEquals; + else if (aRelation.EqualsLiteral("less")) + mRelation = eLess; + else if (aRelation.EqualsLiteral("greater")) + mRelation = eGreater; + else if (aRelation.EqualsLiteral("before")) + mRelation = eBefore; + else if (aRelation.EqualsLiteral("after")) + mRelation = eAfter; + else if (aRelation.EqualsLiteral("startswith")) + mRelation = eStartswith; + else if (aRelation.EqualsLiteral("endswith")) + mRelation = eEndswith; + else if (aRelation.EqualsLiteral("contains")) + mRelation = eContains; + else + mRelation = eUnknown; +} + +bool +nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult) +{ + bool match = false; + + nsAutoString leftString; + if (mSourceVariable) + aResult->GetBindingFor(mSourceVariable, leftString); + else + leftString.Assign(mSource); + + if (mTargetVariable) { + nsAutoString rightString; + aResult->GetBindingFor(mTargetVariable, rightString); + + match = CheckMatchStrings(leftString, rightString); + } + else { + // iterate over the strings in the target and determine + // whether there is a match. + uint32_t length = mTargetList.Length(); + for (uint32_t t = 0; t < length; t++) { + match = CheckMatchStrings(leftString, mTargetList[t]); + + // stop once a match is found. In negate mode, stop once a + // target does not match. + if (match != mNegate) break; + } + } + + return match; +} + + +bool +nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString, + const nsAString& aRightString) +{ + bool match = false; + + if (aRightString.IsEmpty()) { + if ((mRelation == eEquals) && aLeftString.IsEmpty()) + match = true; + } + else { + switch (mRelation) { + case eEquals: + if (mIgnoreCase) + match = aLeftString.Equals(aRightString, + nsCaseInsensitiveStringComparator()); + else + match = aLeftString.Equals(aRightString); + break; + + case eLess: + case eGreater: + { + // non-numbers always compare false + nsresult err; + int32_t leftint = PromiseFlatString(aLeftString).ToInteger(&err); + if (NS_SUCCEEDED(err)) { + int32_t rightint = PromiseFlatString(aRightString).ToInteger(&err); + if (NS_SUCCEEDED(err)) { + match = (mRelation == eLess) ? (leftint < rightint) : + (leftint > rightint); + } + } + + break; + } + + case eBefore: + { + nsICollation* collation = nsXULContentUtils::GetCollation(); + if (collation) { + int32_t sortOrder; + collation->CompareString((mIgnoreCase ? + static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : + static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), + aLeftString, + aRightString, + &sortOrder); + match = (sortOrder < 0); + } + else if (mIgnoreCase) { + match = (Compare(aLeftString, aRightString, + nsCaseInsensitiveStringComparator()) < 0); + } + else { + match = (Compare(aLeftString, aRightString) < 0); + } + break; + } + + case eAfter: + { + nsICollation* collation = nsXULContentUtils::GetCollation(); + if (collation) { + int32_t sortOrder; + collation->CompareString((mIgnoreCase ? + static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : + static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), + aLeftString, + aRightString, + &sortOrder); + match = (sortOrder > 0); + } + else if (mIgnoreCase) { + match = (Compare(aLeftString, aRightString, + nsCaseInsensitiveStringComparator()) > 0); + } + else { + match = (Compare(aLeftString, aRightString) > 0); + } + break; + } + + case eStartswith: + if (mIgnoreCase) + match = (StringBeginsWith(aLeftString, aRightString, + nsCaseInsensitiveStringComparator())); + else + match = (StringBeginsWith(aLeftString, aRightString)); + break; + + case eEndswith: + if (mIgnoreCase) + match = (StringEndsWith(aLeftString, aRightString, + nsCaseInsensitiveStringComparator())); + else + match = (StringEndsWith(aLeftString, aRightString)); + break; + + case eContains: + { + nsAString::const_iterator start, end; + aLeftString.BeginReading(start); + aLeftString.EndReading(end); + if (mIgnoreCase) + match = CaseInsensitiveFindInReadable(aRightString, start, end); + else + match = FindInReadable(aRightString, start, end); + break; + } + + default: + break; + } + } + + if (mNegate) match = !match; + + return match; +} + +nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode, + nsIContent* aAction, + nsTemplateQuerySet* aQuerySet) + : mQuerySet(aQuerySet), + mAction(aAction), + mBindings(nullptr), + mConditions(nullptr) +{ + MOZ_COUNT_CTOR(nsTemplateRule); + mRuleNode = do_QueryInterface(aRuleNode); +} + +nsTemplateRule::nsTemplateRule(const nsTemplateRule& aOtherRule) + : mQuerySet(aOtherRule.mQuerySet), + mRuleNode(aOtherRule.mRuleNode), + mAction(aOtherRule.mAction), + mBindings(nullptr), + mConditions(nullptr) +{ + MOZ_COUNT_CTOR(nsTemplateRule); +} + +nsTemplateRule::~nsTemplateRule() +{ + MOZ_COUNT_DTOR(nsTemplateRule); + + while (mBindings) { + Binding* doomed = mBindings; + mBindings = mBindings->mNext; + delete doomed; + } + + while (mConditions) { + nsTemplateCondition* cdel = mConditions; + mConditions = mConditions->GetNext(); + delete cdel; + } +} + +nsresult +nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const +{ + *aRuleNode = mRuleNode; + NS_IF_ADDREF(*aRuleNode); + return NS_OK; +} + +void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition) +{ + while (mConditions) { + nsTemplateCondition* cdel = mConditions; + mConditions = mConditions->GetNext(); + delete cdel; + } + + mConditions = aCondition; +} + +bool +nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const +{ + // check the conditions in the rule first + nsTemplateCondition* condition = mConditions; + while (condition) { + if (!condition->CheckMatch(aResult)) + return false; + + condition = condition->GetNext(); + } + + if (mRuleFilter) { + // if a rule filter was set, check it for a match. If an error occurs, + // assume that the match was acceptable + bool match; + nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match); + return NS_FAILED(rv) || match; + } + + return true; +} + +bool +nsTemplateRule::HasBinding(nsIAtom* aSourceVariable, + nsAString& aExpr, + nsIAtom* aTargetVariable) const +{ + for (Binding* binding = mBindings; binding != nullptr; binding = binding->mNext) { + if ((binding->mSourceVariable == aSourceVariable) && + (binding->mExpr.Equals(aExpr)) && + (binding->mTargetVariable == aTargetVariable)) + return true; + } + + return false; +} + +nsresult +nsTemplateRule::AddBinding(nsIAtom* aSourceVariable, + nsAString& aExpr, + nsIAtom* aTargetVariable) +{ + NS_PRECONDITION(aSourceVariable != 0, "no source variable!"); + if (! aSourceVariable) + return NS_ERROR_INVALID_ARG; + + NS_PRECONDITION(aTargetVariable != 0, "no target variable!"); + if (! aTargetVariable) + return NS_ERROR_INVALID_ARG; + + NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable), + "binding added twice"); + + Binding* newbinding = new Binding; + if (! newbinding) + return NS_ERROR_OUT_OF_MEMORY; + + newbinding->mSourceVariable = aSourceVariable; + newbinding->mTargetVariable = aTargetVariable; + newbinding->mParent = nullptr; + + newbinding->mExpr.Assign(aExpr); + + Binding* binding = mBindings; + Binding** link = &mBindings; + + // Insert it at the end, unless we detect that an existing + // binding's source is dependent on the newbinding's target. + // + // XXXwaterson this isn't enough to make sure that we get all of + // the dependencies worked out right, but it'll do for now. For + // example, if you have (ab, bc, cd), and insert them in the order + // (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the + // person uses a natural ordering when writing the XUL, it'll all + // work out ok. + while (binding) { + if (binding->mSourceVariable == newbinding->mTargetVariable) { + binding->mParent = newbinding; + break; + } + else if (binding->mTargetVariable == newbinding->mSourceVariable) { + newbinding->mParent = binding; + } + + link = &binding->mNext; + binding = binding->mNext; + } + + // Insert the newbinding + *link = newbinding; + newbinding->mNext = binding; + return NS_OK; +} + +nsresult +nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor) +{ + Binding* binding = mBindings; + + while (binding) { + nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable, + binding->mSourceVariable, binding->mExpr); + if (NS_FAILED(rv)) return rv; + + binding = binding->mNext; + } + + return NS_OK; +} |