nsTreeContentView.cpp (40146B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsTreeContentView.h" 8 9 #include "ChildIterator.h" 10 #include "mozilla/ErrorResult.h" 11 #include "mozilla/dom/Document.h" 12 #include "mozilla/dom/Element.h" 13 #include "mozilla/dom/TreeContentViewBinding.h" 14 #include "mozilla/dom/XULTreeElement.h" 15 #include "nsError.h" 16 #include "nsGkAtoms.h" 17 #include "nsNameSpaceManager.h" 18 #include "nsServiceManagerUtils.h" 19 #include "nsTreeBodyFrame.h" 20 #include "nsTreeColumns.h" 21 #include "nsTreeUtils.h" 22 #include "nsXULSortService.h" 23 24 using namespace mozilla; 25 using namespace mozilla::dom; 26 27 // A content model view implementation for the tree. 28 29 #define ROW_FLAG_CONTAINER 0x01 30 #define ROW_FLAG_OPEN 0x02 31 #define ROW_FLAG_EMPTY 0x04 32 #define ROW_FLAG_SEPARATOR 0x08 33 34 class Row { 35 public: 36 Row(Element* aContent, int32_t aParentIndex) 37 : mContent(aContent), 38 mParentIndex(aParentIndex), 39 mSubtreeSize(0), 40 mFlags(0) {} 41 42 ~Row() = default; 43 44 void SetContainer(bool aContainer) { 45 aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER; 46 } 47 bool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; } 48 49 void SetOpen(bool aOpen) { 50 aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN; 51 } 52 bool IsOpen() { return !!(mFlags & ROW_FLAG_OPEN); } 53 54 void SetEmpty(bool aEmpty) { 55 aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY; 56 } 57 bool IsEmpty() { return !!(mFlags & ROW_FLAG_EMPTY); } 58 59 void SetSeparator(bool aSeparator) { 60 aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR; 61 } 62 bool IsSeparator() { return !!(mFlags & ROW_FLAG_SEPARATOR); } 63 64 // Weak reference to a content item. 65 Element* mContent; 66 67 // The parent index of the item, set to -1 for the top level items. 68 int32_t mParentIndex; 69 70 // Subtree size for this item. 71 int32_t mSubtreeSize; 72 73 private: 74 // State flags 75 int8_t mFlags; 76 }; 77 78 // We don't reference count the reference to the document 79 // If the document goes away first, we'll be informed and we 80 // can drop our reference. 81 // If we go away first, we'll get rid of ourselves from the 82 // document's observer list. 83 84 nsTreeContentView::nsTreeContentView(void) 85 : mTree(nullptr), mSelection(nullptr), mDocument(nullptr) {} 86 87 nsTreeContentView::~nsTreeContentView(void) { 88 // Remove ourselves from mDocument's observers. 89 if (mDocument) { 90 mDocument->RemoveObserver(this); 91 } 92 } 93 94 nsresult NS_NewTreeContentView(nsITreeView** aResult) { 95 *aResult = new nsTreeContentView; 96 if (!*aResult) { 97 return NS_ERROR_OUT_OF_MEMORY; 98 } 99 NS_ADDREF(*aResult); 100 return NS_OK; 101 } 102 103 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsTreeContentView, mTree, mSelection, 104 mBody) 105 106 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeContentView) 107 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeContentView) 108 109 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeContentView) 110 NS_INTERFACE_MAP_ENTRY(nsITreeView) 111 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) 112 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 113 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITreeView) 114 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 115 NS_INTERFACE_MAP_END 116 117 JSObject* nsTreeContentView::WrapObject(JSContext* aCx, 118 JS::Handle<JSObject*> aGivenProto) { 119 return TreeContentView_Binding::Wrap(aCx, this, aGivenProto); 120 } 121 122 nsISupports* nsTreeContentView::GetParentObject() { return mTree; } 123 124 NS_IMETHODIMP 125 nsTreeContentView::GetRowCount(int32_t* aRowCount) { 126 *aRowCount = mRows.Length(); 127 128 return NS_OK; 129 } 130 131 NS_IMETHODIMP 132 nsTreeContentView::GetSelection(nsITreeSelection** aSelection) { 133 NS_IF_ADDREF(*aSelection = GetSelection()); 134 135 return NS_OK; 136 } 137 138 bool nsTreeContentView::CanTrustTreeSelection(nsISupports* aValue) { 139 // Untrusted content is only allowed to specify known-good views 140 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { 141 return true; 142 } 143 nsCOMPtr<nsINativeTreeSelection> nativeTreeSel = do_QueryInterface(aValue); 144 return nativeTreeSel && NS_SUCCEEDED(nativeTreeSel->EnsureNative()); 145 } 146 147 NS_IMETHODIMP 148 nsTreeContentView::SetSelection(nsITreeSelection* aSelection) { 149 ErrorResult rv; 150 SetSelection(aSelection, rv); 151 return rv.StealNSResult(); 152 } 153 154 void nsTreeContentView::SetSelection(nsITreeSelection* aSelection, 155 ErrorResult& aError) { 156 if (aSelection && !CanTrustTreeSelection(aSelection)) { 157 aError.ThrowSecurityError("Not allowed to set tree selection"); 158 return; 159 } 160 161 mSelection = aSelection; 162 } 163 164 void nsTreeContentView::GetRowProperties(int32_t aRow, nsAString& aProperties, 165 ErrorResult& aError) { 166 aProperties.Truncate(); 167 if (!IsValidRowIndex(aRow)) { 168 aError.Throw(NS_ERROR_INVALID_ARG); 169 return; 170 } 171 172 Row* row = mRows[aRow].get(); 173 nsIContent* realRow; 174 if (row->IsSeparator()) { 175 realRow = row->mContent; 176 } else { 177 realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 178 } 179 180 if (realRow && realRow->IsElement()) { 181 realRow->AsElement()->GetAttr(nsGkAtoms::properties, aProperties); 182 } 183 } 184 185 NS_IMETHODIMP 186 nsTreeContentView::GetRowProperties(int32_t aIndex, nsAString& aProps) { 187 ErrorResult rv; 188 GetRowProperties(aIndex, aProps, rv); 189 return rv.StealNSResult(); 190 } 191 192 void nsTreeContentView::GetCellProperties(int32_t aRow, nsTreeColumn& aColumn, 193 nsAString& aProperties, 194 ErrorResult& aError) { 195 if (!IsValidRowIndex(aRow)) { 196 aError.Throw(NS_ERROR_INVALID_ARG); 197 return; 198 } 199 200 Row* row = mRows[aRow].get(); 201 nsIContent* realRow = 202 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 203 if (realRow) { 204 Element* cell = GetCell(realRow, aColumn); 205 if (cell) { 206 cell->GetAttr(nsGkAtoms::properties, aProperties); 207 } 208 } 209 } 210 211 NS_IMETHODIMP 212 nsTreeContentView::GetCellProperties(int32_t aRow, nsTreeColumn* aCol, 213 nsAString& aProps) { 214 NS_ENSURE_ARG(aCol); 215 216 ErrorResult rv; 217 GetCellProperties(aRow, *aCol, aProps, rv); 218 return rv.StealNSResult(); 219 } 220 221 void nsTreeContentView::GetColumnProperties(nsTreeColumn& aColumn, 222 nsAString& aProperties) { 223 RefPtr<Element> element = aColumn.Element(); 224 225 if (element) { 226 element->GetAttr(nsGkAtoms::properties, aProperties); 227 } 228 } 229 230 NS_IMETHODIMP 231 nsTreeContentView::GetColumnProperties(nsTreeColumn* aCol, nsAString& aProps) { 232 NS_ENSURE_ARG(aCol); 233 234 GetColumnProperties(*aCol, aProps); 235 return NS_OK; 236 } 237 238 bool nsTreeContentView::IsContainer(int32_t aRow, ErrorResult& aError) { 239 if (!IsValidRowIndex(aRow)) { 240 aError.Throw(NS_ERROR_INVALID_ARG); 241 return false; 242 } 243 244 return mRows[aRow]->IsContainer(); 245 } 246 247 NS_IMETHODIMP 248 nsTreeContentView::IsContainer(int32_t aIndex, bool* _retval) { 249 ErrorResult rv; 250 *_retval = IsContainer(aIndex, rv); 251 return rv.StealNSResult(); 252 } 253 254 bool nsTreeContentView::IsContainerOpen(int32_t aRow, ErrorResult& aError) { 255 if (!IsValidRowIndex(aRow)) { 256 aError.Throw(NS_ERROR_INVALID_ARG); 257 return false; 258 } 259 260 return mRows[aRow]->IsOpen(); 261 } 262 263 NS_IMETHODIMP 264 nsTreeContentView::IsContainerOpen(int32_t aIndex, bool* _retval) { 265 ErrorResult rv; 266 *_retval = IsContainerOpen(aIndex, rv); 267 return rv.StealNSResult(); 268 } 269 270 bool nsTreeContentView::IsContainerEmpty(int32_t aRow, ErrorResult& aError) { 271 if (!IsValidRowIndex(aRow)) { 272 aError.Throw(NS_ERROR_INVALID_ARG); 273 return false; 274 } 275 276 return mRows[aRow]->IsEmpty(); 277 } 278 279 NS_IMETHODIMP 280 nsTreeContentView::IsContainerEmpty(int32_t aIndex, bool* _retval) { 281 ErrorResult rv; 282 *_retval = IsContainerEmpty(aIndex, rv); 283 return rv.StealNSResult(); 284 } 285 286 bool nsTreeContentView::IsSeparator(int32_t aRow, ErrorResult& aError) { 287 if (!IsValidRowIndex(aRow)) { 288 aError.Throw(NS_ERROR_INVALID_ARG); 289 return false; 290 } 291 292 return mRows[aRow]->IsSeparator(); 293 } 294 295 NS_IMETHODIMP 296 nsTreeContentView::IsSeparator(int32_t aIndex, bool* _retval) { 297 ErrorResult rv; 298 *_retval = IsSeparator(aIndex, rv); 299 return rv.StealNSResult(); 300 } 301 302 NS_IMETHODIMP 303 nsTreeContentView::IsSorted(bool* _retval) { 304 *_retval = IsSorted(); 305 306 return NS_OK; 307 } 308 309 bool nsTreeContentView::CanDrop(int32_t aRow, int32_t aOrientation, 310 ErrorResult& aError) { 311 if (!IsValidRowIndex(aRow)) { 312 aError.Throw(NS_ERROR_INVALID_ARG); 313 } 314 return false; 315 } 316 317 bool nsTreeContentView::CanDrop(int32_t aRow, int32_t aOrientation, 318 DataTransfer* aDataTransfer, 319 ErrorResult& aError) { 320 return CanDrop(aRow, aOrientation, aError); 321 } 322 323 NS_IMETHODIMP 324 nsTreeContentView::CanDrop(int32_t aIndex, int32_t aOrientation, 325 DataTransfer* aDataTransfer, bool* _retval) { 326 ErrorResult rv; 327 *_retval = CanDrop(aIndex, aOrientation, rv); 328 return rv.StealNSResult(); 329 } 330 331 void nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation, 332 ErrorResult& aError) { 333 if (!IsValidRowIndex(aRow)) { 334 aError.Throw(NS_ERROR_INVALID_ARG); 335 } 336 } 337 338 void nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation, 339 DataTransfer* aDataTransfer, ErrorResult& aError) { 340 Drop(aRow, aOrientation, aError); 341 } 342 343 NS_IMETHODIMP 344 nsTreeContentView::Drop(int32_t aRow, int32_t aOrientation, 345 DataTransfer* aDataTransfer) { 346 ErrorResult rv; 347 Drop(aRow, aOrientation, rv); 348 return rv.StealNSResult(); 349 } 350 351 int32_t nsTreeContentView::GetParentIndex(int32_t aRow, ErrorResult& aError) { 352 if (!IsValidRowIndex(aRow)) { 353 aError.Throw(NS_ERROR_INVALID_ARG); 354 return 0; 355 } 356 357 return mRows[aRow]->mParentIndex; 358 } 359 360 NS_IMETHODIMP 361 nsTreeContentView::GetParentIndex(int32_t aRowIndex, int32_t* _retval) { 362 ErrorResult rv; 363 *_retval = GetParentIndex(aRowIndex, rv); 364 return rv.StealNSResult(); 365 } 366 367 bool nsTreeContentView::HasNextSibling(int32_t aRow, int32_t aAfterIndex, 368 ErrorResult& aError) { 369 if (!IsValidRowIndex(aRow)) { 370 aError.Throw(NS_ERROR_INVALID_ARG); 371 return false; 372 } 373 374 // We have a next sibling if the row is not the last in the subtree. 375 int32_t parentIndex = mRows[aRow]->mParentIndex; 376 if (parentIndex < 0) { 377 return uint32_t(aRow) < mRows.Length() - 1; 378 } 379 380 // Compute the last index in this subtree. 381 int32_t lastIndex = parentIndex + (mRows[parentIndex])->mSubtreeSize; 382 Row* row = mRows[lastIndex].get(); 383 while (row->mParentIndex != parentIndex) { 384 lastIndex = row->mParentIndex; 385 row = mRows[lastIndex].get(); 386 } 387 388 return aRow < lastIndex; 389 } 390 391 NS_IMETHODIMP 392 nsTreeContentView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, 393 bool* _retval) { 394 ErrorResult rv; 395 *_retval = HasNextSibling(aRowIndex, aAfterIndex, rv); 396 return rv.StealNSResult(); 397 } 398 399 int32_t nsTreeContentView::GetLevel(int32_t aRow, ErrorResult& aError) { 400 if (!IsValidRowIndex(aRow)) { 401 aError.Throw(NS_ERROR_INVALID_ARG); 402 return 0; 403 } 404 405 int32_t level = 0; 406 Row* row = mRows[aRow].get(); 407 while (row->mParentIndex >= 0) { 408 level++; 409 row = mRows[row->mParentIndex].get(); 410 } 411 return level; 412 } 413 414 NS_IMETHODIMP 415 nsTreeContentView::GetLevel(int32_t aIndex, int32_t* _retval) { 416 ErrorResult rv; 417 *_retval = GetLevel(aIndex, rv); 418 return rv.StealNSResult(); 419 } 420 421 void nsTreeContentView::GetImageSrc(int32_t aRow, nsTreeColumn& aColumn, 422 nsAString& aSrc, ErrorResult& aError) { 423 if (!IsValidRowIndex(aRow)) { 424 aError.Throw(NS_ERROR_INVALID_ARG); 425 return; 426 } 427 428 Row* row = mRows[aRow].get(); 429 430 nsIContent* realRow = 431 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 432 if (realRow) { 433 Element* cell = GetCell(realRow, aColumn); 434 if (cell) { 435 cell->GetAttr(nsGkAtoms::src, aSrc); 436 } 437 } 438 } 439 440 NS_IMETHODIMP 441 nsTreeContentView::GetImageSrc(int32_t aRow, nsTreeColumn* aCol, 442 nsAString& _retval) { 443 NS_ENSURE_ARG(aCol); 444 445 ErrorResult rv; 446 GetImageSrc(aRow, *aCol, _retval, rv); 447 return rv.StealNSResult(); 448 } 449 450 void nsTreeContentView::GetCellValue(int32_t aRow, nsTreeColumn& aColumn, 451 nsAString& aValue, ErrorResult& aError) { 452 if (!IsValidRowIndex(aRow)) { 453 aError.Throw(NS_ERROR_INVALID_ARG); 454 return; 455 } 456 457 Row* row = mRows[aRow].get(); 458 459 nsIContent* realRow = 460 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 461 if (realRow) { 462 Element* cell = GetCell(realRow, aColumn); 463 if (cell) { 464 cell->GetAttr(nsGkAtoms::value, aValue); 465 } 466 } 467 } 468 469 NS_IMETHODIMP 470 nsTreeContentView::GetCellValue(int32_t aRow, nsTreeColumn* aCol, 471 nsAString& _retval) { 472 NS_ENSURE_ARG(aCol); 473 474 ErrorResult rv; 475 GetCellValue(aRow, *aCol, _retval, rv); 476 return rv.StealNSResult(); 477 } 478 479 void nsTreeContentView::GetCellText(int32_t aRow, nsTreeColumn& aColumn, 480 nsAString& aText, ErrorResult& aError) { 481 if (!IsValidRowIndex(aRow)) { 482 aError.Throw(NS_ERROR_INVALID_ARG); 483 return; 484 } 485 486 Row* row = mRows[aRow].get(); 487 488 // Check for a "label" attribute - this is valid on an <treeitem> 489 // with a single implied column. 490 if (row->mContent->GetAttr(nsGkAtoms::label, aText) && !aText.IsEmpty()) { 491 return; 492 } 493 494 if (row->mContent->IsXULElement(nsGkAtoms::treeitem)) { 495 nsIContent* realRow = 496 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 497 if (realRow) { 498 Element* cell = GetCell(realRow, aColumn); 499 if (cell) { 500 cell->GetAttr(nsGkAtoms::label, aText); 501 } 502 } 503 } 504 } 505 506 NS_IMETHODIMP 507 nsTreeContentView::GetCellText(int32_t aRow, nsTreeColumn* aCol, 508 nsAString& _retval) { 509 NS_ENSURE_ARG(aCol); 510 511 ErrorResult rv; 512 GetCellText(aRow, *aCol, _retval, rv); 513 return rv.StealNSResult(); 514 } 515 516 void nsTreeContentView::SetTree(XULTreeElement* aTree, ErrorResult& aError) { 517 aError = SetTree(aTree); 518 } 519 520 NS_IMETHODIMP 521 nsTreeContentView::SetTree(XULTreeElement* aTree) { 522 ClearRows(); 523 524 mTree = aTree; 525 526 if (aTree) { 527 // Add ourselves to document's observers. 528 Document* document = mTree->GetComposedDoc(); 529 if (document) { 530 document->AddObserver(this); 531 mDocument = document; 532 } 533 534 RefPtr<dom::Element> bodyElement = mTree->GetTreeBody(); 535 if (bodyElement) { 536 mBody = std::move(bodyElement); 537 int32_t index = 0; 538 Serialize(mBody, -1, &index, mRows); 539 } 540 } 541 542 return NS_OK; 543 } 544 545 void nsTreeContentView::ToggleOpenState(int32_t aRow, ErrorResult& aError) { 546 if (!IsValidRowIndex(aRow)) { 547 aError.Throw(NS_ERROR_INVALID_ARG); 548 return; 549 } 550 551 // We don't serialize content right here, since content might be generated 552 // lazily. 553 Row* row = mRows[aRow].get(); 554 555 if (row->IsOpen()) { 556 row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, u"false"_ns, 557 true); 558 } else { 559 row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, u"true"_ns, 560 true); 561 } 562 } 563 564 NS_IMETHODIMP 565 nsTreeContentView::ToggleOpenState(int32_t aIndex) { 566 ErrorResult rv; 567 ToggleOpenState(aIndex, rv); 568 return rv.StealNSResult(); 569 } 570 571 void nsTreeContentView::CycleHeader(nsTreeColumn& aColumn, 572 ErrorResult& aError) { 573 if (!mTree) { 574 return; 575 } 576 577 RefPtr<Element> column = aColumn.Element(); 578 nsAutoString sort; 579 column->GetAttr(nsGkAtoms::sort, sort); 580 if (!sort.IsEmpty()) { 581 nsAutoString sortdirection; 582 static Element::AttrValuesArray strings[] = { 583 nsGkAtoms::ascending, nsGkAtoms::descending, nullptr}; 584 switch (column->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::sortDirection, 585 strings, eCaseMatters)) { 586 case 0: 587 sortdirection.AssignLiteral("descending"); 588 break; 589 case 1: 590 sortdirection.AssignLiteral("natural"); 591 break; 592 default: 593 sortdirection.AssignLiteral("ascending"); 594 break; 595 } 596 597 nsAutoString hints; 598 column->GetAttr(nsGkAtoms::sorthints, hints); 599 sortdirection.Append(' '); 600 sortdirection += hints; 601 602 XULWidgetSort(mTree, sort, sortdirection); 603 } 604 } 605 606 NS_IMETHODIMP 607 nsTreeContentView::CycleHeader(nsTreeColumn* aCol) { 608 NS_ENSURE_ARG(aCol); 609 610 ErrorResult rv; 611 CycleHeader(*aCol, rv); 612 return rv.StealNSResult(); 613 } 614 615 NS_IMETHODIMP 616 nsTreeContentView::SelectionChangedXPCOM() { return NS_OK; } 617 618 NS_IMETHODIMP 619 nsTreeContentView::CycleCell(int32_t aRow, nsTreeColumn* aCol) { return NS_OK; } 620 621 bool nsTreeContentView::IsEditable(int32_t aRow, nsTreeColumn& aColumn, 622 ErrorResult& aError) { 623 if (!IsValidRowIndex(aRow)) { 624 aError.Throw(NS_ERROR_INVALID_ARG); 625 return false; 626 } 627 628 Row* row = mRows[aRow].get(); 629 630 nsIContent* realRow = 631 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 632 if (realRow) { 633 Element* cell = GetCell(realRow, aColumn); 634 if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable, 635 nsGkAtoms::_false, eCaseMatters)) { 636 return false; 637 } 638 } 639 640 return true; 641 } 642 643 NS_IMETHODIMP 644 nsTreeContentView::IsEditable(int32_t aRow, nsTreeColumn* aCol, bool* _retval) { 645 NS_ENSURE_ARG(aCol); 646 647 ErrorResult rv; 648 *_retval = IsEditable(aRow, *aCol, rv); 649 return rv.StealNSResult(); 650 } 651 652 void nsTreeContentView::SetCellValue(int32_t aRow, nsTreeColumn& aColumn, 653 const nsAString& aValue, 654 ErrorResult& aError) { 655 if (!IsValidRowIndex(aRow)) { 656 aError.Throw(NS_ERROR_INVALID_ARG); 657 return; 658 } 659 660 Row* row = mRows[aRow].get(); 661 662 nsIContent* realRow = 663 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 664 if (realRow) { 665 Element* cell = GetCell(realRow, aColumn); 666 if (cell) { 667 cell->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, true); 668 } 669 } 670 } 671 672 NS_IMETHODIMP 673 nsTreeContentView::SetCellValue(int32_t aRow, nsTreeColumn* aCol, 674 const nsAString& aValue) { 675 NS_ENSURE_ARG(aCol); 676 677 ErrorResult rv; 678 SetCellValue(aRow, *aCol, aValue, rv); 679 return rv.StealNSResult(); 680 } 681 682 void nsTreeContentView::SetCellText(int32_t aRow, nsTreeColumn& aColumn, 683 const nsAString& aValue, 684 ErrorResult& aError) { 685 if (!IsValidRowIndex(aRow)) { 686 aError.Throw(NS_ERROR_INVALID_ARG); 687 return; 688 } 689 690 Row* row = mRows[aRow].get(); 691 692 nsIContent* realRow = 693 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); 694 if (realRow) { 695 Element* cell = GetCell(realRow, aColumn); 696 if (cell) { 697 cell->SetAttr(kNameSpaceID_None, nsGkAtoms::label, aValue, true); 698 } 699 } 700 } 701 702 NS_IMETHODIMP 703 nsTreeContentView::SetCellText(int32_t aRow, nsTreeColumn* aCol, 704 const nsAString& aValue) { 705 NS_ENSURE_ARG(aCol); 706 707 ErrorResult rv; 708 SetCellText(aRow, *aCol, aValue, rv); 709 return rv.StealNSResult(); 710 } 711 712 Element* nsTreeContentView::GetItemAtIndex(int32_t aIndex, 713 ErrorResult& aError) { 714 if (!IsValidRowIndex(aIndex)) { 715 aError.Throw(NS_ERROR_INVALID_ARG); 716 return nullptr; 717 } 718 719 return mRows[aIndex]->mContent; 720 } 721 722 int32_t nsTreeContentView::GetIndexOfItem(Element* aItem) { 723 return FindContent(aItem); 724 } 725 726 void nsTreeContentView::AttributeChanged(dom::Element* aElement, 727 int32_t aNameSpaceID, 728 nsAtom* aAttribute, AttrModType, 729 const nsAttrValue* aOldValue) { 730 // Lots of codepaths under here that do all sorts of stuff, so be safe. 731 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 732 733 // Make sure this notification concerns us. 734 // First check the tag to see if it's one that we care about. 735 if (aElement == mTree || aElement == mBody) { 736 mTree->ClearStyleAndImageCaches(); 737 mTree->Invalidate(); 738 } 739 740 // We don't consider non-XUL nodes. 741 nsIContent* parent = nullptr; 742 if (!aElement->IsXULElement() || 743 ((parent = aElement->GetParent()) && !parent->IsXULElement())) { 744 return; 745 } 746 if (!aElement->IsAnyOfXULElements(nsGkAtoms::treecol, nsGkAtoms::treeitem, 747 nsGkAtoms::treeseparator, 748 nsGkAtoms::treerow, nsGkAtoms::treecell)) { 749 return; 750 } 751 752 // If we have a legal tag, go up to the tree/select and make sure 753 // that it's ours. 754 755 for (nsIContent* element = aElement; element != mBody; 756 element = element->GetParent()) { 757 if (!element) { 758 return; // this is not for us 759 } 760 if (element->IsXULElement(nsGkAtoms::tree)) { 761 return; // this is not for us 762 } 763 } 764 765 // Handle changes of the hidden attribute. 766 if (aAttribute == nsGkAtoms::hidden && 767 aElement->IsAnyOfXULElements(nsGkAtoms::treeitem, 768 nsGkAtoms::treeseparator)) { 769 bool hidden = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, 770 nsGkAtoms::_true, eCaseMatters); 771 772 int32_t index = FindContent(aElement); 773 if (hidden && index >= 0) { 774 // Hide this row along with its children. 775 int32_t count = RemoveRow(index); 776 if (mTree) { 777 mTree->RowCountChanged(index, -count); 778 } 779 } else if (!hidden && index < 0) { 780 // Show this row along with its children. 781 nsCOMPtr<nsIContent> parent = aElement->GetParent(); 782 if (parent) { 783 InsertRowFor(parent, aElement); 784 } 785 } 786 787 return; 788 } 789 790 if (aElement->IsXULElement(nsGkAtoms::treecol)) { 791 if (aAttribute == nsGkAtoms::properties) { 792 if (mTree) { 793 RefPtr<nsTreeColumns> cols = mTree->GetColumns(); 794 if (cols) { 795 RefPtr<nsTreeColumn> col = cols->GetColumnFor(aElement); 796 mTree->InvalidateColumn(col); 797 } 798 } 799 } 800 } else if (aElement->IsXULElement(nsGkAtoms::treeitem)) { 801 int32_t index = FindContent(aElement); 802 if (index >= 0) { 803 Row* row = mRows[index].get(); 804 if (aAttribute == nsGkAtoms::container) { 805 bool isContainer = 806 aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container, 807 nsGkAtoms::_true, eCaseMatters); 808 row->SetContainer(isContainer); 809 if (mTree) { 810 mTree->InvalidateRow(index); 811 } 812 } else if (aAttribute == nsGkAtoms::open) { 813 bool isOpen = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open, 814 nsGkAtoms::_true, eCaseMatters); 815 bool wasOpen = row->IsOpen(); 816 if (!isOpen && wasOpen) { 817 CloseContainer(index); 818 } else if (isOpen && !wasOpen) { 819 OpenContainer(index); 820 } 821 } else if (aAttribute == nsGkAtoms::empty) { 822 bool isEmpty = 823 aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty, 824 nsGkAtoms::_true, eCaseMatters); 825 row->SetEmpty(isEmpty); 826 if (mTree) { 827 mTree->InvalidateRow(index); 828 } 829 } 830 } 831 } else if (aElement->IsXULElement(nsGkAtoms::treeseparator)) { 832 int32_t index = FindContent(aElement); 833 if (index >= 0) { 834 if (aAttribute == nsGkAtoms::properties && mTree) { 835 mTree->InvalidateRow(index); 836 } 837 } 838 } else if (aElement->IsXULElement(nsGkAtoms::treerow)) { 839 if (aAttribute == nsGkAtoms::properties) { 840 nsCOMPtr<nsIContent> parent = aElement->GetParent(); 841 if (parent) { 842 int32_t index = FindContent(parent); 843 if (index >= 0 && mTree) { 844 mTree->InvalidateRow(index); 845 } 846 } 847 } 848 } else if (aElement->IsXULElement(nsGkAtoms::treecell)) { 849 if (aAttribute == nsGkAtoms::properties || aAttribute == nsGkAtoms::mode || 850 aAttribute == nsGkAtoms::src || aAttribute == nsGkAtoms::value || 851 aAttribute == nsGkAtoms::label) { 852 nsIContent* parent = aElement->GetParent(); 853 if (parent) { 854 nsCOMPtr<nsIContent> grandParent = parent->GetParent(); 855 if (grandParent && grandParent->IsXULElement()) { 856 int32_t index = FindContent(grandParent); 857 if (index >= 0 && mTree) { 858 // XXX Should we make an effort to invalidate only cell ? 859 mTree->InvalidateRow(index); 860 } 861 } 862 } 863 } 864 } 865 } 866 867 void nsTreeContentView::ContentAppended(nsIContent* aFirstNewContent, 868 const ContentAppendInfo& aInfo) { 869 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { 870 // Our contentinserted doesn't use the index 871 ContentInserted(cur, aInfo); 872 } 873 } 874 875 void nsTreeContentView::ContentInserted(nsIContent* aChild, 876 const ContentInsertInfo&) { 877 NS_ASSERTION(aChild, "null ptr"); 878 nsIContent* container = aChild->GetParent(); 879 880 // Make sure this notification concerns us. 881 // First check the tag to see if it's one that we care about. 882 883 // Don't allow non-XUL nodes. 884 if (!aChild->IsXULElement() || !container->IsXULElement()) { 885 return; 886 } 887 888 if (!aChild->IsAnyOfXULElements(nsGkAtoms::treeitem, nsGkAtoms::treeseparator, 889 nsGkAtoms::treechildren, nsGkAtoms::treerow, 890 nsGkAtoms::treecell)) { 891 return; 892 } 893 894 // If we have a legal tag, go up to the tree/select and make sure 895 // that it's ours. 896 897 for (nsIContent* element = container; element != mBody; 898 element = element->GetParent()) { 899 if (!element) { 900 return; // this is not for us 901 } 902 if (element->IsXULElement(nsGkAtoms::tree)) { 903 return; // this is not for us 904 } 905 } 906 907 // Lots of codepaths under here that do all sorts of stuff, so be safe. 908 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 909 910 if (aChild->IsXULElement(nsGkAtoms::treechildren)) { 911 int32_t index = FindContent(container); 912 if (index >= 0) { 913 Row* row = mRows[index].get(); 914 row->SetEmpty(false); 915 if (mTree) { 916 mTree->InvalidateRow(index); 917 } 918 if (row->IsContainer() && row->IsOpen()) { 919 int32_t count = EnsureSubtree(index); 920 if (mTree) { 921 mTree->RowCountChanged(index + 1, count); 922 } 923 } 924 } 925 } else if (aChild->IsAnyOfXULElements(nsGkAtoms::treeitem, 926 nsGkAtoms::treeseparator)) { 927 InsertRowFor(container, aChild); 928 } else if (aChild->IsXULElement(nsGkAtoms::treerow)) { 929 int32_t index = FindContent(container); 930 if (index >= 0 && mTree) { 931 mTree->InvalidateRow(index); 932 } 933 } else if (aChild->IsXULElement(nsGkAtoms::treecell)) { 934 nsCOMPtr<nsIContent> parent = container->GetParent(); 935 if (parent) { 936 int32_t index = FindContent(parent); 937 if (index >= 0 && mTree) { 938 mTree->InvalidateRow(index); 939 } 940 } 941 } 942 } 943 944 void nsTreeContentView::ContentWillBeRemoved(nsIContent* aChild, 945 const ContentRemoveInfo&) { 946 NS_ASSERTION(aChild, "null ptr"); 947 948 nsIContent* container = aChild->GetParent(); 949 // Make sure this notification concerns us. 950 // First check the tag to see if it's one that we care about. 951 952 // We don't consider non-XUL nodes. 953 if (!aChild->IsXULElement() || !container->IsXULElement()) { 954 return; 955 } 956 957 if (!aChild->IsAnyOfXULElements(nsGkAtoms::treeitem, nsGkAtoms::treeseparator, 958 nsGkAtoms::treechildren, nsGkAtoms::treerow, 959 nsGkAtoms::treecell)) { 960 return; 961 } 962 963 // If we have a legal tag, go up to the tree/select and make sure 964 // that it's ours. 965 966 for (nsIContent* element = container; element != mBody; 967 element = element->GetParent()) { 968 if (!element) { 969 return; // this is not for us 970 } 971 if (element->IsXULElement(nsGkAtoms::tree)) { 972 return; // this is not for us 973 } 974 } 975 976 // Lots of codepaths under here that do all sorts of stuff, so be safe. 977 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 978 979 if (aChild->IsXULElement(nsGkAtoms::treechildren)) { 980 int32_t index = FindContent(container); 981 if (index >= 0) { 982 Row* row = mRows[index].get(); 983 row->SetEmpty(true); 984 int32_t count = RemoveSubtree(index); 985 // Invalidate also the row to update twisty. 986 if (mTree) { 987 mTree->InvalidateRow(index); 988 mTree->RowCountChanged(index + 1, -count); 989 } 990 } 991 } else if (aChild->IsAnyOfXULElements(nsGkAtoms::treeitem, 992 nsGkAtoms::treeseparator)) { 993 int32_t index = FindContent(aChild); 994 if (index >= 0) { 995 int32_t count = RemoveRow(index); 996 if (mTree) { 997 mTree->RowCountChanged(index, -count); 998 } 999 } 1000 } else if (aChild->IsXULElement(nsGkAtoms::treerow)) { 1001 int32_t index = FindContent(container); 1002 if (index >= 0 && mTree) { 1003 mTree->InvalidateRow(index); 1004 } 1005 } else if (aChild->IsXULElement(nsGkAtoms::treecell)) { 1006 nsCOMPtr<nsIContent> parent = container->GetParent(); 1007 if (parent) { 1008 int32_t index = FindContent(parent); 1009 if (index >= 0 && mTree) { 1010 mTree->InvalidateRow(index); 1011 } 1012 } 1013 } 1014 } 1015 1016 void nsTreeContentView::NodeWillBeDestroyed(nsINode* aNode) { 1017 // XXXbz do we need this strong ref? Do we drop refs to self in ClearRows? 1018 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 1019 ClearRows(); 1020 } 1021 1022 // Recursively serialize content, starting with aContent. 1023 void nsTreeContentView::Serialize(nsIContent* aContent, int32_t aParentIndex, 1024 int32_t* aIndex, 1025 nsTArray<UniquePtr<Row>>& aRows) { 1026 // Don't allow non-XUL nodes. 1027 if (!aContent->IsXULElement()) { 1028 return; 1029 } 1030 1031 dom::FlattenedChildIterator iter(aContent); 1032 for (nsIContent* content = iter.GetNextChild(); content; 1033 content = iter.GetNextChild()) { 1034 int32_t count = aRows.Length(); 1035 1036 if (content->IsXULElement(nsGkAtoms::treeitem)) { 1037 SerializeItem(content->AsElement(), aParentIndex, aIndex, aRows); 1038 } else if (content->IsXULElement(nsGkAtoms::treeseparator)) { 1039 SerializeSeparator(content->AsElement(), aParentIndex, aIndex, aRows); 1040 } 1041 1042 *aIndex += aRows.Length() - count; 1043 } 1044 } 1045 1046 void nsTreeContentView::SerializeItem(Element* aContent, int32_t aParentIndex, 1047 int32_t* aIndex, 1048 nsTArray<UniquePtr<Row>>& aRows) { 1049 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, 1050 nsGkAtoms::_true, eCaseMatters)) { 1051 return; 1052 } 1053 1054 aRows.AppendElement(MakeUnique<Row>(aContent, aParentIndex)); 1055 Row* row = aRows.LastElement().get(); 1056 1057 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container, 1058 nsGkAtoms::_true, eCaseMatters)) { 1059 row->SetContainer(true); 1060 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open, 1061 nsGkAtoms::_true, eCaseMatters)) { 1062 row->SetOpen(true); 1063 nsIContent* child = 1064 nsTreeUtils::GetImmediateChild(aContent, nsGkAtoms::treechildren); 1065 if (child && child->IsXULElement()) { 1066 // Now, recursively serialize our child. 1067 int32_t count = aRows.Length(); 1068 int32_t index = 0; 1069 Serialize(child, aParentIndex + *aIndex + 1, &index, aRows); 1070 row->mSubtreeSize += aRows.Length() - count; 1071 } else { 1072 row->SetEmpty(true); 1073 } 1074 } else if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty, 1075 nsGkAtoms::_true, eCaseMatters)) { 1076 row->SetEmpty(true); 1077 } 1078 } 1079 } 1080 1081 void nsTreeContentView::SerializeSeparator(Element* aContent, 1082 int32_t aParentIndex, 1083 int32_t* aIndex, 1084 nsTArray<UniquePtr<Row>>& aRows) { 1085 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, 1086 nsGkAtoms::_true, eCaseMatters)) { 1087 return; 1088 } 1089 1090 auto row = MakeUnique<Row>(aContent, aParentIndex); 1091 row->SetSeparator(true); 1092 aRows.AppendElement(std::move(row)); 1093 } 1094 1095 void nsTreeContentView::GetIndexInSubtree(nsIContent* aContainer, 1096 nsIContent* aContent, 1097 int32_t* aIndex) { 1098 if (!aContainer->IsXULElement()) { 1099 return; 1100 } 1101 1102 for (nsIContent* content = aContainer->GetFirstChild(); content; 1103 content = content->GetNextSibling()) { 1104 if (content == aContent) { 1105 break; 1106 } 1107 1108 if (content->IsXULElement(nsGkAtoms::treeitem)) { 1109 if (!content->AsElement()->AttrValueIs(kNameSpaceID_None, 1110 nsGkAtoms::hidden, 1111 nsGkAtoms::_true, eCaseMatters)) { 1112 (*aIndex)++; 1113 if (content->AsElement()->AttrValueIs(kNameSpaceID_None, 1114 nsGkAtoms::container, 1115 nsGkAtoms::_true, eCaseMatters) && 1116 content->AsElement()->AttrValueIs(kNameSpaceID_None, 1117 nsGkAtoms::open, nsGkAtoms::_true, 1118 eCaseMatters)) { 1119 nsIContent* child = 1120 nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treechildren); 1121 if (child && child->IsXULElement()) { 1122 GetIndexInSubtree(child, aContent, aIndex); 1123 } 1124 } 1125 } 1126 } else if (content->IsXULElement(nsGkAtoms::treeseparator)) { 1127 if (!content->AsElement()->AttrValueIs(kNameSpaceID_None, 1128 nsGkAtoms::hidden, 1129 nsGkAtoms::_true, eCaseMatters)) { 1130 (*aIndex)++; 1131 } 1132 } 1133 } 1134 } 1135 1136 int32_t nsTreeContentView::EnsureSubtree(int32_t aIndex) { 1137 Row* row = mRows[aIndex].get(); 1138 1139 nsIContent* child; 1140 child = 1141 nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treechildren); 1142 if (!child || !child->IsXULElement()) { 1143 return 0; 1144 } 1145 1146 AutoTArray<UniquePtr<Row>, 8> rows; 1147 int32_t index = 0; 1148 Serialize(child, aIndex, &index, rows); 1149 // Insert |rows| into |mRows| at position |aIndex|, by first creating empty 1150 // UniquePtr entries and then Move'ing |rows|'s entries into them. (Note 1151 // that we can't simply use InsertElementsAt with an array argument, since 1152 // the destination can't steal ownership from its const source argument.) 1153 UniquePtr<Row>* newRows = mRows.InsertElementsAt(aIndex + 1, rows.Length()); 1154 for (nsTArray<Row>::index_type i = 0; i < rows.Length(); i++) { 1155 newRows[i] = std::move(rows[i]); 1156 } 1157 int32_t count = rows.Length(); 1158 1159 row->mSubtreeSize += count; 1160 UpdateSubtreeSizes(row->mParentIndex, count); 1161 1162 // Update parent indexes, but skip newly added rows. 1163 // They already have correct values. 1164 UpdateParentIndexes(aIndex, count + 1, count); 1165 1166 return count; 1167 } 1168 1169 int32_t nsTreeContentView::RemoveSubtree(int32_t aIndex) { 1170 Row* row = mRows[aIndex].get(); 1171 int32_t count = row->mSubtreeSize; 1172 1173 mRows.RemoveElementsAt(aIndex + 1, count); 1174 1175 row->mSubtreeSize -= count; 1176 UpdateSubtreeSizes(row->mParentIndex, -count); 1177 1178 UpdateParentIndexes(aIndex, 0, -count); 1179 1180 return count; 1181 } 1182 1183 void nsTreeContentView::InsertRowFor(nsIContent* aParent, nsIContent* aChild) { 1184 int32_t grandParentIndex = -1; 1185 bool insertRow = false; 1186 1187 nsCOMPtr<nsIContent> grandParent = aParent->GetParent(); 1188 1189 if (grandParent->IsXULElement(nsGkAtoms::tree)) { 1190 // Allow insertion to the outermost container. 1191 insertRow = true; 1192 } else { 1193 // Test insertion to an inner container. 1194 1195 // First try to find this parent in our array of rows, if we find one 1196 // we can be sure that all other parents are open too. 1197 grandParentIndex = FindContent(grandParent); 1198 if (grandParentIndex >= 0) { 1199 // Got it, now test if it is open. 1200 if (mRows[grandParentIndex]->IsOpen()) { 1201 insertRow = true; 1202 } 1203 } 1204 } 1205 1206 if (insertRow) { 1207 int32_t index = 0; 1208 GetIndexInSubtree(aParent, aChild, &index); 1209 1210 int32_t count = InsertRow(grandParentIndex, index, aChild); 1211 if (mTree) { 1212 mTree->RowCountChanged(grandParentIndex + index + 1, count); 1213 } 1214 } 1215 } 1216 1217 int32_t nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex, 1218 nsIContent* aContent) { 1219 AutoTArray<UniquePtr<Row>, 8> rows; 1220 if (aContent->IsXULElement(nsGkAtoms::treeitem)) { 1221 SerializeItem(aContent->AsElement(), aParentIndex, &aIndex, rows); 1222 } else if (aContent->IsXULElement(nsGkAtoms::treeseparator)) { 1223 SerializeSeparator(aContent->AsElement(), aParentIndex, &aIndex, rows); 1224 } 1225 1226 // We can't use InsertElementsAt since the destination can't steal 1227 // ownership from its const source argument. 1228 int32_t count = rows.Length(); 1229 for (nsTArray<Row>::index_type i = 0; i < size_t(count); i++) { 1230 mRows.InsertElementAt(aParentIndex + aIndex + i + 1, std::move(rows[i])); 1231 } 1232 1233 UpdateSubtreeSizes(aParentIndex, count); 1234 1235 // Update parent indexes, but skip added rows. 1236 // They already have correct values. 1237 UpdateParentIndexes(aParentIndex + aIndex, count + 1, count); 1238 1239 return count; 1240 } 1241 1242 int32_t nsTreeContentView::RemoveRow(int32_t aIndex) { 1243 Row* row = mRows[aIndex].get(); 1244 int32_t count = row->mSubtreeSize + 1; 1245 int32_t parentIndex = row->mParentIndex; 1246 1247 mRows.RemoveElementsAt(aIndex, count); 1248 1249 UpdateSubtreeSizes(parentIndex, -count); 1250 1251 UpdateParentIndexes(aIndex, 0, -count); 1252 1253 return count; 1254 } 1255 1256 void nsTreeContentView::ClearRows() { 1257 mRows.Clear(); 1258 mBody = nullptr; 1259 // Remove ourselves from mDocument's observers. 1260 if (mDocument) { 1261 mDocument->RemoveObserver(this); 1262 mDocument = nullptr; 1263 } 1264 } 1265 1266 void nsTreeContentView::OpenContainer(int32_t aIndex) { 1267 Row* row = mRows[aIndex].get(); 1268 row->SetOpen(true); 1269 1270 int32_t count = EnsureSubtree(aIndex); 1271 if (mTree) { 1272 mTree->InvalidateRow(aIndex); 1273 mTree->RowCountChanged(aIndex + 1, count); 1274 } 1275 } 1276 1277 void nsTreeContentView::CloseContainer(int32_t aIndex) { 1278 Row* row = mRows[aIndex].get(); 1279 row->SetOpen(false); 1280 1281 int32_t count = RemoveSubtree(aIndex); 1282 if (mTree) { 1283 mTree->InvalidateRow(aIndex); 1284 mTree->RowCountChanged(aIndex + 1, -count); 1285 } 1286 } 1287 1288 int32_t nsTreeContentView::FindContent(nsIContent* aContent) { 1289 for (uint32_t i = 0; i < mRows.Length(); i++) { 1290 if (mRows[i]->mContent == aContent) { 1291 return i; 1292 } 1293 } 1294 1295 return -1; 1296 } 1297 1298 void nsTreeContentView::UpdateSubtreeSizes(int32_t aParentIndex, 1299 int32_t count) { 1300 while (aParentIndex >= 0) { 1301 Row* row = mRows[aParentIndex].get(); 1302 row->mSubtreeSize += count; 1303 aParentIndex = row->mParentIndex; 1304 } 1305 } 1306 1307 void nsTreeContentView::UpdateParentIndexes(int32_t aIndex, int32_t aSkip, 1308 int32_t aCount) { 1309 int32_t count = mRows.Length(); 1310 for (int32_t i = aIndex + aSkip; i < count; i++) { 1311 Row* row = mRows[i].get(); 1312 if (row->mParentIndex > aIndex) { 1313 row->mParentIndex += aCount; 1314 } 1315 } 1316 } 1317 1318 Element* nsTreeContentView::GetCell(nsIContent* aContainer, 1319 nsTreeColumn& aCol) { 1320 int32_t colIndex(aCol.GetIndex()); 1321 1322 // Traverse through cells, try to find the cell by index in a row. 1323 Element* result = nullptr; 1324 int32_t j = 0; 1325 dom::FlattenedChildIterator iter(aContainer); 1326 for (nsIContent* cell = iter.GetNextChild(); cell; 1327 cell = iter.GetNextChild()) { 1328 if (cell->IsXULElement(nsGkAtoms::treecell)) { 1329 if (j == colIndex) { 1330 result = cell->AsElement(); 1331 break; 1332 } 1333 j++; 1334 } 1335 } 1336 1337 return result; 1338 } 1339 1340 bool nsTreeContentView::IsValidRowIndex(int32_t aRowIndex) { 1341 return aRowIndex >= 0 && aRowIndex < int32_t(mRows.Length()); 1342 }