nsTemplateRule.cpp 13 KB

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