content/xul/templates/src/nsXULTreeBuilder.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "nscore.h"
michael@0 7 #include "nsError.h"
michael@0 8 #include "nsIContent.h"
michael@0 9 #include "nsINodeInfo.h"
michael@0 10 #include "nsIDOMElement.h"
michael@0 11 #include "nsILocalStore.h"
michael@0 12 #include "nsIBoxObject.h"
michael@0 13 #include "nsITreeBoxObject.h"
michael@0 14 #include "nsITreeSelection.h"
michael@0 15 #include "nsITreeColumns.h"
michael@0 16 #include "nsITreeView.h"
michael@0 17 #include "nsTreeUtils.h"
michael@0 18 #include "nsIServiceManager.h"
michael@0 19 #include "nsReadableUtils.h"
michael@0 20 #include "nsQuickSort.h"
michael@0 21 #include "nsTreeRows.h"
michael@0 22 #include "nsTemplateRule.h"
michael@0 23 #include "nsTemplateMatch.h"
michael@0 24 #include "nsGkAtoms.h"
michael@0 25 #include "nsXULContentUtils.h"
michael@0 26 #include "nsXULTemplateBuilder.h"
michael@0 27 #include "nsIXULSortService.h"
michael@0 28 #include "nsTArray.h"
michael@0 29 #include "nsUnicharUtils.h"
michael@0 30 #include "nsNameSpaceManager.h"
michael@0 31 #include "nsDOMClassInfoID.h"
michael@0 32 #include "nsWhitespaceTokenizer.h"
michael@0 33 #include "nsTreeContentView.h"
michael@0 34
michael@0 35 // For security check
michael@0 36 #include "nsIDocument.h"
michael@0 37
michael@0 38 /**
michael@0 39 * A XUL template builder that serves as an tree view, allowing
michael@0 40 * (pretty much) arbitrary RDF to be presented in an tree.
michael@0 41 */
michael@0 42 class nsXULTreeBuilder : public nsXULTemplateBuilder,
michael@0 43 public nsIXULTreeBuilder,
michael@0 44 public nsINativeTreeView
michael@0 45 {
michael@0 46 public:
michael@0 47 // nsISupports
michael@0 48 NS_DECL_ISUPPORTS_INHERITED
michael@0 49 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
michael@0 50
michael@0 51 // nsIXULTreeBuilder
michael@0 52 NS_DECL_NSIXULTREEBUILDER
michael@0 53
michael@0 54 // nsITreeView
michael@0 55 NS_DECL_NSITREEVIEW
michael@0 56 // nsINativeTreeView: Untrusted code can use us
michael@0 57 NS_IMETHOD EnsureNative() { return NS_OK; }
michael@0 58
michael@0 59 // nsIMutationObserver
michael@0 60 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
michael@0 61
michael@0 62 protected:
michael@0 63 friend nsresult
michael@0 64 NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
michael@0 65
michael@0 66 nsXULTreeBuilder();
michael@0 67
michael@0 68 /**
michael@0 69 * Uninitialize the template builder
michael@0 70 */
michael@0 71 virtual void Uninit(bool aIsFinal);
michael@0 72
michael@0 73 /**
michael@0 74 * Get sort variables from the active <treecol>
michael@0 75 */
michael@0 76 nsresult
michael@0 77 EnsureSortVariables();
michael@0 78
michael@0 79 virtual nsresult
michael@0 80 RebuildAll();
michael@0 81
michael@0 82 /**
michael@0 83 * Given a row, use the row's match to figure out the appropriate
michael@0 84 * <treerow> in the rule's <action>.
michael@0 85 */
michael@0 86 nsresult
michael@0 87 GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult);
michael@0 88
michael@0 89 /**
michael@0 90 * Given a row and a column ID, use the row's match to figure out
michael@0 91 * the appropriate <treecell> in the rule's <action>.
michael@0 92 */
michael@0 93 nsresult
michael@0 94 GetTemplateActionCellFor(int32_t aRow, nsITreeColumn* aCol, nsIContent** aResult);
michael@0 95
michael@0 96 /**
michael@0 97 * Return the resource corresponding to a row in the tree.
michael@0 98 */
michael@0 99 nsresult
michael@0 100 GetResourceFor(int32_t aRow, nsIRDFResource** aResource);
michael@0 101
michael@0 102 /**
michael@0 103 * Open a container row, inserting the container's children into
michael@0 104 * the view.
michael@0 105 */
michael@0 106 nsresult
michael@0 107 OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult);
michael@0 108
michael@0 109 /**
michael@0 110 * Helper for OpenContainer, recursively open subtrees, remembering
michael@0 111 * persisted ``open'' state
michael@0 112 */
michael@0 113 nsresult
michael@0 114 OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
michael@0 115 int32_t aIndex,
michael@0 116 nsIXULTemplateResult *aResult,
michael@0 117 int32_t* aDelta);
michael@0 118
michael@0 119 nsresult
michael@0 120 OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
michael@0 121 int32_t aIndex,
michael@0 122 nsIXULTemplateResult *aResult,
michael@0 123 nsTemplateQuerySet* aQuerySet,
michael@0 124 int32_t* aDelta,
michael@0 125 nsTArray<int32_t>& open);
michael@0 126
michael@0 127 /**
michael@0 128 * Close a container row, removing the container's childrem from
michael@0 129 * the view.
michael@0 130 */
michael@0 131 nsresult
michael@0 132 CloseContainer(int32_t aIndex);
michael@0 133
michael@0 134 /**
michael@0 135 * Remove the matches for the rows in a subtree
michael@0 136 */
michael@0 137 nsresult
michael@0 138 RemoveMatchesFor(nsTreeRows::Subtree& subtree);
michael@0 139
michael@0 140 /**
michael@0 141 * Helper methods that determine if the specified container is open.
michael@0 142 */
michael@0 143 nsresult
michael@0 144 IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen);
michael@0 145
michael@0 146 nsresult
michael@0 147 IsContainerOpen(nsIRDFResource* aResource, bool* aOpen);
michael@0 148
michael@0 149 /**
michael@0 150 * A sorting callback for NS_QuickSort().
michael@0 151 */
michael@0 152 static int
michael@0 153 Compare(const void* aLeft, const void* aRight, void* aClosure);
michael@0 154
michael@0 155 /**
michael@0 156 * The real sort routine
michael@0 157 */
michael@0 158 int32_t
michael@0 159 CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight);
michael@0 160
michael@0 161 /**
michael@0 162 * Sort the specified subtree, and recursively sort any subtrees
michael@0 163 * beneath it.
michael@0 164 */
michael@0 165 nsresult
michael@0 166 SortSubtree(nsTreeRows::Subtree* aSubtree);
michael@0 167
michael@0 168 NS_IMETHOD
michael@0 169 HasGeneratedContent(nsIRDFResource* aResource,
michael@0 170 nsIAtom* aTag,
michael@0 171 bool* aGenerated);
michael@0 172
michael@0 173 // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited
michael@0 174 // from nsXULTemplateBuilder
michael@0 175
michael@0 176 /**
michael@0 177 * Return true if the result can be inserted into the template as a new
michael@0 178 * row.
michael@0 179 */
michael@0 180 bool
michael@0 181 GetInsertionLocations(nsIXULTemplateResult* aResult,
michael@0 182 nsCOMArray<nsIContent>** aLocations);
michael@0 183
michael@0 184 /**
michael@0 185 * Implement result replacement
michael@0 186 */
michael@0 187 virtual nsresult
michael@0 188 ReplaceMatch(nsIXULTemplateResult* aOldResult,
michael@0 189 nsTemplateMatch* aNewMatch,
michael@0 190 nsTemplateRule* aNewMatchRule,
michael@0 191 void *aContext);
michael@0 192
michael@0 193 /**
michael@0 194 * Implement match synchronization
michael@0 195 */
michael@0 196 virtual nsresult
michael@0 197 SynchronizeResult(nsIXULTemplateResult* aResult);
michael@0 198
michael@0 199 /**
michael@0 200 * The tree's box object, used to communicate with the front-end.
michael@0 201 */
michael@0 202 nsCOMPtr<nsITreeBoxObject> mBoxObject;
michael@0 203
michael@0 204 /**
michael@0 205 * The tree's selection object.
michael@0 206 */
michael@0 207 nsCOMPtr<nsITreeSelection> mSelection;
michael@0 208
michael@0 209 /**
michael@0 210 * The datasource that's used to persist open folder information
michael@0 211 */
michael@0 212 nsCOMPtr<nsIRDFDataSource> mPersistStateStore;
michael@0 213
michael@0 214 /**
michael@0 215 * The rows in the view
michael@0 216 */
michael@0 217 nsTreeRows mRows;
michael@0 218
michael@0 219 /**
michael@0 220 * The currently active sort variable
michael@0 221 */
michael@0 222 nsCOMPtr<nsIAtom> mSortVariable;
michael@0 223
michael@0 224 enum Direction {
michael@0 225 eDirection_Descending = -1,
michael@0 226 eDirection_Natural = 0,
michael@0 227 eDirection_Ascending = +1
michael@0 228 };
michael@0 229
michael@0 230 /**
michael@0 231 * The currently active sort order
michael@0 232 */
michael@0 233 Direction mSortDirection;
michael@0 234
michael@0 235 /*
michael@0 236 * Sort hints (compare case, etc)
michael@0 237 */
michael@0 238 uint32_t mSortHints;
michael@0 239
michael@0 240 /**
michael@0 241 * The builder observers.
michael@0 242 */
michael@0 243 nsCOMArray<nsIXULTreeBuilderObserver> mObservers;
michael@0 244 };
michael@0 245
michael@0 246 //----------------------------------------------------------------------
michael@0 247
michael@0 248 nsresult
michael@0 249 NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
michael@0 250 {
michael@0 251 *aResult = nullptr;
michael@0 252
michael@0 253 NS_PRECONDITION(aOuter == nullptr, "no aggregation");
michael@0 254 if (aOuter)
michael@0 255 return NS_ERROR_NO_AGGREGATION;
michael@0 256
michael@0 257 nsresult rv;
michael@0 258 nsXULTreeBuilder* result = new nsXULTreeBuilder();
michael@0 259 if (! result)
michael@0 260 return NS_ERROR_OUT_OF_MEMORY;
michael@0 261
michael@0 262 NS_ADDREF(result); // stabilize
michael@0 263
michael@0 264 rv = result->InitGlobals();
michael@0 265
michael@0 266 if (NS_SUCCEEDED(rv))
michael@0 267 rv = result->QueryInterface(aIID, aResult);
michael@0 268
michael@0 269 NS_RELEASE(result);
michael@0 270 return rv;
michael@0 271 }
michael@0 272
michael@0 273 NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
michael@0 274 NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
michael@0 275
michael@0 276 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder,
michael@0 277 mBoxObject,
michael@0 278 mSelection,
michael@0 279 mPersistStateStore,
michael@0 280 mObservers)
michael@0 281
michael@0 282 DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
michael@0 283
michael@0 284 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder)
michael@0 285 NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder)
michael@0 286 NS_INTERFACE_MAP_ENTRY(nsITreeView)
michael@0 287 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder)
michael@0 288 NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder)
michael@0 289
michael@0 290
michael@0 291 nsXULTreeBuilder::nsXULTreeBuilder()
michael@0 292 : mSortDirection(eDirection_Natural), mSortHints(0)
michael@0 293 {
michael@0 294 }
michael@0 295
michael@0 296 void
michael@0 297 nsXULTreeBuilder::Uninit(bool aIsFinal)
michael@0 298 {
michael@0 299 int32_t count = mRows.Count();
michael@0 300 mRows.Clear();
michael@0 301
michael@0 302 if (mBoxObject) {
michael@0 303 mBoxObject->BeginUpdateBatch();
michael@0 304 mBoxObject->RowCountChanged(0, -count);
michael@0 305 if (mBoxObject) {
michael@0 306 mBoxObject->EndUpdateBatch();
michael@0 307 }
michael@0 308 }
michael@0 309
michael@0 310 nsXULTemplateBuilder::Uninit(aIsFinal);
michael@0 311 }
michael@0 312
michael@0 313
michael@0 314 //----------------------------------------------------------------------
michael@0 315 //
michael@0 316 // nsIXULTreeBuilder methods
michael@0 317 //
michael@0 318
michael@0 319 NS_IMETHODIMP
michael@0 320 nsXULTreeBuilder::GetResourceAtIndex(int32_t aRowIndex, nsIRDFResource** aResult)
michael@0 321 {
michael@0 322 if (aRowIndex < 0 || aRowIndex >= mRows.Count())
michael@0 323 return NS_ERROR_INVALID_ARG;
michael@0 324
michael@0 325 return GetResourceFor(aRowIndex, aResult);
michael@0 326 }
michael@0 327
michael@0 328 NS_IMETHODIMP
michael@0 329 nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource* aResource, int32_t* aResult)
michael@0 330 {
michael@0 331 NS_ENSURE_ARG_POINTER(aResource);
michael@0 332 nsTreeRows::iterator iter = mRows.FindByResource(aResource);
michael@0 333 if (iter == mRows.Last())
michael@0 334 *aResult = -1;
michael@0 335 else
michael@0 336 *aResult = iter.GetRowIndex();
michael@0 337 return NS_OK;
michael@0 338 }
michael@0 339
michael@0 340 NS_IMETHODIMP
michael@0 341 nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver)
michael@0 342 {
michael@0 343 return mObservers.AppendObject(aObserver) ? NS_OK : NS_ERROR_FAILURE;
michael@0 344 }
michael@0 345
michael@0 346 NS_IMETHODIMP
michael@0 347 nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver)
michael@0 348 {
michael@0 349 return mObservers.RemoveObject(aObserver) ? NS_OK : NS_ERROR_FAILURE;
michael@0 350 }
michael@0 351
michael@0 352 NS_IMETHODIMP
michael@0 353 nsXULTreeBuilder::Sort(nsIDOMElement* aElement)
michael@0 354 {
michael@0 355 nsCOMPtr<nsIContent> header = do_QueryInterface(aElement);
michael@0 356 if (! header)
michael@0 357 return NS_ERROR_FAILURE;
michael@0 358
michael@0 359 if (header->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortLocked,
michael@0 360 nsGkAtoms::_true, eCaseMatters))
michael@0 361 return NS_OK;
michael@0 362
michael@0 363 nsAutoString sort;
michael@0 364 header->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
michael@0 365
michael@0 366 if (sort.IsEmpty())
michael@0 367 return NS_OK;
michael@0 368
michael@0 369 // Grab the new sort variable
michael@0 370 mSortVariable = do_GetAtom(sort);
michael@0 371
michael@0 372 nsAutoString hints;
michael@0 373 header->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
michael@0 374
michael@0 375 bool hasNaturalState = true;
michael@0 376 nsWhitespaceTokenizer tokenizer(hints);
michael@0 377 while (tokenizer.hasMoreTokens()) {
michael@0 378 const nsDependentSubstring& token(tokenizer.nextToken());
michael@0 379 if (token.EqualsLiteral("comparecase"))
michael@0 380 mSortHints |= nsIXULSortService::SORT_COMPARECASE;
michael@0 381 else if (token.EqualsLiteral("integer"))
michael@0 382 mSortHints |= nsIXULSortService::SORT_INTEGER;
michael@0 383 else if (token.EqualsLiteral("twostate"))
michael@0 384 hasNaturalState = false;
michael@0 385 }
michael@0 386
michael@0 387 // Cycle the sort direction
michael@0 388 nsAutoString dir;
michael@0 389 header->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, dir);
michael@0 390
michael@0 391 if (dir.EqualsLiteral("ascending")) {
michael@0 392 dir.AssignLiteral("descending");
michael@0 393 mSortDirection = eDirection_Descending;
michael@0 394 }
michael@0 395 else if (hasNaturalState && dir.EqualsLiteral("descending")) {
michael@0 396 dir.AssignLiteral("natural");
michael@0 397 mSortDirection = eDirection_Natural;
michael@0 398 }
michael@0 399 else {
michael@0 400 dir.AssignLiteral("ascending");
michael@0 401 mSortDirection = eDirection_Ascending;
michael@0 402 }
michael@0 403
michael@0 404 // Sort it.
michael@0 405 SortSubtree(mRows.GetRoot());
michael@0 406 mRows.InvalidateCachedRow();
michael@0 407 if (mBoxObject)
michael@0 408 mBoxObject->Invalidate();
michael@0 409
michael@0 410 nsTreeUtils::UpdateSortIndicators(header, dir);
michael@0 411
michael@0 412 return NS_OK;
michael@0 413 }
michael@0 414
michael@0 415 //----------------------------------------------------------------------
michael@0 416 //
michael@0 417 // nsITreeView methods
michael@0 418 //
michael@0 419
michael@0 420 NS_IMETHODIMP
michael@0 421 nsXULTreeBuilder::GetRowCount(int32_t* aRowCount)
michael@0 422 {
michael@0 423 *aRowCount = mRows.Count();
michael@0 424 return NS_OK;
michael@0 425 }
michael@0 426
michael@0 427 NS_IMETHODIMP
michael@0 428 nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection)
michael@0 429 {
michael@0 430 NS_IF_ADDREF(*aSelection = mSelection.get());
michael@0 431 return NS_OK;
michael@0 432 }
michael@0 433
michael@0 434 NS_IMETHODIMP
michael@0 435 nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection)
michael@0 436 {
michael@0 437 NS_ENSURE_TRUE(!aSelection ||
michael@0 438 nsTreeContentView::CanTrustTreeSelection(aSelection),
michael@0 439 NS_ERROR_DOM_SECURITY_ERR);
michael@0 440 mSelection = aSelection;
michael@0 441 return NS_OK;
michael@0 442 }
michael@0 443
michael@0 444 NS_IMETHODIMP
michael@0 445 nsXULTreeBuilder::GetRowProperties(int32_t aIndex, nsAString& aProps)
michael@0 446 {
michael@0 447 NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
michael@0 448 if (aIndex < 0 || aIndex >= mRows.Count())
michael@0 449 return NS_ERROR_INVALID_ARG;
michael@0 450
michael@0 451 nsCOMPtr<nsIContent> row;
michael@0 452 GetTemplateActionRowFor(aIndex, getter_AddRefs(row));
michael@0 453 if (row) {
michael@0 454 nsAutoString raw;
michael@0 455 row->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
michael@0 456
michael@0 457 if (!raw.IsEmpty()) {
michael@0 458 SubstituteText(mRows[aIndex]->mMatch->mResult, raw, aProps);
michael@0 459 }
michael@0 460 }
michael@0 461
michael@0 462 return NS_OK;
michael@0 463 }
michael@0 464
michael@0 465 NS_IMETHODIMP
michael@0 466 nsXULTreeBuilder::GetCellProperties(int32_t aRow, nsITreeColumn* aCol,
michael@0 467 nsAString& aProps)
michael@0 468 {
michael@0 469 NS_ENSURE_ARG_POINTER(aCol);
michael@0 470 NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
michael@0 471 if (aRow < 0 || aRow >= mRows.Count())
michael@0 472 return NS_ERROR_INVALID_ARG;
michael@0 473
michael@0 474 nsCOMPtr<nsIContent> cell;
michael@0 475 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
michael@0 476 if (cell) {
michael@0 477 nsAutoString raw;
michael@0 478 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
michael@0 479
michael@0 480 if (!raw.IsEmpty()) {
michael@0 481 SubstituteText(mRows[aRow]->mMatch->mResult, raw, aProps);
michael@0 482 }
michael@0 483 }
michael@0 484
michael@0 485 return NS_OK;
michael@0 486 }
michael@0 487
michael@0 488 NS_IMETHODIMP
michael@0 489 nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps)
michael@0 490 {
michael@0 491 NS_ENSURE_ARG_POINTER(aCol);
michael@0 492 // XXX sortactive fu
michael@0 493 return NS_OK;
michael@0 494 }
michael@0 495
michael@0 496 NS_IMETHODIMP
michael@0 497 nsXULTreeBuilder::IsContainer(int32_t aIndex, bool* aResult)
michael@0 498 {
michael@0 499 NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
michael@0 500 if (aIndex < 0 || aIndex >= mRows.Count())
michael@0 501 return NS_ERROR_INVALID_ARG;
michael@0 502
michael@0 503 nsTreeRows::iterator iter = mRows[aIndex];
michael@0 504
michael@0 505 bool isContainer;
michael@0 506 iter->mMatch->mResult->GetIsContainer(&isContainer);
michael@0 507
michael@0 508 iter->mContainerType = isContainer
michael@0 509 ? nsTreeRows::eContainerType_Container
michael@0 510 : nsTreeRows::eContainerType_Noncontainer;
michael@0 511
michael@0 512 *aResult = (iter->mContainerType == nsTreeRows::eContainerType_Container);
michael@0 513 return NS_OK;
michael@0 514 }
michael@0 515
michael@0 516 NS_IMETHODIMP
michael@0 517 nsXULTreeBuilder::IsContainerOpen(int32_t aIndex, bool* aOpen)
michael@0 518 {
michael@0 519 NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
michael@0 520 if (aIndex < 0 || aIndex >= mRows.Count())
michael@0 521 return NS_ERROR_INVALID_ARG;
michael@0 522
michael@0 523 nsTreeRows::iterator iter = mRows[aIndex];
michael@0 524
michael@0 525 if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
michael@0 526 bool isOpen;
michael@0 527 IsContainerOpen(iter->mMatch->mResult, &isOpen);
michael@0 528
michael@0 529 iter->mContainerState = isOpen
michael@0 530 ? nsTreeRows::eContainerState_Open
michael@0 531 : nsTreeRows::eContainerState_Closed;
michael@0 532 }
michael@0 533
michael@0 534 *aOpen = (iter->mContainerState == nsTreeRows::eContainerState_Open);
michael@0 535 return NS_OK;
michael@0 536 }
michael@0 537
michael@0 538 NS_IMETHODIMP
michael@0 539 nsXULTreeBuilder::IsContainerEmpty(int32_t aIndex, bool* aResult)
michael@0 540 {
michael@0 541 NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
michael@0 542 if (aIndex < 0 || aIndex >= mRows.Count())
michael@0 543 return NS_ERROR_INVALID_ARG;
michael@0 544
michael@0 545 nsTreeRows::iterator iter = mRows[aIndex];
michael@0 546 NS_ASSERTION(iter->mContainerType == nsTreeRows::eContainerType_Container,
michael@0 547 "asking for empty state on non-container");
michael@0 548
michael@0 549 // if recursion is disabled, pretend that the container is empty. This
michael@0 550 // ensures that folders are still displayed as such, yet won't display
michael@0 551 // their children
michael@0 552 if ((mFlags & eDontRecurse) && (iter->mMatch->mResult != mRootResult)) {
michael@0 553 *aResult = true;
michael@0 554 return NS_OK;
michael@0 555 }
michael@0 556
michael@0 557 if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) {
michael@0 558 bool isEmpty;
michael@0 559 iter->mMatch->mResult->GetIsEmpty(&isEmpty);
michael@0 560
michael@0 561 iter->mContainerFill = isEmpty
michael@0 562 ? nsTreeRows::eContainerFill_Empty
michael@0 563 : nsTreeRows::eContainerFill_Nonempty;
michael@0 564 }
michael@0 565
michael@0 566 *aResult = (iter->mContainerFill == nsTreeRows::eContainerFill_Empty);
michael@0 567 return NS_OK;
michael@0 568 }
michael@0 569
michael@0 570 NS_IMETHODIMP
michael@0 571 nsXULTreeBuilder::IsSeparator(int32_t aIndex, bool* aResult)
michael@0 572 {
michael@0 573 NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
michael@0 574 if (aIndex < 0 || aIndex >= mRows.Count())
michael@0 575 return NS_ERROR_INVALID_ARG;
michael@0 576
michael@0 577 nsAutoString type;
michael@0 578 nsTreeRows::Row& row = *(mRows[aIndex]);
michael@0 579 row.mMatch->mResult->GetType(type);
michael@0 580
michael@0 581 *aResult = type.EqualsLiteral("separator");
michael@0 582
michael@0 583 return NS_OK;
michael@0 584 }
michael@0 585
michael@0 586 NS_IMETHODIMP
michael@0 587 nsXULTreeBuilder::GetParentIndex(int32_t aRowIndex, int32_t* aResult)
michael@0 588 {
michael@0 589 NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
michael@0 590 if (aRowIndex < 0 || aRowIndex >= mRows.Count())
michael@0 591 return NS_ERROR_INVALID_ARG;
michael@0 592
michael@0 593 // Construct a path to the row
michael@0 594 nsTreeRows::iterator iter = mRows[aRowIndex];
michael@0 595
michael@0 596 // The parent of the row will be at the top of the path
michael@0 597 nsTreeRows::Subtree* parent = iter.GetParent();
michael@0 598
michael@0 599 // Now walk through our previous siblings, subtracting off each
michael@0 600 // one's subtree size
michael@0 601 int32_t index = iter.GetChildIndex();
michael@0 602 while (--index >= 0)
michael@0 603 aRowIndex -= mRows.GetSubtreeSizeFor(parent, index) + 1;
michael@0 604
michael@0 605 // Now the parent's index will be the first row's index, less one.
michael@0 606 *aResult = aRowIndex - 1;
michael@0 607 return NS_OK;
michael@0 608 }
michael@0 609
michael@0 610 NS_IMETHODIMP
michael@0 611 nsXULTreeBuilder::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* aResult)
michael@0 612 {
michael@0 613 NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
michael@0 614 if (aRowIndex < 0 || aRowIndex >= mRows.Count())
michael@0 615 return NS_ERROR_INVALID_ARG;
michael@0 616
michael@0 617 // Construct a path to the row
michael@0 618 nsTreeRows::iterator iter = mRows[aRowIndex];
michael@0 619
michael@0 620 // The parent of the row will be at the top of the path
michael@0 621 nsTreeRows::Subtree* parent = iter.GetParent();
michael@0 622
michael@0 623 // We have a next sibling if the child is not the last in the
michael@0 624 // subtree.
michael@0 625 *aResult = iter.GetChildIndex() != parent->Count() - 1;
michael@0 626 return NS_OK;
michael@0 627 }
michael@0 628
michael@0 629 NS_IMETHODIMP
michael@0 630 nsXULTreeBuilder::GetLevel(int32_t aRowIndex, int32_t* aResult)
michael@0 631 {
michael@0 632 NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
michael@0 633 if (aRowIndex < 0 || aRowIndex >= mRows.Count())
michael@0 634 return NS_ERROR_INVALID_ARG;
michael@0 635
michael@0 636 // Construct a path to the row; the ``level'' is the path length
michael@0 637 // less one.
michael@0 638 nsTreeRows::iterator iter = mRows[aRowIndex];
michael@0 639 *aResult = iter.GetDepth() - 1;
michael@0 640 return NS_OK;
michael@0 641 }
michael@0 642
michael@0 643 NS_IMETHODIMP
michael@0 644 nsXULTreeBuilder::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult)
michael@0 645 {
michael@0 646 NS_ENSURE_ARG_POINTER(aCol);
michael@0 647 NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
michael@0 648 if (aRow < 0 || aRow >= mRows.Count())
michael@0 649 return NS_ERROR_INVALID_ARG;
michael@0 650
michael@0 651 // Find the <cell> that corresponds to the column we want.
michael@0 652 nsCOMPtr<nsIContent> cell;
michael@0 653 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
michael@0 654 if (cell) {
michael@0 655 nsAutoString raw;
michael@0 656 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, raw);
michael@0 657
michael@0 658 SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
michael@0 659 }
michael@0 660 else
michael@0 661 aResult.Truncate();
michael@0 662
michael@0 663 return NS_OK;
michael@0 664 }
michael@0 665
michael@0 666
michael@0 667 NS_IMETHODIMP
michael@0 668 nsXULTreeBuilder::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* aResult)
michael@0 669 {
michael@0 670 NS_ENSURE_ARG_POINTER(aCol);
michael@0 671 NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
michael@0 672 if (aRow < 0 || aRow >= mRows.Count())
michael@0 673 return NS_ERROR_INVALID_ARG;
michael@0 674
michael@0 675 *aResult = nsITreeView::PROGRESS_NONE;
michael@0 676
michael@0 677 // Find the <cell> that corresponds to the column we want.
michael@0 678 nsCOMPtr<nsIContent> cell;
michael@0 679 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
michael@0 680 if (cell) {
michael@0 681 nsAutoString raw;
michael@0 682 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::mode, raw);
michael@0 683
michael@0 684 nsAutoString mode;
michael@0 685 SubstituteText(mRows[aRow]->mMatch->mResult, raw, mode);
michael@0 686
michael@0 687 if (mode.EqualsLiteral("normal"))
michael@0 688 *aResult = nsITreeView::PROGRESS_NORMAL;
michael@0 689 else if (mode.EqualsLiteral("undetermined"))
michael@0 690 *aResult = nsITreeView::PROGRESS_UNDETERMINED;
michael@0 691 }
michael@0 692
michael@0 693 return NS_OK;
michael@0 694 }
michael@0 695
michael@0 696 NS_IMETHODIMP
michael@0 697 nsXULTreeBuilder::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult)
michael@0 698 {
michael@0 699 NS_ENSURE_ARG_POINTER(aCol);
michael@0 700 NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
michael@0 701 if (aRow < 0 || aRow >= mRows.Count())
michael@0 702 return NS_ERROR_INVALID_ARG;
michael@0 703
michael@0 704 // Find the <cell> that corresponds to the column we want.
michael@0 705 nsCOMPtr<nsIContent> cell;
michael@0 706 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
michael@0 707 if (cell) {
michael@0 708 nsAutoString raw;
michael@0 709 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, raw);
michael@0 710
michael@0 711 SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
michael@0 712 }
michael@0 713 else
michael@0 714 aResult.Truncate();
michael@0 715
michael@0 716 return NS_OK;
michael@0 717 }
michael@0 718
michael@0 719 NS_IMETHODIMP
michael@0 720 nsXULTreeBuilder::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult)
michael@0 721 {
michael@0 722 NS_ENSURE_ARG_POINTER(aCol);
michael@0 723 NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
michael@0 724 if (aRow < 0 || aRow >= mRows.Count())
michael@0 725 return NS_ERROR_INVALID_ARG;
michael@0 726
michael@0 727 // Find the <cell> that corresponds to the column we want.
michael@0 728 nsCOMPtr<nsIContent> cell;
michael@0 729 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
michael@0 730 if (cell) {
michael@0 731 nsAutoString raw;
michael@0 732 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, raw);
michael@0 733
michael@0 734 SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
michael@0 735
michael@0 736 }
michael@0 737 else
michael@0 738 aResult.Truncate();
michael@0 739
michael@0 740 return NS_OK;
michael@0 741 }
michael@0 742
michael@0 743 NS_IMETHODIMP
michael@0 744 nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree)
michael@0 745 {
michael@0 746 mBoxObject = aTree;
michael@0 747
michael@0 748 // If this is teardown time, then we're done.
michael@0 749 if (!mBoxObject) {
michael@0 750 Uninit(false);
michael@0 751 return NS_OK;
michael@0 752 }
michael@0 753 NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
michael@0 754
michael@0 755 // Is our root's principal trusted?
michael@0 756 bool isTrusted = false;
michael@0 757 nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted);
michael@0 758 if (NS_SUCCEEDED(rv) && isTrusted) {
michael@0 759 // Get the datasource we intend to use to remember open state.
michael@0 760 nsAutoString datasourceStr;
michael@0 761 mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::statedatasource, datasourceStr);
michael@0 762
michael@0 763 // since we are trusted, use the user specified datasource
michael@0 764 // if non specified, use localstore, which gives us
michael@0 765 // persistence across sessions
michael@0 766 if (! datasourceStr.IsEmpty()) {
michael@0 767 gRDFService->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr).get(),
michael@0 768 getter_AddRefs(mPersistStateStore));
michael@0 769 }
michael@0 770 else {
michael@0 771 gRDFService->GetDataSource("rdf:local-store",
michael@0 772 getter_AddRefs(mPersistStateStore));
michael@0 773 }
michael@0 774 }
michael@0 775
michael@0 776 // Either no specific datasource was specified, or we failed
michael@0 777 // to get one because we are not trusted.
michael@0 778 //
michael@0 779 // XXX if it were possible to ``write an arbitrary datasource
michael@0 780 // back'', then we could also allow an untrusted document to
michael@0 781 // use a statedatasource from the same codebase.
michael@0 782 if (! mPersistStateStore) {
michael@0 783 mPersistStateStore =
michael@0 784 do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
michael@0 785 }
michael@0 786
michael@0 787 NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store");
michael@0 788 if (! mPersistStateStore)
michael@0 789 return NS_ERROR_FAILURE;
michael@0 790
michael@0 791 Rebuild();
michael@0 792
michael@0 793 EnsureSortVariables();
michael@0 794 if (mSortVariable)
michael@0 795 SortSubtree(mRows.GetRoot());
michael@0 796
michael@0 797 return NS_OK;
michael@0 798 }
michael@0 799
michael@0 800 NS_IMETHODIMP
michael@0 801 nsXULTreeBuilder::ToggleOpenState(int32_t aIndex)
michael@0 802 {
michael@0 803 if (aIndex < 0 || aIndex >= mRows.Count())
michael@0 804 return NS_ERROR_INVALID_ARG;
michael@0 805
michael@0 806 nsIXULTemplateResult* result = mRows[aIndex]->mMatch->mResult;
michael@0 807 if (! result)
michael@0 808 return NS_ERROR_FAILURE;
michael@0 809
michael@0 810 if (mFlags & eDontRecurse)
michael@0 811 return NS_OK;
michael@0 812
michael@0 813 if (result && result != mRootResult) {
michael@0 814 // don't open containers if child processing isn't allowed
michael@0 815 bool mayProcessChildren;
michael@0 816 nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
michael@0 817 if (NS_FAILED(rv) || !mayProcessChildren)
michael@0 818 return rv;
michael@0 819 }
michael@0 820
michael@0 821 uint32_t count = mObservers.Count();
michael@0 822 for (uint32_t i = 0; i < count; ++i) {
michael@0 823 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 824 if (observer)
michael@0 825 observer->OnToggleOpenState(aIndex);
michael@0 826 }
michael@0 827
michael@0 828 if (mPersistStateStore) {
michael@0 829 bool isOpen;
michael@0 830 IsContainerOpen(aIndex, &isOpen);
michael@0 831
michael@0 832 nsCOMPtr<nsIRDFResource> container;
michael@0 833 GetResourceFor(aIndex, getter_AddRefs(container));
michael@0 834 if (! container)
michael@0 835 return NS_ERROR_FAILURE;
michael@0 836
michael@0 837 bool hasProperty;
michael@0 838 IsContainerOpen(container, &hasProperty);
michael@0 839
michael@0 840 if (isOpen) {
michael@0 841 if (hasProperty) {
michael@0 842 mPersistStateStore->Unassert(container,
michael@0 843 nsXULContentUtils::NC_open,
michael@0 844 nsXULContentUtils::true_);
michael@0 845 }
michael@0 846
michael@0 847 CloseContainer(aIndex);
michael@0 848 }
michael@0 849 else {
michael@0 850 if (! hasProperty) {
michael@0 851 mPersistStateStore->Assert(container,
michael@0 852 nsXULContentUtils::NC_open,
michael@0 853 nsXULContentUtils::true_,
michael@0 854 true);
michael@0 855 }
michael@0 856
michael@0 857 OpenContainer(aIndex, result);
michael@0 858 }
michael@0 859 }
michael@0 860
michael@0 861 return NS_OK;
michael@0 862 }
michael@0 863
michael@0 864 NS_IMETHODIMP
michael@0 865 nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol)
michael@0 866 {
michael@0 867 NS_ENSURE_ARG_POINTER(aCol);
michael@0 868 nsCOMPtr<nsIDOMElement> element;
michael@0 869 aCol->GetElement(getter_AddRefs(element));
michael@0 870
michael@0 871 nsAutoString id;
michael@0 872 aCol->GetId(id);
michael@0 873
michael@0 874 uint32_t count = mObservers.Count();
michael@0 875 for (uint32_t i = 0; i < count; ++i) {
michael@0 876 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 877 if (observer)
michael@0 878 observer->OnCycleHeader(id.get(), element);
michael@0 879 }
michael@0 880
michael@0 881 return Sort(element);
michael@0 882 }
michael@0 883
michael@0 884 NS_IMETHODIMP
michael@0 885 nsXULTreeBuilder::SelectionChanged()
michael@0 886 {
michael@0 887 uint32_t count = mObservers.Count();
michael@0 888 for (uint32_t i = 0; i < count; ++i) {
michael@0 889 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 890 if (observer)
michael@0 891 observer->OnSelectionChanged();
michael@0 892 }
michael@0 893
michael@0 894 return NS_OK;
michael@0 895 }
michael@0 896
michael@0 897 NS_IMETHODIMP
michael@0 898 nsXULTreeBuilder::CycleCell(int32_t aRow, nsITreeColumn* aCol)
michael@0 899 {
michael@0 900 NS_ENSURE_ARG_POINTER(aCol);
michael@0 901
michael@0 902 nsAutoString id;
michael@0 903 aCol->GetId(id);
michael@0 904
michael@0 905 uint32_t count = mObservers.Count();
michael@0 906 for (uint32_t i = 0; i < count; ++i) {
michael@0 907 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 908 if (observer)
michael@0 909 observer->OnCycleCell(aRow, id.get());
michael@0 910 }
michael@0 911
michael@0 912 return NS_OK;
michael@0 913 }
michael@0 914
michael@0 915 NS_IMETHODIMP
michael@0 916 nsXULTreeBuilder::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
michael@0 917 {
michael@0 918 *_retval = true;
michael@0 919 NS_ENSURE_ARG_POINTER(aCol);
michael@0 920 NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
michael@0 921 if (aRow < 0 || aRow >= mRows.Count())
michael@0 922 return NS_ERROR_INVALID_ARG;
michael@0 923
michael@0 924 // Find the <cell> that corresponds to the column we want.
michael@0 925 nsCOMPtr<nsIContent> cell;
michael@0 926 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
michael@0 927 if (cell) {
michael@0 928 nsAutoString raw;
michael@0 929 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::editable, raw);
michael@0 930
michael@0 931 nsAutoString editable;
michael@0 932 SubstituteText(mRows[aRow]->mMatch->mResult, raw, editable);
michael@0 933
michael@0 934 if (editable.EqualsLiteral("false"))
michael@0 935 *_retval = false;
michael@0 936 }
michael@0 937
michael@0 938 return NS_OK;
michael@0 939 }
michael@0 940
michael@0 941 NS_IMETHODIMP
michael@0 942 nsXULTreeBuilder::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* _retval)
michael@0 943 {
michael@0 944 NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
michael@0 945 if (aRow < 0 || aRow >= mRows.Count())
michael@0 946 return NS_ERROR_INVALID_ARG;
michael@0 947
michael@0 948 *_retval = true;
michael@0 949
michael@0 950 // Find the <cell> that corresponds to the column we want.
michael@0 951 nsCOMPtr<nsIContent> cell;
michael@0 952 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
michael@0 953 if (cell) {
michael@0 954 nsAutoString raw;
michael@0 955 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::selectable, raw);
michael@0 956
michael@0 957 nsAutoString selectable;
michael@0 958 SubstituteText(mRows[aRow]->mMatch->mResult, raw, selectable);
michael@0 959
michael@0 960 if (selectable.EqualsLiteral("false"))
michael@0 961 *_retval = false;
michael@0 962 }
michael@0 963
michael@0 964 return NS_OK;
michael@0 965 }
michael@0 966
michael@0 967 NS_IMETHODIMP
michael@0 968 nsXULTreeBuilder::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
michael@0 969 {
michael@0 970 NS_ENSURE_ARG_POINTER(aCol);
michael@0 971 return NS_OK;
michael@0 972 }
michael@0 973
michael@0 974 NS_IMETHODIMP
michael@0 975 nsXULTreeBuilder::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue)
michael@0 976 {
michael@0 977 NS_ENSURE_ARG_POINTER(aCol);
michael@0 978 return NS_OK;
michael@0 979 }
michael@0 980
michael@0 981 NS_IMETHODIMP
michael@0 982 nsXULTreeBuilder::PerformAction(const char16_t* aAction)
michael@0 983 {
michael@0 984 uint32_t count = mObservers.Count();
michael@0 985 for (uint32_t i = 0; i < count; ++i) {
michael@0 986 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 987 if (observer)
michael@0 988 observer->OnPerformAction(aAction);
michael@0 989 }
michael@0 990
michael@0 991 return NS_OK;
michael@0 992 }
michael@0 993
michael@0 994 NS_IMETHODIMP
michael@0 995 nsXULTreeBuilder::PerformActionOnRow(const char16_t* aAction, int32_t aRow)
michael@0 996 {
michael@0 997 uint32_t count = mObservers.Count();
michael@0 998 for (uint32_t i = 0; i < count; ++i) {
michael@0 999 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 1000 if (observer)
michael@0 1001 observer->OnPerformActionOnRow(aAction, aRow);
michael@0 1002 }
michael@0 1003
michael@0 1004 return NS_OK;
michael@0 1005 }
michael@0 1006
michael@0 1007 NS_IMETHODIMP
michael@0 1008 nsXULTreeBuilder::PerformActionOnCell(const char16_t* aAction, int32_t aRow, nsITreeColumn* aCol)
michael@0 1009 {
michael@0 1010 NS_ENSURE_ARG_POINTER(aCol);
michael@0 1011 nsAutoString id;
michael@0 1012 aCol->GetId(id);
michael@0 1013
michael@0 1014 uint32_t count = mObservers.Count();
michael@0 1015 for (uint32_t i = 0; i < count; ++i) {
michael@0 1016 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 1017 if (observer)
michael@0 1018 observer->OnPerformActionOnCell(aAction, aRow, id.get());
michael@0 1019 }
michael@0 1020
michael@0 1021 return NS_OK;
michael@0 1022 }
michael@0 1023
michael@0 1024
michael@0 1025 void
michael@0 1026 nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode* aNode)
michael@0 1027 {
michael@0 1028 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
michael@0 1029 mObservers.Clear();
michael@0 1030
michael@0 1031 nsXULTemplateBuilder::NodeWillBeDestroyed(aNode);
michael@0 1032 }
michael@0 1033
michael@0 1034 NS_IMETHODIMP
michael@0 1035 nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource* aResource,
michael@0 1036 nsIAtom* aTag,
michael@0 1037 bool* aGenerated)
michael@0 1038 {
michael@0 1039 *aGenerated = false;
michael@0 1040 NS_ENSURE_ARG_POINTER(aResource);
michael@0 1041
michael@0 1042 if (!mRootResult)
michael@0 1043 return NS_OK;
michael@0 1044
michael@0 1045 nsCOMPtr<nsIRDFResource> rootresource;
michael@0 1046 nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource));
michael@0 1047 if (NS_FAILED(rv))
michael@0 1048 return rv;
michael@0 1049
michael@0 1050 if (aResource == rootresource ||
michael@0 1051 mRows.FindByResource(aResource) != mRows.Last())
michael@0 1052 *aGenerated = true;
michael@0 1053
michael@0 1054 return NS_OK;
michael@0 1055 }
michael@0 1056
michael@0 1057 bool
michael@0 1058 nsXULTreeBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult,
michael@0 1059 nsCOMArray<nsIContent>** aLocations)
michael@0 1060 {
michael@0 1061 *aLocations = nullptr;
michael@0 1062
michael@0 1063 // Get the reference point and check if it is an open container. Rows
michael@0 1064 // should not be generated otherwise.
michael@0 1065
michael@0 1066 nsAutoString ref;
michael@0 1067 nsresult rv = aResult->GetBindingFor(mRefVariable, ref);
michael@0 1068 if (NS_FAILED(rv) || ref.IsEmpty())
michael@0 1069 return false;
michael@0 1070
michael@0 1071 nsCOMPtr<nsIRDFResource> container;
michael@0 1072 rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
michael@0 1073 if (NS_FAILED(rv))
michael@0 1074 return false;
michael@0 1075
michael@0 1076 // Can always insert into the root resource
michael@0 1077 if (container == mRows.GetRootResource())
michael@0 1078 return true;
michael@0 1079
michael@0 1080 nsTreeRows::iterator iter = mRows.FindByResource(container);
michael@0 1081 if (iter == mRows.Last())
michael@0 1082 return false;
michael@0 1083
michael@0 1084 return (iter->mContainerState == nsTreeRows::eContainerState_Open);
michael@0 1085 }
michael@0 1086
michael@0 1087 nsresult
michael@0 1088 nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
michael@0 1089 nsTemplateMatch* aNewMatch,
michael@0 1090 nsTemplateRule* aNewMatchRule,
michael@0 1091 void *aLocation)
michael@0 1092 {
michael@0 1093 if (! mBoxObject)
michael@0 1094 return NS_OK;
michael@0 1095
michael@0 1096 if (aOldResult) {
michael@0 1097 // Grovel through the rows looking for oldresult.
michael@0 1098 nsTreeRows::iterator iter = mRows.Find(aOldResult);
michael@0 1099
michael@0 1100 NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
michael@0 1101 if (iter == mRows.Last())
michael@0 1102 return NS_ERROR_FAILURE;
michael@0 1103
michael@0 1104 // Remove the rows from the view
michael@0 1105 int32_t row = iter.GetRowIndex();
michael@0 1106
michael@0 1107 // If the row contains children, remove the matches from the
michael@0 1108 // children so that they can be regenerated again if the element
michael@0 1109 // gets added back.
michael@0 1110 int32_t delta = mRows.GetSubtreeSizeFor(iter);
michael@0 1111 if (delta)
michael@0 1112 RemoveMatchesFor(*(iter->mSubtree));
michael@0 1113
michael@0 1114 if (mRows.RemoveRowAt(iter) == 0 && iter.GetRowIndex() >= 0) {
michael@0 1115
michael@0 1116 // In this case iter now points to its parent
michael@0 1117 // Invalidate the row's cached fill state
michael@0 1118 iter->mContainerFill = nsTreeRows::eContainerFill_Unknown;
michael@0 1119
michael@0 1120 nsCOMPtr<nsITreeColumns> cols;
michael@0 1121 mBoxObject->GetColumns(getter_AddRefs(cols));
michael@0 1122 if (cols) {
michael@0 1123 nsCOMPtr<nsITreeColumn> primaryCol;
michael@0 1124 cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
michael@0 1125 if (primaryCol)
michael@0 1126 mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol);
michael@0 1127 }
michael@0 1128 }
michael@0 1129
michael@0 1130 // Notify the box object
michael@0 1131 mBoxObject->RowCountChanged(row, -delta - 1);
michael@0 1132 }
michael@0 1133
michael@0 1134 if (aNewMatch && aNewMatch->mResult) {
michael@0 1135 // Insertion.
michael@0 1136 int32_t row = -1;
michael@0 1137 nsTreeRows::Subtree* parent = nullptr;
michael@0 1138 nsIXULTemplateResult* result = aNewMatch->mResult;
michael@0 1139
michael@0 1140 nsAutoString ref;
michael@0 1141 nsresult rv = result->GetBindingFor(mRefVariable, ref);
michael@0 1142 if (NS_FAILED(rv) || ref.IsEmpty())
michael@0 1143 return rv;
michael@0 1144
michael@0 1145 nsCOMPtr<nsIRDFResource> container;
michael@0 1146 rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
michael@0 1147 if (NS_FAILED(rv))
michael@0 1148 return rv;
michael@0 1149
michael@0 1150 if (container != mRows.GetRootResource()) {
michael@0 1151 nsTreeRows::iterator iter = mRows.FindByResource(container);
michael@0 1152 row = iter.GetRowIndex();
michael@0 1153
michael@0 1154 NS_ASSERTION(iter != mRows.Last(), "couldn't find container row");
michael@0 1155 if (iter == mRows.Last())
michael@0 1156 return NS_ERROR_FAILURE;
michael@0 1157
michael@0 1158 // Use the persist store to remember if the container
michael@0 1159 // is open or closed.
michael@0 1160 bool open = false;
michael@0 1161 IsContainerOpen(row, &open);
michael@0 1162
michael@0 1163 // If it's open, make sure that we've got a subtree structure ready.
michael@0 1164 if (open)
michael@0 1165 parent = mRows.EnsureSubtreeFor(iter);
michael@0 1166
michael@0 1167 // We know something has just been inserted into the
michael@0 1168 // container, so whether its open or closed, make sure
michael@0 1169 // that we've got our tree row's state correct.
michael@0 1170 if ((iter->mContainerType != nsTreeRows::eContainerType_Container) ||
michael@0 1171 (iter->mContainerFill != nsTreeRows::eContainerFill_Nonempty)) {
michael@0 1172 iter->mContainerType = nsTreeRows::eContainerType_Container;
michael@0 1173 iter->mContainerFill = nsTreeRows::eContainerFill_Nonempty;
michael@0 1174 mBoxObject->InvalidateRow(iter.GetRowIndex());
michael@0 1175 }
michael@0 1176 }
michael@0 1177 else {
michael@0 1178 parent = mRows.GetRoot();
michael@0 1179 }
michael@0 1180
michael@0 1181 if (parent) {
michael@0 1182 // If we get here, then we're inserting into an open
michael@0 1183 // container. By default, place the new element at the
michael@0 1184 // end of the container
michael@0 1185 int32_t index = parent->Count();
michael@0 1186
michael@0 1187 if (mSortVariable) {
michael@0 1188 // Figure out where to put the new element by doing an
michael@0 1189 // insertion sort.
michael@0 1190 int32_t left = 0;
michael@0 1191 int32_t right = index;
michael@0 1192
michael@0 1193 while (left < right) {
michael@0 1194 index = (left + right) / 2;
michael@0 1195 int32_t cmp = CompareResults((*parent)[index].mMatch->mResult, result);
michael@0 1196 if (cmp < 0)
michael@0 1197 left = ++index;
michael@0 1198 else if (cmp > 0)
michael@0 1199 right = index;
michael@0 1200 else
michael@0 1201 break;
michael@0 1202 }
michael@0 1203 }
michael@0 1204
michael@0 1205 nsTreeRows::iterator iter =
michael@0 1206 mRows.InsertRowAt(aNewMatch, parent, index);
michael@0 1207
michael@0 1208 mBoxObject->RowCountChanged(iter.GetRowIndex(), +1);
michael@0 1209
michael@0 1210 // See if this newly added row is open; in which case,
michael@0 1211 // recursively add its children to the tree, too.
michael@0 1212
michael@0 1213 if (mFlags & eDontRecurse)
michael@0 1214 return NS_OK;
michael@0 1215
michael@0 1216 if (result != mRootResult) {
michael@0 1217 // don't open containers if child processing isn't allowed
michael@0 1218 bool mayProcessChildren;
michael@0 1219 nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
michael@0 1220 if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
michael@0 1221 }
michael@0 1222
michael@0 1223 bool open;
michael@0 1224 IsContainerOpen(result, &open);
michael@0 1225 if (open)
michael@0 1226 OpenContainer(iter.GetRowIndex(), result);
michael@0 1227 }
michael@0 1228 }
michael@0 1229
michael@0 1230 return NS_OK;
michael@0 1231 }
michael@0 1232
michael@0 1233 nsresult
michael@0 1234 nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
michael@0 1235 {
michael@0 1236 if (mBoxObject) {
michael@0 1237 // XXX we could be more conservative and just invalidate the cells
michael@0 1238 // that got whacked...
michael@0 1239
michael@0 1240 nsTreeRows::iterator iter = mRows.Find(aResult);
michael@0 1241
michael@0 1242 NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
michael@0 1243 if (iter == mRows.Last())
michael@0 1244 return NS_ERROR_FAILURE;
michael@0 1245
michael@0 1246 int32_t row = iter.GetRowIndex();
michael@0 1247 if (row >= 0)
michael@0 1248 mBoxObject->InvalidateRow(row);
michael@0 1249
michael@0 1250 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
michael@0 1251 ("xultemplate[%p] => row %d", this, row));
michael@0 1252 }
michael@0 1253
michael@0 1254 return NS_OK;
michael@0 1255 }
michael@0 1256
michael@0 1257 //----------------------------------------------------------------------
michael@0 1258
michael@0 1259 nsresult
michael@0 1260 nsXULTreeBuilder::EnsureSortVariables()
michael@0 1261 {
michael@0 1262 // Grovel through <treecols> kids to find the <treecol>
michael@0 1263 // with the sort attributes.
michael@0 1264 nsCOMPtr<nsIContent> treecols;
michael@0 1265
michael@0 1266 nsXULContentUtils::FindChildByTag(mRoot, kNameSpaceID_XUL,
michael@0 1267 nsGkAtoms::treecols,
michael@0 1268 getter_AddRefs(treecols));
michael@0 1269
michael@0 1270 if (!treecols)
michael@0 1271 return NS_OK;
michael@0 1272
michael@0 1273 for (nsIContent* child = treecols->GetFirstChild();
michael@0 1274 child;
michael@0 1275 child = child->GetNextSibling()) {
michael@0 1276
michael@0 1277 if (child->NodeInfo()->Equals(nsGkAtoms::treecol,
michael@0 1278 kNameSpaceID_XUL)) {
michael@0 1279 if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortActive,
michael@0 1280 nsGkAtoms::_true, eCaseMatters)) {
michael@0 1281 nsAutoString sort;
michael@0 1282 child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
michael@0 1283 if (! sort.IsEmpty()) {
michael@0 1284 mSortVariable = do_GetAtom(sort);
michael@0 1285
michael@0 1286 static nsIContent::AttrValuesArray strings[] =
michael@0 1287 {&nsGkAtoms::ascending, &nsGkAtoms::descending, nullptr};
michael@0 1288 switch (child->FindAttrValueIn(kNameSpaceID_None,
michael@0 1289 nsGkAtoms::sortDirection,
michael@0 1290 strings, eCaseMatters)) {
michael@0 1291 case 0: mSortDirection = eDirection_Ascending; break;
michael@0 1292 case 1: mSortDirection = eDirection_Descending; break;
michael@0 1293 default: mSortDirection = eDirection_Natural; break;
michael@0 1294 }
michael@0 1295 }
michael@0 1296 break;
michael@0 1297 }
michael@0 1298 }
michael@0 1299 }
michael@0 1300
michael@0 1301 return NS_OK;
michael@0 1302 }
michael@0 1303
michael@0 1304 nsresult
michael@0 1305 nsXULTreeBuilder::RebuildAll()
michael@0 1306 {
michael@0 1307 NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
michael@0 1308
michael@0 1309 nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
michael@0 1310
michael@0 1311 // Bail out early if we are being torn down.
michael@0 1312 if (!doc)
michael@0 1313 return NS_OK;
michael@0 1314
michael@0 1315 if (! mQueryProcessor)
michael@0 1316 return NS_OK;
michael@0 1317
michael@0 1318 if (mBoxObject) {
michael@0 1319 mBoxObject->BeginUpdateBatch();
michael@0 1320 }
michael@0 1321
michael@0 1322 if (mQueriesCompiled) {
michael@0 1323 Uninit(false);
michael@0 1324 }
michael@0 1325 else if (mBoxObject) {
michael@0 1326 int32_t count = mRows.Count();
michael@0 1327 mRows.Clear();
michael@0 1328 mBoxObject->RowCountChanged(0, -count);
michael@0 1329 }
michael@0 1330
michael@0 1331 nsresult rv = CompileQueries();
michael@0 1332 if (NS_SUCCEEDED(rv) && mQuerySets.Length() > 0) {
michael@0 1333 // Seed the rule network with assignments for the tree row variable
michael@0 1334 nsAutoString ref;
michael@0 1335 mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref);
michael@0 1336 if (!ref.IsEmpty()) {
michael@0 1337 rv = mQueryProcessor->TranslateRef(mDataSource, ref,
michael@0 1338 getter_AddRefs(mRootResult));
michael@0 1339 if (NS_SUCCEEDED(rv) && mRootResult) {
michael@0 1340 OpenContainer(-1, mRootResult);
michael@0 1341
michael@0 1342 nsCOMPtr<nsIRDFResource> rootResource;
michael@0 1343 GetResultResource(mRootResult, getter_AddRefs(rootResource));
michael@0 1344
michael@0 1345 mRows.SetRootResource(rootResource);
michael@0 1346 }
michael@0 1347 }
michael@0 1348 }
michael@0 1349
michael@0 1350 if (mBoxObject) {
michael@0 1351 mBoxObject->EndUpdateBatch();
michael@0 1352 }
michael@0 1353
michael@0 1354 return rv;
michael@0 1355 }
michael@0 1356
michael@0 1357 nsresult
michael@0 1358 nsXULTreeBuilder::GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult)
michael@0 1359 {
michael@0 1360 // Get the template in the DOM from which we're supposed to
michael@0 1361 // generate text
michael@0 1362 nsTreeRows::Row& row = *(mRows[aRow]);
michael@0 1363
michael@0 1364 // The match stores the indices of the rule and query to use. Use these
michael@0 1365 // to look up the right nsTemplateRule and use that rule's action to get
michael@0 1366 // the treerow in the template.
michael@0 1367 int16_t ruleindex = row.mMatch->RuleIndex();
michael@0 1368 if (ruleindex >= 0) {
michael@0 1369 nsTemplateQuerySet* qs = mQuerySets[row.mMatch->QuerySetPriority()];
michael@0 1370 nsTemplateRule* rule = qs->GetRuleAt(ruleindex);
michael@0 1371 if (rule) {
michael@0 1372 nsCOMPtr<nsIContent> children;
michael@0 1373 nsXULContentUtils::FindChildByTag(rule->GetAction(), kNameSpaceID_XUL,
michael@0 1374 nsGkAtoms::treechildren,
michael@0 1375 getter_AddRefs(children));
michael@0 1376 if (children) {
michael@0 1377 nsCOMPtr<nsIContent> item;
michael@0 1378 nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL,
michael@0 1379 nsGkAtoms::treeitem,
michael@0 1380 getter_AddRefs(item));
michael@0 1381 if (item)
michael@0 1382 return nsXULContentUtils::FindChildByTag(item,
michael@0 1383 kNameSpaceID_XUL,
michael@0 1384 nsGkAtoms::treerow,
michael@0 1385 aResult);
michael@0 1386 }
michael@0 1387 }
michael@0 1388 }
michael@0 1389
michael@0 1390 *aResult = nullptr;
michael@0 1391 return NS_OK;
michael@0 1392 }
michael@0 1393
michael@0 1394 nsresult
michael@0 1395 nsXULTreeBuilder::GetTemplateActionCellFor(int32_t aRow,
michael@0 1396 nsITreeColumn* aCol,
michael@0 1397 nsIContent** aResult)
michael@0 1398 {
michael@0 1399 *aResult = nullptr;
michael@0 1400
michael@0 1401 if (!aCol) return NS_ERROR_INVALID_ARG;
michael@0 1402
michael@0 1403 nsCOMPtr<nsIContent> row;
michael@0 1404 GetTemplateActionRowFor(aRow, getter_AddRefs(row));
michael@0 1405 if (row) {
michael@0 1406 nsCOMPtr<nsIAtom> colAtom;
michael@0 1407 int32_t colIndex;
michael@0 1408 aCol->GetAtom(getter_AddRefs(colAtom));
michael@0 1409 aCol->GetIndex(&colIndex);
michael@0 1410
michael@0 1411 uint32_t j = 0;
michael@0 1412 for (nsIContent* child = row->GetFirstChild();
michael@0 1413 child;
michael@0 1414 child = child->GetNextSibling()) {
michael@0 1415
michael@0 1416 if (child->NodeInfo()->Equals(nsGkAtoms::treecell,
michael@0 1417 kNameSpaceID_XUL)) {
michael@0 1418 if (colAtom &&
michael@0 1419 child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
michael@0 1420 colAtom, eCaseMatters)) {
michael@0 1421 *aResult = child;
michael@0 1422 break;
michael@0 1423 }
michael@0 1424 else if (j == (uint32_t)colIndex)
michael@0 1425 *aResult = child;
michael@0 1426 j++;
michael@0 1427 }
michael@0 1428 }
michael@0 1429 }
michael@0 1430 NS_IF_ADDREF(*aResult);
michael@0 1431
michael@0 1432 return NS_OK;
michael@0 1433 }
michael@0 1434
michael@0 1435 nsresult
michael@0 1436 nsXULTreeBuilder::GetResourceFor(int32_t aRow, nsIRDFResource** aResource)
michael@0 1437 {
michael@0 1438 nsTreeRows::Row& row = *(mRows[aRow]);
michael@0 1439 return GetResultResource(row.mMatch->mResult, aResource);
michael@0 1440 }
michael@0 1441
michael@0 1442 nsresult
michael@0 1443 nsXULTreeBuilder::OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult)
michael@0 1444 {
michael@0 1445 // A row index of -1 in this case means ``open tree body''
michael@0 1446 NS_ASSERTION(aIndex >= -1 && aIndex < mRows.Count(), "bad row");
michael@0 1447 if (aIndex < -1 || aIndex >= mRows.Count())
michael@0 1448 return NS_ERROR_INVALID_ARG;
michael@0 1449
michael@0 1450 nsTreeRows::Subtree* container;
michael@0 1451
michael@0 1452 if (aIndex >= 0) {
michael@0 1453 nsTreeRows::iterator iter = mRows[aIndex];
michael@0 1454 container = mRows.EnsureSubtreeFor(iter.GetParent(),
michael@0 1455 iter.GetChildIndex());
michael@0 1456
michael@0 1457 iter->mContainerState = nsTreeRows::eContainerState_Open;
michael@0 1458 }
michael@0 1459 else
michael@0 1460 container = mRows.GetRoot();
michael@0 1461
michael@0 1462 if (! container)
michael@0 1463 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1464
michael@0 1465 int32_t count;
michael@0 1466 OpenSubtreeOf(container, aIndex, aResult, &count);
michael@0 1467
michael@0 1468 // Notify the box object
michael@0 1469 if (mBoxObject) {
michael@0 1470 if (aIndex >= 0)
michael@0 1471 mBoxObject->InvalidateRow(aIndex);
michael@0 1472
michael@0 1473 if (count)
michael@0 1474 mBoxObject->RowCountChanged(aIndex + 1, count);
michael@0 1475 }
michael@0 1476
michael@0 1477 return NS_OK;
michael@0 1478 }
michael@0 1479
michael@0 1480 nsresult
michael@0 1481 nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
michael@0 1482 int32_t aIndex,
michael@0 1483 nsIXULTemplateResult *aResult,
michael@0 1484 int32_t* aDelta)
michael@0 1485 {
michael@0 1486 nsAutoTArray<int32_t, 8> open;
michael@0 1487 int32_t count = 0;
michael@0 1488
michael@0 1489 int32_t rulecount = mQuerySets.Length();
michael@0 1490
michael@0 1491 for (int32_t r = 0; r < rulecount; r++) {
michael@0 1492 nsTemplateQuerySet* queryset = mQuerySets[r];
michael@0 1493 OpenSubtreeForQuerySet(aSubtree, aIndex, aResult, queryset, &count, open);
michael@0 1494 }
michael@0 1495
michael@0 1496 // Now recursively deal with any open sub-containers that just got
michael@0 1497 // inserted. We need to do this back-to-front to avoid skewing offsets.
michael@0 1498 for (int32_t i = open.Length() - 1; i >= 0; --i) {
michael@0 1499 int32_t index = open[i];
michael@0 1500
michael@0 1501 nsTreeRows::Subtree* child =
michael@0 1502 mRows.EnsureSubtreeFor(aSubtree, index);
michael@0 1503
michael@0 1504 nsIXULTemplateResult* result = (*aSubtree)[index].mMatch->mResult;
michael@0 1505
michael@0 1506 int32_t delta;
michael@0 1507 OpenSubtreeOf(child, aIndex + index, result, &delta);
michael@0 1508 count += delta;
michael@0 1509 }
michael@0 1510
michael@0 1511 // Sort the container.
michael@0 1512 if (mSortVariable) {
michael@0 1513 NS_QuickSort(mRows.GetRowsFor(aSubtree),
michael@0 1514 aSubtree->Count(),
michael@0 1515 sizeof(nsTreeRows::Row),
michael@0 1516 Compare,
michael@0 1517 this);
michael@0 1518 }
michael@0 1519
michael@0 1520 *aDelta = count;
michael@0 1521 return NS_OK;
michael@0 1522 }
michael@0 1523
michael@0 1524 nsresult
michael@0 1525 nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
michael@0 1526 int32_t aIndex,
michael@0 1527 nsIXULTemplateResult* aResult,
michael@0 1528 nsTemplateQuerySet* aQuerySet,
michael@0 1529 int32_t* aDelta,
michael@0 1530 nsTArray<int32_t>& open)
michael@0 1531 {
michael@0 1532 int32_t count = *aDelta;
michael@0 1533
michael@0 1534 nsCOMPtr<nsISimpleEnumerator> results;
michael@0 1535 nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult,
michael@0 1536 aQuerySet->mCompiledQuery,
michael@0 1537 getter_AddRefs(results));
michael@0 1538 if (NS_FAILED(rv))
michael@0 1539 return rv;
michael@0 1540
michael@0 1541 bool hasMoreResults;
michael@0 1542 rv = results->HasMoreElements(&hasMoreResults);
michael@0 1543
michael@0 1544 for (; NS_SUCCEEDED(rv) && hasMoreResults;
michael@0 1545 rv = results->HasMoreElements(&hasMoreResults)) {
michael@0 1546 nsCOMPtr<nsISupports> nr;
michael@0 1547 rv = results->GetNext(getter_AddRefs(nr));
michael@0 1548 if (NS_FAILED(rv))
michael@0 1549 return rv;
michael@0 1550
michael@0 1551 nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr);
michael@0 1552 if (!nextresult)
michael@0 1553 return NS_ERROR_UNEXPECTED;
michael@0 1554
michael@0 1555 nsCOMPtr<nsIRDFResource> resultid;
michael@0 1556 rv = GetResultResource(nextresult, getter_AddRefs(resultid));
michael@0 1557 if (NS_FAILED(rv))
michael@0 1558 return rv;
michael@0 1559
michael@0 1560 if (! resultid)
michael@0 1561 continue;
michael@0 1562
michael@0 1563 // check if there is already an existing match. If so, a previous
michael@0 1564 // query already generated content so the match is just added to the
michael@0 1565 // end of the set of matches.
michael@0 1566
michael@0 1567 bool generateContent = true;
michael@0 1568
michael@0 1569 nsTemplateMatch* prevmatch = nullptr;
michael@0 1570 nsTemplateMatch* existingmatch = nullptr;
michael@0 1571 if (mMatchMap.Get(resultid, &existingmatch)){
michael@0 1572 // check if there is an existing match that matched a rule
michael@0 1573 while (existingmatch) {
michael@0 1574 if (existingmatch->IsActive())
michael@0 1575 generateContent = false;
michael@0 1576 prevmatch = existingmatch;
michael@0 1577 existingmatch = existingmatch->mNext;
michael@0 1578 }
michael@0 1579 }
michael@0 1580
michael@0 1581 nsTemplateMatch *newmatch =
michael@0 1582 nsTemplateMatch::Create(aQuerySet->Priority(), nextresult, nullptr);
michael@0 1583 if (!newmatch)
michael@0 1584 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1585
michael@0 1586 if (generateContent) {
michael@0 1587 // Don't allow cyclic graphs to get our knickers in a knot.
michael@0 1588 bool cyclic = false;
michael@0 1589
michael@0 1590 if (aIndex >= 0) {
michael@0 1591 for (nsTreeRows::iterator iter = mRows[aIndex]; iter.GetDepth() > 0; iter.Pop()) {
michael@0 1592 nsCOMPtr<nsIRDFResource> parentid;
michael@0 1593 rv = GetResultResource(iter->mMatch->mResult, getter_AddRefs(parentid));
michael@0 1594 if (NS_FAILED(rv)) {
michael@0 1595 nsTemplateMatch::Destroy(newmatch, false);
michael@0 1596 return rv;
michael@0 1597 }
michael@0 1598
michael@0 1599 if (resultid == parentid) {
michael@0 1600 cyclic = true;
michael@0 1601 break;
michael@0 1602 }
michael@0 1603 }
michael@0 1604 }
michael@0 1605
michael@0 1606 if (cyclic) {
michael@0 1607 NS_WARNING("tree cannot handle cyclic graphs");
michael@0 1608 nsTemplateMatch::Destroy(newmatch, false);
michael@0 1609 continue;
michael@0 1610 }
michael@0 1611
michael@0 1612 int16_t ruleindex;
michael@0 1613 nsTemplateRule* matchedrule = nullptr;
michael@0 1614 rv = DetermineMatchedRule(nullptr, nextresult, aQuerySet,
michael@0 1615 &matchedrule, &ruleindex);
michael@0 1616 if (NS_FAILED(rv)) {
michael@0 1617 nsTemplateMatch::Destroy(newmatch, false);
michael@0 1618 return rv;
michael@0 1619 }
michael@0 1620
michael@0 1621 if (matchedrule) {
michael@0 1622 rv = newmatch->RuleMatched(aQuerySet, matchedrule, ruleindex,
michael@0 1623 nextresult);
michael@0 1624 if (NS_FAILED(rv)) {
michael@0 1625 nsTemplateMatch::Destroy(newmatch, false);
michael@0 1626 return rv;
michael@0 1627 }
michael@0 1628
michael@0 1629 // Remember that this match applied to this row
michael@0 1630 mRows.InsertRowAt(newmatch, aSubtree, count);
michael@0 1631
michael@0 1632 // If this is open, then remember it so we can recursively add
michael@0 1633 // *its* rows to the tree.
michael@0 1634 bool isOpen = false;
michael@0 1635 IsContainerOpen(nextresult, &isOpen);
michael@0 1636 if (isOpen) {
michael@0 1637 if (open.AppendElement(count) == nullptr)
michael@0 1638 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1639 }
michael@0 1640
michael@0 1641 ++count;
michael@0 1642 }
michael@0 1643
michael@0 1644 if (mFlags & eLoggingEnabled)
michael@0 1645 OutputMatchToLog(resultid, newmatch, true);
michael@0 1646
michael@0 1647 }
michael@0 1648
michael@0 1649 if (prevmatch) {
michael@0 1650 prevmatch->mNext = newmatch;
michael@0 1651 }
michael@0 1652 else {
michael@0 1653 mMatchMap.Put(resultid, newmatch);
michael@0 1654 }
michael@0 1655 }
michael@0 1656
michael@0 1657 *aDelta = count;
michael@0 1658 return rv;
michael@0 1659 }
michael@0 1660
michael@0 1661 nsresult
michael@0 1662 nsXULTreeBuilder::CloseContainer(int32_t aIndex)
michael@0 1663 {
michael@0 1664 NS_ASSERTION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
michael@0 1665 if (aIndex < 0 || aIndex >= mRows.Count())
michael@0 1666 return NS_ERROR_INVALID_ARG;
michael@0 1667
michael@0 1668 nsTreeRows::iterator iter = mRows[aIndex];
michael@0 1669
michael@0 1670 if (iter->mSubtree)
michael@0 1671 RemoveMatchesFor(*iter->mSubtree);
michael@0 1672
michael@0 1673
michael@0 1674 int32_t count = mRows.GetSubtreeSizeFor(iter);
michael@0 1675 mRows.RemoveSubtreeFor(iter);
michael@0 1676
michael@0 1677 iter->mContainerState = nsTreeRows::eContainerState_Closed;
michael@0 1678
michael@0 1679 if (mBoxObject) {
michael@0 1680 mBoxObject->InvalidateRow(aIndex);
michael@0 1681
michael@0 1682 if (count)
michael@0 1683 mBoxObject->RowCountChanged(aIndex + 1, -count);
michael@0 1684 }
michael@0 1685
michael@0 1686 return NS_OK;
michael@0 1687 }
michael@0 1688
michael@0 1689 nsresult
michael@0 1690 nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree)
michael@0 1691 {
michael@0 1692 for (int32_t i = subtree.Count() - 1; i >= 0; --i) {
michael@0 1693 nsTreeRows::Row& row = subtree[i];
michael@0 1694
michael@0 1695 nsTemplateMatch* match = row.mMatch;
michael@0 1696
michael@0 1697 nsCOMPtr<nsIRDFResource> id;
michael@0 1698 nsresult rv = GetResultResource(match->mResult, getter_AddRefs(id));
michael@0 1699 if (NS_FAILED(rv))
michael@0 1700 return rv;
michael@0 1701
michael@0 1702 nsTemplateMatch* existingmatch;
michael@0 1703 if (mMatchMap.Get(id, &existingmatch)) {
michael@0 1704 while (existingmatch) {
michael@0 1705 nsTemplateMatch* nextmatch = existingmatch->mNext;
michael@0 1706 nsTemplateMatch::Destroy(existingmatch, true);
michael@0 1707 existingmatch = nextmatch;
michael@0 1708 }
michael@0 1709
michael@0 1710 mMatchMap.Remove(id);
michael@0 1711 }
michael@0 1712
michael@0 1713 if ((row.mContainerState == nsTreeRows::eContainerState_Open) && row.mSubtree)
michael@0 1714 RemoveMatchesFor(*(row.mSubtree));
michael@0 1715 }
michael@0 1716
michael@0 1717 return NS_OK;
michael@0 1718 }
michael@0 1719
michael@0 1720 nsresult
michael@0 1721 nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen)
michael@0 1722 {
michael@0 1723 // items are never open if recursion is disabled
michael@0 1724 if ((mFlags & eDontRecurse) && aResult != mRootResult) {
michael@0 1725 *aOpen = false;
michael@0 1726 return NS_OK;
michael@0 1727 }
michael@0 1728
michael@0 1729 nsCOMPtr<nsIRDFResource> id;
michael@0 1730 nsresult rv = GetResultResource(aResult, getter_AddRefs(id));
michael@0 1731 if (NS_FAILED(rv))
michael@0 1732 return rv;
michael@0 1733
michael@0 1734 return IsContainerOpen(id, aOpen);
michael@0 1735 }
michael@0 1736
michael@0 1737 nsresult
michael@0 1738 nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, bool* aOpen)
michael@0 1739 {
michael@0 1740 if (mPersistStateStore)
michael@0 1741 mPersistStateStore->HasAssertion(aResource,
michael@0 1742 nsXULContentUtils::NC_open,
michael@0 1743 nsXULContentUtils::true_,
michael@0 1744 true,
michael@0 1745 aOpen);
michael@0 1746 else
michael@0 1747 *aOpen = false;
michael@0 1748
michael@0 1749 return NS_OK;
michael@0 1750 }
michael@0 1751
michael@0 1752 int
michael@0 1753 nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure)
michael@0 1754 {
michael@0 1755 nsXULTreeBuilder* self = static_cast<nsXULTreeBuilder*>(aClosure);
michael@0 1756
michael@0 1757 nsTreeRows::Row* left = static_cast<nsTreeRows::Row*>
michael@0 1758 (const_cast<void*>(aLeft));
michael@0 1759
michael@0 1760 nsTreeRows::Row* right = static_cast<nsTreeRows::Row*>
michael@0 1761 (const_cast<void*>(aRight));
michael@0 1762
michael@0 1763 return self->CompareResults(left->mMatch->mResult, right->mMatch->mResult);
michael@0 1764 }
michael@0 1765
michael@0 1766 int32_t
michael@0 1767 nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight)
michael@0 1768 {
michael@0 1769 // this is an extra check done for RDF queries such that results appear in
michael@0 1770 // the order they appear in their containing Seq
michael@0 1771 if (mSortDirection == eDirection_Natural && mDB) {
michael@0 1772 // If the sort order is ``natural'', then see if the container
michael@0 1773 // is an RDF sequence. If so, we'll try to use the ordinal
michael@0 1774 // properties to determine order.
michael@0 1775 //
michael@0 1776 // XXX the problem with this is, it doesn't always get the
michael@0 1777 // *real* container; e.g.,
michael@0 1778 //
michael@0 1779 // <treerow uri="?uri" />
michael@0 1780 //
michael@0 1781 // <triple subject="?uri"
michael@0 1782 // predicate="http://home.netscape.com/NC-rdf#subheadings"
michael@0 1783 // object="?subheadings" />
michael@0 1784 //
michael@0 1785 // <member container="?subheadings" child="?subheading" />
michael@0 1786 //
michael@0 1787 // In this case mRefVariable is bound to ?uri, not
michael@0 1788 // ?subheadings. (The ``container'' in the template sense !=
michael@0 1789 // container in the RDF sense.)
michael@0 1790
michael@0 1791 nsCOMPtr<nsISupports> ref;
michael@0 1792 nsresult rv = aLeft->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref));
michael@0 1793 if (NS_FAILED(rv))
michael@0 1794 return 0;
michael@0 1795
michael@0 1796 nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
michael@0 1797 if (container) {
michael@0 1798 bool isSequence = false;
michael@0 1799 gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
michael@0 1800 if (isSequence) {
michael@0 1801 // Determine the indices of the left and right elements
michael@0 1802 // in the container.
michael@0 1803 int32_t lindex = 0, rindex = 0;
michael@0 1804
michael@0 1805 nsCOMPtr<nsIRDFResource> leftitem;
michael@0 1806 aLeft->GetResource(getter_AddRefs(leftitem));
michael@0 1807 if (leftitem) {
michael@0 1808 gRDFContainerUtils->IndexOf(mDB, container, leftitem, &lindex);
michael@0 1809 if (lindex < 0)
michael@0 1810 return 0;
michael@0 1811 }
michael@0 1812
michael@0 1813 nsCOMPtr<nsIRDFResource> rightitem;
michael@0 1814 aRight->GetResource(getter_AddRefs(rightitem));
michael@0 1815 if (rightitem) {
michael@0 1816 gRDFContainerUtils->IndexOf(mDB, container, rightitem, &rindex);
michael@0 1817 if (rindex < 0)
michael@0 1818 return 0;
michael@0 1819 }
michael@0 1820
michael@0 1821 return lindex - rindex;
michael@0 1822 }
michael@0 1823 }
michael@0 1824 }
michael@0 1825
michael@0 1826 int32_t sortorder;
michael@0 1827 if (!mQueryProcessor)
michael@0 1828 return 0;
michael@0 1829
michael@0 1830 mQueryProcessor->CompareResults(aLeft, aRight, mSortVariable, mSortHints, &sortorder);
michael@0 1831
michael@0 1832 if (sortorder)
michael@0 1833 sortorder = sortorder * mSortDirection;
michael@0 1834 return sortorder;
michael@0 1835 }
michael@0 1836
michael@0 1837 nsresult
michael@0 1838 nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree)
michael@0 1839 {
michael@0 1840 NS_QuickSort(mRows.GetRowsFor(aSubtree),
michael@0 1841 aSubtree->Count(),
michael@0 1842 sizeof(nsTreeRows::Row),
michael@0 1843 Compare,
michael@0 1844 this);
michael@0 1845
michael@0 1846 for (int32_t i = aSubtree->Count() - 1; i >= 0; --i) {
michael@0 1847 nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree;
michael@0 1848 if (child)
michael@0 1849 SortSubtree(child);
michael@0 1850 }
michael@0 1851
michael@0 1852 return NS_OK;
michael@0 1853 }
michael@0 1854
michael@0 1855
michael@0 1856 /* boolean canDrop (in long index, in long orientation); */
michael@0 1857 NS_IMETHODIMP
michael@0 1858 nsXULTreeBuilder::CanDrop(int32_t index, int32_t orientation,
michael@0 1859 nsIDOMDataTransfer* dataTransfer, bool *_retval)
michael@0 1860 {
michael@0 1861 *_retval = false;
michael@0 1862 uint32_t count = mObservers.Count();
michael@0 1863 for (uint32_t i = 0; i < count; ++i) {
michael@0 1864 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 1865 if (observer) {
michael@0 1866 observer->CanDrop(index, orientation, dataTransfer, _retval);
michael@0 1867 if (*_retval)
michael@0 1868 break;
michael@0 1869 }
michael@0 1870 }
michael@0 1871
michael@0 1872 return NS_OK;
michael@0 1873 }
michael@0 1874
michael@0 1875 NS_IMETHODIMP
michael@0 1876 nsXULTreeBuilder::Drop(int32_t row, int32_t orient, nsIDOMDataTransfer* dataTransfer)
michael@0 1877 {
michael@0 1878 uint32_t count = mObservers.Count();
michael@0 1879 for (uint32_t i = 0; i < count; ++i) {
michael@0 1880 nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
michael@0 1881 if (observer) {
michael@0 1882 bool canDrop = false;
michael@0 1883 observer->CanDrop(row, orient, dataTransfer, &canDrop);
michael@0 1884 if (canDrop)
michael@0 1885 observer->OnDrop(row, orient, dataTransfer);
michael@0 1886 }
michael@0 1887 }
michael@0 1888
michael@0 1889 return NS_OK;
michael@0 1890 }
michael@0 1891
michael@0 1892 NS_IMETHODIMP
michael@0 1893 nsXULTreeBuilder::IsSorted(bool *_retval)
michael@0 1894 {
michael@0 1895 *_retval = (mSortVariable != nullptr);
michael@0 1896 return NS_OK;
michael@0 1897 }
michael@0 1898

mercurial