tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsTreeBodyFrame.cpp (138413B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsTreeBodyFrame.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "ScrollbarActivity.h"
     12 #include "SimpleXULLeafFrame.h"
     13 #include "gfxContext.h"
     14 #include "gfxUtils.h"
     15 #include "imgIContainer.h"
     16 #include "imgIRequest.h"
     17 #include "mozilla/AsyncEventDispatcher.h"
     18 #include "mozilla/ComputedStyle.h"
     19 #include "mozilla/ContentEvents.h"
     20 #include "mozilla/DebugOnly.h"
     21 #include "mozilla/EventDispatcher.h"
     22 #include "mozilla/Likely.h"
     23 #include "mozilla/LookAndFeel.h"
     24 #include "mozilla/MathAlgorithms.h"
     25 #include "mozilla/MouseEvents.h"
     26 #include "mozilla/PresShell.h"
     27 #include "mozilla/ScrollContainerFrame.h"
     28 #include "mozilla/Try.h"
     29 #include "mozilla/dom/CustomEvent.h"
     30 #include "mozilla/dom/Document.h"
     31 #include "mozilla/dom/Event.h"
     32 #include "mozilla/dom/NodeInfo.h"
     33 #include "mozilla/dom/ReferrerInfo.h"
     34 #include "mozilla/dom/ScriptSettings.h"
     35 #include "mozilla/dom/ToJSValue.h"
     36 #include "mozilla/dom/TreeColumnBinding.h"
     37 #include "mozilla/gfx/2D.h"
     38 #include "mozilla/gfx/PathHelpers.h"
     39 #include "mozilla/intl/Segmenter.h"
     40 #include "nsCOMPtr.h"
     41 #include "nsCSSAnonBoxes.h"
     42 #include "nsCSSRendering.h"
     43 #include "nsComponentManagerUtils.h"
     44 #include "nsContainerFrame.h"
     45 #include "nsContentUtils.h"
     46 #include "nsDisplayList.h"
     47 #include "nsFontMetrics.h"
     48 #include "nsGkAtoms.h"
     49 #include "nsIContent.h"
     50 #include "nsIFrameInlines.h"
     51 #include "nsITreeView.h"
     52 #include "nsLayoutUtils.h"
     53 #include "nsNameSpaceManager.h"
     54 #include "nsPresContext.h"
     55 #include "nsString.h"
     56 #include "nsStyleConsts.h"
     57 #include "nsTreeContentView.h"
     58 #include "nsTreeImageListener.h"
     59 #include "nsTreeSelection.h"
     60 #include "nsTreeUtils.h"
     61 #include "nsWidgetsCID.h"
     62 
     63 #ifdef ACCESSIBILITY
     64 #  include "nsAccessibilityService.h"
     65 #  include "nsIWritablePropertyBag2.h"
     66 #endif
     67 #include "nsBidiUtils.h"
     68 
     69 using namespace mozilla;
     70 using namespace mozilla::dom;
     71 using namespace mozilla::gfx;
     72 using namespace mozilla::image;
     73 using namespace mozilla::layout;
     74 
     75 enum CroppingStyle { CropNone, CropLeft, CropRight, CropCenter, CropAuto };
     76 
     77 // FIXME: Maybe unify with MiddleCroppingBlockFrame?
     78 static void CropStringForWidth(nsAString& aText, gfxContext& aRenderingContext,
     79                               nsFontMetrics& aFontMetrics, nscoord aWidth,
     80                               CroppingStyle aCropType) {
     81  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
     82 
     83  // See if the width is even smaller than the ellipsis
     84  // If so, clear the text completely.
     85  const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
     86  aFontMetrics.SetTextRunRTL(false);
     87  nscoord ellipsisWidth =
     88      nsLayoutUtils::AppUnitWidthOfString(kEllipsis, aFontMetrics, drawTarget);
     89 
     90  if (ellipsisWidth > aWidth) {
     91    aText.Truncate(0);
     92    return;
     93  }
     94  if (ellipsisWidth == aWidth) {
     95    aText.Assign(kEllipsis);
     96    return;
     97  }
     98 
     99  // We will be drawing an ellipsis, thank you very much.
    100  // Subtract out the required width of the ellipsis.
    101  // This is the total remaining width we have to play with.
    102  aWidth -= ellipsisWidth;
    103 
    104  using mozilla::intl::GraphemeClusterBreakIteratorUtf16;
    105  using mozilla::intl::GraphemeClusterBreakReverseIteratorUtf16;
    106 
    107  // Now we crop. This is quite basic: it will not be really accurate in the
    108  // presence of complex scripts with contextual shaping, etc., as it measures
    109  // each grapheme cluster in isolation, not in its proper context.
    110  switch (aCropType) {
    111    case CropAuto:
    112    case CropNone:
    113    case CropRight: {
    114      const Span text(aText);
    115      GraphemeClusterBreakIteratorUtf16 iter(text);
    116      uint32_t pos = 0;
    117      nscoord totalWidth = 0;
    118 
    119      while (Maybe<uint32_t> nextPos = iter.Next()) {
    120        const nscoord charWidth = nsLayoutUtils::AppUnitWidthOfString(
    121            text.FromTo(pos, *nextPos), aFontMetrics, drawTarget);
    122        if (totalWidth + charWidth > aWidth) {
    123          break;
    124        }
    125        pos = *nextPos;
    126        totalWidth += charWidth;
    127      }
    128 
    129      if (pos < aText.Length()) {
    130        aText.Replace(pos, aText.Length() - pos, kEllipsis);
    131      }
    132    } break;
    133 
    134    case CropLeft: {
    135      const Span text(aText);
    136      GraphemeClusterBreakReverseIteratorUtf16 iter(text);
    137      uint32_t pos = text.Length();
    138      nscoord totalWidth = 0;
    139 
    140      // nextPos is decreasing since we use a reverse iterator.
    141      while (Maybe<uint32_t> nextPos = iter.Next()) {
    142        const nscoord charWidth = nsLayoutUtils::AppUnitWidthOfString(
    143            text.FromTo(*nextPos, pos), aFontMetrics, drawTarget);
    144        if (totalWidth + charWidth > aWidth) {
    145          break;
    146        }
    147 
    148        pos = *nextPos;
    149        totalWidth += charWidth;
    150      }
    151 
    152      if (pos > 0) {
    153        aText.Replace(0, pos, kEllipsis);
    154      }
    155    } break;
    156 
    157    case CropCenter: {
    158      const Span text(aText);
    159      nscoord totalWidth = 0;
    160      GraphemeClusterBreakIteratorUtf16 leftIter(text);
    161      GraphemeClusterBreakReverseIteratorUtf16 rightIter(text);
    162      uint32_t leftPos = 0;
    163      uint32_t rightPos = text.Length();
    164 
    165      while (leftPos < rightPos) {
    166        Maybe<uint32_t> nextPos = leftIter.Next();
    167        nscoord charWidth = nsLayoutUtils::AppUnitWidthOfString(
    168            text.FromTo(leftPos, *nextPos), aFontMetrics, drawTarget);
    169        if (totalWidth + charWidth > aWidth) {
    170          break;
    171        }
    172 
    173        leftPos = *nextPos;
    174        totalWidth += charWidth;
    175 
    176        if (leftPos >= rightPos) {
    177          break;
    178        }
    179 
    180        nextPos = rightIter.Next();
    181        charWidth = nsLayoutUtils::AppUnitWidthOfString(
    182            text.FromTo(*nextPos, rightPos), aFontMetrics, drawTarget);
    183        if (totalWidth + charWidth > aWidth) {
    184          break;
    185        }
    186 
    187        rightPos = *nextPos;
    188        totalWidth += charWidth;
    189      }
    190 
    191      if (leftPos < rightPos) {
    192        aText.Replace(leftPos, rightPos - leftPos, kEllipsis);
    193      }
    194    } break;
    195  }
    196 }
    197 
    198 nsTreeImageCacheEntry::nsTreeImageCacheEntry() = default;
    199 nsTreeImageCacheEntry::nsTreeImageCacheEntry(imgIRequest* aRequest,
    200                                             nsTreeImageListener* aListener)
    201    : request(aRequest), listener(aListener) {}
    202 nsTreeImageCacheEntry::~nsTreeImageCacheEntry() = default;
    203 
    204 static void DoCancelImageCacheEntry(const nsTreeImageCacheEntry& aEntry,
    205                                    nsPresContext* aPc) {
    206  // If our imgIRequest object was registered with the refresh driver
    207  // then we need to deregister it.
    208  aEntry.listener->ClearFrame();
    209  nsLayoutUtils::DeregisterImageRequest(aPc, aEntry.request, nullptr);
    210  aEntry.request->UnlockImage();
    211  aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
    212 }
    213 
    214 // Function that cancels all the image requests in our cache.
    215 void nsTreeBodyFrame::CancelImageRequests() {
    216  auto* pc = PresContext();
    217  for (const nsTreeImageCacheEntry& entry : mImageCache.Values()) {
    218    DoCancelImageCacheEntry(entry, pc);
    219  }
    220 }
    221 
    222 //
    223 // NS_NewTreeFrame
    224 //
    225 // Creates a new tree frame
    226 //
    227 nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
    228  return new (aPresShell) nsTreeBodyFrame(aStyle, aPresShell->GetPresContext());
    229 }
    230 
    231 NS_IMPL_FRAMEARENA_HELPERS(nsTreeBodyFrame)
    232 
    233 NS_QUERYFRAME_HEAD(nsTreeBodyFrame)
    234  NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
    235  NS_QUERYFRAME_ENTRY(nsTreeBodyFrame)
    236 NS_QUERYFRAME_TAIL_INHERITING(SimpleXULLeafFrame)
    237 
    238 // Constructor
    239 nsTreeBodyFrame::nsTreeBodyFrame(ComputedStyle* aStyle,
    240                                 nsPresContext* aPresContext)
    241    : SimpleXULLeafFrame(aStyle, aPresContext, kClassID),
    242      mTopRowIndex(0),
    243      mPageLength(0),
    244      mHorzPosition(0),
    245      mOriginalHorzWidth(-1),
    246      mHorzWidth(0),
    247      mRowHeight(0),
    248      mIndentation(0),
    249      mUpdateBatchNest(0),
    250      mRowCount(0),
    251      mMouseOverRow(-1),
    252      mFocused(false),
    253      mHasFixedRowCount(false),
    254      mVerticalOverflow(false),
    255      mReflowCallbackPosted(false),
    256      mCheckingOverflow(false) {
    257  mColumns = new nsTreeColumns(this);
    258 }
    259 
    260 // Destructor
    261 nsTreeBodyFrame::~nsTreeBodyFrame() { CancelImageRequests(); }
    262 
    263 static void GetBorderPadding(ComputedStyle* aStyle, nsMargin& aMargin) {
    264  aMargin.SizeTo(0, 0, 0, 0);
    265  aStyle->StylePadding()->GetPadding(aMargin);
    266  aMargin += aStyle->StyleBorder()->GetComputedBorder();
    267 }
    268 
    269 static void AdjustForBorderPadding(ComputedStyle* aStyle, nsRect& aRect) {
    270  nsMargin borderPadding(0, 0, 0, 0);
    271  GetBorderPadding(aStyle, borderPadding);
    272  aRect.Deflate(borderPadding);
    273 }
    274 
    275 void nsTreeBodyFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
    276                           nsIFrame* aPrevInFlow) {
    277  SimpleXULLeafFrame::Init(aContent, aParent, aPrevInFlow);
    278 
    279  mIndentation = GetIndentation();
    280  mRowHeight = GetRowHeight();
    281 
    282  // Call GetBaseElement so that mTree is assigned.
    283  RefPtr<XULTreeElement> tree(GetBaseElement());
    284  if (MOZ_LIKELY(tree)) {
    285    nsAutoString rows;
    286    if (tree->GetAttr(nsGkAtoms::rows, rows)) {
    287      nsresult err;
    288      mPageLength = rows.ToInteger(&err);
    289      mHasFixedRowCount = true;
    290    }
    291  }
    292 
    293  if (PresContext()->UseOverlayScrollbars()) {
    294    mScrollbarActivity =
    295        new ScrollbarActivity(static_cast<nsIScrollbarMediator*>(this));
    296  }
    297 }
    298 
    299 void nsTreeBodyFrame::Destroy(DestroyContext& aContext) {
    300  if (mScrollbarActivity) {
    301    mScrollbarActivity->Destroy();
    302    mScrollbarActivity = nullptr;
    303  }
    304 
    305  mScrollEvent.Revoke();
    306  // Make sure we cancel any posted callbacks.
    307  if (mReflowCallbackPosted) {
    308    PresShell()->CancelReflowCallback(this);
    309    mReflowCallbackPosted = false;
    310  }
    311 
    312  if (mColumns) {
    313    mColumns->SetTree(nullptr);
    314  }
    315 
    316  RefPtr tree = mTree;
    317 
    318  if (nsCOMPtr<nsITreeView> view = std::move(mView)) {
    319    nsCOMPtr<nsITreeSelection> sel;
    320    view->GetSelection(getter_AddRefs(sel));
    321    if (sel) {
    322      sel->SetTree(nullptr);
    323    }
    324    view->SetTree(nullptr);
    325  }
    326 
    327  // Make this call now because view->SetTree can run js which can undo this
    328  // call.
    329  if (tree) {
    330    tree->BodyDestroyed(mTopRowIndex);
    331  }
    332  if (mTree && mTree != tree) {
    333    mTree->BodyDestroyed(mTopRowIndex);
    334  }
    335 
    336  SimpleXULLeafFrame::Destroy(aContext);
    337 }
    338 
    339 void nsTreeBodyFrame::EnsureView() {
    340  if (mView) {
    341    return;
    342  }
    343 
    344  if (PresShell()->IsReflowLocked()) {
    345    if (!mReflowCallbackPosted) {
    346      mReflowCallbackPosted = true;
    347      PresShell()->PostReflowCallback(this);
    348    }
    349    return;
    350  }
    351 
    352  AutoWeakFrame weakFrame(this);
    353 
    354  RefPtr<XULTreeElement> tree = GetBaseElement();
    355  if (!tree) {
    356    return;
    357  }
    358  nsCOMPtr<nsITreeView> treeView = tree->GetView();
    359  if (!treeView || !weakFrame.IsAlive()) {
    360    return;
    361  }
    362  int32_t rowIndex = tree->GetCachedTopVisibleRow();
    363 
    364  // Set our view.
    365  SetView(treeView);
    366  NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
    367 
    368  // Scroll to the given row.
    369  // XXX is this optimal if we haven't laid out yet?
    370  ScrollToRow(rowIndex);
    371  NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
    372 }
    373 
    374 void nsTreeBodyFrame::ManageReflowCallback() {
    375  const nscoord horzWidth = mRect.width;
    376  if (!mReflowCallbackPosted) {
    377    if (!mLastReflowRect || !mLastReflowRect->IsEqualEdges(mRect) ||
    378        mHorzWidth != horzWidth) {
    379      PresShell()->PostReflowCallback(this);
    380      mReflowCallbackPosted = true;
    381      mOriginalHorzWidth = mHorzWidth;
    382    }
    383  } else if (mHorzWidth != horzWidth && mOriginalHorzWidth == horzWidth) {
    384    // FIXME(emilio): This doesn't seem sound to me, if the rect changes in the
    385    // block axis.
    386    PresShell()->CancelReflowCallback(this);
    387    mReflowCallbackPosted = false;
    388    mOriginalHorzWidth = -1;
    389  }
    390  mLastReflowRect = Some(mRect);
    391  mHorzWidth = horzWidth;
    392 }
    393 
    394 IntrinsicSize nsTreeBodyFrame::GetIntrinsicSize() {
    395  IntrinsicSize intrinsicSize;
    396  if (mHasFixedRowCount) {
    397    intrinsicSize.BSize(GetWritingMode()).emplace(mRowHeight * mPageLength);
    398  }
    399  return intrinsicSize;
    400 }
    401 
    402 void nsTreeBodyFrame::DidReflow(nsPresContext* aPresContext,
    403                                const ReflowInput* aReflowInput) {
    404  ManageReflowCallback();
    405  SimpleXULLeafFrame::DidReflow(aPresContext, aReflowInput);
    406 }
    407 
    408 bool nsTreeBodyFrame::ReflowFinished() {
    409  if (!mView) {
    410    AutoWeakFrame weakFrame(this);
    411    EnsureView();
    412    NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
    413  }
    414  if (mView) {
    415    CalcInnerBox();
    416    ScrollParts parts = GetScrollParts();
    417    mHorzWidth = mRect.width;
    418    if (!mHasFixedRowCount) {
    419      mPageLength =
    420          (mRowHeight > 0) ? (mInnerBox.height / mRowHeight) : mRowCount;
    421    }
    422 
    423    int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength);
    424    if (mTopRowIndex > lastPageTopRow) {
    425      ScrollToRowInternal(parts, lastPageTopRow);
    426    }
    427 
    428    XULTreeElement* treeContent = GetBaseElement();
    429    if (treeContent && treeContent->AttrValueIs(
    430                           kNameSpaceID_None, nsGkAtoms::keepcurrentinview,
    431                           nsGkAtoms::_true, eCaseMatters)) {
    432      // make sure that the current selected item is still
    433      // visible after the tree changes size.
    434      if (nsCOMPtr<nsITreeSelection> sel = GetSelection()) {
    435        int32_t currentIndex;
    436        sel->GetCurrentIndex(&currentIndex);
    437        if (currentIndex != -1) {
    438          EnsureRowIsVisibleInternal(parts, currentIndex);
    439        }
    440      }
    441    }
    442 
    443    if (!FullScrollbarsUpdate(false)) {
    444      return false;
    445    }
    446  }
    447 
    448  mReflowCallbackPosted = false;
    449  return false;
    450 }
    451 
    452 void nsTreeBodyFrame::ReflowCallbackCanceled() {
    453  mReflowCallbackPosted = false;
    454 }
    455 
    456 nsresult nsTreeBodyFrame::GetView(nsITreeView** aView) {
    457  *aView = nullptr;
    458  AutoWeakFrame weakFrame(this);
    459  EnsureView();
    460  NS_ENSURE_STATE(weakFrame.IsAlive());
    461  NS_IF_ADDREF(*aView = mView);
    462  return NS_OK;
    463 }
    464 
    465 nsresult nsTreeBodyFrame::SetView(nsITreeView* aView) {
    466  if (aView == mView) {
    467    return NS_OK;
    468  }
    469 
    470  // First clear out the old view.
    471  nsCOMPtr<nsITreeView> oldView = std::move(mView);
    472  if (oldView) {
    473    AutoWeakFrame weakFrame(this);
    474 
    475    nsCOMPtr<nsITreeSelection> sel;
    476    oldView->GetSelection(getter_AddRefs(sel));
    477    if (sel) {
    478      sel->SetTree(nullptr);
    479    }
    480    oldView->SetTree(nullptr);
    481 
    482    NS_ENSURE_STATE(weakFrame.IsAlive());
    483 
    484    // Only reset the top row index and delete the columns if we had an old
    485    // non-null view.
    486    mTopRowIndex = 0;
    487  }
    488 
    489  // Tree, meet the view.
    490  mView = aView;
    491 
    492  // Changing the view causes us to refetch our data.  This will
    493  // necessarily entail a full invalidation of the tree.
    494  Invalidate();
    495 
    496  RefPtr<XULTreeElement> treeContent = GetBaseElement();
    497  if (treeContent) {
    498 #ifdef ACCESSIBILITY
    499    if (nsAccessibilityService* accService = GetAccService()) {
    500      accService->TreeViewChanged(PresContext()->GetPresShell(), treeContent,
    501                                  mView);
    502    }
    503 #endif  // #ifdef ACCESSIBILITY
    504    FireDOMEvent(u"TreeViewChanged"_ns, treeContent);
    505  }
    506 
    507  if (aView) {
    508    // Give the view a new empty selection object to play with, but only if it
    509    // doesn't have one already.
    510    nsCOMPtr<nsITreeSelection> sel;
    511    aView->GetSelection(getter_AddRefs(sel));
    512    if (sel) {
    513      sel->SetTree(treeContent);
    514    } else {
    515      NS_NewTreeSelection(treeContent, getter_AddRefs(sel));
    516      aView->SetSelection(sel);
    517    }
    518 
    519    // View, meet the tree.
    520    AutoWeakFrame weakFrame(this);
    521    aView->SetTree(treeContent);
    522    NS_ENSURE_STATE(weakFrame.IsAlive());
    523    aView->GetRowCount(&mRowCount);
    524 
    525    if (!PresShell()->IsReflowLocked()) {
    526      // The scrollbar will need to be updated.
    527      FullScrollbarsUpdate(false);
    528    } else if (!mReflowCallbackPosted) {
    529      mReflowCallbackPosted = true;
    530      PresShell()->PostReflowCallback(this);
    531    }
    532  }
    533 
    534  return NS_OK;
    535 }
    536 
    537 already_AddRefed<nsITreeSelection> nsTreeBodyFrame::GetSelection() const {
    538  nsCOMPtr<nsITreeSelection> sel;
    539  if (nsCOMPtr<nsITreeView> view = GetExistingView()) {
    540    view->GetSelection(getter_AddRefs(sel));
    541  }
    542  return sel.forget();
    543 }
    544 
    545 nsresult nsTreeBodyFrame::SetFocused(bool aFocused) {
    546  if (mFocused != aFocused) {
    547    mFocused = aFocused;
    548    if (nsCOMPtr<nsITreeSelection> sel = GetSelection()) {
    549      sel->InvalidateSelection();
    550    }
    551  }
    552  return NS_OK;
    553 }
    554 
    555 nsresult nsTreeBodyFrame::GetTreeBody(Element** aElement) {
    556  // NS_ASSERTION(mContent, "no content, see bug #104878");
    557  if (!mContent) {
    558    return NS_ERROR_NULL_POINTER;
    559  }
    560 
    561  RefPtr<Element> element = mContent->AsElement();
    562  element.forget(aElement);
    563  return NS_OK;
    564 }
    565 
    566 int32_t nsTreeBodyFrame::RowHeight() const {
    567  return nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
    568 }
    569 
    570 int32_t nsTreeBodyFrame::RowWidth() {
    571  return nsPresContext::AppUnitsToIntCSSPixels(mRect.width);
    572 }
    573 
    574 Maybe<CSSIntRegion> nsTreeBodyFrame::GetSelectionRegion() {
    575  if (!mView) {
    576    return Nothing();
    577  }
    578 
    579  AutoWeakFrame wf(this);
    580  nsCOMPtr<nsITreeSelection> selection = GetSelection();
    581  if (!selection || !wf.IsAlive()) {
    582    return Nothing();
    583  }
    584 
    585  nsIntRect rect = mRect.ToOutsidePixels(AppUnitsPerCSSPixel());
    586 
    587  nsIFrame* rootFrame = PresShell()->GetRootFrame();
    588  nsPoint origin = GetOffsetTo(rootFrame);
    589 
    590  CSSIntRegion region;
    591 
    592  // iterate through the visible rows and add the selected ones to the
    593  // drag region
    594  int32_t x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
    595  int32_t y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
    596  int32_t top = y;
    597  int32_t end = LastVisibleRow();
    598  int32_t rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
    599  for (int32_t i = mTopRowIndex; i <= end; i++) {
    600    bool isSelected;
    601    selection->IsSelected(i, &isSelected);
    602    if (isSelected) {
    603      region.OrWith(CSSIntRect(x, y, rect.width, rowHeight));
    604    }
    605    y += rowHeight;
    606  }
    607 
    608  // clip to the tree boundary in case one row extends past it
    609  region.AndWith(CSSIntRect(x, top, rect.width, rect.height));
    610 
    611  return Some(region);
    612 }
    613 
    614 nsresult nsTreeBodyFrame::Invalidate() {
    615  if (mUpdateBatchNest) {
    616    return NS_OK;
    617  }
    618 
    619  InvalidateFrame();
    620 
    621  return NS_OK;
    622 }
    623 
    624 nsresult nsTreeBodyFrame::InvalidateColumn(nsTreeColumn* aCol) {
    625  if (mUpdateBatchNest) {
    626    return NS_OK;
    627  }
    628 
    629  if (!aCol) {
    630    return NS_ERROR_INVALID_ARG;
    631  }
    632 
    633 #ifdef ACCESSIBILITY
    634  if (GetAccService()) {
    635    FireInvalidateEvent(-1, -1, aCol, aCol);
    636  }
    637 #endif  // #ifdef ACCESSIBILITY
    638 
    639  nsRect columnRect;
    640  nsresult rv = aCol->GetRect(this, mInnerBox.y, mInnerBox.height, &columnRect);
    641  NS_ENSURE_SUCCESS(rv, rv);
    642 
    643  // When false then column is out of view
    644  if (OffsetForHorzScroll(columnRect, true)) {
    645    InvalidateFrameWithRect(columnRect);
    646  }
    647 
    648  return NS_OK;
    649 }
    650 
    651 nsresult nsTreeBodyFrame::InvalidateRow(int32_t aIndex) {
    652  if (mUpdateBatchNest) {
    653    return NS_OK;
    654  }
    655 
    656 #ifdef ACCESSIBILITY
    657  if (GetAccService()) {
    658    FireInvalidateEvent(aIndex, aIndex, nullptr, nullptr);
    659  }
    660 #endif  // #ifdef ACCESSIBILITY
    661 
    662  aIndex -= mTopRowIndex;
    663  if (aIndex < 0 || aIndex > mPageLength) {
    664    return NS_OK;
    665  }
    666 
    667  nsRect rowRect(mInnerBox.x, mInnerBox.y + mRowHeight * aIndex,
    668                 mInnerBox.width, mRowHeight);
    669  InvalidateFrameWithRect(rowRect);
    670 
    671  return NS_OK;
    672 }
    673 
    674 nsresult nsTreeBodyFrame::InvalidateCell(int32_t aIndex, nsTreeColumn* aCol) {
    675  if (mUpdateBatchNest) {
    676    return NS_OK;
    677  }
    678 
    679 #ifdef ACCESSIBILITY
    680  if (GetAccService()) {
    681    FireInvalidateEvent(aIndex, aIndex, aCol, aCol);
    682  }
    683 #endif  // #ifdef ACCESSIBILITY
    684 
    685  aIndex -= mTopRowIndex;
    686  if (aIndex < 0 || aIndex > mPageLength) {
    687    return NS_OK;
    688  }
    689 
    690  if (!aCol) {
    691    return NS_ERROR_INVALID_ARG;
    692  }
    693 
    694  nsRect cellRect;
    695  nsresult rv = aCol->GetRect(this, mInnerBox.y + mRowHeight * aIndex,
    696                              mRowHeight, &cellRect);
    697  NS_ENSURE_SUCCESS(rv, rv);
    698 
    699  if (OffsetForHorzScroll(cellRect, true)) {
    700    InvalidateFrameWithRect(cellRect);
    701  }
    702 
    703  return NS_OK;
    704 }
    705 
    706 nsresult nsTreeBodyFrame::InvalidateRange(int32_t aStart, int32_t aEnd) {
    707  if (mUpdateBatchNest) {
    708    return NS_OK;
    709  }
    710 
    711  if (aStart == aEnd) {
    712    return InvalidateRow(aStart);
    713  }
    714 
    715  int32_t last = LastVisibleRow();
    716  if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last) {
    717    return NS_OK;
    718  }
    719 
    720  if (aStart < mTopRowIndex) {
    721    aStart = mTopRowIndex;
    722  }
    723 
    724  if (aEnd > last) {
    725    aEnd = last;
    726  }
    727 
    728 #ifdef ACCESSIBILITY
    729  if (GetAccService()) {
    730    int32_t end =
    731        mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0;
    732    FireInvalidateEvent(aStart, end, nullptr, nullptr);
    733  }
    734 #endif  // #ifdef ACCESSIBILITY
    735 
    736  nsRect rangeRect(mInnerBox.x,
    737                   mInnerBox.y + mRowHeight * (aStart - mTopRowIndex),
    738                   mInnerBox.width, mRowHeight * (aEnd - aStart + 1));
    739  InvalidateFrameWithRect(rangeRect);
    740 
    741  return NS_OK;
    742 }
    743 
    744 static void FindScrollParts(nsIFrame* aCurrFrame,
    745                            nsTreeBodyFrame::ScrollParts* aResult) {
    746  if (nsScrollbarFrame* sf = do_QueryFrame(aCurrFrame)) {
    747    if (!sf->IsHorizontal() && !aResult->mVScrollbar) {
    748      aResult->mVScrollbar = sf;
    749    }
    750    // don't bother searching inside a scrollbar
    751    return;
    752  }
    753 
    754  nsIFrame* child = aCurrFrame->PrincipalChildList().FirstChild();
    755  while (child && !child->GetContent()->IsRootOfNativeAnonymousSubtree() &&
    756         !aResult->mVScrollbar) {
    757    FindScrollParts(child, aResult);
    758    child = child->GetNextSibling();
    759  }
    760 }
    761 
    762 nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts() {
    763  ScrollParts result;
    764  XULTreeElement* tree = GetBaseElement();
    765  if (nsIFrame* treeFrame = tree ? tree->GetPrimaryFrame() : nullptr) {
    766    // The way we do this, searching through the entire frame subtree, is pretty
    767    // dumb! We should know where these frames are.
    768    FindScrollParts(treeFrame, &result);
    769    if (result.mVScrollbar) {
    770      result.mVScrollbar->SetOverrideScrollbarMediator(this);
    771      result.mVScrollbarContent = result.mVScrollbar->GetContent()->AsElement();
    772    }
    773  }
    774  return result;
    775 }
    776 
    777 void nsTreeBodyFrame::UpdateScrollbars(const ScrollParts& aParts) {
    778  if (!aParts.mVScrollbar) {
    779    return;
    780  }
    781  CSSIntCoord rowHeightAsPixels =
    782      nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
    783  CSSIntCoord pos = mTopRowIndex * rowHeightAsPixels;
    784  if (!aParts.mVScrollbar->SetCurPos(pos)) {
    785    return;
    786  }
    787  if (mScrollbarActivity) {
    788    mScrollbarActivity->ActivityOccurred();
    789  }
    790 }
    791 
    792 void nsTreeBodyFrame::CheckOverflow(const ScrollParts& aParts) {
    793  bool verticalOverflowChanged = false;
    794  if (!mVerticalOverflow && mRowCount > mPageLength) {
    795    mVerticalOverflow = true;
    796    verticalOverflowChanged = true;
    797  } else if (mVerticalOverflow && mRowCount <= mPageLength) {
    798    mVerticalOverflow = false;
    799    verticalOverflowChanged = true;
    800  }
    801 
    802  if (!verticalOverflowChanged) {
    803    return;
    804  }
    805 
    806  AutoWeakFrame weakFrame(this);
    807 
    808  RefPtr<nsPresContext> presContext = PresContext();
    809  RefPtr<mozilla::PresShell> presShell = presContext->GetPresShell();
    810  nsCOMPtr<nsIContent> content = mContent;
    811 
    812  InternalScrollPortEvent event(
    813      true, mVerticalOverflow ? eScrollPortOverflow : eScrollPortUnderflow,
    814      nullptr);
    815  event.mOrient = InternalScrollPortEvent::eVertical;
    816  EventDispatcher::Dispatch(content, presContext, &event);
    817 
    818  // The synchronous event dispatch above can trigger reflow notifications.
    819  // Flush those explicitly now, so that we can guard against potential infinite
    820  // recursion. See bug 905909.
    821  if (!weakFrame.IsAlive()) {
    822    return;
    823  }
    824  NS_ASSERTION(!mCheckingOverflow,
    825               "mCheckingOverflow should not already be set");
    826  // Don't use AutoRestore since we want to not touch mCheckingOverflow if we
    827  // fail the weakFrame.IsAlive() check below
    828  mCheckingOverflow = true;
    829  presShell->FlushPendingNotifications(FlushType::Layout);
    830  if (!weakFrame.IsAlive()) {
    831    return;
    832  }
    833  mCheckingOverflow = false;
    834 }
    835 
    836 void nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts) {
    837  if (mUpdateBatchNest || !mView || !aParts.mVScrollbar) {
    838    return;
    839  }
    840  nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
    841  CSSIntCoord size = rowHeightAsPixels *
    842                     (mRowCount > mPageLength ? mRowCount - mPageLength : 0);
    843  CSSIntCoord pageincrement = mPageLength * rowHeightAsPixels;
    844  bool changed = false;
    845  changed |= aParts.mVScrollbar->SetMaxPos(size);
    846  changed |= aParts.mVScrollbar->SetPageIncrement(pageincrement);
    847  if (changed && mScrollbarActivity) {
    848    mScrollbarActivity->ActivityOccurred();
    849  }
    850 }
    851 
    852 // Takes client x/y in pixels, converts them to appunits, and converts into
    853 // values relative to this nsTreeBodyFrame frame.
    854 nsPoint nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(int32_t aX,
    855                                                           int32_t aY) {
    856  nsPoint point(nsPresContext::CSSPixelsToAppUnits(aX),
    857                nsPresContext::CSSPixelsToAppUnits(aY));
    858 
    859  nsPresContext* presContext = PresContext();
    860  point -= GetOffsetTo(presContext->GetPresShell()->GetRootFrame());
    861 
    862  // Adjust by the inner box coords, so that we're in the inner box's
    863  // coordinate space.
    864  point -= mInnerBox.TopLeft();
    865  return point;
    866 }  // AdjustClientCoordsToBoxCoordSpace
    867 
    868 int32_t nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY) {
    869  if (!mView) {
    870    return 0;
    871  }
    872 
    873  nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
    874 
    875  // Check if the coordinates are above our visible space.
    876  if (point.y < 0) {
    877    return -1;
    878  }
    879 
    880  return GetRowAtInternal(point.x, point.y);
    881 }
    882 
    883 nsresult nsTreeBodyFrame::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow,
    884                                    nsTreeColumn** aCol,
    885                                    nsACString& aChildElt) {
    886  if (!mView) {
    887    return NS_OK;
    888  }
    889 
    890  nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
    891 
    892  // Check if the coordinates are above our visible space.
    893  if (point.y < 0) {
    894    *aRow = -1;
    895    return NS_OK;
    896  }
    897 
    898  nsTreeColumn* col;
    899  nsCSSAnonBoxPseudoStaticAtom* child;
    900  GetCellAt(point.x, point.y, aRow, &col, &child);
    901 
    902  if (col) {
    903    NS_ADDREF(*aCol = col);
    904    if (child == nsCSSAnonBoxes::mozTreeCell()) {
    905      aChildElt.AssignLiteral("cell");
    906    } else if (child == nsCSSAnonBoxes::mozTreeTwisty()) {
    907      aChildElt.AssignLiteral("twisty");
    908    } else if (child == nsCSSAnonBoxes::mozTreeImage()) {
    909      aChildElt.AssignLiteral("image");
    910    } else if (child == nsCSSAnonBoxes::mozTreeCellText()) {
    911      aChildElt.AssignLiteral("text");
    912    }
    913  }
    914 
    915  return NS_OK;
    916 }
    917 
    918 //
    919 // GetCoordsForCellItem
    920 //
    921 // Find the x/y location and width/height (all in PIXELS) of the given object
    922 // in the given column.
    923 //
    924 // XXX IMPORTANT XXX:
    925 // Hyatt says in the bug for this, that the following needs to be done:
    926 // (1) You need to deal with overflow when computing cell rects.  See other
    927 // column iteration examples... if you don't deal with this, you'll mistakenly
    928 // extend the cell into the scrollbar's rect.
    929 //
    930 // (2) You are adjusting the cell rect by the *row" border padding.  That's
    931 // wrong.  You need to first adjust a row rect by its border/padding, and then
    932 // the cell rect fits inside the adjusted row rect.  It also can have
    933 // border/padding as well as margins.  The vertical direction isn't that
    934 // important, but you need to get the horizontal direction right.
    935 //
    936 // (3) GetImageSize() does not include margins (but it does include
    937 // border/padding). You need to make sure to add in the image's margins as well.
    938 //
    939 nsresult nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsTreeColumn* aCol,
    940                                               const nsACString& aElement,
    941                                               int32_t* aX, int32_t* aY,
    942                                               int32_t* aWidth,
    943                                               int32_t* aHeight) {
    944  *aX = 0;
    945  *aY = 0;
    946  *aWidth = 0;
    947  *aHeight = 0;
    948 
    949  bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
    950  nscoord currX = mInnerBox.x - mHorzPosition;
    951 
    952  // The Rect for the requested item.
    953  nsRect theRect;
    954 
    955  nsPresContext* presContext = PresContext();
    956 
    957  nsCOMPtr<nsITreeView> view = GetExistingView();
    958 
    959  for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
    960       currCol = currCol->GetNext()) {
    961    // The Rect for the current cell.
    962    nscoord colWidth;
    963 #ifdef DEBUG
    964    nsresult rv =
    965 #endif
    966        currCol->GetWidthInTwips(this, &colWidth);
    967    NS_ASSERTION(NS_SUCCEEDED(rv), "invalid column");
    968 
    969    nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex),
    970                    colWidth, mRowHeight);
    971 
    972    // Check the ID of the current column to see if it matches. If it doesn't
    973    // increment the current X value and continue to the next column.
    974    if (currCol != aCol) {
    975      currX += cellRect.width;
    976      continue;
    977    }
    978    // Now obtain the properties for our cell.
    979    PrefillPropertyArray(aRow, currCol);
    980 
    981    nsAutoString properties;
    982    view->GetCellProperties(aRow, currCol, properties);
    983    nsTreeUtils::TokenizeProperties(properties, mScratchArray);
    984 
    985    ComputedStyle* rowContext =
    986        GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeRow());
    987 
    988    // We don't want to consider any of the decorations that may be present
    989    // on the current row, so we have to deflate the rect by the border and
    990    // padding and offset its left and top coordinates appropriately.
    991    AdjustForBorderPadding(rowContext, cellRect);
    992 
    993    ComputedStyle* cellContext =
    994        GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCell());
    995 
    996    constexpr auto cell = "cell"_ns;
    997    if (currCol->IsCycler() || cell.Equals(aElement)) {
    998      // If the current Column is a Cycler, then the Rect is just the cell - the
    999      // margins. Similarly, if we're just being asked for the cell rect,
   1000      // provide it.
   1001 
   1002      theRect = cellRect;
   1003      nsMargin cellMargin;
   1004      cellContext->StyleMargin()->GetMargin(cellMargin);
   1005      theRect.Deflate(cellMargin);
   1006      break;
   1007    }
   1008 
   1009    // Since we're not looking for the cell, and since the cell isn't a cycler,
   1010    // we're looking for some subcomponent, and now we need to subtract the
   1011    // borders and padding of the cell from cellRect so this does not
   1012    // interfere with our computations.
   1013    AdjustForBorderPadding(cellContext, cellRect);
   1014 
   1015    UniquePtr<gfxContext> rc =
   1016        presContext->PresShell()->CreateReferenceRenderingContext();
   1017 
   1018    // Now we'll start making our way across the cell, starting at the edge of
   1019    // the cell and proceeding until we hit the right edge. |cellX| is the
   1020    // working X value that we will increment as we crawl from left to right.
   1021    nscoord cellX = cellRect.x;
   1022    nscoord remainWidth = cellRect.width;
   1023 
   1024    if (currCol->IsPrimary()) {
   1025      // If the current Column is a Primary, then we need to take into account
   1026      // the indentation and possibly a twisty.
   1027 
   1028      // The amount of indentation is the indentation width (|mIndentation|) by
   1029      // the level.
   1030      int32_t level;
   1031      view->GetLevel(aRow, &level);
   1032      if (!isRTL) {
   1033        cellX += mIndentation * level;
   1034      }
   1035      remainWidth -= mIndentation * level;
   1036 
   1037      // Find the twisty rect by computing its size.
   1038      nsRect imageRect;
   1039      nsRect twistyRect(cellRect);
   1040      ComputedStyle* twistyContext =
   1041          GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
   1042      GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext,
   1043                    twistyContext);
   1044 
   1045      if ("twisty"_ns.Equals(aElement)) {
   1046        // If we're looking for the twisty Rect, just return the size
   1047        theRect = twistyRect;
   1048        break;
   1049      }
   1050 
   1051      // Now we need to add in the margins of the twisty element, so that we
   1052      // can find the offset of the next element in the cell.
   1053      nsMargin twistyMargin;
   1054      twistyContext->StyleMargin()->GetMargin(twistyMargin);
   1055      twistyRect.Inflate(twistyMargin);
   1056 
   1057      // Adjust our working X value with the twisty width (image size, margins,
   1058      // borders, padding.
   1059      if (!isRTL) {
   1060        cellX += twistyRect.width;
   1061      }
   1062    }
   1063 
   1064    // Cell Image
   1065    ComputedStyle* imageContext =
   1066        GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeImage());
   1067 
   1068    nsRect imageSize = GetImageSize(aRow, currCol, false, imageContext);
   1069    if ("image"_ns.Equals(aElement)) {
   1070      theRect = imageSize;
   1071      theRect.x = cellX;
   1072      theRect.y = cellRect.y;
   1073      break;
   1074    }
   1075 
   1076    // Add in the margins of the cell image.
   1077    nsMargin imageMargin;
   1078    imageContext->StyleMargin()->GetMargin(imageMargin);
   1079    imageSize.Inflate(imageMargin);
   1080 
   1081    // Increment cellX by the image width
   1082    if (!isRTL) {
   1083      cellX += imageSize.width;
   1084    }
   1085 
   1086    // Cell Text
   1087    nsAutoString cellText;
   1088    view->GetCellText(aRow, currCol, cellText);
   1089    // We're going to measure this text so we need to ensure bidi is enabled if
   1090    // necessary
   1091    CheckTextForBidi(cellText);
   1092 
   1093    // Create a scratch rect to represent the text rectangle, with the current
   1094    // X and Y coords, and a guess at the width and height. The width is the
   1095    // remaining width we have left to traverse in the cell, which will be the
   1096    // widest possible value for the text rect, and the row height.
   1097    nsRect textRect(cellX, cellRect.y, remainWidth, cellRect.height);
   1098 
   1099    // Measure the width of the text. If the width of the text is greater than
   1100    // the remaining width available, then we just assume that the text has
   1101    // been cropped and use the remaining rect as the text Rect. Otherwise,
   1102    // we add in borders and padding to the text dimension and give that back.
   1103    ComputedStyle* textContext =
   1104        GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCellText());
   1105 
   1106    RefPtr<nsFontMetrics> fm =
   1107        nsLayoutUtils::GetFontMetricsForComputedStyle(textContext, presContext);
   1108    nscoord height = fm->MaxHeight();
   1109 
   1110    nsMargin textMargin;
   1111    textContext->StyleMargin()->GetMargin(textMargin);
   1112    textRect.Deflate(textMargin);
   1113 
   1114    // Center the text. XXX Obey vertical-align style prop?
   1115    if (height < textRect.height) {
   1116      textRect.y += (textRect.height - height) / 2;
   1117      textRect.height = height;
   1118    }
   1119 
   1120    nsMargin bp(0, 0, 0, 0);
   1121    GetBorderPadding(textContext, bp);
   1122    textRect.height += bp.top + bp.bottom;
   1123 
   1124    AdjustForCellText(cellText, aRow, currCol, *rc, *fm, textRect);
   1125 
   1126    theRect = textRect;
   1127  }
   1128 
   1129  if (isRTL) {
   1130    theRect.x = mInnerBox.width - theRect.x - theRect.width;
   1131  }
   1132 
   1133  *aX = nsPresContext::AppUnitsToIntCSSPixels(theRect.x);
   1134  *aY = nsPresContext::AppUnitsToIntCSSPixels(theRect.y);
   1135  *aWidth = nsPresContext::AppUnitsToIntCSSPixels(theRect.width);
   1136  *aHeight = nsPresContext::AppUnitsToIntCSSPixels(theRect.height);
   1137 
   1138  return NS_OK;
   1139 }
   1140 
   1141 int32_t nsTreeBodyFrame::GetRowAtInternal(nscoord aX, nscoord aY) {
   1142  if (mRowHeight <= 0) {
   1143    return -1;
   1144  }
   1145 
   1146  // Now just mod by our total inner box height and add to our top row index.
   1147  int32_t row = (aY / mRowHeight) + mTopRowIndex;
   1148 
   1149  // Check if the coordinates are below our visible space (or within our visible
   1150  // space but below any row).
   1151  if (row > mTopRowIndex + mPageLength || row >= mRowCount) {
   1152    return -1;
   1153  }
   1154 
   1155  return row;
   1156 }
   1157 
   1158 void nsTreeBodyFrame::CheckTextForBidi(nsAutoString& aText) {
   1159  // We could check to see whether the prescontext already has bidi enabled,
   1160  // but usually it won't, so it's probably faster to avoid the call to
   1161  // GetPresContext() when it's not needed.
   1162  if (HasRTLChars(aText)) {
   1163    PresContext()->SetBidiEnabled();
   1164  }
   1165 }
   1166 
   1167 void nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText, int32_t aRowIndex,
   1168                                        nsTreeColumn* aColumn,
   1169                                        gfxContext& aRenderingContext,
   1170                                        nsFontMetrics& aFontMetrics,
   1171                                        nsRect& aTextRect) {
   1172  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   1173 
   1174  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   1175 
   1176  nscoord maxWidth = aTextRect.width;
   1177  bool widthIsGreater = nsLayoutUtils::StringWidthIsGreaterThan(
   1178      aText, aFontMetrics, drawTarget, maxWidth);
   1179 
   1180  nsCOMPtr<nsITreeView> view = GetExistingView();
   1181  if (aColumn->Overflow()) {
   1182    DebugOnly<nsresult> rv;
   1183    nsTreeColumn* nextColumn = aColumn->GetNext();
   1184    while (nextColumn && widthIsGreater) {
   1185      while (nextColumn) {
   1186        nscoord width;
   1187        rv = nextColumn->GetWidthInTwips(this, &width);
   1188        NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
   1189 
   1190        if (width != 0) {
   1191          break;
   1192        }
   1193 
   1194        nextColumn = nextColumn->GetNext();
   1195      }
   1196 
   1197      if (nextColumn) {
   1198        nsAutoString nextText;
   1199        view->GetCellText(aRowIndex, nextColumn, nextText);
   1200        // We don't measure or draw this text so no need to check it for
   1201        // bidi-ness
   1202 
   1203        if (nextText.Length() == 0) {
   1204          nscoord width;
   1205          rv = nextColumn->GetWidthInTwips(this, &width);
   1206          NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
   1207 
   1208          maxWidth += width;
   1209          widthIsGreater = nsLayoutUtils::StringWidthIsGreaterThan(
   1210              aText, aFontMetrics, drawTarget, maxWidth);
   1211 
   1212          nextColumn = nextColumn->GetNext();
   1213        } else {
   1214          nextColumn = nullptr;
   1215        }
   1216      }
   1217    }
   1218  }
   1219 
   1220  CroppingStyle cropType = CroppingStyle::CropRight;
   1221  if (aColumn->GetCropStyle() == 1) {
   1222    cropType = CroppingStyle::CropCenter;
   1223  } else if (aColumn->GetCropStyle() == 2) {
   1224    cropType = CroppingStyle::CropLeft;
   1225  }
   1226  CropStringForWidth(aText, aRenderingContext, aFontMetrics, maxWidth,
   1227                     cropType);
   1228 
   1229  nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(
   1230      aText, this, aFontMetrics, aRenderingContext);
   1231 
   1232  switch (aColumn->GetTextAlignment()) {
   1233    case mozilla::StyleTextAlign::Right:
   1234      aTextRect.x += aTextRect.width - width;
   1235      break;
   1236    case mozilla::StyleTextAlign::Center:
   1237      aTextRect.x += (aTextRect.width - width) / 2;
   1238      break;
   1239    default:
   1240      break;
   1241  }
   1242 
   1243  aTextRect.width = width;
   1244 }
   1245 
   1246 nsCSSAnonBoxPseudoStaticAtom* nsTreeBodyFrame::GetItemWithinCellAt(
   1247    nscoord aX, const nsRect& aCellRect, int32_t aRowIndex,
   1248    nsTreeColumn* aColumn) {
   1249  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   1250 
   1251  // Obtain the properties for our cell.
   1252  PrefillPropertyArray(aRowIndex, aColumn);
   1253  nsAutoString properties;
   1254  nsCOMPtr<nsITreeView> view = GetExistingView();
   1255  view->GetCellProperties(aRowIndex, aColumn, properties);
   1256  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
   1257 
   1258  // Resolve style for the cell.
   1259  ComputedStyle* cellContext =
   1260      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCell());
   1261 
   1262  // Obtain the margins for the cell and then deflate our rect by that
   1263  // amount.  The cell is assumed to be contained within the deflated rect.
   1264  nsRect cellRect(aCellRect);
   1265  nsMargin cellMargin;
   1266  cellContext->StyleMargin()->GetMargin(cellMargin);
   1267  cellRect.Deflate(cellMargin);
   1268 
   1269  // Adjust the rect for its border and padding.
   1270  AdjustForBorderPadding(cellContext, cellRect);
   1271 
   1272  if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) {
   1273    // The user clicked within the cell's margins/borders/padding.  This
   1274    // constitutes a click on the cell.
   1275    return nsCSSAnonBoxes::mozTreeCell();
   1276  }
   1277 
   1278  nscoord currX = cellRect.x;
   1279  nscoord remainingWidth = cellRect.width;
   1280 
   1281  // Handle right alignment hit testing.
   1282  bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
   1283 
   1284  nsPresContext* presContext = PresContext();
   1285  UniquePtr<gfxContext> rc =
   1286      presContext->PresShell()->CreateReferenceRenderingContext();
   1287 
   1288  if (aColumn->IsPrimary()) {
   1289    // If we're the primary column, we have indentation and a twisty.
   1290    int32_t level;
   1291    view->GetLevel(aRowIndex, &level);
   1292 
   1293    if (!isRTL) {
   1294      currX += mIndentation * level;
   1295    }
   1296    remainingWidth -= mIndentation * level;
   1297 
   1298    if ((isRTL && aX > currX + remainingWidth) || (!isRTL && aX < currX)) {
   1299      // The user clicked within the indentation.
   1300      return nsCSSAnonBoxes::mozTreeCell();
   1301    }
   1302 
   1303    // Always leave space for the twisty.
   1304    nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
   1305    bool hasTwisty = false;
   1306    bool isContainer = false;
   1307    view->IsContainer(aRowIndex, &isContainer);
   1308    if (isContainer) {
   1309      bool isContainerEmpty = false;
   1310      view->IsContainerEmpty(aRowIndex, &isContainerEmpty);
   1311      if (!isContainerEmpty) {
   1312        hasTwisty = true;
   1313      }
   1314    }
   1315 
   1316    // Resolve style for the twisty.
   1317    ComputedStyle* twistyContext =
   1318        GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
   1319 
   1320    nsRect imageSize;
   1321    GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, presContext,
   1322                  twistyContext);
   1323 
   1324    // We will treat a click as hitting the twisty if it happens on the margins,
   1325    // borders, padding, or content of the twisty object.  By allowing a "slop"
   1326    // into the margin, we make it a little bit easier for a user to hit the
   1327    // twisty.  (We don't want to be too picky here.)
   1328    nsMargin twistyMargin;
   1329    twistyContext->StyleMargin()->GetMargin(twistyMargin);
   1330    twistyRect.Inflate(twistyMargin);
   1331    if (isRTL) {
   1332      twistyRect.x = currX + remainingWidth - twistyRect.width;
   1333    }
   1334 
   1335    // Now we test to see if aX is actually within the twistyRect.  If it is,
   1336    // and if the item should have a twisty, then we return "twisty".  If it is
   1337    // within the rect but we shouldn't have a twisty, then we return "cell".
   1338    if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) {
   1339      if (hasTwisty) {
   1340        return nsCSSAnonBoxes::mozTreeTwisty();
   1341      }
   1342      return nsCSSAnonBoxes::mozTreeCell();
   1343    }
   1344 
   1345    if (!isRTL) {
   1346      currX += twistyRect.width;
   1347    }
   1348    remainingWidth -= twistyRect.width;
   1349  }
   1350 
   1351  // Now test to see if the user hit the icon for the cell.
   1352  nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
   1353 
   1354  // Resolve style for the image.
   1355  ComputedStyle* imageContext =
   1356      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeImage());
   1357 
   1358  nsRect iconSize = GetImageSize(aRowIndex, aColumn, false, imageContext);
   1359  nsMargin imageMargin;
   1360  imageContext->StyleMargin()->GetMargin(imageMargin);
   1361  iconSize.Inflate(imageMargin);
   1362  iconRect.width = iconSize.width;
   1363  if (isRTL) {
   1364    iconRect.x = currX + remainingWidth - iconRect.width;
   1365  }
   1366 
   1367  if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) {
   1368    // The user clicked on the image.
   1369    return nsCSSAnonBoxes::mozTreeImage();
   1370  }
   1371 
   1372  if (!isRTL) {
   1373    currX += iconRect.width;
   1374  }
   1375  remainingWidth -= iconRect.width;
   1376 
   1377  nsAutoString cellText;
   1378  view->GetCellText(aRowIndex, aColumn, cellText);
   1379  // We're going to measure this text so we need to ensure bidi is enabled if
   1380  // necessary
   1381  CheckTextForBidi(cellText);
   1382 
   1383  nsRect textRect(currX, cellRect.y, remainingWidth, cellRect.height);
   1384 
   1385  ComputedStyle* textContext =
   1386      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCellText());
   1387 
   1388  nsMargin textMargin;
   1389  textContext->StyleMargin()->GetMargin(textMargin);
   1390  textRect.Deflate(textMargin);
   1391 
   1392  AdjustForBorderPadding(textContext, textRect);
   1393 
   1394  RefPtr<nsFontMetrics> fm =
   1395      nsLayoutUtils::GetFontMetricsForComputedStyle(textContext, presContext);
   1396  AdjustForCellText(cellText, aRowIndex, aColumn, *rc, *fm, textRect);
   1397 
   1398  if (aX >= textRect.x && aX < textRect.x + textRect.width) {
   1399    return nsCSSAnonBoxes::mozTreeCellText();
   1400  }
   1401  return nsCSSAnonBoxes::mozTreeCell();
   1402 }
   1403 
   1404 void nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow,
   1405                                nsTreeColumn** aCol,
   1406                                nsCSSAnonBoxPseudoStaticAtom** aChildElt) {
   1407  *aCol = nullptr;
   1408  *aChildElt = nullptr;
   1409 
   1410  *aRow = GetRowAtInternal(aX, aY);
   1411  if (*aRow < 0) {
   1412    return;
   1413  }
   1414 
   1415  // Determine the column hit.
   1416  for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
   1417       currCol = currCol->GetNext()) {
   1418    nsRect cellRect;
   1419    nsresult rv = currCol->GetRect(
   1420        this, mInnerBox.y + mRowHeight * (*aRow - mTopRowIndex), mRowHeight,
   1421        &cellRect);
   1422    if (NS_FAILED(rv)) {
   1423      MOZ_ASSERT_UNREACHABLE("column has no frame");
   1424      continue;
   1425    }
   1426 
   1427    if (!OffsetForHorzScroll(cellRect, false)) {
   1428      continue;
   1429    }
   1430 
   1431    if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) {
   1432      // We know the column hit now.
   1433      *aCol = currCol;
   1434 
   1435      if (currCol->IsCycler()) {
   1436        // Cyclers contain only images.  Fill this in immediately and return.
   1437        *aChildElt = nsCSSAnonBoxes::mozTreeImage();
   1438      } else {
   1439        *aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol);
   1440      }
   1441      break;
   1442    }
   1443  }
   1444 }
   1445 
   1446 nsresult nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
   1447                                       gfxContext* aRenderingContext,
   1448                                       nscoord& aDesiredSize,
   1449                                       nscoord& aCurrentSize) {
   1450  MOZ_ASSERT(aCol, "aCol must not be null");
   1451  MOZ_ASSERT(aRenderingContext, "aRenderingContext must not be null");
   1452 
   1453  // The rect for the current cell.
   1454  nscoord colWidth;
   1455  nsresult rv = aCol->GetWidthInTwips(this, &colWidth);
   1456  NS_ENSURE_SUCCESS(rv, rv);
   1457 
   1458  nsRect cellRect(0, 0, colWidth, mRowHeight);
   1459 
   1460  int32_t overflow =
   1461      cellRect.x + cellRect.width - (mInnerBox.x + mInnerBox.width);
   1462  if (overflow > 0) {
   1463    cellRect.width -= overflow;
   1464  }
   1465 
   1466  // Adjust borders and padding for the cell.
   1467  ComputedStyle* cellContext =
   1468      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCell());
   1469  nsMargin bp(0, 0, 0, 0);
   1470  GetBorderPadding(cellContext, bp);
   1471 
   1472  aCurrentSize = cellRect.width;
   1473  aDesiredSize = bp.left + bp.right;
   1474  nsCOMPtr<nsITreeView> view = GetExistingView();
   1475 
   1476  if (aCol->IsPrimary()) {
   1477    // If the current Column is a Primary, then we need to take into account
   1478    // the indentation and possibly a twisty.
   1479 
   1480    // The amount of indentation is the indentation width (|mIndentation|) by
   1481    // the level.
   1482    int32_t level;
   1483    view->GetLevel(aRow, &level);
   1484    aDesiredSize += mIndentation * level;
   1485 
   1486    // Find the twisty rect by computing its size.
   1487    ComputedStyle* twistyContext =
   1488        GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
   1489 
   1490    nsRect imageSize;
   1491    nsRect twistyRect(cellRect);
   1492    GetTwistyRect(aRow, aCol, imageSize, twistyRect, PresContext(),
   1493                  twistyContext);
   1494 
   1495    // Add in the margins of the twisty element.
   1496    nsMargin twistyMargin;
   1497    twistyContext->StyleMargin()->GetMargin(twistyMargin);
   1498    twistyRect.Inflate(twistyMargin);
   1499 
   1500    aDesiredSize += twistyRect.width;
   1501  }
   1502 
   1503  ComputedStyle* imageContext =
   1504      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeImage());
   1505 
   1506  // Account for the width of the cell image.
   1507  nsRect imageSize = GetImageSize(aRow, aCol, false, imageContext);
   1508  // Add in the margins of the cell image.
   1509  nsMargin imageMargin;
   1510  imageContext->StyleMargin()->GetMargin(imageMargin);
   1511  imageSize.Inflate(imageMargin);
   1512 
   1513  aDesiredSize += imageSize.width;
   1514 
   1515  // Get the cell text.
   1516  nsAutoString cellText;
   1517  view->GetCellText(aRow, aCol, cellText);
   1518  // We're going to measure this text so we need to ensure bidi is enabled if
   1519  // necessary
   1520  CheckTextForBidi(cellText);
   1521 
   1522  ComputedStyle* textContext =
   1523      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCellText());
   1524 
   1525  // Get the borders and padding for the text.
   1526  GetBorderPadding(textContext, bp);
   1527 
   1528  RefPtr<nsFontMetrics> fm =
   1529      nsLayoutUtils::GetFontMetricsForComputedStyle(textContext, PresContext());
   1530  // Get the width of the text itself
   1531  nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(cellText, this, *fm,
   1532                                                          *aRenderingContext);
   1533  nscoord totalTextWidth = width + bp.left + bp.right;
   1534  aDesiredSize += totalTextWidth;
   1535  return NS_OK;
   1536 }
   1537 
   1538 nsresult nsTreeBodyFrame::IsCellCropped(int32_t aRow, nsTreeColumn* aCol,
   1539                                        bool* _retval) {
   1540  nscoord currentSize, desiredSize;
   1541  nsresult rv;
   1542 
   1543  if (!aCol) {
   1544    return NS_ERROR_INVALID_ARG;
   1545  }
   1546 
   1547  UniquePtr<gfxContext> rc = PresShell()->CreateReferenceRenderingContext();
   1548 
   1549  rv = GetCellWidth(aRow, aCol, rc.get(), desiredSize, currentSize);
   1550  NS_ENSURE_SUCCESS(rv, rv);
   1551 
   1552  *_retval = desiredSize > currentSize;
   1553 
   1554  return NS_OK;
   1555 }
   1556 
   1557 nsresult nsTreeBodyFrame::CreateTimer(const LookAndFeel::IntID aID,
   1558                                      nsTimerCallbackFunc aFunc, int32_t aType,
   1559                                      nsITimer** aTimer,
   1560                                      const nsACString& aName) {
   1561  // Get the delay from the look and feel service.
   1562  int32_t delay = LookAndFeel::GetInt(aID, 0);
   1563 
   1564  nsCOMPtr<nsITimer> timer;
   1565 
   1566  // Create a new timer only if the delay is greater than zero.
   1567  // Zero value means that this feature is completely disabled.
   1568  if (delay > 0) {
   1569    timer = MOZ_TRY(NS_NewTimerWithFuncCallback(
   1570        aFunc, this, delay, aType, aName, GetMainThreadSerialEventTarget()));
   1571  }
   1572 
   1573  timer.forget(aTimer);
   1574  return NS_OK;
   1575 }
   1576 
   1577 nsresult nsTreeBodyFrame::RowCountChanged(int32_t aIndex, int32_t aCount) {
   1578  if (aCount == 0 || !mView) {
   1579    return NS_OK;  // Nothing to do.
   1580  }
   1581 
   1582 #ifdef ACCESSIBILITY
   1583  if (GetAccService()) {
   1584    FireRowCountChangedEvent(aIndex, aCount);
   1585  }
   1586 #endif  // #ifdef ACCESSIBILITY
   1587 
   1588  AutoWeakFrame weakFrame(this);
   1589 
   1590  // Adjust our selection.
   1591  if (nsCOMPtr<nsITreeSelection> sel = GetSelection()) {
   1592    sel->AdjustSelection(aIndex, aCount);
   1593  }
   1594 
   1595  NS_ENSURE_STATE(weakFrame.IsAlive());
   1596 
   1597  if (mUpdateBatchNest) {
   1598    return NS_OK;
   1599  }
   1600 
   1601  mRowCount += aCount;
   1602 #ifdef DEBUG
   1603  int32_t rowCount = mRowCount;
   1604  mView->GetRowCount(&rowCount);
   1605  NS_ASSERTION(
   1606      rowCount == mRowCount,
   1607      "row count did not change by the amount suggested, check caller");
   1608 #endif
   1609 
   1610  int32_t count = Abs(aCount);
   1611  int32_t last = LastVisibleRow();
   1612  if (aIndex >= mTopRowIndex && aIndex <= last) {
   1613    InvalidateRange(aIndex, last);
   1614  }
   1615 
   1616  ScrollParts parts = GetScrollParts();
   1617 
   1618  if (mTopRowIndex == 0) {
   1619    // Just update the scrollbar and return.
   1620    FullScrollbarsUpdate(false);
   1621    return NS_OK;
   1622  }
   1623 
   1624  bool needsInvalidation = false;
   1625  // Adjust our top row index.
   1626  if (aCount > 0) {
   1627    if (mTopRowIndex > aIndex) {
   1628      // Rows came in above us.  Augment the top row index.
   1629      mTopRowIndex += aCount;
   1630    }
   1631  } else if (aCount < 0) {
   1632    if (mTopRowIndex > aIndex + count - 1) {
   1633      // No need to invalidate. The remove happened
   1634      // completely above us (offscreen).
   1635      mTopRowIndex -= count;
   1636    } else if (mTopRowIndex >= aIndex) {
   1637      // This is a full-blown invalidate.
   1638      if (mTopRowIndex + mPageLength > mRowCount - 1) {
   1639        mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
   1640      }
   1641      needsInvalidation = true;
   1642    }
   1643  }
   1644 
   1645  int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength);
   1646  if (mTopRowIndex > lastPageTopRow) {
   1647    mTopRowIndex = lastPageTopRow;
   1648    needsInvalidation = true;
   1649  }
   1650 
   1651  FullScrollbarsUpdate(needsInvalidation);
   1652  return NS_OK;
   1653 }
   1654 
   1655 nsresult nsTreeBodyFrame::BeginUpdateBatch() {
   1656  ++mUpdateBatchNest;
   1657 
   1658  return NS_OK;
   1659 }
   1660 
   1661 nsresult nsTreeBodyFrame::EndUpdateBatch() {
   1662  NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
   1663 
   1664  if (--mUpdateBatchNest != 0) {
   1665    return NS_OK;
   1666  }
   1667 
   1668  nsCOMPtr<nsITreeView> view = GetExistingView();
   1669  if (!view) {
   1670    return NS_OK;
   1671  }
   1672 
   1673  Invalidate();
   1674  int32_t countBeforeUpdate = mRowCount;
   1675  view->GetRowCount(&mRowCount);
   1676  if (countBeforeUpdate != mRowCount) {
   1677    if (mTopRowIndex + mPageLength > mRowCount - 1) {
   1678      mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
   1679    }
   1680    FullScrollbarsUpdate(false);
   1681  }
   1682 
   1683  return NS_OK;
   1684 }
   1685 
   1686 void nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex,
   1687                                           nsTreeColumn* aCol) {
   1688  MOZ_ASSERT(!aCol || aCol->GetFrame(), "invalid column passed");
   1689  mScratchArray.Clear();
   1690 
   1691  // focus
   1692  if (mFocused) {
   1693    mScratchArray.AppendElement(nsGkAtoms::focus);
   1694  } else {
   1695    mScratchArray.AppendElement(nsGkAtoms::blur);
   1696  }
   1697 
   1698  // sort
   1699  bool sorted = false;
   1700  mView->IsSorted(&sorted);
   1701  if (sorted) {
   1702    mScratchArray.AppendElement(nsGkAtoms::sorted);
   1703  }
   1704 
   1705  // drag session
   1706  if (mSlots && mSlots->mIsDragging) {
   1707    mScratchArray.AppendElement(nsGkAtoms::dragSession);
   1708  }
   1709 
   1710  if (aRowIndex != -1) {
   1711    if (aRowIndex == mMouseOverRow) {
   1712      mScratchArray.AppendElement(nsGkAtoms::hover);
   1713    }
   1714 
   1715    nsCOMPtr<nsITreeSelection> selection = GetSelection();
   1716    if (selection) {
   1717      // selected
   1718      bool isSelected;
   1719      selection->IsSelected(aRowIndex, &isSelected);
   1720      if (isSelected) {
   1721        mScratchArray.AppendElement(nsGkAtoms::selected);
   1722      }
   1723 
   1724      // current
   1725      int32_t currentIndex;
   1726      selection->GetCurrentIndex(&currentIndex);
   1727      if (aRowIndex == currentIndex) {
   1728        mScratchArray.AppendElement(nsGkAtoms::current);
   1729      }
   1730    }
   1731 
   1732    // container or leaf
   1733    bool isContainer = false;
   1734    mView->IsContainer(aRowIndex, &isContainer);
   1735    if (isContainer) {
   1736      mScratchArray.AppendElement(nsGkAtoms::container);
   1737 
   1738      // open or closed
   1739      bool isOpen = false;
   1740      mView->IsContainerOpen(aRowIndex, &isOpen);
   1741      if (isOpen) {
   1742        mScratchArray.AppendElement(nsGkAtoms::open);
   1743      } else {
   1744        mScratchArray.AppendElement(nsGkAtoms::closed);
   1745      }
   1746    } else {
   1747      mScratchArray.AppendElement(nsGkAtoms::leaf);
   1748    }
   1749 
   1750    // drop orientation
   1751    if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) {
   1752      if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) {
   1753        mScratchArray.AppendElement(nsGkAtoms::dropBefore);
   1754      } else if (mSlots->mDropOrient == nsITreeView::DROP_ON) {
   1755        mScratchArray.AppendElement(nsGkAtoms::dropOn);
   1756      } else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) {
   1757        mScratchArray.AppendElement(nsGkAtoms::dropAfter);
   1758      }
   1759    }
   1760 
   1761    // odd or even
   1762    if (aRowIndex % 2) {
   1763      mScratchArray.AppendElement(nsGkAtoms::odd);
   1764    } else {
   1765      mScratchArray.AppendElement(nsGkAtoms::even);
   1766    }
   1767 
   1768    XULTreeElement* tree = GetBaseElement();
   1769    if (tree && tree->HasAttr(nsGkAtoms::editing)) {
   1770      mScratchArray.AppendElement(nsGkAtoms::editing);
   1771    }
   1772 
   1773    // multiple columns
   1774    if (mColumns->GetColumnAt(1)) {
   1775      mScratchArray.AppendElement(nsGkAtoms::multicol);
   1776    }
   1777  }
   1778 
   1779  if (aCol) {
   1780    mScratchArray.AppendElement(aCol->GetAtom());
   1781 
   1782    if (aCol->IsPrimary()) {
   1783      mScratchArray.AppendElement(nsGkAtoms::primary);
   1784    }
   1785 
   1786    if (aCol->GetType() == TreeColumn_Binding::TYPE_CHECKBOX) {
   1787      mScratchArray.AppendElement(nsGkAtoms::checkbox);
   1788 
   1789      if (aRowIndex != -1) {
   1790        nsAutoString value;
   1791        mView->GetCellValue(aRowIndex, aCol, value);
   1792        if (value.EqualsLiteral("true")) {
   1793          mScratchArray.AppendElement(nsGkAtoms::checked);
   1794        }
   1795      }
   1796    }
   1797 
   1798    if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ordinal,
   1799                                    u"1"_ns, eIgnoreCase)) {
   1800      mScratchArray.AppendElement(nsGkAtoms::firstColumn);
   1801    }
   1802 
   1803    // Read special properties from attributes on the column content node
   1804    if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::insertbefore,
   1805                                    nsGkAtoms::_true, eCaseMatters)) {
   1806      mScratchArray.AppendElement(nsGkAtoms::insertbefore);
   1807    }
   1808    if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::insertafter,
   1809                                    nsGkAtoms::_true, eCaseMatters)) {
   1810      mScratchArray.AppendElement(nsGkAtoms::insertafter);
   1811    }
   1812  }
   1813 }
   1814 
   1815 void nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, nsTreeColumn* aColumn,
   1816                                    nsRect& aImageRect, nsRect& aTwistyRect,
   1817                                    nsPresContext* aPresContext,
   1818                                    ComputedStyle* aTwistyContext) {
   1819  // The twisty rect extends all the way to the end of the cell.  This is
   1820  // incorrect.  We need to determine the twisty rect's true width.  This is
   1821  // done by examining the ComputedStyle for a width first.  If it has one, we
   1822  // use that.  If it doesn't, we use the image's natural width. If the image
   1823  // hasn't loaded and if no width is specified, then we just bail. If there is
   1824  // a -moz-appearance involved, adjust the rect by the minimum widget size
   1825  // provided by the theme implementation.
   1826  aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext);
   1827  if (aImageRect.height > aTwistyRect.height) {
   1828    aImageRect.height = aTwistyRect.height;
   1829  }
   1830  if (aImageRect.width > aTwistyRect.width) {
   1831    aImageRect.width = aTwistyRect.width;
   1832  } else {
   1833    aTwistyRect.width = aImageRect.width;
   1834  }
   1835 }
   1836 
   1837 already_AddRefed<imgIContainer> nsTreeBodyFrame::GetImage(
   1838    int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
   1839    ComputedStyle* aComputedStyle) {
   1840  Document* doc = PresContext()->Document();
   1841  nsAutoString imageSrc;
   1842  mView->GetImageSrc(aRowIndex, aCol, imageSrc);
   1843  RefPtr<imgRequestProxy> styleRequest;
   1844  nsCOMPtr<nsIURI> uri;
   1845  if (aUseContext || imageSrc.IsEmpty()) {
   1846    // Obtain the URL from the ComputedStyle.
   1847    styleRequest =
   1848        aComputedStyle->StyleList()->mListStyleImage.GetImageRequest();
   1849    if (!styleRequest) {
   1850      return nullptr;
   1851    }
   1852    styleRequest->GetURI(getter_AddRefs(uri));
   1853  } else {
   1854    nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), imageSrc,
   1855                                              doc, mContent->GetBaseURI());
   1856  }
   1857  if (!uri) {
   1858    return nullptr;
   1859  }
   1860  // Look the image up in our cache.
   1861  nsTreeImageCacheEntry entry;
   1862  if (mImageCache.Get(uri, &entry)) {
   1863    nsCOMPtr<imgIContainer> result;
   1864    entry.request->GetImage(getter_AddRefs(result));
   1865    entry.listener->AddCell(aRowIndex, aCol);
   1866    return result.forget();
   1867  }
   1868 
   1869  // Create a new nsTreeImageListener object and pass it our row and column
   1870  // information.
   1871  auto listener = MakeRefPtr<nsTreeImageListener>(this);
   1872  listener->AddCell(aRowIndex, aCol);
   1873 
   1874  RefPtr<imgRequestProxy> imageRequest;
   1875  if (styleRequest) {
   1876    styleRequest->SyncClone(listener, doc, getter_AddRefs(imageRequest));
   1877  } else {
   1878    auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
   1879    // XXXbz what's the origin principal for this stuff that comes from our
   1880    // view?  I guess we should assume that it's the node's principal...
   1881    nsContentUtils::LoadImage(uri, mContent, doc, mContent->NodePrincipal(), 0,
   1882                              referrerInfo, listener, nsIRequest::LOAD_NORMAL,
   1883                              u""_ns, getter_AddRefs(imageRequest));
   1884  }
   1885  listener->UnsuppressInvalidation();
   1886  if (!imageRequest) {
   1887    return nullptr;
   1888  }
   1889 
   1890  // NOTE(heycam): If it's an SVG image, and we need to want the image to
   1891  // able to respond to media query changes, it needs to be added to the
   1892  // document's tracked image set.  For now, assume we don't need this.
   1893  // We don't want discarding/decode-on-draw for xul images
   1894  imageRequest->StartDecoding(imgIContainer::FLAG_ASYNC_NOTIFY);
   1895  imageRequest->LockImage();
   1896 
   1897  // In a case it was already cached.
   1898  mImageCache.InsertOrUpdate(uri,
   1899                             nsTreeImageCacheEntry(imageRequest, listener));
   1900  nsCOMPtr<imgIContainer> result;
   1901  imageRequest->GetImage(getter_AddRefs(result));
   1902  return result.forget();
   1903 }
   1904 
   1905 nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol,
   1906                                     bool aUseContext,
   1907                                     ComputedStyle* aComputedStyle) {
   1908  // XXX We should respond to visibility rules for collapsed vs. hidden.
   1909 
   1910  // This method returns the width of the twisty INCLUDING borders and padding.
   1911  // It first checks the ComputedStyle for a width.  If none is found, it tries
   1912  // to use the default image width for the twisty.  If no image is found, it
   1913  // defaults to border+padding.
   1914  nsRect r(0, 0, 0, 0);
   1915  nsMargin bp(0, 0, 0, 0);
   1916  GetBorderPadding(aComputedStyle, bp);
   1917  r.Inflate(bp);
   1918 
   1919  // Now r contains our border+padding info.  We now need to get our width and
   1920  // height.
   1921  bool needWidth = false;
   1922  bool needHeight = false;
   1923 
   1924  // We have to load image even though we already have a size.
   1925  // Don't change this, otherwise things start to go awry.
   1926  nsCOMPtr<imgIContainer> image =
   1927      GetImage(aRowIndex, aCol, aUseContext, aComputedStyle);
   1928 
   1929  const nsStylePosition* myPosition = aComputedStyle->StylePosition();
   1930  const AnchorPosResolutionParams anchorResolutionParams{
   1931      this, aComputedStyle->StyleDisplay()->mPosition};
   1932  const auto width = myPosition->GetWidth(anchorResolutionParams);
   1933  if (width->ConvertsToLength()) {
   1934    int32_t val = width->ToLength();
   1935    r.width += val;
   1936  } else {
   1937    needWidth = true;
   1938  }
   1939 
   1940  const auto height = myPosition->GetHeight(anchorResolutionParams);
   1941  if (height->ConvertsToLength()) {
   1942    int32_t val = height->ToLength();
   1943    r.height += val;
   1944  } else {
   1945    needHeight = true;
   1946  }
   1947 
   1948  if (image) {
   1949    if (needWidth || needHeight) {
   1950      // Get the natural image size.
   1951 
   1952      if (needWidth) {
   1953        // Get the size from the image.
   1954        nscoord width;
   1955        image->GetWidth(&width);
   1956        r.width += nsPresContext::CSSPixelsToAppUnits(width);
   1957      }
   1958 
   1959      if (needHeight) {
   1960        nscoord height;
   1961        image->GetHeight(&height);
   1962        r.height += nsPresContext::CSSPixelsToAppUnits(height);
   1963      }
   1964    }
   1965  }
   1966 
   1967  return r;
   1968 }
   1969 
   1970 // GetImageDestSize returns the destination size of the image.
   1971 // The width and height do not include borders and padding.
   1972 // The width and height have not been adjusted to fit in the row height
   1973 // or cell width.
   1974 // The width and height reflect the destination size specified in CSS,
   1975 // or the image region specified in CSS, or the natural size of the
   1976 // image.
   1977 // If only the destination width has been specified in CSS, the height is
   1978 // calculated to maintain the aspect ratio of the image.
   1979 // If only the destination height has been specified in CSS, the width is
   1980 // calculated to maintain the aspect ratio of the image.
   1981 nsSize nsTreeBodyFrame::GetImageDestSize(ComputedStyle* aComputedStyle,
   1982                                         imgIContainer* image) {
   1983  nsSize size(0, 0);
   1984 
   1985  // We need to get the width and height.
   1986  bool needWidth = false;
   1987  bool needHeight = false;
   1988 
   1989  // Get the style position to see if the CSS has specified the
   1990  // destination width/height.
   1991  const nsStylePosition* myPosition = aComputedStyle->StylePosition();
   1992  const AnchorPosResolutionParams anchorResolutionParams{
   1993      this, aComputedStyle->StyleDisplay()->mPosition};
   1994  const auto width = myPosition->GetWidth(anchorResolutionParams);
   1995  if (width->ConvertsToLength()) {
   1996    // CSS has specified the destination width.
   1997    size.width = width->ToLength();
   1998  } else {
   1999    // We'll need to get the width of the image/region.
   2000    needWidth = true;
   2001  }
   2002 
   2003  const auto height = myPosition->GetHeight(anchorResolutionParams);
   2004  if (height->ConvertsToLength()) {
   2005    // CSS has specified the destination height.
   2006    size.height = height->ToLength();
   2007  } else {
   2008    // We'll need to get the height of the image/region.
   2009    needHeight = true;
   2010  }
   2011 
   2012  if (needWidth || needHeight) {
   2013    // We need to get the size of the image/region.
   2014    nsSize imageSize(0, 0);
   2015    if (image) {
   2016      nscoord width;
   2017      image->GetWidth(&width);
   2018      imageSize.width = nsPresContext::CSSPixelsToAppUnits(width);
   2019      nscoord height;
   2020      image->GetHeight(&height);
   2021      imageSize.height = nsPresContext::CSSPixelsToAppUnits(height);
   2022    }
   2023 
   2024    if (needWidth) {
   2025      if (!needHeight && imageSize.height != 0) {
   2026        // The CSS specified the destination height, but not the destination
   2027        // width. We need to calculate the width so that we maintain the
   2028        // image's aspect ratio.
   2029        size.width = imageSize.width * size.height / imageSize.height;
   2030      } else {
   2031        size.width = imageSize.width;
   2032      }
   2033    }
   2034 
   2035    if (needHeight) {
   2036      if (!needWidth && imageSize.width != 0) {
   2037        // The CSS specified the destination width, but not the destination
   2038        // height. We need to calculate the height so that we maintain the
   2039        // image's aspect ratio.
   2040        size.height = imageSize.height * size.width / imageSize.width;
   2041      } else {
   2042        size.height = imageSize.height;
   2043      }
   2044    }
   2045  }
   2046 
   2047  return size;
   2048 }
   2049 
   2050 // GetImageSourceRect returns the source rectangle of the image to be
   2051 // displayed.
   2052 // The width and height reflect the image region specified in CSS, or
   2053 // the natural size of the image.
   2054 // The width and height do not include borders and padding.
   2055 // The width and height do not reflect the destination size specified
   2056 // in CSS.
   2057 nsRect nsTreeBodyFrame::GetImageSourceRect(ComputedStyle* aComputedStyle,
   2058                                           imgIContainer* image) {
   2059  if (!image) {
   2060    return nsRect();
   2061  }
   2062 
   2063  nsRect r;
   2064  // Use the actual image size.
   2065  nscoord coord;
   2066  if (NS_SUCCEEDED(image->GetWidth(&coord))) {
   2067    r.width = nsPresContext::CSSPixelsToAppUnits(coord);
   2068  }
   2069  if (NS_SUCCEEDED(image->GetHeight(&coord))) {
   2070    r.height = nsPresContext::CSSPixelsToAppUnits(coord);
   2071  }
   2072  return r;
   2073 }
   2074 
   2075 int32_t nsTreeBodyFrame::GetRowHeight() {
   2076  // Look up the correct height.  It is equal to the specified height
   2077  // + the specified margins.
   2078  mScratchArray.Clear();
   2079  ComputedStyle* rowContext =
   2080      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeRow());
   2081  if (rowContext) {
   2082    const nsStylePosition* myPosition = rowContext->StylePosition();
   2083    const AnchorPosResolutionParams anchorResolutionParams{
   2084        this, rowContext->StyleDisplay()->mPosition};
   2085 
   2086    nscoord minHeight = 0;
   2087    const auto styleMinHeight =
   2088        myPosition->GetMinHeight(anchorResolutionParams);
   2089    if (styleMinHeight->ConvertsToLength()) {
   2090      minHeight = styleMinHeight->ToLength();
   2091    }
   2092 
   2093    nscoord height = 0;
   2094    const auto styleHeight = myPosition->GetHeight(anchorResolutionParams);
   2095    if (styleHeight->ConvertsToLength()) {
   2096      height = styleHeight->ToLength();
   2097    }
   2098 
   2099    if (height < minHeight) {
   2100      height = minHeight;
   2101    }
   2102 
   2103    if (height > 0) {
   2104      height = nsPresContext::AppUnitsToIntCSSPixels(height);
   2105      height += height % 2;
   2106      height = nsPresContext::CSSPixelsToAppUnits(height);
   2107 
   2108      // XXX Check box-sizing to determine if border/padding should augment the
   2109      // height Inflate the height by our margins.
   2110      nsRect rowRect(0, 0, 0, height);
   2111      nsMargin rowMargin;
   2112      rowContext->StyleMargin()->GetMargin(rowMargin);
   2113      rowRect.Inflate(rowMargin);
   2114      height = rowRect.height;
   2115      return height;
   2116    }
   2117  }
   2118 
   2119  return nsPresContext::CSSPixelsToAppUnits(18);  // As good a default as any.
   2120 }
   2121 
   2122 int32_t nsTreeBodyFrame::GetIndentation() {
   2123  // Look up the correct indentation.  It is equal to the specified indentation
   2124  // width.
   2125  mScratchArray.Clear();
   2126  ComputedStyle* indentContext =
   2127      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeIndentation());
   2128  if (indentContext) {
   2129    const nsStylePosition* myPosition = indentContext->StylePosition();
   2130    const AnchorPosResolutionParams anchorResolutionParams{
   2131        this, indentContext->StyleDisplay()->mPosition};
   2132    const auto width = myPosition->GetWidth(anchorResolutionParams);
   2133    if (width->ConvertsToLength()) {
   2134      return width->ToLength();
   2135    }
   2136  }
   2137 
   2138  return nsPresContext::CSSPixelsToAppUnits(16);  // As good a default as any.
   2139 }
   2140 
   2141 void nsTreeBodyFrame::CalcInnerBox() {
   2142  mInnerBox.SetRect(0, 0, mRect.width, mRect.height);
   2143  AdjustForBorderPadding(mComputedStyle, mInnerBox);
   2144 }
   2145 
   2146 nsIFrame::Cursor nsTreeBodyFrame::GetCursor(const nsPoint& aPoint) {
   2147  // Check the GetScriptHandlingObject so we don't end up running code when
   2148  // the document is a zombie.
   2149  bool dummy;
   2150  if (mView && GetContent()->GetComposedDoc()->GetScriptHandlingObject(dummy)) {
   2151    int32_t row;
   2152    nsTreeColumn* col;
   2153    nsCSSAnonBoxPseudoStaticAtom* child;
   2154    GetCellAt(aPoint.x, aPoint.y, &row, &col, &child);
   2155 
   2156    if (child) {
   2157      // Our scratch array is already prefilled.
   2158      RefPtr<ComputedStyle> childContext = GetPseudoComputedStyle(child);
   2159      StyleCursorKind kind = childContext->StyleUI()->Cursor().keyword;
   2160      if (kind == StyleCursorKind::Auto) {
   2161        kind = StyleCursorKind::Default;
   2162      }
   2163      return Cursor{kind, AllowCustomCursorImage::Yes, std::move(childContext)};
   2164    }
   2165  }
   2166  return SimpleXULLeafFrame::GetCursor(aPoint);
   2167 }
   2168 
   2169 static uint32_t GetDropEffect(WidgetGUIEvent* aEvent) {
   2170  NS_ASSERTION(aEvent->mClass == eDragEventClass, "wrong event type");
   2171  WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
   2172  nsContentUtils::SetDataTransferInEvent(dragEvent);
   2173 
   2174  uint32_t action = 0;
   2175  if (dragEvent->mDataTransfer) {
   2176    action = dragEvent->mDataTransfer->DropEffectInt();
   2177  }
   2178  return action;
   2179 }
   2180 
   2181 nsresult nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext,
   2182                                      WidgetGUIEvent* aEvent,
   2183                                      nsEventStatus* aEventStatus) {
   2184  if (aEvent->mMessage == eMouseOver || aEvent->mMessage == eMouseMove) {
   2185    nsPoint pt =
   2186        nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, RelativeTo{this});
   2187    int32_t xTwips = pt.x - mInnerBox.x;
   2188    int32_t yTwips = pt.y - mInnerBox.y;
   2189    int32_t newrow = GetRowAtInternal(xTwips, yTwips);
   2190    if (mMouseOverRow != newrow) {
   2191      // redraw the old and the new row
   2192      if (mMouseOverRow != -1) {
   2193        InvalidateRow(mMouseOverRow);
   2194      }
   2195      mMouseOverRow = newrow;
   2196      if (mMouseOverRow != -1) {
   2197        InvalidateRow(mMouseOverRow);
   2198      }
   2199    }
   2200  } else if (aEvent->mMessage == eMouseOut) {
   2201    if (mMouseOverRow != -1) {
   2202      InvalidateRow(mMouseOverRow);
   2203      mMouseOverRow = -1;
   2204    }
   2205  } else if (aEvent->mMessage == eDragEnter) {
   2206    if (!mSlots) {
   2207      mSlots = MakeUnique<Slots>();
   2208    }
   2209 
   2210    // Cache several things we'll need throughout the course of our work. These
   2211    // will all get released on a drag exit.
   2212 
   2213    if (mSlots->mTimer) {
   2214      mSlots->mTimer->Cancel();
   2215      mSlots->mTimer = nullptr;
   2216    }
   2217 
   2218    // Cache the drag session.
   2219    mSlots->mIsDragging = true;
   2220    mSlots->mDropRow = -1;
   2221    mSlots->mDropOrient = -1;
   2222    mSlots->mDragAction = GetDropEffect(aEvent);
   2223  } else if (aEvent->mMessage == eDragOver) {
   2224    // The mouse is hovering over this tree. If we determine things are
   2225    // different from the last time, invalidate the drop feedback at the old
   2226    // position, query the view to see if the current location is droppable,
   2227    // and then invalidate the drop feedback at the new location if it is.
   2228    // The mouse may or may not have changed position from the last time
   2229    // we were called, so optimize out a lot of the extra notifications by
   2230    // checking if anything changed first. For drop feedback we use drop,
   2231    // dropBefore and dropAfter property.
   2232    if (!mView || !mSlots) {
   2233      return NS_OK;
   2234    }
   2235 
   2236    // Save last values, we will need them.
   2237    int32_t lastDropRow = mSlots->mDropRow;
   2238    int16_t lastDropOrient = mSlots->mDropOrient;
   2239 #ifndef XP_MACOSX
   2240    int16_t lastScrollLines = mSlots->mScrollLines;
   2241 #endif
   2242 
   2243    // Find out the current drag action
   2244    uint32_t lastDragAction = mSlots->mDragAction;
   2245    mSlots->mDragAction = GetDropEffect(aEvent);
   2246 
   2247    // Compute the row mouse is over and the above/below/on state.
   2248    // Below we'll use this to see if anything changed.
   2249    // Also check if we want to auto-scroll.
   2250    ComputeDropPosition(aEvent, &mSlots->mDropRow, &mSlots->mDropOrient,
   2251                        &mSlots->mScrollLines);
   2252 
   2253    // While we're here, handle tracking of scrolling during a drag.
   2254    if (mSlots->mScrollLines) {
   2255      if (mSlots->mDropAllowed) {
   2256        // Invalidate primary cell at old location.
   2257        mSlots->mDropAllowed = false;
   2258        InvalidateDropFeedback(lastDropRow, lastDropOrient);
   2259      }
   2260 #ifdef XP_MACOSX
   2261      ScrollByLines(mSlots->mScrollLines);
   2262 #else
   2263      if (!lastScrollLines) {
   2264        // Cancel any previously initialized timer.
   2265        if (mSlots->mTimer) {
   2266          mSlots->mTimer->Cancel();
   2267          mSlots->mTimer = nullptr;
   2268        }
   2269 
   2270        // Set a timer to trigger the tree scrolling.
   2271        CreateTimer(LookAndFeel::IntID::TreeLazyScrollDelay, LazyScrollCallback,
   2272                    nsITimer::TYPE_ONE_SHOT, getter_AddRefs(mSlots->mTimer),
   2273                    "nsTreeBodyFrame::LazyScrollCallback"_ns);
   2274      }
   2275 #endif
   2276      // Bail out to prevent spring loaded timer and feedback line settings.
   2277      return NS_OK;
   2278    }
   2279 
   2280    // If changed from last time, invalidate primary cell at the old location
   2281    // and if allowed, invalidate primary cell at the new location. If nothing
   2282    // changed, just bail.
   2283    if (mSlots->mDropRow != lastDropRow ||
   2284        mSlots->mDropOrient != lastDropOrient ||
   2285        mSlots->mDragAction != lastDragAction) {
   2286      // Invalidate row at the old location.
   2287      if (mSlots->mDropAllowed) {
   2288        mSlots->mDropAllowed = false;
   2289        InvalidateDropFeedback(lastDropRow, lastDropOrient);
   2290      }
   2291 
   2292      if (mSlots->mTimer) {
   2293        // Timer is active but for a different row than the current one, kill
   2294        // it.
   2295        mSlots->mTimer->Cancel();
   2296        mSlots->mTimer = nullptr;
   2297      }
   2298 
   2299      if (mSlots->mDropRow >= 0) {
   2300        if (!mSlots->mTimer && mSlots->mDropOrient == nsITreeView::DROP_ON) {
   2301          // Either there wasn't a timer running or it was just killed above.
   2302          // If over a folder, start up a timer to open the folder.
   2303          bool isContainer = false;
   2304          mView->IsContainer(mSlots->mDropRow, &isContainer);
   2305          if (isContainer) {
   2306            bool isOpen = false;
   2307            mView->IsContainerOpen(mSlots->mDropRow, &isOpen);
   2308            if (!isOpen) {
   2309              // This node isn't expanded, set a timer to expand it.
   2310              CreateTimer(LookAndFeel::IntID::TreeOpenDelay, OpenCallback,
   2311                          nsITimer::TYPE_ONE_SHOT,
   2312                          getter_AddRefs(mSlots->mTimer),
   2313                          "nsTreeBodyFrame::OpenCallback"_ns);
   2314            }
   2315          }
   2316        }
   2317 
   2318        // The dataTransfer was initialized by the call to GetDropEffect above.
   2319        bool canDropAtNewLocation = false;
   2320        mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient,
   2321                       aEvent->AsDragEvent()->mDataTransfer,
   2322                       &canDropAtNewLocation);
   2323 
   2324        if (canDropAtNewLocation) {
   2325          // Invalidate row at the new location.
   2326          mSlots->mDropAllowed = canDropAtNewLocation;
   2327          InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
   2328        }
   2329      }
   2330    }
   2331 
   2332    // Indicate that the drop is allowed by preventing the default behaviour.
   2333    if (mSlots->mDropAllowed) {
   2334      *aEventStatus = nsEventStatus_eConsumeNoDefault;
   2335    }
   2336  } else if (aEvent->mMessage == eDrop) {
   2337    // this event was meant for another frame, so ignore it
   2338    if (!mSlots) {
   2339      return NS_OK;
   2340    }
   2341 
   2342    // Tell the view where the drop happened.
   2343 
   2344    // Remove the drop folder and all its parents from the array.
   2345    int32_t parentIndex;
   2346    nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex);
   2347    while (NS_SUCCEEDED(rv) && parentIndex >= 0) {
   2348      mSlots->mArray.RemoveElement(parentIndex);
   2349      rv = mView->GetParentIndex(parentIndex, &parentIndex);
   2350    }
   2351 
   2352    NS_ASSERTION(aEvent->mClass == eDragEventClass, "wrong event type");
   2353    WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
   2354    nsContentUtils::SetDataTransferInEvent(dragEvent);
   2355 
   2356    mView->Drop(mSlots->mDropRow, mSlots->mDropOrient,
   2357                dragEvent->mDataTransfer);
   2358    mSlots->mDropRow = -1;
   2359    mSlots->mDropOrient = -1;
   2360    mSlots->mIsDragging = false;
   2361    *aEventStatus =
   2362        nsEventStatus_eConsumeNoDefault;  // already handled the drop
   2363  } else if (aEvent->mMessage == eDragExit) {
   2364    // this event was meant for another frame, so ignore it
   2365    if (!mSlots) {
   2366      return NS_OK;
   2367    }
   2368 
   2369    // Clear out all our tracking vars.
   2370 
   2371    if (mSlots->mDropAllowed) {
   2372      mSlots->mDropAllowed = false;
   2373      InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
   2374    } else {
   2375      mSlots->mDropAllowed = false;
   2376    }
   2377    mSlots->mIsDragging = false;
   2378    mSlots->mScrollLines = 0;
   2379    // If a drop is occuring, the exit event will fire just before the drop
   2380    // event, so don't reset mDropRow or mDropOrient as these fields are used
   2381    // by the drop event.
   2382    if (mSlots->mTimer) {
   2383      mSlots->mTimer->Cancel();
   2384      mSlots->mTimer = nullptr;
   2385    }
   2386 
   2387    if (!mSlots->mArray.IsEmpty()) {
   2388      // Close all spring loaded folders except the drop folder.
   2389      CreateTimer(LookAndFeel::IntID::TreeCloseDelay, CloseCallback,
   2390                  nsITimer::TYPE_ONE_SHOT, getter_AddRefs(mSlots->mTimer),
   2391                  "nsTreeBodyFrame::CloseCallback"_ns);
   2392    }
   2393  }
   2394 
   2395  return NS_OK;
   2396 }
   2397 
   2398 namespace mozilla {
   2399 
   2400 class nsDisplayTreeBody final : public nsPaintedDisplayItem {
   2401 public:
   2402  nsDisplayTreeBody(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
   2403      : nsPaintedDisplayItem(aBuilder, aFrame) {
   2404    MOZ_COUNT_CTOR(nsDisplayTreeBody);
   2405  }
   2406 
   2407  MOZ_COUNTED_DTOR_FINAL(nsDisplayTreeBody)
   2408 
   2409  nsDisplayItemGeometry* AllocateGeometry(
   2410      nsDisplayListBuilder* aBuilder) override {
   2411    return new nsDisplayTreeBodyGeometry(this, aBuilder, IsWindowActive());
   2412  }
   2413 
   2414  void Destroy(nsDisplayListBuilder* aBuilder) override {
   2415    aBuilder->UnregisterThemeGeometry(this);
   2416    nsPaintedDisplayItem::Destroy(aBuilder);
   2417  }
   2418 
   2419  bool IsWindowActive() const {
   2420    return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
   2421  }
   2422 
   2423  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
   2424                                 const nsDisplayItemGeometry* aGeometry,
   2425                                 nsRegion* aInvalidRegion) const override {
   2426    auto geometry = static_cast<const nsDisplayTreeBodyGeometry*>(aGeometry);
   2427 
   2428    if (IsWindowActive() != geometry->mWindowIsActive) {
   2429      bool snap;
   2430      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   2431    }
   2432 
   2433    nsPaintedDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry,
   2434                                                    aInvalidRegion);
   2435  }
   2436 
   2437  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
   2438    MOZ_ASSERT(aBuilder);
   2439    (void)static_cast<nsTreeBodyFrame*>(mFrame)->PaintTreeBody(
   2440        *aCtx, GetPaintRect(aBuilder, aCtx), ToReferenceFrame(), aBuilder);
   2441  }
   2442 
   2443  NS_DISPLAY_DECL_NAME("XULTreeBody", TYPE_XUL_TREE_BODY)
   2444 
   2445  nsRect GetComponentAlphaBounds(
   2446      nsDisplayListBuilder* aBuilder) const override {
   2447    bool snap;
   2448    return GetBounds(aBuilder, &snap);
   2449  }
   2450 };
   2451 
   2452 }  // namespace mozilla
   2453 
   2454 // Painting routines
   2455 void nsTreeBodyFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
   2456                                       const nsDisplayListSet& aLists) {
   2457  // REVIEW: why did we paint if we were collapsed? that makes no sense!
   2458  if (!IsVisibleForPainting()) {
   2459    return;  // We're invisible.  Don't paint.
   2460  }
   2461 
   2462  // Handles painting our background, border, and outline.
   2463  SimpleXULLeafFrame::BuildDisplayList(aBuilder, aLists);
   2464 
   2465  // Bail out now if there's no view or we can't run script because the
   2466  // document is a zombie
   2467  if (!mView || !GetContent()->GetComposedDoc()->GetWindow()) {
   2468    return;
   2469  }
   2470 
   2471  nsDisplayItem* item = MakeDisplayItem<nsDisplayTreeBody>(aBuilder, this);
   2472  aLists.Content()->AppendToTop(item);
   2473 }
   2474 
   2475 ImgDrawResult nsTreeBodyFrame::PaintTreeBody(gfxContext& aRenderingContext,
   2476                                             const nsRect& aDirtyRect,
   2477                                             nsPoint aPt,
   2478                                             nsDisplayListBuilder* aBuilder) {
   2479  // Update our available height and our page count.
   2480  CalcInnerBox();
   2481 
   2482  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   2483 
   2484  aRenderingContext.Save();
   2485  aRenderingContext.Clip(NSRectToSnappedRect(
   2486      mInnerBox + aPt, PresContext()->AppUnitsPerDevPixel(), *drawTarget));
   2487  int32_t oldPageCount = mPageLength;
   2488  if (!mHasFixedRowCount) {
   2489    mPageLength =
   2490        (mRowHeight > 0) ? (mInnerBox.height / mRowHeight) : mRowCount;
   2491  }
   2492 
   2493  if (oldPageCount != mPageLength || mHorzWidth != mRect.width) {
   2494    // Schedule a ResizeReflow that will update our info properly.
   2495    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
   2496                                  NS_FRAME_IS_DIRTY);
   2497  }
   2498 #ifdef DEBUG
   2499  int32_t rowCount = mRowCount;
   2500  mView->GetRowCount(&rowCount);
   2501  NS_WARNING_ASSERTION(mRowCount == rowCount, "row count changed unexpectedly");
   2502 #endif
   2503 
   2504  ImgDrawResult result = ImgDrawResult::SUCCESS;
   2505 
   2506  // Loop through our columns and paint them (e.g., for sorting).  This is only
   2507  // relevant when painting backgrounds, since columns contain no content.
   2508  // Content is contained in the rows.
   2509  for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
   2510       currCol = currCol->GetNext()) {
   2511    nsRect colRect;
   2512    nsresult rv =
   2513        currCol->GetRect(this, mInnerBox.y, mInnerBox.height, &colRect);
   2514    // Don't paint hidden columns.
   2515    if (NS_FAILED(rv) || colRect.width == 0) {
   2516      continue;
   2517    }
   2518 
   2519    if (OffsetForHorzScroll(colRect, false)) {
   2520      nsRect dirtyRect;
   2521      colRect += aPt;
   2522      if (dirtyRect.IntersectRect(aDirtyRect, colRect)) {
   2523        result &= PaintColumn(currCol, colRect, PresContext(),
   2524                              aRenderingContext, aDirtyRect);
   2525      }
   2526    }
   2527  }
   2528  // Loop through our on-screen rows.
   2529  for (int32_t i = mTopRowIndex;
   2530       i < mRowCount && i <= mTopRowIndex + mPageLength; i++) {
   2531    nsRect rowRect(mInnerBox.x, mInnerBox.y + mRowHeight * (i - mTopRowIndex),
   2532                   mInnerBox.width, mRowHeight);
   2533    nsRect dirtyRect;
   2534    if (dirtyRect.IntersectRect(aDirtyRect, rowRect + aPt) &&
   2535        rowRect.y < (mInnerBox.y + mInnerBox.height)) {
   2536      result &= PaintRow(i, rowRect + aPt, PresContext(), aRenderingContext,
   2537                         aDirtyRect, aPt, aBuilder);
   2538    }
   2539  }
   2540 
   2541  if (mSlots && mSlots->mDropAllowed &&
   2542      (mSlots->mDropOrient == nsITreeView::DROP_BEFORE ||
   2543       mSlots->mDropOrient == nsITreeView::DROP_AFTER)) {
   2544    nscoord yPos = mInnerBox.y +
   2545                   mRowHeight * (mSlots->mDropRow - mTopRowIndex) -
   2546                   mRowHeight / 2;
   2547    nsRect feedbackRect(mInnerBox.x, yPos, mInnerBox.width, mRowHeight);
   2548    if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) {
   2549      feedbackRect.y += mRowHeight;
   2550    }
   2551 
   2552    nsRect dirtyRect;
   2553    feedbackRect += aPt;
   2554    if (dirtyRect.IntersectRect(aDirtyRect, feedbackRect)) {
   2555      result &= PaintDropFeedback(feedbackRect, PresContext(),
   2556                                  aRenderingContext, aDirtyRect, aPt);
   2557    }
   2558  }
   2559  aRenderingContext.Restore();
   2560 
   2561  return result;
   2562 }
   2563 
   2564 ImgDrawResult nsTreeBodyFrame::PaintColumn(nsTreeColumn* aColumn,
   2565                                           const nsRect& aColumnRect,
   2566                                           nsPresContext* aPresContext,
   2567                                           gfxContext& aRenderingContext,
   2568                                           const nsRect& aDirtyRect) {
   2569  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   2570 
   2571  // Now obtain the properties for our cell.
   2572  PrefillPropertyArray(-1, aColumn);
   2573  nsAutoString properties;
   2574 
   2575  nsCOMPtr<nsITreeView> view = GetExistingView();
   2576  view->GetColumnProperties(aColumn, properties);
   2577  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
   2578 
   2579  // Resolve style for the column.  It contains all the info we need to lay
   2580  // ourselves out and to paint.
   2581  ComputedStyle* colContext =
   2582      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeColumn());
   2583 
   2584  // Obtain the margins for the cell and then deflate our rect by that
   2585  // amount.  The cell is assumed to be contained within the deflated rect.
   2586  nsRect colRect(aColumnRect);
   2587  nsMargin colMargin;
   2588  colContext->StyleMargin()->GetMargin(colMargin);
   2589  colRect.Deflate(colMargin);
   2590 
   2591  return PaintBackgroundLayer(colContext, aPresContext, aRenderingContext,
   2592                              colRect, aDirtyRect);
   2593 }
   2594 
   2595 ImgDrawResult nsTreeBodyFrame::PaintRow(int32_t aRowIndex,
   2596                                        const nsRect& aRowRect,
   2597                                        nsPresContext* aPresContext,
   2598                                        gfxContext& aRenderingContext,
   2599                                        const nsRect& aDirtyRect, nsPoint aPt,
   2600                                        nsDisplayListBuilder* aBuilder) {
   2601  // We have been given a rect for our row.  We treat this row like a full-blown
   2602  // frame, meaning that it can have borders, margins, padding, and a
   2603  // background.
   2604 
   2605  // Without a view, we have no data. Check for this up front.
   2606  nsCOMPtr<nsITreeView> view = GetExistingView();
   2607  if (!view) {
   2608    return ImgDrawResult::SUCCESS;
   2609  }
   2610 
   2611  nsresult rv;
   2612 
   2613  // Now obtain the properties for our row.
   2614  // XXX Automatically fill in the following props: open, closed, container,
   2615  // leaf, selected, focused
   2616  PrefillPropertyArray(aRowIndex, nullptr);
   2617 
   2618  nsAutoString properties;
   2619  view->GetRowProperties(aRowIndex, properties);
   2620  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
   2621 
   2622  // Resolve style for the row.  It contains all the info we need to lay
   2623  // ourselves out and to paint.
   2624  ComputedStyle* rowContext =
   2625      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeRow());
   2626 
   2627  // Obtain the margins for the row and then deflate our rect by that
   2628  // amount.  The row is assumed to be contained within the deflated rect.
   2629  nsRect rowRect(aRowRect);
   2630  nsMargin rowMargin;
   2631  rowContext->StyleMargin()->GetMargin(rowMargin);
   2632  rowRect.Deflate(rowMargin);
   2633 
   2634  ImgDrawResult result = ImgDrawResult::SUCCESS;
   2635 
   2636  // Paint our borders and background for our row rect.
   2637  result &= PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext,
   2638                                 rowRect, aDirtyRect);
   2639 
   2640  // Adjust the rect for its border and padding.
   2641  nsRect originalRowRect = rowRect;
   2642  AdjustForBorderPadding(rowContext, rowRect);
   2643 
   2644  bool isSeparator = false;
   2645  view->IsSeparator(aRowIndex, &isSeparator);
   2646  if (isSeparator) {
   2647    // The row is a separator.
   2648 
   2649    nscoord primaryX = rowRect.x;
   2650    nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
   2651    if (primaryCol) {
   2652      // Paint the primary cell.
   2653      nsRect cellRect;
   2654      rv = primaryCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
   2655      if (NS_FAILED(rv)) {
   2656        MOZ_ASSERT_UNREACHABLE("primary column is invalid");
   2657        return result;
   2658      }
   2659 
   2660      if (OffsetForHorzScroll(cellRect, false)) {
   2661        cellRect.x += aPt.x;
   2662        nsRect dirtyRect;
   2663        nsRect checkRect(cellRect.x, originalRowRect.y, cellRect.width,
   2664                         originalRowRect.height);
   2665        if (dirtyRect.IntersectRect(aDirtyRect, checkRect)) {
   2666          result &=
   2667              PaintCell(aRowIndex, primaryCol, cellRect, aPresContext,
   2668                        aRenderingContext, aDirtyRect, primaryX, aPt, aBuilder);
   2669        }
   2670      }
   2671 
   2672      // Paint the left side of the separator.
   2673      nscoord currX;
   2674      nsTreeColumn* previousCol = primaryCol->GetPrevious();
   2675      if (previousCol) {
   2676        nsRect prevColRect;
   2677        rv = previousCol->GetRect(this, 0, 0, &prevColRect);
   2678        if (NS_SUCCEEDED(rv)) {
   2679          currX = (prevColRect.x - mHorzPosition) + prevColRect.width + aPt.x;
   2680        } else {
   2681          MOZ_ASSERT_UNREACHABLE(
   2682              "The column before the primary column is "
   2683              "invalid");
   2684          currX = rowRect.x;
   2685        }
   2686      } else {
   2687        currX = rowRect.x;
   2688      }
   2689 
   2690      int32_t level;
   2691      view->GetLevel(aRowIndex, &level);
   2692      if (level == 0) {
   2693        currX += mIndentation;
   2694      }
   2695 
   2696      if (currX > rowRect.x) {
   2697        nsRect separatorRect(rowRect);
   2698        separatorRect.width -= rowRect.x + rowRect.width - currX;
   2699        result &= PaintSeparator(aRowIndex, separatorRect, aPresContext,
   2700                                 aRenderingContext, aDirtyRect);
   2701      }
   2702    }
   2703 
   2704    // Paint the right side (whole) separator.
   2705    nsRect separatorRect(rowRect);
   2706    if (primaryX > rowRect.x) {
   2707      separatorRect.width -= primaryX - rowRect.x;
   2708      separatorRect.x += primaryX - rowRect.x;
   2709    }
   2710    result &= PaintSeparator(aRowIndex, separatorRect, aPresContext,
   2711                             aRenderingContext, aDirtyRect);
   2712  } else {
   2713    // Now loop over our cells. Only paint a cell if it intersects with our
   2714    // dirty rect.
   2715    for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
   2716         currCol = currCol->GetNext()) {
   2717      nsRect cellRect;
   2718      rv = currCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
   2719      // Don't paint cells in hidden columns.
   2720      if (NS_FAILED(rv) || cellRect.width == 0) {
   2721        continue;
   2722      }
   2723 
   2724      if (OffsetForHorzScroll(cellRect, false)) {
   2725        cellRect.x += aPt.x;
   2726 
   2727        // for primary columns, use the row's vertical size so that the
   2728        // lines get drawn properly
   2729        nsRect checkRect = cellRect;
   2730        if (currCol->IsPrimary()) {
   2731          checkRect = nsRect(cellRect.x, originalRowRect.y, cellRect.width,
   2732                             originalRowRect.height);
   2733        }
   2734 
   2735        nsRect dirtyRect;
   2736        nscoord dummy;
   2737        if (dirtyRect.IntersectRect(aDirtyRect, checkRect)) {
   2738          result &=
   2739              PaintCell(aRowIndex, currCol, cellRect, aPresContext,
   2740                        aRenderingContext, aDirtyRect, dummy, aPt, aBuilder);
   2741        }
   2742      }
   2743    }
   2744  }
   2745 
   2746  return result;
   2747 }
   2748 
   2749 ImgDrawResult nsTreeBodyFrame::PaintSeparator(int32_t aRowIndex,
   2750                                              const nsRect& aSeparatorRect,
   2751                                              nsPresContext* aPresContext,
   2752                                              gfxContext& aRenderingContext,
   2753                                              const nsRect& aDirtyRect) {
   2754  // Resolve style for the separator.
   2755  ComputedStyle* separatorContext =
   2756      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeSeparator());
   2757 
   2758  const nsStylePosition* stylePosition = separatorContext->StylePosition();
   2759  const AnchorPosResolutionParams anchorResolutionParams{
   2760      this, separatorContext->StyleDisplay()->mPosition};
   2761 
   2762  // Obtain the height for the separator or use the default value.
   2763  nscoord height;
   2764  const auto styleHeight = stylePosition->GetHeight(anchorResolutionParams);
   2765  if (styleHeight->ConvertsToLength()) {
   2766    height = styleHeight->ToLength();
   2767  } else {
   2768    // Use default height 2px.
   2769    height = nsPresContext::CSSPixelsToAppUnits(2);
   2770  }
   2771 
   2772  // Obtain the margins for the separator and then deflate our rect by that
   2773  // amount. The separator is assumed to be contained within the deflated
   2774  // rect.
   2775  nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width,
   2776                       height);
   2777  nsMargin separatorMargin;
   2778  separatorContext->StyleMargin()->GetMargin(separatorMargin);
   2779  separatorRect.Deflate(separatorMargin);
   2780 
   2781  // Center the separator.
   2782  separatorRect.y += (aSeparatorRect.height - height) / 2;
   2783 
   2784  return PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext,
   2785                              separatorRect, aDirtyRect);
   2786 }
   2787 
   2788 ImgDrawResult nsTreeBodyFrame::PaintCell(
   2789    int32_t aRowIndex, nsTreeColumn* aColumn, const nsRect& aCellRect,
   2790    nsPresContext* aPresContext, gfxContext& aRenderingContext,
   2791    const nsRect& aDirtyRect, nscoord& aCurrX, nsPoint aPt,
   2792    nsDisplayListBuilder* aBuilder) {
   2793  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   2794 
   2795  // Now obtain the properties for our cell.
   2796  // XXX Automatically fill in the following props: open, closed, container,
   2797  // leaf, selected, focused, and the col ID.
   2798  PrefillPropertyArray(aRowIndex, aColumn);
   2799  nsAutoString properties;
   2800  nsCOMPtr<nsITreeView> view = GetExistingView();
   2801  view->GetCellProperties(aRowIndex, aColumn, properties);
   2802  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
   2803 
   2804  // Resolve style for the cell.  It contains all the info we need to lay
   2805  // ourselves out and to paint.
   2806  ComputedStyle* cellContext =
   2807      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCell());
   2808 
   2809  bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
   2810 
   2811  // Obtain the margins for the cell and then deflate our rect by that
   2812  // amount.  The cell is assumed to be contained within the deflated rect.
   2813  nsRect cellRect(aCellRect);
   2814  nsMargin cellMargin;
   2815  cellContext->StyleMargin()->GetMargin(cellMargin);
   2816  cellRect.Deflate(cellMargin);
   2817 
   2818  // Paint our borders and background for our row rect.
   2819  ImgDrawResult result = PaintBackgroundLayer(
   2820      cellContext, aPresContext, aRenderingContext, cellRect, aDirtyRect);
   2821 
   2822  // Adjust the rect for its border and padding.
   2823  AdjustForBorderPadding(cellContext, cellRect);
   2824 
   2825  nscoord currX = cellRect.x;
   2826  nscoord remainingWidth = cellRect.width;
   2827 
   2828  // Now we paint the contents of the cells.
   2829  // Directionality of the tree determines the order in which we paint.
   2830  // StyleDirection::Ltr means paint from left to right.
   2831  // StyleDirection::Rtl means paint from right to left.
   2832 
   2833  if (aColumn->IsPrimary()) {
   2834    // If we're the primary column, we need to indent and paint the twisty and
   2835    // any connecting lines between siblings.
   2836 
   2837    int32_t level;
   2838    view->GetLevel(aRowIndex, &level);
   2839 
   2840    if (!isRTL) {
   2841      currX += mIndentation * level;
   2842    }
   2843    remainingWidth -= mIndentation * level;
   2844 
   2845    // Resolve the style to use for the connecting lines.
   2846    ComputedStyle* lineContext =
   2847        GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeLine());
   2848 
   2849    if (mIndentation && level &&
   2850        lineContext->StyleVisibility()->IsVisibleOrCollapsed()) {
   2851      // Paint the thread lines.
   2852 
   2853      // Get the size of the twisty. We don't want to paint the twisty
   2854      // before painting of connecting lines since it would paint lines over
   2855      // the twisty. But we need to leave a place for it.
   2856      ComputedStyle* twistyContext =
   2857          GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
   2858 
   2859      nsRect imageSize;
   2860      nsRect twistyRect(aCellRect);
   2861      GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext,
   2862                    twistyContext);
   2863 
   2864      nsMargin twistyMargin;
   2865      twistyContext->StyleMargin()->GetMargin(twistyMargin);
   2866      twistyRect.Inflate(twistyMargin);
   2867 
   2868      const nsStyleBorder* borderStyle = lineContext->StyleBorder();
   2869      // Resolve currentcolor values against the treeline context
   2870      nscolor color = borderStyle->mBorderLeftColor.CalcColor(*lineContext);
   2871      ColorPattern colorPatt(ToDeviceColor(color));
   2872 
   2873      StyleBorderStyle style = borderStyle->GetBorderStyle(eSideLeft);
   2874      StrokeOptions strokeOptions;
   2875      nsLayoutUtils::InitDashPattern(strokeOptions, style);
   2876 
   2877      nscoord srcX = currX + twistyRect.width - mIndentation / 2;
   2878      nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y;
   2879 
   2880      DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   2881      nsPresContext* pc = PresContext();
   2882 
   2883      // Don't paint off our cell.
   2884      if (srcX <= cellRect.x + cellRect.width) {
   2885        nscoord destX = currX + twistyRect.width;
   2886        if (destX > cellRect.x + cellRect.width) {
   2887          destX = cellRect.x + cellRect.width;
   2888        }
   2889        if (isRTL) {
   2890          srcX = currX + remainingWidth - (srcX - cellRect.x);
   2891          destX = currX + remainingWidth - (destX - cellRect.x);
   2892        }
   2893        Point p1(pc->AppUnitsToGfxUnits(srcX),
   2894                 pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2));
   2895        Point p2(pc->AppUnitsToGfxUnits(destX),
   2896                 pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2));
   2897        SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget,
   2898                                          strokeOptions.mLineWidth);
   2899        drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions);
   2900      }
   2901 
   2902      int32_t currentParent = aRowIndex;
   2903      for (int32_t i = level; i > 0; i--) {
   2904        if (srcX <= cellRect.x + cellRect.width) {
   2905          // Paint full vertical line only if we have next sibling.
   2906          bool hasNextSibling;
   2907          view->HasNextSibling(currentParent, aRowIndex, &hasNextSibling);
   2908          if (hasNextSibling || i == level) {
   2909            Point p1(pc->AppUnitsToGfxUnits(srcX),
   2910                     pc->AppUnitsToGfxUnits(lineY));
   2911            Point p2;
   2912            p2.x = pc->AppUnitsToGfxUnits(srcX);
   2913 
   2914            if (hasNextSibling) {
   2915              p2.y = pc->AppUnitsToGfxUnits(lineY + mRowHeight);
   2916            } else if (i == level) {
   2917              p2.y = pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2);
   2918            }
   2919 
   2920            SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget,
   2921                                              strokeOptions.mLineWidth);
   2922            drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions);
   2923          }
   2924        }
   2925 
   2926        int32_t parent;
   2927        if (NS_FAILED(view->GetParentIndex(currentParent, &parent)) ||
   2928            parent < 0) {
   2929          break;
   2930        }
   2931        currentParent = parent;
   2932        srcX -= mIndentation;
   2933      }
   2934    }
   2935 
   2936    // Always leave space for the twisty.
   2937    nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
   2938    result &= PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext,
   2939                          aRenderingContext, aDirtyRect, remainingWidth, currX);
   2940  }
   2941 
   2942  // Now paint the icon for our cell.
   2943  nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
   2944  nsRect dirtyRect;
   2945  if (dirtyRect.IntersectRect(aDirtyRect, iconRect)) {
   2946    result &= PaintImage(aRowIndex, aColumn, iconRect, aPresContext,
   2947                         aRenderingContext, aDirtyRect, remainingWidth, currX,
   2948                         aBuilder);
   2949  }
   2950 
   2951  // Now paint our element, but only if we aren't a cycler column.
   2952  // XXX until we have the ability to load images, allow the view to
   2953  // insert text into cycler columns...
   2954  if (!aColumn->IsCycler()) {
   2955    nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height);
   2956    nsRect dirtyRect;
   2957    if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) {
   2958      switch (aColumn->GetType()) {
   2959        case TreeColumn_Binding::TYPE_TEXT:
   2960          result &= PaintText(aRowIndex, aColumn, elementRect, aPresContext,
   2961                              aRenderingContext, aDirtyRect, currX);
   2962          break;
   2963        case TreeColumn_Binding::TYPE_CHECKBOX:
   2964          result &= PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext,
   2965                                  aRenderingContext, aDirtyRect);
   2966          break;
   2967      }
   2968    }
   2969  }
   2970 
   2971  aCurrX = currX;
   2972 
   2973  return result;
   2974 }
   2975 
   2976 ImgDrawResult nsTreeBodyFrame::PaintTwisty(
   2977    int32_t aRowIndex, nsTreeColumn* aColumn, const nsRect& aTwistyRect,
   2978    nsPresContext* aPresContext, gfxContext& aRenderingContext,
   2979    const nsRect& aDirtyRect, nscoord& aRemainingWidth, nscoord& aCurrX) {
   2980  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   2981 
   2982  bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
   2983  nscoord rightEdge = aCurrX + aRemainingWidth;
   2984  // Paint the twisty, but only if we are a non-empty container.
   2985  bool shouldPaint = false;
   2986  bool isContainer = false;
   2987  nsCOMPtr<nsITreeView> view = GetExistingView();
   2988  view->IsContainer(aRowIndex, &isContainer);
   2989  if (isContainer) {
   2990    bool isContainerEmpty = false;
   2991    view->IsContainerEmpty(aRowIndex, &isContainerEmpty);
   2992    if (!isContainerEmpty) {
   2993      shouldPaint = true;
   2994    }
   2995  }
   2996 
   2997  // Resolve style for the twisty.
   2998  ComputedStyle* twistyContext =
   2999      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
   3000 
   3001  // Obtain the margins for the twisty and then deflate our rect by that
   3002  // amount.  The twisty is assumed to be contained within the deflated rect.
   3003  nsRect twistyRect(aTwistyRect);
   3004  nsMargin twistyMargin;
   3005  twistyContext->StyleMargin()->GetMargin(twistyMargin);
   3006  twistyRect.Deflate(twistyMargin);
   3007 
   3008  nsRect imageSize;
   3009  GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext,
   3010                twistyContext);
   3011 
   3012  // Subtract out the remaining width.  This is done even when we don't actually
   3013  // paint a twisty in this cell, so that cells in different rows still line up.
   3014  nsRect copyRect(twistyRect);
   3015  copyRect.Inflate(twistyMargin);
   3016  aRemainingWidth -= copyRect.width;
   3017  if (!isRTL) {
   3018    aCurrX += copyRect.width;
   3019  }
   3020 
   3021  auto result = ImgDrawResult::SUCCESS;
   3022  if (!shouldPaint) {
   3023    return result;
   3024  }
   3025  // Paint our borders and background for our image rect.
   3026  result &= PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext,
   3027                                 twistyRect, aDirtyRect);
   3028 
   3029  // Time to paint the twisty.
   3030  // Adjust the rect for its border and padding.
   3031  nsMargin bp;
   3032  GetBorderPadding(twistyContext, bp);
   3033  twistyRect.Deflate(bp);
   3034  if (isRTL) {
   3035    twistyRect.x = rightEdge - twistyRect.width;
   3036  }
   3037  imageSize.Deflate(bp);
   3038 
   3039  // Get the image for drawing.
   3040  nsCOMPtr<imgIContainer> image =
   3041      GetImage(aRowIndex, aColumn, true, twistyContext);
   3042  if (!image) {
   3043    return result;
   3044  }
   3045  nsPoint anchorPoint = twistyRect.TopLeft();
   3046 
   3047  // Center the image. XXX Obey vertical-align style prop?
   3048  if (imageSize.height < twistyRect.height) {
   3049    anchorPoint.y += (twistyRect.height - imageSize.height) / 2;
   3050  }
   3051 
   3052  // Apply context paint if applicable
   3053  SVGImageContext svgContext;
   3054  SVGImageContext::MaybeStoreContextPaint(svgContext, *aPresContext,
   3055                                          *twistyContext, image);
   3056 
   3057  // Paint the image.
   3058  result &= nsLayoutUtils::DrawSingleUnscaledImage(
   3059      aRenderingContext, aPresContext, image, SamplingFilter::POINT,
   3060      anchorPoint, &aDirtyRect, svgContext, imgIContainer::FLAG_NONE,
   3061      &imageSize);
   3062  return result;
   3063 }
   3064 
   3065 ImgDrawResult nsTreeBodyFrame::PaintImage(
   3066    int32_t aRowIndex, nsTreeColumn* aColumn, const nsRect& aImageRect,
   3067    nsPresContext* aPresContext, gfxContext& aRenderingContext,
   3068    const nsRect& aDirtyRect, nscoord& aRemainingWidth, nscoord& aCurrX,
   3069    nsDisplayListBuilder* aBuilder) {
   3070  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   3071 
   3072  bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
   3073  nscoord rightEdge = aCurrX + aRemainingWidth;
   3074  // Resolve style for the image.
   3075  ComputedStyle* imageContext =
   3076      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeImage());
   3077 
   3078  // Obtain the margins for the image and then deflate our rect by that
   3079  // amount.  The image is assumed to be contained within the deflated rect.
   3080  nsRect imageRect(aImageRect);
   3081  nsMargin imageMargin;
   3082  imageContext->StyleMargin()->GetMargin(imageMargin);
   3083  imageRect.Deflate(imageMargin);
   3084 
   3085  // Get the image.
   3086  nsCOMPtr<imgIContainer> image =
   3087      GetImage(aRowIndex, aColumn, false, imageContext);
   3088 
   3089  // Get the image destination size.
   3090  nsSize imageDestSize = GetImageDestSize(imageContext, image);
   3091  if (!imageDestSize.width || !imageDestSize.height) {
   3092    return ImgDrawResult::SUCCESS;
   3093  }
   3094 
   3095  // Get the borders and padding.
   3096  nsMargin bp(0, 0, 0, 0);
   3097  GetBorderPadding(imageContext, bp);
   3098 
   3099  // destRect will be passed as the aDestRect argument in the DrawImage method.
   3100  // Start with the imageDestSize width and height.
   3101  nsRect destRect(0, 0, imageDestSize.width, imageDestSize.height);
   3102  // Inflate destRect for borders and padding so that we can compare/adjust
   3103  // with respect to imageRect.
   3104  destRect.Inflate(bp);
   3105 
   3106  // The destRect width and height have not been adjusted to fit within the
   3107  // cell width and height.
   3108  // We must adjust the width even if image is null, because the width is used
   3109  // to update the aRemainingWidth and aCurrX values.
   3110  // Since the height isn't used unless the image is not null, we will adjust
   3111  // the height inside the if (image) block below.
   3112 
   3113  if (destRect.width > imageRect.width) {
   3114    // The destRect is too wide to fit within the cell width.
   3115    // Adjust destRect width to fit within the cell width.
   3116    destRect.width = imageRect.width;
   3117  } else {
   3118    // The cell is wider than the destRect.
   3119    // In a cycler column, the image is centered horizontally.
   3120    if (!aColumn->IsCycler()) {
   3121      // If this column is not a cycler, we won't center the image horizontally.
   3122      // We adjust the imageRect width so that the image is placed at the start
   3123      // of the cell.
   3124      imageRect.width = destRect.width;
   3125    }
   3126  }
   3127 
   3128  ImgDrawResult result = ImgDrawResult::SUCCESS;
   3129 
   3130  if (image) {
   3131    if (isRTL) {
   3132      imageRect.x = rightEdge - imageRect.width;
   3133    }
   3134    // Paint our borders and background for our image rect
   3135    result &= PaintBackgroundLayer(imageContext, aPresContext,
   3136                                   aRenderingContext, imageRect, aDirtyRect);
   3137 
   3138    // The destRect x and y have not been set yet. Let's do that now.
   3139    // Initially, we use the imageRect x and y.
   3140    destRect.x = imageRect.x;
   3141    destRect.y = imageRect.y;
   3142 
   3143    if (destRect.width < imageRect.width) {
   3144      // The destRect width is smaller than the cell width.
   3145      // Center the image horizontally in the cell.
   3146      // Adjust the destRect x accordingly.
   3147      destRect.x += (imageRect.width - destRect.width) / 2;
   3148    }
   3149 
   3150    // Now it's time to adjust the destRect height to fit within the cell
   3151    // height.
   3152    if (destRect.height > imageRect.height) {
   3153      // The destRect height is larger than the cell height.
   3154      // Adjust destRect height to fit within the cell height.
   3155      destRect.height = imageRect.height;
   3156    } else if (destRect.height < imageRect.height) {
   3157      // The destRect height is smaller than the cell height.
   3158      // Center the image vertically in the cell.
   3159      // Adjust the destRect y accordingly.
   3160      destRect.y += (imageRect.height - destRect.height) / 2;
   3161    }
   3162 
   3163    // It's almost time to paint the image.
   3164    // Deflate destRect for the border and padding.
   3165    destRect.Deflate(bp);
   3166 
   3167    // Compute the area where our whole image would be mapped, to get the
   3168    // desired subregion onto our actual destRect:
   3169    nsRect wholeImageDest;
   3170    CSSIntSize rawImageCSSIntSize;
   3171    if (NS_SUCCEEDED(image->GetWidth(&rawImageCSSIntSize.width)) &&
   3172        NS_SUCCEEDED(image->GetHeight(&rawImageCSSIntSize.height))) {
   3173      // Get the image source rectangle - the rectangle containing the part of
   3174      // the image that we are going to display.  sourceRect will be passed as
   3175      // the aSrcRect argument in the DrawImage method.
   3176      nsRect sourceRect = GetImageSourceRect(imageContext, image);
   3177 
   3178      // Let's say that the image is 100 pixels tall and that the CSS has
   3179      // specified that the destination height should be 50 pixels tall. Let's
   3180      // say that the cell height is only 20 pixels. So, in those 20 visible
   3181      // pixels, we want to see the top 20/50ths of the image.  So, the
   3182      // sourceRect.height should be 100 * 20 / 50, which is 40 pixels.
   3183      // Essentially, we are scaling the image as dictated by the CSS
   3184      // destination height and width, and we are then clipping the scaled
   3185      // image by the cell width and height.
   3186      nsSize rawImageSize(CSSPixel::ToAppUnits(rawImageCSSIntSize));
   3187      wholeImageDest = nsLayoutUtils::GetWholeImageDestination(
   3188          rawImageSize, sourceRect, nsRect(destRect.TopLeft(), imageDestSize));
   3189    } else {
   3190      // GetWidth/GetHeight failed, so we can't easily map a subregion of the
   3191      // source image onto the destination area.
   3192      // * If this happens with a RasterImage, it probably means the image is
   3193      // in an error state, and we shouldn't draw anything. Hence, we leave
   3194      // wholeImageDest as an empty rect (its initial state).
   3195      // * If this happens with a VectorImage, it probably means the image has
   3196      // no explicit width or height attribute -- but we can still proceed and
   3197      // just treat the destination area as our whole SVG image area. Hence, we
   3198      // set wholeImageDest to the full destRect.
   3199      if (image->GetType() == imgIContainer::TYPE_VECTOR) {
   3200        wholeImageDest = destRect;
   3201      }
   3202    }
   3203 
   3204    const auto* styleEffects = imageContext->StyleEffects();
   3205    gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&aRenderingContext);
   3206    if (!styleEffects->IsOpaque()) {
   3207      autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
   3208                                              styleEffects->mOpacity);
   3209    }
   3210 
   3211    uint32_t drawFlags = aBuilder && aBuilder->UseHighQualityScaling()
   3212                             ? imgIContainer::FLAG_HIGH_QUALITY_SCALING
   3213                             : imgIContainer::FLAG_NONE;
   3214    result &= nsLayoutUtils::DrawImage(
   3215        aRenderingContext, imageContext, aPresContext, image,
   3216        nsLayoutUtils::GetSamplingFilterForFrame(this), wholeImageDest,
   3217        destRect, destRect.TopLeft(), aDirtyRect, drawFlags);
   3218  }
   3219 
   3220  // Update the aRemainingWidth and aCurrX values.
   3221  imageRect.Inflate(imageMargin);
   3222  aRemainingWidth -= imageRect.width;
   3223  if (!isRTL) {
   3224    aCurrX += imageRect.width;
   3225  }
   3226 
   3227  return result;
   3228 }
   3229 
   3230 ImgDrawResult nsTreeBodyFrame::PaintText(
   3231    int32_t aRowIndex, nsTreeColumn* aColumn, const nsRect& aTextRect,
   3232    nsPresContext* aPresContext, gfxContext& aRenderingContext,
   3233    const nsRect& aDirtyRect, nscoord& aCurrX) {
   3234  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   3235 
   3236  bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
   3237 
   3238  // Now obtain the text for our cell.
   3239  nsAutoString text;
   3240  nsCOMPtr<nsITreeView> view = GetExistingView();
   3241  view->GetCellText(aRowIndex, aColumn, text);
   3242 
   3243  // We're going to paint this text so we need to ensure bidi is enabled if
   3244  // necessary
   3245  CheckTextForBidi(text);
   3246 
   3247  ImgDrawResult result = ImgDrawResult::SUCCESS;
   3248 
   3249  if (text.Length() == 0) {
   3250    // Don't paint an empty string. XXX What about background/borders? Still
   3251    // paint?
   3252    return result;
   3253  }
   3254 
   3255  int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
   3256  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   3257 
   3258  // Resolve style for the text.  It contains all the info we need to lay
   3259  // ourselves out and to paint.
   3260  ComputedStyle* textContext =
   3261      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCellText());
   3262 
   3263  // Obtain the margins for the text and then deflate our rect by that
   3264  // amount.  The text is assumed to be contained within the deflated rect.
   3265  nsRect textRect(aTextRect);
   3266  nsMargin textMargin;
   3267  textContext->StyleMargin()->GetMargin(textMargin);
   3268  textRect.Deflate(textMargin);
   3269 
   3270  // Adjust the rect for its border and padding.
   3271  nsMargin bp(0, 0, 0, 0);
   3272  GetBorderPadding(textContext, bp);
   3273  textRect.Deflate(bp);
   3274 
   3275  // Compute our text size.
   3276  RefPtr<nsFontMetrics> fontMet =
   3277      nsLayoutUtils::GetFontMetricsForComputedStyle(textContext, PresContext());
   3278 
   3279  nscoord height = fontMet->MaxHeight();
   3280  nscoord baseline = fontMet->MaxAscent();
   3281 
   3282  // Center the text. XXX Obey vertical-align style prop?
   3283  if (height < textRect.height) {
   3284    textRect.y += (textRect.height - height) / 2;
   3285    textRect.height = height;
   3286  }
   3287 
   3288  // Set our font.
   3289  AdjustForCellText(text, aRowIndex, aColumn, aRenderingContext, *fontMet,
   3290                    textRect);
   3291  textRect.Inflate(bp);
   3292 
   3293  // Subtract out the remaining width.
   3294  if (!isRTL) {
   3295    aCurrX += textRect.width + textMargin.LeftRight();
   3296  }
   3297 
   3298  result &= PaintBackgroundLayer(textContext, aPresContext, aRenderingContext,
   3299                                 textRect, aDirtyRect);
   3300 
   3301  // Time to paint our text.
   3302  textRect.Deflate(bp);
   3303 
   3304  // Set our color.
   3305  ColorPattern color(ToDeviceColor(textContext->StyleText()->mColor));
   3306 
   3307  // Draw decorations.
   3308  StyleTextDecorationLine decorations =
   3309      textContext->StyleTextReset()->mTextDecorationLine;
   3310 
   3311  nscoord offset;
   3312  nscoord size;
   3313  if (decorations & (StyleTextDecorationLine::OVERLINE |
   3314                     StyleTextDecorationLine::UNDERLINE)) {
   3315    fontMet->GetUnderline(offset, size);
   3316    if (decorations & StyleTextDecorationLine::OVERLINE) {
   3317      nsRect r(textRect.x, textRect.y, textRect.width, size);
   3318      Rect devPxRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
   3319      drawTarget->FillRect(devPxRect, color);
   3320    }
   3321    if (decorations & StyleTextDecorationLine::UNDERLINE) {
   3322      nsRect r(textRect.x, textRect.y + baseline - offset, textRect.width,
   3323               size);
   3324      Rect devPxRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
   3325      drawTarget->FillRect(devPxRect, color);
   3326    }
   3327  }
   3328  if (decorations & StyleTextDecorationLine::LINE_THROUGH) {
   3329    fontMet->GetStrikeout(offset, size);
   3330    nsRect r(textRect.x, textRect.y + baseline - offset, textRect.width, size);
   3331    Rect devPxRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
   3332    drawTarget->FillRect(devPxRect, color);
   3333  }
   3334  ComputedStyle* cellContext =
   3335      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCell());
   3336 
   3337  const auto* styleEffects = textContext->StyleEffects();
   3338  gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&aRenderingContext);
   3339  if (!styleEffects->IsOpaque()) {
   3340    autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
   3341                                            styleEffects->mOpacity);
   3342  }
   3343 
   3344  aRenderingContext.SetColor(
   3345      sRGBColor::FromABGR(textContext->StyleText()->mColor.ToColor()));
   3346  nsLayoutUtils::DrawString(
   3347      this, *fontMet, &aRenderingContext, text.get(), text.Length(),
   3348      textRect.TopLeft() + nsPoint(0, baseline), cellContext);
   3349 
   3350  return result;
   3351 }
   3352 
   3353 ImgDrawResult nsTreeBodyFrame::PaintCheckbox(int32_t aRowIndex,
   3354                                             nsTreeColumn* aColumn,
   3355                                             const nsRect& aCheckboxRect,
   3356                                             nsPresContext* aPresContext,
   3357                                             gfxContext& aRenderingContext,
   3358                                             const nsRect& aDirtyRect) {
   3359  MOZ_ASSERT(aColumn && aColumn->GetFrame(), "invalid column passed");
   3360 
   3361  // Resolve style for the checkbox.
   3362  ComputedStyle* checkboxContext =
   3363      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCheckbox());
   3364 
   3365  nscoord rightEdge = aCheckboxRect.XMost();
   3366 
   3367  // Obtain the margins for the checkbox and then deflate our rect by that
   3368  // amount.  The checkbox is assumed to be contained within the deflated rect.
   3369  nsRect checkboxRect(aCheckboxRect);
   3370  nsMargin checkboxMargin;
   3371  checkboxContext->StyleMargin()->GetMargin(checkboxMargin);
   3372  checkboxRect.Deflate(checkboxMargin);
   3373 
   3374  nsRect imageSize = GetImageSize(aRowIndex, aColumn, true, checkboxContext);
   3375 
   3376  if (imageSize.height > checkboxRect.height) {
   3377    imageSize.height = checkboxRect.height;
   3378  }
   3379  if (imageSize.width > checkboxRect.width) {
   3380    imageSize.width = checkboxRect.width;
   3381  }
   3382 
   3383  if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
   3384    checkboxRect.x = rightEdge - checkboxRect.width;
   3385  }
   3386 
   3387  // Paint our borders and background for our image rect.
   3388  ImgDrawResult result =
   3389      PaintBackgroundLayer(checkboxContext, aPresContext, aRenderingContext,
   3390                           checkboxRect, aDirtyRect);
   3391 
   3392  // Time to paint the checkbox.
   3393  // Adjust the rect for its border and padding.
   3394  nsMargin bp(0, 0, 0, 0);
   3395  GetBorderPadding(checkboxContext, bp);
   3396  checkboxRect.Deflate(bp);
   3397 
   3398  // Get the image for drawing.
   3399  nsCOMPtr<imgIContainer> image =
   3400      GetImage(aRowIndex, aColumn, true, checkboxContext);
   3401  if (image) {
   3402    nsPoint pt = checkboxRect.TopLeft();
   3403 
   3404    if (imageSize.height < checkboxRect.height) {
   3405      pt.y += (checkboxRect.height - imageSize.height) / 2;
   3406    }
   3407 
   3408    if (imageSize.width < checkboxRect.width) {
   3409      pt.x += (checkboxRect.width - imageSize.width) / 2;
   3410    }
   3411 
   3412    // Apply context paint if applicable
   3413    SVGImageContext svgContext;
   3414    SVGImageContext::MaybeStoreContextPaint(svgContext, *aPresContext,
   3415                                            *checkboxContext, image);
   3416    // Paint the image.
   3417    result &= nsLayoutUtils::DrawSingleUnscaledImage(
   3418        aRenderingContext, aPresContext, image, SamplingFilter::POINT, pt,
   3419        &aDirtyRect, svgContext, imgIContainer::FLAG_NONE, &imageSize);
   3420  }
   3421 
   3422  return result;
   3423 }
   3424 
   3425 ImgDrawResult nsTreeBodyFrame::PaintDropFeedback(
   3426    const nsRect& aDropFeedbackRect, nsPresContext* aPresContext,
   3427    gfxContext& aRenderingContext, const nsRect& aDirtyRect, nsPoint aPt) {
   3428  // Paint the drop feedback in between rows.
   3429 
   3430  nscoord currX;
   3431 
   3432  // Adjust for the primary cell.
   3433  nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
   3434 
   3435  if (primaryCol) {
   3436 #ifdef DEBUG
   3437    nsresult rv =
   3438 #endif
   3439        primaryCol->GetXInTwips(this, &currX);
   3440    NS_ASSERTION(NS_SUCCEEDED(rv), "primary column is invalid?");
   3441 
   3442    currX += aPt.x - mHorzPosition;
   3443  } else {
   3444    currX = aDropFeedbackRect.x;
   3445  }
   3446 
   3447  PrefillPropertyArray(mSlots->mDropRow, primaryCol);
   3448 
   3449  // Resolve the style to use for the drop feedback.
   3450  ComputedStyle* feedbackContext =
   3451      GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeDropFeedback());
   3452 
   3453  ImgDrawResult result = ImgDrawResult::SUCCESS;
   3454 
   3455  // Paint only if it is visible.
   3456  nsCOMPtr<nsITreeView> view = GetExistingView();
   3457  if (feedbackContext->StyleVisibility()->IsVisibleOrCollapsed()) {
   3458    int32_t level;
   3459    view->GetLevel(mSlots->mDropRow, &level);
   3460 
   3461    // If our previous or next row has greater level use that for
   3462    // correct visual indentation.
   3463    if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) {
   3464      if (mSlots->mDropRow > 0) {
   3465        int32_t previousLevel;
   3466        view->GetLevel(mSlots->mDropRow - 1, &previousLevel);
   3467        if (previousLevel > level) {
   3468          level = previousLevel;
   3469        }
   3470      }
   3471    } else {
   3472      if (mSlots->mDropRow < mRowCount - 1) {
   3473        int32_t nextLevel;
   3474        view->GetLevel(mSlots->mDropRow + 1, &nextLevel);
   3475        if (nextLevel > level) {
   3476          level = nextLevel;
   3477        }
   3478      }
   3479    }
   3480 
   3481    currX += mIndentation * level;
   3482 
   3483    if (primaryCol) {
   3484      ComputedStyle* twistyContext =
   3485          GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
   3486      nsRect imageSize;
   3487      nsRect twistyRect;
   3488      GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect,
   3489                    aPresContext, twistyContext);
   3490      nsMargin twistyMargin;
   3491      twistyContext->StyleMargin()->GetMargin(twistyMargin);
   3492      twistyRect.Inflate(twistyMargin);
   3493      currX += twistyRect.width;
   3494    }
   3495 
   3496    const nsStylePosition* stylePosition = feedbackContext->StylePosition();
   3497 
   3498    // Obtain the width for the drop feedback or use default value.
   3499    nscoord width;
   3500    const AnchorPosResolutionParams anchorResolutionParams{
   3501        this, feedbackContext->StyleDisplay()->mPosition};
   3502    const auto styleWidth = stylePosition->GetWidth(anchorResolutionParams);
   3503    if (styleWidth->ConvertsToLength()) {
   3504      width = styleWidth->ToLength();
   3505    } else {
   3506      // Use default width 50px.
   3507      width = nsPresContext::CSSPixelsToAppUnits(50);
   3508    }
   3509 
   3510    // Obtain the height for the drop feedback or use default value.
   3511    nscoord height;
   3512    const auto styleHeight = stylePosition->GetHeight(anchorResolutionParams);
   3513    if (styleHeight->ConvertsToLength()) {
   3514      height = styleHeight->ToLength();
   3515    } else {
   3516      // Use default height 2px.
   3517      height = nsPresContext::CSSPixelsToAppUnits(2);
   3518    }
   3519 
   3520    // Obtain the margins for the drop feedback and then deflate our rect
   3521    // by that amount.
   3522    nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height);
   3523    nsMargin margin;
   3524    feedbackContext->StyleMargin()->GetMargin(margin);
   3525    feedbackRect.Deflate(margin);
   3526 
   3527    feedbackRect.y += (aDropFeedbackRect.height - height) / 2;
   3528 
   3529    // Finally paint the drop feedback.
   3530    result &= PaintBackgroundLayer(feedbackContext, aPresContext,
   3531                                   aRenderingContext, feedbackRect, aDirtyRect);
   3532  }
   3533 
   3534  return result;
   3535 }
   3536 
   3537 ImgDrawResult nsTreeBodyFrame::PaintBackgroundLayer(
   3538    ComputedStyle* aComputedStyle, nsPresContext* aPresContext,
   3539    gfxContext& aRenderingContext, const nsRect& aRect,
   3540    const nsRect& aDirtyRect) {
   3541  const nsStyleBorder* myBorder = aComputedStyle->StyleBorder();
   3542  nsCSSRendering::PaintBGParams params =
   3543      nsCSSRendering::PaintBGParams::ForAllLayers(
   3544          *aPresContext, aDirtyRect, aRect, this,
   3545          nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES);
   3546  ImgDrawResult result = nsCSSRendering::PaintStyleImageLayerWithSC(
   3547      params, aRenderingContext, aComputedStyle, *myBorder);
   3548 
   3549  result &= nsCSSRendering::PaintBorderWithStyleBorder(
   3550      aPresContext, aRenderingContext, this, aDirtyRect, aRect, *myBorder,
   3551      mComputedStyle, PaintBorderFlags::SyncDecodeImages);
   3552 
   3553  nsCSSRendering::PaintNonThemedOutline(aPresContext, aRenderingContext, this,
   3554                                        aDirtyRect, aRect, aComputedStyle);
   3555 
   3556  return result;
   3557 }
   3558 
   3559 // Scrolling
   3560 nsresult nsTreeBodyFrame::EnsureRowIsVisible(int32_t aRow) {
   3561  ScrollParts parts = GetScrollParts();
   3562  nsresult rv = EnsureRowIsVisibleInternal(parts, aRow);
   3563  NS_ENSURE_SUCCESS(rv, rv);
   3564  UpdateScrollbars(parts);
   3565  return rv;
   3566 }
   3567 
   3568 nsresult nsTreeBodyFrame::EnsureRowIsVisibleInternal(const ScrollParts& aParts,
   3569                                                     int32_t aRow) {
   3570  if (!mView || !mPageLength) {
   3571    return NS_OK;
   3572  }
   3573 
   3574  if (mTopRowIndex <= aRow && mTopRowIndex + mPageLength > aRow) {
   3575    return NS_OK;
   3576  }
   3577 
   3578  if (aRow < mTopRowIndex) {
   3579    ScrollToRowInternal(aParts, aRow);
   3580  } else {
   3581    // Bring it just on-screen.
   3582    int32_t distance = aRow - (mTopRowIndex + mPageLength) + 1;
   3583    ScrollToRowInternal(aParts, mTopRowIndex + distance);
   3584  }
   3585 
   3586  return NS_OK;
   3587 }
   3588 
   3589 nsresult nsTreeBodyFrame::EnsureCellIsVisible(int32_t aRow,
   3590                                              nsTreeColumn* aCol) {
   3591  if (!aCol) {
   3592    return NS_ERROR_INVALID_ARG;
   3593  }
   3594 
   3595  ScrollParts parts = GetScrollParts();
   3596  nsresult rv = EnsureRowIsVisibleInternal(parts, aRow);
   3597  NS_ENSURE_SUCCESS(rv, rv);
   3598  UpdateScrollbars(parts);
   3599  return rv;
   3600 }
   3601 
   3602 void nsTreeBodyFrame::ScrollToRow(int32_t aRow) {
   3603  ScrollParts parts = GetScrollParts();
   3604  ScrollToRowInternal(parts, aRow);
   3605  UpdateScrollbars(parts);
   3606 }
   3607 
   3608 nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts,
   3609                                              int32_t aRow) {
   3610  ScrollInternal(aParts, aRow);
   3611 
   3612  return NS_OK;
   3613 }
   3614 
   3615 void nsTreeBodyFrame::ScrollByLines(int32_t aNumLines) {
   3616  if (!mView) {
   3617    return;
   3618  }
   3619  int32_t newIndex = mTopRowIndex + aNumLines;
   3620  ScrollToRow(newIndex);
   3621 }
   3622 
   3623 void nsTreeBodyFrame::ScrollByPages(int32_t aNumPages) {
   3624  if (!mView) {
   3625    return;
   3626  }
   3627  int32_t newIndex = mTopRowIndex + aNumPages * mPageLength;
   3628  ScrollToRow(newIndex);
   3629 }
   3630 
   3631 nsresult nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts,
   3632                                         int32_t aRow) {
   3633  if (!mView) {
   3634    return NS_OK;
   3635  }
   3636 
   3637  // Note that we may be "over scrolled" at this point; that is the
   3638  // current mTopRowIndex may be larger than mRowCount - mPageLength.
   3639  // This can happen when items are removed for example. (bug 1085050)
   3640 
   3641  int32_t maxTopRowIndex = std::max(0, mRowCount - mPageLength);
   3642  aRow = std::clamp(aRow, 0, maxTopRowIndex);
   3643  if (aRow == mTopRowIndex) {
   3644    return NS_OK;
   3645  }
   3646  mTopRowIndex = aRow;
   3647  Invalidate();
   3648  PostScrollEvent();
   3649  return NS_OK;
   3650 }
   3651 
   3652 void nsTreeBodyFrame::ScrollByPage(nsScrollbarFrame* aScrollbar,
   3653                                   int32_t aDirection,
   3654                                   ScrollSnapFlags aSnapFlags) {
   3655  // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
   3656  MOZ_ASSERT(aScrollbar != nullptr);
   3657  ScrollByPages(aDirection);
   3658 }
   3659 
   3660 void nsTreeBodyFrame::ScrollByWhole(nsScrollbarFrame* aScrollbar,
   3661                                    int32_t aDirection,
   3662                                    ScrollSnapFlags aSnapFlags) {
   3663  // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
   3664  MOZ_ASSERT(aScrollbar != nullptr);
   3665  int32_t newIndex = aDirection < 0 ? 0 : mTopRowIndex;
   3666  ScrollToRow(newIndex);
   3667 }
   3668 
   3669 void nsTreeBodyFrame::ScrollByLine(nsScrollbarFrame* aScrollbar,
   3670                                   int32_t aDirection,
   3671                                   ScrollSnapFlags aSnapFlags) {
   3672  // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
   3673  MOZ_ASSERT(aScrollbar != nullptr);
   3674  ScrollByLines(aDirection);
   3675 }
   3676 
   3677 void nsTreeBodyFrame::ScrollByUnit(nsScrollbarFrame* aScrollbar,
   3678                                   ScrollMode aMode, int32_t aDirection,
   3679                                   ScrollUnit aUnit,
   3680                                   ScrollSnapFlags aSnapFlags) {
   3681  MOZ_ASSERT_UNREACHABLE("Can't get here, we don't call MoveToNewPosition");
   3682 }
   3683 
   3684 void nsTreeBodyFrame::RepeatButtonScroll(nsScrollbarFrame* aScrollbar) {
   3685  MOZ_ASSERT(!aScrollbar->IsHorizontal());
   3686  ScrollParts parts = GetScrollParts();
   3687  int32_t direction = aScrollbar->GetButtonScrollDirection();
   3688  AutoWeakFrame weakFrame(this);
   3689  ScrollToRowInternal(parts, mTopRowIndex + direction);
   3690 
   3691  if (weakFrame.IsAlive() && mScrollbarActivity) {
   3692    mScrollbarActivity->ActivityOccurred();
   3693  }
   3694  if (weakFrame.IsAlive()) {
   3695    UpdateScrollbars(parts);
   3696  }
   3697 }
   3698 
   3699 void nsTreeBodyFrame::ThumbMoved(nsScrollbarFrame* aScrollbar, nscoord aOldPos,
   3700                                 nscoord aNewPos) {
   3701  ScrollParts parts = GetScrollParts();
   3702 
   3703  if (aOldPos == aNewPos) {
   3704    return;
   3705  }
   3706 
   3707  AutoWeakFrame weakFrame(this);
   3708 
   3709  // Vertical Scrollbar
   3710  if (parts.mVScrollbar == aScrollbar) {
   3711    nscoord rh = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   3712    nscoord newIndex = nsPresContext::AppUnitsToIntCSSPixels(aNewPos);
   3713    nscoord newrow = (rh > 0) ? (newIndex / rh) : 0;
   3714    ScrollInternal(parts, newrow);
   3715  }
   3716  if (weakFrame.IsAlive()) {
   3717    UpdateScrollbars(parts);
   3718  }
   3719 }
   3720 
   3721 // The style cache.
   3722 ComputedStyle* nsTreeBodyFrame::GetPseudoComputedStyle(
   3723    nsCSSAnonBoxPseudoStaticAtom* aPseudoElement) {
   3724  return mStyleCache.GetComputedStyle(PresContext(), mContent, mComputedStyle,
   3725                                      aPseudoElement, mScratchArray);
   3726 }
   3727 
   3728 XULTreeElement* nsTreeBodyFrame::GetBaseElement() {
   3729  if (!mTree) {
   3730    nsIFrame* parent = GetParent();
   3731    while (parent) {
   3732      nsIContent* content = parent->GetContent();
   3733      if (content && content->IsXULElement(nsGkAtoms::tree)) {
   3734        mTree = XULTreeElement::FromNodeOrNull(content->AsElement());
   3735        break;
   3736      }
   3737 
   3738      parent = parent->GetInFlowParent();
   3739    }
   3740  }
   3741 
   3742  return mTree;
   3743 }
   3744 
   3745 nsresult nsTreeBodyFrame::ClearStyleAndImageCaches() {
   3746  mStyleCache.Clear();
   3747  CancelImageRequests();
   3748  mImageCache.Clear();
   3749  return NS_OK;
   3750 }
   3751 
   3752 void nsTreeBodyFrame::RemoveImageCacheEntry(int32_t aRowIndex,
   3753                                            nsTreeColumn* aCol) {
   3754  nsAutoString imageSrc;
   3755  nsCOMPtr<nsITreeView> view = GetExistingView();
   3756  if (!view || NS_FAILED(view->GetImageSrc(aRowIndex, aCol, imageSrc))) {
   3757    return;
   3758  }
   3759  nsCOMPtr<nsIURI> uri;
   3760  auto* pc = PresContext();
   3761  nsContentUtils::NewURIWithDocumentCharset(
   3762      getter_AddRefs(uri), imageSrc, pc->Document(), mContent->GetBaseURI());
   3763  if (!uri) {
   3764    return;
   3765  }
   3766  auto lookup = mImageCache.Lookup(uri);
   3767  if (!lookup) {
   3768    return;
   3769  }
   3770  DoCancelImageCacheEntry(*lookup, pc);
   3771  lookup.Remove();
   3772 }
   3773 
   3774 /* virtual */
   3775 void nsTreeBodyFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
   3776  SimpleXULLeafFrame::DidSetComputedStyle(aOldComputedStyle);
   3777 
   3778  // Clear the style cache; the pointers are no longer even valid
   3779  mStyleCache.Clear();
   3780  // XXX The following is hacky, but it's not incorrect,
   3781  // and appears to fix a few bugs with style changes, like text zoom and
   3782  // dpi changes
   3783  mIndentation = GetIndentation();
   3784  mRowHeight = GetRowHeight();
   3785 }
   3786 
   3787 bool nsTreeBodyFrame::OffsetForHorzScroll(nsRect& rect, bool clip) {
   3788  rect.x -= mHorzPosition;
   3789 
   3790  // Scrolled out before
   3791  if (rect.XMost() <= mInnerBox.x) {
   3792    return false;
   3793  }
   3794 
   3795  // Scrolled out after
   3796  if (rect.x > mInnerBox.XMost()) {
   3797    return false;
   3798  }
   3799 
   3800  if (clip) {
   3801    nscoord leftEdge = std::max(rect.x, mInnerBox.x);
   3802    nscoord rightEdge = std::min(rect.XMost(), mInnerBox.XMost());
   3803    rect.x = leftEdge;
   3804    rect.width = rightEdge - leftEdge;
   3805 
   3806    // Should have returned false above
   3807    NS_ASSERTION(rect.width >= 0, "horz scroll code out of sync");
   3808  }
   3809 
   3810  return true;
   3811 }
   3812 
   3813 bool nsTreeBodyFrame::CanAutoScroll(int32_t aRowIndex) {
   3814  // Check first for partially visible last row.
   3815  if (aRowIndex == mRowCount - 1) {
   3816    nscoord y = mInnerBox.y + (aRowIndex - mTopRowIndex) * mRowHeight;
   3817    if (y < mInnerBox.height && y + mRowHeight > mInnerBox.height) {
   3818      return true;
   3819    }
   3820  }
   3821 
   3822  if (aRowIndex > 0 && aRowIndex < mRowCount - 1) {
   3823    return true;
   3824  }
   3825 
   3826  return false;
   3827 }
   3828 
   3829 // Given a dom event, figure out which row in the tree the mouse is over,
   3830 // if we should drop before/after/on that row or we should auto-scroll.
   3831 // Doesn't query the content about if the drag is allowable, that's done
   3832 // elsewhere.
   3833 //
   3834 // For containers, we break up the vertical space of the row as follows: if in
   3835 // the topmost 25%, the drop is _before_ the row the mouse is over; if in the
   3836 // last 25%, _after_; in the middle 50%, we consider it a drop _on_ the
   3837 // container.
   3838 //
   3839 // For non-containers, if the mouse is in the top 50% of the row, the drop is
   3840 // _before_ and the bottom 50% _after_
   3841 void nsTreeBodyFrame::ComputeDropPosition(WidgetGUIEvent* aEvent, int32_t* aRow,
   3842                                          int16_t* aOrient,
   3843                                          int16_t* aScrollLines) {
   3844  *aOrient = -1;
   3845  *aScrollLines = 0;
   3846 
   3847  // Convert the event's point to our coordinates.  We want it in
   3848  // the coordinates of our inner box's coordinates.
   3849  nsPoint pt =
   3850      nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, RelativeTo{this});
   3851  int32_t xTwips = pt.x - mInnerBox.x;
   3852  int32_t yTwips = pt.y - mInnerBox.y;
   3853 
   3854  nsCOMPtr<nsITreeView> view = GetExistingView();
   3855  *aRow = GetRowAtInternal(xTwips, yTwips);
   3856  if (*aRow >= 0) {
   3857    // Compute the top/bottom of the row in question.
   3858    int32_t yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex);
   3859 
   3860    bool isContainer = false;
   3861    view->IsContainer(*aRow, &isContainer);
   3862    if (isContainer) {
   3863      // for a container, use a 25%/50%/25% breakdown
   3864      if (yOffset < mRowHeight / 4) {
   3865        *aOrient = nsITreeView::DROP_BEFORE;
   3866      } else if (yOffset > mRowHeight - (mRowHeight / 4)) {
   3867        *aOrient = nsITreeView::DROP_AFTER;
   3868      } else {
   3869        *aOrient = nsITreeView::DROP_ON;
   3870      }
   3871    } else {
   3872      // for a non-container use a 50%/50% breakdown
   3873      if (yOffset < mRowHeight / 2) {
   3874        *aOrient = nsITreeView::DROP_BEFORE;
   3875      } else {
   3876        *aOrient = nsITreeView::DROP_AFTER;
   3877      }
   3878    }
   3879  }
   3880 
   3881  if (CanAutoScroll(*aRow)) {
   3882    // Get the max value from the look and feel service.
   3883    int32_t scrollLinesMax =
   3884        LookAndFeel::GetInt(LookAndFeel::IntID::TreeScrollLinesMax, 0);
   3885    scrollLinesMax--;
   3886    if (scrollLinesMax < 0) {
   3887      scrollLinesMax = 0;
   3888    }
   3889 
   3890    // Determine if we're w/in a margin of the top/bottom of the tree during a
   3891    // drag. This will ultimately cause us to scroll, but that's done elsewhere.
   3892    nscoord height = (3 * mRowHeight) / 4;
   3893    if (yTwips < height) {
   3894      // scroll up
   3895      *aScrollLines =
   3896          NSToIntRound(-scrollLinesMax * (1 - (float)yTwips / height) - 1);
   3897    } else if (yTwips > mRect.height - height) {
   3898      // scroll down
   3899      *aScrollLines = NSToIntRound(
   3900          scrollLinesMax * (1 - (float)(mRect.height - yTwips) / height) + 1);
   3901    }
   3902  }
   3903 }  // ComputeDropPosition
   3904 
   3905 void nsTreeBodyFrame::OpenCallback(nsITimer* aTimer, void* aClosure) {
   3906  auto* self = static_cast<nsTreeBodyFrame*>(aClosure);
   3907  if (!self) {
   3908    return;
   3909  }
   3910 
   3911  aTimer->Cancel();
   3912  self->mSlots->mTimer = nullptr;
   3913 
   3914  nsCOMPtr<nsITreeView> view = self->GetExistingView();
   3915  if (self->mSlots->mDropRow >= 0) {
   3916    self->mSlots->mArray.AppendElement(self->mSlots->mDropRow);
   3917    view->ToggleOpenState(self->mSlots->mDropRow);
   3918  }
   3919 }
   3920 
   3921 void nsTreeBodyFrame::CloseCallback(nsITimer* aTimer, void* aClosure) {
   3922  auto* self = static_cast<nsTreeBodyFrame*>(aClosure);
   3923  if (!self) {
   3924    return;
   3925  }
   3926 
   3927  aTimer->Cancel();
   3928  self->mSlots->mTimer = nullptr;
   3929 
   3930  nsCOMPtr<nsITreeView> view = self->GetExistingView();
   3931  auto array = std::move(self->mSlots->mArray);
   3932  if (!view) {
   3933    return;
   3934  }
   3935  for (auto elem : Reversed(array)) {
   3936    view->ToggleOpenState(elem);
   3937  }
   3938 }
   3939 
   3940 void nsTreeBodyFrame::LazyScrollCallback(nsITimer* aTimer, void* aClosure) {
   3941  nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
   3942  if (self) {
   3943    aTimer->Cancel();
   3944    self->mSlots->mTimer = nullptr;
   3945 
   3946    if (self->mView) {
   3947      // Set a new timer to scroll the tree repeatedly.
   3948      self->CreateTimer(LookAndFeel::IntID::TreeScrollDelay, ScrollCallback,
   3949                        nsITimer::TYPE_REPEATING_SLACK,
   3950                        getter_AddRefs(self->mSlots->mTimer),
   3951                        "nsTreeBodyFrame::ScrollCallback"_ns);
   3952      self->ScrollByLines(self->mSlots->mScrollLines);
   3953      // ScrollByLines may have deleted |self|.
   3954    }
   3955  }
   3956 }
   3957 
   3958 void nsTreeBodyFrame::ScrollCallback(nsITimer* aTimer, void* aClosure) {
   3959  nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
   3960  if (self) {
   3961    // Don't scroll if we are already at the top or bottom of the view.
   3962    if (self->mView && self->CanAutoScroll(self->mSlots->mDropRow)) {
   3963      self->ScrollByLines(self->mSlots->mScrollLines);
   3964    } else {
   3965      aTimer->Cancel();
   3966      self->mSlots->mTimer = nullptr;
   3967    }
   3968  }
   3969 }
   3970 
   3971 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
   3972 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsTreeBodyFrame::ScrollEvent::Run() {
   3973  if (mInner) {
   3974    mInner->FireScrollEvent();
   3975  }
   3976  return NS_OK;
   3977 }
   3978 
   3979 void nsTreeBodyFrame::FireScrollEvent() {
   3980  mScrollEvent.Forget();
   3981  WidgetGUIEvent event(true, eScroll, nullptr);
   3982  // scroll events fired at elements don't bubble
   3983  event.mFlags.mBubbles = false;
   3984  RefPtr<nsIContent> content = GetContent();
   3985  RefPtr<nsPresContext> presContext = PresContext();
   3986  EventDispatcher::Dispatch(content, presContext, &event);
   3987 }
   3988 
   3989 void nsTreeBodyFrame::PostScrollEvent() {
   3990  if (mScrollEvent.IsPending()) {
   3991    return;
   3992  }
   3993 
   3994  RefPtr<ScrollEvent> event = new ScrollEvent(this);
   3995  nsresult rv = mContent->OwnerDoc()->Dispatch(do_AddRef(event));
   3996  if (NS_FAILED(rv)) {
   3997    NS_WARNING("failed to dispatch ScrollEvent");
   3998  } else {
   3999    mScrollEvent = std::move(event);
   4000  }
   4001 }
   4002 
   4003 void nsTreeBodyFrame::ScrollbarActivityStarted() const {
   4004  if (mScrollbarActivity) {
   4005    mScrollbarActivity->ActivityStarted();
   4006  }
   4007 }
   4008 
   4009 void nsTreeBodyFrame::ScrollbarActivityStopped() const {
   4010  if (mScrollbarActivity) {
   4011    mScrollbarActivity->ActivityStopped();
   4012  }
   4013 }
   4014 
   4015 #ifdef ACCESSIBILITY
   4016 static void InitCustomEvent(CustomEvent* aEvent, const nsAString& aType,
   4017                            nsIWritablePropertyBag2* aDetail) {
   4018  AutoJSAPI jsapi;
   4019  if (!jsapi.Init(aEvent->GetParentObject())) {
   4020    return;
   4021  }
   4022 
   4023  JSContext* cx = jsapi.cx();
   4024  JS::Rooted<JS::Value> detail(cx);
   4025  if (!ToJSValue(cx, aDetail, &detail)) {
   4026    jsapi.ClearException();
   4027    return;
   4028  }
   4029 
   4030  aEvent->InitCustomEvent(cx, aType, /* aCanBubble = */ true,
   4031                          /* aCancelable = */ false, detail);
   4032 }
   4033 
   4034 void nsTreeBodyFrame::FireRowCountChangedEvent(int32_t aIndex, int32_t aCount) {
   4035  RefPtr<XULTreeElement> tree(GetBaseElement());
   4036  if (!tree) {
   4037    return;
   4038  }
   4039 
   4040  RefPtr<Document> doc = tree->OwnerDoc();
   4041  MOZ_ASSERT(doc);
   4042 
   4043  RefPtr<Event> event =
   4044      doc->CreateEvent(u"customevent"_ns, CallerType::System, IgnoreErrors());
   4045 
   4046  CustomEvent* treeEvent = event->AsCustomEvent();
   4047  if (!treeEvent) {
   4048    return;
   4049  }
   4050 
   4051  nsCOMPtr<nsIWritablePropertyBag2> propBag(
   4052      do_CreateInstance("@mozilla.org/hash-property-bag;1"));
   4053  if (!propBag) {
   4054    return;
   4055  }
   4056 
   4057  // Set 'index' data - the row index rows are changed from.
   4058  propBag->SetPropertyAsInt32(u"index"_ns, aIndex);
   4059 
   4060  // Set 'count' data - the number of changed rows.
   4061  propBag->SetPropertyAsInt32(u"count"_ns, aCount);
   4062 
   4063  InitCustomEvent(treeEvent, u"TreeRowCountChanged"_ns, propBag);
   4064 
   4065  event->SetTrusted(true);
   4066 
   4067  RefPtr<AsyncEventDispatcher> asyncDispatcher =
   4068      new AsyncEventDispatcher(tree, event.forget());
   4069  asyncDispatcher->PostDOMEvent();
   4070 }
   4071 
   4072 void nsTreeBodyFrame::FireInvalidateEvent(int32_t aStartRowIdx,
   4073                                          int32_t aEndRowIdx,
   4074                                          nsTreeColumn* aStartCol,
   4075                                          nsTreeColumn* aEndCol) {
   4076  RefPtr<XULTreeElement> tree(GetBaseElement());
   4077  if (!tree) {
   4078    return;
   4079  }
   4080 
   4081  RefPtr<Document> doc = tree->OwnerDoc();
   4082 
   4083  RefPtr<Event> event =
   4084      doc->CreateEvent(u"customevent"_ns, CallerType::System, IgnoreErrors());
   4085 
   4086  CustomEvent* treeEvent = event->AsCustomEvent();
   4087  if (!treeEvent) {
   4088    return;
   4089  }
   4090 
   4091  nsCOMPtr<nsIWritablePropertyBag2> propBag(
   4092      do_CreateInstance("@mozilla.org/hash-property-bag;1"));
   4093  if (!propBag) {
   4094    return;
   4095  }
   4096 
   4097  if (aStartRowIdx != -1 && aEndRowIdx != -1) {
   4098    // Set 'startrow' data - the start index of invalidated rows.
   4099    propBag->SetPropertyAsInt32(u"startrow"_ns, aStartRowIdx);
   4100 
   4101    // Set 'endrow' data - the end index of invalidated rows.
   4102    propBag->SetPropertyAsInt32(u"endrow"_ns, aEndRowIdx);
   4103  }
   4104 
   4105  if (aStartCol && aEndCol) {
   4106    // Set 'startcolumn' data - the start index of invalidated rows.
   4107    int32_t startColIdx = aStartCol->GetIndex();
   4108 
   4109    propBag->SetPropertyAsInt32(u"startcolumn"_ns, startColIdx);
   4110 
   4111    // Set 'endcolumn' data - the start index of invalidated rows.
   4112    int32_t endColIdx = aEndCol->GetIndex();
   4113    propBag->SetPropertyAsInt32(u"endcolumn"_ns, endColIdx);
   4114  }
   4115 
   4116  InitCustomEvent(treeEvent, u"TreeInvalidated"_ns, propBag);
   4117 
   4118  event->SetTrusted(true);
   4119 
   4120  RefPtr<AsyncEventDispatcher> asyncDispatcher =
   4121      new AsyncEventDispatcher(tree, event.forget());
   4122  asyncDispatcher->PostDOMEvent();
   4123 }
   4124 #endif
   4125 
   4126 class nsOverflowChecker : public Runnable {
   4127 public:
   4128  explicit nsOverflowChecker(nsTreeBodyFrame* aFrame)
   4129      : mozilla::Runnable("nsOverflowChecker"), mFrame(aFrame) {}
   4130  NS_IMETHOD Run() override {
   4131    if (mFrame.IsAlive()) {
   4132      nsTreeBodyFrame* tree = static_cast<nsTreeBodyFrame*>(mFrame.GetFrame());
   4133      nsTreeBodyFrame::ScrollParts parts = tree->GetScrollParts();
   4134      tree->CheckOverflow(parts);
   4135    }
   4136    return NS_OK;
   4137  }
   4138 
   4139 private:
   4140  WeakFrame mFrame;
   4141 };
   4142 
   4143 bool nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation) {
   4144  ScrollParts parts = GetScrollParts();
   4145  AutoWeakFrame weakFrame(this);
   4146  UpdateScrollbars(parts);
   4147  NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   4148  if (aNeedsFullInvalidation) {
   4149    Invalidate();
   4150  }
   4151  InvalidateScrollbars(parts);
   4152  NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   4153 
   4154  // Overflow checking dispatches synchronous events, which can cause infinite
   4155  // recursion during reflow. Do the first overflow check synchronously, but
   4156  // force any nested checks to round-trip through the event loop. See bug
   4157  // 905909.
   4158  RefPtr<nsOverflowChecker> checker = new nsOverflowChecker(this);
   4159  if (!mCheckingOverflow) {
   4160    nsContentUtils::AddScriptRunner(checker);
   4161  } else {
   4162    mContent->OwnerDoc()->Dispatch(checker.forget());
   4163  }
   4164  return weakFrame.IsAlive();
   4165 }
   4166 
   4167 void nsTreeBodyFrame::OnImageIsAnimated(imgIRequest* aRequest) {
   4168  nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, nullptr);
   4169 }