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.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 5 | * |
michael@0 | 6 | * This Original Code has been modified by IBM Corporation. |
michael@0 | 7 | * Modifications made by IBM described herein are |
michael@0 | 8 | * Copyright (c) International Business Machines |
michael@0 | 9 | * Corporation, 2000 |
michael@0 | 10 | * |
michael@0 | 11 | * Modifications to Mozilla code or documentation |
michael@0 | 12 | * identified per MPL Section 3.3 |
michael@0 | 13 | * |
michael@0 | 14 | * Date Modified by Description of modification |
michael@0 | 15 | * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink |
michael@0 | 16 | * use in OS2 |
michael@0 | 17 | */ |
michael@0 | 18 | |
michael@0 | 19 | /* |
michael@0 | 20 | This file provides the implementation for the sort service manager. |
michael@0 | 21 | */ |
michael@0 | 22 | |
michael@0 | 23 | #include "nsCOMPtr.h" |
michael@0 | 24 | #include "nsIContent.h" |
michael@0 | 25 | #include "nsIDOMElement.h" |
michael@0 | 26 | #include "nsIDOMNode.h" |
michael@0 | 27 | #include "nsIServiceManager.h" |
michael@0 | 28 | #include "nsGkAtoms.h" |
michael@0 | 29 | #include "nsNameSpaceManager.h" |
michael@0 | 30 | #include "nsXULContentUtils.h" |
michael@0 | 31 | #include "nsString.h" |
michael@0 | 32 | #include "nsQuickSort.h" |
michael@0 | 33 | #include "nsWhitespaceTokenizer.h" |
michael@0 | 34 | #include "nsXULSortService.h" |
michael@0 | 35 | #include "nsIDOMXULElement.h" |
michael@0 | 36 | #include "nsIXULTemplateBuilder.h" |
michael@0 | 37 | #include "nsTemplateMatch.h" |
michael@0 | 38 | #include "nsICollation.h" |
michael@0 | 39 | #include "nsUnicharUtils.h" |
michael@0 | 40 | |
michael@0 | 41 | NS_IMPL_ISUPPORTS(XULSortServiceImpl, nsIXULSortService) |
michael@0 | 42 | |
michael@0 | 43 | void |
michael@0 | 44 | XULSortServiceImpl::SetSortHints(nsIContent *aNode, nsSortState* aSortState) |
michael@0 | 45 | { |
michael@0 | 46 | // set sort and sortDirection attributes when is sort is done |
michael@0 | 47 | aNode->SetAttr(kNameSpaceID_None, nsGkAtoms::sort, |
michael@0 | 48 | aSortState->sort, true); |
michael@0 | 49 | |
michael@0 | 50 | nsAutoString direction; |
michael@0 | 51 | if (aSortState->direction == nsSortState_descending) |
michael@0 | 52 | direction.AssignLiteral("descending"); |
michael@0 | 53 | else if (aSortState->direction == nsSortState_ascending) |
michael@0 | 54 | direction.AssignLiteral("ascending"); |
michael@0 | 55 | aNode->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, |
michael@0 | 56 | direction, true); |
michael@0 | 57 | |
michael@0 | 58 | // for trees, also set the sort info on the currently sorted column |
michael@0 | 59 | if (aNode->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { |
michael@0 | 60 | if (aSortState->sortKeys.Count() >= 1) { |
michael@0 | 61 | nsAutoString sortkey; |
michael@0 | 62 | aSortState->sortKeys[0]->ToString(sortkey); |
michael@0 | 63 | SetSortColumnHints(aNode, sortkey, direction); |
michael@0 | 64 | } |
michael@0 | 65 | } |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | void |
michael@0 | 69 | XULSortServiceImpl::SetSortColumnHints(nsIContent *content, |
michael@0 | 70 | const nsAString &sortResource, |
michael@0 | 71 | const nsAString &sortDirection) |
michael@0 | 72 | { |
michael@0 | 73 | // set sort info on current column. This ensures that the |
michael@0 | 74 | // column header sort indicator is updated properly. |
michael@0 | 75 | for (nsIContent* child = content->GetFirstChild(); |
michael@0 | 76 | child; |
michael@0 | 77 | child = child->GetNextSibling()) { |
michael@0 | 78 | if (child->IsXUL()) { |
michael@0 | 79 | nsIAtom *tag = child->Tag(); |
michael@0 | 80 | |
michael@0 | 81 | if (tag == nsGkAtoms::treecols) { |
michael@0 | 82 | SetSortColumnHints(child, sortResource, sortDirection); |
michael@0 | 83 | } else if (tag == nsGkAtoms::treecol) { |
michael@0 | 84 | nsAutoString value; |
michael@0 | 85 | child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, value); |
michael@0 | 86 | // also check the resource attribute for older code |
michael@0 | 87 | if (value.IsEmpty()) |
michael@0 | 88 | child->GetAttr(kNameSpaceID_None, nsGkAtoms::resource, value); |
michael@0 | 89 | if (value == sortResource) { |
michael@0 | 90 | child->SetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, |
michael@0 | 91 | NS_LITERAL_STRING("true"), true); |
michael@0 | 92 | child->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, |
michael@0 | 93 | sortDirection, true); |
michael@0 | 94 | // Note: don't break out of loop; want to set/unset |
michael@0 | 95 | // attribs on ALL sort columns |
michael@0 | 96 | } else if (!value.IsEmpty()) { |
michael@0 | 97 | child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, |
michael@0 | 98 | true); |
michael@0 | 99 | child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, |
michael@0 | 100 | true); |
michael@0 | 101 | } |
michael@0 | 102 | } |
michael@0 | 103 | } |
michael@0 | 104 | } |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | nsresult |
michael@0 | 108 | XULSortServiceImpl::GetItemsToSort(nsIContent *aContainer, |
michael@0 | 109 | nsSortState* aSortState, |
michael@0 | 110 | nsTArray<contentSortInfo>& aSortItems) |
michael@0 | 111 | { |
michael@0 | 112 | // if there is a template attached to the sort node, use the builder to get |
michael@0 | 113 | // the items to be sorted |
michael@0 | 114 | nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(aContainer); |
michael@0 | 115 | if (element) { |
michael@0 | 116 | nsCOMPtr<nsIXULTemplateBuilder> builder; |
michael@0 | 117 | element->GetBuilder(getter_AddRefs(builder)); |
michael@0 | 118 | |
michael@0 | 119 | if (builder) { |
michael@0 | 120 | nsresult rv = builder->GetQueryProcessor(getter_AddRefs(aSortState->processor)); |
michael@0 | 121 | if (NS_FAILED(rv) || !aSortState->processor) |
michael@0 | 122 | return rv; |
michael@0 | 123 | |
michael@0 | 124 | return GetTemplateItemsToSort(aContainer, builder, aSortState, aSortItems); |
michael@0 | 125 | } |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | // if there is no template builder, just get the children. For trees, |
michael@0 | 129 | // get the treechildren element as use that as the parent |
michael@0 | 130 | nsCOMPtr<nsIContent> treechildren; |
michael@0 | 131 | if (aContainer->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { |
michael@0 | 132 | nsXULContentUtils::FindChildByTag(aContainer, |
michael@0 | 133 | kNameSpaceID_XUL, |
michael@0 | 134 | nsGkAtoms::treechildren, |
michael@0 | 135 | getter_AddRefs(treechildren)); |
michael@0 | 136 | if (!treechildren) |
michael@0 | 137 | return NS_OK; |
michael@0 | 138 | |
michael@0 | 139 | aContainer = treechildren; |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | for (nsIContent* child = aContainer->GetFirstChild(); |
michael@0 | 143 | child; |
michael@0 | 144 | child = child->GetNextSibling()) { |
michael@0 | 145 | contentSortInfo* cinfo = aSortItems.AppendElement(); |
michael@0 | 146 | if (!cinfo) |
michael@0 | 147 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 148 | |
michael@0 | 149 | cinfo->content = child; |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | return NS_OK; |
michael@0 | 153 | } |
michael@0 | 154 | |
michael@0 | 155 | |
michael@0 | 156 | nsresult |
michael@0 | 157 | XULSortServiceImpl::GetTemplateItemsToSort(nsIContent* aContainer, |
michael@0 | 158 | nsIXULTemplateBuilder* aBuilder, |
michael@0 | 159 | nsSortState* aSortState, |
michael@0 | 160 | nsTArray<contentSortInfo>& aSortItems) |
michael@0 | 161 | { |
michael@0 | 162 | for (nsIContent* child = aContainer->GetFirstChild(); |
michael@0 | 163 | child; |
michael@0 | 164 | child = child->GetNextSibling()) { |
michael@0 | 165 | |
michael@0 | 166 | nsCOMPtr<nsIDOMElement> childnode = do_QueryInterface(child); |
michael@0 | 167 | |
michael@0 | 168 | nsCOMPtr<nsIXULTemplateResult> result; |
michael@0 | 169 | nsresult rv = aBuilder->GetResultForContent(childnode, getter_AddRefs(result)); |
michael@0 | 170 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 171 | |
michael@0 | 172 | if (result) { |
michael@0 | 173 | contentSortInfo* cinfo = aSortItems.AppendElement(); |
michael@0 | 174 | if (!cinfo) |
michael@0 | 175 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 176 | |
michael@0 | 177 | cinfo->content = child; |
michael@0 | 178 | cinfo->result = result; |
michael@0 | 179 | } |
michael@0 | 180 | else if (aContainer->Tag() != nsGkAtoms::_template) { |
michael@0 | 181 | rv = GetTemplateItemsToSort(child, aBuilder, aSortState, aSortItems); |
michael@0 | 182 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 183 | } |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | return NS_OK; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | int |
michael@0 | 190 | testSortCallback(const void *data1, const void *data2, void *privateData) |
michael@0 | 191 | { |
michael@0 | 192 | /// Note: testSortCallback is a small C callback stub for NS_QuickSort |
michael@0 | 193 | contentSortInfo *left = (contentSortInfo *)data1; |
michael@0 | 194 | contentSortInfo *right = (contentSortInfo *)data2; |
michael@0 | 195 | nsSortState* sortState = (nsSortState *)privateData; |
michael@0 | 196 | |
michael@0 | 197 | int32_t sortOrder = 0; |
michael@0 | 198 | |
michael@0 | 199 | if (sortState->direction == nsSortState_natural && sortState->processor) { |
michael@0 | 200 | // sort in natural order |
michael@0 | 201 | sortState->processor->CompareResults(left->result, right->result, |
michael@0 | 202 | nullptr, sortState->sortHints, &sortOrder); |
michael@0 | 203 | } |
michael@0 | 204 | else { |
michael@0 | 205 | int32_t length = sortState->sortKeys.Count(); |
michael@0 | 206 | for (int32_t t = 0; t < length; t++) { |
michael@0 | 207 | // for templates, use the query processor to do sorting |
michael@0 | 208 | if (sortState->processor) { |
michael@0 | 209 | sortState->processor->CompareResults(left->result, right->result, |
michael@0 | 210 | sortState->sortKeys[t], |
michael@0 | 211 | sortState->sortHints, &sortOrder); |
michael@0 | 212 | if (sortOrder) |
michael@0 | 213 | break; |
michael@0 | 214 | } |
michael@0 | 215 | else { |
michael@0 | 216 | // no template, so just compare attributes. Ignore namespaces for now. |
michael@0 | 217 | nsAutoString leftstr, rightstr; |
michael@0 | 218 | left->content->GetAttr(kNameSpaceID_None, sortState->sortKeys[t], leftstr); |
michael@0 | 219 | right->content->GetAttr(kNameSpaceID_None, sortState->sortKeys[t], rightstr); |
michael@0 | 220 | |
michael@0 | 221 | sortOrder = XULSortServiceImpl::CompareValues(leftstr, rightstr, sortState->sortHints); |
michael@0 | 222 | } |
michael@0 | 223 | } |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | if (sortState->direction == nsSortState_descending) |
michael@0 | 227 | sortOrder = -sortOrder; |
michael@0 | 228 | |
michael@0 | 229 | return sortOrder; |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | nsresult |
michael@0 | 233 | XULSortServiceImpl::SortContainer(nsIContent *aContainer, nsSortState* aSortState) |
michael@0 | 234 | { |
michael@0 | 235 | nsTArray<contentSortInfo> items; |
michael@0 | 236 | nsresult rv = GetItemsToSort(aContainer, aSortState, items); |
michael@0 | 237 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 238 | |
michael@0 | 239 | uint32_t numResults = items.Length(); |
michael@0 | 240 | if (!numResults) |
michael@0 | 241 | return NS_OK; |
michael@0 | 242 | |
michael@0 | 243 | uint32_t i; |
michael@0 | 244 | |
michael@0 | 245 | // inbetweenSeparatorSort sorts the items between separators independently |
michael@0 | 246 | if (aSortState->inbetweenSeparatorSort) { |
michael@0 | 247 | uint32_t startIndex = 0; |
michael@0 | 248 | for (i = 0; i < numResults; i++) { |
michael@0 | 249 | if (i > startIndex + 1) { |
michael@0 | 250 | nsAutoString type; |
michael@0 | 251 | items[i].result->GetType(type); |
michael@0 | 252 | if (type.EqualsLiteral("separator")) { |
michael@0 | 253 | if (aSortState->invertSort) |
michael@0 | 254 | InvertSortInfo(items, startIndex, i - startIndex); |
michael@0 | 255 | else |
michael@0 | 256 | NS_QuickSort((void *)(items.Elements() + startIndex), i - startIndex, |
michael@0 | 257 | sizeof(contentSortInfo), testSortCallback, (void*)aSortState); |
michael@0 | 258 | |
michael@0 | 259 | startIndex = i + 1; |
michael@0 | 260 | } |
michael@0 | 261 | } |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | if (i > startIndex + 1) { |
michael@0 | 265 | if (aSortState->invertSort) |
michael@0 | 266 | InvertSortInfo(items, startIndex, i - startIndex); |
michael@0 | 267 | else |
michael@0 | 268 | NS_QuickSort((void *)(items.Elements() + startIndex), i - startIndex, |
michael@0 | 269 | sizeof(contentSortInfo), testSortCallback, (void*)aSortState); |
michael@0 | 270 | } |
michael@0 | 271 | } else { |
michael@0 | 272 | // if the items are just being inverted, that is, just switching between |
michael@0 | 273 | // ascending and descending, just reverse the list. |
michael@0 | 274 | if (aSortState->invertSort) |
michael@0 | 275 | InvertSortInfo(items, 0, numResults); |
michael@0 | 276 | else |
michael@0 | 277 | NS_QuickSort((void *)items.Elements(), numResults, |
michael@0 | 278 | sizeof(contentSortInfo), testSortCallback, (void*)aSortState); |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | // first remove the items from the old positions |
michael@0 | 282 | for (i = 0; i < numResults; i++) { |
michael@0 | 283 | nsIContent* child = items[i].content; |
michael@0 | 284 | nsIContent* parent = child->GetParent(); |
michael@0 | 285 | |
michael@0 | 286 | if (parent) { |
michael@0 | 287 | // remember the parent so that it can be reinserted back |
michael@0 | 288 | // into the same parent. This is necessary as multiple rules |
michael@0 | 289 | // may generate results which get placed in different locations. |
michael@0 | 290 | items[i].parent = parent; |
michael@0 | 291 | int32_t index = parent->IndexOf(child); |
michael@0 | 292 | parent->RemoveChildAt(index, true); |
michael@0 | 293 | } |
michael@0 | 294 | } |
michael@0 | 295 | |
michael@0 | 296 | // now add the items back in sorted order |
michael@0 | 297 | for (i = 0; i < numResults; i++) |
michael@0 | 298 | { |
michael@0 | 299 | nsIContent* child = items[i].content; |
michael@0 | 300 | nsIContent* parent = items[i].parent; |
michael@0 | 301 | if (parent) { |
michael@0 | 302 | parent->AppendChildTo(child, true); |
michael@0 | 303 | |
michael@0 | 304 | // if it's a container in a tree or menu, find its children, |
michael@0 | 305 | // and sort those also |
michael@0 | 306 | if (!child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container, |
michael@0 | 307 | nsGkAtoms::_true, eCaseMatters)) |
michael@0 | 308 | continue; |
michael@0 | 309 | |
michael@0 | 310 | for (nsIContent* grandchild = child->GetFirstChild(); |
michael@0 | 311 | grandchild; |
michael@0 | 312 | grandchild = grandchild->GetNextSibling()) { |
michael@0 | 313 | nsINodeInfo *ni = grandchild->NodeInfo(); |
michael@0 | 314 | nsIAtom *localName = ni->NameAtom(); |
michael@0 | 315 | if (ni->NamespaceID() == kNameSpaceID_XUL && |
michael@0 | 316 | (localName == nsGkAtoms::treechildren || |
michael@0 | 317 | localName == nsGkAtoms::menupopup)) { |
michael@0 | 318 | SortContainer(grandchild, aSortState); |
michael@0 | 319 | } |
michael@0 | 320 | } |
michael@0 | 321 | } |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | return NS_OK; |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | nsresult |
michael@0 | 328 | XULSortServiceImpl::InvertSortInfo(nsTArray<contentSortInfo>& aData, |
michael@0 | 329 | int32_t aStart, int32_t aNumItems) |
michael@0 | 330 | { |
michael@0 | 331 | if (aNumItems > 1) { |
michael@0 | 332 | // reverse the items in the array starting from aStart |
michael@0 | 333 | int32_t upPoint = (aNumItems + 1) / 2 + aStart; |
michael@0 | 334 | int32_t downPoint = (aNumItems - 2) / 2 + aStart; |
michael@0 | 335 | int32_t half = aNumItems / 2; |
michael@0 | 336 | while (half-- > 0) { |
michael@0 | 337 | aData[downPoint--].swap(aData[upPoint++]); |
michael@0 | 338 | } |
michael@0 | 339 | } |
michael@0 | 340 | return NS_OK; |
michael@0 | 341 | } |
michael@0 | 342 | |
michael@0 | 343 | nsresult |
michael@0 | 344 | XULSortServiceImpl::InitializeSortState(nsIContent* aRootElement, |
michael@0 | 345 | nsIContent* aContainer, |
michael@0 | 346 | const nsAString& aSortKey, |
michael@0 | 347 | const nsAString& aSortHints, |
michael@0 | 348 | nsSortState* aSortState) |
michael@0 | 349 | { |
michael@0 | 350 | // used as an optimization for the content builder |
michael@0 | 351 | if (aContainer != aSortState->lastContainer.get()) { |
michael@0 | 352 | aSortState->lastContainer = aContainer; |
michael@0 | 353 | aSortState->lastWasFirst = false; |
michael@0 | 354 | aSortState->lastWasLast = false; |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | // The attributes allowed are either: |
michael@0 | 358 | // sort="key1 key2 ..." |
michael@0 | 359 | // or sortResource="key1" sortResource2="key2" |
michael@0 | 360 | // The latter is for backwards compatibility, and is equivalent to concatenating |
michael@0 | 361 | // both values in the sort attribute |
michael@0 | 362 | nsAutoString sort(aSortKey); |
michael@0 | 363 | aSortState->sortKeys.Clear(); |
michael@0 | 364 | if (sort.IsEmpty()) { |
michael@0 | 365 | nsAutoString sortResource, sortResource2; |
michael@0 | 366 | aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource, sortResource); |
michael@0 | 367 | if (!sortResource.IsEmpty()) { |
michael@0 | 368 | nsCOMPtr<nsIAtom> sortkeyatom = do_GetAtom(sortResource); |
michael@0 | 369 | aSortState->sortKeys.AppendObject(sortkeyatom); |
michael@0 | 370 | sort.Append(sortResource); |
michael@0 | 371 | |
michael@0 | 372 | aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource2, sortResource2); |
michael@0 | 373 | if (!sortResource2.IsEmpty()) { |
michael@0 | 374 | nsCOMPtr<nsIAtom> sortkeyatom2 = do_GetAtom(sortResource2); |
michael@0 | 375 | aSortState->sortKeys.AppendObject(sortkeyatom2); |
michael@0 | 376 | sort.AppendLiteral(" "); |
michael@0 | 377 | sort.Append(sortResource2); |
michael@0 | 378 | } |
michael@0 | 379 | } |
michael@0 | 380 | } |
michael@0 | 381 | else { |
michael@0 | 382 | nsWhitespaceTokenizer tokenizer(sort); |
michael@0 | 383 | while (tokenizer.hasMoreTokens()) { |
michael@0 | 384 | nsCOMPtr<nsIAtom> keyatom = do_GetAtom(tokenizer.nextToken()); |
michael@0 | 385 | NS_ENSURE_TRUE(keyatom, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 386 | aSortState->sortKeys.AppendObject(keyatom); |
michael@0 | 387 | } |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | aSortState->sort.Assign(sort); |
michael@0 | 391 | aSortState->direction = nsSortState_natural; |
michael@0 | 392 | |
michael@0 | 393 | bool noNaturalState = false; |
michael@0 | 394 | nsWhitespaceTokenizer tokenizer(aSortHints); |
michael@0 | 395 | while (tokenizer.hasMoreTokens()) { |
michael@0 | 396 | const nsDependentSubstring& token(tokenizer.nextToken()); |
michael@0 | 397 | if (token.EqualsLiteral("comparecase")) |
michael@0 | 398 | aSortState->sortHints |= nsIXULSortService::SORT_COMPARECASE; |
michael@0 | 399 | else if (token.EqualsLiteral("integer")) |
michael@0 | 400 | aSortState->sortHints |= nsIXULSortService::SORT_INTEGER; |
michael@0 | 401 | else if (token.EqualsLiteral("descending")) |
michael@0 | 402 | aSortState->direction = nsSortState_descending; |
michael@0 | 403 | else if (token.EqualsLiteral("ascending")) |
michael@0 | 404 | aSortState->direction = nsSortState_ascending; |
michael@0 | 405 | else if (token.EqualsLiteral("twostate")) |
michael@0 | 406 | noNaturalState = true; |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | // if the twostate flag was set, the natural order is skipped and only |
michael@0 | 410 | // ascending and descending are allowed |
michael@0 | 411 | if (aSortState->direction == nsSortState_natural && noNaturalState) { |
michael@0 | 412 | aSortState->direction = nsSortState_ascending; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | // set up sort order info |
michael@0 | 416 | aSortState->invertSort = false; |
michael@0 | 417 | |
michael@0 | 418 | nsAutoString existingsort; |
michael@0 | 419 | aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, existingsort); |
michael@0 | 420 | nsAutoString existingsortDirection; |
michael@0 | 421 | aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, existingsortDirection); |
michael@0 | 422 | |
michael@0 | 423 | // if just switching direction, set the invertSort flag |
michael@0 | 424 | if (sort.Equals(existingsort)) { |
michael@0 | 425 | if (aSortState->direction == nsSortState_descending) { |
michael@0 | 426 | if (existingsortDirection.EqualsLiteral("ascending")) |
michael@0 | 427 | aSortState->invertSort = true; |
michael@0 | 428 | } |
michael@0 | 429 | else if (aSortState->direction == nsSortState_ascending && |
michael@0 | 430 | existingsortDirection.EqualsLiteral("descending")) { |
michael@0 | 431 | aSortState->invertSort = true; |
michael@0 | 432 | } |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | // sort items between separators independently |
michael@0 | 436 | aSortState->inbetweenSeparatorSort = |
michael@0 | 437 | aRootElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortSeparators, |
michael@0 | 438 | nsGkAtoms::_true, eCaseMatters); |
michael@0 | 439 | |
michael@0 | 440 | // sort static content (non template generated nodes) after generated content |
michael@0 | 441 | aSortState->sortStaticsLast = aRootElement->AttrValueIs(kNameSpaceID_None, |
michael@0 | 442 | nsGkAtoms::sortStaticsLast, |
michael@0 | 443 | nsGkAtoms::_true, eCaseMatters); |
michael@0 | 444 | |
michael@0 | 445 | aSortState->initialized = true; |
michael@0 | 446 | |
michael@0 | 447 | return NS_OK; |
michael@0 | 448 | } |
michael@0 | 449 | |
michael@0 | 450 | int32_t |
michael@0 | 451 | XULSortServiceImpl::CompareValues(const nsAString& aLeft, |
michael@0 | 452 | const nsAString& aRight, |
michael@0 | 453 | uint32_t aSortHints) |
michael@0 | 454 | { |
michael@0 | 455 | if (aSortHints & SORT_INTEGER) { |
michael@0 | 456 | nsresult err; |
michael@0 | 457 | int32_t leftint = PromiseFlatString(aLeft).ToInteger(&err); |
michael@0 | 458 | if (NS_SUCCEEDED(err)) { |
michael@0 | 459 | int32_t rightint = PromiseFlatString(aRight).ToInteger(&err); |
michael@0 | 460 | if (NS_SUCCEEDED(err)) { |
michael@0 | 461 | return leftint - rightint; |
michael@0 | 462 | } |
michael@0 | 463 | } |
michael@0 | 464 | // if they aren't integers, just fall through and compare strings |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | if (aSortHints & SORT_COMPARECASE) { |
michael@0 | 468 | return ::Compare(aLeft, aRight); |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | nsICollation* collation = nsXULContentUtils::GetCollation(); |
michael@0 | 472 | if (collation) { |
michael@0 | 473 | int32_t result; |
michael@0 | 474 | collation->CompareString(nsICollation::kCollationCaseInSensitive, |
michael@0 | 475 | aLeft, aRight, &result); |
michael@0 | 476 | return result; |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | return ::Compare(aLeft, aRight, nsCaseInsensitiveStringComparator()); |
michael@0 | 480 | } |
michael@0 | 481 | |
michael@0 | 482 | NS_IMETHODIMP |
michael@0 | 483 | XULSortServiceImpl::Sort(nsIDOMNode* aNode, |
michael@0 | 484 | const nsAString& aSortKey, |
michael@0 | 485 | const nsAString& aSortHints) |
michael@0 | 486 | { |
michael@0 | 487 | // get root content node |
michael@0 | 488 | nsCOMPtr<nsIContent> sortNode = do_QueryInterface(aNode); |
michael@0 | 489 | if (!sortNode) |
michael@0 | 490 | return NS_ERROR_FAILURE; |
michael@0 | 491 | |
michael@0 | 492 | nsSortState sortState; |
michael@0 | 493 | nsresult rv = InitializeSortState(sortNode, sortNode, |
michael@0 | 494 | aSortKey, aSortHints, &sortState); |
michael@0 | 495 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 496 | |
michael@0 | 497 | // store sort info in attributes on content |
michael@0 | 498 | SetSortHints(sortNode, &sortState); |
michael@0 | 499 | rv = SortContainer(sortNode, &sortState); |
michael@0 | 500 | |
michael@0 | 501 | sortState.processor = nullptr; // don't hang on to this reference |
michael@0 | 502 | return rv; |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | nsresult |
michael@0 | 506 | NS_NewXULSortService(nsIXULSortService** sortService) |
michael@0 | 507 | { |
michael@0 | 508 | *sortService = new XULSortServiceImpl(); |
michael@0 | 509 | if (!*sortService) |
michael@0 | 510 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 511 | |
michael@0 | 512 | NS_ADDREF(*sortService); |
michael@0 | 513 | return NS_OK; |
michael@0 | 514 | } |