layout/xul/tree/nsTreeBodyFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/AsyncEventDispatcher.h"
     7 #include "mozilla/ContentEvents.h"
     8 #include "mozilla/DebugOnly.h"
     9 #include "mozilla/EventDispatcher.h"
    10 #include "mozilla/MathAlgorithms.h"
    11 #include "mozilla/MouseEvents.h"
    12 #include "mozilla/Likely.h"
    14 #include "nsCOMPtr.h"
    15 #include "nsPresContext.h"
    16 #include "nsNameSpaceManager.h"
    18 #include "nsTreeBodyFrame.h"
    19 #include "nsTreeSelection.h"
    20 #include "nsTreeImageListener.h"
    22 #include "nsGkAtoms.h"
    23 #include "nsCSSAnonBoxes.h"
    25 #include "nsIContent.h"
    26 #include "nsStyleContext.h"
    27 #include "nsIBoxObject.h"
    28 #include "nsIDOMCustomEvent.h"
    29 #include "nsIDOMMouseEvent.h"
    30 #include "nsIDOMElement.h"
    31 #include "nsIDOMNodeList.h"
    32 #include "nsIDOMDocument.h"
    33 #include "nsIDOMXULElement.h"
    34 #include "nsIDocument.h"
    35 #include "mozilla/css/StyleRule.h"
    36 #include "nsCSSRendering.h"
    37 #include "nsIXULTemplateBuilder.h"
    38 #include "nsXPIDLString.h"
    39 #include "nsContainerFrame.h"
    40 #include "nsView.h"
    41 #include "nsViewManager.h"
    42 #include "nsWidgetsCID.h"
    43 #include "nsBoxFrame.h"
    44 #include "nsBoxObject.h"
    45 #include "nsIURL.h"
    46 #include "nsNetUtil.h"
    47 #include "nsBoxLayoutState.h"
    48 #include "nsTreeContentView.h"
    49 #include "nsTreeUtils.h"
    50 #include "nsITheme.h"
    51 #include "imgIRequest.h"
    52 #include "imgIContainer.h"
    53 #include "imgILoader.h"
    54 #include "nsINodeInfo.h"
    55 #include "nsContentUtils.h"
    56 #include "nsLayoutUtils.h"
    57 #include "nsIScrollableFrame.h"
    58 #include "nsDisplayList.h"
    59 #include "nsTreeBoxObject.h"
    60 #include "nsRenderingContext.h"
    61 #include "nsIScriptableRegion.h"
    62 #include <algorithm>
    63 #include "ScrollbarActivity.h"
    65 #ifdef ACCESSIBILITY
    66 #include "nsAccessibilityService.h"
    67 #include "nsIWritablePropertyBag2.h"
    68 #endif
    69 #include "nsBidiUtils.h"
    71 using namespace mozilla;
    72 using namespace mozilla::layout;
    74 // Enumeration function that cancels all the image requests in our cache
    75 static PLDHashOperator
    76 CancelImageRequest(const nsAString& aKey,
    77                    nsTreeImageCacheEntry aEntry, void* aData)
    78 {
    80   // If our imgIRequest object was registered with the refresh driver,
    81   // then we need to deregister it.
    82   nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData);
    84   nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request,
    85                                         nullptr);
    87   aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
    88   return PL_DHASH_NEXT;
    89 }
    91 //
    92 // NS_NewTreeFrame
    93 //
    94 // Creates a new tree frame
    95 //
    96 nsIFrame*
    97 NS_NewTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    98 {
    99   return new (aPresShell) nsTreeBodyFrame(aPresShell, aContext);
   100 }
   102 NS_IMPL_FRAMEARENA_HELPERS(nsTreeBodyFrame)
   104 NS_QUERYFRAME_HEAD(nsTreeBodyFrame)
   105   NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
   106   NS_QUERYFRAME_ENTRY(nsIScrollbarOwner)
   107   NS_QUERYFRAME_ENTRY(nsTreeBodyFrame)
   108 NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame)
   110 // Constructor
   111 nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   112 :nsLeafBoxFrame(aPresShell, aContext),
   113  mSlots(nullptr),
   114  mImageCache(16),
   115  mTopRowIndex(0),
   116  mPageLength(0),
   117  mHorzPosition(0),
   118  mOriginalHorzWidth(-1),
   119  mHorzWidth(0),
   120  mAdjustWidth(0),
   121  mRowHeight(0),
   122  mIndentation(0),
   123  mStringWidth(-1),
   124  mUpdateBatchNest(0),
   125  mRowCount(0),
   126  mMouseOverRow(-1),
   127  mFocused(false),
   128  mHasFixedRowCount(false),
   129  mVerticalOverflow(false),
   130  mHorizontalOverflow(false),
   131  mReflowCallbackPosted(false),
   132  mCheckingOverflow(false)
   133 {
   134   mColumns = new nsTreeColumns(this);
   135 }
   137 // Destructor
   138 nsTreeBodyFrame::~nsTreeBodyFrame()
   139 {
   140   mImageCache.EnumerateRead(CancelImageRequest, this);
   141   DetachImageListeners();
   142   delete mSlots;
   143 }
   145 static void
   146 GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin)
   147 {
   148   aMargin.SizeTo(0, 0, 0, 0);
   149   if (!aContext->StylePadding()->GetPadding(aMargin)) {
   150     NS_NOTYETIMPLEMENTED("percentage padding");
   151   }
   152   aMargin += aContext->StyleBorder()->GetComputedBorder();
   153 }
   155 static void
   156 AdjustForBorderPadding(nsStyleContext* aContext, nsRect& aRect)
   157 {
   158   nsMargin borderPadding(0, 0, 0, 0);
   159   GetBorderPadding(aContext, borderPadding);
   160   aRect.Deflate(borderPadding);
   161 }
   163 void
   164 nsTreeBodyFrame::Init(nsIContent*     aContent,
   165                       nsIFrame*       aParent,
   166                       nsIFrame*       aPrevInFlow)
   167 {
   168   nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
   170   mIndentation = GetIndentation();
   171   mRowHeight = GetRowHeight();
   173   EnsureBoxObject();
   175   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
   176     mScrollbarActivity = new ScrollbarActivity(
   177                            static_cast<nsIScrollbarOwner*>(this));
   178   }
   179 }
   181 nsSize
   182 nsTreeBodyFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
   183 {
   184   EnsureView();
   186   nsIContent* baseElement = GetBaseElement();
   188   nsSize min(0,0);
   189   int32_t desiredRows;
   190   if (MOZ_UNLIKELY(!baseElement)) {
   191     desiredRows = 0;
   192   }
   193   else if (baseElement->Tag() == nsGkAtoms::select &&
   194            baseElement->IsHTML()) {
   195     min.width = CalcMaxRowWidth();
   196     nsAutoString size;
   197     baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::size, size);
   198     if (!size.IsEmpty()) {
   199       nsresult err;
   200       desiredRows = size.ToInteger(&err);
   201       mHasFixedRowCount = true;
   202       mPageLength = desiredRows;
   203     }
   204     else {
   205       desiredRows = 1;
   206     }
   207   }
   208   else {
   209     // tree
   210     nsAutoString rows;
   211     baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
   212     if (!rows.IsEmpty()) {
   213       nsresult err;
   214       desiredRows = rows.ToInteger(&err);
   215       mPageLength = desiredRows;
   216     }
   217     else {
   218       desiredRows = 0;
   219     }
   220   }
   222   min.height = mRowHeight * desiredRows;
   224   AddBorderAndPadding(min);
   225   bool widthSet, heightSet;
   226   nsIFrame::AddCSSMinSize(aBoxLayoutState, this, min, widthSet, heightSet);
   228   return min;
   229 }
   231 nscoord
   232 nsTreeBodyFrame::CalcMaxRowWidth()
   233 {
   234   if (mStringWidth != -1)
   235     return mStringWidth;
   237   if (!mView)
   238     return 0;
   240   nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
   241   nsMargin rowMargin(0,0,0,0);
   242   GetBorderPadding(rowContext, rowMargin);
   244   nscoord rowWidth;
   245   nsTreeColumn* col;
   247   nsRefPtr<nsRenderingContext> rc =
   248     PresContext()->PresShell()->CreateReferenceRenderingContext();
   250   for (int32_t row = 0; row < mRowCount; ++row) {
   251     rowWidth = 0;
   253     for (col = mColumns->GetFirstColumn(); col; col = col->GetNext()) {
   254       nscoord desiredWidth, currentWidth;
   255       nsresult rv = GetCellWidth(row, col, rc, desiredWidth, currentWidth);
   256       if (NS_FAILED(rv)) {
   257         NS_NOTREACHED("invalid column");
   258         continue;
   259       }
   260       rowWidth += desiredWidth;
   261     }
   263     if (rowWidth > mStringWidth)
   264       mStringWidth = rowWidth;
   265   }
   267   mStringWidth += rowMargin.left + rowMargin.right;
   268   return mStringWidth;
   269 }
   271 void
   272 nsTreeBodyFrame::DestroyFrom(nsIFrame* aDestructRoot)
   273 {
   274   if (mScrollbarActivity) {
   275     mScrollbarActivity->Destroy();
   276     mScrollbarActivity = nullptr;
   277   }
   279   mScrollEvent.Revoke();
   280   // Make sure we cancel any posted callbacks. 
   281   if (mReflowCallbackPosted) {
   282     PresContext()->PresShell()->CancelReflowCallback(this);
   283     mReflowCallbackPosted = false;
   284   }
   286   if (mColumns)
   287     mColumns->SetTree(nullptr);
   289   // Save off our info into the box object.
   290   nsCOMPtr<nsPIBoxObject> box(do_QueryInterface(mTreeBoxObject));
   291   if (box) {
   292     if (mTopRowIndex > 0) {
   293       nsAutoString topRowStr; topRowStr.AssignLiteral("topRow");
   294       nsAutoString topRow;
   295       topRow.AppendInt(mTopRowIndex);
   296       box->SetProperty(topRowStr.get(), topRow.get());
   297     }
   299     // Always null out the cached tree body frame.
   300     box->ClearCachedValues();
   302     mTreeBoxObject = nullptr; // Drop our ref here.
   303   }
   305   if (mView) {
   306     nsCOMPtr<nsITreeSelection> sel;
   307     mView->GetSelection(getter_AddRefs(sel));
   308     if (sel)
   309       sel->SetTree(nullptr);
   310     mView->SetTree(nullptr);
   311     mView = nullptr;
   312   }
   314   nsLeafBoxFrame::DestroyFrom(aDestructRoot);
   315 }
   317 void
   318 nsTreeBodyFrame::EnsureBoxObject()
   319 {
   320   if (!mTreeBoxObject) {
   321     nsIContent* parent = GetBaseElement();
   322     if (parent) {
   323       nsIDocument* nsDoc = parent->GetDocument();
   324       if (!nsDoc) // there may be no document, if we're called from Destroy()
   325         return;
   326       ErrorResult ignored;
   327       nsCOMPtr<nsIBoxObject> box =
   328         nsDoc->GetBoxObjectFor(parent->AsElement(), ignored);
   329       // Ensure that we got a native box object.
   330       nsCOMPtr<nsPIBoxObject> pBox = do_QueryInterface(box);
   331       if (pBox) {
   332         nsCOMPtr<nsITreeBoxObject> realTreeBoxObject = do_QueryInterface(pBox);
   333         if (realTreeBoxObject) {
   334           nsTreeBodyFrame* innerTreeBoxObject =
   335             static_cast<nsTreeBoxObject*>(realTreeBoxObject.get())
   336               ->GetCachedTreeBody();
   337           ENSURE_TRUE(!innerTreeBoxObject || innerTreeBoxObject == this);
   338           mTreeBoxObject = realTreeBoxObject;
   339         }
   340       }
   341     }
   342   }
   343 }
   345 void
   346 nsTreeBodyFrame::EnsureView()
   347 {
   348   if (!mView) {
   349     if (PresContext()->PresShell()->IsReflowLocked()) {
   350       if (!mReflowCallbackPosted) {
   351         mReflowCallbackPosted = true;
   352         PresContext()->PresShell()->PostReflowCallback(this);
   353       }
   354       return;
   355     }
   356     nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject);
   357     if (box) {
   358       nsWeakFrame weakFrame(this);
   359       nsCOMPtr<nsITreeView> treeView;
   360       mTreeBoxObject->GetView(getter_AddRefs(treeView));
   361       if (treeView && weakFrame.IsAlive()) {
   362         nsXPIDLString rowStr;
   363         box->GetProperty(MOZ_UTF16("topRow"),
   364                          getter_Copies(rowStr));
   365         nsAutoString rowStr2(rowStr);
   366         nsresult error;
   367         int32_t rowIndex = rowStr2.ToInteger(&error);
   369         // Set our view.
   370         SetView(treeView);
   371         ENSURE_TRUE(weakFrame.IsAlive());
   373         // Scroll to the given row.
   374         // XXX is this optimal if we haven't laid out yet?
   375         ScrollToRow(rowIndex);
   376         ENSURE_TRUE(weakFrame.IsAlive());
   378         // Clear out the property info for the top row, but we always keep the
   379         // view current.
   380         box->RemoveProperty(MOZ_UTF16("topRow"));
   381       }
   382     }
   383   }
   384 }
   386 void
   387 nsTreeBodyFrame::ManageReflowCallback(const nsRect& aRect, nscoord aHorzWidth)
   388 {
   389   if (!mReflowCallbackPosted &&
   390       (!aRect.IsEqualEdges(mRect) || mHorzWidth != aHorzWidth)) {
   391     PresContext()->PresShell()->PostReflowCallback(this);
   392     mReflowCallbackPosted = true;
   393     mOriginalHorzWidth = mHorzWidth;
   394   }
   395   else if (mReflowCallbackPosted &&
   396            mHorzWidth != aHorzWidth && mOriginalHorzWidth == aHorzWidth) {
   397     PresContext()->PresShell()->CancelReflowCallback(this);
   398     mReflowCallbackPosted = false;
   399     mOriginalHorzWidth = -1;
   400   }
   401 }
   403 void
   404 nsTreeBodyFrame::SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect,
   405                            bool aRemoveOverflowArea)
   406 {
   407   nscoord horzWidth = CalcHorzWidth(GetScrollParts());
   408   ManageReflowCallback(aRect, horzWidth);
   409   mHorzWidth = horzWidth;
   411   nsLeafBoxFrame::SetBounds(aBoxLayoutState, aRect, aRemoveOverflowArea);
   412 }
   415 bool
   416 nsTreeBodyFrame::ReflowFinished()
   417 {
   418   if (!mView) {
   419     nsWeakFrame weakFrame(this);
   420     EnsureView();
   421     NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   422   }
   423   if (mView) {
   424     CalcInnerBox();
   425     ScrollParts parts = GetScrollParts();
   426     mHorzWidth = CalcHorzWidth(parts);
   427     if (!mHasFixedRowCount) {
   428       mPageLength = mInnerBox.height / mRowHeight;
   429     }
   431     int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength);
   432     if (mTopRowIndex > lastPageTopRow)
   433       ScrollToRowInternal(parts, lastPageTopRow);
   435     nsIContent *treeContent = GetBaseElement();
   436     if (treeContent &&
   437         treeContent->AttrValueIs(kNameSpaceID_None,
   438                                  nsGkAtoms::keepcurrentinview,
   439                                  nsGkAtoms::_true, eCaseMatters)) {
   440       // make sure that the current selected item is still
   441       // visible after the tree changes size.
   442       nsCOMPtr<nsITreeSelection> sel;
   443       mView->GetSelection(getter_AddRefs(sel));
   444       if (sel) {
   445         int32_t currentIndex;
   446         sel->GetCurrentIndex(&currentIndex);
   447         if (currentIndex != -1)
   448           EnsureRowIsVisibleInternal(parts, currentIndex);
   449       }
   450     }
   452     if (!FullScrollbarsUpdate(false)) {
   453       return false;
   454     }
   455   }
   457   mReflowCallbackPosted = false;
   458   return false;
   459 }
   461 void
   462 nsTreeBodyFrame::ReflowCallbackCanceled()
   463 {
   464   mReflowCallbackPosted = false;
   465 }
   467 nsresult
   468 nsTreeBodyFrame::GetView(nsITreeView * *aView)
   469 {
   470   *aView = nullptr;
   471   nsWeakFrame weakFrame(this);
   472   EnsureView();
   473   NS_ENSURE_STATE(weakFrame.IsAlive());
   474   NS_IF_ADDREF(*aView = mView);
   475   return NS_OK;
   476 }
   478 nsresult
   479 nsTreeBodyFrame::SetView(nsITreeView * aView)
   480 {
   481   // First clear out the old view.
   482   if (mView) {
   483     nsCOMPtr<nsITreeSelection> sel;
   484     mView->GetSelection(getter_AddRefs(sel));
   485     if (sel)
   486       sel->SetTree(nullptr);
   487     mView->SetTree(nullptr);
   489     // Only reset the top row index and delete the columns if we had an old non-null view.
   490     mTopRowIndex = 0;
   491   }
   493   // Tree, meet the view.
   494   mView = aView;
   496   // Changing the view causes us to refetch our data.  This will
   497   // necessarily entail a full invalidation of the tree.
   498   Invalidate();
   500   nsIContent *treeContent = GetBaseElement();
   501   if (treeContent) {
   502 #ifdef ACCESSIBILITY
   503     nsAccessibilityService* accService = nsIPresShell::AccService();
   504     if (accService)
   505       accService->TreeViewChanged(PresContext()->GetPresShell(), treeContent, mView);
   506 #endif
   507     FireDOMEvent(NS_LITERAL_STRING("TreeViewChanged"), treeContent);
   508   }
   510   if (mView) {
   511     // Give the view a new empty selection object to play with, but only if it
   512     // doesn't have one already.
   513     nsCOMPtr<nsITreeSelection> sel;
   514     mView->GetSelection(getter_AddRefs(sel));
   515     if (sel) {
   516       sel->SetTree(mTreeBoxObject);
   517     }
   518     else {
   519       NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel));
   520       mView->SetSelection(sel);
   521     }
   523     // View, meet the tree.
   524     nsWeakFrame weakFrame(this);
   525     mView->SetTree(mTreeBoxObject);
   526     NS_ENSURE_STATE(weakFrame.IsAlive());
   527     mView->GetRowCount(&mRowCount);
   529     if (!PresContext()->PresShell()->IsReflowLocked()) {
   530       // The scrollbar will need to be updated.
   531       FullScrollbarsUpdate(false);
   532     } else if (!mReflowCallbackPosted) {
   533       mReflowCallbackPosted = true;
   534       PresContext()->PresShell()->PostReflowCallback(this);
   535     }
   536   }
   538   return NS_OK;
   539 }
   541 nsresult
   542 nsTreeBodyFrame::GetFocused(bool* aFocused)
   543 {
   544   *aFocused = mFocused;
   545   return NS_OK;
   546 }
   548 nsresult
   549 nsTreeBodyFrame::SetFocused(bool aFocused)
   550 {
   551   if (mFocused != aFocused) {
   552     mFocused = aFocused;
   553     if (mView) {
   554       nsCOMPtr<nsITreeSelection> sel;
   555       mView->GetSelection(getter_AddRefs(sel));
   556       if (sel)
   557         sel->InvalidateSelection();
   558     }
   559   }
   560   return NS_OK;
   561 }
   563 nsresult
   564 nsTreeBodyFrame::GetTreeBody(nsIDOMElement** aElement)
   565 {
   566   //NS_ASSERTION(mContent, "no content, see bug #104878");
   567   if (!mContent)
   568     return NS_ERROR_NULL_POINTER;
   570   return mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aElement);
   571 }
   573 nsresult
   574 nsTreeBodyFrame::GetRowHeight(int32_t* _retval)
   575 {
   576   *_retval = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   577   return NS_OK;
   578 }
   580 nsresult
   581 nsTreeBodyFrame::GetRowWidth(int32_t *aRowWidth)
   582 {
   583   *aRowWidth = nsPresContext::AppUnitsToIntCSSPixels(CalcHorzWidth(GetScrollParts()));
   584   return NS_OK;
   585 }
   587 nsresult
   588 nsTreeBodyFrame::GetHorizontalPosition(int32_t *aHorizontalPosition)
   589 {
   590   *aHorizontalPosition = nsPresContext::AppUnitsToIntCSSPixels(mHorzPosition); 
   591   return NS_OK;
   592 }
   594 nsresult
   595 nsTreeBodyFrame::GetSelectionRegion(nsIScriptableRegion **aRegion)
   596 {
   597   *aRegion = nullptr;
   599   nsCOMPtr<nsITreeSelection> selection;
   600   mView->GetSelection(getter_AddRefs(selection));
   601   NS_ENSURE_TRUE(selection, NS_OK);
   603   nsCOMPtr<nsIScriptableRegion> region = do_CreateInstance("@mozilla.org/gfx/region;1");
   604   NS_ENSURE_TRUE(region, NS_ERROR_FAILURE);
   605   region->Init();
   607   nsRefPtr<nsPresContext> presContext = PresContext();
   608   nsIntRect rect = mRect.ToOutsidePixels(presContext->AppUnitsPerCSSPixel());
   610   nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
   611   nsPoint origin = GetOffsetTo(rootFrame);
   613   // iterate through the visible rows and add the selected ones to the
   614   // drag region
   615   int32_t x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
   616   int32_t y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
   617   int32_t top = y;
   618   int32_t end = LastVisibleRow();
   619   int32_t rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   620   for (int32_t i = mTopRowIndex; i <= end; i++) {
   621     bool isSelected;
   622     selection->IsSelected(i, &isSelected);
   623     if (isSelected)
   624       region->UnionRect(x, y, rect.width, rowHeight);
   625     y += rowHeight;
   626   }
   628   // clip to the tree boundary in case one row extends past it
   629   region->IntersectRect(x, top, rect.width, rect.height);
   631   NS_ADDREF(*aRegion = region);
   632   return NS_OK;
   633 }
   635 nsresult
   636 nsTreeBodyFrame::Invalidate()
   637 {
   638   if (mUpdateBatchNest)
   639     return NS_OK;
   641   InvalidateFrame();
   643   return NS_OK;
   644 }
   646 nsresult
   647 nsTreeBodyFrame::InvalidateColumn(nsITreeColumn* aCol)
   648 {
   649   if (mUpdateBatchNest)
   650     return NS_OK;
   652   nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
   653   if (!col)
   654     return NS_ERROR_INVALID_ARG;
   656 #ifdef ACCESSIBILITY
   657   if (nsIPresShell::IsAccessibilityActive())
   658     FireInvalidateEvent(-1, -1, aCol, aCol);
   659 #endif
   661   nsRect columnRect;
   662   nsresult rv = col->GetRect(this, mInnerBox.y, mInnerBox.height, &columnRect);
   663   NS_ENSURE_SUCCESS(rv, rv);
   665   // When false then column is out of view
   666   if (OffsetForHorzScroll(columnRect, true))
   667       InvalidateFrameWithRect(columnRect);
   669   return NS_OK;
   670 }
   672 nsresult
   673 nsTreeBodyFrame::InvalidateRow(int32_t aIndex)
   674 {
   675   if (mUpdateBatchNest)
   676     return NS_OK;
   678 #ifdef ACCESSIBILITY
   679   if (nsIPresShell::IsAccessibilityActive())
   680     FireInvalidateEvent(aIndex, aIndex, nullptr, nullptr);
   681 #endif
   683   aIndex -= mTopRowIndex;
   684   if (aIndex < 0 || aIndex > mPageLength)
   685     return NS_OK;
   687   nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*aIndex, mInnerBox.width, mRowHeight);
   688   InvalidateFrameWithRect(rowRect);
   690   return NS_OK;
   691 }
   693 nsresult
   694 nsTreeBodyFrame::InvalidateCell(int32_t aIndex, nsITreeColumn* aCol)
   695 {
   696   if (mUpdateBatchNest)
   697     return NS_OK;
   699 #ifdef ACCESSIBILITY
   700   if (nsIPresShell::IsAccessibilityActive())
   701     FireInvalidateEvent(aIndex, aIndex, aCol, aCol);
   702 #endif
   704   aIndex -= mTopRowIndex;
   705   if (aIndex < 0 || aIndex > mPageLength)
   706     return NS_OK;
   708   nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
   709   if (!col)
   710     return NS_ERROR_INVALID_ARG;
   712   nsRect cellRect;
   713   nsresult rv = col->GetRect(this, mInnerBox.y+mRowHeight*aIndex, mRowHeight,
   714                              &cellRect);
   715   NS_ENSURE_SUCCESS(rv, rv);
   717   if (OffsetForHorzScroll(cellRect, true))
   718     InvalidateFrameWithRect(cellRect);
   720   return NS_OK;
   721 }
   723 nsresult
   724 nsTreeBodyFrame::InvalidateRange(int32_t aStart, int32_t aEnd)
   725 {
   726   if (mUpdateBatchNest)
   727     return NS_OK;
   729   if (aStart == aEnd)
   730     return InvalidateRow(aStart);
   732   int32_t last = LastVisibleRow();
   733   if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last)
   734     return NS_OK;
   736   if (aStart < mTopRowIndex)
   737     aStart = mTopRowIndex;
   739   if (aEnd > last)
   740     aEnd = last;
   742 #ifdef ACCESSIBILITY
   743   if (nsIPresShell::IsAccessibilityActive()) {
   744     int32_t end =
   745       mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0;
   746     FireInvalidateEvent(aStart, end, nullptr, nullptr);
   747   }
   748 #endif
   750   nsRect rangeRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), mInnerBox.width, mRowHeight*(aEnd-aStart+1));
   751   InvalidateFrameWithRect(rangeRect);
   753   return NS_OK;
   754 }
   756 nsresult
   757 nsTreeBodyFrame::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol)
   758 {
   759   if (mUpdateBatchNest)
   760     return NS_OK;
   762   nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
   763   if (!col)
   764     return NS_ERROR_INVALID_ARG;
   766   if (aStart == aEnd)
   767     return InvalidateCell(aStart, col);
   769   int32_t last = LastVisibleRow();
   770   if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last)
   771     return NS_OK;
   773   if (aStart < mTopRowIndex)
   774     aStart = mTopRowIndex;
   776   if (aEnd > last)
   777     aEnd = last;
   779 #ifdef ACCESSIBILITY
   780   if (nsIPresShell::IsAccessibilityActive()) {
   781     int32_t end =
   782       mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0;
   783     FireInvalidateEvent(aStart, end, aCol, aCol);
   784   }
   785 #endif
   787   nsRect rangeRect;
   788   nsresult rv = col->GetRect(this, 
   789                              mInnerBox.y+mRowHeight*(aStart-mTopRowIndex),
   790                              mRowHeight*(aEnd-aStart+1),
   791                              &rangeRect);
   792   NS_ENSURE_SUCCESS(rv, rv);
   794   InvalidateFrameWithRect(rangeRect);
   796   return NS_OK;
   797 }
   799 static void
   800 FindScrollParts(nsIFrame* aCurrFrame, nsTreeBodyFrame::ScrollParts* aResult)
   801 {
   802   if (!aResult->mColumnsScrollFrame) {
   803     nsIScrollableFrame* f = do_QueryFrame(aCurrFrame);
   804     if (f) {
   805       aResult->mColumnsFrame = aCurrFrame;
   806       aResult->mColumnsScrollFrame = f;
   807     }
   808   }
   810   nsScrollbarFrame *sf = do_QueryFrame(aCurrFrame);
   811   if (sf) {
   812     if (!aCurrFrame->IsHorizontal()) {
   813       if (!aResult->mVScrollbar) {
   814         aResult->mVScrollbar = sf;
   815       }
   816     } else {
   817       if (!aResult->mHScrollbar) {
   818         aResult->mHScrollbar = sf;
   819       }
   820     }
   821     // don't bother searching inside a scrollbar
   822     return;
   823   }
   825   nsIFrame* child = aCurrFrame->GetFirstPrincipalChild();
   826   while (child &&
   827          !child->GetContent()->IsRootOfNativeAnonymousSubtree() &&
   828          (!aResult->mVScrollbar || !aResult->mHScrollbar ||
   829           !aResult->mColumnsScrollFrame)) {
   830     FindScrollParts(child, aResult);
   831     child = child->GetNextSibling();
   832   }
   833 }
   835 nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts()
   836 {
   837   ScrollParts result = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
   838   nsIContent* baseElement = GetBaseElement();
   839   nsIFrame* treeFrame =
   840     baseElement ? baseElement->GetPrimaryFrame() : nullptr;
   841   if (treeFrame) {
   842     // The way we do this, searching through the entire frame subtree, is pretty
   843     // dumb! We should know where these frames are.
   844     FindScrollParts(treeFrame, &result);
   845     if (result.mHScrollbar) {
   846       result.mHScrollbar->SetScrollbarMediatorContent(GetContent());
   847       nsIFrame* f = do_QueryFrame(result.mHScrollbar);
   848       result.mHScrollbarContent = f->GetContent();
   849     }
   850     if (result.mVScrollbar) {
   851       result.mVScrollbar->SetScrollbarMediatorContent(GetContent());
   852       nsIFrame* f = do_QueryFrame(result.mVScrollbar);
   853       result.mVScrollbarContent = f->GetContent();
   854     }
   855   }
   856   return result;
   857 }
   859 void
   860 nsTreeBodyFrame::UpdateScrollbars(const ScrollParts& aParts)
   861 {
   862   nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   864   nsWeakFrame weakFrame(this);
   866   if (aParts.mVScrollbar) {
   867     nsAutoString curPos;
   868     curPos.AppendInt(mTopRowIndex*rowHeightAsPixels);
   869     aParts.mVScrollbarContent->
   870       SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true);
   871     // 'this' might be deleted here
   872   }
   874   if (weakFrame.IsAlive() && aParts.mHScrollbar) {
   875     nsAutoString curPos;
   876     curPos.AppendInt(mHorzPosition);
   877     aParts.mHScrollbarContent->
   878       SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true);
   879     // 'this' might be deleted here
   880   }
   882   if (weakFrame.IsAlive() && mScrollbarActivity) {
   883     mScrollbarActivity->ActivityOccurred();
   884   }
   885 }
   887 void
   888 nsTreeBodyFrame::CheckOverflow(const ScrollParts& aParts)
   889 {
   890   bool verticalOverflowChanged = false;
   891   bool horizontalOverflowChanged = false;
   893   if (!mVerticalOverflow && mRowCount > mPageLength) {
   894     mVerticalOverflow = true;
   895     verticalOverflowChanged = true;
   896   }
   897   else if (mVerticalOverflow && mRowCount <= mPageLength) {
   898     mVerticalOverflow = false;
   899     verticalOverflowChanged = true;
   900   }
   902   if (aParts.mColumnsFrame) {
   903     nsRect bounds = aParts.mColumnsFrame->GetRect();
   904     if (bounds.width != 0) {
   905       /* Ignore overflows that are less than half a pixel. Yes these happen
   906          all over the place when flex boxes are compressed real small. 
   907          Probably a result of a rounding errors somewhere in the layout code. */
   908       bounds.width += nsPresContext::CSSPixelsToAppUnits(0.5f);
   909       if (!mHorizontalOverflow && bounds.width < mHorzWidth) {
   910         mHorizontalOverflow = true;
   911         horizontalOverflowChanged = true;
   912       } else if (mHorizontalOverflow && bounds.width >= mHorzWidth) {
   913         mHorizontalOverflow = false;
   914         horizontalOverflowChanged = true;
   915       }
   916     }
   917   }
   919   nsWeakFrame weakFrame(this);
   921   nsRefPtr<nsPresContext> presContext = PresContext();
   922   nsCOMPtr<nsIPresShell> presShell = presContext->GetPresShell();
   923   nsCOMPtr<nsIContent> content = mContent;
   925   if (verticalOverflowChanged) {
   926     InternalScrollPortEvent event(true,
   927       mVerticalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW,
   928       nullptr);
   929     event.orient = InternalScrollPortEvent::vertical;
   930     EventDispatcher::Dispatch(content, presContext, &event);
   931   }
   933   if (horizontalOverflowChanged) {
   934     InternalScrollPortEvent event(true,
   935       mHorizontalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW,
   936       nullptr);
   937     event.orient = InternalScrollPortEvent::horizontal;
   938     EventDispatcher::Dispatch(content, presContext, &event);
   939   }
   941   // The synchronous event dispatch above can trigger reflow notifications.
   942   // Flush those explicitly now, so that we can guard against potential infinite
   943   // recursion. See bug 905909.
   944   if (!weakFrame.IsAlive()) {
   945     return;
   946   }
   947   NS_ASSERTION(!mCheckingOverflow, "mCheckingOverflow should not already be set");
   948   // Don't use AutoRestore since we want to not touch mCheckingOverflow if we fail
   949   // the weakFrame.IsAlive() check below
   950   mCheckingOverflow = true;
   951   presShell->FlushPendingNotifications(Flush_Layout);
   952   if (!weakFrame.IsAlive()) {
   953     return;
   954   }
   955   mCheckingOverflow = false;
   956 }
   958 void
   959 nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame)
   960 {
   961   if (mUpdateBatchNest || !mView)
   962     return;
   963   nsWeakFrame weakFrame(this);
   965   if (aParts.mVScrollbar) {
   966     // Do Vertical Scrollbar 
   967     nsAutoString maxposStr;
   969     nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   971     int32_t size = rowHeightAsPixels * (mRowCount > mPageLength ? mRowCount - mPageLength : 0);
   972     maxposStr.AppendInt(size);
   973     aParts.mVScrollbarContent->
   974       SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true);
   975     ENSURE_TRUE(weakFrame.IsAlive());
   977     // Also set our page increment and decrement.
   978     nscoord pageincrement = mPageLength*rowHeightAsPixels;
   979     nsAutoString pageStr;
   980     pageStr.AppendInt(pageincrement);
   981     aParts.mVScrollbarContent->
   982       SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true);
   983     ENSURE_TRUE(weakFrame.IsAlive());
   984   }
   986   if (aParts.mHScrollbar && aParts.mColumnsFrame && aWeakColumnsFrame.IsAlive()) {
   987     // And now Horizontal scrollbar
   988     nsRect bounds = aParts.mColumnsFrame->GetRect();
   989     nsAutoString maxposStr;
   991     maxposStr.AppendInt(mHorzWidth > bounds.width ? mHorzWidth - bounds.width : 0);
   992     aParts.mHScrollbarContent->
   993       SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true);
   994     ENSURE_TRUE(weakFrame.IsAlive());
   996     nsAutoString pageStr;
   997     pageStr.AppendInt(bounds.width);
   998     aParts.mHScrollbarContent->
   999       SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true);
  1000     ENSURE_TRUE(weakFrame.IsAlive());
  1002     pageStr.Truncate();
  1003     pageStr.AppendInt(nsPresContext::CSSPixelsToAppUnits(16));
  1004     aParts.mHScrollbarContent->
  1005       SetAttr(kNameSpaceID_None, nsGkAtoms::increment, pageStr, true);
  1008   if (weakFrame.IsAlive() && mScrollbarActivity) {
  1009     mScrollbarActivity->ActivityOccurred();
  1013 // Takes client x/y in pixels, converts them to appunits, and converts into
  1014 // values relative to this nsTreeBodyFrame frame.
  1015 nsPoint
  1016 nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(int32_t aX, int32_t aY)
  1018   nsPoint point(nsPresContext::CSSPixelsToAppUnits(aX),
  1019                 nsPresContext::CSSPixelsToAppUnits(aY));
  1021   nsPresContext* presContext = PresContext();
  1022   point -= GetOffsetTo(presContext->GetPresShell()->GetRootFrame());
  1024   // Adjust by the inner box coords, so that we're in the inner box's
  1025   // coordinate space.
  1026   point -= mInnerBox.TopLeft();
  1027   return point;
  1028 } // AdjustClientCoordsToBoxCoordSpace
  1030 nsresult
  1031 nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY, int32_t* _retval)
  1033   if (!mView)
  1034     return NS_OK;
  1036   nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
  1038   // Check if the coordinates are above our visible space.
  1039   if (point.y < 0) {
  1040     *_retval = -1;
  1041     return NS_OK;
  1044   *_retval = GetRowAt(point.x, point.y);
  1046   return NS_OK;
  1049 nsresult
  1050 nsTreeBodyFrame::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow, nsITreeColumn** aCol,
  1051                            nsACString& aChildElt)
  1053   if (!mView)
  1054     return NS_OK;
  1056   nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
  1058   // Check if the coordinates are above our visible space.
  1059   if (point.y < 0) {
  1060     *aRow = -1;
  1061     return NS_OK;
  1064   nsTreeColumn* col;
  1065   nsIAtom* child;
  1066   GetCellAt(point.x, point.y, aRow, &col, &child);
  1068   if (col) {
  1069     NS_ADDREF(*aCol = col);
  1070     if (child == nsCSSAnonBoxes::moztreecell)
  1071       aChildElt.AssignLiteral("cell");
  1072     else if (child == nsCSSAnonBoxes::moztreetwisty)
  1073       aChildElt.AssignLiteral("twisty");
  1074     else if (child == nsCSSAnonBoxes::moztreeimage)
  1075       aChildElt.AssignLiteral("image");
  1076     else if (child == nsCSSAnonBoxes::moztreecelltext)
  1077       aChildElt.AssignLiteral("text");
  1080   return NS_OK;
  1084 //
  1085 // GetCoordsForCellItem
  1086 //
  1087 // Find the x/y location and width/height (all in PIXELS) of the given object
  1088 // in the given column. 
  1089 //
  1090 // XXX IMPORTANT XXX:
  1091 // Hyatt says in the bug for this, that the following needs to be done:
  1092 // (1) You need to deal with overflow when computing cell rects.  See other column 
  1093 // iteration examples... if you don't deal with this, you'll mistakenly extend the 
  1094 // cell into the scrollbar's rect.
  1095 //
  1096 // (2) You are adjusting the cell rect by the *row" border padding.  That's 
  1097 // wrong.  You need to first adjust a row rect by its border/padding, and then the 
  1098 // cell rect fits inside the adjusted row rect.  It also can have border/padding 
  1099 // as well as margins.  The vertical direction isn't that important, but you need 
  1100 // to get the horizontal direction right.
  1101 //
  1102 // (3) GetImageSize() does not include margins (but it does include border/padding).  
  1103 // You need to make sure to add in the image's margins as well.
  1104 //
  1105 nsresult
  1106 nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsACString& aElement, 
  1107                                       int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight)
  1109   *aX = 0;
  1110   *aY = 0;
  1111   *aWidth = 0;
  1112   *aHeight = 0;
  1114   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1115   nscoord currX = mInnerBox.x - mHorzPosition;
  1117   // The Rect for the requested item. 
  1118   nsRect theRect;
  1120   nsPresContext* presContext = PresContext();
  1122   for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; currCol = currCol->GetNext()) {
  1124     // The Rect for the current cell.
  1125     nscoord colWidth;
  1126 #ifdef DEBUG
  1127     nsresult rv =
  1128 #endif
  1129       currCol->GetWidthInTwips(this, &colWidth);
  1130     NS_ASSERTION(NS_SUCCEEDED(rv), "invalid column");
  1132     nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex),
  1133                     colWidth, mRowHeight);
  1135     // Check the ID of the current column to see if it matches. If it doesn't 
  1136     // increment the current X value and continue to the next column.
  1137     if (currCol != aCol) {
  1138       currX += cellRect.width;
  1139       continue;
  1141     // Now obtain the properties for our cell.
  1142     PrefillPropertyArray(aRow, currCol);
  1144     nsAutoString properties;
  1145     mView->GetCellProperties(aRow, currCol, properties);
  1146     nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  1148     nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
  1150     // We don't want to consider any of the decorations that may be present
  1151     // on the current row, so we have to deflate the rect by the border and 
  1152     // padding and offset its left and top coordinates appropriately. 
  1153     AdjustForBorderPadding(rowContext, cellRect);
  1155     nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1157     NS_NAMED_LITERAL_CSTRING(cell, "cell");
  1158     if (currCol->IsCycler() || cell.Equals(aElement)) {
  1159       // If the current Column is a Cycler, then the Rect is just the cell - the margins. 
  1160       // Similarly, if we're just being asked for the cell rect, provide it. 
  1162       theRect = cellRect;
  1163       nsMargin cellMargin;
  1164       cellContext->StyleMargin()->GetMargin(cellMargin);
  1165       theRect.Deflate(cellMargin);
  1166       break;
  1169     // Since we're not looking for the cell, and since the cell isn't a cycler,
  1170     // we're looking for some subcomponent, and now we need to subtract the 
  1171     // borders and padding of the cell from cellRect so this does not 
  1172     // interfere with our computations.
  1173     AdjustForBorderPadding(cellContext, cellRect);
  1175     nsRefPtr<nsRenderingContext> rc =
  1176       presContext->PresShell()->CreateReferenceRenderingContext();
  1178     // Now we'll start making our way across the cell, starting at the edge of 
  1179     // the cell and proceeding until we hit the right edge. |cellX| is the 
  1180     // working X value that we will increment as we crawl from left to right.
  1181     nscoord cellX = cellRect.x;
  1182     nscoord remainWidth = cellRect.width;
  1184     if (currCol->IsPrimary()) {
  1185       // If the current Column is a Primary, then we need to take into account the indentation
  1186       // and possibly a twisty. 
  1188       // The amount of indentation is the indentation width (|mIndentation|) by the level. 
  1189       int32_t level;
  1190       mView->GetLevel(aRow, &level);
  1191       if (!isRTL)
  1192         cellX += mIndentation * level;
  1193       remainWidth -= mIndentation * level;
  1195       // Find the twisty rect by computing its size. 
  1196       nsRect imageRect;
  1197       nsRect twistyRect(cellRect);
  1198       nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1199       GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext,
  1200                     *rc, twistyContext);
  1202       if (NS_LITERAL_CSTRING("twisty").Equals(aElement)) {
  1203         // If we're looking for the twisty Rect, just return the size
  1204         theRect = twistyRect;
  1205         break;
  1208       // Now we need to add in the margins of the twisty element, so that we 
  1209       // can find the offset of the next element in the cell. 
  1210       nsMargin twistyMargin;
  1211       twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1212       twistyRect.Inflate(twistyMargin);
  1214       // Adjust our working X value with the twisty width (image size, margins,
  1215       // borders, padding.
  1216       if (!isRTL)
  1217         cellX += twistyRect.width;
  1220     // Cell Image
  1221     nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  1223     nsRect imageSize = GetImageSize(aRow, currCol, false, imageContext);
  1224     if (NS_LITERAL_CSTRING("image").Equals(aElement)) {
  1225       theRect = imageSize;
  1226       theRect.x = cellX;
  1227       theRect.y = cellRect.y;
  1228       break;
  1231     // Add in the margins of the cell image.
  1232     nsMargin imageMargin;
  1233     imageContext->StyleMargin()->GetMargin(imageMargin);
  1234     imageSize.Inflate(imageMargin);
  1236     // Increment cellX by the image width
  1237     if (!isRTL)
  1238       cellX += imageSize.width;
  1240     // Cell Text 
  1241     nsAutoString cellText;
  1242     mView->GetCellText(aRow, currCol, cellText);
  1243     // We're going to measure this text so we need to ensure bidi is enabled if
  1244     // necessary
  1245     CheckTextForBidi(cellText);
  1247     // Create a scratch rect to represent the text rectangle, with the current 
  1248     // X and Y coords, and a guess at the width and height. The width is the 
  1249     // remaining width we have left to traverse in the cell, which will be the
  1250     // widest possible value for the text rect, and the row height. 
  1251     nsRect textRect(cellX, cellRect.y, remainWidth, cellRect.height);
  1253     // Measure the width of the text. If the width of the text is greater than 
  1254     // the remaining width available, then we just assume that the text has 
  1255     // been cropped and use the remaining rect as the text Rect. Otherwise,
  1256     // we add in borders and padding to the text dimension and give that back. 
  1257     nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  1259     nsRefPtr<nsFontMetrics> fm;
  1260     nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  1261                                                  getter_AddRefs(fm));
  1262     nscoord height = fm->MaxHeight();
  1264     nsMargin textMargin;
  1265     textContext->StyleMargin()->GetMargin(textMargin);
  1266     textRect.Deflate(textMargin);
  1268     // Center the text. XXX Obey vertical-align style prop?
  1269     if (height < textRect.height) {
  1270       textRect.y += (textRect.height - height) / 2;
  1271       textRect.height = height;
  1274     nsMargin bp(0,0,0,0);
  1275     GetBorderPadding(textContext, bp);
  1276     textRect.height += bp.top + bp.bottom;
  1278     rc->SetFont(fm);
  1279     AdjustForCellText(cellText, aRow, currCol, *rc, textRect);
  1281     theRect = textRect;
  1284   if (isRTL)
  1285     theRect.x = mInnerBox.width - theRect.x - theRect.width;
  1287   *aX = nsPresContext::AppUnitsToIntCSSPixels(theRect.x);
  1288   *aY = nsPresContext::AppUnitsToIntCSSPixels(theRect.y);
  1289   *aWidth = nsPresContext::AppUnitsToIntCSSPixels(theRect.width);
  1290   *aHeight = nsPresContext::AppUnitsToIntCSSPixels(theRect.height);
  1292   return NS_OK;
  1295 int32_t
  1296 nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY)
  1298   // Now just mod by our total inner box height and add to our top row index.
  1299   int32_t row = (aY/mRowHeight)+mTopRowIndex;
  1301   // Check if the coordinates are below our visible space (or within our visible
  1302   // space but below any row).
  1303   if (row > mTopRowIndex + mPageLength || row >= mRowCount)
  1304     return -1;
  1306   return row;
  1309 void
  1310 nsTreeBodyFrame::CheckTextForBidi(nsAutoString& aText)
  1312   // We could check to see whether the prescontext already has bidi enabled,
  1313   // but usually it won't, so it's probably faster to avoid the call to
  1314   // GetPresContext() when it's not needed.
  1315   if (HasRTLChars(aText)) {
  1316     PresContext()->SetBidiEnabled();
  1320 void
  1321 nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText,
  1322                                    int32_t aRowIndex,  nsTreeColumn* aColumn,
  1323                                    nsRenderingContext& aRenderingContext,
  1324                                    nsRect& aTextRect)
  1326   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1328   nscoord width =
  1329     nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length());
  1330   nscoord maxWidth = aTextRect.width;
  1332   if (aColumn->Overflow()) {
  1333     DebugOnly<nsresult> rv;
  1334     nsTreeColumn* nextColumn = aColumn->GetNext();
  1335     while (nextColumn && width > maxWidth) {
  1336       while (nextColumn) {
  1337         nscoord width;
  1338         rv = nextColumn->GetWidthInTwips(this, &width);
  1339         NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
  1341         if (width != 0)
  1342           break;
  1344         nextColumn = nextColumn->GetNext();
  1347       if (nextColumn) {
  1348         nsAutoString nextText;
  1349         mView->GetCellText(aRowIndex, nextColumn, nextText);
  1350         // We don't measure or draw this text so no need to check it for
  1351         // bidi-ness
  1353         if (nextText.Length() == 0) {
  1354           nscoord width;
  1355           rv = nextColumn->GetWidthInTwips(this, &width);
  1356           NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
  1358           maxWidth += width;
  1360           nextColumn = nextColumn->GetNext();
  1362         else {
  1363           nextColumn = nullptr;
  1369   if (width > maxWidth) {
  1370     // See if the width is even smaller than the ellipsis
  1371     // If so, clear the text completely.
  1372     const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
  1373     aRenderingContext.SetTextRunRTL(false);
  1374     nscoord ellipsisWidth = aRenderingContext.GetWidth(kEllipsis);
  1376     width = maxWidth;
  1377     if (ellipsisWidth > width)
  1378       aText.SetLength(0);
  1379     else if (ellipsisWidth == width)
  1380       aText.Assign(kEllipsis);
  1381     else {
  1382       // We will be drawing an ellipsis, thank you very much.
  1383       // Subtract out the required width of the ellipsis.
  1384       // This is the total remaining width we have to play with.
  1385       width -= ellipsisWidth;
  1387       // Now we crop.
  1388       switch (aColumn->GetCropStyle()) {
  1389         default:
  1390         case 0: {
  1391           // Crop right.
  1392           nscoord cwidth;
  1393           nscoord twidth = 0;
  1394           uint32_t length = aText.Length();
  1395           uint32_t i;
  1396           for (i = 0; i < length; ++i) {
  1397             char16_t ch = aText[i];
  1398             // XXX this is horrible and doesn't handle clusters
  1399             cwidth = aRenderingContext.GetWidth(ch);
  1400             if (twidth + cwidth > width)
  1401               break;
  1402             twidth += cwidth;
  1404           aText.Truncate(i);
  1405           aText.Append(kEllipsis);
  1407         break;
  1409         case 2: {
  1410           // Crop left.
  1411           nscoord cwidth;
  1412           nscoord twidth = 0;
  1413           int32_t length = aText.Length();
  1414           int32_t i;
  1415           for (i=length-1; i >= 0; --i) {
  1416             char16_t ch = aText[i];
  1417             cwidth = aRenderingContext.GetWidth(ch);
  1418             if (twidth + cwidth > width)
  1419               break;
  1420             twidth += cwidth;
  1423           nsAutoString copy;
  1424           aText.Right(copy, length-1-i);
  1425           aText.Assign(kEllipsis);
  1426           aText += copy;
  1428         break;
  1430         case 1:
  1432           // Crop center.
  1433           nsAutoString leftStr, rightStr;
  1434           nscoord cwidth, twidth = 0;
  1435           int32_t length = aText.Length();
  1436           int32_t rightPos = length - 1;
  1437           for (int32_t leftPos = 0; leftPos < rightPos; ++leftPos) {
  1438             char16_t ch = aText[leftPos];
  1439             cwidth = aRenderingContext.GetWidth(ch);
  1440             twidth += cwidth;
  1441             if (twidth > width)
  1442               break;
  1443             leftStr.Append(ch);
  1445             ch = aText[rightPos];
  1446             cwidth = aRenderingContext.GetWidth(ch);
  1447             twidth += cwidth;
  1448             if (twidth > width)
  1449               break;
  1450             rightStr.Insert(ch, 0);
  1451             --rightPos;
  1453           aText = leftStr;
  1454           aText.Append(kEllipsis);
  1455           aText += rightStr;
  1457         break;
  1461     width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length());
  1464   switch (aColumn->GetTextAlignment()) {
  1465     case NS_STYLE_TEXT_ALIGN_RIGHT: {
  1466       aTextRect.x += aTextRect.width - width;
  1468     break;
  1469     case NS_STYLE_TEXT_ALIGN_CENTER: {
  1470       aTextRect.x += (aTextRect.width - width) / 2;
  1472     break;
  1475   aTextRect.width = width;
  1478 nsIAtom*
  1479 nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, 
  1480                                      int32_t aRowIndex,
  1481                                      nsTreeColumn* aColumn)
  1483   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1485   // Obtain the properties for our cell.
  1486   PrefillPropertyArray(aRowIndex, aColumn);
  1487   nsAutoString properties;
  1488   mView->GetCellProperties(aRowIndex, aColumn, properties);
  1489   nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  1491   // Resolve style for the cell.
  1492   nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1494   // Obtain the margins for the cell and then deflate our rect by that 
  1495   // amount.  The cell is assumed to be contained within the deflated rect.
  1496   nsRect cellRect(aCellRect);
  1497   nsMargin cellMargin;
  1498   cellContext->StyleMargin()->GetMargin(cellMargin);
  1499   cellRect.Deflate(cellMargin);
  1501   // Adjust the rect for its border and padding.
  1502   AdjustForBorderPadding(cellContext, cellRect);
  1504   if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) {
  1505     // The user clicked within the cell's margins/borders/padding.  This constitutes a click on the cell.
  1506     return nsCSSAnonBoxes::moztreecell;
  1509   nscoord currX = cellRect.x;
  1510   nscoord remainingWidth = cellRect.width;
  1512   // Handle right alignment hit testing.
  1513   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1515   nsPresContext* presContext = PresContext();
  1516   nsRefPtr<nsRenderingContext> rc =
  1517     presContext->PresShell()->CreateReferenceRenderingContext();
  1519   if (aColumn->IsPrimary()) {
  1520     // If we're the primary column, we have indentation and a twisty.
  1521     int32_t level;
  1522     mView->GetLevel(aRowIndex, &level);
  1524     if (!isRTL)
  1525       currX += mIndentation*level;
  1526     remainingWidth -= mIndentation*level;
  1528     if ((isRTL && aX > currX + remainingWidth) ||
  1529         (!isRTL && aX < currX)) {
  1530       // The user clicked within the indentation.
  1531       return nsCSSAnonBoxes::moztreecell;
  1534     // Always leave space for the twisty.
  1535     nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1536     bool hasTwisty = false;
  1537     bool isContainer = false;
  1538     mView->IsContainer(aRowIndex, &isContainer);
  1539     if (isContainer) {
  1540       bool isContainerEmpty = false;
  1541       mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
  1542       if (!isContainerEmpty)
  1543         hasTwisty = true;
  1546     // Resolve style for the twisty.
  1547     nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1549     nsRect imageSize;
  1550     GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, presContext,
  1551                   *rc, twistyContext);
  1553     // We will treat a click as hitting the twisty if it happens on the margins, borders, padding,
  1554     // or content of the twisty object.  By allowing a "slop" into the margin, we make it a little
  1555     // bit easier for a user to hit the twisty.  (We don't want to be too picky here.)
  1556     nsMargin twistyMargin;
  1557     twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1558     twistyRect.Inflate(twistyMargin);
  1559     if (isRTL)
  1560       twistyRect.x = currX + remainingWidth - twistyRect.width;
  1562     // Now we test to see if aX is actually within the twistyRect.  If it is, and if the item should
  1563     // have a twisty, then we return "twisty".  If it is within the rect but we shouldn't have a twisty,
  1564     // then we return "cell".
  1565     if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) {
  1566       if (hasTwisty)
  1567         return nsCSSAnonBoxes::moztreetwisty;
  1568       else
  1569         return nsCSSAnonBoxes::moztreecell;
  1572     if (!isRTL)
  1573       currX += twistyRect.width;
  1574     remainingWidth -= twistyRect.width;    
  1577   // Now test to see if the user hit the icon for the cell.
  1578   nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1580   // Resolve style for the image.
  1581   nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  1583   nsRect iconSize = GetImageSize(aRowIndex, aColumn, false, imageContext);
  1584   nsMargin imageMargin;
  1585   imageContext->StyleMargin()->GetMargin(imageMargin);
  1586   iconSize.Inflate(imageMargin);
  1587   iconRect.width = iconSize.width;
  1588   if (isRTL)
  1589     iconRect.x = currX + remainingWidth - iconRect.width;
  1591   if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) {
  1592     // The user clicked on the image.
  1593     return nsCSSAnonBoxes::moztreeimage;
  1596   if (!isRTL)
  1597     currX += iconRect.width;
  1598   remainingWidth -= iconRect.width;    
  1600   nsAutoString cellText;
  1601   mView->GetCellText(aRowIndex, aColumn, cellText);
  1602   // We're going to measure this text so we need to ensure bidi is enabled if
  1603   // necessary
  1604   CheckTextForBidi(cellText);
  1606   nsRect textRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1608   nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  1610   nsMargin textMargin;
  1611   textContext->StyleMargin()->GetMargin(textMargin);
  1612   textRect.Deflate(textMargin);
  1614   AdjustForBorderPadding(textContext, textRect);
  1616   nsRefPtr<nsFontMetrics> fm;
  1617   nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  1618                                                getter_AddRefs(fm));
  1619   rc->SetFont(fm);
  1621   AdjustForCellText(cellText, aRowIndex, aColumn, *rc, textRect);
  1623   if (aX >= textRect.x && aX < textRect.x + textRect.width)
  1624     return nsCSSAnonBoxes::moztreecelltext;
  1625   else
  1626     return nsCSSAnonBoxes::moztreecell;
  1629 void
  1630 nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow,
  1631                            nsTreeColumn** aCol, nsIAtom** aChildElt)
  1633   *aCol = nullptr;
  1634   *aChildElt = nullptr;
  1636   *aRow = GetRowAt(aX, aY);
  1637   if (*aRow < 0)
  1638     return;
  1640   // Determine the column hit.
  1641   for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; 
  1642        currCol = currCol->GetNext()) {
  1643     nsRect cellRect;
  1644     nsresult rv = currCol->GetRect(this,
  1645                                    mInnerBox.y +
  1646                                          mRowHeight * (*aRow - mTopRowIndex),
  1647                                    mRowHeight,
  1648                                    &cellRect);
  1649     if (NS_FAILED(rv)) {
  1650       NS_NOTREACHED("column has no frame");
  1651       continue;
  1654     if (!OffsetForHorzScroll(cellRect, false))
  1655       continue;
  1657     if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) {
  1658       // We know the column hit now.
  1659       *aCol = currCol;
  1661       if (currCol->IsCycler())
  1662         // Cyclers contain only images.  Fill this in immediately and return.
  1663         *aChildElt = nsCSSAnonBoxes::moztreeimage;
  1664       else
  1665         *aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol);
  1666       break;
  1671 nsresult
  1672 nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
  1673                               nsRenderingContext* aRenderingContext,
  1674                               nscoord& aDesiredSize, nscoord& aCurrentSize)
  1676   NS_PRECONDITION(aCol, "aCol must not be null");
  1677   NS_PRECONDITION(aRenderingContext, "aRenderingContext must not be null");
  1679   // The rect for the current cell.
  1680   nscoord colWidth;
  1681   nsresult rv = aCol->GetWidthInTwips(this, &colWidth);
  1682   NS_ENSURE_SUCCESS(rv, rv);
  1684   nsRect cellRect(0, 0, colWidth, mRowHeight);
  1686   int32_t overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
  1687   if (overflow > 0)
  1688     cellRect.width -= overflow;
  1690   // Adjust borders and padding for the cell.
  1691   nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1692   nsMargin bp(0,0,0,0);
  1693   GetBorderPadding(cellContext, bp);
  1695   aCurrentSize = cellRect.width;
  1696   aDesiredSize = bp.left + bp.right;
  1698   if (aCol->IsPrimary()) {
  1699     // If the current Column is a Primary, then we need to take into account 
  1700     // the indentation and possibly a twisty. 
  1702     // The amount of indentation is the indentation width (|mIndentation|) by the level.
  1703     int32_t level;
  1704     mView->GetLevel(aRow, &level);
  1705     aDesiredSize += mIndentation * level;
  1707     // Find the twisty rect by computing its size.
  1708     nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1710     nsRect imageSize;
  1711     nsRect twistyRect(cellRect);
  1712     GetTwistyRect(aRow, aCol, imageSize, twistyRect, PresContext(),
  1713                   *aRenderingContext, twistyContext);
  1715     // Add in the margins of the twisty element.
  1716     nsMargin twistyMargin;
  1717     twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1718     twistyRect.Inflate(twistyMargin);
  1720     aDesiredSize += twistyRect.width;
  1723   nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  1725   // Account for the width of the cell image.
  1726   nsRect imageSize = GetImageSize(aRow, aCol, false, imageContext);
  1727   // Add in the margins of the cell image.
  1728   nsMargin imageMargin;
  1729   imageContext->StyleMargin()->GetMargin(imageMargin);
  1730   imageSize.Inflate(imageMargin);
  1732   aDesiredSize += imageSize.width;
  1734   // Get the cell text.
  1735   nsAutoString cellText;
  1736   mView->GetCellText(aRow, aCol, cellText);
  1737   // We're going to measure this text so we need to ensure bidi is enabled if
  1738   // necessary
  1739   CheckTextForBidi(cellText);
  1741   nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  1743   // Get the borders and padding for the text.
  1744   GetBorderPadding(textContext, bp);
  1746   nsRefPtr<nsFontMetrics> fm;
  1747   nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  1748                                                getter_AddRefs(fm));
  1749   aRenderingContext->SetFont(fm);
  1751   // Get the width of the text itself
  1752   nscoord width =
  1753     nsLayoutUtils::GetStringWidth(this, aRenderingContext, cellText.get(), cellText.Length());
  1754   nscoord totalTextWidth = width + bp.left + bp.right;
  1755   aDesiredSize += totalTextWidth;
  1756   return NS_OK;
  1759 nsresult
  1760 nsTreeBodyFrame::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *_retval)
  1762   nscoord currentSize, desiredSize;
  1763   nsresult rv;
  1765   nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
  1766   if (!col)
  1767     return NS_ERROR_INVALID_ARG;
  1769   nsRefPtr<nsRenderingContext> rc =
  1770     PresContext()->PresShell()->CreateReferenceRenderingContext();
  1772   rv = GetCellWidth(aRow, col, rc, desiredSize, currentSize);
  1773   NS_ENSURE_SUCCESS(rv, rv);
  1775   *_retval = desiredSize > currentSize;
  1777   return NS_OK;
  1780 void
  1781 nsTreeBodyFrame::MarkDirtyIfSelect()
  1783   nsIContent* baseElement = GetBaseElement();
  1785   if (baseElement && baseElement->Tag() == nsGkAtoms::select &&
  1786       baseElement->IsHTML()) {
  1787     // If we are an intrinsically sized select widget, we may need to
  1788     // resize, if the widest item was removed or a new item was added.
  1789     // XXX optimize this more
  1791     mStringWidth = -1;
  1792     PresContext()->PresShell()->FrameNeedsReflow(this,
  1793                                                  nsIPresShell::eTreeChange,
  1794                                                  NS_FRAME_IS_DIRTY);
  1798 nsresult
  1799 nsTreeBodyFrame::CreateTimer(const LookAndFeel::IntID aID,
  1800                              nsTimerCallbackFunc aFunc, int32_t aType,
  1801                              nsITimer** aTimer)
  1803   // Get the delay from the look and feel service.
  1804   int32_t delay = LookAndFeel::GetInt(aID, 0);
  1806   nsCOMPtr<nsITimer> timer;
  1808   // Create a new timer only if the delay is greater than zero.
  1809   // Zero value means that this feature is completely disabled.
  1810   if (delay > 0) {
  1811     timer = do_CreateInstance("@mozilla.org/timer;1");
  1812     if (timer)
  1813       timer->InitWithFuncCallback(aFunc, this, delay, aType);
  1816   NS_IF_ADDREF(*aTimer = timer);
  1818   return NS_OK;
  1821 nsresult
  1822 nsTreeBodyFrame::RowCountChanged(int32_t aIndex, int32_t aCount)
  1824   if (aCount == 0 || !mView)
  1825     return NS_OK; // Nothing to do.
  1827 #ifdef ACCESSIBILITY
  1828   if (nsIPresShell::IsAccessibilityActive())
  1829     FireRowCountChangedEvent(aIndex, aCount);
  1830 #endif
  1832   // Adjust our selection.
  1833   nsCOMPtr<nsITreeSelection> sel;
  1834   mView->GetSelection(getter_AddRefs(sel));
  1835   if (sel)
  1836     sel->AdjustSelection(aIndex, aCount);
  1838   if (mUpdateBatchNest)
  1839     return NS_OK;
  1841   mRowCount += aCount;
  1842 #ifdef DEBUG
  1843   int32_t rowCount = mRowCount;
  1844   mView->GetRowCount(&rowCount);
  1845   NS_ASSERTION(rowCount == mRowCount, "row count did not change by the amount suggested, check caller");
  1846 #endif
  1848   int32_t count = Abs(aCount);
  1849   int32_t last = LastVisibleRow();
  1850   if (aIndex >= mTopRowIndex && aIndex <= last)
  1851     InvalidateRange(aIndex, last);
  1853   ScrollParts parts = GetScrollParts();
  1855   if (mTopRowIndex == 0) {    
  1856     // Just update the scrollbar and return.
  1857     if (FullScrollbarsUpdate(false)) {
  1858       MarkDirtyIfSelect();
  1860     return NS_OK;
  1863   bool needsInvalidation = false;
  1864   // Adjust our top row index.
  1865   if (aCount > 0) {
  1866     if (mTopRowIndex > aIndex) {
  1867       // Rows came in above us.  Augment the top row index.
  1868       mTopRowIndex += aCount;
  1871   else if (aCount < 0) {
  1872     if (mTopRowIndex > aIndex+count-1) {
  1873       // No need to invalidate. The remove happened
  1874       // completely above us (offscreen).
  1875       mTopRowIndex -= count;
  1877     else if (mTopRowIndex >= aIndex) {
  1878       // This is a full-blown invalidate.
  1879       if (mTopRowIndex + mPageLength > mRowCount - 1) {
  1880         mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
  1882       needsInvalidation = true;
  1886   if (FullScrollbarsUpdate(needsInvalidation)) {
  1887     MarkDirtyIfSelect();
  1889   return NS_OK;
  1892 nsresult
  1893 nsTreeBodyFrame::BeginUpdateBatch()
  1895   ++mUpdateBatchNest;
  1897   return NS_OK;
  1900 nsresult
  1901 nsTreeBodyFrame::EndUpdateBatch()
  1903   NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
  1905   if (--mUpdateBatchNest == 0) {
  1906     if (mView) {
  1907       Invalidate();
  1908       int32_t countBeforeUpdate = mRowCount;
  1909       mView->GetRowCount(&mRowCount);
  1910       if (countBeforeUpdate != mRowCount) {
  1911         if (mTopRowIndex + mPageLength > mRowCount - 1) {
  1912           mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
  1914         FullScrollbarsUpdate(false);
  1919   return NS_OK;
  1922 void
  1923 nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex, nsTreeColumn* aCol)
  1925   NS_PRECONDITION(!aCol || aCol->GetFrame(), "invalid column passed");
  1926   mScratchArray.Clear();
  1928   // focus
  1929   if (mFocused)
  1930     mScratchArray.AppendElement(nsGkAtoms::focus);
  1932   // sort
  1933   bool sorted = false;
  1934   mView->IsSorted(&sorted);
  1935   if (sorted)
  1936     mScratchArray.AppendElement(nsGkAtoms::sorted);
  1938   // drag session
  1939   if (mSlots && mSlots->mIsDragging)
  1940     mScratchArray.AppendElement(nsGkAtoms::dragSession);
  1942   if (aRowIndex != -1) {
  1943     if (aRowIndex == mMouseOverRow)
  1944       mScratchArray.AppendElement(nsGkAtoms::hover);
  1946     nsCOMPtr<nsITreeSelection> selection;
  1947     mView->GetSelection(getter_AddRefs(selection));
  1949     if (selection) {
  1950       // selected
  1951       bool isSelected;
  1952       selection->IsSelected(aRowIndex, &isSelected);
  1953       if (isSelected)
  1954         mScratchArray.AppendElement(nsGkAtoms::selected);
  1956       // current
  1957       int32_t currentIndex;
  1958       selection->GetCurrentIndex(&currentIndex);
  1959       if (aRowIndex == currentIndex)
  1960         mScratchArray.AppendElement(nsGkAtoms::current);
  1962       // active
  1963       if (aCol) {
  1964         nsCOMPtr<nsITreeColumn> currentColumn;
  1965         selection->GetCurrentColumn(getter_AddRefs(currentColumn));
  1966         if (aCol == currentColumn)
  1967           mScratchArray.AppendElement(nsGkAtoms::active);
  1971     // container or leaf
  1972     bool isContainer = false;
  1973     mView->IsContainer(aRowIndex, &isContainer);
  1974     if (isContainer) {
  1975       mScratchArray.AppendElement(nsGkAtoms::container);
  1977       // open or closed
  1978       bool isOpen = false;
  1979       mView->IsContainerOpen(aRowIndex, &isOpen);
  1980       if (isOpen)
  1981         mScratchArray.AppendElement(nsGkAtoms::open);
  1982       else
  1983         mScratchArray.AppendElement(nsGkAtoms::closed);
  1985     else {
  1986       mScratchArray.AppendElement(nsGkAtoms::leaf);
  1989     // drop orientation
  1990     if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) {
  1991       if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE)
  1992         mScratchArray.AppendElement(nsGkAtoms::dropBefore);
  1993       else if (mSlots->mDropOrient == nsITreeView::DROP_ON)
  1994         mScratchArray.AppendElement(nsGkAtoms::dropOn);
  1995       else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
  1996         mScratchArray.AppendElement(nsGkAtoms::dropAfter);
  1999     // odd or even
  2000     if (aRowIndex % 2)
  2001       mScratchArray.AppendElement(nsGkAtoms::odd);
  2002     else
  2003       mScratchArray.AppendElement(nsGkAtoms::even);
  2005     nsIContent* baseContent = GetBaseElement();
  2006     if (baseContent && baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::editing))
  2007       mScratchArray.AppendElement(nsGkAtoms::editing);
  2009     // multiple columns
  2010     if (mColumns->GetColumnAt(1))
  2011       mScratchArray.AppendElement(nsGkAtoms::multicol);
  2014   if (aCol) {
  2015     mScratchArray.AppendElement(aCol->GetAtom());
  2017     if (aCol->IsPrimary())
  2018       mScratchArray.AppendElement(nsGkAtoms::primary);
  2020     if (aCol->GetType() == nsITreeColumn::TYPE_CHECKBOX) {
  2021       mScratchArray.AppendElement(nsGkAtoms::checkbox);
  2023       if (aRowIndex != -1) {
  2024         nsAutoString value;
  2025         mView->GetCellValue(aRowIndex, aCol, value);
  2026         if (value.EqualsLiteral("true"))
  2027           mScratchArray.AppendElement(nsGkAtoms::checked);
  2030     else if (aCol->GetType() == nsITreeColumn::TYPE_PROGRESSMETER) {
  2031       mScratchArray.AppendElement(nsGkAtoms::progressmeter);
  2033       if (aRowIndex != -1) {
  2034         int32_t state;
  2035         mView->GetProgressMode(aRowIndex, aCol, &state);
  2036         if (state == nsITreeView::PROGRESS_NORMAL)
  2037           mScratchArray.AppendElement(nsGkAtoms::progressNormal);
  2038         else if (state == nsITreeView::PROGRESS_UNDETERMINED)
  2039           mScratchArray.AppendElement(nsGkAtoms::progressUndetermined);
  2043     // Read special properties from attributes on the column content node
  2044     if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
  2045                                     nsGkAtoms::insertbefore,
  2046                                     nsGkAtoms::_true, eCaseMatters))
  2047       mScratchArray.AppendElement(nsGkAtoms::insertbefore);
  2048     if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
  2049                                     nsGkAtoms::insertafter,
  2050                                     nsGkAtoms::_true, eCaseMatters))
  2051       mScratchArray.AppendElement(nsGkAtoms::insertafter);
  2055 nsITheme*
  2056 nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex,
  2057                                nsTreeColumn* aColumn,
  2058                                nsRect& aImageRect,
  2059                                nsRect& aTwistyRect,
  2060                                nsPresContext* aPresContext,
  2061                                nsRenderingContext& aRenderingContext,
  2062                                nsStyleContext* aTwistyContext)
  2064   // The twisty rect extends all the way to the end of the cell.  This is incorrect.  We need to
  2065   // determine the twisty rect's true width.  This is done by examining the style context for
  2066   // a width first.  If it has one, we use that.  If it doesn't, we use the image's natural width.
  2067   // If the image hasn't loaded and if no width is specified, then we just bail. If there is
  2068   // a -moz-appearance involved, adjust the rect by the minimum widget size provided by
  2069   // the theme implementation.
  2070   aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext);
  2071   if (aImageRect.height > aTwistyRect.height)
  2072     aImageRect.height = aTwistyRect.height;
  2073   if (aImageRect.width > aTwistyRect.width)
  2074     aImageRect.width = aTwistyRect.width;
  2075   else
  2076     aTwistyRect.width = aImageRect.width;
  2078   bool useTheme = false;
  2079   nsITheme *theme = nullptr;
  2080   const nsStyleDisplay* twistyDisplayData = aTwistyContext->StyleDisplay();
  2081   if (twistyDisplayData->mAppearance) {
  2082     theme = aPresContext->GetTheme();
  2083     if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, twistyDisplayData->mAppearance))
  2084       useTheme = true;
  2087   if (useTheme) {
  2088     nsIntSize minTwistySizePx(0,0);
  2089     bool canOverride = true;
  2090     theme->GetMinimumWidgetSize(&aRenderingContext, this, twistyDisplayData->mAppearance,
  2091                                 &minTwistySizePx, &canOverride);
  2093     // GMWS() returns size in pixels, we need to convert it back to app units
  2094     nsSize minTwistySize;
  2095     minTwistySize.width = aPresContext->DevPixelsToAppUnits(minTwistySizePx.width);
  2096     minTwistySize.height = aPresContext->DevPixelsToAppUnits(minTwistySizePx.height);
  2098     if (aTwistyRect.width < minTwistySize.width || !canOverride)
  2099       aTwistyRect.width = minTwistySize.width;
  2102   return useTheme ? theme : nullptr;
  2105 nsresult
  2106 nsTreeBodyFrame::GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
  2107                           nsStyleContext* aStyleContext, bool& aAllowImageRegions, imgIContainer** aResult)
  2109   *aResult = nullptr;
  2111   nsAutoString imageSrc;
  2112   mView->GetImageSrc(aRowIndex, aCol, imageSrc);
  2113   nsRefPtr<imgRequestProxy> styleRequest;
  2114   if (!aUseContext && !imageSrc.IsEmpty()) {
  2115     aAllowImageRegions = false;
  2117   else {
  2118     // Obtain the URL from the style context.
  2119     aAllowImageRegions = true;
  2120     styleRequest = aStyleContext->StyleList()->GetListStyleImage();
  2121     if (!styleRequest)
  2122       return NS_OK;
  2123     nsCOMPtr<nsIURI> uri;
  2124     styleRequest->GetURI(getter_AddRefs(uri));
  2125     nsAutoCString spec;
  2126     uri->GetSpec(spec);
  2127     CopyUTF8toUTF16(spec, imageSrc);
  2130   // Look the image up in our cache.
  2131   nsTreeImageCacheEntry entry;
  2132   if (mImageCache.Get(imageSrc, &entry)) {
  2133     // Find out if the image has loaded.
  2134     uint32_t status;
  2135     imgIRequest *imgReq = entry.request;
  2136     imgReq->GetImageStatus(&status);
  2137     imgReq->GetImage(aResult); // We hand back the image here.  The GetImage call addrefs *aResult.
  2138     bool animated = true; // Assuming animated is the safe option
  2140     // We can only call GetAnimated if we're decoded
  2141     if (*aResult && (status & imgIRequest::STATUS_DECODE_COMPLETE))
  2142       (*aResult)->GetAnimated(&animated);
  2144     if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || animated) {
  2145       // We either aren't done loading, or we're animating. Add our row as a listener for invalidations.
  2146       nsCOMPtr<imgINotificationObserver> obs;
  2147       imgReq->GetNotificationObserver(getter_AddRefs(obs));
  2149       if (obs) {
  2150         static_cast<nsTreeImageListener*> (obs.get())->AddCell(aRowIndex, aCol);
  2153       return NS_OK;
  2157   if (!*aResult) {
  2158     // Create a new nsTreeImageListener object and pass it our row and column
  2159     // information.
  2160     nsTreeImageListener* listener = new nsTreeImageListener(this);
  2161     if (!listener)
  2162       return NS_ERROR_OUT_OF_MEMORY;
  2164     if (!mCreatedListeners.PutEntry(listener)) {
  2165       return NS_ERROR_FAILURE;
  2168     listener->AddCell(aRowIndex, aCol);
  2169     nsCOMPtr<imgINotificationObserver> imgNotificationObserver = listener;
  2171     nsRefPtr<imgRequestProxy> imageRequest;
  2172     if (styleRequest) {
  2173       styleRequest->Clone(imgNotificationObserver, getter_AddRefs(imageRequest));
  2174     } else {
  2175       nsIDocument* doc = mContent->GetDocument();
  2176       if (!doc)
  2177         // The page is currently being torn down.  Why bother.
  2178         return NS_ERROR_FAILURE;
  2180       nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
  2182       nsCOMPtr<nsIURI> srcURI;
  2183       nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcURI),
  2184                                                 imageSrc,
  2185                                                 doc,
  2186                                                 baseURI);
  2187       if (!srcURI)
  2188         return NS_ERROR_FAILURE;
  2190       // XXXbz what's the origin principal for this stuff that comes from our
  2191       // view?  I guess we should assume that it's the node's principal...
  2192       if (nsContentUtils::CanLoadImage(srcURI, mContent, doc,
  2193                                        mContent->NodePrincipal())) {
  2194         nsresult rv = nsContentUtils::LoadImage(srcURI,
  2195                                                 doc,
  2196                                                 mContent->NodePrincipal(),
  2197                                                 doc->GetDocumentURI(),
  2198                                                 imgNotificationObserver,
  2199                                                 nsIRequest::LOAD_NORMAL,
  2200                                                 EmptyString(),
  2201                                                 getter_AddRefs(imageRequest));
  2202         NS_ENSURE_SUCCESS(rv, rv);
  2206     listener->UnsuppressInvalidation();
  2208     if (!imageRequest)
  2209       return NS_ERROR_FAILURE;
  2211     // We don't want discarding/decode-on-draw for xul images
  2212     imageRequest->StartDecoding();
  2213     imageRequest->LockImage();
  2215     // In a case it was already cached.
  2216     imageRequest->GetImage(aResult);
  2217     nsTreeImageCacheEntry cacheEntry(imageRequest, imgNotificationObserver);
  2218     mImageCache.Put(imageSrc, cacheEntry);
  2220   return NS_OK;
  2223 nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
  2224                                      nsStyleContext* aStyleContext)
  2226   // XXX We should respond to visibility rules for collapsed vs. hidden.
  2228   // This method returns the width of the twisty INCLUDING borders and padding.
  2229   // It first checks the style context for a width.  If none is found, it tries to
  2230   // use the default image width for the twisty.  If no image is found, it defaults
  2231   // to border+padding.
  2232   nsRect r(0,0,0,0);
  2233   nsMargin bp(0,0,0,0);
  2234   GetBorderPadding(aStyleContext, bp);
  2235   r.Inflate(bp);
  2237   // Now r contains our border+padding info.  We now need to get our width and
  2238   // height.
  2239   bool needWidth = false;
  2240   bool needHeight = false;
  2242   // We have to load image even though we already have a size.
  2243   // Don't change this, otherwise things start to go crazy.
  2244   bool useImageRegion = true;
  2245   nsCOMPtr<imgIContainer> image;
  2246   GetImage(aRowIndex, aCol, aUseContext, aStyleContext, useImageRegion, getter_AddRefs(image));
  2248   const nsStylePosition* myPosition = aStyleContext->StylePosition();
  2249   const nsStyleList* myList = aStyleContext->StyleList();
  2251   if (useImageRegion) {
  2252     r.x += myList->mImageRegion.x;
  2253     r.y += myList->mImageRegion.y;
  2256   if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord)  {
  2257     int32_t val = myPosition->mWidth.GetCoordValue();
  2258     r.width += val;
  2260   else if (useImageRegion && myList->mImageRegion.width > 0)
  2261     r.width += myList->mImageRegion.width;
  2262   else 
  2263     needWidth = true;
  2265   if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)  {
  2266     int32_t val = myPosition->mHeight.GetCoordValue();
  2267     r.height += val;
  2269   else if (useImageRegion && myList->mImageRegion.height > 0)
  2270     r.height += myList->mImageRegion.height;
  2271   else 
  2272     needHeight = true;
  2274   if (image) {
  2275     if (needWidth || needHeight) {
  2276       // Get the natural image size.
  2278       if (needWidth) {
  2279         // Get the size from the image.
  2280         nscoord width;
  2281         image->GetWidth(&width);
  2282         r.width += nsPresContext::CSSPixelsToAppUnits(width); 
  2285       if (needHeight) {
  2286         nscoord height;
  2287         image->GetHeight(&height);
  2288         r.height += nsPresContext::CSSPixelsToAppUnits(height); 
  2293   return r;
  2296 // GetImageDestSize returns the destination size of the image.
  2297 // The width and height do not include borders and padding.
  2298 // The width and height have not been adjusted to fit in the row height
  2299 // or cell width.
  2300 // The width and height reflect the destination size specified in CSS,
  2301 // or the image region specified in CSS, or the natural size of the
  2302 // image.
  2303 // If only the destination width has been specified in CSS, the height is
  2304 // calculated to maintain the aspect ratio of the image.
  2305 // If only the destination height has been specified in CSS, the width is
  2306 // calculated to maintain the aspect ratio of the image.
  2307 nsSize
  2308 nsTreeBodyFrame::GetImageDestSize(nsStyleContext* aStyleContext,
  2309                                   bool useImageRegion,
  2310                                   imgIContainer* image)
  2312   nsSize size(0,0);
  2314   // We need to get the width and height.
  2315   bool needWidth = false;
  2316   bool needHeight = false;
  2318   // Get the style position to see if the CSS has specified the
  2319   // destination width/height.
  2320   const nsStylePosition* myPosition = aStyleContext->StylePosition();
  2322   if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
  2323     // CSS has specified the destination width.
  2324     size.width = myPosition->mWidth.GetCoordValue();
  2326   else {
  2327     // We'll need to get the width of the image/region.
  2328     needWidth = true;
  2331   if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)  {
  2332     // CSS has specified the destination height.
  2333     size.height = myPosition->mHeight.GetCoordValue();
  2335   else {
  2336     // We'll need to get the height of the image/region.
  2337     needHeight = true;
  2340   if (needWidth || needHeight) {
  2341     // We need to get the size of the image/region.
  2342     nsSize imageSize(0,0);
  2344     const nsStyleList* myList = aStyleContext->StyleList();
  2346     if (useImageRegion && myList->mImageRegion.width > 0) {
  2347       // CSS has specified an image region.
  2348       // Use the width of the image region.
  2349       imageSize.width = myList->mImageRegion.width;
  2351     else if (image) {
  2352       nscoord width;
  2353       image->GetWidth(&width);
  2354       imageSize.width = nsPresContext::CSSPixelsToAppUnits(width);
  2357     if (useImageRegion && myList->mImageRegion.height > 0) {
  2358       // CSS has specified an image region.
  2359       // Use the height of the image region.
  2360       imageSize.height = myList->mImageRegion.height;
  2362     else if (image) {
  2363       nscoord height;
  2364       image->GetHeight(&height);
  2365       imageSize.height = nsPresContext::CSSPixelsToAppUnits(height);
  2368     if (needWidth) {
  2369       if (!needHeight && imageSize.height != 0) {
  2370         // The CSS specified the destination height, but not the destination
  2371         // width. We need to calculate the width so that we maintain the
  2372         // image's aspect ratio.
  2373         size.width = imageSize.width * size.height / imageSize.height;
  2375       else {
  2376         size.width = imageSize.width;
  2380     if (needHeight) {
  2381       if (!needWidth && imageSize.width != 0) {
  2382         // The CSS specified the destination width, but not the destination
  2383         // height. We need to calculate the height so that we maintain the
  2384         // image's aspect ratio.
  2385         size.height = imageSize.height * size.width / imageSize.width;
  2387       else {
  2388         size.height = imageSize.height;
  2393   return size;
  2396 // GetImageSourceRect returns the source rectangle of the image to be
  2397 // displayed.
  2398 // The width and height reflect the image region specified in CSS, or
  2399 // the natural size of the image.
  2400 // The width and height do not include borders and padding.
  2401 // The width and height do not reflect the destination size specified
  2402 // in CSS.
  2403 nsRect
  2404 nsTreeBodyFrame::GetImageSourceRect(nsStyleContext* aStyleContext,
  2405                                     bool useImageRegion,
  2406                                     imgIContainer* image)
  2408   nsRect r(0,0,0,0);
  2410   const nsStyleList* myList = aStyleContext->StyleList();
  2412   if (useImageRegion &&
  2413       (myList->mImageRegion.width > 0 || myList->mImageRegion.height > 0)) {
  2414     // CSS has specified an image region.
  2415     r = myList->mImageRegion;
  2417   else if (image) {
  2418     // Use the actual image size.
  2419     nscoord coord;
  2420     image->GetWidth(&coord);
  2421     r.width = nsPresContext::CSSPixelsToAppUnits(coord);
  2422     image->GetHeight(&coord);
  2423     r.height = nsPresContext::CSSPixelsToAppUnits(coord);
  2426   return r;
  2429 int32_t nsTreeBodyFrame::GetRowHeight()
  2431   // Look up the correct height.  It is equal to the specified height
  2432   // + the specified margins.
  2433   mScratchArray.Clear();
  2434   nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
  2435   if (rowContext) {
  2436     const nsStylePosition* myPosition = rowContext->StylePosition();
  2438     nscoord minHeight = 0;
  2439     if (myPosition->mMinHeight.GetUnit() == eStyleUnit_Coord)
  2440       minHeight = myPosition->mMinHeight.GetCoordValue();
  2442     nscoord height = 0;
  2443     if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)
  2444       height = myPosition->mHeight.GetCoordValue();
  2446     if (height < minHeight)
  2447       height = minHeight;
  2449     if (height > 0) {
  2450       height = nsPresContext::AppUnitsToIntCSSPixels(height);
  2451       height += height % 2;
  2452       height = nsPresContext::CSSPixelsToAppUnits(height);
  2454       // XXX Check box-sizing to determine if border/padding should augment the height
  2455       // Inflate the height by our margins.
  2456       nsRect rowRect(0,0,0,height);
  2457       nsMargin rowMargin;
  2458       rowContext->StyleMargin()->GetMargin(rowMargin);
  2459       rowRect.Inflate(rowMargin);
  2460       height = rowRect.height;
  2461       return height;
  2465   return nsPresContext::CSSPixelsToAppUnits(18); // As good a default as any.
  2468 int32_t nsTreeBodyFrame::GetIndentation()
  2470   // Look up the correct indentation.  It is equal to the specified indentation width.
  2471   mScratchArray.Clear();
  2472   nsStyleContext* indentContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeindentation);
  2473   if (indentContext) {
  2474     const nsStylePosition* myPosition = indentContext->StylePosition();
  2475     if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord)  {
  2476       nscoord val = myPosition->mWidth.GetCoordValue();
  2477       return val;
  2481   return nsPresContext::CSSPixelsToAppUnits(16); // As good a default as any.
  2484 void nsTreeBodyFrame::CalcInnerBox()
  2486   mInnerBox.SetRect(0, 0, mRect.width, mRect.height);
  2487   AdjustForBorderPadding(mStyleContext, mInnerBox);
  2490 nscoord
  2491 nsTreeBodyFrame::CalcHorzWidth(const ScrollParts& aParts)
  2493   // Compute the adjustment to the last column. This varies depending on the
  2494   // visibility of the columnpicker and the scrollbar.
  2495   if (aParts.mColumnsFrame)
  2496     mAdjustWidth = mRect.width - aParts.mColumnsFrame->GetRect().width;
  2497   else
  2498     mAdjustWidth = 0;
  2500   nscoord width = 0;
  2502   // We calculate this from the scrollable frame, so that it 
  2503   // properly covers all contingencies of what could be 
  2504   // scrollable (columns, body, etc...)
  2506   if (aParts.mColumnsScrollFrame) {
  2507     width = aParts.mColumnsScrollFrame->GetScrollRange().width +
  2508       aParts.mColumnsScrollFrame->GetScrollPortRect().width;
  2511   // If no horz scrolling periphery is present, then just return our width
  2512   if (width == 0)
  2513     width = mRect.width;
  2515   return width;
  2518 nsresult
  2519 nsTreeBodyFrame::GetCursor(const nsPoint& aPoint,
  2520                            nsIFrame::Cursor& aCursor)
  2522   // Check the GetScriptHandlingObject so we don't end up running code when
  2523   // the document is a zombie.
  2524   bool dummy;
  2525   if (mView && GetContent()->GetCurrentDoc()->GetScriptHandlingObject(dummy)) {
  2526     int32_t row;
  2527     nsTreeColumn* col;
  2528     nsIAtom* child;
  2529     GetCellAt(aPoint.x, aPoint.y, &row, &col, &child);
  2531     if (child) {
  2532       // Our scratch array is already prefilled.
  2533       nsStyleContext* childContext = GetPseudoStyleContext(child);
  2535       FillCursorInformationFromStyle(childContext->StyleUserInterface(),
  2536                                      aCursor);
  2537       if (aCursor.mCursor == NS_STYLE_CURSOR_AUTO)
  2538         aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
  2540       return NS_OK;
  2544   return nsLeafBoxFrame::GetCursor(aPoint, aCursor);
  2547 static uint32_t GetDropEffect(WidgetGUIEvent* aEvent)
  2549   NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type");
  2550   WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
  2551   nsContentUtils::SetDataTransferInEvent(dragEvent);
  2553   uint32_t action = 0;
  2554   if (dragEvent->dataTransfer)
  2555     dragEvent->dataTransfer->GetDropEffectInt(&action);
  2556   return action;
  2559 nsresult
  2560 nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext,
  2561                              WidgetGUIEvent* aEvent,
  2562                              nsEventStatus* aEventStatus)
  2564   if (aEvent->message == NS_MOUSE_ENTER_SYNTH || aEvent->message == NS_MOUSE_MOVE) {
  2565     nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
  2566     int32_t xTwips = pt.x - mInnerBox.x;
  2567     int32_t yTwips = pt.y - mInnerBox.y;
  2568     int32_t newrow = GetRowAt(xTwips, yTwips);
  2569     if (mMouseOverRow != newrow) {
  2570       // redraw the old and the new row
  2571       if (mMouseOverRow != -1)
  2572         InvalidateRow(mMouseOverRow);
  2573       mMouseOverRow = newrow;
  2574       if (mMouseOverRow != -1)
  2575         InvalidateRow(mMouseOverRow);
  2578   else if (aEvent->message == NS_MOUSE_EXIT_SYNTH) {
  2579     if (mMouseOverRow != -1) {
  2580       InvalidateRow(mMouseOverRow);
  2581       mMouseOverRow = -1;
  2584   else if (aEvent->message == NS_DRAGDROP_ENTER) {
  2585     if (!mSlots)
  2586       mSlots = new Slots();
  2588     // Cache several things we'll need throughout the course of our work. These
  2589     // will all get released on a drag exit.
  2591     if (mSlots->mTimer) {
  2592       mSlots->mTimer->Cancel();
  2593       mSlots->mTimer = nullptr;
  2596     // Cache the drag session.
  2597     mSlots->mIsDragging = true;
  2598     mSlots->mDropRow = -1;
  2599     mSlots->mDropOrient = -1;
  2600     mSlots->mDragAction = GetDropEffect(aEvent);
  2602   else if (aEvent->message == NS_DRAGDROP_OVER) {
  2603     // The mouse is hovering over this tree. If we determine things are
  2604     // different from the last time, invalidate the drop feedback at the old
  2605     // position, query the view to see if the current location is droppable,
  2606     // and then invalidate the drop feedback at the new location if it is.
  2607     // The mouse may or may not have changed position from the last time
  2608     // we were called, so optimize out a lot of the extra notifications by
  2609     // checking if anything changed first. For drop feedback we use drop,
  2610     // dropBefore and dropAfter property.
  2612     if (!mView || !mSlots)
  2613       return NS_OK;
  2615     // Save last values, we will need them.
  2616     int32_t lastDropRow = mSlots->mDropRow;
  2617     int16_t lastDropOrient = mSlots->mDropOrient;
  2618 #ifndef XP_MACOSX
  2619     int16_t lastScrollLines = mSlots->mScrollLines;
  2620 #endif
  2622     // Find out the current drag action
  2623     uint32_t lastDragAction = mSlots->mDragAction;
  2624     mSlots->mDragAction = GetDropEffect(aEvent);
  2626     // Compute the row mouse is over and the above/below/on state.
  2627     // Below we'll use this to see if anything changed.
  2628     // Also check if we want to auto-scroll.
  2629     ComputeDropPosition(aEvent, &mSlots->mDropRow, &mSlots->mDropOrient, &mSlots->mScrollLines);
  2631     // While we're here, handle tracking of scrolling during a drag.
  2632     if (mSlots->mScrollLines) {
  2633       if (mSlots->mDropAllowed) {
  2634         // Invalidate primary cell at old location.
  2635         mSlots->mDropAllowed = false;
  2636         InvalidateDropFeedback(lastDropRow, lastDropOrient);
  2638 #ifdef XP_MACOSX
  2639       ScrollByLines(mSlots->mScrollLines);
  2640 #else
  2641       if (!lastScrollLines) {
  2642         // Cancel any previously initialized timer.
  2643         if (mSlots->mTimer) {
  2644           mSlots->mTimer->Cancel();
  2645           mSlots->mTimer = nullptr;
  2648         // Set a timer to trigger the tree scrolling.
  2649         CreateTimer(LookAndFeel::eIntID_TreeLazyScrollDelay,
  2650                     LazyScrollCallback, nsITimer::TYPE_ONE_SHOT,
  2651                     getter_AddRefs(mSlots->mTimer));
  2653 #endif
  2654       // Bail out to prevent spring loaded timer and feedback line settings.
  2655       return NS_OK;
  2658     // If changed from last time, invalidate primary cell at the old location and if allowed, 
  2659     // invalidate primary cell at the new location. If nothing changed, just bail.
  2660     if (mSlots->mDropRow != lastDropRow ||
  2661         mSlots->mDropOrient != lastDropOrient ||
  2662         mSlots->mDragAction != lastDragAction) {
  2664       // Invalidate row at the old location.
  2665       if (mSlots->mDropAllowed) {
  2666         mSlots->mDropAllowed = false;
  2667         InvalidateDropFeedback(lastDropRow, lastDropOrient);
  2670       if (mSlots->mTimer) {
  2671         // Timer is active but for a different row than the current one, kill it.
  2672         mSlots->mTimer->Cancel();
  2673         mSlots->mTimer = nullptr;
  2676       if (mSlots->mDropRow >= 0) {
  2677         if (!mSlots->mTimer && mSlots->mDropOrient == nsITreeView::DROP_ON) {
  2678           // Either there wasn't a timer running or it was just killed above.
  2679           // If over a folder, start up a timer to open the folder.
  2680           bool isContainer = false;
  2681           mView->IsContainer(mSlots->mDropRow, &isContainer);
  2682           if (isContainer) {
  2683             bool isOpen = false;
  2684             mView->IsContainerOpen(mSlots->mDropRow, &isOpen);
  2685             if (!isOpen) {
  2686               // This node isn't expanded, set a timer to expand it.
  2687               CreateTimer(LookAndFeel::eIntID_TreeOpenDelay,
  2688                           OpenCallback, nsITimer::TYPE_ONE_SHOT,
  2689                           getter_AddRefs(mSlots->mTimer));
  2694         // The dataTransfer was initialized by the call to GetDropEffect above.
  2695         bool canDropAtNewLocation = false;
  2696         mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient,
  2697                        aEvent->AsDragEvent()->dataTransfer,
  2698                        &canDropAtNewLocation);
  2700         if (canDropAtNewLocation) {
  2701           // Invalidate row at the new location.
  2702           mSlots->mDropAllowed = canDropAtNewLocation;
  2703           InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
  2708     // Indicate that the drop is allowed by preventing the default behaviour.
  2709     if (mSlots->mDropAllowed)
  2710       *aEventStatus = nsEventStatus_eConsumeNoDefault;
  2712   else if (aEvent->message == NS_DRAGDROP_DROP) {
  2713      // this event was meant for another frame, so ignore it
  2714      if (!mSlots)
  2715        return NS_OK;
  2717     // Tell the view where the drop happened.
  2719     // Remove the drop folder and all its parents from the array.
  2720     int32_t parentIndex;
  2721     nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex);
  2722     while (NS_SUCCEEDED(rv) && parentIndex >= 0) {
  2723       mSlots->mArray.RemoveElement(parentIndex);
  2724       rv = mView->GetParentIndex(parentIndex, &parentIndex);
  2727     NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type");
  2728     WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
  2729     nsContentUtils::SetDataTransferInEvent(dragEvent);
  2731     mView->Drop(mSlots->mDropRow, mSlots->mDropOrient, dragEvent->dataTransfer);
  2732     mSlots->mDropRow = -1;
  2733     mSlots->mDropOrient = -1;
  2734     mSlots->mIsDragging = false;
  2735     *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop
  2737   else if (aEvent->message == NS_DRAGDROP_EXIT) {
  2738     // this event was meant for another frame, so ignore it
  2739     if (!mSlots)
  2740       return NS_OK;
  2742     // Clear out all our tracking vars.
  2744     if (mSlots->mDropAllowed) {
  2745       mSlots->mDropAllowed = false;
  2746       InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
  2748     else
  2749       mSlots->mDropAllowed = false;
  2750     mSlots->mIsDragging = false;
  2751     mSlots->mScrollLines = 0;
  2752     // If a drop is occuring, the exit event will fire just before the drop
  2753     // event, so don't reset mDropRow or mDropOrient as these fields are used
  2754     // by the drop event.
  2755     if (mSlots->mTimer) {
  2756       mSlots->mTimer->Cancel();
  2757       mSlots->mTimer = nullptr;
  2760     if (!mSlots->mArray.IsEmpty()) {
  2761       // Close all spring loaded folders except the drop folder.
  2762       CreateTimer(LookAndFeel::eIntID_TreeCloseDelay,
  2763                   CloseCallback, nsITimer::TYPE_ONE_SHOT,
  2764                   getter_AddRefs(mSlots->mTimer));
  2768   return NS_OK;
  2771 static nsLineStyle
  2772 ConvertBorderStyleToLineStyle(uint8_t aBorderStyle)
  2774   switch (aBorderStyle) {
  2775     case NS_STYLE_BORDER_STYLE_DOTTED:
  2776       return nsLineStyle_kDotted;
  2777     case NS_STYLE_BORDER_STYLE_DASHED:
  2778       return nsLineStyle_kDashed;
  2779     default:
  2780       return nsLineStyle_kSolid;
  2784 static void
  2785 PaintTreeBody(nsIFrame* aFrame, nsRenderingContext* aCtx,
  2786               const nsRect& aDirtyRect, nsPoint aPt)
  2788   static_cast<nsTreeBodyFrame*>(aFrame)->PaintTreeBody(*aCtx, aDirtyRect, aPt);
  2791 // Painting routines
  2792 void
  2793 nsTreeBodyFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  2794                                   const nsRect&           aDirtyRect,
  2795                                   const nsDisplayListSet& aLists)
  2797   // REVIEW: why did we paint if we were collapsed? that makes no sense!
  2798   if (!IsVisibleForPainting(aBuilder))
  2799     return; // We're invisible.  Don't paint.
  2801   // Handles painting our background, border, and outline.
  2802   nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
  2804   // Bail out now if there's no view or we can't run script because the
  2805   // document is a zombie
  2806   if (!mView || !GetContent()->GetCurrentDoc()->GetWindow())
  2807     return;
  2809   aLists.Content()->AppendNewToTop(new (aBuilder)
  2810     nsDisplayGeneric(aBuilder, this, ::PaintTreeBody, "XULTreeBody",
  2811                      nsDisplayItem::TYPE_XUL_TREE_BODY));
  2814 void
  2815 nsTreeBodyFrame::PaintTreeBody(nsRenderingContext& aRenderingContext,
  2816                                const nsRect& aDirtyRect, nsPoint aPt)
  2818   // Update our available height and our page count.
  2819   CalcInnerBox();
  2820   aRenderingContext.PushState();
  2821   aRenderingContext.IntersectClip(mInnerBox + aPt);
  2822   int32_t oldPageCount = mPageLength;
  2823   if (!mHasFixedRowCount)
  2824     mPageLength = mInnerBox.height/mRowHeight;
  2826   if (oldPageCount != mPageLength || mHorzWidth != CalcHorzWidth(GetScrollParts())) {
  2827     // Schedule a ResizeReflow that will update our info properly.
  2828     PresContext()->PresShell()->
  2829       FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
  2831 #ifdef DEBUG
  2832   int32_t rowCount = mRowCount;
  2833   mView->GetRowCount(&rowCount);
  2834   NS_WARN_IF_FALSE(mRowCount == rowCount, "row count changed unexpectedly");
  2835 #endif
  2837   // Loop through our columns and paint them (e.g., for sorting).  This is only
  2838   // relevant when painting backgrounds, since columns contain no content.  Content
  2839   // is contained in the rows.
  2840   for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
  2841        currCol = currCol->GetNext()) {
  2842     nsRect colRect;
  2843     nsresult rv = currCol->GetRect(this, mInnerBox.y, mInnerBox.height,
  2844                                    &colRect);
  2845     // Don't paint hidden columns.
  2846     if (NS_FAILED(rv) || colRect.width == 0) continue;
  2848     if (OffsetForHorzScroll(colRect, false)) {
  2849       nsRect dirtyRect;
  2850       colRect += aPt;
  2851       if (dirtyRect.IntersectRect(aDirtyRect, colRect)) {
  2852         PaintColumn(currCol, colRect, PresContext(), aRenderingContext, aDirtyRect);
  2856   // Loop through our on-screen rows.
  2857   for (int32_t i = mTopRowIndex; i < mRowCount && i <= mTopRowIndex+mPageLength; i++) {
  2858     nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(i-mTopRowIndex), mInnerBox.width, mRowHeight);
  2859     nsRect dirtyRect;
  2860     if (dirtyRect.IntersectRect(aDirtyRect, rowRect + aPt) &&
  2861         rowRect.y < (mInnerBox.y+mInnerBox.height)) {
  2862       PaintRow(i, rowRect + aPt, PresContext(), aRenderingContext, aDirtyRect, aPt);
  2866   if (mSlots && mSlots->mDropAllowed && (mSlots->mDropOrient == nsITreeView::DROP_BEFORE ||
  2867       mSlots->mDropOrient == nsITreeView::DROP_AFTER)) {
  2868     nscoord yPos = mInnerBox.y + mRowHeight * (mSlots->mDropRow - mTopRowIndex) - mRowHeight / 2;
  2869     nsRect feedbackRect(mInnerBox.x, yPos, mInnerBox.width, mRowHeight);
  2870     if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
  2871       feedbackRect.y += mRowHeight;
  2873     nsRect dirtyRect;
  2874     feedbackRect += aPt;
  2875     if (dirtyRect.IntersectRect(aDirtyRect, feedbackRect)) {
  2876       PaintDropFeedback(feedbackRect, PresContext(), aRenderingContext, aDirtyRect, aPt);
  2879   aRenderingContext.PopState();
  2884 void
  2885 nsTreeBodyFrame::PaintColumn(nsTreeColumn*        aColumn,
  2886                              const nsRect&        aColumnRect,
  2887                              nsPresContext*      aPresContext,
  2888                              nsRenderingContext& aRenderingContext,
  2889                              const nsRect&        aDirtyRect)
  2891   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  2893   // Now obtain the properties for our cell.
  2894   PrefillPropertyArray(-1, aColumn);
  2895   nsAutoString properties;
  2896   mView->GetColumnProperties(aColumn, properties);
  2897   nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  2899   // Resolve style for the column.  It contains all the info we need to lay ourselves
  2900   // out and to paint.
  2901   nsStyleContext* colContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecolumn);
  2903   // Obtain the margins for the cell and then deflate our rect by that 
  2904   // amount.  The cell is assumed to be contained within the deflated rect.
  2905   nsRect colRect(aColumnRect);
  2906   nsMargin colMargin;
  2907   colContext->StyleMargin()->GetMargin(colMargin);
  2908   colRect.Deflate(colMargin);
  2910   PaintBackgroundLayer(colContext, aPresContext, aRenderingContext, colRect, aDirtyRect);
  2913 void
  2914 nsTreeBodyFrame::PaintRow(int32_t              aRowIndex,
  2915                           const nsRect&        aRowRect,
  2916                           nsPresContext*       aPresContext,
  2917                           nsRenderingContext& aRenderingContext,
  2918                           const nsRect&        aDirtyRect,
  2919                           nsPoint              aPt)
  2921   // We have been given a rect for our row.  We treat this row like a full-blown
  2922   // frame, meaning that it can have borders, margins, padding, and a background.
  2924   // Without a view, we have no data. Check for this up front.
  2925   if (!mView)
  2926     return;
  2928   nsresult rv;
  2930   // Now obtain the properties for our row.
  2931   // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused
  2932   PrefillPropertyArray(aRowIndex, nullptr);
  2934   nsAutoString properties;
  2935   mView->GetRowProperties(aRowIndex, properties);
  2936   nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  2938   // Resolve style for the row.  It contains all the info we need to lay ourselves
  2939   // out and to paint.
  2940   nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
  2942   // Obtain the margins for the row and then deflate our rect by that 
  2943   // amount.  The row is assumed to be contained within the deflated rect.
  2944   nsRect rowRect(aRowRect);
  2945   nsMargin rowMargin;
  2946   rowContext->StyleMargin()->GetMargin(rowMargin);
  2947   rowRect.Deflate(rowMargin);
  2949   // Paint our borders and background for our row rect.
  2950   // If a -moz-appearance is provided, use theme drawing only if the current row
  2951   // is not selected (since we draw the selection as part of drawing the background).
  2952   bool useTheme = false;
  2953   nsITheme *theme = nullptr;
  2954   const nsStyleDisplay* displayData = rowContext->StyleDisplay();
  2955   if (displayData->mAppearance) {
  2956     theme = aPresContext->GetTheme();
  2957     if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance))
  2958       useTheme = true;
  2960   bool isSelected = false;
  2961   nsCOMPtr<nsITreeSelection> selection;
  2962   mView->GetSelection(getter_AddRefs(selection));
  2963   if (selection) 
  2964     selection->IsSelected(aRowIndex, &isSelected);
  2965   if (useTheme && !isSelected) {
  2966     nsRect dirty;
  2967     dirty.IntersectRect(rowRect, aDirtyRect);
  2968     theme->DrawWidgetBackground(&aRenderingContext, this, 
  2969                                 displayData->mAppearance, rowRect, dirty);
  2970   } else {
  2971     PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, rowRect, aDirtyRect);
  2974   // Adjust the rect for its border and padding.
  2975   nsRect originalRowRect = rowRect;
  2976   AdjustForBorderPadding(rowContext, rowRect);
  2978   bool isSeparator = false;
  2979   mView->IsSeparator(aRowIndex, &isSeparator);
  2980   if (isSeparator) {
  2981     // The row is a separator.
  2983     nscoord primaryX = rowRect.x;
  2984     nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
  2985     if (primaryCol) {
  2986       // Paint the primary cell.
  2987       nsRect cellRect;
  2988       rv = primaryCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
  2989       if (NS_FAILED(rv)) {
  2990         NS_NOTREACHED("primary column is invalid");
  2991         return;
  2994       if (OffsetForHorzScroll(cellRect, false)) {
  2995         cellRect.x += aPt.x;
  2996         nsRect dirtyRect;
  2997         nsRect checkRect(cellRect.x, originalRowRect.y,
  2998                          cellRect.width, originalRowRect.height);
  2999         if (dirtyRect.IntersectRect(aDirtyRect, checkRect))
  3000           PaintCell(aRowIndex, primaryCol, cellRect, aPresContext,
  3001                     aRenderingContext, aDirtyRect, primaryX, aPt);
  3004       // Paint the left side of the separator.
  3005       nscoord currX;
  3006       nsTreeColumn* previousCol = primaryCol->GetPrevious();
  3007       if (previousCol) {
  3008         nsRect prevColRect;
  3009         rv = previousCol->GetRect(this, 0, 0, &prevColRect);
  3010         if (NS_SUCCEEDED(rv)) {
  3011           currX = (prevColRect.x - mHorzPosition) + prevColRect.width + aPt.x;
  3012         } else {
  3013           NS_NOTREACHED("The column before the primary column is invalid");
  3014           currX = rowRect.x;
  3016       } else {
  3017         currX = rowRect.x;
  3020       int32_t level;
  3021       mView->GetLevel(aRowIndex, &level);
  3022       if (level == 0)
  3023         currX += mIndentation;
  3025       if (currX > rowRect.x) {
  3026         nsRect separatorRect(rowRect);
  3027         separatorRect.width -= rowRect.x + rowRect.width - currX;
  3028         PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
  3032     // Paint the right side (whole) separator.
  3033     nsRect separatorRect(rowRect);
  3034     if (primaryX > rowRect.x) {
  3035       separatorRect.width -= primaryX - rowRect.x;
  3036       separatorRect.x += primaryX - rowRect.x;
  3038     PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
  3040   else {
  3041     // Now loop over our cells. Only paint a cell if it intersects with our dirty rect.
  3042     for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
  3043          currCol = currCol->GetNext()) {
  3044       nsRect cellRect;
  3045       rv = currCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
  3046       // Don't paint cells in hidden columns.
  3047       if (NS_FAILED(rv) || cellRect.width == 0)
  3048         continue;
  3050       if (OffsetForHorzScroll(cellRect, false)) {
  3051         cellRect.x += aPt.x;
  3053         // for primary columns, use the row's vertical size so that the
  3054         // lines get drawn properly
  3055         nsRect checkRect = cellRect;
  3056         if (currCol->IsPrimary())
  3057           checkRect = nsRect(cellRect.x, originalRowRect.y,
  3058                              cellRect.width, originalRowRect.height);
  3060         nsRect dirtyRect;
  3061         nscoord dummy;
  3062         if (dirtyRect.IntersectRect(aDirtyRect, checkRect))
  3063           PaintCell(aRowIndex, currCol, cellRect, aPresContext,
  3064                     aRenderingContext, aDirtyRect, dummy, aPt);
  3070 void
  3071 nsTreeBodyFrame::PaintSeparator(int32_t              aRowIndex,
  3072                                 const nsRect&        aSeparatorRect,
  3073                                 nsPresContext*      aPresContext,
  3074                                 nsRenderingContext& aRenderingContext,
  3075                                 const nsRect&        aDirtyRect)
  3077   // Resolve style for the separator.
  3078   nsStyleContext* separatorContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeseparator);
  3079   bool useTheme = false;
  3080   nsITheme *theme = nullptr;
  3081   const nsStyleDisplay* displayData = separatorContext->StyleDisplay();
  3082   if ( displayData->mAppearance ) {
  3083     theme = aPresContext->GetTheme();
  3084     if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance))
  3085       useTheme = true;
  3088   // use -moz-appearance if provided.
  3089   if (useTheme) {
  3090     nsRect dirty;
  3091     dirty.IntersectRect(aSeparatorRect, aDirtyRect);
  3092     theme->DrawWidgetBackground(&aRenderingContext, this,
  3093                                 displayData->mAppearance, aSeparatorRect, dirty); 
  3095   else {
  3096     const nsStylePosition* stylePosition = separatorContext->StylePosition();
  3098     // Obtain the height for the separator or use the default value.
  3099     nscoord height;
  3100     if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
  3101       height = stylePosition->mHeight.GetCoordValue();
  3102     else {
  3103       // Use default height 2px.
  3104       height = nsPresContext::CSSPixelsToAppUnits(2);
  3107     // Obtain the margins for the separator and then deflate our rect by that 
  3108     // amount. The separator is assumed to be contained within the deflated rect.
  3109     nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width, height);
  3110     nsMargin separatorMargin;
  3111     separatorContext->StyleMargin()->GetMargin(separatorMargin);
  3112     separatorRect.Deflate(separatorMargin);
  3114     // Center the separator.
  3115     separatorRect.y += (aSeparatorRect.height - height) / 2;
  3117     PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext, separatorRect, aDirtyRect);
  3121 void
  3122 nsTreeBodyFrame::PaintCell(int32_t              aRowIndex,
  3123                            nsTreeColumn*        aColumn,
  3124                            const nsRect&        aCellRect,
  3125                            nsPresContext*       aPresContext,
  3126                            nsRenderingContext& aRenderingContext,
  3127                            const nsRect&        aDirtyRect,
  3128                            nscoord&             aCurrX,
  3129                            nsPoint              aPt)
  3131   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  3133   // Now obtain the properties for our cell.
  3134   // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID.
  3135   PrefillPropertyArray(aRowIndex, aColumn);
  3136   nsAutoString properties;
  3137   mView->GetCellProperties(aRowIndex, aColumn, properties);
  3138   nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  3140   // Resolve style for the cell.  It contains all the info we need to lay ourselves
  3141   // out and to paint.
  3142   nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  3144   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  3146   // Obtain the margins for the cell and then deflate our rect by that 
  3147   // amount.  The cell is assumed to be contained within the deflated rect.
  3148   nsRect cellRect(aCellRect);
  3149   nsMargin cellMargin;
  3150   cellContext->StyleMargin()->GetMargin(cellMargin);
  3151   cellRect.Deflate(cellMargin);
  3153   // Paint our borders and background for our row rect.
  3154   PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect, aDirtyRect);
  3156   // Adjust the rect for its border and padding.
  3157   AdjustForBorderPadding(cellContext, cellRect);
  3159   nscoord currX = cellRect.x;
  3160   nscoord remainingWidth = cellRect.width;
  3162   // Now we paint the contents of the cells.
  3163   // Directionality of the tree determines the order in which we paint.  
  3164   // NS_STYLE_DIRECTION_LTR means paint from left to right.
  3165   // NS_STYLE_DIRECTION_RTL means paint from right to left.
  3167   if (aColumn->IsPrimary()) {
  3168     // If we're the primary column, we need to indent and paint the twisty and any connecting lines
  3169     // between siblings.
  3171     int32_t level;
  3172     mView->GetLevel(aRowIndex, &level);
  3174     if (!isRTL)
  3175       currX += mIndentation * level;
  3176     remainingWidth -= mIndentation * level;
  3178     // Resolve the style to use for the connecting lines.
  3179     nsStyleContext* lineContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeline);
  3181     if (mIndentation && level &&
  3182         lineContext->StyleVisibility()->IsVisibleOrCollapsed()) {
  3183       // Paint the thread lines.
  3185       // Get the size of the twisty. We don't want to paint the twisty
  3186       // before painting of connecting lines since it would paint lines over
  3187       // the twisty. But we need to leave a place for it.
  3188       nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  3190       nsRect imageSize;
  3191       nsRect twistyRect(aCellRect);
  3192       GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext,
  3193                     aRenderingContext, twistyContext);
  3195       nsMargin twistyMargin;
  3196       twistyContext->StyleMargin()->GetMargin(twistyMargin);
  3197       twistyRect.Inflate(twistyMargin);
  3199       aRenderingContext.PushState();
  3201       const nsStyleBorder* borderStyle = lineContext->StyleBorder();
  3202       nscolor color;
  3203       bool foreground;
  3204       borderStyle->GetBorderColor(NS_SIDE_LEFT, color, foreground);
  3205       if (foreground) {
  3206         // GetBorderColor didn't touch color, thus grab it from the treeline context
  3207         color = lineContext->StyleColor()->mColor;
  3209       aRenderingContext.SetColor(color);
  3210       uint8_t style;
  3211       style = borderStyle->GetBorderStyle(NS_SIDE_LEFT);
  3212       aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style));
  3214       nscoord srcX = currX + twistyRect.width - mIndentation / 2;
  3215       nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y;
  3217       // Don't paint off our cell.
  3218       if (srcX <= cellRect.x + cellRect.width) {
  3219         nscoord destX = currX + twistyRect.width;
  3220         if (destX > cellRect.x + cellRect.width)
  3221           destX = cellRect.x + cellRect.width;
  3222         if (isRTL) {
  3223           srcX = currX + remainingWidth - (srcX - cellRect.x);
  3224           destX = currX + remainingWidth - (destX - cellRect.x);
  3226         aRenderingContext.DrawLine(srcX, lineY + mRowHeight / 2, destX, lineY + mRowHeight / 2);
  3229       int32_t currentParent = aRowIndex;
  3230       for (int32_t i = level; i > 0; i--) {
  3231         if (srcX <= cellRect.x + cellRect.width) {
  3232           // Paint full vertical line only if we have next sibling.
  3233           bool hasNextSibling;
  3234           mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling);
  3235           if (hasNextSibling)
  3236             aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight);
  3237           else if (i == level)
  3238             aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight / 2);
  3241         int32_t parent;
  3242         if (NS_FAILED(mView->GetParentIndex(currentParent, &parent)) || parent < 0)
  3243           break;
  3244         currentParent = parent;
  3245         srcX -= mIndentation;
  3248       aRenderingContext.PopState();
  3251     // Always leave space for the twisty.
  3252     nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
  3253     PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, aRenderingContext, aDirtyRect,
  3254                 remainingWidth, currX);
  3257   // Now paint the icon for our cell.
  3258   nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
  3259   nsRect dirtyRect;
  3260   if (dirtyRect.IntersectRect(aDirtyRect, iconRect))
  3261     PaintImage(aRowIndex, aColumn, iconRect, aPresContext, aRenderingContext, aDirtyRect,
  3262                remainingWidth, currX);
  3264   // Now paint our element, but only if we aren't a cycler column.
  3265   // XXX until we have the ability to load images, allow the view to 
  3266   // insert text into cycler columns...
  3267   if (!aColumn->IsCycler()) {
  3268     nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height);
  3269     nsRect dirtyRect;
  3270     if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) {
  3271       switch (aColumn->GetType()) {
  3272         case nsITreeColumn::TYPE_TEXT:
  3273           PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
  3274           break;
  3275         case nsITreeColumn::TYPE_CHECKBOX:
  3276           PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
  3277           break;
  3278         case nsITreeColumn::TYPE_PROGRESSMETER:
  3279           int32_t state;
  3280           mView->GetProgressMode(aRowIndex, aColumn, &state);
  3281           switch (state) {
  3282             case nsITreeView::PROGRESS_NORMAL:
  3283             case nsITreeView::PROGRESS_UNDETERMINED:
  3284               PaintProgressMeter(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
  3285               break;
  3286             case nsITreeView::PROGRESS_NONE:
  3287             default:
  3288               PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
  3289               break;
  3291           break;
  3296   aCurrX = currX;
  3299 void
  3300 nsTreeBodyFrame::PaintTwisty(int32_t              aRowIndex,
  3301                              nsTreeColumn*        aColumn,
  3302                              const nsRect&        aTwistyRect,
  3303                              nsPresContext*      aPresContext,
  3304                              nsRenderingContext& aRenderingContext,
  3305                              const nsRect&        aDirtyRect,
  3306                              nscoord&             aRemainingWidth,
  3307                              nscoord&             aCurrX)
  3309   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  3311   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  3312   nscoord rightEdge = aCurrX + aRemainingWidth;
  3313   // Paint the twisty, but only if we are a non-empty container.
  3314   bool shouldPaint = false;
  3315   bool isContainer = false;
  3316   mView->IsContainer(aRowIndex, &isContainer);
  3317   if (isContainer) {
  3318     bool isContainerEmpty = false;
  3319     mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
  3320     if (!isContainerEmpty)
  3321       shouldPaint = true;
  3324   // Resolve style for the twisty.
  3325   nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  3327   // Obtain the margins for the twisty and then deflate our rect by that 
  3328   // amount.  The twisty is assumed to be contained within the deflated rect.
  3329   nsRect twistyRect(aTwistyRect);
  3330   nsMargin twistyMargin;
  3331   twistyContext->StyleMargin()->GetMargin(twistyMargin);
  3332   twistyRect.Deflate(twistyMargin);
  3334   nsRect imageSize;
  3335   nsITheme* theme = GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect,
  3336                                   aPresContext, aRenderingContext, twistyContext);
  3338   // Subtract out the remaining width.  This is done even when we don't actually paint a twisty in 
  3339   // this cell, so that cells in different rows still line up.
  3340   nsRect copyRect(twistyRect);
  3341   copyRect.Inflate(twistyMargin);
  3342   aRemainingWidth -= copyRect.width;
  3343   if (!isRTL)
  3344     aCurrX += copyRect.width;
  3346   if (shouldPaint) {
  3347     // Paint our borders and background for our image rect.
  3348     PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext, twistyRect, aDirtyRect);
  3350     if (theme) {
  3351       if (isRTL)
  3352         twistyRect.x = rightEdge - twistyRect.width;
  3353       // yeah, I know it says we're drawing a background, but a twisty is really a fg
  3354       // object since it doesn't have anything that gecko would want to draw over it. Besides,
  3355       // we have to prevent imagelib from drawing it.
  3356       nsRect dirty;
  3357       dirty.IntersectRect(twistyRect, aDirtyRect);
  3358       theme->DrawWidgetBackground(&aRenderingContext, this, 
  3359                                   twistyContext->StyleDisplay()->mAppearance, twistyRect, dirty);
  3361     else {
  3362       // Time to paint the twisty.
  3363       // Adjust the rect for its border and padding.
  3364       nsMargin bp(0,0,0,0);
  3365       GetBorderPadding(twistyContext, bp);
  3366       twistyRect.Deflate(bp);
  3367       if (isRTL)
  3368         twistyRect.x = rightEdge - twistyRect.width;
  3369       imageSize.Deflate(bp);
  3371       // Get the image for drawing.
  3372       nsCOMPtr<imgIContainer> image;
  3373       bool useImageRegion = true;
  3374       GetImage(aRowIndex, aColumn, true, twistyContext, useImageRegion, getter_AddRefs(image));
  3375       if (image) {
  3376         nsPoint pt = twistyRect.TopLeft();
  3378         // Center the image. XXX Obey vertical-align style prop?
  3379         if (imageSize.height < twistyRect.height) {
  3380           pt.y += (twistyRect.height - imageSize.height)/2;
  3383         // Paint the image.
  3384         nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
  3385             GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect,
  3386             imgIContainer::FLAG_NONE, &imageSize);
  3392 void
  3393 nsTreeBodyFrame::PaintImage(int32_t              aRowIndex,
  3394                             nsTreeColumn*        aColumn,
  3395                             const nsRect&        aImageRect,
  3396                             nsPresContext*       aPresContext,
  3397                             nsRenderingContext& aRenderingContext,
  3398                             const nsRect&        aDirtyRect,
  3399                             nscoord&             aRemainingWidth,
  3400                             nscoord&             aCurrX)
  3402   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  3404   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  3405   nscoord rightEdge = aCurrX + aRemainingWidth;
  3406   // Resolve style for the image.
  3407   nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  3409   // Obtain opacity value for the image.
  3410   float opacity = imageContext->StyleDisplay()->mOpacity;
  3412   // Obtain the margins for the image and then deflate our rect by that
  3413   // amount.  The image is assumed to be contained within the deflated rect.
  3414   nsRect imageRect(aImageRect);
  3415   nsMargin imageMargin;
  3416   imageContext->StyleMargin()->GetMargin(imageMargin);
  3417   imageRect.Deflate(imageMargin);
  3419   // Get the image.
  3420   bool useImageRegion = true;
  3421   nsCOMPtr<imgIContainer> image;
  3422   GetImage(aRowIndex, aColumn, false, imageContext, useImageRegion, getter_AddRefs(image));
  3424   // Get the image destination size.
  3425   nsSize imageDestSize = GetImageDestSize(imageContext, useImageRegion, image);
  3426   if (!imageDestSize.width || !imageDestSize.height)
  3427     return;
  3429   // Get the borders and padding.
  3430   nsMargin bp(0,0,0,0);
  3431   GetBorderPadding(imageContext, bp);
  3433   // destRect will be passed as the aDestRect argument in the DrawImage method.
  3434   // Start with the imageDestSize width and height.
  3435   nsRect destRect(0, 0, imageDestSize.width, imageDestSize.height);
  3436   // Inflate destRect for borders and padding so that we can compare/adjust
  3437   // with respect to imageRect.
  3438   destRect.Inflate(bp);
  3440   // The destRect width and height have not been adjusted to fit within the
  3441   // cell width and height.
  3442   // We must adjust the width even if image is null, because the width is used
  3443   // to update the aRemainingWidth and aCurrX values.
  3444   // Since the height isn't used unless the image is not null, we will adjust
  3445   // the height inside the if (image) block below.
  3447   if (destRect.width > imageRect.width) {
  3448     // The destRect is too wide to fit within the cell width.
  3449     // Adjust destRect width to fit within the cell width.
  3450     destRect.width = imageRect.width;
  3452   else {
  3453     // The cell is wider than the destRect.
  3454     // In a cycler column, the image is centered horizontally.
  3455     if (!aColumn->IsCycler()) {
  3456       // If this column is not a cycler, we won't center the image horizontally.
  3457       // We adjust the imageRect width so that the image is placed at the start
  3458       // of the cell.
  3459       imageRect.width = destRect.width;
  3463   if (image) {
  3464     if (isRTL)
  3465       imageRect.x = rightEdge - imageRect.width;
  3466     // Paint our borders and background for our image rect
  3467     PaintBackgroundLayer(imageContext, aPresContext, aRenderingContext, imageRect, aDirtyRect);
  3469     // The destRect x and y have not been set yet. Let's do that now.
  3470     // Initially, we use the imageRect x and y.
  3471     destRect.x = imageRect.x;
  3472     destRect.y = imageRect.y;
  3474     if (destRect.width < imageRect.width) {
  3475       // The destRect width is smaller than the cell width.
  3476       // Center the image horizontally in the cell.
  3477       // Adjust the destRect x accordingly.
  3478       destRect.x += (imageRect.width - destRect.width)/2;
  3481     // Now it's time to adjust the destRect height to fit within the cell height.
  3482     if (destRect.height > imageRect.height) {
  3483       // The destRect height is larger than the cell height.
  3484       // Adjust destRect height to fit within the cell height.
  3485       destRect.height = imageRect.height;
  3487     else if (destRect.height < imageRect.height) {
  3488       // The destRect height is smaller than the cell height.
  3489       // Center the image vertically in the cell.
  3490       // Adjust the destRect y accordingly.
  3491       destRect.y += (imageRect.height - destRect.height)/2;
  3494     // It's almost time to paint the image.
  3495     // Deflate destRect for the border and padding.
  3496     destRect.Deflate(bp);
  3498     // Get the image source rectangle - the rectangle containing the part of
  3499     // the image that we are going to display.
  3500     // sourceRect will be passed as the aSrcRect argument in the DrawImage method.
  3501     nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image);
  3503     // Let's say that the image is 100 pixels tall and
  3504     // that the CSS has specified that the destination height should be 50
  3505     // pixels tall. Let's say that the cell height is only 20 pixels. So, in
  3506     // those 20 visible pixels, we want to see the top 20/50ths of the image.
  3507     // So, the sourceRect.height should be 100 * 20 / 50, which is 40 pixels.
  3508     // Essentially, we are scaling the image as dictated by the CSS destination
  3509     // height and width, and we are then clipping the scaled image by the cell
  3510     // width and height.
  3511     nsIntSize rawImageSize;
  3512     image->GetWidth(&rawImageSize.width);
  3513     image->GetHeight(&rawImageSize.height);
  3514     nsRect wholeImageDest =
  3515       nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect,
  3516           nsRect(destRect.TopLeft(), imageDestSize));
  3518     gfxContext* ctx = aRenderingContext.ThebesContext();
  3519     if (opacity != 1.0f) {
  3520       ctx->PushGroup(gfxContentType::COLOR_ALPHA);
  3523     nsLayoutUtils::DrawImage(&aRenderingContext, image,
  3524         nsLayoutUtils::GetGraphicsFilterForFrame(this),
  3525         wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect,
  3526         imgIContainer::FLAG_NONE);
  3528     if (opacity != 1.0f) {
  3529       ctx->PopGroupToSource();
  3530       ctx->Paint(opacity);
  3534   // Update the aRemainingWidth and aCurrX values.
  3535   imageRect.Inflate(imageMargin);
  3536   aRemainingWidth -= imageRect.width;
  3537   if (!isRTL)
  3538     aCurrX += imageRect.width;
  3541 void
  3542 nsTreeBodyFrame::PaintText(int32_t              aRowIndex,
  3543                            nsTreeColumn*        aColumn,
  3544                            const nsRect&        aTextRect,
  3545                            nsPresContext*      aPresContext,
  3546                            nsRenderingContext& aRenderingContext,
  3547                            const nsRect&        aDirtyRect,
  3548                            nscoord&             aCurrX)
  3550   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  3552   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  3554   // Now obtain the text for our cell.
  3555   nsAutoString text;
  3556   mView->GetCellText(aRowIndex, aColumn, text);
  3557   // We're going to paint this text so we need to ensure bidi is enabled if
  3558   // necessary
  3559   CheckTextForBidi(text);
  3561   if (text.Length() == 0)
  3562     return; // Don't paint an empty string. XXX What about background/borders? Still paint?
  3564   // Resolve style for the text.  It contains all the info we need to lay ourselves
  3565   // out and to paint.
  3566   nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  3568   // Obtain opacity value for the image.
  3569   float opacity = textContext->StyleDisplay()->mOpacity;
  3571   // Obtain the margins for the text and then deflate our rect by that 
  3572   // amount.  The text is assumed to be contained within the deflated rect.
  3573   nsRect textRect(aTextRect);
  3574   nsMargin textMargin;
  3575   textContext->StyleMargin()->GetMargin(textMargin);
  3576   textRect.Deflate(textMargin);
  3578   // Adjust the rect for its border and padding.
  3579   nsMargin bp(0,0,0,0);
  3580   GetBorderPadding(textContext, bp);
  3581   textRect.Deflate(bp);
  3583   // Compute our text size.
  3584   nsRefPtr<nsFontMetrics> fontMet;
  3585   nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  3586                                                getter_AddRefs(fontMet));
  3588   nscoord height = fontMet->MaxHeight();
  3589   nscoord baseline = fontMet->MaxAscent();
  3591   // Center the text. XXX Obey vertical-align style prop?
  3592   if (height < textRect.height) {
  3593     textRect.y += (textRect.height - height)/2;
  3594     textRect.height = height;
  3597   // Set our font.
  3598   aRenderingContext.SetFont(fontMet);
  3600   AdjustForCellText(text, aRowIndex, aColumn, aRenderingContext, textRect);
  3601   textRect.Inflate(bp);
  3603   // Subtract out the remaining width.
  3604   if (!isRTL)
  3605     aCurrX += textRect.width + textMargin.LeftRight();
  3607   PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, textRect, aDirtyRect);
  3609   // Time to paint our text.
  3610   textRect.Deflate(bp);
  3612   // Set our color.
  3613   aRenderingContext.SetColor(textContext->StyleColor()->mColor);
  3615   // Draw decorations.
  3616   uint8_t decorations = textContext->StyleTextReset()->mTextDecorationLine;
  3618   nscoord offset;
  3619   nscoord size;
  3620   if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
  3621     fontMet->GetUnderline(offset, size);
  3622     if (decorations & NS_FONT_DECORATION_OVERLINE)
  3623       aRenderingContext.FillRect(textRect.x, textRect.y, textRect.width, size);
  3624     if (decorations & NS_FONT_DECORATION_UNDERLINE)
  3625       aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
  3627   if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
  3628     fontMet->GetStrikeout(offset, size);
  3629     aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
  3631   nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  3633   gfxContext* ctx = aRenderingContext.ThebesContext();
  3634   if (opacity != 1.0f) {
  3635     ctx->PushGroup(gfxContentType::COLOR_ALPHA);
  3638   nsLayoutUtils::DrawString(this, &aRenderingContext, text.get(), text.Length(),
  3639                             textRect.TopLeft() + nsPoint(0, baseline), cellContext);
  3641   if (opacity != 1.0f) {
  3642     ctx->PopGroupToSource();
  3643     ctx->Paint(opacity);
  3648 void
  3649 nsTreeBodyFrame::PaintCheckbox(int32_t              aRowIndex,
  3650                                nsTreeColumn*        aColumn,
  3651                                const nsRect&        aCheckboxRect,
  3652                                nsPresContext*      aPresContext,
  3653                                nsRenderingContext& aRenderingContext,
  3654                                const nsRect&        aDirtyRect)
  3656   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  3658   // Resolve style for the checkbox.
  3659   nsStyleContext* checkboxContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecheckbox);
  3661   nscoord rightEdge = aCheckboxRect.XMost();
  3663   // Obtain the margins for the checkbox and then deflate our rect by that 
  3664   // amount.  The checkbox is assumed to be contained within the deflated rect.
  3665   nsRect checkboxRect(aCheckboxRect);
  3666   nsMargin checkboxMargin;
  3667   checkboxContext->StyleMargin()->GetMargin(checkboxMargin);
  3668   checkboxRect.Deflate(checkboxMargin);
  3670   nsRect imageSize = GetImageSize(aRowIndex, aColumn, true, checkboxContext);
  3672   if (imageSize.height > checkboxRect.height)
  3673     imageSize.height = checkboxRect.height;
  3674   if (imageSize.width > checkboxRect.width)
  3675     imageSize.width = checkboxRect.width;
  3677   if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
  3678     checkboxRect.x = rightEdge - checkboxRect.width;
  3680   // Paint our borders and background for our image rect.
  3681   PaintBackgroundLayer(checkboxContext, aPresContext, aRenderingContext, checkboxRect, aDirtyRect);
  3683   // Time to paint the checkbox.
  3684   // Adjust the rect for its border and padding.
  3685   nsMargin bp(0,0,0,0);
  3686   GetBorderPadding(checkboxContext, bp);
  3687   checkboxRect.Deflate(bp);
  3689   // Get the image for drawing.
  3690   nsCOMPtr<imgIContainer> image;
  3691   bool useImageRegion = true;
  3692   GetImage(aRowIndex, aColumn, true, checkboxContext, useImageRegion, getter_AddRefs(image));
  3693   if (image) {
  3694     nsPoint pt = checkboxRect.TopLeft();
  3696     if (imageSize.height < checkboxRect.height) {
  3697       pt.y += (checkboxRect.height - imageSize.height)/2;
  3700     if (imageSize.width < checkboxRect.width) {
  3701       pt.x += (checkboxRect.width - imageSize.width)/2;
  3704     // Paint the image.
  3705     nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
  3706         GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect,
  3707         imgIContainer::FLAG_NONE, &imageSize);
  3711 void
  3712 nsTreeBodyFrame::PaintProgressMeter(int32_t              aRowIndex,
  3713                                     nsTreeColumn*        aColumn,
  3714                                     const nsRect&        aProgressMeterRect,
  3715                                     nsPresContext*      aPresContext,
  3716                                     nsRenderingContext& aRenderingContext,
  3717                                     const nsRect&        aDirtyRect)
  3719   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  3721   // Resolve style for the progress meter.  It contains all the info we need
  3722   // to lay ourselves out and to paint.
  3723   nsStyleContext* meterContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeprogressmeter);
  3725   // Obtain the margins for the progress meter and then deflate our rect by that 
  3726   // amount. The progress meter is assumed to be contained within the deflated
  3727   // rect.
  3728   nsRect meterRect(aProgressMeterRect);
  3729   nsMargin meterMargin;
  3730   meterContext->StyleMargin()->GetMargin(meterMargin);
  3731   meterRect.Deflate(meterMargin);
  3733   // Paint our borders and background for our progress meter rect.
  3734   PaintBackgroundLayer(meterContext, aPresContext, aRenderingContext, meterRect, aDirtyRect);
  3736   // Time to paint our progress. 
  3737   int32_t state;
  3738   mView->GetProgressMode(aRowIndex, aColumn, &state);
  3739   if (state == nsITreeView::PROGRESS_NORMAL) {
  3740     // Adjust the rect for its border and padding.
  3741     AdjustForBorderPadding(meterContext, meterRect);
  3743     // Set our color.
  3744     aRenderingContext.SetColor(meterContext->StyleColor()->mColor);
  3746     // Now obtain the value for our cell.
  3747     nsAutoString value;
  3748     mView->GetCellValue(aRowIndex, aColumn, value);
  3750     nsresult rv;
  3751     int32_t intValue = value.ToInteger(&rv);
  3752     if (intValue < 0)
  3753       intValue = 0;
  3754     else if (intValue > 100)
  3755       intValue = 100;
  3757     nscoord meterWidth = NSToCoordRound((float)intValue / 100 * meterRect.width);
  3758     if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
  3759       meterRect.x += meterRect.width - meterWidth; // right align
  3760     meterRect.width = meterWidth;
  3761     bool useImageRegion = true;
  3762     nsCOMPtr<imgIContainer> image;
  3763     GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image));
  3764     if (image) {
  3765       int32_t width, height;
  3766       image->GetWidth(&width);
  3767       image->GetHeight(&height);
  3768       nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(),
  3769                   height*nsDeviceContext::AppUnitsPerCSSPixel());
  3770       nsLayoutUtils::DrawImage(&aRenderingContext, image,
  3771           nsLayoutUtils::GetGraphicsFilterForFrame(this),
  3772           nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
  3773           aDirtyRect, imgIContainer::FLAG_NONE);
  3774     } else {
  3775       aRenderingContext.FillRect(meterRect);
  3778   else if (state == nsITreeView::PROGRESS_UNDETERMINED) {
  3779     // Adjust the rect for its border and padding.
  3780     AdjustForBorderPadding(meterContext, meterRect);
  3782     bool useImageRegion = true;
  3783     nsCOMPtr<imgIContainer> image;
  3784     GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image));
  3785     if (image) {
  3786       int32_t width, height;
  3787       image->GetWidth(&width);
  3788       image->GetHeight(&height);
  3789       nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(),
  3790                   height*nsDeviceContext::AppUnitsPerCSSPixel());
  3791       nsLayoutUtils::DrawImage(&aRenderingContext, image,
  3792           nsLayoutUtils::GetGraphicsFilterForFrame(this),
  3793           nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
  3794           aDirtyRect, imgIContainer::FLAG_NONE);
  3800 void
  3801 nsTreeBodyFrame::PaintDropFeedback(const nsRect&        aDropFeedbackRect,
  3802                                    nsPresContext*      aPresContext,
  3803                                    nsRenderingContext& aRenderingContext,
  3804                                    const nsRect&        aDirtyRect,
  3805                                    nsPoint              aPt)
  3807   // Paint the drop feedback in between rows.
  3809   nscoord currX;
  3811   // Adjust for the primary cell.
  3812   nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
  3814   if (primaryCol) {
  3815 #ifdef DEBUG
  3816     nsresult rv =
  3817 #endif
  3818       primaryCol->GetXInTwips(this, &currX);
  3819     NS_ASSERTION(NS_SUCCEEDED(rv), "primary column is invalid?");
  3821     currX += aPt.x - mHorzPosition;
  3822   } else {
  3823     currX = aDropFeedbackRect.x;
  3826   PrefillPropertyArray(mSlots->mDropRow, primaryCol);
  3828   // Resolve the style to use for the drop feedback.
  3829   nsStyleContext* feedbackContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreedropfeedback);
  3831   // Paint only if it is visible.
  3832   if (feedbackContext->StyleVisibility()->IsVisibleOrCollapsed()) {
  3833     int32_t level;
  3834     mView->GetLevel(mSlots->mDropRow, &level);
  3836     // If our previous or next row has greater level use that for 
  3837     // correct visual indentation.
  3838     if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) {
  3839       if (mSlots->mDropRow > 0) {
  3840         int32_t previousLevel;
  3841         mView->GetLevel(mSlots->mDropRow - 1, &previousLevel);
  3842         if (previousLevel > level)
  3843           level = previousLevel;
  3846     else {
  3847       if (mSlots->mDropRow < mRowCount - 1) {
  3848         int32_t nextLevel;
  3849         mView->GetLevel(mSlots->mDropRow + 1, &nextLevel);
  3850         if (nextLevel > level)
  3851           level = nextLevel;
  3855     currX += mIndentation * level;
  3857     if (primaryCol){
  3858       nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  3859       nsRect imageSize;
  3860       nsRect twistyRect;
  3861       GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect, aPresContext,
  3862                     aRenderingContext, twistyContext);
  3863       nsMargin twistyMargin;
  3864       twistyContext->StyleMargin()->GetMargin(twistyMargin);
  3865       twistyRect.Inflate(twistyMargin);
  3866       currX += twistyRect.width;
  3869     const nsStylePosition* stylePosition = feedbackContext->StylePosition();
  3871     // Obtain the width for the drop feedback or use default value.
  3872     nscoord width;
  3873     if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord)
  3874       width = stylePosition->mWidth.GetCoordValue();
  3875     else {
  3876       // Use default width 50px.
  3877       width = nsPresContext::CSSPixelsToAppUnits(50);
  3880     // Obtain the height for the drop feedback or use default value.
  3881     nscoord height;
  3882     if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
  3883       height = stylePosition->mHeight.GetCoordValue();
  3884     else {
  3885       // Use default height 2px.
  3886       height = nsPresContext::CSSPixelsToAppUnits(2);
  3889     // Obtain the margins for the drop feedback and then deflate our rect
  3890     // by that amount.
  3891     nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height);
  3892     nsMargin margin;
  3893     feedbackContext->StyleMargin()->GetMargin(margin);
  3894     feedbackRect.Deflate(margin);
  3896     feedbackRect.y += (aDropFeedbackRect.height - height) / 2;
  3898     // Finally paint the drop feedback.
  3899     PaintBackgroundLayer(feedbackContext, aPresContext, aRenderingContext, feedbackRect, aDirtyRect);
  3903 void
  3904 nsTreeBodyFrame::PaintBackgroundLayer(nsStyleContext*      aStyleContext,
  3905                                       nsPresContext*      aPresContext,
  3906                                       nsRenderingContext& aRenderingContext,
  3907                                       const nsRect&        aRect,
  3908                                       const nsRect&        aDirtyRect)
  3910   const nsStyleBorder* myBorder = aStyleContext->StyleBorder();
  3912   nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext,
  3913                                         this, aDirtyRect, aRect,
  3914                                         aStyleContext, *myBorder,
  3915                                         nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES);
  3917   nsCSSRendering::PaintBorderWithStyleBorder(aPresContext, aRenderingContext,
  3918                                              this, aDirtyRect, aRect,
  3919                                              *myBorder, mStyleContext);
  3921   nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
  3922                                aDirtyRect, aRect, aStyleContext);
  3925 // Scrolling
  3926 nsresult
  3927 nsTreeBodyFrame::EnsureRowIsVisible(int32_t aRow)
  3929   ScrollParts parts = GetScrollParts();
  3930   nsresult rv = EnsureRowIsVisibleInternal(parts, aRow);
  3931   NS_ENSURE_SUCCESS(rv, rv);
  3932   UpdateScrollbars(parts);
  3933   return rv;
  3936 nsresult nsTreeBodyFrame::EnsureRowIsVisibleInternal(const ScrollParts& aParts, int32_t aRow)
  3938   if (!mView || !mPageLength)
  3939     return NS_OK;
  3941   if (mTopRowIndex <= aRow && mTopRowIndex+mPageLength > aRow)
  3942     return NS_OK;
  3944   if (aRow < mTopRowIndex)
  3945     ScrollToRowInternal(aParts, aRow);
  3946   else {
  3947     // Bring it just on-screen.
  3948     int32_t distance = aRow - (mTopRowIndex+mPageLength)+1;
  3949     ScrollToRowInternal(aParts, mTopRowIndex+distance);
  3952   return NS_OK;
  3955 nsresult
  3956 nsTreeBodyFrame::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol)
  3958   nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
  3959   if (!col)
  3960     return NS_ERROR_INVALID_ARG;
  3962   ScrollParts parts = GetScrollParts();
  3964   nscoord result = -1;
  3965   nsresult rv;
  3967   nscoord columnPos;
  3968   rv = col->GetXInTwips(this, &columnPos);
  3969   if(NS_FAILED(rv)) return rv;
  3971   nscoord columnWidth;
  3972   rv = col->GetWidthInTwips(this, &columnWidth);
  3973   if(NS_FAILED(rv)) return rv;
  3975   // If the start of the column is before the
  3976   // start of the horizontal view, then scroll
  3977   if (columnPos < mHorzPosition)
  3978     result = columnPos;
  3979   // If the end of the column is past the end of 
  3980   // the horizontal view, then scroll
  3981   else if ((columnPos + columnWidth) > (mHorzPosition + mInnerBox.width))
  3982     result = ((columnPos + columnWidth) - (mHorzPosition + mInnerBox.width)) + mHorzPosition;
  3984   if (result != -1) {
  3985     rv = ScrollHorzInternal(parts, result);
  3986     if(NS_FAILED(rv)) return rv;
  3989   rv = EnsureRowIsVisibleInternal(parts, aRow);
  3990   NS_ENSURE_SUCCESS(rv, rv);
  3991   UpdateScrollbars(parts);
  3992   return rv;
  3995 nsresult
  3996 nsTreeBodyFrame::ScrollToCell(int32_t aRow, nsITreeColumn* aCol)
  3998   ScrollParts parts = GetScrollParts();
  3999   nsresult rv = ScrollToRowInternal(parts, aRow);
  4000   NS_ENSURE_SUCCESS(rv, rv);
  4002   rv = ScrollToColumnInternal(parts, aCol);
  4003   NS_ENSURE_SUCCESS(rv, rv);
  4005   UpdateScrollbars(parts);
  4006   return rv;
  4009 nsresult
  4010 nsTreeBodyFrame::ScrollToColumn(nsITreeColumn* aCol)
  4012   ScrollParts parts = GetScrollParts();
  4013   nsresult rv = ScrollToColumnInternal(parts, aCol);
  4014   NS_ENSURE_SUCCESS(rv, rv);
  4015   UpdateScrollbars(parts);
  4016   return rv;
  4019 nsresult nsTreeBodyFrame::ScrollToColumnInternal(const ScrollParts& aParts,
  4020                                                  nsITreeColumn* aCol)
  4022   nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
  4023   if (!col)
  4024     return NS_ERROR_INVALID_ARG;
  4026   nscoord x;
  4027   nsresult rv = col->GetXInTwips(this, &x);
  4028   if (NS_FAILED(rv))
  4029     return rv;
  4031   return ScrollHorzInternal(aParts, x);
  4034 nsresult
  4035 nsTreeBodyFrame::ScrollToHorizontalPosition(int32_t aHorizontalPosition)
  4037   ScrollParts parts = GetScrollParts();
  4038   int32_t position = nsPresContext::CSSPixelsToAppUnits(aHorizontalPosition);
  4039   nsresult rv = ScrollHorzInternal(parts, position);
  4040   NS_ENSURE_SUCCESS(rv, rv);
  4041   UpdateScrollbars(parts);
  4042   return rv;
  4045 nsresult
  4046 nsTreeBodyFrame::ScrollToRow(int32_t aRow)
  4048   ScrollParts parts = GetScrollParts();
  4049   nsresult rv = ScrollToRowInternal(parts, aRow);
  4050   NS_ENSURE_SUCCESS(rv, rv);
  4051   UpdateScrollbars(parts);
  4052   return rv;
  4055 nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow)
  4057   ScrollInternal(aParts, aRow);
  4059   return NS_OK;
  4062 nsresult
  4063 nsTreeBodyFrame::ScrollByLines(int32_t aNumLines)
  4065   if (!mView)
  4066     return NS_OK;
  4068   int32_t newIndex = mTopRowIndex + aNumLines;
  4069   if (newIndex < 0)
  4070     newIndex = 0;
  4071   else {
  4072     int32_t lastPageTopRow = mRowCount - mPageLength;
  4073     if (newIndex > lastPageTopRow)
  4074       newIndex = lastPageTopRow;
  4076   ScrollToRow(newIndex);
  4078   return NS_OK;
  4081 nsresult
  4082 nsTreeBodyFrame::ScrollByPages(int32_t aNumPages)
  4084   if (!mView)
  4085     return NS_OK;
  4087   int32_t newIndex = mTopRowIndex + aNumPages * mPageLength;
  4088   if (newIndex < 0)
  4089     newIndex = 0;
  4090   else {
  4091     int32_t lastPageTopRow = mRowCount - mPageLength;
  4092     if (newIndex > lastPageTopRow)
  4093       newIndex = lastPageTopRow;
  4095   ScrollToRow(newIndex);
  4097   return NS_OK;
  4100 nsresult
  4101 nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, int32_t aRow)
  4103   if (!mView)
  4104     return NS_OK;
  4106   int32_t delta = aRow - mTopRowIndex;
  4108   if (delta > 0) {
  4109     if (mTopRowIndex == (mRowCount - mPageLength + 1))
  4110       return NS_OK;
  4112   else {
  4113     if (mTopRowIndex == 0)
  4114       return NS_OK;
  4117   mTopRowIndex += delta;
  4119   Invalidate();
  4121   PostScrollEvent();
  4122   return NS_OK;
  4125 nsresult
  4126 nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition)
  4128   if (!mView || !aParts.mColumnsScrollFrame || !aParts.mHScrollbar)
  4129     return NS_OK;
  4131   if (aPosition == mHorzPosition)
  4132     return NS_OK;
  4134   if (aPosition < 0 || aPosition > mHorzWidth)
  4135     return NS_OK;
  4137   nsRect bounds = aParts.mColumnsFrame->GetRect();
  4138   if (aPosition > (mHorzWidth - bounds.width)) 
  4139     aPosition = mHorzWidth - bounds.width;
  4141   mHorzPosition = aPosition;
  4143   Invalidate();
  4145   // Update the column scroll view
  4146   nsWeakFrame weakFrame(this);
  4147   aParts.mColumnsScrollFrame->ScrollTo(nsPoint(mHorzPosition, 0),
  4148                                        nsIScrollableFrame::INSTANT);
  4149   if (!weakFrame.IsAlive()) {
  4150     return NS_ERROR_FAILURE;
  4152   // And fire off an event about it all
  4153   PostScrollEvent();
  4154   return NS_OK;
  4157 NS_IMETHODIMP
  4158 nsTreeBodyFrame::ScrollbarButtonPressed(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t aNewIndex)
  4160   ScrollParts parts = GetScrollParts();
  4162   if (aScrollbar == parts.mVScrollbar) {
  4163     if (aNewIndex > aOldIndex)
  4164       ScrollToRowInternal(parts, mTopRowIndex+1);
  4165     else if (aNewIndex < aOldIndex)
  4166       ScrollToRowInternal(parts, mTopRowIndex-1);
  4167   } else {
  4168     nsresult rv = ScrollHorzInternal(parts, aNewIndex);
  4169     if (NS_FAILED(rv)) return rv;
  4172   UpdateScrollbars(parts);
  4174   return NS_OK;
  4177 NS_IMETHODIMP
  4178 nsTreeBodyFrame::PositionChanged(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t& aNewIndex)
  4180   ScrollParts parts = GetScrollParts();
  4182   if (aOldIndex == aNewIndex)
  4183     return NS_OK;
  4185   // Vertical Scrollbar 
  4186   if (parts.mVScrollbar == aScrollbar) {
  4187     nscoord rh = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
  4189     nscoord newrow = aNewIndex/rh;
  4190     ScrollInternal(parts, newrow);
  4191   // Horizontal Scrollbar
  4192   } else if (parts.mHScrollbar == aScrollbar) {
  4193     nsresult rv = ScrollHorzInternal(parts, aNewIndex);
  4194     if (NS_FAILED(rv)) return rv;
  4197   UpdateScrollbars(parts);
  4198   return NS_OK;
  4201 // The style cache.
  4202 nsStyleContext*
  4203 nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement)
  4205   return mStyleCache.GetStyleContext(this, PresContext(), mContent,
  4206                                      mStyleContext, aPseudoElement,
  4207                                      mScratchArray);
  4210 // Our comparator for resolving our complex pseudos
  4211 bool
  4212 nsTreeBodyFrame::PseudoMatches(nsCSSSelector* aSelector)
  4214   // Iterate the class list.  For each item in the list, see if
  4215   // it is contained in our scratch array.  If we have a miss, then
  4216   // we aren't a match.  If all items in the class list are
  4217   // present in the scratch array, then we have a match.
  4218   nsAtomList* curr = aSelector->mClassList;
  4219   while (curr) {
  4220     if (!mScratchArray.Contains(curr->mAtom))
  4221       return false;
  4222     curr = curr->mNext;
  4224   return true;
  4227 nsIContent*
  4228 nsTreeBodyFrame::GetBaseElement()
  4230   nsIFrame* parent = GetParent();
  4231   while (parent) {
  4232     nsIContent* content = parent->GetContent();
  4233     if (content) {
  4234       nsINodeInfo* ni = content->NodeInfo();
  4236       if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL) ||
  4237           (ni->Equals(nsGkAtoms::select) &&
  4238            content->IsHTML()))
  4239         return content;
  4242     parent = parent->GetParent();
  4245   return nullptr;
  4248 nsresult
  4249 nsTreeBodyFrame::ClearStyleAndImageCaches()
  4251   mStyleCache.Clear();
  4252   mImageCache.EnumerateRead(CancelImageRequest, this);
  4253   mImageCache.Clear();
  4254   return NS_OK;
  4257 /* virtual */ void
  4258 nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
  4260   nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
  4262   // Clear the style cache; the pointers are no longer even valid
  4263   mStyleCache.Clear();
  4264   // XXX The following is hacky, but it's not incorrect,
  4265   // and appears to fix a few bugs with style changes, like text zoom and
  4266   // dpi changes
  4267   mIndentation = GetIndentation();
  4268   mRowHeight = GetRowHeight();
  4269   mStringWidth = -1;
  4272 bool 
  4273 nsTreeBodyFrame::OffsetForHorzScroll(nsRect& rect, bool clip)
  4275   rect.x -= mHorzPosition;
  4277   // Scrolled out before
  4278   if (rect.XMost() <= mInnerBox.x)
  4279     return false;
  4281   // Scrolled out after
  4282   if (rect.x > mInnerBox.XMost())
  4283     return false;
  4285   if (clip) {
  4286     nscoord leftEdge = std::max(rect.x, mInnerBox.x);
  4287     nscoord rightEdge = std::min(rect.XMost(), mInnerBox.XMost());
  4288     rect.x = leftEdge;
  4289     rect.width = rightEdge - leftEdge;
  4291     // Should have returned false above
  4292     NS_ASSERTION(rect.width >= 0, "horz scroll code out of sync");
  4295   return true;
  4298 bool
  4299 nsTreeBodyFrame::CanAutoScroll(int32_t aRowIndex)
  4301   // Check first for partially visible last row.
  4302   if (aRowIndex == mRowCount - 1) {
  4303     nscoord y = mInnerBox.y + (aRowIndex - mTopRowIndex) * mRowHeight;
  4304     if (y < mInnerBox.height && y + mRowHeight > mInnerBox.height)
  4305       return true;
  4308   if (aRowIndex > 0 && aRowIndex < mRowCount - 1)
  4309     return true;
  4311   return false;
  4314 // Given a dom event, figure out which row in the tree the mouse is over,
  4315 // if we should drop before/after/on that row or we should auto-scroll.
  4316 // Doesn't query the content about if the drag is allowable, that's done elsewhere.
  4317 //
  4318 // For containers, we break up the vertical space of the row as follows: if in
  4319 // the topmost 25%, the drop is _before_ the row the mouse is over; if in the
  4320 // last 25%, _after_; in the middle 50%, we consider it a drop _on_ the container.
  4321 //
  4322 // For non-containers, if the mouse is in the top 50% of the row, the drop is
  4323 // _before_ and the bottom 50% _after_
  4324 void 
  4325 nsTreeBodyFrame::ComputeDropPosition(WidgetGUIEvent* aEvent,
  4326                                      int32_t* aRow,
  4327                                      int16_t* aOrient,
  4328                                      int16_t* aScrollLines)
  4330   *aOrient = -1;
  4331   *aScrollLines = 0;
  4333   // Convert the event's point to our coordinates.  We want it in
  4334   // the coordinates of our inner box's coordinates.
  4335   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
  4336   int32_t xTwips = pt.x - mInnerBox.x;
  4337   int32_t yTwips = pt.y - mInnerBox.y;
  4339   *aRow = GetRowAt(xTwips, yTwips);
  4340   if (*aRow >=0) {
  4341     // Compute the top/bottom of the row in question.
  4342     int32_t yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex);
  4344     bool isContainer = false;
  4345     mView->IsContainer (*aRow, &isContainer);
  4346     if (isContainer) {
  4347       // for a container, use a 25%/50%/25% breakdown
  4348       if (yOffset < mRowHeight / 4)
  4349         *aOrient = nsITreeView::DROP_BEFORE;
  4350       else if (yOffset > mRowHeight - (mRowHeight / 4))
  4351         *aOrient = nsITreeView::DROP_AFTER;
  4352       else
  4353         *aOrient = nsITreeView::DROP_ON;
  4355     else {
  4356       // for a non-container use a 50%/50% breakdown
  4357       if (yOffset < mRowHeight / 2)
  4358         *aOrient = nsITreeView::DROP_BEFORE;
  4359       else
  4360         *aOrient = nsITreeView::DROP_AFTER;
  4364   if (CanAutoScroll(*aRow)) {
  4365     // Get the max value from the look and feel service.
  4366     int32_t scrollLinesMax =
  4367       LookAndFeel::GetInt(LookAndFeel::eIntID_TreeScrollLinesMax, 0);
  4368     scrollLinesMax--;
  4369     if (scrollLinesMax < 0)
  4370       scrollLinesMax = 0;
  4372     // Determine if we're w/in a margin of the top/bottom of the tree during a drag.
  4373     // This will ultimately cause us to scroll, but that's done elsewhere.
  4374     nscoord height = (3 * mRowHeight) / 4;
  4375     if (yTwips < height) {
  4376       // scroll up
  4377       *aScrollLines = NSToIntRound(-scrollLinesMax * (1 - (float)yTwips / height) - 1);
  4379     else if (yTwips > mRect.height - height) {
  4380       // scroll down
  4381       *aScrollLines = NSToIntRound(scrollLinesMax * (1 - (float)(mRect.height - yTwips) / height) + 1);
  4384 } // ComputeDropPosition
  4386 void
  4387 nsTreeBodyFrame::OpenCallback(nsITimer *aTimer, void *aClosure)
  4389   nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  4390   if (self) {
  4391     aTimer->Cancel();
  4392     self->mSlots->mTimer = nullptr;
  4394     if (self->mSlots->mDropRow >= 0) {
  4395       self->mSlots->mArray.AppendElement(self->mSlots->mDropRow);
  4396       self->mView->ToggleOpenState(self->mSlots->mDropRow);
  4401 void
  4402 nsTreeBodyFrame::CloseCallback(nsITimer *aTimer, void *aClosure)
  4404   nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  4405   if (self) {
  4406     aTimer->Cancel();
  4407     self->mSlots->mTimer = nullptr;
  4409     for (uint32_t i = self->mSlots->mArray.Length(); i--; ) {
  4410       if (self->mView)
  4411         self->mView->ToggleOpenState(self->mSlots->mArray[i]);
  4413     self->mSlots->mArray.Clear();
  4417 void
  4418 nsTreeBodyFrame::LazyScrollCallback(nsITimer *aTimer, void *aClosure)
  4420   nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  4421   if (self) {
  4422     aTimer->Cancel();
  4423     self->mSlots->mTimer = nullptr;
  4425     if (self->mView) {
  4426       // Set a new timer to scroll the tree repeatedly.
  4427       self->CreateTimer(LookAndFeel::eIntID_TreeScrollDelay,
  4428                         ScrollCallback, nsITimer::TYPE_REPEATING_SLACK,
  4429                         getter_AddRefs(self->mSlots->mTimer));
  4430       self->ScrollByLines(self->mSlots->mScrollLines);
  4431       // ScrollByLines may have deleted |self|.
  4436 void
  4437 nsTreeBodyFrame::ScrollCallback(nsITimer *aTimer, void *aClosure)
  4439   nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  4440   if (self) {
  4441     // Don't scroll if we are already at the top or bottom of the view.
  4442     if (self->mView && self->CanAutoScroll(self->mSlots->mDropRow)) {
  4443       self->ScrollByLines(self->mSlots->mScrollLines);
  4445     else {
  4446       aTimer->Cancel();
  4447       self->mSlots->mTimer = nullptr;
  4452 NS_IMETHODIMP
  4453 nsTreeBodyFrame::ScrollEvent::Run()
  4455   if (mInner) {
  4456     mInner->FireScrollEvent();
  4458   return NS_OK;
  4462 void
  4463 nsTreeBodyFrame::FireScrollEvent()
  4465   mScrollEvent.Forget();
  4466   WidgetGUIEvent event(true, NS_SCROLL_EVENT, nullptr);
  4467   // scroll events fired at elements don't bubble
  4468   event.mFlags.mBubbles = false;
  4469   EventDispatcher::Dispatch(GetContent(), PresContext(), &event);
  4472 void
  4473 nsTreeBodyFrame::PostScrollEvent()
  4475   if (mScrollEvent.IsPending())
  4476     return;
  4478   nsRefPtr<ScrollEvent> ev = new ScrollEvent(this);
  4479   if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
  4480     NS_WARNING("failed to dispatch ScrollEvent");
  4481   } else {
  4482     mScrollEvent = ev;
  4486 void
  4487 nsTreeBodyFrame::ScrollbarActivityStarted() const
  4489   if (mScrollbarActivity) {
  4490     mScrollbarActivity->ActivityStarted();
  4494 void
  4495 nsTreeBodyFrame::ScrollbarActivityStopped() const
  4497   if (mScrollbarActivity) {
  4498     mScrollbarActivity->ActivityStopped();
  4502 void
  4503 nsTreeBodyFrame::DetachImageListeners()
  4505   mCreatedListeners.Clear();
  4508 void
  4509 nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener)
  4511   if (aListener) {
  4512     mCreatedListeners.RemoveEntry(aListener);
  4516 #ifdef ACCESSIBILITY
  4517 void
  4518 nsTreeBodyFrame::FireRowCountChangedEvent(int32_t aIndex, int32_t aCount)
  4520   nsCOMPtr<nsIContent> content(GetBaseElement());
  4521   if (!content)
  4522     return;
  4524   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc());
  4525   if (!domDoc)
  4526     return;
  4528   nsCOMPtr<nsIDOMEvent> event;
  4529   domDoc->CreateEvent(NS_LITERAL_STRING("customevent"),
  4530                       getter_AddRefs(event));
  4532   nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event));
  4533   if (!treeEvent)
  4534     return;
  4536   nsCOMPtr<nsIWritablePropertyBag2> propBag(
  4537     do_CreateInstance("@mozilla.org/hash-property-bag;1"));
  4538   if (!propBag)
  4539     return;
  4541   // Set 'index' data - the row index rows are changed from.
  4542   propBag->SetPropertyAsInt32(NS_LITERAL_STRING("index"), aIndex);
  4544   // Set 'count' data - the number of changed rows.
  4545   propBag->SetPropertyAsInt32(NS_LITERAL_STRING("count"), aCount);
  4547   nsCOMPtr<nsIWritableVariant> detailVariant(
  4548     do_CreateInstance("@mozilla.org/variant;1"));
  4549   if (!detailVariant)
  4550     return;
  4552   detailVariant->SetAsISupports(propBag);
  4553   treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeRowCountChanged"),
  4554                              true, false, detailVariant);
  4556   event->SetTrusted(true);
  4558   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
  4559     new AsyncEventDispatcher(content, event);
  4560   asyncDispatcher->PostDOMEvent();
  4563 void
  4564 nsTreeBodyFrame::FireInvalidateEvent(int32_t aStartRowIdx, int32_t aEndRowIdx,
  4565                                      nsITreeColumn *aStartCol,
  4566                                      nsITreeColumn *aEndCol)
  4568   nsCOMPtr<nsIContent> content(GetBaseElement());
  4569   if (!content)
  4570     return;
  4572   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc());
  4573   if (!domDoc)
  4574     return;
  4576   nsCOMPtr<nsIDOMEvent> event;
  4577   domDoc->CreateEvent(NS_LITERAL_STRING("customevent"),
  4578                       getter_AddRefs(event));
  4580   nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event));
  4581   if (!treeEvent)
  4582     return;
  4584   nsCOMPtr<nsIWritablePropertyBag2> propBag(
  4585     do_CreateInstance("@mozilla.org/hash-property-bag;1"));
  4586   if (!propBag)
  4587     return;
  4589   if (aStartRowIdx != -1 && aEndRowIdx != -1) {
  4590     // Set 'startrow' data - the start index of invalidated rows.
  4591     propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startrow"),
  4592                                 aStartRowIdx);
  4594     // Set 'endrow' data - the end index of invalidated rows.
  4595     propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endrow"),
  4596                                 aEndRowIdx);
  4599   if (aStartCol && aEndCol) {
  4600     // Set 'startcolumn' data - the start index of invalidated rows.
  4601     int32_t startColIdx = 0;
  4602     nsresult rv = aStartCol->GetIndex(&startColIdx);
  4603     if (NS_FAILED(rv))
  4604       return;
  4606     propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"),
  4607                                 startColIdx);
  4609     // Set 'endcolumn' data - the start index of invalidated rows.
  4610     int32_t endColIdx = 0;
  4611     rv = aEndCol->GetIndex(&endColIdx);
  4612     if (NS_FAILED(rv))
  4613       return;
  4615     propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"),
  4616                                 endColIdx);
  4619   nsCOMPtr<nsIWritableVariant> detailVariant(
  4620     do_CreateInstance("@mozilla.org/variant;1"));
  4621   if (!detailVariant)
  4622     return;
  4624   detailVariant->SetAsISupports(propBag);
  4625   treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeInvalidated"),
  4626                              true, false, detailVariant);
  4628   event->SetTrusted(true);
  4630   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
  4631     new AsyncEventDispatcher(content, event);
  4632   asyncDispatcher->PostDOMEvent();
  4634 #endif
  4636 class nsOverflowChecker : public nsRunnable
  4638 public:
  4639   nsOverflowChecker(nsTreeBodyFrame* aFrame) : mFrame(aFrame) {}
  4640   NS_IMETHOD Run() MOZ_OVERRIDE
  4642     if (mFrame.IsAlive()) {
  4643       nsTreeBodyFrame* tree = static_cast<nsTreeBodyFrame*>(mFrame.GetFrame());
  4644       nsTreeBodyFrame::ScrollParts parts = tree->GetScrollParts();
  4645       tree->CheckOverflow(parts);
  4647     return NS_OK;
  4649 private:
  4650   nsWeakFrame mFrame;
  4651 };
  4653 bool
  4654 nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation)
  4656   ScrollParts parts = GetScrollParts();
  4657   nsWeakFrame weakFrame(this);
  4658   nsWeakFrame weakColumnsFrame(parts.mColumnsFrame);
  4659   UpdateScrollbars(parts);
  4660   NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
  4661   if (aNeedsFullInvalidation) {
  4662     Invalidate();
  4664   InvalidateScrollbars(parts, weakColumnsFrame);
  4665   NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
  4667   // Overflow checking dispatches synchronous events, which can cause infinite
  4668   // recursion during reflow. Do the first overflow check synchronously, but
  4669   // force any nested checks to round-trip through the event loop. See bug
  4670   // 905909.
  4671   nsRefPtr<nsOverflowChecker> checker = new nsOverflowChecker(this);
  4672   if (!mCheckingOverflow) {
  4673     nsContentUtils::AddScriptRunner(checker);
  4674   } else {
  4675     NS_DispatchToCurrentThread(checker);
  4677   return weakFrame.IsAlive();
  4680 nsresult
  4681 nsTreeBodyFrame::OnImageIsAnimated(imgIRequest* aRequest)
  4683   nsLayoutUtils::RegisterImageRequest(PresContext(),
  4684                                       aRequest, nullptr);
  4686   return NS_OK;

mercurial