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