layout/xul/tree/nsTreeContentView.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial