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