Coverage Report

Created: 2018-09-25 14:53

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