layout/xul/tree/nsTreeBodyFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/xul/tree/nsTreeBodyFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4687 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/AsyncEventDispatcher.h"
    1.10 +#include "mozilla/ContentEvents.h"
    1.11 +#include "mozilla/DebugOnly.h"
    1.12 +#include "mozilla/EventDispatcher.h"
    1.13 +#include "mozilla/MathAlgorithms.h"
    1.14 +#include "mozilla/MouseEvents.h"
    1.15 +#include "mozilla/Likely.h"
    1.16 +
    1.17 +#include "nsCOMPtr.h"
    1.18 +#include "nsPresContext.h"
    1.19 +#include "nsNameSpaceManager.h"
    1.20 +
    1.21 +#include "nsTreeBodyFrame.h"
    1.22 +#include "nsTreeSelection.h"
    1.23 +#include "nsTreeImageListener.h"
    1.24 +
    1.25 +#include "nsGkAtoms.h"
    1.26 +#include "nsCSSAnonBoxes.h"
    1.27 +
    1.28 +#include "nsIContent.h"
    1.29 +#include "nsStyleContext.h"
    1.30 +#include "nsIBoxObject.h"
    1.31 +#include "nsIDOMCustomEvent.h"
    1.32 +#include "nsIDOMMouseEvent.h"
    1.33 +#include "nsIDOMElement.h"
    1.34 +#include "nsIDOMNodeList.h"
    1.35 +#include "nsIDOMDocument.h"
    1.36 +#include "nsIDOMXULElement.h"
    1.37 +#include "nsIDocument.h"
    1.38 +#include "mozilla/css/StyleRule.h"
    1.39 +#include "nsCSSRendering.h"
    1.40 +#include "nsIXULTemplateBuilder.h"
    1.41 +#include "nsXPIDLString.h"
    1.42 +#include "nsContainerFrame.h"
    1.43 +#include "nsView.h"
    1.44 +#include "nsViewManager.h"
    1.45 +#include "nsWidgetsCID.h"
    1.46 +#include "nsBoxFrame.h"
    1.47 +#include "nsBoxObject.h"
    1.48 +#include "nsIURL.h"
    1.49 +#include "nsNetUtil.h"
    1.50 +#include "nsBoxLayoutState.h"
    1.51 +#include "nsTreeContentView.h"
    1.52 +#include "nsTreeUtils.h"
    1.53 +#include "nsITheme.h"
    1.54 +#include "imgIRequest.h"
    1.55 +#include "imgIContainer.h"
    1.56 +#include "imgILoader.h"
    1.57 +#include "nsINodeInfo.h"
    1.58 +#include "nsContentUtils.h"
    1.59 +#include "nsLayoutUtils.h"
    1.60 +#include "nsIScrollableFrame.h"
    1.61 +#include "nsDisplayList.h"
    1.62 +#include "nsTreeBoxObject.h"
    1.63 +#include "nsRenderingContext.h"
    1.64 +#include "nsIScriptableRegion.h"
    1.65 +#include <algorithm>
    1.66 +#include "ScrollbarActivity.h"
    1.67 +
    1.68 +#ifdef ACCESSIBILITY
    1.69 +#include "nsAccessibilityService.h"
    1.70 +#include "nsIWritablePropertyBag2.h"
    1.71 +#endif
    1.72 +#include "nsBidiUtils.h"
    1.73 +
    1.74 +using namespace mozilla;
    1.75 +using namespace mozilla::layout;
    1.76 +
    1.77 +// Enumeration function that cancels all the image requests in our cache
    1.78 +static PLDHashOperator
    1.79 +CancelImageRequest(const nsAString& aKey,
    1.80 +                   nsTreeImageCacheEntry aEntry, void* aData)
    1.81 +{
    1.82 +
    1.83 +  // If our imgIRequest object was registered with the refresh driver,
    1.84 +  // then we need to deregister it.
    1.85 +  nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData);
    1.86 +
    1.87 +  nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request,
    1.88 +                                        nullptr);
    1.89 +
    1.90 +  aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
    1.91 +  return PL_DHASH_NEXT;
    1.92 +}
    1.93 +
    1.94 +//
    1.95 +// NS_NewTreeFrame
    1.96 +//
    1.97 +// Creates a new tree frame
    1.98 +//
    1.99 +nsIFrame*
   1.100 +NS_NewTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.101 +{
   1.102 +  return new (aPresShell) nsTreeBodyFrame(aPresShell, aContext);
   1.103 +}
   1.104 +
   1.105 +NS_IMPL_FRAMEARENA_HELPERS(nsTreeBodyFrame)
   1.106 +
   1.107 +NS_QUERYFRAME_HEAD(nsTreeBodyFrame)
   1.108 +  NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
   1.109 +  NS_QUERYFRAME_ENTRY(nsIScrollbarOwner)
   1.110 +  NS_QUERYFRAME_ENTRY(nsTreeBodyFrame)
   1.111 +NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame)
   1.112 +
   1.113 +// Constructor
   1.114 +nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.115 +:nsLeafBoxFrame(aPresShell, aContext),
   1.116 + mSlots(nullptr),
   1.117 + mImageCache(16),
   1.118 + mTopRowIndex(0),
   1.119 + mPageLength(0),
   1.120 + mHorzPosition(0),
   1.121 + mOriginalHorzWidth(-1),
   1.122 + mHorzWidth(0),
   1.123 + mAdjustWidth(0),
   1.124 + mRowHeight(0),
   1.125 + mIndentation(0),
   1.126 + mStringWidth(-1),
   1.127 + mUpdateBatchNest(0),
   1.128 + mRowCount(0),
   1.129 + mMouseOverRow(-1),
   1.130 + mFocused(false),
   1.131 + mHasFixedRowCount(false),
   1.132 + mVerticalOverflow(false),
   1.133 + mHorizontalOverflow(false),
   1.134 + mReflowCallbackPosted(false),
   1.135 + mCheckingOverflow(false)
   1.136 +{
   1.137 +  mColumns = new nsTreeColumns(this);
   1.138 +}
   1.139 +
   1.140 +// Destructor
   1.141 +nsTreeBodyFrame::~nsTreeBodyFrame()
   1.142 +{
   1.143 +  mImageCache.EnumerateRead(CancelImageRequest, this);
   1.144 +  DetachImageListeners();
   1.145 +  delete mSlots;
   1.146 +}
   1.147 +
   1.148 +static void
   1.149 +GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin)
   1.150 +{
   1.151 +  aMargin.SizeTo(0, 0, 0, 0);
   1.152 +  if (!aContext->StylePadding()->GetPadding(aMargin)) {
   1.153 +    NS_NOTYETIMPLEMENTED("percentage padding");
   1.154 +  }
   1.155 +  aMargin += aContext->StyleBorder()->GetComputedBorder();
   1.156 +}
   1.157 +
   1.158 +static void
   1.159 +AdjustForBorderPadding(nsStyleContext* aContext, nsRect& aRect)
   1.160 +{
   1.161 +  nsMargin borderPadding(0, 0, 0, 0);
   1.162 +  GetBorderPadding(aContext, borderPadding);
   1.163 +  aRect.Deflate(borderPadding);
   1.164 +}
   1.165 +
   1.166 +void
   1.167 +nsTreeBodyFrame::Init(nsIContent*     aContent,
   1.168 +                      nsIFrame*       aParent,
   1.169 +                      nsIFrame*       aPrevInFlow)
   1.170 +{
   1.171 +  nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
   1.172 +
   1.173 +  mIndentation = GetIndentation();
   1.174 +  mRowHeight = GetRowHeight();
   1.175 +
   1.176 +  EnsureBoxObject();
   1.177 +
   1.178 +  if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
   1.179 +    mScrollbarActivity = new ScrollbarActivity(
   1.180 +                           static_cast<nsIScrollbarOwner*>(this));
   1.181 +  }
   1.182 +}
   1.183 +
   1.184 +nsSize
   1.185 +nsTreeBodyFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
   1.186 +{
   1.187 +  EnsureView();
   1.188 +
   1.189 +  nsIContent* baseElement = GetBaseElement();
   1.190 +
   1.191 +  nsSize min(0,0);
   1.192 +  int32_t desiredRows;
   1.193 +  if (MOZ_UNLIKELY(!baseElement)) {
   1.194 +    desiredRows = 0;
   1.195 +  }
   1.196 +  else if (baseElement->Tag() == nsGkAtoms::select &&
   1.197 +           baseElement->IsHTML()) {
   1.198 +    min.width = CalcMaxRowWidth();
   1.199 +    nsAutoString size;
   1.200 +    baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::size, size);
   1.201 +    if (!size.IsEmpty()) {
   1.202 +      nsresult err;
   1.203 +      desiredRows = size.ToInteger(&err);
   1.204 +      mHasFixedRowCount = true;
   1.205 +      mPageLength = desiredRows;
   1.206 +    }
   1.207 +    else {
   1.208 +      desiredRows = 1;
   1.209 +    }
   1.210 +  }
   1.211 +  else {
   1.212 +    // tree
   1.213 +    nsAutoString rows;
   1.214 +    baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
   1.215 +    if (!rows.IsEmpty()) {
   1.216 +      nsresult err;
   1.217 +      desiredRows = rows.ToInteger(&err);
   1.218 +      mPageLength = desiredRows;
   1.219 +    }
   1.220 +    else {
   1.221 +      desiredRows = 0;
   1.222 +    }
   1.223 +  }
   1.224 +
   1.225 +  min.height = mRowHeight * desiredRows;
   1.226 +
   1.227 +  AddBorderAndPadding(min);
   1.228 +  bool widthSet, heightSet;
   1.229 +  nsIFrame::AddCSSMinSize(aBoxLayoutState, this, min, widthSet, heightSet);
   1.230 +
   1.231 +  return min;
   1.232 +}
   1.233 +
   1.234 +nscoord
   1.235 +nsTreeBodyFrame::CalcMaxRowWidth()
   1.236 +{
   1.237 +  if (mStringWidth != -1)
   1.238 +    return mStringWidth;
   1.239 +
   1.240 +  if (!mView)
   1.241 +    return 0;
   1.242 +
   1.243 +  nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
   1.244 +  nsMargin rowMargin(0,0,0,0);
   1.245 +  GetBorderPadding(rowContext, rowMargin);
   1.246 +
   1.247 +  nscoord rowWidth;
   1.248 +  nsTreeColumn* col;
   1.249 +
   1.250 +  nsRefPtr<nsRenderingContext> rc =
   1.251 +    PresContext()->PresShell()->CreateReferenceRenderingContext();
   1.252 +
   1.253 +  for (int32_t row = 0; row < mRowCount; ++row) {
   1.254 +    rowWidth = 0;
   1.255 +
   1.256 +    for (col = mColumns->GetFirstColumn(); col; col = col->GetNext()) {
   1.257 +      nscoord desiredWidth, currentWidth;
   1.258 +      nsresult rv = GetCellWidth(row, col, rc, desiredWidth, currentWidth);
   1.259 +      if (NS_FAILED(rv)) {
   1.260 +        NS_NOTREACHED("invalid column");
   1.261 +        continue;
   1.262 +      }
   1.263 +      rowWidth += desiredWidth;
   1.264 +    }
   1.265 +
   1.266 +    if (rowWidth > mStringWidth)
   1.267 +      mStringWidth = rowWidth;
   1.268 +  }
   1.269 +
   1.270 +  mStringWidth += rowMargin.left + rowMargin.right;
   1.271 +  return mStringWidth;
   1.272 +}
   1.273 +
   1.274 +void
   1.275 +nsTreeBodyFrame::DestroyFrom(nsIFrame* aDestructRoot)
   1.276 +{
   1.277 +  if (mScrollbarActivity) {
   1.278 +    mScrollbarActivity->Destroy();
   1.279 +    mScrollbarActivity = nullptr;
   1.280 +  }
   1.281 +
   1.282 +  mScrollEvent.Revoke();
   1.283 +  // Make sure we cancel any posted callbacks. 
   1.284 +  if (mReflowCallbackPosted) {
   1.285 +    PresContext()->PresShell()->CancelReflowCallback(this);
   1.286 +    mReflowCallbackPosted = false;
   1.287 +  }
   1.288 +
   1.289 +  if (mColumns)
   1.290 +    mColumns->SetTree(nullptr);
   1.291 +
   1.292 +  // Save off our info into the box object.
   1.293 +  nsCOMPtr<nsPIBoxObject> box(do_QueryInterface(mTreeBoxObject));
   1.294 +  if (box) {
   1.295 +    if (mTopRowIndex > 0) {
   1.296 +      nsAutoString topRowStr; topRowStr.AssignLiteral("topRow");
   1.297 +      nsAutoString topRow;
   1.298 +      topRow.AppendInt(mTopRowIndex);
   1.299 +      box->SetProperty(topRowStr.get(), topRow.get());
   1.300 +    }
   1.301 +
   1.302 +    // Always null out the cached tree body frame.
   1.303 +    box->ClearCachedValues();
   1.304 +
   1.305 +    mTreeBoxObject = nullptr; // Drop our ref here.
   1.306 +  }
   1.307 +
   1.308 +  if (mView) {
   1.309 +    nsCOMPtr<nsITreeSelection> sel;
   1.310 +    mView->GetSelection(getter_AddRefs(sel));
   1.311 +    if (sel)
   1.312 +      sel->SetTree(nullptr);
   1.313 +    mView->SetTree(nullptr);
   1.314 +    mView = nullptr;
   1.315 +  }
   1.316 +
   1.317 +  nsLeafBoxFrame::DestroyFrom(aDestructRoot);
   1.318 +}
   1.319 +
   1.320 +void
   1.321 +nsTreeBodyFrame::EnsureBoxObject()
   1.322 +{
   1.323 +  if (!mTreeBoxObject) {
   1.324 +    nsIContent* parent = GetBaseElement();
   1.325 +    if (parent) {
   1.326 +      nsIDocument* nsDoc = parent->GetDocument();
   1.327 +      if (!nsDoc) // there may be no document, if we're called from Destroy()
   1.328 +        return;
   1.329 +      ErrorResult ignored;
   1.330 +      nsCOMPtr<nsIBoxObject> box =
   1.331 +        nsDoc->GetBoxObjectFor(parent->AsElement(), ignored);
   1.332 +      // Ensure that we got a native box object.
   1.333 +      nsCOMPtr<nsPIBoxObject> pBox = do_QueryInterface(box);
   1.334 +      if (pBox) {
   1.335 +        nsCOMPtr<nsITreeBoxObject> realTreeBoxObject = do_QueryInterface(pBox);
   1.336 +        if (realTreeBoxObject) {
   1.337 +          nsTreeBodyFrame* innerTreeBoxObject =
   1.338 +            static_cast<nsTreeBoxObject*>(realTreeBoxObject.get())
   1.339 +              ->GetCachedTreeBody();
   1.340 +          ENSURE_TRUE(!innerTreeBoxObject || innerTreeBoxObject == this);
   1.341 +          mTreeBoxObject = realTreeBoxObject;
   1.342 +        }
   1.343 +      }
   1.344 +    }
   1.345 +  }
   1.346 +}
   1.347 +
   1.348 +void
   1.349 +nsTreeBodyFrame::EnsureView()
   1.350 +{
   1.351 +  if (!mView) {
   1.352 +    if (PresContext()->PresShell()->IsReflowLocked()) {
   1.353 +      if (!mReflowCallbackPosted) {
   1.354 +        mReflowCallbackPosted = true;
   1.355 +        PresContext()->PresShell()->PostReflowCallback(this);
   1.356 +      }
   1.357 +      return;
   1.358 +    }
   1.359 +    nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject);
   1.360 +    if (box) {
   1.361 +      nsWeakFrame weakFrame(this);
   1.362 +      nsCOMPtr<nsITreeView> treeView;
   1.363 +      mTreeBoxObject->GetView(getter_AddRefs(treeView));
   1.364 +      if (treeView && weakFrame.IsAlive()) {
   1.365 +        nsXPIDLString rowStr;
   1.366 +        box->GetProperty(MOZ_UTF16("topRow"),
   1.367 +                         getter_Copies(rowStr));
   1.368 +        nsAutoString rowStr2(rowStr);
   1.369 +        nsresult error;
   1.370 +        int32_t rowIndex = rowStr2.ToInteger(&error);
   1.371 +
   1.372 +        // Set our view.
   1.373 +        SetView(treeView);
   1.374 +        ENSURE_TRUE(weakFrame.IsAlive());
   1.375 +
   1.376 +        // Scroll to the given row.
   1.377 +        // XXX is this optimal if we haven't laid out yet?
   1.378 +        ScrollToRow(rowIndex);
   1.379 +        ENSURE_TRUE(weakFrame.IsAlive());
   1.380 +
   1.381 +        // Clear out the property info for the top row, but we always keep the
   1.382 +        // view current.
   1.383 +        box->RemoveProperty(MOZ_UTF16("topRow"));
   1.384 +      }
   1.385 +    }
   1.386 +  }
   1.387 +}
   1.388 +
   1.389 +void
   1.390 +nsTreeBodyFrame::ManageReflowCallback(const nsRect& aRect, nscoord aHorzWidth)
   1.391 +{
   1.392 +  if (!mReflowCallbackPosted &&
   1.393 +      (!aRect.IsEqualEdges(mRect) || mHorzWidth != aHorzWidth)) {
   1.394 +    PresContext()->PresShell()->PostReflowCallback(this);
   1.395 +    mReflowCallbackPosted = true;
   1.396 +    mOriginalHorzWidth = mHorzWidth;
   1.397 +  }
   1.398 +  else if (mReflowCallbackPosted &&
   1.399 +           mHorzWidth != aHorzWidth && mOriginalHorzWidth == aHorzWidth) {
   1.400 +    PresContext()->PresShell()->CancelReflowCallback(this);
   1.401 +    mReflowCallbackPosted = false;
   1.402 +    mOriginalHorzWidth = -1;
   1.403 +  }
   1.404 +}
   1.405 +
   1.406 +void
   1.407 +nsTreeBodyFrame::SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect,
   1.408 +                           bool aRemoveOverflowArea)
   1.409 +{
   1.410 +  nscoord horzWidth = CalcHorzWidth(GetScrollParts());
   1.411 +  ManageReflowCallback(aRect, horzWidth);
   1.412 +  mHorzWidth = horzWidth;
   1.413 +
   1.414 +  nsLeafBoxFrame::SetBounds(aBoxLayoutState, aRect, aRemoveOverflowArea);
   1.415 +}
   1.416 +
   1.417 +
   1.418 +bool
   1.419 +nsTreeBodyFrame::ReflowFinished()
   1.420 +{
   1.421 +  if (!mView) {
   1.422 +    nsWeakFrame weakFrame(this);
   1.423 +    EnsureView();
   1.424 +    NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   1.425 +  }
   1.426 +  if (mView) {
   1.427 +    CalcInnerBox();
   1.428 +    ScrollParts parts = GetScrollParts();
   1.429 +    mHorzWidth = CalcHorzWidth(parts);
   1.430 +    if (!mHasFixedRowCount) {
   1.431 +      mPageLength = mInnerBox.height / mRowHeight;
   1.432 +    }
   1.433 +
   1.434 +    int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength);
   1.435 +    if (mTopRowIndex > lastPageTopRow)
   1.436 +      ScrollToRowInternal(parts, lastPageTopRow);
   1.437 +
   1.438 +    nsIContent *treeContent = GetBaseElement();
   1.439 +    if (treeContent &&
   1.440 +        treeContent->AttrValueIs(kNameSpaceID_None,
   1.441 +                                 nsGkAtoms::keepcurrentinview,
   1.442 +                                 nsGkAtoms::_true, eCaseMatters)) {
   1.443 +      // make sure that the current selected item is still
   1.444 +      // visible after the tree changes size.
   1.445 +      nsCOMPtr<nsITreeSelection> sel;
   1.446 +      mView->GetSelection(getter_AddRefs(sel));
   1.447 +      if (sel) {
   1.448 +        int32_t currentIndex;
   1.449 +        sel->GetCurrentIndex(&currentIndex);
   1.450 +        if (currentIndex != -1)
   1.451 +          EnsureRowIsVisibleInternal(parts, currentIndex);
   1.452 +      }
   1.453 +    }
   1.454 +
   1.455 +    if (!FullScrollbarsUpdate(false)) {
   1.456 +      return false;
   1.457 +    }
   1.458 +  }
   1.459 +
   1.460 +  mReflowCallbackPosted = false;
   1.461 +  return false;
   1.462 +}
   1.463 +
   1.464 +void
   1.465 +nsTreeBodyFrame::ReflowCallbackCanceled()
   1.466 +{
   1.467 +  mReflowCallbackPosted = false;
   1.468 +}
   1.469 +
   1.470 +nsresult
   1.471 +nsTreeBodyFrame::GetView(nsITreeView * *aView)
   1.472 +{
   1.473 +  *aView = nullptr;
   1.474 +  nsWeakFrame weakFrame(this);
   1.475 +  EnsureView();
   1.476 +  NS_ENSURE_STATE(weakFrame.IsAlive());
   1.477 +  NS_IF_ADDREF(*aView = mView);
   1.478 +  return NS_OK;
   1.479 +}
   1.480 +
   1.481 +nsresult
   1.482 +nsTreeBodyFrame::SetView(nsITreeView * aView)
   1.483 +{
   1.484 +  // First clear out the old view.
   1.485 +  if (mView) {
   1.486 +    nsCOMPtr<nsITreeSelection> sel;
   1.487 +    mView->GetSelection(getter_AddRefs(sel));
   1.488 +    if (sel)
   1.489 +      sel->SetTree(nullptr);
   1.490 +    mView->SetTree(nullptr);
   1.491 +
   1.492 +    // Only reset the top row index and delete the columns if we had an old non-null view.
   1.493 +    mTopRowIndex = 0;
   1.494 +  }
   1.495 +
   1.496 +  // Tree, meet the view.
   1.497 +  mView = aView;
   1.498 + 
   1.499 +  // Changing the view causes us to refetch our data.  This will
   1.500 +  // necessarily entail a full invalidation of the tree.
   1.501 +  Invalidate();
   1.502 + 
   1.503 +  nsIContent *treeContent = GetBaseElement();
   1.504 +  if (treeContent) {
   1.505 +#ifdef ACCESSIBILITY
   1.506 +    nsAccessibilityService* accService = nsIPresShell::AccService();
   1.507 +    if (accService)
   1.508 +      accService->TreeViewChanged(PresContext()->GetPresShell(), treeContent, mView);
   1.509 +#endif
   1.510 +    FireDOMEvent(NS_LITERAL_STRING("TreeViewChanged"), treeContent);
   1.511 +  }
   1.512 +
   1.513 +  if (mView) {
   1.514 +    // Give the view a new empty selection object to play with, but only if it
   1.515 +    // doesn't have one already.
   1.516 +    nsCOMPtr<nsITreeSelection> sel;
   1.517 +    mView->GetSelection(getter_AddRefs(sel));
   1.518 +    if (sel) {
   1.519 +      sel->SetTree(mTreeBoxObject);
   1.520 +    }
   1.521 +    else {
   1.522 +      NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel));
   1.523 +      mView->SetSelection(sel);
   1.524 +    }
   1.525 +
   1.526 +    // View, meet the tree.
   1.527 +    nsWeakFrame weakFrame(this);
   1.528 +    mView->SetTree(mTreeBoxObject);
   1.529 +    NS_ENSURE_STATE(weakFrame.IsAlive());
   1.530 +    mView->GetRowCount(&mRowCount);
   1.531 + 
   1.532 +    if (!PresContext()->PresShell()->IsReflowLocked()) {
   1.533 +      // The scrollbar will need to be updated.
   1.534 +      FullScrollbarsUpdate(false);
   1.535 +    } else if (!mReflowCallbackPosted) {
   1.536 +      mReflowCallbackPosted = true;
   1.537 +      PresContext()->PresShell()->PostReflowCallback(this);
   1.538 +    }
   1.539 +  }
   1.540 + 
   1.541 +  return NS_OK;
   1.542 +}
   1.543 +
   1.544 +nsresult
   1.545 +nsTreeBodyFrame::GetFocused(bool* aFocused)
   1.546 +{
   1.547 +  *aFocused = mFocused;
   1.548 +  return NS_OK;
   1.549 +}
   1.550 +
   1.551 +nsresult
   1.552 +nsTreeBodyFrame::SetFocused(bool aFocused)
   1.553 +{
   1.554 +  if (mFocused != aFocused) {
   1.555 +    mFocused = aFocused;
   1.556 +    if (mView) {
   1.557 +      nsCOMPtr<nsITreeSelection> sel;
   1.558 +      mView->GetSelection(getter_AddRefs(sel));
   1.559 +      if (sel)
   1.560 +        sel->InvalidateSelection();
   1.561 +    }
   1.562 +  }
   1.563 +  return NS_OK;
   1.564 +}
   1.565 +
   1.566 +nsresult
   1.567 +nsTreeBodyFrame::GetTreeBody(nsIDOMElement** aElement)
   1.568 +{
   1.569 +  //NS_ASSERTION(mContent, "no content, see bug #104878");
   1.570 +  if (!mContent)
   1.571 +    return NS_ERROR_NULL_POINTER;
   1.572 +
   1.573 +  return mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aElement);
   1.574 +}
   1.575 +
   1.576 +nsresult
   1.577 +nsTreeBodyFrame::GetRowHeight(int32_t* _retval)
   1.578 +{
   1.579 +  *_retval = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   1.580 +  return NS_OK;
   1.581 +}
   1.582 +
   1.583 +nsresult
   1.584 +nsTreeBodyFrame::GetRowWidth(int32_t *aRowWidth)
   1.585 +{
   1.586 +  *aRowWidth = nsPresContext::AppUnitsToIntCSSPixels(CalcHorzWidth(GetScrollParts()));
   1.587 +  return NS_OK;
   1.588 +}
   1.589 +
   1.590 +nsresult
   1.591 +nsTreeBodyFrame::GetHorizontalPosition(int32_t *aHorizontalPosition)
   1.592 +{
   1.593 +  *aHorizontalPosition = nsPresContext::AppUnitsToIntCSSPixels(mHorzPosition); 
   1.594 +  return NS_OK;
   1.595 +}
   1.596 +
   1.597 +nsresult
   1.598 +nsTreeBodyFrame::GetSelectionRegion(nsIScriptableRegion **aRegion)
   1.599 +{
   1.600 +  *aRegion = nullptr;
   1.601 +
   1.602 +  nsCOMPtr<nsITreeSelection> selection;
   1.603 +  mView->GetSelection(getter_AddRefs(selection));
   1.604 +  NS_ENSURE_TRUE(selection, NS_OK);
   1.605 +
   1.606 +  nsCOMPtr<nsIScriptableRegion> region = do_CreateInstance("@mozilla.org/gfx/region;1");
   1.607 +  NS_ENSURE_TRUE(region, NS_ERROR_FAILURE);
   1.608 +  region->Init();
   1.609 +
   1.610 +  nsRefPtr<nsPresContext> presContext = PresContext();
   1.611 +  nsIntRect rect = mRect.ToOutsidePixels(presContext->AppUnitsPerCSSPixel());
   1.612 +
   1.613 +  nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
   1.614 +  nsPoint origin = GetOffsetTo(rootFrame);
   1.615 +
   1.616 +  // iterate through the visible rows and add the selected ones to the
   1.617 +  // drag region
   1.618 +  int32_t x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
   1.619 +  int32_t y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
   1.620 +  int32_t top = y;
   1.621 +  int32_t end = LastVisibleRow();
   1.622 +  int32_t rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   1.623 +  for (int32_t i = mTopRowIndex; i <= end; i++) {
   1.624 +    bool isSelected;
   1.625 +    selection->IsSelected(i, &isSelected);
   1.626 +    if (isSelected)
   1.627 +      region->UnionRect(x, y, rect.width, rowHeight);
   1.628 +    y += rowHeight;
   1.629 +  }
   1.630 +
   1.631 +  // clip to the tree boundary in case one row extends past it
   1.632 +  region->IntersectRect(x, top, rect.width, rect.height);
   1.633 +
   1.634 +  NS_ADDREF(*aRegion = region);
   1.635 +  return NS_OK;
   1.636 +}
   1.637 +
   1.638 +nsresult
   1.639 +nsTreeBodyFrame::Invalidate()
   1.640 +{
   1.641 +  if (mUpdateBatchNest)
   1.642 +    return NS_OK;
   1.643 +
   1.644 +  InvalidateFrame();
   1.645 +
   1.646 +  return NS_OK;
   1.647 +}
   1.648 +
   1.649 +nsresult
   1.650 +nsTreeBodyFrame::InvalidateColumn(nsITreeColumn* aCol)
   1.651 +{
   1.652 +  if (mUpdateBatchNest)
   1.653 +    return NS_OK;
   1.654 +
   1.655 +  nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
   1.656 +  if (!col)
   1.657 +    return NS_ERROR_INVALID_ARG;
   1.658 +
   1.659 +#ifdef ACCESSIBILITY
   1.660 +  if (nsIPresShell::IsAccessibilityActive())
   1.661 +    FireInvalidateEvent(-1, -1, aCol, aCol);
   1.662 +#endif
   1.663 +
   1.664 +  nsRect columnRect;
   1.665 +  nsresult rv = col->GetRect(this, mInnerBox.y, mInnerBox.height, &columnRect);
   1.666 +  NS_ENSURE_SUCCESS(rv, rv);
   1.667 +
   1.668 +  // When false then column is out of view
   1.669 +  if (OffsetForHorzScroll(columnRect, true))
   1.670 +      InvalidateFrameWithRect(columnRect);
   1.671 +
   1.672 +  return NS_OK;
   1.673 +}
   1.674 +
   1.675 +nsresult
   1.676 +nsTreeBodyFrame::InvalidateRow(int32_t aIndex)
   1.677 +{
   1.678 +  if (mUpdateBatchNest)
   1.679 +    return NS_OK;
   1.680 +
   1.681 +#ifdef ACCESSIBILITY
   1.682 +  if (nsIPresShell::IsAccessibilityActive())
   1.683 +    FireInvalidateEvent(aIndex, aIndex, nullptr, nullptr);
   1.684 +#endif
   1.685 +
   1.686 +  aIndex -= mTopRowIndex;
   1.687 +  if (aIndex < 0 || aIndex > mPageLength)
   1.688 +    return NS_OK;
   1.689 +
   1.690 +  nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*aIndex, mInnerBox.width, mRowHeight);
   1.691 +  InvalidateFrameWithRect(rowRect);
   1.692 +
   1.693 +  return NS_OK;
   1.694 +}
   1.695 +
   1.696 +nsresult
   1.697 +nsTreeBodyFrame::InvalidateCell(int32_t aIndex, nsITreeColumn* aCol)
   1.698 +{
   1.699 +  if (mUpdateBatchNest)
   1.700 +    return NS_OK;
   1.701 +
   1.702 +#ifdef ACCESSIBILITY
   1.703 +  if (nsIPresShell::IsAccessibilityActive())
   1.704 +    FireInvalidateEvent(aIndex, aIndex, aCol, aCol);
   1.705 +#endif
   1.706 +
   1.707 +  aIndex -= mTopRowIndex;
   1.708 +  if (aIndex < 0 || aIndex > mPageLength)
   1.709 +    return NS_OK;
   1.710 +
   1.711 +  nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
   1.712 +  if (!col)
   1.713 +    return NS_ERROR_INVALID_ARG;
   1.714 +
   1.715 +  nsRect cellRect;
   1.716 +  nsresult rv = col->GetRect(this, mInnerBox.y+mRowHeight*aIndex, mRowHeight,
   1.717 +                             &cellRect);
   1.718 +  NS_ENSURE_SUCCESS(rv, rv);
   1.719 +
   1.720 +  if (OffsetForHorzScroll(cellRect, true))
   1.721 +    InvalidateFrameWithRect(cellRect);
   1.722 +
   1.723 +  return NS_OK;
   1.724 +}
   1.725 +
   1.726 +nsresult
   1.727 +nsTreeBodyFrame::InvalidateRange(int32_t aStart, int32_t aEnd)
   1.728 +{
   1.729 +  if (mUpdateBatchNest)
   1.730 +    return NS_OK;
   1.731 +
   1.732 +  if (aStart == aEnd)
   1.733 +    return InvalidateRow(aStart);
   1.734 +
   1.735 +  int32_t last = LastVisibleRow();
   1.736 +  if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last)
   1.737 +    return NS_OK;
   1.738 +
   1.739 +  if (aStart < mTopRowIndex)
   1.740 +    aStart = mTopRowIndex;
   1.741 +
   1.742 +  if (aEnd > last)
   1.743 +    aEnd = last;
   1.744 +
   1.745 +#ifdef ACCESSIBILITY
   1.746 +  if (nsIPresShell::IsAccessibilityActive()) {
   1.747 +    int32_t end =
   1.748 +      mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0;
   1.749 +    FireInvalidateEvent(aStart, end, nullptr, nullptr);
   1.750 +  }
   1.751 +#endif
   1.752 +
   1.753 +  nsRect rangeRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), mInnerBox.width, mRowHeight*(aEnd-aStart+1));
   1.754 +  InvalidateFrameWithRect(rangeRect);
   1.755 +
   1.756 +  return NS_OK;
   1.757 +}
   1.758 +
   1.759 +nsresult
   1.760 +nsTreeBodyFrame::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol)
   1.761 +{
   1.762 +  if (mUpdateBatchNest)
   1.763 +    return NS_OK;
   1.764 +
   1.765 +  nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
   1.766 +  if (!col)
   1.767 +    return NS_ERROR_INVALID_ARG;
   1.768 +
   1.769 +  if (aStart == aEnd)
   1.770 +    return InvalidateCell(aStart, col);
   1.771 +
   1.772 +  int32_t last = LastVisibleRow();
   1.773 +  if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last)
   1.774 +    return NS_OK;
   1.775 +
   1.776 +  if (aStart < mTopRowIndex)
   1.777 +    aStart = mTopRowIndex;
   1.778 +
   1.779 +  if (aEnd > last)
   1.780 +    aEnd = last;
   1.781 +
   1.782 +#ifdef ACCESSIBILITY
   1.783 +  if (nsIPresShell::IsAccessibilityActive()) {
   1.784 +    int32_t end =
   1.785 +      mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0;
   1.786 +    FireInvalidateEvent(aStart, end, aCol, aCol);
   1.787 +  }
   1.788 +#endif
   1.789 +
   1.790 +  nsRect rangeRect;
   1.791 +  nsresult rv = col->GetRect(this, 
   1.792 +                             mInnerBox.y+mRowHeight*(aStart-mTopRowIndex),
   1.793 +                             mRowHeight*(aEnd-aStart+1),
   1.794 +                             &rangeRect);
   1.795 +  NS_ENSURE_SUCCESS(rv, rv);
   1.796 +
   1.797 +  InvalidateFrameWithRect(rangeRect);
   1.798 +
   1.799 +  return NS_OK;
   1.800 +}
   1.801 +
   1.802 +static void
   1.803 +FindScrollParts(nsIFrame* aCurrFrame, nsTreeBodyFrame::ScrollParts* aResult)
   1.804 +{
   1.805 +  if (!aResult->mColumnsScrollFrame) {
   1.806 +    nsIScrollableFrame* f = do_QueryFrame(aCurrFrame);
   1.807 +    if (f) {
   1.808 +      aResult->mColumnsFrame = aCurrFrame;
   1.809 +      aResult->mColumnsScrollFrame = f;
   1.810 +    }
   1.811 +  }
   1.812 +  
   1.813 +  nsScrollbarFrame *sf = do_QueryFrame(aCurrFrame);
   1.814 +  if (sf) {
   1.815 +    if (!aCurrFrame->IsHorizontal()) {
   1.816 +      if (!aResult->mVScrollbar) {
   1.817 +        aResult->mVScrollbar = sf;
   1.818 +      }
   1.819 +    } else {
   1.820 +      if (!aResult->mHScrollbar) {
   1.821 +        aResult->mHScrollbar = sf;
   1.822 +      }
   1.823 +    }
   1.824 +    // don't bother searching inside a scrollbar
   1.825 +    return;
   1.826 +  }
   1.827 +  
   1.828 +  nsIFrame* child = aCurrFrame->GetFirstPrincipalChild();
   1.829 +  while (child &&
   1.830 +         !child->GetContent()->IsRootOfNativeAnonymousSubtree() &&
   1.831 +         (!aResult->mVScrollbar || !aResult->mHScrollbar ||
   1.832 +          !aResult->mColumnsScrollFrame)) {
   1.833 +    FindScrollParts(child, aResult);
   1.834 +    child = child->GetNextSibling();
   1.835 +  }
   1.836 +}
   1.837 +
   1.838 +nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts()
   1.839 +{
   1.840 +  ScrollParts result = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
   1.841 +  nsIContent* baseElement = GetBaseElement();
   1.842 +  nsIFrame* treeFrame =
   1.843 +    baseElement ? baseElement->GetPrimaryFrame() : nullptr;
   1.844 +  if (treeFrame) {
   1.845 +    // The way we do this, searching through the entire frame subtree, is pretty
   1.846 +    // dumb! We should know where these frames are.
   1.847 +    FindScrollParts(treeFrame, &result);
   1.848 +    if (result.mHScrollbar) {
   1.849 +      result.mHScrollbar->SetScrollbarMediatorContent(GetContent());
   1.850 +      nsIFrame* f = do_QueryFrame(result.mHScrollbar);
   1.851 +      result.mHScrollbarContent = f->GetContent();
   1.852 +    }
   1.853 +    if (result.mVScrollbar) {
   1.854 +      result.mVScrollbar->SetScrollbarMediatorContent(GetContent());
   1.855 +      nsIFrame* f = do_QueryFrame(result.mVScrollbar);
   1.856 +      result.mVScrollbarContent = f->GetContent();
   1.857 +    }
   1.858 +  }
   1.859 +  return result;
   1.860 +}
   1.861 +
   1.862 +void
   1.863 +nsTreeBodyFrame::UpdateScrollbars(const ScrollParts& aParts)
   1.864 +{
   1.865 +  nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   1.866 +
   1.867 +  nsWeakFrame weakFrame(this);
   1.868 +
   1.869 +  if (aParts.mVScrollbar) {
   1.870 +    nsAutoString curPos;
   1.871 +    curPos.AppendInt(mTopRowIndex*rowHeightAsPixels);
   1.872 +    aParts.mVScrollbarContent->
   1.873 +      SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true);
   1.874 +    // 'this' might be deleted here
   1.875 +  }
   1.876 +
   1.877 +  if (weakFrame.IsAlive() && aParts.mHScrollbar) {
   1.878 +    nsAutoString curPos;
   1.879 +    curPos.AppendInt(mHorzPosition);
   1.880 +    aParts.mHScrollbarContent->
   1.881 +      SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true);
   1.882 +    // 'this' might be deleted here
   1.883 +  }
   1.884 +
   1.885 +  if (weakFrame.IsAlive() && mScrollbarActivity) {
   1.886 +    mScrollbarActivity->ActivityOccurred();
   1.887 +  }
   1.888 +}
   1.889 +
   1.890 +void
   1.891 +nsTreeBodyFrame::CheckOverflow(const ScrollParts& aParts)
   1.892 +{
   1.893 +  bool verticalOverflowChanged = false;
   1.894 +  bool horizontalOverflowChanged = false;
   1.895 +
   1.896 +  if (!mVerticalOverflow && mRowCount > mPageLength) {
   1.897 +    mVerticalOverflow = true;
   1.898 +    verticalOverflowChanged = true;
   1.899 +  }
   1.900 +  else if (mVerticalOverflow && mRowCount <= mPageLength) {
   1.901 +    mVerticalOverflow = false;
   1.902 +    verticalOverflowChanged = true;
   1.903 +  }
   1.904 +
   1.905 +  if (aParts.mColumnsFrame) {
   1.906 +    nsRect bounds = aParts.mColumnsFrame->GetRect();
   1.907 +    if (bounds.width != 0) {
   1.908 +      /* Ignore overflows that are less than half a pixel. Yes these happen
   1.909 +         all over the place when flex boxes are compressed real small. 
   1.910 +         Probably a result of a rounding errors somewhere in the layout code. */
   1.911 +      bounds.width += nsPresContext::CSSPixelsToAppUnits(0.5f);
   1.912 +      if (!mHorizontalOverflow && bounds.width < mHorzWidth) {
   1.913 +        mHorizontalOverflow = true;
   1.914 +        horizontalOverflowChanged = true;
   1.915 +      } else if (mHorizontalOverflow && bounds.width >= mHorzWidth) {
   1.916 +        mHorizontalOverflow = false;
   1.917 +        horizontalOverflowChanged = true;
   1.918 +      }
   1.919 +    }
   1.920 +  }
   1.921 +
   1.922 +  nsWeakFrame weakFrame(this);
   1.923 +
   1.924 +  nsRefPtr<nsPresContext> presContext = PresContext();
   1.925 +  nsCOMPtr<nsIPresShell> presShell = presContext->GetPresShell();
   1.926 +  nsCOMPtr<nsIContent> content = mContent;
   1.927 +
   1.928 +  if (verticalOverflowChanged) {
   1.929 +    InternalScrollPortEvent event(true,
   1.930 +      mVerticalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW,
   1.931 +      nullptr);
   1.932 +    event.orient = InternalScrollPortEvent::vertical;
   1.933 +    EventDispatcher::Dispatch(content, presContext, &event);
   1.934 +  }
   1.935 +
   1.936 +  if (horizontalOverflowChanged) {
   1.937 +    InternalScrollPortEvent event(true,
   1.938 +      mHorizontalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW,
   1.939 +      nullptr);
   1.940 +    event.orient = InternalScrollPortEvent::horizontal;
   1.941 +    EventDispatcher::Dispatch(content, presContext, &event);
   1.942 +  }
   1.943 +
   1.944 +  // The synchronous event dispatch above can trigger reflow notifications.
   1.945 +  // Flush those explicitly now, so that we can guard against potential infinite
   1.946 +  // recursion. See bug 905909.
   1.947 +  if (!weakFrame.IsAlive()) {
   1.948 +    return;
   1.949 +  }
   1.950 +  NS_ASSERTION(!mCheckingOverflow, "mCheckingOverflow should not already be set");
   1.951 +  // Don't use AutoRestore since we want to not touch mCheckingOverflow if we fail
   1.952 +  // the weakFrame.IsAlive() check below
   1.953 +  mCheckingOverflow = true;
   1.954 +  presShell->FlushPendingNotifications(Flush_Layout);
   1.955 +  if (!weakFrame.IsAlive()) {
   1.956 +    return;
   1.957 +  }
   1.958 +  mCheckingOverflow = false;
   1.959 +}
   1.960 +
   1.961 +void
   1.962 +nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame)
   1.963 +{
   1.964 +  if (mUpdateBatchNest || !mView)
   1.965 +    return;
   1.966 +  nsWeakFrame weakFrame(this);
   1.967 +
   1.968 +  if (aParts.mVScrollbar) {
   1.969 +    // Do Vertical Scrollbar 
   1.970 +    nsAutoString maxposStr;
   1.971 +
   1.972 +    nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
   1.973 +
   1.974 +    int32_t size = rowHeightAsPixels * (mRowCount > mPageLength ? mRowCount - mPageLength : 0);
   1.975 +    maxposStr.AppendInt(size);
   1.976 +    aParts.mVScrollbarContent->
   1.977 +      SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true);
   1.978 +    ENSURE_TRUE(weakFrame.IsAlive());
   1.979 +
   1.980 +    // Also set our page increment and decrement.
   1.981 +    nscoord pageincrement = mPageLength*rowHeightAsPixels;
   1.982 +    nsAutoString pageStr;
   1.983 +    pageStr.AppendInt(pageincrement);
   1.984 +    aParts.mVScrollbarContent->
   1.985 +      SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true);
   1.986 +    ENSURE_TRUE(weakFrame.IsAlive());
   1.987 +  }
   1.988 +
   1.989 +  if (aParts.mHScrollbar && aParts.mColumnsFrame && aWeakColumnsFrame.IsAlive()) {
   1.990 +    // And now Horizontal scrollbar
   1.991 +    nsRect bounds = aParts.mColumnsFrame->GetRect();
   1.992 +    nsAutoString maxposStr;
   1.993 +
   1.994 +    maxposStr.AppendInt(mHorzWidth > bounds.width ? mHorzWidth - bounds.width : 0);
   1.995 +    aParts.mHScrollbarContent->
   1.996 +      SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true);
   1.997 +    ENSURE_TRUE(weakFrame.IsAlive());
   1.998 +  
   1.999 +    nsAutoString pageStr;
  1.1000 +    pageStr.AppendInt(bounds.width);
  1.1001 +    aParts.mHScrollbarContent->
  1.1002 +      SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true);
  1.1003 +    ENSURE_TRUE(weakFrame.IsAlive());
  1.1004 +  
  1.1005 +    pageStr.Truncate();
  1.1006 +    pageStr.AppendInt(nsPresContext::CSSPixelsToAppUnits(16));
  1.1007 +    aParts.mHScrollbarContent->
  1.1008 +      SetAttr(kNameSpaceID_None, nsGkAtoms::increment, pageStr, true);
  1.1009 +  }
  1.1010 +
  1.1011 +  if (weakFrame.IsAlive() && mScrollbarActivity) {
  1.1012 +    mScrollbarActivity->ActivityOccurred();
  1.1013 +  }
  1.1014 +}
  1.1015 +
  1.1016 +// Takes client x/y in pixels, converts them to appunits, and converts into
  1.1017 +// values relative to this nsTreeBodyFrame frame.
  1.1018 +nsPoint
  1.1019 +nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(int32_t aX, int32_t aY)
  1.1020 +{
  1.1021 +  nsPoint point(nsPresContext::CSSPixelsToAppUnits(aX),
  1.1022 +                nsPresContext::CSSPixelsToAppUnits(aY));
  1.1023 +
  1.1024 +  nsPresContext* presContext = PresContext();
  1.1025 +  point -= GetOffsetTo(presContext->GetPresShell()->GetRootFrame());
  1.1026 +
  1.1027 +  // Adjust by the inner box coords, so that we're in the inner box's
  1.1028 +  // coordinate space.
  1.1029 +  point -= mInnerBox.TopLeft();
  1.1030 +  return point;
  1.1031 +} // AdjustClientCoordsToBoxCoordSpace
  1.1032 +
  1.1033 +nsresult
  1.1034 +nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY, int32_t* _retval)
  1.1035 +{
  1.1036 +  if (!mView)
  1.1037 +    return NS_OK;
  1.1038 +
  1.1039 +  nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
  1.1040 +
  1.1041 +  // Check if the coordinates are above our visible space.
  1.1042 +  if (point.y < 0) {
  1.1043 +    *_retval = -1;
  1.1044 +    return NS_OK;
  1.1045 +  }
  1.1046 +
  1.1047 +  *_retval = GetRowAt(point.x, point.y);
  1.1048 +
  1.1049 +  return NS_OK;
  1.1050 +}
  1.1051 +
  1.1052 +nsresult
  1.1053 +nsTreeBodyFrame::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow, nsITreeColumn** aCol,
  1.1054 +                           nsACString& aChildElt)
  1.1055 +{
  1.1056 +  if (!mView)
  1.1057 +    return NS_OK;
  1.1058 +
  1.1059 +  nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
  1.1060 +
  1.1061 +  // Check if the coordinates are above our visible space.
  1.1062 +  if (point.y < 0) {
  1.1063 +    *aRow = -1;
  1.1064 +    return NS_OK;
  1.1065 +  }
  1.1066 +
  1.1067 +  nsTreeColumn* col;
  1.1068 +  nsIAtom* child;
  1.1069 +  GetCellAt(point.x, point.y, aRow, &col, &child);
  1.1070 +
  1.1071 +  if (col) {
  1.1072 +    NS_ADDREF(*aCol = col);
  1.1073 +    if (child == nsCSSAnonBoxes::moztreecell)
  1.1074 +      aChildElt.AssignLiteral("cell");
  1.1075 +    else if (child == nsCSSAnonBoxes::moztreetwisty)
  1.1076 +      aChildElt.AssignLiteral("twisty");
  1.1077 +    else if (child == nsCSSAnonBoxes::moztreeimage)
  1.1078 +      aChildElt.AssignLiteral("image");
  1.1079 +    else if (child == nsCSSAnonBoxes::moztreecelltext)
  1.1080 +      aChildElt.AssignLiteral("text");
  1.1081 +  }
  1.1082 +
  1.1083 +  return NS_OK;
  1.1084 +}
  1.1085 +
  1.1086 +
  1.1087 +//
  1.1088 +// GetCoordsForCellItem
  1.1089 +//
  1.1090 +// Find the x/y location and width/height (all in PIXELS) of the given object
  1.1091 +// in the given column. 
  1.1092 +//
  1.1093 +// XXX IMPORTANT XXX:
  1.1094 +// Hyatt says in the bug for this, that the following needs to be done:
  1.1095 +// (1) You need to deal with overflow when computing cell rects.  See other column 
  1.1096 +// iteration examples... if you don't deal with this, you'll mistakenly extend the 
  1.1097 +// cell into the scrollbar's rect.
  1.1098 +//
  1.1099 +// (2) You are adjusting the cell rect by the *row" border padding.  That's 
  1.1100 +// wrong.  You need to first adjust a row rect by its border/padding, and then the 
  1.1101 +// cell rect fits inside the adjusted row rect.  It also can have border/padding 
  1.1102 +// as well as margins.  The vertical direction isn't that important, but you need 
  1.1103 +// to get the horizontal direction right.
  1.1104 +//
  1.1105 +// (3) GetImageSize() does not include margins (but it does include border/padding).  
  1.1106 +// You need to make sure to add in the image's margins as well.
  1.1107 +//
  1.1108 +nsresult
  1.1109 +nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsACString& aElement, 
  1.1110 +                                      int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight)
  1.1111 +{
  1.1112 +  *aX = 0;
  1.1113 +  *aY = 0;
  1.1114 +  *aWidth = 0;
  1.1115 +  *aHeight = 0;
  1.1116 +
  1.1117 +  bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1.1118 +  nscoord currX = mInnerBox.x - mHorzPosition;
  1.1119 +
  1.1120 +  // The Rect for the requested item. 
  1.1121 +  nsRect theRect;
  1.1122 +
  1.1123 +  nsPresContext* presContext = PresContext();
  1.1124 +
  1.1125 +  for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; currCol = currCol->GetNext()) {
  1.1126 +
  1.1127 +    // The Rect for the current cell.
  1.1128 +    nscoord colWidth;
  1.1129 +#ifdef DEBUG
  1.1130 +    nsresult rv =
  1.1131 +#endif
  1.1132 +      currCol->GetWidthInTwips(this, &colWidth);
  1.1133 +    NS_ASSERTION(NS_SUCCEEDED(rv), "invalid column");
  1.1134 +
  1.1135 +    nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex),
  1.1136 +                    colWidth, mRowHeight);
  1.1137 +
  1.1138 +    // Check the ID of the current column to see if it matches. If it doesn't 
  1.1139 +    // increment the current X value and continue to the next column.
  1.1140 +    if (currCol != aCol) {
  1.1141 +      currX += cellRect.width;
  1.1142 +      continue;
  1.1143 +    }
  1.1144 +    // Now obtain the properties for our cell.
  1.1145 +    PrefillPropertyArray(aRow, currCol);
  1.1146 +
  1.1147 +    nsAutoString properties;
  1.1148 +    mView->GetCellProperties(aRow, currCol, properties);
  1.1149 +    nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  1.1150 +
  1.1151 +    nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
  1.1152 +
  1.1153 +    // We don't want to consider any of the decorations that may be present
  1.1154 +    // on the current row, so we have to deflate the rect by the border and 
  1.1155 +    // padding and offset its left and top coordinates appropriately. 
  1.1156 +    AdjustForBorderPadding(rowContext, cellRect);
  1.1157 +
  1.1158 +    nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1.1159 +
  1.1160 +    NS_NAMED_LITERAL_CSTRING(cell, "cell");
  1.1161 +    if (currCol->IsCycler() || cell.Equals(aElement)) {
  1.1162 +      // If the current Column is a Cycler, then the Rect is just the cell - the margins. 
  1.1163 +      // Similarly, if we're just being asked for the cell rect, provide it. 
  1.1164 +
  1.1165 +      theRect = cellRect;
  1.1166 +      nsMargin cellMargin;
  1.1167 +      cellContext->StyleMargin()->GetMargin(cellMargin);
  1.1168 +      theRect.Deflate(cellMargin);
  1.1169 +      break;
  1.1170 +    }
  1.1171 +
  1.1172 +    // Since we're not looking for the cell, and since the cell isn't a cycler,
  1.1173 +    // we're looking for some subcomponent, and now we need to subtract the 
  1.1174 +    // borders and padding of the cell from cellRect so this does not 
  1.1175 +    // interfere with our computations.
  1.1176 +    AdjustForBorderPadding(cellContext, cellRect);
  1.1177 +
  1.1178 +    nsRefPtr<nsRenderingContext> rc =
  1.1179 +      presContext->PresShell()->CreateReferenceRenderingContext();
  1.1180 +
  1.1181 +    // Now we'll start making our way across the cell, starting at the edge of 
  1.1182 +    // the cell and proceeding until we hit the right edge. |cellX| is the 
  1.1183 +    // working X value that we will increment as we crawl from left to right.
  1.1184 +    nscoord cellX = cellRect.x;
  1.1185 +    nscoord remainWidth = cellRect.width;
  1.1186 +
  1.1187 +    if (currCol->IsPrimary()) {
  1.1188 +      // If the current Column is a Primary, then we need to take into account the indentation
  1.1189 +      // and possibly a twisty. 
  1.1190 +
  1.1191 +      // The amount of indentation is the indentation width (|mIndentation|) by the level. 
  1.1192 +      int32_t level;
  1.1193 +      mView->GetLevel(aRow, &level);
  1.1194 +      if (!isRTL)
  1.1195 +        cellX += mIndentation * level;
  1.1196 +      remainWidth -= mIndentation * level;
  1.1197 +
  1.1198 +      // Find the twisty rect by computing its size. 
  1.1199 +      nsRect imageRect;
  1.1200 +      nsRect twistyRect(cellRect);
  1.1201 +      nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1.1202 +      GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext,
  1.1203 +                    *rc, twistyContext);
  1.1204 +
  1.1205 +      if (NS_LITERAL_CSTRING("twisty").Equals(aElement)) {
  1.1206 +        // If we're looking for the twisty Rect, just return the size
  1.1207 +        theRect = twistyRect;
  1.1208 +        break;
  1.1209 +      }
  1.1210 +      
  1.1211 +      // Now we need to add in the margins of the twisty element, so that we 
  1.1212 +      // can find the offset of the next element in the cell. 
  1.1213 +      nsMargin twistyMargin;
  1.1214 +      twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1.1215 +      twistyRect.Inflate(twistyMargin);
  1.1216 +
  1.1217 +      // Adjust our working X value with the twisty width (image size, margins,
  1.1218 +      // borders, padding.
  1.1219 +      if (!isRTL)
  1.1220 +        cellX += twistyRect.width;
  1.1221 +    }
  1.1222 +
  1.1223 +    // Cell Image
  1.1224 +    nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  1.1225 +
  1.1226 +    nsRect imageSize = GetImageSize(aRow, currCol, false, imageContext);
  1.1227 +    if (NS_LITERAL_CSTRING("image").Equals(aElement)) {
  1.1228 +      theRect = imageSize;
  1.1229 +      theRect.x = cellX;
  1.1230 +      theRect.y = cellRect.y;
  1.1231 +      break;
  1.1232 +    }
  1.1233 +
  1.1234 +    // Add in the margins of the cell image.
  1.1235 +    nsMargin imageMargin;
  1.1236 +    imageContext->StyleMargin()->GetMargin(imageMargin);
  1.1237 +    imageSize.Inflate(imageMargin);
  1.1238 +
  1.1239 +    // Increment cellX by the image width
  1.1240 +    if (!isRTL)
  1.1241 +      cellX += imageSize.width;
  1.1242 +    
  1.1243 +    // Cell Text 
  1.1244 +    nsAutoString cellText;
  1.1245 +    mView->GetCellText(aRow, currCol, cellText);
  1.1246 +    // We're going to measure this text so we need to ensure bidi is enabled if
  1.1247 +    // necessary
  1.1248 +    CheckTextForBidi(cellText);
  1.1249 +
  1.1250 +    // Create a scratch rect to represent the text rectangle, with the current 
  1.1251 +    // X and Y coords, and a guess at the width and height. The width is the 
  1.1252 +    // remaining width we have left to traverse in the cell, which will be the
  1.1253 +    // widest possible value for the text rect, and the row height. 
  1.1254 +    nsRect textRect(cellX, cellRect.y, remainWidth, cellRect.height);
  1.1255 +
  1.1256 +    // Measure the width of the text. If the width of the text is greater than 
  1.1257 +    // the remaining width available, then we just assume that the text has 
  1.1258 +    // been cropped and use the remaining rect as the text Rect. Otherwise,
  1.1259 +    // we add in borders and padding to the text dimension and give that back. 
  1.1260 +    nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  1.1261 +
  1.1262 +    nsRefPtr<nsFontMetrics> fm;
  1.1263 +    nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  1.1264 +                                                 getter_AddRefs(fm));
  1.1265 +    nscoord height = fm->MaxHeight();
  1.1266 +
  1.1267 +    nsMargin textMargin;
  1.1268 +    textContext->StyleMargin()->GetMargin(textMargin);
  1.1269 +    textRect.Deflate(textMargin);
  1.1270 +
  1.1271 +    // Center the text. XXX Obey vertical-align style prop?
  1.1272 +    if (height < textRect.height) {
  1.1273 +      textRect.y += (textRect.height - height) / 2;
  1.1274 +      textRect.height = height;
  1.1275 +    }
  1.1276 +
  1.1277 +    nsMargin bp(0,0,0,0);
  1.1278 +    GetBorderPadding(textContext, bp);
  1.1279 +    textRect.height += bp.top + bp.bottom;
  1.1280 +
  1.1281 +    rc->SetFont(fm);
  1.1282 +    AdjustForCellText(cellText, aRow, currCol, *rc, textRect);
  1.1283 +
  1.1284 +    theRect = textRect;
  1.1285 +  }
  1.1286 +
  1.1287 +  if (isRTL)
  1.1288 +    theRect.x = mInnerBox.width - theRect.x - theRect.width;
  1.1289 +
  1.1290 +  *aX = nsPresContext::AppUnitsToIntCSSPixels(theRect.x);
  1.1291 +  *aY = nsPresContext::AppUnitsToIntCSSPixels(theRect.y);
  1.1292 +  *aWidth = nsPresContext::AppUnitsToIntCSSPixels(theRect.width);
  1.1293 +  *aHeight = nsPresContext::AppUnitsToIntCSSPixels(theRect.height);
  1.1294 +
  1.1295 +  return NS_OK;
  1.1296 +}
  1.1297 +
  1.1298 +int32_t
  1.1299 +nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY)
  1.1300 +{
  1.1301 +  // Now just mod by our total inner box height and add to our top row index.
  1.1302 +  int32_t row = (aY/mRowHeight)+mTopRowIndex;
  1.1303 +
  1.1304 +  // Check if the coordinates are below our visible space (or within our visible
  1.1305 +  // space but below any row).
  1.1306 +  if (row > mTopRowIndex + mPageLength || row >= mRowCount)
  1.1307 +    return -1;
  1.1308 +
  1.1309 +  return row;
  1.1310 +}
  1.1311 +
  1.1312 +void
  1.1313 +nsTreeBodyFrame::CheckTextForBidi(nsAutoString& aText)
  1.1314 +{
  1.1315 +  // We could check to see whether the prescontext already has bidi enabled,
  1.1316 +  // but usually it won't, so it's probably faster to avoid the call to
  1.1317 +  // GetPresContext() when it's not needed.
  1.1318 +  if (HasRTLChars(aText)) {
  1.1319 +    PresContext()->SetBidiEnabled();
  1.1320 +  }
  1.1321 +}
  1.1322 +
  1.1323 +void
  1.1324 +nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText,
  1.1325 +                                   int32_t aRowIndex,  nsTreeColumn* aColumn,
  1.1326 +                                   nsRenderingContext& aRenderingContext,
  1.1327 +                                   nsRect& aTextRect)
  1.1328 +{
  1.1329 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.1330 +
  1.1331 +  nscoord width =
  1.1332 +    nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length());
  1.1333 +  nscoord maxWidth = aTextRect.width;
  1.1334 +
  1.1335 +  if (aColumn->Overflow()) {
  1.1336 +    DebugOnly<nsresult> rv;
  1.1337 +    nsTreeColumn* nextColumn = aColumn->GetNext();
  1.1338 +    while (nextColumn && width > maxWidth) {
  1.1339 +      while (nextColumn) {
  1.1340 +        nscoord width;
  1.1341 +        rv = nextColumn->GetWidthInTwips(this, &width);
  1.1342 +        NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
  1.1343 +
  1.1344 +        if (width != 0)
  1.1345 +          break;
  1.1346 +
  1.1347 +        nextColumn = nextColumn->GetNext();
  1.1348 +      }
  1.1349 +
  1.1350 +      if (nextColumn) {
  1.1351 +        nsAutoString nextText;
  1.1352 +        mView->GetCellText(aRowIndex, nextColumn, nextText);
  1.1353 +        // We don't measure or draw this text so no need to check it for
  1.1354 +        // bidi-ness
  1.1355 +
  1.1356 +        if (nextText.Length() == 0) {
  1.1357 +          nscoord width;
  1.1358 +          rv = nextColumn->GetWidthInTwips(this, &width);
  1.1359 +          NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
  1.1360 +
  1.1361 +          maxWidth += width;
  1.1362 +
  1.1363 +          nextColumn = nextColumn->GetNext();
  1.1364 +        }
  1.1365 +        else {
  1.1366 +          nextColumn = nullptr;
  1.1367 +        }
  1.1368 +      }
  1.1369 +    }
  1.1370 +  }
  1.1371 +
  1.1372 +  if (width > maxWidth) {
  1.1373 +    // See if the width is even smaller than the ellipsis
  1.1374 +    // If so, clear the text completely.
  1.1375 +    const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
  1.1376 +    aRenderingContext.SetTextRunRTL(false);
  1.1377 +    nscoord ellipsisWidth = aRenderingContext.GetWidth(kEllipsis);
  1.1378 +
  1.1379 +    width = maxWidth;
  1.1380 +    if (ellipsisWidth > width)
  1.1381 +      aText.SetLength(0);
  1.1382 +    else if (ellipsisWidth == width)
  1.1383 +      aText.Assign(kEllipsis);
  1.1384 +    else {
  1.1385 +      // We will be drawing an ellipsis, thank you very much.
  1.1386 +      // Subtract out the required width of the ellipsis.
  1.1387 +      // This is the total remaining width we have to play with.
  1.1388 +      width -= ellipsisWidth;
  1.1389 +
  1.1390 +      // Now we crop.
  1.1391 +      switch (aColumn->GetCropStyle()) {
  1.1392 +        default:
  1.1393 +        case 0: {
  1.1394 +          // Crop right.
  1.1395 +          nscoord cwidth;
  1.1396 +          nscoord twidth = 0;
  1.1397 +          uint32_t length = aText.Length();
  1.1398 +          uint32_t i;
  1.1399 +          for (i = 0; i < length; ++i) {
  1.1400 +            char16_t ch = aText[i];
  1.1401 +            // XXX this is horrible and doesn't handle clusters
  1.1402 +            cwidth = aRenderingContext.GetWidth(ch);
  1.1403 +            if (twidth + cwidth > width)
  1.1404 +              break;
  1.1405 +            twidth += cwidth;
  1.1406 +          }
  1.1407 +          aText.Truncate(i);
  1.1408 +          aText.Append(kEllipsis);
  1.1409 +        }
  1.1410 +        break;
  1.1411 +
  1.1412 +        case 2: {
  1.1413 +          // Crop left.
  1.1414 +          nscoord cwidth;
  1.1415 +          nscoord twidth = 0;
  1.1416 +          int32_t length = aText.Length();
  1.1417 +          int32_t i;
  1.1418 +          for (i=length-1; i >= 0; --i) {
  1.1419 +            char16_t ch = aText[i];
  1.1420 +            cwidth = aRenderingContext.GetWidth(ch);
  1.1421 +            if (twidth + cwidth > width)
  1.1422 +              break;
  1.1423 +            twidth += cwidth;
  1.1424 +          }
  1.1425 +
  1.1426 +          nsAutoString copy;
  1.1427 +          aText.Right(copy, length-1-i);
  1.1428 +          aText.Assign(kEllipsis);
  1.1429 +          aText += copy;
  1.1430 +        }
  1.1431 +        break;
  1.1432 +
  1.1433 +        case 1:
  1.1434 +        {
  1.1435 +          // Crop center.
  1.1436 +          nsAutoString leftStr, rightStr;
  1.1437 +          nscoord cwidth, twidth = 0;
  1.1438 +          int32_t length = aText.Length();
  1.1439 +          int32_t rightPos = length - 1;
  1.1440 +          for (int32_t leftPos = 0; leftPos < rightPos; ++leftPos) {
  1.1441 +            char16_t ch = aText[leftPos];
  1.1442 +            cwidth = aRenderingContext.GetWidth(ch);
  1.1443 +            twidth += cwidth;
  1.1444 +            if (twidth > width)
  1.1445 +              break;
  1.1446 +            leftStr.Append(ch);
  1.1447 +
  1.1448 +            ch = aText[rightPos];
  1.1449 +            cwidth = aRenderingContext.GetWidth(ch);
  1.1450 +            twidth += cwidth;
  1.1451 +            if (twidth > width)
  1.1452 +              break;
  1.1453 +            rightStr.Insert(ch, 0);
  1.1454 +            --rightPos;
  1.1455 +          }
  1.1456 +          aText = leftStr;
  1.1457 +          aText.Append(kEllipsis);
  1.1458 +          aText += rightStr;
  1.1459 +        }
  1.1460 +        break;
  1.1461 +      }
  1.1462 +    }
  1.1463 +
  1.1464 +    width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length());
  1.1465 +  }
  1.1466 +
  1.1467 +  switch (aColumn->GetTextAlignment()) {
  1.1468 +    case NS_STYLE_TEXT_ALIGN_RIGHT: {
  1.1469 +      aTextRect.x += aTextRect.width - width;
  1.1470 +    }
  1.1471 +    break;
  1.1472 +    case NS_STYLE_TEXT_ALIGN_CENTER: {
  1.1473 +      aTextRect.x += (aTextRect.width - width) / 2;
  1.1474 +    }
  1.1475 +    break;
  1.1476 +  }
  1.1477 +
  1.1478 +  aTextRect.width = width;
  1.1479 +}
  1.1480 +
  1.1481 +nsIAtom*
  1.1482 +nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, 
  1.1483 +                                     int32_t aRowIndex,
  1.1484 +                                     nsTreeColumn* aColumn)
  1.1485 +{
  1.1486 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.1487 +
  1.1488 +  // Obtain the properties for our cell.
  1.1489 +  PrefillPropertyArray(aRowIndex, aColumn);
  1.1490 +  nsAutoString properties;
  1.1491 +  mView->GetCellProperties(aRowIndex, aColumn, properties);
  1.1492 +  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  1.1493 +
  1.1494 +  // Resolve style for the cell.
  1.1495 +  nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1.1496 +
  1.1497 +  // Obtain the margins for the cell and then deflate our rect by that 
  1.1498 +  // amount.  The cell is assumed to be contained within the deflated rect.
  1.1499 +  nsRect cellRect(aCellRect);
  1.1500 +  nsMargin cellMargin;
  1.1501 +  cellContext->StyleMargin()->GetMargin(cellMargin);
  1.1502 +  cellRect.Deflate(cellMargin);
  1.1503 +
  1.1504 +  // Adjust the rect for its border and padding.
  1.1505 +  AdjustForBorderPadding(cellContext, cellRect);
  1.1506 +
  1.1507 +  if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) {
  1.1508 +    // The user clicked within the cell's margins/borders/padding.  This constitutes a click on the cell.
  1.1509 +    return nsCSSAnonBoxes::moztreecell;
  1.1510 +  }
  1.1511 +
  1.1512 +  nscoord currX = cellRect.x;
  1.1513 +  nscoord remainingWidth = cellRect.width;
  1.1514 +
  1.1515 +  // Handle right alignment hit testing.
  1.1516 +  bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1.1517 +
  1.1518 +  nsPresContext* presContext = PresContext();
  1.1519 +  nsRefPtr<nsRenderingContext> rc =
  1.1520 +    presContext->PresShell()->CreateReferenceRenderingContext();
  1.1521 +
  1.1522 +  if (aColumn->IsPrimary()) {
  1.1523 +    // If we're the primary column, we have indentation and a twisty.
  1.1524 +    int32_t level;
  1.1525 +    mView->GetLevel(aRowIndex, &level);
  1.1526 +
  1.1527 +    if (!isRTL)
  1.1528 +      currX += mIndentation*level;
  1.1529 +    remainingWidth -= mIndentation*level;
  1.1530 +
  1.1531 +    if ((isRTL && aX > currX + remainingWidth) ||
  1.1532 +        (!isRTL && aX < currX)) {
  1.1533 +      // The user clicked within the indentation.
  1.1534 +      return nsCSSAnonBoxes::moztreecell;
  1.1535 +    }
  1.1536 +
  1.1537 +    // Always leave space for the twisty.
  1.1538 +    nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1.1539 +    bool hasTwisty = false;
  1.1540 +    bool isContainer = false;
  1.1541 +    mView->IsContainer(aRowIndex, &isContainer);
  1.1542 +    if (isContainer) {
  1.1543 +      bool isContainerEmpty = false;
  1.1544 +      mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
  1.1545 +      if (!isContainerEmpty)
  1.1546 +        hasTwisty = true;
  1.1547 +    }
  1.1548 +
  1.1549 +    // Resolve style for the twisty.
  1.1550 +    nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1.1551 +
  1.1552 +    nsRect imageSize;
  1.1553 +    GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, presContext,
  1.1554 +                  *rc, twistyContext);
  1.1555 +
  1.1556 +    // We will treat a click as hitting the twisty if it happens on the margins, borders, padding,
  1.1557 +    // or content of the twisty object.  By allowing a "slop" into the margin, we make it a little
  1.1558 +    // bit easier for a user to hit the twisty.  (We don't want to be too picky here.)
  1.1559 +    nsMargin twistyMargin;
  1.1560 +    twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1.1561 +    twistyRect.Inflate(twistyMargin);
  1.1562 +    if (isRTL)
  1.1563 +      twistyRect.x = currX + remainingWidth - twistyRect.width;
  1.1564 +
  1.1565 +    // Now we test to see if aX is actually within the twistyRect.  If it is, and if the item should
  1.1566 +    // have a twisty, then we return "twisty".  If it is within the rect but we shouldn't have a twisty,
  1.1567 +    // then we return "cell".
  1.1568 +    if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) {
  1.1569 +      if (hasTwisty)
  1.1570 +        return nsCSSAnonBoxes::moztreetwisty;
  1.1571 +      else
  1.1572 +        return nsCSSAnonBoxes::moztreecell;
  1.1573 +    }
  1.1574 +
  1.1575 +    if (!isRTL)
  1.1576 +      currX += twistyRect.width;
  1.1577 +    remainingWidth -= twistyRect.width;    
  1.1578 +  }
  1.1579 +  
  1.1580 +  // Now test to see if the user hit the icon for the cell.
  1.1581 +  nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1.1582 +  
  1.1583 +  // Resolve style for the image.
  1.1584 +  nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  1.1585 +
  1.1586 +  nsRect iconSize = GetImageSize(aRowIndex, aColumn, false, imageContext);
  1.1587 +  nsMargin imageMargin;
  1.1588 +  imageContext->StyleMargin()->GetMargin(imageMargin);
  1.1589 +  iconSize.Inflate(imageMargin);
  1.1590 +  iconRect.width = iconSize.width;
  1.1591 +  if (isRTL)
  1.1592 +    iconRect.x = currX + remainingWidth - iconRect.width;
  1.1593 +
  1.1594 +  if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) {
  1.1595 +    // The user clicked on the image.
  1.1596 +    return nsCSSAnonBoxes::moztreeimage;
  1.1597 +  }
  1.1598 +
  1.1599 +  if (!isRTL)
  1.1600 +    currX += iconRect.width;
  1.1601 +  remainingWidth -= iconRect.width;    
  1.1602 +
  1.1603 +  nsAutoString cellText;
  1.1604 +  mView->GetCellText(aRowIndex, aColumn, cellText);
  1.1605 +  // We're going to measure this text so we need to ensure bidi is enabled if
  1.1606 +  // necessary
  1.1607 +  CheckTextForBidi(cellText);
  1.1608 +
  1.1609 +  nsRect textRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1.1610 +
  1.1611 +  nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  1.1612 +
  1.1613 +  nsMargin textMargin;
  1.1614 +  textContext->StyleMargin()->GetMargin(textMargin);
  1.1615 +  textRect.Deflate(textMargin);
  1.1616 +
  1.1617 +  AdjustForBorderPadding(textContext, textRect);
  1.1618 +
  1.1619 +  nsRefPtr<nsFontMetrics> fm;
  1.1620 +  nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  1.1621 +                                               getter_AddRefs(fm));
  1.1622 +  rc->SetFont(fm);
  1.1623 +
  1.1624 +  AdjustForCellText(cellText, aRowIndex, aColumn, *rc, textRect);
  1.1625 +
  1.1626 +  if (aX >= textRect.x && aX < textRect.x + textRect.width)
  1.1627 +    return nsCSSAnonBoxes::moztreecelltext;
  1.1628 +  else
  1.1629 +    return nsCSSAnonBoxes::moztreecell;
  1.1630 +}
  1.1631 +
  1.1632 +void
  1.1633 +nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow,
  1.1634 +                           nsTreeColumn** aCol, nsIAtom** aChildElt)
  1.1635 +{
  1.1636 +  *aCol = nullptr;
  1.1637 +  *aChildElt = nullptr;
  1.1638 +
  1.1639 +  *aRow = GetRowAt(aX, aY);
  1.1640 +  if (*aRow < 0)
  1.1641 +    return;
  1.1642 +
  1.1643 +  // Determine the column hit.
  1.1644 +  for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; 
  1.1645 +       currCol = currCol->GetNext()) {
  1.1646 +    nsRect cellRect;
  1.1647 +    nsresult rv = currCol->GetRect(this,
  1.1648 +                                   mInnerBox.y +
  1.1649 +                                         mRowHeight * (*aRow - mTopRowIndex),
  1.1650 +                                   mRowHeight,
  1.1651 +                                   &cellRect);
  1.1652 +    if (NS_FAILED(rv)) {
  1.1653 +      NS_NOTREACHED("column has no frame");
  1.1654 +      continue;
  1.1655 +    }
  1.1656 +
  1.1657 +    if (!OffsetForHorzScroll(cellRect, false))
  1.1658 +      continue;
  1.1659 +
  1.1660 +    if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) {
  1.1661 +      // We know the column hit now.
  1.1662 +      *aCol = currCol;
  1.1663 +
  1.1664 +      if (currCol->IsCycler())
  1.1665 +        // Cyclers contain only images.  Fill this in immediately and return.
  1.1666 +        *aChildElt = nsCSSAnonBoxes::moztreeimage;
  1.1667 +      else
  1.1668 +        *aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol);
  1.1669 +      break;
  1.1670 +    }
  1.1671 +  }
  1.1672 +}
  1.1673 +
  1.1674 +nsresult
  1.1675 +nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
  1.1676 +                              nsRenderingContext* aRenderingContext,
  1.1677 +                              nscoord& aDesiredSize, nscoord& aCurrentSize)
  1.1678 +{
  1.1679 +  NS_PRECONDITION(aCol, "aCol must not be null");
  1.1680 +  NS_PRECONDITION(aRenderingContext, "aRenderingContext must not be null");
  1.1681 +
  1.1682 +  // The rect for the current cell.
  1.1683 +  nscoord colWidth;
  1.1684 +  nsresult rv = aCol->GetWidthInTwips(this, &colWidth);
  1.1685 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1686 +
  1.1687 +  nsRect cellRect(0, 0, colWidth, mRowHeight);
  1.1688 +
  1.1689 +  int32_t overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
  1.1690 +  if (overflow > 0)
  1.1691 +    cellRect.width -= overflow;
  1.1692 +
  1.1693 +  // Adjust borders and padding for the cell.
  1.1694 +  nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1.1695 +  nsMargin bp(0,0,0,0);
  1.1696 +  GetBorderPadding(cellContext, bp);
  1.1697 +
  1.1698 +  aCurrentSize = cellRect.width;
  1.1699 +  aDesiredSize = bp.left + bp.right;
  1.1700 +
  1.1701 +  if (aCol->IsPrimary()) {
  1.1702 +    // If the current Column is a Primary, then we need to take into account 
  1.1703 +    // the indentation and possibly a twisty. 
  1.1704 +
  1.1705 +    // The amount of indentation is the indentation width (|mIndentation|) by the level.
  1.1706 +    int32_t level;
  1.1707 +    mView->GetLevel(aRow, &level);
  1.1708 +    aDesiredSize += mIndentation * level;
  1.1709 +    
  1.1710 +    // Find the twisty rect by computing its size.
  1.1711 +    nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1.1712 +
  1.1713 +    nsRect imageSize;
  1.1714 +    nsRect twistyRect(cellRect);
  1.1715 +    GetTwistyRect(aRow, aCol, imageSize, twistyRect, PresContext(),
  1.1716 +                  *aRenderingContext, twistyContext);
  1.1717 +
  1.1718 +    // Add in the margins of the twisty element.
  1.1719 +    nsMargin twistyMargin;
  1.1720 +    twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1.1721 +    twistyRect.Inflate(twistyMargin);
  1.1722 +
  1.1723 +    aDesiredSize += twistyRect.width;
  1.1724 +  }
  1.1725 +
  1.1726 +  nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  1.1727 +
  1.1728 +  // Account for the width of the cell image.
  1.1729 +  nsRect imageSize = GetImageSize(aRow, aCol, false, imageContext);
  1.1730 +  // Add in the margins of the cell image.
  1.1731 +  nsMargin imageMargin;
  1.1732 +  imageContext->StyleMargin()->GetMargin(imageMargin);
  1.1733 +  imageSize.Inflate(imageMargin);
  1.1734 +
  1.1735 +  aDesiredSize += imageSize.width;
  1.1736 +  
  1.1737 +  // Get the cell text.
  1.1738 +  nsAutoString cellText;
  1.1739 +  mView->GetCellText(aRow, aCol, cellText);
  1.1740 +  // We're going to measure this text so we need to ensure bidi is enabled if
  1.1741 +  // necessary
  1.1742 +  CheckTextForBidi(cellText);
  1.1743 +
  1.1744 +  nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  1.1745 +
  1.1746 +  // Get the borders and padding for the text.
  1.1747 +  GetBorderPadding(textContext, bp);
  1.1748 +
  1.1749 +  nsRefPtr<nsFontMetrics> fm;
  1.1750 +  nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  1.1751 +                                               getter_AddRefs(fm));
  1.1752 +  aRenderingContext->SetFont(fm);
  1.1753 +
  1.1754 +  // Get the width of the text itself
  1.1755 +  nscoord width =
  1.1756 +    nsLayoutUtils::GetStringWidth(this, aRenderingContext, cellText.get(), cellText.Length());
  1.1757 +  nscoord totalTextWidth = width + bp.left + bp.right;
  1.1758 +  aDesiredSize += totalTextWidth;
  1.1759 +  return NS_OK;
  1.1760 +}
  1.1761 +
  1.1762 +nsresult
  1.1763 +nsTreeBodyFrame::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *_retval)
  1.1764 +{  
  1.1765 +  nscoord currentSize, desiredSize;
  1.1766 +  nsresult rv;
  1.1767 +
  1.1768 +  nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
  1.1769 +  if (!col)
  1.1770 +    return NS_ERROR_INVALID_ARG;
  1.1771 +
  1.1772 +  nsRefPtr<nsRenderingContext> rc =
  1.1773 +    PresContext()->PresShell()->CreateReferenceRenderingContext();
  1.1774 +
  1.1775 +  rv = GetCellWidth(aRow, col, rc, desiredSize, currentSize);
  1.1776 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1777 +
  1.1778 +  *_retval = desiredSize > currentSize;
  1.1779 +
  1.1780 +  return NS_OK;
  1.1781 +}
  1.1782 +
  1.1783 +void
  1.1784 +nsTreeBodyFrame::MarkDirtyIfSelect()
  1.1785 +{
  1.1786 +  nsIContent* baseElement = GetBaseElement();
  1.1787 +
  1.1788 +  if (baseElement && baseElement->Tag() == nsGkAtoms::select &&
  1.1789 +      baseElement->IsHTML()) {
  1.1790 +    // If we are an intrinsically sized select widget, we may need to
  1.1791 +    // resize, if the widest item was removed or a new item was added.
  1.1792 +    // XXX optimize this more
  1.1793 +
  1.1794 +    mStringWidth = -1;
  1.1795 +    PresContext()->PresShell()->FrameNeedsReflow(this,
  1.1796 +                                                 nsIPresShell::eTreeChange,
  1.1797 +                                                 NS_FRAME_IS_DIRTY);
  1.1798 +  }
  1.1799 +}
  1.1800 +
  1.1801 +nsresult
  1.1802 +nsTreeBodyFrame::CreateTimer(const LookAndFeel::IntID aID,
  1.1803 +                             nsTimerCallbackFunc aFunc, int32_t aType,
  1.1804 +                             nsITimer** aTimer)
  1.1805 +{
  1.1806 +  // Get the delay from the look and feel service.
  1.1807 +  int32_t delay = LookAndFeel::GetInt(aID, 0);
  1.1808 +
  1.1809 +  nsCOMPtr<nsITimer> timer;
  1.1810 +
  1.1811 +  // Create a new timer only if the delay is greater than zero.
  1.1812 +  // Zero value means that this feature is completely disabled.
  1.1813 +  if (delay > 0) {
  1.1814 +    timer = do_CreateInstance("@mozilla.org/timer;1");
  1.1815 +    if (timer)
  1.1816 +      timer->InitWithFuncCallback(aFunc, this, delay, aType);
  1.1817 +  }
  1.1818 +
  1.1819 +  NS_IF_ADDREF(*aTimer = timer);
  1.1820 +
  1.1821 +  return NS_OK;
  1.1822 +}
  1.1823 +
  1.1824 +nsresult
  1.1825 +nsTreeBodyFrame::RowCountChanged(int32_t aIndex, int32_t aCount)
  1.1826 +{
  1.1827 +  if (aCount == 0 || !mView)
  1.1828 +    return NS_OK; // Nothing to do.
  1.1829 +
  1.1830 +#ifdef ACCESSIBILITY
  1.1831 +  if (nsIPresShell::IsAccessibilityActive())
  1.1832 +    FireRowCountChangedEvent(aIndex, aCount);
  1.1833 +#endif
  1.1834 +
  1.1835 +  // Adjust our selection.
  1.1836 +  nsCOMPtr<nsITreeSelection> sel;
  1.1837 +  mView->GetSelection(getter_AddRefs(sel));
  1.1838 +  if (sel)
  1.1839 +    sel->AdjustSelection(aIndex, aCount);
  1.1840 +
  1.1841 +  if (mUpdateBatchNest)
  1.1842 +    return NS_OK;
  1.1843 +
  1.1844 +  mRowCount += aCount;
  1.1845 +#ifdef DEBUG
  1.1846 +  int32_t rowCount = mRowCount;
  1.1847 +  mView->GetRowCount(&rowCount);
  1.1848 +  NS_ASSERTION(rowCount == mRowCount, "row count did not change by the amount suggested, check caller");
  1.1849 +#endif
  1.1850 +
  1.1851 +  int32_t count = Abs(aCount);
  1.1852 +  int32_t last = LastVisibleRow();
  1.1853 +  if (aIndex >= mTopRowIndex && aIndex <= last)
  1.1854 +    InvalidateRange(aIndex, last);
  1.1855 +    
  1.1856 +  ScrollParts parts = GetScrollParts();
  1.1857 +
  1.1858 +  if (mTopRowIndex == 0) {    
  1.1859 +    // Just update the scrollbar and return.
  1.1860 +    if (FullScrollbarsUpdate(false)) {
  1.1861 +      MarkDirtyIfSelect();
  1.1862 +    }
  1.1863 +    return NS_OK;
  1.1864 +  }
  1.1865 +
  1.1866 +  bool needsInvalidation = false;
  1.1867 +  // Adjust our top row index.
  1.1868 +  if (aCount > 0) {
  1.1869 +    if (mTopRowIndex > aIndex) {
  1.1870 +      // Rows came in above us.  Augment the top row index.
  1.1871 +      mTopRowIndex += aCount;
  1.1872 +    }
  1.1873 +  }
  1.1874 +  else if (aCount < 0) {
  1.1875 +    if (mTopRowIndex > aIndex+count-1) {
  1.1876 +      // No need to invalidate. The remove happened
  1.1877 +      // completely above us (offscreen).
  1.1878 +      mTopRowIndex -= count;
  1.1879 +    }
  1.1880 +    else if (mTopRowIndex >= aIndex) {
  1.1881 +      // This is a full-blown invalidate.
  1.1882 +      if (mTopRowIndex + mPageLength > mRowCount - 1) {
  1.1883 +        mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
  1.1884 +      }
  1.1885 +      needsInvalidation = true;
  1.1886 +    }
  1.1887 +  }
  1.1888 +
  1.1889 +  if (FullScrollbarsUpdate(needsInvalidation)) {
  1.1890 +    MarkDirtyIfSelect();
  1.1891 +  }
  1.1892 +  return NS_OK;
  1.1893 +}
  1.1894 +
  1.1895 +nsresult
  1.1896 +nsTreeBodyFrame::BeginUpdateBatch()
  1.1897 +{
  1.1898 +  ++mUpdateBatchNest;
  1.1899 +
  1.1900 +  return NS_OK;
  1.1901 +}
  1.1902 +
  1.1903 +nsresult
  1.1904 +nsTreeBodyFrame::EndUpdateBatch()
  1.1905 +{
  1.1906 +  NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
  1.1907 +
  1.1908 +  if (--mUpdateBatchNest == 0) {
  1.1909 +    if (mView) {
  1.1910 +      Invalidate();
  1.1911 +      int32_t countBeforeUpdate = mRowCount;
  1.1912 +      mView->GetRowCount(&mRowCount);
  1.1913 +      if (countBeforeUpdate != mRowCount) {
  1.1914 +        if (mTopRowIndex + mPageLength > mRowCount - 1) {
  1.1915 +          mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
  1.1916 +        }
  1.1917 +        FullScrollbarsUpdate(false);
  1.1918 +      }
  1.1919 +    }
  1.1920 +  }
  1.1921 +
  1.1922 +  return NS_OK;
  1.1923 +}
  1.1924 +
  1.1925 +void
  1.1926 +nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex, nsTreeColumn* aCol)
  1.1927 +{
  1.1928 +  NS_PRECONDITION(!aCol || aCol->GetFrame(), "invalid column passed");
  1.1929 +  mScratchArray.Clear();
  1.1930 +  
  1.1931 +  // focus
  1.1932 +  if (mFocused)
  1.1933 +    mScratchArray.AppendElement(nsGkAtoms::focus);
  1.1934 +
  1.1935 +  // sort
  1.1936 +  bool sorted = false;
  1.1937 +  mView->IsSorted(&sorted);
  1.1938 +  if (sorted)
  1.1939 +    mScratchArray.AppendElement(nsGkAtoms::sorted);
  1.1940 +
  1.1941 +  // drag session
  1.1942 +  if (mSlots && mSlots->mIsDragging)
  1.1943 +    mScratchArray.AppendElement(nsGkAtoms::dragSession);
  1.1944 +
  1.1945 +  if (aRowIndex != -1) {
  1.1946 +    if (aRowIndex == mMouseOverRow)
  1.1947 +      mScratchArray.AppendElement(nsGkAtoms::hover);
  1.1948 +  
  1.1949 +    nsCOMPtr<nsITreeSelection> selection;
  1.1950 +    mView->GetSelection(getter_AddRefs(selection));
  1.1951 +
  1.1952 +    if (selection) {
  1.1953 +      // selected
  1.1954 +      bool isSelected;
  1.1955 +      selection->IsSelected(aRowIndex, &isSelected);
  1.1956 +      if (isSelected)
  1.1957 +        mScratchArray.AppendElement(nsGkAtoms::selected);
  1.1958 +
  1.1959 +      // current
  1.1960 +      int32_t currentIndex;
  1.1961 +      selection->GetCurrentIndex(&currentIndex);
  1.1962 +      if (aRowIndex == currentIndex)
  1.1963 +        mScratchArray.AppendElement(nsGkAtoms::current);
  1.1964 +  
  1.1965 +      // active
  1.1966 +      if (aCol) {
  1.1967 +        nsCOMPtr<nsITreeColumn> currentColumn;
  1.1968 +        selection->GetCurrentColumn(getter_AddRefs(currentColumn));
  1.1969 +        if (aCol == currentColumn)
  1.1970 +          mScratchArray.AppendElement(nsGkAtoms::active);
  1.1971 +      }
  1.1972 +    }
  1.1973 +
  1.1974 +    // container or leaf
  1.1975 +    bool isContainer = false;
  1.1976 +    mView->IsContainer(aRowIndex, &isContainer);
  1.1977 +    if (isContainer) {
  1.1978 +      mScratchArray.AppendElement(nsGkAtoms::container);
  1.1979 +
  1.1980 +      // open or closed
  1.1981 +      bool isOpen = false;
  1.1982 +      mView->IsContainerOpen(aRowIndex, &isOpen);
  1.1983 +      if (isOpen)
  1.1984 +        mScratchArray.AppendElement(nsGkAtoms::open);
  1.1985 +      else
  1.1986 +        mScratchArray.AppendElement(nsGkAtoms::closed);
  1.1987 +    }
  1.1988 +    else {
  1.1989 +      mScratchArray.AppendElement(nsGkAtoms::leaf);
  1.1990 +    }
  1.1991 +
  1.1992 +    // drop orientation
  1.1993 +    if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) {
  1.1994 +      if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE)
  1.1995 +        mScratchArray.AppendElement(nsGkAtoms::dropBefore);
  1.1996 +      else if (mSlots->mDropOrient == nsITreeView::DROP_ON)
  1.1997 +        mScratchArray.AppendElement(nsGkAtoms::dropOn);
  1.1998 +      else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
  1.1999 +        mScratchArray.AppendElement(nsGkAtoms::dropAfter);
  1.2000 +    }
  1.2001 +
  1.2002 +    // odd or even
  1.2003 +    if (aRowIndex % 2)
  1.2004 +      mScratchArray.AppendElement(nsGkAtoms::odd);
  1.2005 +    else
  1.2006 +      mScratchArray.AppendElement(nsGkAtoms::even);
  1.2007 +
  1.2008 +    nsIContent* baseContent = GetBaseElement();
  1.2009 +    if (baseContent && baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::editing))
  1.2010 +      mScratchArray.AppendElement(nsGkAtoms::editing);
  1.2011 +
  1.2012 +    // multiple columns
  1.2013 +    if (mColumns->GetColumnAt(1))
  1.2014 +      mScratchArray.AppendElement(nsGkAtoms::multicol);
  1.2015 +  }
  1.2016 +
  1.2017 +  if (aCol) {
  1.2018 +    mScratchArray.AppendElement(aCol->GetAtom());
  1.2019 +
  1.2020 +    if (aCol->IsPrimary())
  1.2021 +      mScratchArray.AppendElement(nsGkAtoms::primary);
  1.2022 +
  1.2023 +    if (aCol->GetType() == nsITreeColumn::TYPE_CHECKBOX) {
  1.2024 +      mScratchArray.AppendElement(nsGkAtoms::checkbox);
  1.2025 +
  1.2026 +      if (aRowIndex != -1) {
  1.2027 +        nsAutoString value;
  1.2028 +        mView->GetCellValue(aRowIndex, aCol, value);
  1.2029 +        if (value.EqualsLiteral("true"))
  1.2030 +          mScratchArray.AppendElement(nsGkAtoms::checked);
  1.2031 +      }
  1.2032 +    }
  1.2033 +    else if (aCol->GetType() == nsITreeColumn::TYPE_PROGRESSMETER) {
  1.2034 +      mScratchArray.AppendElement(nsGkAtoms::progressmeter);
  1.2035 +
  1.2036 +      if (aRowIndex != -1) {
  1.2037 +        int32_t state;
  1.2038 +        mView->GetProgressMode(aRowIndex, aCol, &state);
  1.2039 +        if (state == nsITreeView::PROGRESS_NORMAL)
  1.2040 +          mScratchArray.AppendElement(nsGkAtoms::progressNormal);
  1.2041 +        else if (state == nsITreeView::PROGRESS_UNDETERMINED)
  1.2042 +          mScratchArray.AppendElement(nsGkAtoms::progressUndetermined);
  1.2043 +      }
  1.2044 +    }
  1.2045 +
  1.2046 +    // Read special properties from attributes on the column content node
  1.2047 +    if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
  1.2048 +                                    nsGkAtoms::insertbefore,
  1.2049 +                                    nsGkAtoms::_true, eCaseMatters))
  1.2050 +      mScratchArray.AppendElement(nsGkAtoms::insertbefore);
  1.2051 +    if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
  1.2052 +                                    nsGkAtoms::insertafter,
  1.2053 +                                    nsGkAtoms::_true, eCaseMatters))
  1.2054 +      mScratchArray.AppendElement(nsGkAtoms::insertafter);
  1.2055 +  }
  1.2056 +}
  1.2057 +
  1.2058 +nsITheme*
  1.2059 +nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex,
  1.2060 +                               nsTreeColumn* aColumn,
  1.2061 +                               nsRect& aImageRect,
  1.2062 +                               nsRect& aTwistyRect,
  1.2063 +                               nsPresContext* aPresContext,
  1.2064 +                               nsRenderingContext& aRenderingContext,
  1.2065 +                               nsStyleContext* aTwistyContext)
  1.2066 +{
  1.2067 +  // The twisty rect extends all the way to the end of the cell.  This is incorrect.  We need to
  1.2068 +  // determine the twisty rect's true width.  This is done by examining the style context for
  1.2069 +  // a width first.  If it has one, we use that.  If it doesn't, we use the image's natural width.
  1.2070 +  // If the image hasn't loaded and if no width is specified, then we just bail. If there is
  1.2071 +  // a -moz-appearance involved, adjust the rect by the minimum widget size provided by
  1.2072 +  // the theme implementation.
  1.2073 +  aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext);
  1.2074 +  if (aImageRect.height > aTwistyRect.height)
  1.2075 +    aImageRect.height = aTwistyRect.height;
  1.2076 +  if (aImageRect.width > aTwistyRect.width)
  1.2077 +    aImageRect.width = aTwistyRect.width;
  1.2078 +  else
  1.2079 +    aTwistyRect.width = aImageRect.width;
  1.2080 +
  1.2081 +  bool useTheme = false;
  1.2082 +  nsITheme *theme = nullptr;
  1.2083 +  const nsStyleDisplay* twistyDisplayData = aTwistyContext->StyleDisplay();
  1.2084 +  if (twistyDisplayData->mAppearance) {
  1.2085 +    theme = aPresContext->GetTheme();
  1.2086 +    if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, twistyDisplayData->mAppearance))
  1.2087 +      useTheme = true;
  1.2088 +  }
  1.2089 +
  1.2090 +  if (useTheme) {
  1.2091 +    nsIntSize minTwistySizePx(0,0);
  1.2092 +    bool canOverride = true;
  1.2093 +    theme->GetMinimumWidgetSize(&aRenderingContext, this, twistyDisplayData->mAppearance,
  1.2094 +                                &minTwistySizePx, &canOverride);
  1.2095 +
  1.2096 +    // GMWS() returns size in pixels, we need to convert it back to app units
  1.2097 +    nsSize minTwistySize;
  1.2098 +    minTwistySize.width = aPresContext->DevPixelsToAppUnits(minTwistySizePx.width);
  1.2099 +    minTwistySize.height = aPresContext->DevPixelsToAppUnits(minTwistySizePx.height);
  1.2100 +
  1.2101 +    if (aTwistyRect.width < minTwistySize.width || !canOverride)
  1.2102 +      aTwistyRect.width = minTwistySize.width;
  1.2103 +  }
  1.2104 +
  1.2105 +  return useTheme ? theme : nullptr;
  1.2106 +}
  1.2107 +
  1.2108 +nsresult
  1.2109 +nsTreeBodyFrame::GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
  1.2110 +                          nsStyleContext* aStyleContext, bool& aAllowImageRegions, imgIContainer** aResult)
  1.2111 +{
  1.2112 +  *aResult = nullptr;
  1.2113 +
  1.2114 +  nsAutoString imageSrc;
  1.2115 +  mView->GetImageSrc(aRowIndex, aCol, imageSrc);
  1.2116 +  nsRefPtr<imgRequestProxy> styleRequest;
  1.2117 +  if (!aUseContext && !imageSrc.IsEmpty()) {
  1.2118 +    aAllowImageRegions = false;
  1.2119 +  }
  1.2120 +  else {
  1.2121 +    // Obtain the URL from the style context.
  1.2122 +    aAllowImageRegions = true;
  1.2123 +    styleRequest = aStyleContext->StyleList()->GetListStyleImage();
  1.2124 +    if (!styleRequest)
  1.2125 +      return NS_OK;
  1.2126 +    nsCOMPtr<nsIURI> uri;
  1.2127 +    styleRequest->GetURI(getter_AddRefs(uri));
  1.2128 +    nsAutoCString spec;
  1.2129 +    uri->GetSpec(spec);
  1.2130 +    CopyUTF8toUTF16(spec, imageSrc);
  1.2131 +  }
  1.2132 +
  1.2133 +  // Look the image up in our cache.
  1.2134 +  nsTreeImageCacheEntry entry;
  1.2135 +  if (mImageCache.Get(imageSrc, &entry)) {
  1.2136 +    // Find out if the image has loaded.
  1.2137 +    uint32_t status;
  1.2138 +    imgIRequest *imgReq = entry.request;
  1.2139 +    imgReq->GetImageStatus(&status);
  1.2140 +    imgReq->GetImage(aResult); // We hand back the image here.  The GetImage call addrefs *aResult.
  1.2141 +    bool animated = true; // Assuming animated is the safe option
  1.2142 +
  1.2143 +    // We can only call GetAnimated if we're decoded
  1.2144 +    if (*aResult && (status & imgIRequest::STATUS_DECODE_COMPLETE))
  1.2145 +      (*aResult)->GetAnimated(&animated);
  1.2146 +
  1.2147 +    if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || animated) {
  1.2148 +      // We either aren't done loading, or we're animating. Add our row as a listener for invalidations.
  1.2149 +      nsCOMPtr<imgINotificationObserver> obs;
  1.2150 +      imgReq->GetNotificationObserver(getter_AddRefs(obs));
  1.2151 +
  1.2152 +      if (obs) {
  1.2153 +        static_cast<nsTreeImageListener*> (obs.get())->AddCell(aRowIndex, aCol);
  1.2154 +      }
  1.2155 +
  1.2156 +      return NS_OK;
  1.2157 +    }
  1.2158 +  }
  1.2159 +
  1.2160 +  if (!*aResult) {
  1.2161 +    // Create a new nsTreeImageListener object and pass it our row and column
  1.2162 +    // information.
  1.2163 +    nsTreeImageListener* listener = new nsTreeImageListener(this);
  1.2164 +    if (!listener)
  1.2165 +      return NS_ERROR_OUT_OF_MEMORY;
  1.2166 +
  1.2167 +    if (!mCreatedListeners.PutEntry(listener)) {
  1.2168 +      return NS_ERROR_FAILURE;
  1.2169 +    }
  1.2170 +
  1.2171 +    listener->AddCell(aRowIndex, aCol);
  1.2172 +    nsCOMPtr<imgINotificationObserver> imgNotificationObserver = listener;
  1.2173 +
  1.2174 +    nsRefPtr<imgRequestProxy> imageRequest;
  1.2175 +    if (styleRequest) {
  1.2176 +      styleRequest->Clone(imgNotificationObserver, getter_AddRefs(imageRequest));
  1.2177 +    } else {
  1.2178 +      nsIDocument* doc = mContent->GetDocument();
  1.2179 +      if (!doc)
  1.2180 +        // The page is currently being torn down.  Why bother.
  1.2181 +        return NS_ERROR_FAILURE;
  1.2182 +
  1.2183 +      nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
  1.2184 +
  1.2185 +      nsCOMPtr<nsIURI> srcURI;
  1.2186 +      nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcURI),
  1.2187 +                                                imageSrc,
  1.2188 +                                                doc,
  1.2189 +                                                baseURI);
  1.2190 +      if (!srcURI)
  1.2191 +        return NS_ERROR_FAILURE;
  1.2192 +
  1.2193 +      // XXXbz what's the origin principal for this stuff that comes from our
  1.2194 +      // view?  I guess we should assume that it's the node's principal...
  1.2195 +      if (nsContentUtils::CanLoadImage(srcURI, mContent, doc,
  1.2196 +                                       mContent->NodePrincipal())) {
  1.2197 +        nsresult rv = nsContentUtils::LoadImage(srcURI,
  1.2198 +                                                doc,
  1.2199 +                                                mContent->NodePrincipal(),
  1.2200 +                                                doc->GetDocumentURI(),
  1.2201 +                                                imgNotificationObserver,
  1.2202 +                                                nsIRequest::LOAD_NORMAL,
  1.2203 +                                                EmptyString(),
  1.2204 +                                                getter_AddRefs(imageRequest));
  1.2205 +        NS_ENSURE_SUCCESS(rv, rv);
  1.2206 +                                  
  1.2207 +      }
  1.2208 +    }
  1.2209 +    listener->UnsuppressInvalidation();
  1.2210 +
  1.2211 +    if (!imageRequest)
  1.2212 +      return NS_ERROR_FAILURE;
  1.2213 +
  1.2214 +    // We don't want discarding/decode-on-draw for xul images
  1.2215 +    imageRequest->StartDecoding();
  1.2216 +    imageRequest->LockImage();
  1.2217 +
  1.2218 +    // In a case it was already cached.
  1.2219 +    imageRequest->GetImage(aResult);
  1.2220 +    nsTreeImageCacheEntry cacheEntry(imageRequest, imgNotificationObserver);
  1.2221 +    mImageCache.Put(imageSrc, cacheEntry);
  1.2222 +  }
  1.2223 +  return NS_OK;
  1.2224 +}
  1.2225 +
  1.2226 +nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
  1.2227 +                                     nsStyleContext* aStyleContext)
  1.2228 +{
  1.2229 +  // XXX We should respond to visibility rules for collapsed vs. hidden.
  1.2230 +
  1.2231 +  // This method returns the width of the twisty INCLUDING borders and padding.
  1.2232 +  // It first checks the style context for a width.  If none is found, it tries to
  1.2233 +  // use the default image width for the twisty.  If no image is found, it defaults
  1.2234 +  // to border+padding.
  1.2235 +  nsRect r(0,0,0,0);
  1.2236 +  nsMargin bp(0,0,0,0);
  1.2237 +  GetBorderPadding(aStyleContext, bp);
  1.2238 +  r.Inflate(bp);
  1.2239 +
  1.2240 +  // Now r contains our border+padding info.  We now need to get our width and
  1.2241 +  // height.
  1.2242 +  bool needWidth = false;
  1.2243 +  bool needHeight = false;
  1.2244 +
  1.2245 +  // We have to load image even though we already have a size.
  1.2246 +  // Don't change this, otherwise things start to go crazy.
  1.2247 +  bool useImageRegion = true;
  1.2248 +  nsCOMPtr<imgIContainer> image;
  1.2249 +  GetImage(aRowIndex, aCol, aUseContext, aStyleContext, useImageRegion, getter_AddRefs(image));
  1.2250 +
  1.2251 +  const nsStylePosition* myPosition = aStyleContext->StylePosition();
  1.2252 +  const nsStyleList* myList = aStyleContext->StyleList();
  1.2253 +
  1.2254 +  if (useImageRegion) {
  1.2255 +    r.x += myList->mImageRegion.x;
  1.2256 +    r.y += myList->mImageRegion.y;
  1.2257 +  }
  1.2258 +
  1.2259 +  if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord)  {
  1.2260 +    int32_t val = myPosition->mWidth.GetCoordValue();
  1.2261 +    r.width += val;
  1.2262 +  }
  1.2263 +  else if (useImageRegion && myList->mImageRegion.width > 0)
  1.2264 +    r.width += myList->mImageRegion.width;
  1.2265 +  else 
  1.2266 +    needWidth = true;
  1.2267 +
  1.2268 +  if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)  {
  1.2269 +    int32_t val = myPosition->mHeight.GetCoordValue();
  1.2270 +    r.height += val;
  1.2271 +  }
  1.2272 +  else if (useImageRegion && myList->mImageRegion.height > 0)
  1.2273 +    r.height += myList->mImageRegion.height;
  1.2274 +  else 
  1.2275 +    needHeight = true;
  1.2276 +
  1.2277 +  if (image) {
  1.2278 +    if (needWidth || needHeight) {
  1.2279 +      // Get the natural image size.
  1.2280 +
  1.2281 +      if (needWidth) {
  1.2282 +        // Get the size from the image.
  1.2283 +        nscoord width;
  1.2284 +        image->GetWidth(&width);
  1.2285 +        r.width += nsPresContext::CSSPixelsToAppUnits(width); 
  1.2286 +      }
  1.2287 +    
  1.2288 +      if (needHeight) {
  1.2289 +        nscoord height;
  1.2290 +        image->GetHeight(&height);
  1.2291 +        r.height += nsPresContext::CSSPixelsToAppUnits(height); 
  1.2292 +      }
  1.2293 +    }
  1.2294 +  }
  1.2295 +
  1.2296 +  return r;
  1.2297 +}
  1.2298 +
  1.2299 +// GetImageDestSize returns the destination size of the image.
  1.2300 +// The width and height do not include borders and padding.
  1.2301 +// The width and height have not been adjusted to fit in the row height
  1.2302 +// or cell width.
  1.2303 +// The width and height reflect the destination size specified in CSS,
  1.2304 +// or the image region specified in CSS, or the natural size of the
  1.2305 +// image.
  1.2306 +// If only the destination width has been specified in CSS, the height is
  1.2307 +// calculated to maintain the aspect ratio of the image.
  1.2308 +// If only the destination height has been specified in CSS, the width is
  1.2309 +// calculated to maintain the aspect ratio of the image.
  1.2310 +nsSize
  1.2311 +nsTreeBodyFrame::GetImageDestSize(nsStyleContext* aStyleContext,
  1.2312 +                                  bool useImageRegion,
  1.2313 +                                  imgIContainer* image)
  1.2314 +{
  1.2315 +  nsSize size(0,0);
  1.2316 +
  1.2317 +  // We need to get the width and height.
  1.2318 +  bool needWidth = false;
  1.2319 +  bool needHeight = false;
  1.2320 +
  1.2321 +  // Get the style position to see if the CSS has specified the
  1.2322 +  // destination width/height.
  1.2323 +  const nsStylePosition* myPosition = aStyleContext->StylePosition();
  1.2324 +
  1.2325 +  if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
  1.2326 +    // CSS has specified the destination width.
  1.2327 +    size.width = myPosition->mWidth.GetCoordValue();
  1.2328 +  }
  1.2329 +  else {
  1.2330 +    // We'll need to get the width of the image/region.
  1.2331 +    needWidth = true;
  1.2332 +  }
  1.2333 +
  1.2334 +  if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)  {
  1.2335 +    // CSS has specified the destination height.
  1.2336 +    size.height = myPosition->mHeight.GetCoordValue();
  1.2337 +  }
  1.2338 +  else {
  1.2339 +    // We'll need to get the height of the image/region.
  1.2340 +    needHeight = true;
  1.2341 +  }
  1.2342 +
  1.2343 +  if (needWidth || needHeight) {
  1.2344 +    // We need to get the size of the image/region.
  1.2345 +    nsSize imageSize(0,0);
  1.2346 +
  1.2347 +    const nsStyleList* myList = aStyleContext->StyleList();
  1.2348 +
  1.2349 +    if (useImageRegion && myList->mImageRegion.width > 0) {
  1.2350 +      // CSS has specified an image region.
  1.2351 +      // Use the width of the image region.
  1.2352 +      imageSize.width = myList->mImageRegion.width;
  1.2353 +    }
  1.2354 +    else if (image) {
  1.2355 +      nscoord width;
  1.2356 +      image->GetWidth(&width);
  1.2357 +      imageSize.width = nsPresContext::CSSPixelsToAppUnits(width);
  1.2358 +    }
  1.2359 +
  1.2360 +    if (useImageRegion && myList->mImageRegion.height > 0) {
  1.2361 +      // CSS has specified an image region.
  1.2362 +      // Use the height of the image region.
  1.2363 +      imageSize.height = myList->mImageRegion.height;
  1.2364 +    }
  1.2365 +    else if (image) {
  1.2366 +      nscoord height;
  1.2367 +      image->GetHeight(&height);
  1.2368 +      imageSize.height = nsPresContext::CSSPixelsToAppUnits(height);
  1.2369 +    }
  1.2370 +
  1.2371 +    if (needWidth) {
  1.2372 +      if (!needHeight && imageSize.height != 0) {
  1.2373 +        // The CSS specified the destination height, but not the destination
  1.2374 +        // width. We need to calculate the width so that we maintain the
  1.2375 +        // image's aspect ratio.
  1.2376 +        size.width = imageSize.width * size.height / imageSize.height;
  1.2377 +      }
  1.2378 +      else {
  1.2379 +        size.width = imageSize.width;
  1.2380 +      }
  1.2381 +    }
  1.2382 +
  1.2383 +    if (needHeight) {
  1.2384 +      if (!needWidth && imageSize.width != 0) {
  1.2385 +        // The CSS specified the destination width, but not the destination
  1.2386 +        // height. We need to calculate the height so that we maintain the
  1.2387 +        // image's aspect ratio.
  1.2388 +        size.height = imageSize.height * size.width / imageSize.width;
  1.2389 +      }
  1.2390 +      else {
  1.2391 +        size.height = imageSize.height;
  1.2392 +      }
  1.2393 +    }
  1.2394 +  }
  1.2395 +
  1.2396 +  return size;
  1.2397 +}
  1.2398 +
  1.2399 +// GetImageSourceRect returns the source rectangle of the image to be
  1.2400 +// displayed.
  1.2401 +// The width and height reflect the image region specified in CSS, or
  1.2402 +// the natural size of the image.
  1.2403 +// The width and height do not include borders and padding.
  1.2404 +// The width and height do not reflect the destination size specified
  1.2405 +// in CSS.
  1.2406 +nsRect
  1.2407 +nsTreeBodyFrame::GetImageSourceRect(nsStyleContext* aStyleContext,
  1.2408 +                                    bool useImageRegion,
  1.2409 +                                    imgIContainer* image)
  1.2410 +{
  1.2411 +  nsRect r(0,0,0,0);
  1.2412 +
  1.2413 +  const nsStyleList* myList = aStyleContext->StyleList();
  1.2414 +
  1.2415 +  if (useImageRegion &&
  1.2416 +      (myList->mImageRegion.width > 0 || myList->mImageRegion.height > 0)) {
  1.2417 +    // CSS has specified an image region.
  1.2418 +    r = myList->mImageRegion;
  1.2419 +  }
  1.2420 +  else if (image) {
  1.2421 +    // Use the actual image size.
  1.2422 +    nscoord coord;
  1.2423 +    image->GetWidth(&coord);
  1.2424 +    r.width = nsPresContext::CSSPixelsToAppUnits(coord);
  1.2425 +    image->GetHeight(&coord);
  1.2426 +    r.height = nsPresContext::CSSPixelsToAppUnits(coord);
  1.2427 +  }
  1.2428 +
  1.2429 +  return r;
  1.2430 +}
  1.2431 +
  1.2432 +int32_t nsTreeBodyFrame::GetRowHeight()
  1.2433 +{
  1.2434 +  // Look up the correct height.  It is equal to the specified height
  1.2435 +  // + the specified margins.
  1.2436 +  mScratchArray.Clear();
  1.2437 +  nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
  1.2438 +  if (rowContext) {
  1.2439 +    const nsStylePosition* myPosition = rowContext->StylePosition();
  1.2440 +
  1.2441 +    nscoord minHeight = 0;
  1.2442 +    if (myPosition->mMinHeight.GetUnit() == eStyleUnit_Coord)
  1.2443 +      minHeight = myPosition->mMinHeight.GetCoordValue();
  1.2444 +
  1.2445 +    nscoord height = 0;
  1.2446 +    if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)
  1.2447 +      height = myPosition->mHeight.GetCoordValue();
  1.2448 +
  1.2449 +    if (height < minHeight)
  1.2450 +      height = minHeight;
  1.2451 +
  1.2452 +    if (height > 0) {
  1.2453 +      height = nsPresContext::AppUnitsToIntCSSPixels(height);
  1.2454 +      height += height % 2;
  1.2455 +      height = nsPresContext::CSSPixelsToAppUnits(height);
  1.2456 +
  1.2457 +      // XXX Check box-sizing to determine if border/padding should augment the height
  1.2458 +      // Inflate the height by our margins.
  1.2459 +      nsRect rowRect(0,0,0,height);
  1.2460 +      nsMargin rowMargin;
  1.2461 +      rowContext->StyleMargin()->GetMargin(rowMargin);
  1.2462 +      rowRect.Inflate(rowMargin);
  1.2463 +      height = rowRect.height;
  1.2464 +      return height;
  1.2465 +    }
  1.2466 +  }
  1.2467 +
  1.2468 +  return nsPresContext::CSSPixelsToAppUnits(18); // As good a default as any.
  1.2469 +}
  1.2470 +
  1.2471 +int32_t nsTreeBodyFrame::GetIndentation()
  1.2472 +{
  1.2473 +  // Look up the correct indentation.  It is equal to the specified indentation width.
  1.2474 +  mScratchArray.Clear();
  1.2475 +  nsStyleContext* indentContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeindentation);
  1.2476 +  if (indentContext) {
  1.2477 +    const nsStylePosition* myPosition = indentContext->StylePosition();
  1.2478 +    if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord)  {
  1.2479 +      nscoord val = myPosition->mWidth.GetCoordValue();
  1.2480 +      return val;
  1.2481 +    }
  1.2482 +  }
  1.2483 +
  1.2484 +  return nsPresContext::CSSPixelsToAppUnits(16); // As good a default as any.
  1.2485 +}
  1.2486 +
  1.2487 +void nsTreeBodyFrame::CalcInnerBox()
  1.2488 +{
  1.2489 +  mInnerBox.SetRect(0, 0, mRect.width, mRect.height);
  1.2490 +  AdjustForBorderPadding(mStyleContext, mInnerBox);
  1.2491 +}
  1.2492 +
  1.2493 +nscoord
  1.2494 +nsTreeBodyFrame::CalcHorzWidth(const ScrollParts& aParts)
  1.2495 +{
  1.2496 +  // Compute the adjustment to the last column. This varies depending on the
  1.2497 +  // visibility of the columnpicker and the scrollbar.
  1.2498 +  if (aParts.mColumnsFrame)
  1.2499 +    mAdjustWidth = mRect.width - aParts.mColumnsFrame->GetRect().width;
  1.2500 +  else
  1.2501 +    mAdjustWidth = 0;
  1.2502 +
  1.2503 +  nscoord width = 0;
  1.2504 +
  1.2505 +  // We calculate this from the scrollable frame, so that it 
  1.2506 +  // properly covers all contingencies of what could be 
  1.2507 +  // scrollable (columns, body, etc...)
  1.2508 +
  1.2509 +  if (aParts.mColumnsScrollFrame) {
  1.2510 +    width = aParts.mColumnsScrollFrame->GetScrollRange().width +
  1.2511 +      aParts.mColumnsScrollFrame->GetScrollPortRect().width;
  1.2512 +  }
  1.2513 +
  1.2514 +  // If no horz scrolling periphery is present, then just return our width
  1.2515 +  if (width == 0)
  1.2516 +    width = mRect.width;
  1.2517 +
  1.2518 +  return width;
  1.2519 +}
  1.2520 +
  1.2521 +nsresult
  1.2522 +nsTreeBodyFrame::GetCursor(const nsPoint& aPoint,
  1.2523 +                           nsIFrame::Cursor& aCursor)
  1.2524 +{
  1.2525 +  // Check the GetScriptHandlingObject so we don't end up running code when
  1.2526 +  // the document is a zombie.
  1.2527 +  bool dummy;
  1.2528 +  if (mView && GetContent()->GetCurrentDoc()->GetScriptHandlingObject(dummy)) {
  1.2529 +    int32_t row;
  1.2530 +    nsTreeColumn* col;
  1.2531 +    nsIAtom* child;
  1.2532 +    GetCellAt(aPoint.x, aPoint.y, &row, &col, &child);
  1.2533 +
  1.2534 +    if (child) {
  1.2535 +      // Our scratch array is already prefilled.
  1.2536 +      nsStyleContext* childContext = GetPseudoStyleContext(child);
  1.2537 +
  1.2538 +      FillCursorInformationFromStyle(childContext->StyleUserInterface(),
  1.2539 +                                     aCursor);
  1.2540 +      if (aCursor.mCursor == NS_STYLE_CURSOR_AUTO)
  1.2541 +        aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
  1.2542 +
  1.2543 +      return NS_OK;
  1.2544 +    }
  1.2545 +  }
  1.2546 +
  1.2547 +  return nsLeafBoxFrame::GetCursor(aPoint, aCursor);
  1.2548 +}
  1.2549 +
  1.2550 +static uint32_t GetDropEffect(WidgetGUIEvent* aEvent)
  1.2551 +{
  1.2552 +  NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type");
  1.2553 +  WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
  1.2554 +  nsContentUtils::SetDataTransferInEvent(dragEvent);
  1.2555 +
  1.2556 +  uint32_t action = 0;
  1.2557 +  if (dragEvent->dataTransfer)
  1.2558 +    dragEvent->dataTransfer->GetDropEffectInt(&action);
  1.2559 +  return action;
  1.2560 +}
  1.2561 +
  1.2562 +nsresult
  1.2563 +nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext,
  1.2564 +                             WidgetGUIEvent* aEvent,
  1.2565 +                             nsEventStatus* aEventStatus)
  1.2566 +{
  1.2567 +  if (aEvent->message == NS_MOUSE_ENTER_SYNTH || aEvent->message == NS_MOUSE_MOVE) {
  1.2568 +    nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
  1.2569 +    int32_t xTwips = pt.x - mInnerBox.x;
  1.2570 +    int32_t yTwips = pt.y - mInnerBox.y;
  1.2571 +    int32_t newrow = GetRowAt(xTwips, yTwips);
  1.2572 +    if (mMouseOverRow != newrow) {
  1.2573 +      // redraw the old and the new row
  1.2574 +      if (mMouseOverRow != -1)
  1.2575 +        InvalidateRow(mMouseOverRow);
  1.2576 +      mMouseOverRow = newrow;
  1.2577 +      if (mMouseOverRow != -1)
  1.2578 +        InvalidateRow(mMouseOverRow);
  1.2579 +    }
  1.2580 +  }
  1.2581 +  else if (aEvent->message == NS_MOUSE_EXIT_SYNTH) {
  1.2582 +    if (mMouseOverRow != -1) {
  1.2583 +      InvalidateRow(mMouseOverRow);
  1.2584 +      mMouseOverRow = -1;
  1.2585 +    }
  1.2586 +  }
  1.2587 +  else if (aEvent->message == NS_DRAGDROP_ENTER) {
  1.2588 +    if (!mSlots)
  1.2589 +      mSlots = new Slots();
  1.2590 +
  1.2591 +    // Cache several things we'll need throughout the course of our work. These
  1.2592 +    // will all get released on a drag exit.
  1.2593 +
  1.2594 +    if (mSlots->mTimer) {
  1.2595 +      mSlots->mTimer->Cancel();
  1.2596 +      mSlots->mTimer = nullptr;
  1.2597 +    }
  1.2598 +
  1.2599 +    // Cache the drag session.
  1.2600 +    mSlots->mIsDragging = true;
  1.2601 +    mSlots->mDropRow = -1;
  1.2602 +    mSlots->mDropOrient = -1;
  1.2603 +    mSlots->mDragAction = GetDropEffect(aEvent);
  1.2604 +  }
  1.2605 +  else if (aEvent->message == NS_DRAGDROP_OVER) {
  1.2606 +    // The mouse is hovering over this tree. If we determine things are
  1.2607 +    // different from the last time, invalidate the drop feedback at the old
  1.2608 +    // position, query the view to see if the current location is droppable,
  1.2609 +    // and then invalidate the drop feedback at the new location if it is.
  1.2610 +    // The mouse may or may not have changed position from the last time
  1.2611 +    // we were called, so optimize out a lot of the extra notifications by
  1.2612 +    // checking if anything changed first. For drop feedback we use drop,
  1.2613 +    // dropBefore and dropAfter property.
  1.2614 +
  1.2615 +    if (!mView || !mSlots)
  1.2616 +      return NS_OK;
  1.2617 +
  1.2618 +    // Save last values, we will need them.
  1.2619 +    int32_t lastDropRow = mSlots->mDropRow;
  1.2620 +    int16_t lastDropOrient = mSlots->mDropOrient;
  1.2621 +#ifndef XP_MACOSX
  1.2622 +    int16_t lastScrollLines = mSlots->mScrollLines;
  1.2623 +#endif
  1.2624 +
  1.2625 +    // Find out the current drag action
  1.2626 +    uint32_t lastDragAction = mSlots->mDragAction;
  1.2627 +    mSlots->mDragAction = GetDropEffect(aEvent);
  1.2628 +
  1.2629 +    // Compute the row mouse is over and the above/below/on state.
  1.2630 +    // Below we'll use this to see if anything changed.
  1.2631 +    // Also check if we want to auto-scroll.
  1.2632 +    ComputeDropPosition(aEvent, &mSlots->mDropRow, &mSlots->mDropOrient, &mSlots->mScrollLines);
  1.2633 +
  1.2634 +    // While we're here, handle tracking of scrolling during a drag.
  1.2635 +    if (mSlots->mScrollLines) {
  1.2636 +      if (mSlots->mDropAllowed) {
  1.2637 +        // Invalidate primary cell at old location.
  1.2638 +        mSlots->mDropAllowed = false;
  1.2639 +        InvalidateDropFeedback(lastDropRow, lastDropOrient);
  1.2640 +      }
  1.2641 +#ifdef XP_MACOSX
  1.2642 +      ScrollByLines(mSlots->mScrollLines);
  1.2643 +#else
  1.2644 +      if (!lastScrollLines) {
  1.2645 +        // Cancel any previously initialized timer.
  1.2646 +        if (mSlots->mTimer) {
  1.2647 +          mSlots->mTimer->Cancel();
  1.2648 +          mSlots->mTimer = nullptr;
  1.2649 +        }
  1.2650 +
  1.2651 +        // Set a timer to trigger the tree scrolling.
  1.2652 +        CreateTimer(LookAndFeel::eIntID_TreeLazyScrollDelay,
  1.2653 +                    LazyScrollCallback, nsITimer::TYPE_ONE_SHOT,
  1.2654 +                    getter_AddRefs(mSlots->mTimer));
  1.2655 +       }
  1.2656 +#endif
  1.2657 +      // Bail out to prevent spring loaded timer and feedback line settings.
  1.2658 +      return NS_OK;
  1.2659 +    }
  1.2660 +
  1.2661 +    // If changed from last time, invalidate primary cell at the old location and if allowed, 
  1.2662 +    // invalidate primary cell at the new location. If nothing changed, just bail.
  1.2663 +    if (mSlots->mDropRow != lastDropRow ||
  1.2664 +        mSlots->mDropOrient != lastDropOrient ||
  1.2665 +        mSlots->mDragAction != lastDragAction) {
  1.2666 +
  1.2667 +      // Invalidate row at the old location.
  1.2668 +      if (mSlots->mDropAllowed) {
  1.2669 +        mSlots->mDropAllowed = false;
  1.2670 +        InvalidateDropFeedback(lastDropRow, lastDropOrient);
  1.2671 +      }
  1.2672 +
  1.2673 +      if (mSlots->mTimer) {
  1.2674 +        // Timer is active but for a different row than the current one, kill it.
  1.2675 +        mSlots->mTimer->Cancel();
  1.2676 +        mSlots->mTimer = nullptr;
  1.2677 +      }
  1.2678 +
  1.2679 +      if (mSlots->mDropRow >= 0) {
  1.2680 +        if (!mSlots->mTimer && mSlots->mDropOrient == nsITreeView::DROP_ON) {
  1.2681 +          // Either there wasn't a timer running or it was just killed above.
  1.2682 +          // If over a folder, start up a timer to open the folder.
  1.2683 +          bool isContainer = false;
  1.2684 +          mView->IsContainer(mSlots->mDropRow, &isContainer);
  1.2685 +          if (isContainer) {
  1.2686 +            bool isOpen = false;
  1.2687 +            mView->IsContainerOpen(mSlots->mDropRow, &isOpen);
  1.2688 +            if (!isOpen) {
  1.2689 +              // This node isn't expanded, set a timer to expand it.
  1.2690 +              CreateTimer(LookAndFeel::eIntID_TreeOpenDelay,
  1.2691 +                          OpenCallback, nsITimer::TYPE_ONE_SHOT,
  1.2692 +                          getter_AddRefs(mSlots->mTimer));
  1.2693 +            }
  1.2694 +          }
  1.2695 +        }
  1.2696 +
  1.2697 +        // The dataTransfer was initialized by the call to GetDropEffect above.
  1.2698 +        bool canDropAtNewLocation = false;
  1.2699 +        mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient,
  1.2700 +                       aEvent->AsDragEvent()->dataTransfer,
  1.2701 +                       &canDropAtNewLocation);
  1.2702 +
  1.2703 +        if (canDropAtNewLocation) {
  1.2704 +          // Invalidate row at the new location.
  1.2705 +          mSlots->mDropAllowed = canDropAtNewLocation;
  1.2706 +          InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
  1.2707 +        }
  1.2708 +      }
  1.2709 +    }
  1.2710 +
  1.2711 +    // Indicate that the drop is allowed by preventing the default behaviour.
  1.2712 +    if (mSlots->mDropAllowed)
  1.2713 +      *aEventStatus = nsEventStatus_eConsumeNoDefault;
  1.2714 +  }
  1.2715 +  else if (aEvent->message == NS_DRAGDROP_DROP) {
  1.2716 +     // this event was meant for another frame, so ignore it
  1.2717 +     if (!mSlots)
  1.2718 +       return NS_OK;
  1.2719 +
  1.2720 +    // Tell the view where the drop happened.
  1.2721 +
  1.2722 +    // Remove the drop folder and all its parents from the array.
  1.2723 +    int32_t parentIndex;
  1.2724 +    nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex);
  1.2725 +    while (NS_SUCCEEDED(rv) && parentIndex >= 0) {
  1.2726 +      mSlots->mArray.RemoveElement(parentIndex);
  1.2727 +      rv = mView->GetParentIndex(parentIndex, &parentIndex);
  1.2728 +    }
  1.2729 +
  1.2730 +    NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type");
  1.2731 +    WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
  1.2732 +    nsContentUtils::SetDataTransferInEvent(dragEvent);
  1.2733 +
  1.2734 +    mView->Drop(mSlots->mDropRow, mSlots->mDropOrient, dragEvent->dataTransfer);
  1.2735 +    mSlots->mDropRow = -1;
  1.2736 +    mSlots->mDropOrient = -1;
  1.2737 +    mSlots->mIsDragging = false;
  1.2738 +    *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop
  1.2739 +  }
  1.2740 +  else if (aEvent->message == NS_DRAGDROP_EXIT) {
  1.2741 +    // this event was meant for another frame, so ignore it
  1.2742 +    if (!mSlots)
  1.2743 +      return NS_OK;
  1.2744 +
  1.2745 +    // Clear out all our tracking vars.
  1.2746 +
  1.2747 +    if (mSlots->mDropAllowed) {
  1.2748 +      mSlots->mDropAllowed = false;
  1.2749 +      InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
  1.2750 +    }
  1.2751 +    else
  1.2752 +      mSlots->mDropAllowed = false;
  1.2753 +    mSlots->mIsDragging = false;
  1.2754 +    mSlots->mScrollLines = 0;
  1.2755 +    // If a drop is occuring, the exit event will fire just before the drop
  1.2756 +    // event, so don't reset mDropRow or mDropOrient as these fields are used
  1.2757 +    // by the drop event.
  1.2758 +    if (mSlots->mTimer) {
  1.2759 +      mSlots->mTimer->Cancel();
  1.2760 +      mSlots->mTimer = nullptr;
  1.2761 +    }
  1.2762 +
  1.2763 +    if (!mSlots->mArray.IsEmpty()) {
  1.2764 +      // Close all spring loaded folders except the drop folder.
  1.2765 +      CreateTimer(LookAndFeel::eIntID_TreeCloseDelay,
  1.2766 +                  CloseCallback, nsITimer::TYPE_ONE_SHOT,
  1.2767 +                  getter_AddRefs(mSlots->mTimer));
  1.2768 +    }
  1.2769 +  }
  1.2770 +
  1.2771 +  return NS_OK;
  1.2772 +}
  1.2773 +
  1.2774 +static nsLineStyle
  1.2775 +ConvertBorderStyleToLineStyle(uint8_t aBorderStyle)
  1.2776 +{
  1.2777 +  switch (aBorderStyle) {
  1.2778 +    case NS_STYLE_BORDER_STYLE_DOTTED:
  1.2779 +      return nsLineStyle_kDotted;
  1.2780 +    case NS_STYLE_BORDER_STYLE_DASHED:
  1.2781 +      return nsLineStyle_kDashed;
  1.2782 +    default:
  1.2783 +      return nsLineStyle_kSolid;
  1.2784 +  }
  1.2785 +}
  1.2786 +
  1.2787 +static void
  1.2788 +PaintTreeBody(nsIFrame* aFrame, nsRenderingContext* aCtx,
  1.2789 +              const nsRect& aDirtyRect, nsPoint aPt)
  1.2790 +{
  1.2791 +  static_cast<nsTreeBodyFrame*>(aFrame)->PaintTreeBody(*aCtx, aDirtyRect, aPt);
  1.2792 +}
  1.2793 +
  1.2794 +// Painting routines
  1.2795 +void
  1.2796 +nsTreeBodyFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1.2797 +                                  const nsRect&           aDirtyRect,
  1.2798 +                                  const nsDisplayListSet& aLists)
  1.2799 +{
  1.2800 +  // REVIEW: why did we paint if we were collapsed? that makes no sense!
  1.2801 +  if (!IsVisibleForPainting(aBuilder))
  1.2802 +    return; // We're invisible.  Don't paint.
  1.2803 +
  1.2804 +  // Handles painting our background, border, and outline.
  1.2805 +  nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
  1.2806 +
  1.2807 +  // Bail out now if there's no view or we can't run script because the
  1.2808 +  // document is a zombie
  1.2809 +  if (!mView || !GetContent()->GetCurrentDoc()->GetWindow())
  1.2810 +    return;
  1.2811 +
  1.2812 +  aLists.Content()->AppendNewToTop(new (aBuilder)
  1.2813 +    nsDisplayGeneric(aBuilder, this, ::PaintTreeBody, "XULTreeBody",
  1.2814 +                     nsDisplayItem::TYPE_XUL_TREE_BODY));
  1.2815 +}
  1.2816 +
  1.2817 +void
  1.2818 +nsTreeBodyFrame::PaintTreeBody(nsRenderingContext& aRenderingContext,
  1.2819 +                               const nsRect& aDirtyRect, nsPoint aPt)
  1.2820 +{
  1.2821 +  // Update our available height and our page count.
  1.2822 +  CalcInnerBox();
  1.2823 +  aRenderingContext.PushState();
  1.2824 +  aRenderingContext.IntersectClip(mInnerBox + aPt);
  1.2825 +  int32_t oldPageCount = mPageLength;
  1.2826 +  if (!mHasFixedRowCount)
  1.2827 +    mPageLength = mInnerBox.height/mRowHeight;
  1.2828 +
  1.2829 +  if (oldPageCount != mPageLength || mHorzWidth != CalcHorzWidth(GetScrollParts())) {
  1.2830 +    // Schedule a ResizeReflow that will update our info properly.
  1.2831 +    PresContext()->PresShell()->
  1.2832 +      FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
  1.2833 +  }
  1.2834 +#ifdef DEBUG
  1.2835 +  int32_t rowCount = mRowCount;
  1.2836 +  mView->GetRowCount(&rowCount);
  1.2837 +  NS_WARN_IF_FALSE(mRowCount == rowCount, "row count changed unexpectedly");
  1.2838 +#endif
  1.2839 +
  1.2840 +  // Loop through our columns and paint them (e.g., for sorting).  This is only
  1.2841 +  // relevant when painting backgrounds, since columns contain no content.  Content
  1.2842 +  // is contained in the rows.
  1.2843 +  for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
  1.2844 +       currCol = currCol->GetNext()) {
  1.2845 +    nsRect colRect;
  1.2846 +    nsresult rv = currCol->GetRect(this, mInnerBox.y, mInnerBox.height,
  1.2847 +                                   &colRect);
  1.2848 +    // Don't paint hidden columns.
  1.2849 +    if (NS_FAILED(rv) || colRect.width == 0) continue;
  1.2850 +
  1.2851 +    if (OffsetForHorzScroll(colRect, false)) {
  1.2852 +      nsRect dirtyRect;
  1.2853 +      colRect += aPt;
  1.2854 +      if (dirtyRect.IntersectRect(aDirtyRect, colRect)) {
  1.2855 +        PaintColumn(currCol, colRect, PresContext(), aRenderingContext, aDirtyRect);
  1.2856 +      }
  1.2857 +    }
  1.2858 +  }
  1.2859 +  // Loop through our on-screen rows.
  1.2860 +  for (int32_t i = mTopRowIndex; i < mRowCount && i <= mTopRowIndex+mPageLength; i++) {
  1.2861 +    nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(i-mTopRowIndex), mInnerBox.width, mRowHeight);
  1.2862 +    nsRect dirtyRect;
  1.2863 +    if (dirtyRect.IntersectRect(aDirtyRect, rowRect + aPt) &&
  1.2864 +        rowRect.y < (mInnerBox.y+mInnerBox.height)) {
  1.2865 +      PaintRow(i, rowRect + aPt, PresContext(), aRenderingContext, aDirtyRect, aPt);
  1.2866 +    }
  1.2867 +  }
  1.2868 +
  1.2869 +  if (mSlots && mSlots->mDropAllowed && (mSlots->mDropOrient == nsITreeView::DROP_BEFORE ||
  1.2870 +      mSlots->mDropOrient == nsITreeView::DROP_AFTER)) {
  1.2871 +    nscoord yPos = mInnerBox.y + mRowHeight * (mSlots->mDropRow - mTopRowIndex) - mRowHeight / 2;
  1.2872 +    nsRect feedbackRect(mInnerBox.x, yPos, mInnerBox.width, mRowHeight);
  1.2873 +    if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
  1.2874 +      feedbackRect.y += mRowHeight;
  1.2875 +
  1.2876 +    nsRect dirtyRect;
  1.2877 +    feedbackRect += aPt;
  1.2878 +    if (dirtyRect.IntersectRect(aDirtyRect, feedbackRect)) {
  1.2879 +      PaintDropFeedback(feedbackRect, PresContext(), aRenderingContext, aDirtyRect, aPt);
  1.2880 +    }
  1.2881 +  }
  1.2882 +  aRenderingContext.PopState();
  1.2883 +}
  1.2884 +
  1.2885 +
  1.2886 +
  1.2887 +void
  1.2888 +nsTreeBodyFrame::PaintColumn(nsTreeColumn*        aColumn,
  1.2889 +                             const nsRect&        aColumnRect,
  1.2890 +                             nsPresContext*      aPresContext,
  1.2891 +                             nsRenderingContext& aRenderingContext,
  1.2892 +                             const nsRect&        aDirtyRect)
  1.2893 +{
  1.2894 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.2895 +
  1.2896 +  // Now obtain the properties for our cell.
  1.2897 +  PrefillPropertyArray(-1, aColumn);
  1.2898 +  nsAutoString properties;
  1.2899 +  mView->GetColumnProperties(aColumn, properties);
  1.2900 +  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  1.2901 +
  1.2902 +  // Resolve style for the column.  It contains all the info we need to lay ourselves
  1.2903 +  // out and to paint.
  1.2904 +  nsStyleContext* colContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecolumn);
  1.2905 +
  1.2906 +  // Obtain the margins for the cell and then deflate our rect by that 
  1.2907 +  // amount.  The cell is assumed to be contained within the deflated rect.
  1.2908 +  nsRect colRect(aColumnRect);
  1.2909 +  nsMargin colMargin;
  1.2910 +  colContext->StyleMargin()->GetMargin(colMargin);
  1.2911 +  colRect.Deflate(colMargin);
  1.2912 +
  1.2913 +  PaintBackgroundLayer(colContext, aPresContext, aRenderingContext, colRect, aDirtyRect);
  1.2914 +}
  1.2915 +
  1.2916 +void
  1.2917 +nsTreeBodyFrame::PaintRow(int32_t              aRowIndex,
  1.2918 +                          const nsRect&        aRowRect,
  1.2919 +                          nsPresContext*       aPresContext,
  1.2920 +                          nsRenderingContext& aRenderingContext,
  1.2921 +                          const nsRect&        aDirtyRect,
  1.2922 +                          nsPoint              aPt)
  1.2923 +{
  1.2924 +  // We have been given a rect for our row.  We treat this row like a full-blown
  1.2925 +  // frame, meaning that it can have borders, margins, padding, and a background.
  1.2926 +  
  1.2927 +  // Without a view, we have no data. Check for this up front.
  1.2928 +  if (!mView)
  1.2929 +    return;
  1.2930 +
  1.2931 +  nsresult rv;
  1.2932 +
  1.2933 +  // Now obtain the properties for our row.
  1.2934 +  // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused
  1.2935 +  PrefillPropertyArray(aRowIndex, nullptr);
  1.2936 +
  1.2937 +  nsAutoString properties;
  1.2938 +  mView->GetRowProperties(aRowIndex, properties);
  1.2939 +  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  1.2940 +
  1.2941 +  // Resolve style for the row.  It contains all the info we need to lay ourselves
  1.2942 +  // out and to paint.
  1.2943 +  nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
  1.2944 +
  1.2945 +  // Obtain the margins for the row and then deflate our rect by that 
  1.2946 +  // amount.  The row is assumed to be contained within the deflated rect.
  1.2947 +  nsRect rowRect(aRowRect);
  1.2948 +  nsMargin rowMargin;
  1.2949 +  rowContext->StyleMargin()->GetMargin(rowMargin);
  1.2950 +  rowRect.Deflate(rowMargin);
  1.2951 +
  1.2952 +  // Paint our borders and background for our row rect.
  1.2953 +  // If a -moz-appearance is provided, use theme drawing only if the current row
  1.2954 +  // is not selected (since we draw the selection as part of drawing the background).
  1.2955 +  bool useTheme = false;
  1.2956 +  nsITheme *theme = nullptr;
  1.2957 +  const nsStyleDisplay* displayData = rowContext->StyleDisplay();
  1.2958 +  if (displayData->mAppearance) {
  1.2959 +    theme = aPresContext->GetTheme();
  1.2960 +    if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance))
  1.2961 +      useTheme = true;
  1.2962 +  }
  1.2963 +  bool isSelected = false;
  1.2964 +  nsCOMPtr<nsITreeSelection> selection;
  1.2965 +  mView->GetSelection(getter_AddRefs(selection));
  1.2966 +  if (selection) 
  1.2967 +    selection->IsSelected(aRowIndex, &isSelected);
  1.2968 +  if (useTheme && !isSelected) {
  1.2969 +    nsRect dirty;
  1.2970 +    dirty.IntersectRect(rowRect, aDirtyRect);
  1.2971 +    theme->DrawWidgetBackground(&aRenderingContext, this, 
  1.2972 +                                displayData->mAppearance, rowRect, dirty);
  1.2973 +  } else {
  1.2974 +    PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, rowRect, aDirtyRect);
  1.2975 +  }
  1.2976 +  
  1.2977 +  // Adjust the rect for its border and padding.
  1.2978 +  nsRect originalRowRect = rowRect;
  1.2979 +  AdjustForBorderPadding(rowContext, rowRect);
  1.2980 +
  1.2981 +  bool isSeparator = false;
  1.2982 +  mView->IsSeparator(aRowIndex, &isSeparator);
  1.2983 +  if (isSeparator) {
  1.2984 +    // The row is a separator.
  1.2985 +
  1.2986 +    nscoord primaryX = rowRect.x;
  1.2987 +    nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
  1.2988 +    if (primaryCol) {
  1.2989 +      // Paint the primary cell.
  1.2990 +      nsRect cellRect;
  1.2991 +      rv = primaryCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
  1.2992 +      if (NS_FAILED(rv)) {
  1.2993 +        NS_NOTREACHED("primary column is invalid");
  1.2994 +        return;
  1.2995 +      }
  1.2996 +
  1.2997 +      if (OffsetForHorzScroll(cellRect, false)) {
  1.2998 +        cellRect.x += aPt.x;
  1.2999 +        nsRect dirtyRect;
  1.3000 +        nsRect checkRect(cellRect.x, originalRowRect.y,
  1.3001 +                         cellRect.width, originalRowRect.height);
  1.3002 +        if (dirtyRect.IntersectRect(aDirtyRect, checkRect))
  1.3003 +          PaintCell(aRowIndex, primaryCol, cellRect, aPresContext,
  1.3004 +                    aRenderingContext, aDirtyRect, primaryX, aPt);
  1.3005 +      }
  1.3006 +
  1.3007 +      // Paint the left side of the separator.
  1.3008 +      nscoord currX;
  1.3009 +      nsTreeColumn* previousCol = primaryCol->GetPrevious();
  1.3010 +      if (previousCol) {
  1.3011 +        nsRect prevColRect;
  1.3012 +        rv = previousCol->GetRect(this, 0, 0, &prevColRect);
  1.3013 +        if (NS_SUCCEEDED(rv)) {
  1.3014 +          currX = (prevColRect.x - mHorzPosition) + prevColRect.width + aPt.x;
  1.3015 +        } else {
  1.3016 +          NS_NOTREACHED("The column before the primary column is invalid");
  1.3017 +          currX = rowRect.x;
  1.3018 +        }
  1.3019 +      } else {
  1.3020 +        currX = rowRect.x;
  1.3021 +      }
  1.3022 +
  1.3023 +      int32_t level;
  1.3024 +      mView->GetLevel(aRowIndex, &level);
  1.3025 +      if (level == 0)
  1.3026 +        currX += mIndentation;
  1.3027 +
  1.3028 +      if (currX > rowRect.x) {
  1.3029 +        nsRect separatorRect(rowRect);
  1.3030 +        separatorRect.width -= rowRect.x + rowRect.width - currX;
  1.3031 +        PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
  1.3032 +      }
  1.3033 +    }
  1.3034 +
  1.3035 +    // Paint the right side (whole) separator.
  1.3036 +    nsRect separatorRect(rowRect);
  1.3037 +    if (primaryX > rowRect.x) {
  1.3038 +      separatorRect.width -= primaryX - rowRect.x;
  1.3039 +      separatorRect.x += primaryX - rowRect.x;
  1.3040 +    }
  1.3041 +    PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
  1.3042 +  }
  1.3043 +  else {
  1.3044 +    // Now loop over our cells. Only paint a cell if it intersects with our dirty rect.
  1.3045 +    for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
  1.3046 +         currCol = currCol->GetNext()) {
  1.3047 +      nsRect cellRect;
  1.3048 +      rv = currCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
  1.3049 +      // Don't paint cells in hidden columns.
  1.3050 +      if (NS_FAILED(rv) || cellRect.width == 0)
  1.3051 +        continue;
  1.3052 +
  1.3053 +      if (OffsetForHorzScroll(cellRect, false)) {
  1.3054 +        cellRect.x += aPt.x;
  1.3055 +
  1.3056 +        // for primary columns, use the row's vertical size so that the
  1.3057 +        // lines get drawn properly
  1.3058 +        nsRect checkRect = cellRect;
  1.3059 +        if (currCol->IsPrimary())
  1.3060 +          checkRect = nsRect(cellRect.x, originalRowRect.y,
  1.3061 +                             cellRect.width, originalRowRect.height);
  1.3062 +
  1.3063 +        nsRect dirtyRect;
  1.3064 +        nscoord dummy;
  1.3065 +        if (dirtyRect.IntersectRect(aDirtyRect, checkRect))
  1.3066 +          PaintCell(aRowIndex, currCol, cellRect, aPresContext,
  1.3067 +                    aRenderingContext, aDirtyRect, dummy, aPt);
  1.3068 +      }
  1.3069 +    }
  1.3070 +  }
  1.3071 +}
  1.3072 +
  1.3073 +void
  1.3074 +nsTreeBodyFrame::PaintSeparator(int32_t              aRowIndex,
  1.3075 +                                const nsRect&        aSeparatorRect,
  1.3076 +                                nsPresContext*      aPresContext,
  1.3077 +                                nsRenderingContext& aRenderingContext,
  1.3078 +                                const nsRect&        aDirtyRect)
  1.3079 +{
  1.3080 +  // Resolve style for the separator.
  1.3081 +  nsStyleContext* separatorContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeseparator);
  1.3082 +  bool useTheme = false;
  1.3083 +  nsITheme *theme = nullptr;
  1.3084 +  const nsStyleDisplay* displayData = separatorContext->StyleDisplay();
  1.3085 +  if ( displayData->mAppearance ) {
  1.3086 +    theme = aPresContext->GetTheme();
  1.3087 +    if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance))
  1.3088 +      useTheme = true;
  1.3089 +  }
  1.3090 +
  1.3091 +  // use -moz-appearance if provided.
  1.3092 +  if (useTheme) {
  1.3093 +    nsRect dirty;
  1.3094 +    dirty.IntersectRect(aSeparatorRect, aDirtyRect);
  1.3095 +    theme->DrawWidgetBackground(&aRenderingContext, this,
  1.3096 +                                displayData->mAppearance, aSeparatorRect, dirty); 
  1.3097 +  }
  1.3098 +  else {
  1.3099 +    const nsStylePosition* stylePosition = separatorContext->StylePosition();
  1.3100 +
  1.3101 +    // Obtain the height for the separator or use the default value.
  1.3102 +    nscoord height;
  1.3103 +    if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
  1.3104 +      height = stylePosition->mHeight.GetCoordValue();
  1.3105 +    else {
  1.3106 +      // Use default height 2px.
  1.3107 +      height = nsPresContext::CSSPixelsToAppUnits(2);
  1.3108 +    }
  1.3109 +
  1.3110 +    // Obtain the margins for the separator and then deflate our rect by that 
  1.3111 +    // amount. The separator is assumed to be contained within the deflated rect.
  1.3112 +    nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width, height);
  1.3113 +    nsMargin separatorMargin;
  1.3114 +    separatorContext->StyleMargin()->GetMargin(separatorMargin);
  1.3115 +    separatorRect.Deflate(separatorMargin);
  1.3116 +
  1.3117 +    // Center the separator.
  1.3118 +    separatorRect.y += (aSeparatorRect.height - height) / 2;
  1.3119 +
  1.3120 +    PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext, separatorRect, aDirtyRect);
  1.3121 +  }
  1.3122 +}
  1.3123 +
  1.3124 +void
  1.3125 +nsTreeBodyFrame::PaintCell(int32_t              aRowIndex,
  1.3126 +                           nsTreeColumn*        aColumn,
  1.3127 +                           const nsRect&        aCellRect,
  1.3128 +                           nsPresContext*       aPresContext,
  1.3129 +                           nsRenderingContext& aRenderingContext,
  1.3130 +                           const nsRect&        aDirtyRect,
  1.3131 +                           nscoord&             aCurrX,
  1.3132 +                           nsPoint              aPt)
  1.3133 +{
  1.3134 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.3135 +
  1.3136 +  // Now obtain the properties for our cell.
  1.3137 +  // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID.
  1.3138 +  PrefillPropertyArray(aRowIndex, aColumn);
  1.3139 +  nsAutoString properties;
  1.3140 +  mView->GetCellProperties(aRowIndex, aColumn, properties);
  1.3141 +  nsTreeUtils::TokenizeProperties(properties, mScratchArray);
  1.3142 +
  1.3143 +  // Resolve style for the cell.  It contains all the info we need to lay ourselves
  1.3144 +  // out and to paint.
  1.3145 +  nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1.3146 +
  1.3147 +  bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1.3148 +
  1.3149 +  // Obtain the margins for the cell and then deflate our rect by that 
  1.3150 +  // amount.  The cell is assumed to be contained within the deflated rect.
  1.3151 +  nsRect cellRect(aCellRect);
  1.3152 +  nsMargin cellMargin;
  1.3153 +  cellContext->StyleMargin()->GetMargin(cellMargin);
  1.3154 +  cellRect.Deflate(cellMargin);
  1.3155 +
  1.3156 +  // Paint our borders and background for our row rect.
  1.3157 +  PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect, aDirtyRect);
  1.3158 +
  1.3159 +  // Adjust the rect for its border and padding.
  1.3160 +  AdjustForBorderPadding(cellContext, cellRect);
  1.3161 +
  1.3162 +  nscoord currX = cellRect.x;
  1.3163 +  nscoord remainingWidth = cellRect.width;
  1.3164 +
  1.3165 +  // Now we paint the contents of the cells.
  1.3166 +  // Directionality of the tree determines the order in which we paint.  
  1.3167 +  // NS_STYLE_DIRECTION_LTR means paint from left to right.
  1.3168 +  // NS_STYLE_DIRECTION_RTL means paint from right to left.
  1.3169 +
  1.3170 +  if (aColumn->IsPrimary()) {
  1.3171 +    // If we're the primary column, we need to indent and paint the twisty and any connecting lines
  1.3172 +    // between siblings.
  1.3173 +
  1.3174 +    int32_t level;
  1.3175 +    mView->GetLevel(aRowIndex, &level);
  1.3176 +
  1.3177 +    if (!isRTL)
  1.3178 +      currX += mIndentation * level;
  1.3179 +    remainingWidth -= mIndentation * level;
  1.3180 +
  1.3181 +    // Resolve the style to use for the connecting lines.
  1.3182 +    nsStyleContext* lineContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeline);
  1.3183 +    
  1.3184 +    if (mIndentation && level &&
  1.3185 +        lineContext->StyleVisibility()->IsVisibleOrCollapsed()) {
  1.3186 +      // Paint the thread lines.
  1.3187 +
  1.3188 +      // Get the size of the twisty. We don't want to paint the twisty
  1.3189 +      // before painting of connecting lines since it would paint lines over
  1.3190 +      // the twisty. But we need to leave a place for it.
  1.3191 +      nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1.3192 +
  1.3193 +      nsRect imageSize;
  1.3194 +      nsRect twistyRect(aCellRect);
  1.3195 +      GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext,
  1.3196 +                    aRenderingContext, twistyContext);
  1.3197 +
  1.3198 +      nsMargin twistyMargin;
  1.3199 +      twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1.3200 +      twistyRect.Inflate(twistyMargin);
  1.3201 +
  1.3202 +      aRenderingContext.PushState();
  1.3203 +
  1.3204 +      const nsStyleBorder* borderStyle = lineContext->StyleBorder();
  1.3205 +      nscolor color;
  1.3206 +      bool foreground;
  1.3207 +      borderStyle->GetBorderColor(NS_SIDE_LEFT, color, foreground);
  1.3208 +      if (foreground) {
  1.3209 +        // GetBorderColor didn't touch color, thus grab it from the treeline context
  1.3210 +        color = lineContext->StyleColor()->mColor;
  1.3211 +      }
  1.3212 +      aRenderingContext.SetColor(color);
  1.3213 +      uint8_t style;
  1.3214 +      style = borderStyle->GetBorderStyle(NS_SIDE_LEFT);
  1.3215 +      aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style));
  1.3216 +
  1.3217 +      nscoord srcX = currX + twistyRect.width - mIndentation / 2;
  1.3218 +      nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y;
  1.3219 +
  1.3220 +      // Don't paint off our cell.
  1.3221 +      if (srcX <= cellRect.x + cellRect.width) {
  1.3222 +        nscoord destX = currX + twistyRect.width;
  1.3223 +        if (destX > cellRect.x + cellRect.width)
  1.3224 +          destX = cellRect.x + cellRect.width;
  1.3225 +        if (isRTL) {
  1.3226 +          srcX = currX + remainingWidth - (srcX - cellRect.x);
  1.3227 +          destX = currX + remainingWidth - (destX - cellRect.x);
  1.3228 +        }
  1.3229 +        aRenderingContext.DrawLine(srcX, lineY + mRowHeight / 2, destX, lineY + mRowHeight / 2);
  1.3230 +      }
  1.3231 +
  1.3232 +      int32_t currentParent = aRowIndex;
  1.3233 +      for (int32_t i = level; i > 0; i--) {
  1.3234 +        if (srcX <= cellRect.x + cellRect.width) {
  1.3235 +          // Paint full vertical line only if we have next sibling.
  1.3236 +          bool hasNextSibling;
  1.3237 +          mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling);
  1.3238 +          if (hasNextSibling)
  1.3239 +            aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight);
  1.3240 +          else if (i == level)
  1.3241 +            aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight / 2);
  1.3242 +        }
  1.3243 +
  1.3244 +        int32_t parent;
  1.3245 +        if (NS_FAILED(mView->GetParentIndex(currentParent, &parent)) || parent < 0)
  1.3246 +          break;
  1.3247 +        currentParent = parent;
  1.3248 +        srcX -= mIndentation;
  1.3249 +      }
  1.3250 +
  1.3251 +      aRenderingContext.PopState();
  1.3252 +    }
  1.3253 +
  1.3254 +    // Always leave space for the twisty.
  1.3255 +    nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1.3256 +    PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, aRenderingContext, aDirtyRect,
  1.3257 +                remainingWidth, currX);
  1.3258 +  }
  1.3259 +  
  1.3260 +  // Now paint the icon for our cell.
  1.3261 +  nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1.3262 +  nsRect dirtyRect;
  1.3263 +  if (dirtyRect.IntersectRect(aDirtyRect, iconRect))
  1.3264 +    PaintImage(aRowIndex, aColumn, iconRect, aPresContext, aRenderingContext, aDirtyRect,
  1.3265 +               remainingWidth, currX);
  1.3266 +
  1.3267 +  // Now paint our element, but only if we aren't a cycler column.
  1.3268 +  // XXX until we have the ability to load images, allow the view to 
  1.3269 +  // insert text into cycler columns...
  1.3270 +  if (!aColumn->IsCycler()) {
  1.3271 +    nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height);
  1.3272 +    nsRect dirtyRect;
  1.3273 +    if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) {
  1.3274 +      switch (aColumn->GetType()) {
  1.3275 +        case nsITreeColumn::TYPE_TEXT:
  1.3276 +          PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
  1.3277 +          break;
  1.3278 +        case nsITreeColumn::TYPE_CHECKBOX:
  1.3279 +          PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
  1.3280 +          break;
  1.3281 +        case nsITreeColumn::TYPE_PROGRESSMETER:
  1.3282 +          int32_t state;
  1.3283 +          mView->GetProgressMode(aRowIndex, aColumn, &state);
  1.3284 +          switch (state) {
  1.3285 +            case nsITreeView::PROGRESS_NORMAL:
  1.3286 +            case nsITreeView::PROGRESS_UNDETERMINED:
  1.3287 +              PaintProgressMeter(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
  1.3288 +              break;
  1.3289 +            case nsITreeView::PROGRESS_NONE:
  1.3290 +            default:
  1.3291 +              PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
  1.3292 +              break;
  1.3293 +          }
  1.3294 +          break;
  1.3295 +      }
  1.3296 +    }
  1.3297 +  }
  1.3298 +
  1.3299 +  aCurrX = currX;
  1.3300 +}
  1.3301 +
  1.3302 +void
  1.3303 +nsTreeBodyFrame::PaintTwisty(int32_t              aRowIndex,
  1.3304 +                             nsTreeColumn*        aColumn,
  1.3305 +                             const nsRect&        aTwistyRect,
  1.3306 +                             nsPresContext*      aPresContext,
  1.3307 +                             nsRenderingContext& aRenderingContext,
  1.3308 +                             const nsRect&        aDirtyRect,
  1.3309 +                             nscoord&             aRemainingWidth,
  1.3310 +                             nscoord&             aCurrX)
  1.3311 +{
  1.3312 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.3313 +
  1.3314 +  bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1.3315 +  nscoord rightEdge = aCurrX + aRemainingWidth;
  1.3316 +  // Paint the twisty, but only if we are a non-empty container.
  1.3317 +  bool shouldPaint = false;
  1.3318 +  bool isContainer = false;
  1.3319 +  mView->IsContainer(aRowIndex, &isContainer);
  1.3320 +  if (isContainer) {
  1.3321 +    bool isContainerEmpty = false;
  1.3322 +    mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
  1.3323 +    if (!isContainerEmpty)
  1.3324 +      shouldPaint = true;
  1.3325 +  }
  1.3326 +
  1.3327 +  // Resolve style for the twisty.
  1.3328 +  nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1.3329 +
  1.3330 +  // Obtain the margins for the twisty and then deflate our rect by that 
  1.3331 +  // amount.  The twisty is assumed to be contained within the deflated rect.
  1.3332 +  nsRect twistyRect(aTwistyRect);
  1.3333 +  nsMargin twistyMargin;
  1.3334 +  twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1.3335 +  twistyRect.Deflate(twistyMargin);
  1.3336 +
  1.3337 +  nsRect imageSize;
  1.3338 +  nsITheme* theme = GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect,
  1.3339 +                                  aPresContext, aRenderingContext, twistyContext);
  1.3340 +
  1.3341 +  // Subtract out the remaining width.  This is done even when we don't actually paint a twisty in 
  1.3342 +  // this cell, so that cells in different rows still line up.
  1.3343 +  nsRect copyRect(twistyRect);
  1.3344 +  copyRect.Inflate(twistyMargin);
  1.3345 +  aRemainingWidth -= copyRect.width;
  1.3346 +  if (!isRTL)
  1.3347 +    aCurrX += copyRect.width;
  1.3348 +
  1.3349 +  if (shouldPaint) {
  1.3350 +    // Paint our borders and background for our image rect.
  1.3351 +    PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext, twistyRect, aDirtyRect);
  1.3352 +
  1.3353 +    if (theme) {
  1.3354 +      if (isRTL)
  1.3355 +        twistyRect.x = rightEdge - twistyRect.width;
  1.3356 +      // yeah, I know it says we're drawing a background, but a twisty is really a fg
  1.3357 +      // object since it doesn't have anything that gecko would want to draw over it. Besides,
  1.3358 +      // we have to prevent imagelib from drawing it.
  1.3359 +      nsRect dirty;
  1.3360 +      dirty.IntersectRect(twistyRect, aDirtyRect);
  1.3361 +      theme->DrawWidgetBackground(&aRenderingContext, this, 
  1.3362 +                                  twistyContext->StyleDisplay()->mAppearance, twistyRect, dirty);
  1.3363 +    }
  1.3364 +    else {
  1.3365 +      // Time to paint the twisty.
  1.3366 +      // Adjust the rect for its border and padding.
  1.3367 +      nsMargin bp(0,0,0,0);
  1.3368 +      GetBorderPadding(twistyContext, bp);
  1.3369 +      twistyRect.Deflate(bp);
  1.3370 +      if (isRTL)
  1.3371 +        twistyRect.x = rightEdge - twistyRect.width;
  1.3372 +      imageSize.Deflate(bp);
  1.3373 +
  1.3374 +      // Get the image for drawing.
  1.3375 +      nsCOMPtr<imgIContainer> image;
  1.3376 +      bool useImageRegion = true;
  1.3377 +      GetImage(aRowIndex, aColumn, true, twistyContext, useImageRegion, getter_AddRefs(image));
  1.3378 +      if (image) {
  1.3379 +        nsPoint pt = twistyRect.TopLeft();
  1.3380 +
  1.3381 +        // Center the image. XXX Obey vertical-align style prop?
  1.3382 +        if (imageSize.height < twistyRect.height) {
  1.3383 +          pt.y += (twistyRect.height - imageSize.height)/2;
  1.3384 +        }
  1.3385 +          
  1.3386 +        // Paint the image.
  1.3387 +        nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
  1.3388 +            GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect,
  1.3389 +            imgIContainer::FLAG_NONE, &imageSize);
  1.3390 +      }
  1.3391 +    }
  1.3392 +  }
  1.3393 +}
  1.3394 +
  1.3395 +void
  1.3396 +nsTreeBodyFrame::PaintImage(int32_t              aRowIndex,
  1.3397 +                            nsTreeColumn*        aColumn,
  1.3398 +                            const nsRect&        aImageRect,
  1.3399 +                            nsPresContext*       aPresContext,
  1.3400 +                            nsRenderingContext& aRenderingContext,
  1.3401 +                            const nsRect&        aDirtyRect,
  1.3402 +                            nscoord&             aRemainingWidth,
  1.3403 +                            nscoord&             aCurrX)
  1.3404 +{
  1.3405 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.3406 +
  1.3407 +  bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1.3408 +  nscoord rightEdge = aCurrX + aRemainingWidth;
  1.3409 +  // Resolve style for the image.
  1.3410 +  nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
  1.3411 +
  1.3412 +  // Obtain opacity value for the image.
  1.3413 +  float opacity = imageContext->StyleDisplay()->mOpacity;
  1.3414 +
  1.3415 +  // Obtain the margins for the image and then deflate our rect by that
  1.3416 +  // amount.  The image is assumed to be contained within the deflated rect.
  1.3417 +  nsRect imageRect(aImageRect);
  1.3418 +  nsMargin imageMargin;
  1.3419 +  imageContext->StyleMargin()->GetMargin(imageMargin);
  1.3420 +  imageRect.Deflate(imageMargin);
  1.3421 +
  1.3422 +  // Get the image.
  1.3423 +  bool useImageRegion = true;
  1.3424 +  nsCOMPtr<imgIContainer> image;
  1.3425 +  GetImage(aRowIndex, aColumn, false, imageContext, useImageRegion, getter_AddRefs(image));
  1.3426 +
  1.3427 +  // Get the image destination size.
  1.3428 +  nsSize imageDestSize = GetImageDestSize(imageContext, useImageRegion, image);
  1.3429 +  if (!imageDestSize.width || !imageDestSize.height)
  1.3430 +    return;
  1.3431 +
  1.3432 +  // Get the borders and padding.
  1.3433 +  nsMargin bp(0,0,0,0);
  1.3434 +  GetBorderPadding(imageContext, bp);
  1.3435 +
  1.3436 +  // destRect will be passed as the aDestRect argument in the DrawImage method.
  1.3437 +  // Start with the imageDestSize width and height.
  1.3438 +  nsRect destRect(0, 0, imageDestSize.width, imageDestSize.height);
  1.3439 +  // Inflate destRect for borders and padding so that we can compare/adjust
  1.3440 +  // with respect to imageRect.
  1.3441 +  destRect.Inflate(bp);
  1.3442 +
  1.3443 +  // The destRect width and height have not been adjusted to fit within the
  1.3444 +  // cell width and height.
  1.3445 +  // We must adjust the width even if image is null, because the width is used
  1.3446 +  // to update the aRemainingWidth and aCurrX values.
  1.3447 +  // Since the height isn't used unless the image is not null, we will adjust
  1.3448 +  // the height inside the if (image) block below.
  1.3449 +
  1.3450 +  if (destRect.width > imageRect.width) {
  1.3451 +    // The destRect is too wide to fit within the cell width.
  1.3452 +    // Adjust destRect width to fit within the cell width.
  1.3453 +    destRect.width = imageRect.width;
  1.3454 +  }
  1.3455 +  else {
  1.3456 +    // The cell is wider than the destRect.
  1.3457 +    // In a cycler column, the image is centered horizontally.
  1.3458 +    if (!aColumn->IsCycler()) {
  1.3459 +      // If this column is not a cycler, we won't center the image horizontally.
  1.3460 +      // We adjust the imageRect width so that the image is placed at the start
  1.3461 +      // of the cell.
  1.3462 +      imageRect.width = destRect.width;
  1.3463 +    }
  1.3464 +  }
  1.3465 +
  1.3466 +  if (image) {
  1.3467 +    if (isRTL)
  1.3468 +      imageRect.x = rightEdge - imageRect.width;
  1.3469 +    // Paint our borders and background for our image rect
  1.3470 +    PaintBackgroundLayer(imageContext, aPresContext, aRenderingContext, imageRect, aDirtyRect);
  1.3471 +
  1.3472 +    // The destRect x and y have not been set yet. Let's do that now.
  1.3473 +    // Initially, we use the imageRect x and y.
  1.3474 +    destRect.x = imageRect.x;
  1.3475 +    destRect.y = imageRect.y;
  1.3476 +
  1.3477 +    if (destRect.width < imageRect.width) {
  1.3478 +      // The destRect width is smaller than the cell width.
  1.3479 +      // Center the image horizontally in the cell.
  1.3480 +      // Adjust the destRect x accordingly.
  1.3481 +      destRect.x += (imageRect.width - destRect.width)/2;
  1.3482 +    }
  1.3483 +
  1.3484 +    // Now it's time to adjust the destRect height to fit within the cell height.
  1.3485 +    if (destRect.height > imageRect.height) {
  1.3486 +      // The destRect height is larger than the cell height.
  1.3487 +      // Adjust destRect height to fit within the cell height.
  1.3488 +      destRect.height = imageRect.height;
  1.3489 +    }
  1.3490 +    else if (destRect.height < imageRect.height) {
  1.3491 +      // The destRect height is smaller than the cell height.
  1.3492 +      // Center the image vertically in the cell.
  1.3493 +      // Adjust the destRect y accordingly.
  1.3494 +      destRect.y += (imageRect.height - destRect.height)/2;
  1.3495 +    }
  1.3496 +
  1.3497 +    // It's almost time to paint the image.
  1.3498 +    // Deflate destRect for the border and padding.
  1.3499 +    destRect.Deflate(bp);
  1.3500 +
  1.3501 +    // Get the image source rectangle - the rectangle containing the part of
  1.3502 +    // the image that we are going to display.
  1.3503 +    // sourceRect will be passed as the aSrcRect argument in the DrawImage method.
  1.3504 +    nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image);
  1.3505 +
  1.3506 +    // Let's say that the image is 100 pixels tall and
  1.3507 +    // that the CSS has specified that the destination height should be 50
  1.3508 +    // pixels tall. Let's say that the cell height is only 20 pixels. So, in
  1.3509 +    // those 20 visible pixels, we want to see the top 20/50ths of the image.
  1.3510 +    // So, the sourceRect.height should be 100 * 20 / 50, which is 40 pixels.
  1.3511 +    // Essentially, we are scaling the image as dictated by the CSS destination
  1.3512 +    // height and width, and we are then clipping the scaled image by the cell
  1.3513 +    // width and height.
  1.3514 +    nsIntSize rawImageSize;
  1.3515 +    image->GetWidth(&rawImageSize.width);
  1.3516 +    image->GetHeight(&rawImageSize.height);
  1.3517 +    nsRect wholeImageDest =
  1.3518 +      nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect,
  1.3519 +          nsRect(destRect.TopLeft(), imageDestSize));
  1.3520 +
  1.3521 +    gfxContext* ctx = aRenderingContext.ThebesContext();
  1.3522 +    if (opacity != 1.0f) {
  1.3523 +      ctx->PushGroup(gfxContentType::COLOR_ALPHA);
  1.3524 +    }
  1.3525 +
  1.3526 +    nsLayoutUtils::DrawImage(&aRenderingContext, image,
  1.3527 +        nsLayoutUtils::GetGraphicsFilterForFrame(this),
  1.3528 +        wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect,
  1.3529 +        imgIContainer::FLAG_NONE);
  1.3530 +
  1.3531 +    if (opacity != 1.0f) {
  1.3532 +      ctx->PopGroupToSource();
  1.3533 +      ctx->Paint(opacity);
  1.3534 +    }
  1.3535 +  }
  1.3536 +
  1.3537 +  // Update the aRemainingWidth and aCurrX values.
  1.3538 +  imageRect.Inflate(imageMargin);
  1.3539 +  aRemainingWidth -= imageRect.width;
  1.3540 +  if (!isRTL)
  1.3541 +    aCurrX += imageRect.width;
  1.3542 +}
  1.3543 +
  1.3544 +void
  1.3545 +nsTreeBodyFrame::PaintText(int32_t              aRowIndex,
  1.3546 +                           nsTreeColumn*        aColumn,
  1.3547 +                           const nsRect&        aTextRect,
  1.3548 +                           nsPresContext*      aPresContext,
  1.3549 +                           nsRenderingContext& aRenderingContext,
  1.3550 +                           const nsRect&        aDirtyRect,
  1.3551 +                           nscoord&             aCurrX)
  1.3552 +{
  1.3553 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.3554 +
  1.3555 +  bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1.3556 +
  1.3557 +  // Now obtain the text for our cell.
  1.3558 +  nsAutoString text;
  1.3559 +  mView->GetCellText(aRowIndex, aColumn, text);
  1.3560 +  // We're going to paint this text so we need to ensure bidi is enabled if
  1.3561 +  // necessary
  1.3562 +  CheckTextForBidi(text);
  1.3563 +
  1.3564 +  if (text.Length() == 0)
  1.3565 +    return; // Don't paint an empty string. XXX What about background/borders? Still paint?
  1.3566 +
  1.3567 +  // Resolve style for the text.  It contains all the info we need to lay ourselves
  1.3568 +  // out and to paint.
  1.3569 +  nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
  1.3570 +
  1.3571 +  // Obtain opacity value for the image.
  1.3572 +  float opacity = textContext->StyleDisplay()->mOpacity;
  1.3573 +
  1.3574 +  // Obtain the margins for the text and then deflate our rect by that 
  1.3575 +  // amount.  The text is assumed to be contained within the deflated rect.
  1.3576 +  nsRect textRect(aTextRect);
  1.3577 +  nsMargin textMargin;
  1.3578 +  textContext->StyleMargin()->GetMargin(textMargin);
  1.3579 +  textRect.Deflate(textMargin);
  1.3580 +
  1.3581 +  // Adjust the rect for its border and padding.
  1.3582 +  nsMargin bp(0,0,0,0);
  1.3583 +  GetBorderPadding(textContext, bp);
  1.3584 +  textRect.Deflate(bp);
  1.3585 +
  1.3586 +  // Compute our text size.
  1.3587 +  nsRefPtr<nsFontMetrics> fontMet;
  1.3588 +  nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
  1.3589 +                                               getter_AddRefs(fontMet));
  1.3590 +
  1.3591 +  nscoord height = fontMet->MaxHeight();
  1.3592 +  nscoord baseline = fontMet->MaxAscent();
  1.3593 +
  1.3594 +  // Center the text. XXX Obey vertical-align style prop?
  1.3595 +  if (height < textRect.height) {
  1.3596 +    textRect.y += (textRect.height - height)/2;
  1.3597 +    textRect.height = height;
  1.3598 +  }
  1.3599 +
  1.3600 +  // Set our font.
  1.3601 +  aRenderingContext.SetFont(fontMet);
  1.3602 +
  1.3603 +  AdjustForCellText(text, aRowIndex, aColumn, aRenderingContext, textRect);
  1.3604 +  textRect.Inflate(bp);
  1.3605 +
  1.3606 +  // Subtract out the remaining width.
  1.3607 +  if (!isRTL)
  1.3608 +    aCurrX += textRect.width + textMargin.LeftRight();
  1.3609 +
  1.3610 +  PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, textRect, aDirtyRect);
  1.3611 +
  1.3612 +  // Time to paint our text.
  1.3613 +  textRect.Deflate(bp);
  1.3614 +
  1.3615 +  // Set our color.
  1.3616 +  aRenderingContext.SetColor(textContext->StyleColor()->mColor);
  1.3617 +
  1.3618 +  // Draw decorations.
  1.3619 +  uint8_t decorations = textContext->StyleTextReset()->mTextDecorationLine;
  1.3620 +
  1.3621 +  nscoord offset;
  1.3622 +  nscoord size;
  1.3623 +  if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
  1.3624 +    fontMet->GetUnderline(offset, size);
  1.3625 +    if (decorations & NS_FONT_DECORATION_OVERLINE)
  1.3626 +      aRenderingContext.FillRect(textRect.x, textRect.y, textRect.width, size);
  1.3627 +    if (decorations & NS_FONT_DECORATION_UNDERLINE)
  1.3628 +      aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
  1.3629 +  }
  1.3630 +  if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
  1.3631 +    fontMet->GetStrikeout(offset, size);
  1.3632 +    aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
  1.3633 +  }
  1.3634 +  nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
  1.3635 +
  1.3636 +  gfxContext* ctx = aRenderingContext.ThebesContext();
  1.3637 +  if (opacity != 1.0f) {
  1.3638 +    ctx->PushGroup(gfxContentType::COLOR_ALPHA);
  1.3639 +  }
  1.3640 +
  1.3641 +  nsLayoutUtils::DrawString(this, &aRenderingContext, text.get(), text.Length(),
  1.3642 +                            textRect.TopLeft() + nsPoint(0, baseline), cellContext);
  1.3643 +
  1.3644 +  if (opacity != 1.0f) {
  1.3645 +    ctx->PopGroupToSource();
  1.3646 +    ctx->Paint(opacity);
  1.3647 +  }
  1.3648 +
  1.3649 +}
  1.3650 +
  1.3651 +void
  1.3652 +nsTreeBodyFrame::PaintCheckbox(int32_t              aRowIndex,
  1.3653 +                               nsTreeColumn*        aColumn,
  1.3654 +                               const nsRect&        aCheckboxRect,
  1.3655 +                               nsPresContext*      aPresContext,
  1.3656 +                               nsRenderingContext& aRenderingContext,
  1.3657 +                               const nsRect&        aDirtyRect)
  1.3658 +{
  1.3659 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.3660 +
  1.3661 +  // Resolve style for the checkbox.
  1.3662 +  nsStyleContext* checkboxContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecheckbox);
  1.3663 +
  1.3664 +  nscoord rightEdge = aCheckboxRect.XMost();
  1.3665 +
  1.3666 +  // Obtain the margins for the checkbox and then deflate our rect by that 
  1.3667 +  // amount.  The checkbox is assumed to be contained within the deflated rect.
  1.3668 +  nsRect checkboxRect(aCheckboxRect);
  1.3669 +  nsMargin checkboxMargin;
  1.3670 +  checkboxContext->StyleMargin()->GetMargin(checkboxMargin);
  1.3671 +  checkboxRect.Deflate(checkboxMargin);
  1.3672 +  
  1.3673 +  nsRect imageSize = GetImageSize(aRowIndex, aColumn, true, checkboxContext);
  1.3674 +
  1.3675 +  if (imageSize.height > checkboxRect.height)
  1.3676 +    imageSize.height = checkboxRect.height;
  1.3677 +  if (imageSize.width > checkboxRect.width)
  1.3678 +    imageSize.width = checkboxRect.width;
  1.3679 +
  1.3680 +  if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
  1.3681 +    checkboxRect.x = rightEdge - checkboxRect.width;
  1.3682 +
  1.3683 +  // Paint our borders and background for our image rect.
  1.3684 +  PaintBackgroundLayer(checkboxContext, aPresContext, aRenderingContext, checkboxRect, aDirtyRect);
  1.3685 +
  1.3686 +  // Time to paint the checkbox.
  1.3687 +  // Adjust the rect for its border and padding.
  1.3688 +  nsMargin bp(0,0,0,0);
  1.3689 +  GetBorderPadding(checkboxContext, bp);
  1.3690 +  checkboxRect.Deflate(bp);
  1.3691 +
  1.3692 +  // Get the image for drawing.
  1.3693 +  nsCOMPtr<imgIContainer> image;
  1.3694 +  bool useImageRegion = true;
  1.3695 +  GetImage(aRowIndex, aColumn, true, checkboxContext, useImageRegion, getter_AddRefs(image));
  1.3696 +  if (image) {
  1.3697 +    nsPoint pt = checkboxRect.TopLeft();
  1.3698 +          
  1.3699 +    if (imageSize.height < checkboxRect.height) {
  1.3700 +      pt.y += (checkboxRect.height - imageSize.height)/2;
  1.3701 +    }
  1.3702 +
  1.3703 +    if (imageSize.width < checkboxRect.width) {
  1.3704 +      pt.x += (checkboxRect.width - imageSize.width)/2;
  1.3705 +    }
  1.3706 +
  1.3707 +    // Paint the image.
  1.3708 +    nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
  1.3709 +        GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect,
  1.3710 +        imgIContainer::FLAG_NONE, &imageSize);
  1.3711 +  }
  1.3712 +}
  1.3713 +
  1.3714 +void
  1.3715 +nsTreeBodyFrame::PaintProgressMeter(int32_t              aRowIndex,
  1.3716 +                                    nsTreeColumn*        aColumn,
  1.3717 +                                    const nsRect&        aProgressMeterRect,
  1.3718 +                                    nsPresContext*      aPresContext,
  1.3719 +                                    nsRenderingContext& aRenderingContext,
  1.3720 +                                    const nsRect&        aDirtyRect)
  1.3721 +{
  1.3722 +  NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
  1.3723 +
  1.3724 +  // Resolve style for the progress meter.  It contains all the info we need
  1.3725 +  // to lay ourselves out and to paint.
  1.3726 +  nsStyleContext* meterContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeprogressmeter);
  1.3727 +
  1.3728 +  // Obtain the margins for the progress meter and then deflate our rect by that 
  1.3729 +  // amount. The progress meter is assumed to be contained within the deflated
  1.3730 +  // rect.
  1.3731 +  nsRect meterRect(aProgressMeterRect);
  1.3732 +  nsMargin meterMargin;
  1.3733 +  meterContext->StyleMargin()->GetMargin(meterMargin);
  1.3734 +  meterRect.Deflate(meterMargin);
  1.3735 +
  1.3736 +  // Paint our borders and background for our progress meter rect.
  1.3737 +  PaintBackgroundLayer(meterContext, aPresContext, aRenderingContext, meterRect, aDirtyRect);
  1.3738 +
  1.3739 +  // Time to paint our progress. 
  1.3740 +  int32_t state;
  1.3741 +  mView->GetProgressMode(aRowIndex, aColumn, &state);
  1.3742 +  if (state == nsITreeView::PROGRESS_NORMAL) {
  1.3743 +    // Adjust the rect for its border and padding.
  1.3744 +    AdjustForBorderPadding(meterContext, meterRect);
  1.3745 +
  1.3746 +    // Set our color.
  1.3747 +    aRenderingContext.SetColor(meterContext->StyleColor()->mColor);
  1.3748 +
  1.3749 +    // Now obtain the value for our cell.
  1.3750 +    nsAutoString value;
  1.3751 +    mView->GetCellValue(aRowIndex, aColumn, value);
  1.3752 +
  1.3753 +    nsresult rv;
  1.3754 +    int32_t intValue = value.ToInteger(&rv);
  1.3755 +    if (intValue < 0)
  1.3756 +      intValue = 0;
  1.3757 +    else if (intValue > 100)
  1.3758 +      intValue = 100;
  1.3759 +
  1.3760 +    nscoord meterWidth = NSToCoordRound((float)intValue / 100 * meterRect.width);
  1.3761 +    if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
  1.3762 +      meterRect.x += meterRect.width - meterWidth; // right align
  1.3763 +    meterRect.width = meterWidth;
  1.3764 +    bool useImageRegion = true;
  1.3765 +    nsCOMPtr<imgIContainer> image;
  1.3766 +    GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image));
  1.3767 +    if (image) {
  1.3768 +      int32_t width, height;
  1.3769 +      image->GetWidth(&width);
  1.3770 +      image->GetHeight(&height);
  1.3771 +      nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(),
  1.3772 +                  height*nsDeviceContext::AppUnitsPerCSSPixel());
  1.3773 +      nsLayoutUtils::DrawImage(&aRenderingContext, image,
  1.3774 +          nsLayoutUtils::GetGraphicsFilterForFrame(this),
  1.3775 +          nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
  1.3776 +          aDirtyRect, imgIContainer::FLAG_NONE);
  1.3777 +    } else {
  1.3778 +      aRenderingContext.FillRect(meterRect);
  1.3779 +    }
  1.3780 +  }
  1.3781 +  else if (state == nsITreeView::PROGRESS_UNDETERMINED) {
  1.3782 +    // Adjust the rect for its border and padding.
  1.3783 +    AdjustForBorderPadding(meterContext, meterRect);
  1.3784 +
  1.3785 +    bool useImageRegion = true;
  1.3786 +    nsCOMPtr<imgIContainer> image;
  1.3787 +    GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image));
  1.3788 +    if (image) {
  1.3789 +      int32_t width, height;
  1.3790 +      image->GetWidth(&width);
  1.3791 +      image->GetHeight(&height);
  1.3792 +      nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(),
  1.3793 +                  height*nsDeviceContext::AppUnitsPerCSSPixel());
  1.3794 +      nsLayoutUtils::DrawImage(&aRenderingContext, image,
  1.3795 +          nsLayoutUtils::GetGraphicsFilterForFrame(this),
  1.3796 +          nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
  1.3797 +          aDirtyRect, imgIContainer::FLAG_NONE);
  1.3798 +    }
  1.3799 +  }
  1.3800 +}
  1.3801 +
  1.3802 +
  1.3803 +void
  1.3804 +nsTreeBodyFrame::PaintDropFeedback(const nsRect&        aDropFeedbackRect,
  1.3805 +                                   nsPresContext*      aPresContext,
  1.3806 +                                   nsRenderingContext& aRenderingContext,
  1.3807 +                                   const nsRect&        aDirtyRect,
  1.3808 +                                   nsPoint              aPt)
  1.3809 +{
  1.3810 +  // Paint the drop feedback in between rows.
  1.3811 +
  1.3812 +  nscoord currX;
  1.3813 +
  1.3814 +  // Adjust for the primary cell.
  1.3815 +  nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
  1.3816 +
  1.3817 +  if (primaryCol) {
  1.3818 +#ifdef DEBUG
  1.3819 +    nsresult rv =
  1.3820 +#endif
  1.3821 +      primaryCol->GetXInTwips(this, &currX);
  1.3822 +    NS_ASSERTION(NS_SUCCEEDED(rv), "primary column is invalid?");
  1.3823 +
  1.3824 +    currX += aPt.x - mHorzPosition;
  1.3825 +  } else {
  1.3826 +    currX = aDropFeedbackRect.x;
  1.3827 +  }
  1.3828 +
  1.3829 +  PrefillPropertyArray(mSlots->mDropRow, primaryCol);
  1.3830 +
  1.3831 +  // Resolve the style to use for the drop feedback.
  1.3832 +  nsStyleContext* feedbackContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreedropfeedback);
  1.3833 +
  1.3834 +  // Paint only if it is visible.
  1.3835 +  if (feedbackContext->StyleVisibility()->IsVisibleOrCollapsed()) {
  1.3836 +    int32_t level;
  1.3837 +    mView->GetLevel(mSlots->mDropRow, &level);
  1.3838 +
  1.3839 +    // If our previous or next row has greater level use that for 
  1.3840 +    // correct visual indentation.
  1.3841 +    if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) {
  1.3842 +      if (mSlots->mDropRow > 0) {
  1.3843 +        int32_t previousLevel;
  1.3844 +        mView->GetLevel(mSlots->mDropRow - 1, &previousLevel);
  1.3845 +        if (previousLevel > level)
  1.3846 +          level = previousLevel;
  1.3847 +      }
  1.3848 +    }
  1.3849 +    else {
  1.3850 +      if (mSlots->mDropRow < mRowCount - 1) {
  1.3851 +        int32_t nextLevel;
  1.3852 +        mView->GetLevel(mSlots->mDropRow + 1, &nextLevel);
  1.3853 +        if (nextLevel > level)
  1.3854 +          level = nextLevel;
  1.3855 +      }
  1.3856 +    }
  1.3857 +
  1.3858 +    currX += mIndentation * level;
  1.3859 +
  1.3860 +    if (primaryCol){
  1.3861 +      nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
  1.3862 +      nsRect imageSize;
  1.3863 +      nsRect twistyRect;
  1.3864 +      GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect, aPresContext,
  1.3865 +                    aRenderingContext, twistyContext);
  1.3866 +      nsMargin twistyMargin;
  1.3867 +      twistyContext->StyleMargin()->GetMargin(twistyMargin);
  1.3868 +      twistyRect.Inflate(twistyMargin);
  1.3869 +      currX += twistyRect.width;
  1.3870 +    }
  1.3871 +
  1.3872 +    const nsStylePosition* stylePosition = feedbackContext->StylePosition();
  1.3873 +
  1.3874 +    // Obtain the width for the drop feedback or use default value.
  1.3875 +    nscoord width;
  1.3876 +    if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord)
  1.3877 +      width = stylePosition->mWidth.GetCoordValue();
  1.3878 +    else {
  1.3879 +      // Use default width 50px.
  1.3880 +      width = nsPresContext::CSSPixelsToAppUnits(50);
  1.3881 +    }
  1.3882 +
  1.3883 +    // Obtain the height for the drop feedback or use default value.
  1.3884 +    nscoord height;
  1.3885 +    if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
  1.3886 +      height = stylePosition->mHeight.GetCoordValue();
  1.3887 +    else {
  1.3888 +      // Use default height 2px.
  1.3889 +      height = nsPresContext::CSSPixelsToAppUnits(2);
  1.3890 +    }
  1.3891 +
  1.3892 +    // Obtain the margins for the drop feedback and then deflate our rect
  1.3893 +    // by that amount.
  1.3894 +    nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height);
  1.3895 +    nsMargin margin;
  1.3896 +    feedbackContext->StyleMargin()->GetMargin(margin);
  1.3897 +    feedbackRect.Deflate(margin);
  1.3898 +
  1.3899 +    feedbackRect.y += (aDropFeedbackRect.height - height) / 2;
  1.3900 +
  1.3901 +    // Finally paint the drop feedback.
  1.3902 +    PaintBackgroundLayer(feedbackContext, aPresContext, aRenderingContext, feedbackRect, aDirtyRect);
  1.3903 +  }
  1.3904 +}
  1.3905 +
  1.3906 +void
  1.3907 +nsTreeBodyFrame::PaintBackgroundLayer(nsStyleContext*      aStyleContext,
  1.3908 +                                      nsPresContext*      aPresContext,
  1.3909 +                                      nsRenderingContext& aRenderingContext,
  1.3910 +                                      const nsRect&        aRect,
  1.3911 +                                      const nsRect&        aDirtyRect)
  1.3912 +{
  1.3913 +  const nsStyleBorder* myBorder = aStyleContext->StyleBorder();
  1.3914 +
  1.3915 +  nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext,
  1.3916 +                                        this, aDirtyRect, aRect,
  1.3917 +                                        aStyleContext, *myBorder,
  1.3918 +                                        nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES);
  1.3919 +
  1.3920 +  nsCSSRendering::PaintBorderWithStyleBorder(aPresContext, aRenderingContext,
  1.3921 +                                             this, aDirtyRect, aRect,
  1.3922 +                                             *myBorder, mStyleContext);
  1.3923 +
  1.3924 +  nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
  1.3925 +                               aDirtyRect, aRect, aStyleContext);
  1.3926 +}
  1.3927 +
  1.3928 +// Scrolling
  1.3929 +nsresult
  1.3930 +nsTreeBodyFrame::EnsureRowIsVisible(int32_t aRow)
  1.3931 +{
  1.3932 +  ScrollParts parts = GetScrollParts();
  1.3933 +  nsresult rv = EnsureRowIsVisibleInternal(parts, aRow);
  1.3934 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3935 +  UpdateScrollbars(parts);
  1.3936 +  return rv;
  1.3937 +}
  1.3938 +
  1.3939 +nsresult nsTreeBodyFrame::EnsureRowIsVisibleInternal(const ScrollParts& aParts, int32_t aRow)
  1.3940 +{
  1.3941 +  if (!mView || !mPageLength)
  1.3942 +    return NS_OK;
  1.3943 +
  1.3944 +  if (mTopRowIndex <= aRow && mTopRowIndex+mPageLength > aRow)
  1.3945 +    return NS_OK;
  1.3946 +
  1.3947 +  if (aRow < mTopRowIndex)
  1.3948 +    ScrollToRowInternal(aParts, aRow);
  1.3949 +  else {
  1.3950 +    // Bring it just on-screen.
  1.3951 +    int32_t distance = aRow - (mTopRowIndex+mPageLength)+1;
  1.3952 +    ScrollToRowInternal(aParts, mTopRowIndex+distance);
  1.3953 +  }
  1.3954 +
  1.3955 +  return NS_OK;
  1.3956 +}
  1.3957 +
  1.3958 +nsresult
  1.3959 +nsTreeBodyFrame::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol)
  1.3960 +{
  1.3961 +  nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
  1.3962 +  if (!col)
  1.3963 +    return NS_ERROR_INVALID_ARG;
  1.3964 +
  1.3965 +  ScrollParts parts = GetScrollParts();
  1.3966 +
  1.3967 +  nscoord result = -1;
  1.3968 +  nsresult rv;
  1.3969 +
  1.3970 +  nscoord columnPos;
  1.3971 +  rv = col->GetXInTwips(this, &columnPos);
  1.3972 +  if(NS_FAILED(rv)) return rv;
  1.3973 +
  1.3974 +  nscoord columnWidth;
  1.3975 +  rv = col->GetWidthInTwips(this, &columnWidth);
  1.3976 +  if(NS_FAILED(rv)) return rv;
  1.3977 +
  1.3978 +  // If the start of the column is before the
  1.3979 +  // start of the horizontal view, then scroll
  1.3980 +  if (columnPos < mHorzPosition)
  1.3981 +    result = columnPos;
  1.3982 +  // If the end of the column is past the end of 
  1.3983 +  // the horizontal view, then scroll
  1.3984 +  else if ((columnPos + columnWidth) > (mHorzPosition + mInnerBox.width))
  1.3985 +    result = ((columnPos + columnWidth) - (mHorzPosition + mInnerBox.width)) + mHorzPosition;
  1.3986 +
  1.3987 +  if (result != -1) {
  1.3988 +    rv = ScrollHorzInternal(parts, result);
  1.3989 +    if(NS_FAILED(rv)) return rv;
  1.3990 +  }
  1.3991 +
  1.3992 +  rv = EnsureRowIsVisibleInternal(parts, aRow);
  1.3993 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3994 +  UpdateScrollbars(parts);
  1.3995 +  return rv;
  1.3996 +}
  1.3997 +
  1.3998 +nsresult
  1.3999 +nsTreeBodyFrame::ScrollToCell(int32_t aRow, nsITreeColumn* aCol)
  1.4000 +{
  1.4001 +  ScrollParts parts = GetScrollParts();
  1.4002 +  nsresult rv = ScrollToRowInternal(parts, aRow);
  1.4003 +  NS_ENSURE_SUCCESS(rv, rv);
  1.4004 +
  1.4005 +  rv = ScrollToColumnInternal(parts, aCol);
  1.4006 +  NS_ENSURE_SUCCESS(rv, rv);
  1.4007 +
  1.4008 +  UpdateScrollbars(parts);
  1.4009 +  return rv;
  1.4010 +}
  1.4011 +
  1.4012 +nsresult
  1.4013 +nsTreeBodyFrame::ScrollToColumn(nsITreeColumn* aCol)
  1.4014 +{
  1.4015 +  ScrollParts parts = GetScrollParts();
  1.4016 +  nsresult rv = ScrollToColumnInternal(parts, aCol);
  1.4017 +  NS_ENSURE_SUCCESS(rv, rv);
  1.4018 +  UpdateScrollbars(parts);
  1.4019 +  return rv;
  1.4020 +}
  1.4021 +
  1.4022 +nsresult nsTreeBodyFrame::ScrollToColumnInternal(const ScrollParts& aParts,
  1.4023 +                                                 nsITreeColumn* aCol)
  1.4024 +{
  1.4025 +  nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
  1.4026 +  if (!col)
  1.4027 +    return NS_ERROR_INVALID_ARG;
  1.4028 +
  1.4029 +  nscoord x;
  1.4030 +  nsresult rv = col->GetXInTwips(this, &x);
  1.4031 +  if (NS_FAILED(rv))
  1.4032 +    return rv;
  1.4033 +
  1.4034 +  return ScrollHorzInternal(aParts, x);
  1.4035 +}
  1.4036 +
  1.4037 +nsresult
  1.4038 +nsTreeBodyFrame::ScrollToHorizontalPosition(int32_t aHorizontalPosition)
  1.4039 +{
  1.4040 +  ScrollParts parts = GetScrollParts();
  1.4041 +  int32_t position = nsPresContext::CSSPixelsToAppUnits(aHorizontalPosition);
  1.4042 +  nsresult rv = ScrollHorzInternal(parts, position);
  1.4043 +  NS_ENSURE_SUCCESS(rv, rv);
  1.4044 +  UpdateScrollbars(parts);
  1.4045 +  return rv;
  1.4046 +}
  1.4047 +
  1.4048 +nsresult
  1.4049 +nsTreeBodyFrame::ScrollToRow(int32_t aRow)
  1.4050 +{
  1.4051 +  ScrollParts parts = GetScrollParts();
  1.4052 +  nsresult rv = ScrollToRowInternal(parts, aRow);
  1.4053 +  NS_ENSURE_SUCCESS(rv, rv);
  1.4054 +  UpdateScrollbars(parts);
  1.4055 +  return rv;
  1.4056 +}
  1.4057 +
  1.4058 +nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow)
  1.4059 +{
  1.4060 +  ScrollInternal(aParts, aRow);
  1.4061 +
  1.4062 +  return NS_OK;
  1.4063 +}
  1.4064 +
  1.4065 +nsresult
  1.4066 +nsTreeBodyFrame::ScrollByLines(int32_t aNumLines)
  1.4067 +{
  1.4068 +  if (!mView)
  1.4069 +    return NS_OK;
  1.4070 +
  1.4071 +  int32_t newIndex = mTopRowIndex + aNumLines;
  1.4072 +  if (newIndex < 0)
  1.4073 +    newIndex = 0;
  1.4074 +  else {
  1.4075 +    int32_t lastPageTopRow = mRowCount - mPageLength;
  1.4076 +    if (newIndex > lastPageTopRow)
  1.4077 +      newIndex = lastPageTopRow;
  1.4078 +  }
  1.4079 +  ScrollToRow(newIndex);
  1.4080 +  
  1.4081 +  return NS_OK;
  1.4082 +}
  1.4083 +
  1.4084 +nsresult
  1.4085 +nsTreeBodyFrame::ScrollByPages(int32_t aNumPages)
  1.4086 +{
  1.4087 +  if (!mView)
  1.4088 +    return NS_OK;
  1.4089 +
  1.4090 +  int32_t newIndex = mTopRowIndex + aNumPages * mPageLength;
  1.4091 +  if (newIndex < 0)
  1.4092 +    newIndex = 0;
  1.4093 +  else {
  1.4094 +    int32_t lastPageTopRow = mRowCount - mPageLength;
  1.4095 +    if (newIndex > lastPageTopRow)
  1.4096 +      newIndex = lastPageTopRow;
  1.4097 +  }
  1.4098 +  ScrollToRow(newIndex);
  1.4099 +    
  1.4100 +  return NS_OK;
  1.4101 +}
  1.4102 +
  1.4103 +nsresult
  1.4104 +nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, int32_t aRow)
  1.4105 +{
  1.4106 +  if (!mView)
  1.4107 +    return NS_OK;
  1.4108 +
  1.4109 +  int32_t delta = aRow - mTopRowIndex;
  1.4110 +
  1.4111 +  if (delta > 0) {
  1.4112 +    if (mTopRowIndex == (mRowCount - mPageLength + 1))
  1.4113 +      return NS_OK;
  1.4114 +  }
  1.4115 +  else {
  1.4116 +    if (mTopRowIndex == 0)
  1.4117 +      return NS_OK;
  1.4118 +  }
  1.4119 +
  1.4120 +  mTopRowIndex += delta;
  1.4121 +
  1.4122 +  Invalidate();
  1.4123 +
  1.4124 +  PostScrollEvent();
  1.4125 +  return NS_OK;
  1.4126 +}
  1.4127 +
  1.4128 +nsresult
  1.4129 +nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition)
  1.4130 +{
  1.4131 +  if (!mView || !aParts.mColumnsScrollFrame || !aParts.mHScrollbar)
  1.4132 +    return NS_OK;
  1.4133 +
  1.4134 +  if (aPosition == mHorzPosition)
  1.4135 +    return NS_OK;
  1.4136 +
  1.4137 +  if (aPosition < 0 || aPosition > mHorzWidth)
  1.4138 +    return NS_OK;
  1.4139 +
  1.4140 +  nsRect bounds = aParts.mColumnsFrame->GetRect();
  1.4141 +  if (aPosition > (mHorzWidth - bounds.width)) 
  1.4142 +    aPosition = mHorzWidth - bounds.width;
  1.4143 +
  1.4144 +  mHorzPosition = aPosition;
  1.4145 +
  1.4146 +  Invalidate();
  1.4147 +
  1.4148 +  // Update the column scroll view
  1.4149 +  nsWeakFrame weakFrame(this);
  1.4150 +  aParts.mColumnsScrollFrame->ScrollTo(nsPoint(mHorzPosition, 0),
  1.4151 +                                       nsIScrollableFrame::INSTANT);
  1.4152 +  if (!weakFrame.IsAlive()) {
  1.4153 +    return NS_ERROR_FAILURE;
  1.4154 +  }
  1.4155 +  // And fire off an event about it all
  1.4156 +  PostScrollEvent();
  1.4157 +  return NS_OK;
  1.4158 +}
  1.4159 +
  1.4160 +NS_IMETHODIMP
  1.4161 +nsTreeBodyFrame::ScrollbarButtonPressed(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t aNewIndex)
  1.4162 +{
  1.4163 +  ScrollParts parts = GetScrollParts();
  1.4164 +
  1.4165 +  if (aScrollbar == parts.mVScrollbar) {
  1.4166 +    if (aNewIndex > aOldIndex)
  1.4167 +      ScrollToRowInternal(parts, mTopRowIndex+1);
  1.4168 +    else if (aNewIndex < aOldIndex)
  1.4169 +      ScrollToRowInternal(parts, mTopRowIndex-1);
  1.4170 +  } else {
  1.4171 +    nsresult rv = ScrollHorzInternal(parts, aNewIndex);
  1.4172 +    if (NS_FAILED(rv)) return rv;
  1.4173 +  }
  1.4174 +
  1.4175 +  UpdateScrollbars(parts);
  1.4176 +
  1.4177 +  return NS_OK;
  1.4178 +}
  1.4179 +  
  1.4180 +NS_IMETHODIMP
  1.4181 +nsTreeBodyFrame::PositionChanged(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t& aNewIndex)
  1.4182 +{
  1.4183 +  ScrollParts parts = GetScrollParts();
  1.4184 +  
  1.4185 +  if (aOldIndex == aNewIndex)
  1.4186 +    return NS_OK;
  1.4187 +
  1.4188 +  // Vertical Scrollbar 
  1.4189 +  if (parts.mVScrollbar == aScrollbar) {
  1.4190 +    nscoord rh = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
  1.4191 +
  1.4192 +    nscoord newrow = aNewIndex/rh;
  1.4193 +    ScrollInternal(parts, newrow);
  1.4194 +  // Horizontal Scrollbar
  1.4195 +  } else if (parts.mHScrollbar == aScrollbar) {
  1.4196 +    nsresult rv = ScrollHorzInternal(parts, aNewIndex);
  1.4197 +    if (NS_FAILED(rv)) return rv;
  1.4198 +  }
  1.4199 +
  1.4200 +  UpdateScrollbars(parts);
  1.4201 +  return NS_OK;
  1.4202 +}
  1.4203 +
  1.4204 +// The style cache.
  1.4205 +nsStyleContext*
  1.4206 +nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement)
  1.4207 +{
  1.4208 +  return mStyleCache.GetStyleContext(this, PresContext(), mContent,
  1.4209 +                                     mStyleContext, aPseudoElement,
  1.4210 +                                     mScratchArray);
  1.4211 +}
  1.4212 +
  1.4213 +// Our comparator for resolving our complex pseudos
  1.4214 +bool
  1.4215 +nsTreeBodyFrame::PseudoMatches(nsCSSSelector* aSelector)
  1.4216 +{
  1.4217 +  // Iterate the class list.  For each item in the list, see if
  1.4218 +  // it is contained in our scratch array.  If we have a miss, then
  1.4219 +  // we aren't a match.  If all items in the class list are
  1.4220 +  // present in the scratch array, then we have a match.
  1.4221 +  nsAtomList* curr = aSelector->mClassList;
  1.4222 +  while (curr) {
  1.4223 +    if (!mScratchArray.Contains(curr->mAtom))
  1.4224 +      return false;
  1.4225 +    curr = curr->mNext;
  1.4226 +  }
  1.4227 +  return true;
  1.4228 +}
  1.4229 +
  1.4230 +nsIContent*
  1.4231 +nsTreeBodyFrame::GetBaseElement()
  1.4232 +{
  1.4233 +  nsIFrame* parent = GetParent();
  1.4234 +  while (parent) {
  1.4235 +    nsIContent* content = parent->GetContent();
  1.4236 +    if (content) {
  1.4237 +      nsINodeInfo* ni = content->NodeInfo();
  1.4238 +
  1.4239 +      if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL) ||
  1.4240 +          (ni->Equals(nsGkAtoms::select) &&
  1.4241 +           content->IsHTML()))
  1.4242 +        return content;
  1.4243 +    }
  1.4244 +
  1.4245 +    parent = parent->GetParent();
  1.4246 +  }
  1.4247 +
  1.4248 +  return nullptr;
  1.4249 +}
  1.4250 +
  1.4251 +nsresult
  1.4252 +nsTreeBodyFrame::ClearStyleAndImageCaches()
  1.4253 +{
  1.4254 +  mStyleCache.Clear();
  1.4255 +  mImageCache.EnumerateRead(CancelImageRequest, this);
  1.4256 +  mImageCache.Clear();
  1.4257 +  return NS_OK;
  1.4258 +}
  1.4259 +
  1.4260 +/* virtual */ void
  1.4261 +nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
  1.4262 +{
  1.4263 +  nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
  1.4264 +
  1.4265 +  // Clear the style cache; the pointers are no longer even valid
  1.4266 +  mStyleCache.Clear();
  1.4267 +  // XXX The following is hacky, but it's not incorrect,
  1.4268 +  // and appears to fix a few bugs with style changes, like text zoom and
  1.4269 +  // dpi changes
  1.4270 +  mIndentation = GetIndentation();
  1.4271 +  mRowHeight = GetRowHeight();
  1.4272 +  mStringWidth = -1;
  1.4273 +}
  1.4274 +
  1.4275 +bool 
  1.4276 +nsTreeBodyFrame::OffsetForHorzScroll(nsRect& rect, bool clip)
  1.4277 +{
  1.4278 +  rect.x -= mHorzPosition;
  1.4279 +
  1.4280 +  // Scrolled out before
  1.4281 +  if (rect.XMost() <= mInnerBox.x)
  1.4282 +    return false;
  1.4283 +
  1.4284 +  // Scrolled out after
  1.4285 +  if (rect.x > mInnerBox.XMost())
  1.4286 +    return false;
  1.4287 +
  1.4288 +  if (clip) {
  1.4289 +    nscoord leftEdge = std::max(rect.x, mInnerBox.x);
  1.4290 +    nscoord rightEdge = std::min(rect.XMost(), mInnerBox.XMost());
  1.4291 +    rect.x = leftEdge;
  1.4292 +    rect.width = rightEdge - leftEdge;
  1.4293 +
  1.4294 +    // Should have returned false above
  1.4295 +    NS_ASSERTION(rect.width >= 0, "horz scroll code out of sync");
  1.4296 +  }
  1.4297 +
  1.4298 +  return true;
  1.4299 +}
  1.4300 +
  1.4301 +bool
  1.4302 +nsTreeBodyFrame::CanAutoScroll(int32_t aRowIndex)
  1.4303 +{
  1.4304 +  // Check first for partially visible last row.
  1.4305 +  if (aRowIndex == mRowCount - 1) {
  1.4306 +    nscoord y = mInnerBox.y + (aRowIndex - mTopRowIndex) * mRowHeight;
  1.4307 +    if (y < mInnerBox.height && y + mRowHeight > mInnerBox.height)
  1.4308 +      return true;
  1.4309 +  }
  1.4310 +
  1.4311 +  if (aRowIndex > 0 && aRowIndex < mRowCount - 1)
  1.4312 +    return true;
  1.4313 +
  1.4314 +  return false;
  1.4315 +}
  1.4316 +
  1.4317 +// Given a dom event, figure out which row in the tree the mouse is over,
  1.4318 +// if we should drop before/after/on that row or we should auto-scroll.
  1.4319 +// Doesn't query the content about if the drag is allowable, that's done elsewhere.
  1.4320 +//
  1.4321 +// For containers, we break up the vertical space of the row as follows: if in
  1.4322 +// the topmost 25%, the drop is _before_ the row the mouse is over; if in the
  1.4323 +// last 25%, _after_; in the middle 50%, we consider it a drop _on_ the container.
  1.4324 +//
  1.4325 +// For non-containers, if the mouse is in the top 50% of the row, the drop is
  1.4326 +// _before_ and the bottom 50% _after_
  1.4327 +void 
  1.4328 +nsTreeBodyFrame::ComputeDropPosition(WidgetGUIEvent* aEvent,
  1.4329 +                                     int32_t* aRow,
  1.4330 +                                     int16_t* aOrient,
  1.4331 +                                     int16_t* aScrollLines)
  1.4332 +{
  1.4333 +  *aOrient = -1;
  1.4334 +  *aScrollLines = 0;
  1.4335 +
  1.4336 +  // Convert the event's point to our coordinates.  We want it in
  1.4337 +  // the coordinates of our inner box's coordinates.
  1.4338 +  nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
  1.4339 +  int32_t xTwips = pt.x - mInnerBox.x;
  1.4340 +  int32_t yTwips = pt.y - mInnerBox.y;
  1.4341 +
  1.4342 +  *aRow = GetRowAt(xTwips, yTwips);
  1.4343 +  if (*aRow >=0) {
  1.4344 +    // Compute the top/bottom of the row in question.
  1.4345 +    int32_t yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex);
  1.4346 +   
  1.4347 +    bool isContainer = false;
  1.4348 +    mView->IsContainer (*aRow, &isContainer);
  1.4349 +    if (isContainer) {
  1.4350 +      // for a container, use a 25%/50%/25% breakdown
  1.4351 +      if (yOffset < mRowHeight / 4)
  1.4352 +        *aOrient = nsITreeView::DROP_BEFORE;
  1.4353 +      else if (yOffset > mRowHeight - (mRowHeight / 4))
  1.4354 +        *aOrient = nsITreeView::DROP_AFTER;
  1.4355 +      else
  1.4356 +        *aOrient = nsITreeView::DROP_ON;
  1.4357 +    }
  1.4358 +    else {
  1.4359 +      // for a non-container use a 50%/50% breakdown
  1.4360 +      if (yOffset < mRowHeight / 2)
  1.4361 +        *aOrient = nsITreeView::DROP_BEFORE;
  1.4362 +      else
  1.4363 +        *aOrient = nsITreeView::DROP_AFTER;
  1.4364 +    }
  1.4365 +  }
  1.4366 +
  1.4367 +  if (CanAutoScroll(*aRow)) {
  1.4368 +    // Get the max value from the look and feel service.
  1.4369 +    int32_t scrollLinesMax =
  1.4370 +      LookAndFeel::GetInt(LookAndFeel::eIntID_TreeScrollLinesMax, 0);
  1.4371 +    scrollLinesMax--;
  1.4372 +    if (scrollLinesMax < 0)
  1.4373 +      scrollLinesMax = 0;
  1.4374 +
  1.4375 +    // Determine if we're w/in a margin of the top/bottom of the tree during a drag.
  1.4376 +    // This will ultimately cause us to scroll, but that's done elsewhere.
  1.4377 +    nscoord height = (3 * mRowHeight) / 4;
  1.4378 +    if (yTwips < height) {
  1.4379 +      // scroll up
  1.4380 +      *aScrollLines = NSToIntRound(-scrollLinesMax * (1 - (float)yTwips / height) - 1);
  1.4381 +    }
  1.4382 +    else if (yTwips > mRect.height - height) {
  1.4383 +      // scroll down
  1.4384 +      *aScrollLines = NSToIntRound(scrollLinesMax * (1 - (float)(mRect.height - yTwips) / height) + 1);
  1.4385 +    }
  1.4386 +  }
  1.4387 +} // ComputeDropPosition
  1.4388 +
  1.4389 +void
  1.4390 +nsTreeBodyFrame::OpenCallback(nsITimer *aTimer, void *aClosure)
  1.4391 +{
  1.4392 +  nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  1.4393 +  if (self) {
  1.4394 +    aTimer->Cancel();
  1.4395 +    self->mSlots->mTimer = nullptr;
  1.4396 +
  1.4397 +    if (self->mSlots->mDropRow >= 0) {
  1.4398 +      self->mSlots->mArray.AppendElement(self->mSlots->mDropRow);
  1.4399 +      self->mView->ToggleOpenState(self->mSlots->mDropRow);
  1.4400 +    }
  1.4401 +  }
  1.4402 +}
  1.4403 +
  1.4404 +void
  1.4405 +nsTreeBodyFrame::CloseCallback(nsITimer *aTimer, void *aClosure)
  1.4406 +{
  1.4407 +  nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  1.4408 +  if (self) {
  1.4409 +    aTimer->Cancel();
  1.4410 +    self->mSlots->mTimer = nullptr;
  1.4411 +
  1.4412 +    for (uint32_t i = self->mSlots->mArray.Length(); i--; ) {
  1.4413 +      if (self->mView)
  1.4414 +        self->mView->ToggleOpenState(self->mSlots->mArray[i]);
  1.4415 +    }
  1.4416 +    self->mSlots->mArray.Clear();
  1.4417 +  }
  1.4418 +}
  1.4419 +
  1.4420 +void
  1.4421 +nsTreeBodyFrame::LazyScrollCallback(nsITimer *aTimer, void *aClosure)
  1.4422 +{
  1.4423 +  nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  1.4424 +  if (self) {
  1.4425 +    aTimer->Cancel();
  1.4426 +    self->mSlots->mTimer = nullptr;
  1.4427 +
  1.4428 +    if (self->mView) {
  1.4429 +      // Set a new timer to scroll the tree repeatedly.
  1.4430 +      self->CreateTimer(LookAndFeel::eIntID_TreeScrollDelay,
  1.4431 +                        ScrollCallback, nsITimer::TYPE_REPEATING_SLACK,
  1.4432 +                        getter_AddRefs(self->mSlots->mTimer));
  1.4433 +      self->ScrollByLines(self->mSlots->mScrollLines);
  1.4434 +      // ScrollByLines may have deleted |self|.
  1.4435 +    }
  1.4436 +  }
  1.4437 +}
  1.4438 +
  1.4439 +void
  1.4440 +nsTreeBodyFrame::ScrollCallback(nsITimer *aTimer, void *aClosure)
  1.4441 +{
  1.4442 +  nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
  1.4443 +  if (self) {
  1.4444 +    // Don't scroll if we are already at the top or bottom of the view.
  1.4445 +    if (self->mView && self->CanAutoScroll(self->mSlots->mDropRow)) {
  1.4446 +      self->ScrollByLines(self->mSlots->mScrollLines);
  1.4447 +    }
  1.4448 +    else {
  1.4449 +      aTimer->Cancel();
  1.4450 +      self->mSlots->mTimer = nullptr;
  1.4451 +    }
  1.4452 +  }
  1.4453 +}
  1.4454 +
  1.4455 +NS_IMETHODIMP
  1.4456 +nsTreeBodyFrame::ScrollEvent::Run()
  1.4457 +{
  1.4458 +  if (mInner) {
  1.4459 +    mInner->FireScrollEvent();
  1.4460 +  }
  1.4461 +  return NS_OK;
  1.4462 +}
  1.4463 +
  1.4464 +
  1.4465 +void
  1.4466 +nsTreeBodyFrame::FireScrollEvent()
  1.4467 +{
  1.4468 +  mScrollEvent.Forget();
  1.4469 +  WidgetGUIEvent event(true, NS_SCROLL_EVENT, nullptr);
  1.4470 +  // scroll events fired at elements don't bubble
  1.4471 +  event.mFlags.mBubbles = false;
  1.4472 +  EventDispatcher::Dispatch(GetContent(), PresContext(), &event);
  1.4473 +}
  1.4474 +
  1.4475 +void
  1.4476 +nsTreeBodyFrame::PostScrollEvent()
  1.4477 +{
  1.4478 +  if (mScrollEvent.IsPending())
  1.4479 +    return;
  1.4480 +
  1.4481 +  nsRefPtr<ScrollEvent> ev = new ScrollEvent(this);
  1.4482 +  if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
  1.4483 +    NS_WARNING("failed to dispatch ScrollEvent");
  1.4484 +  } else {
  1.4485 +    mScrollEvent = ev;
  1.4486 +  }
  1.4487 +}
  1.4488 +
  1.4489 +void
  1.4490 +nsTreeBodyFrame::ScrollbarActivityStarted() const
  1.4491 +{
  1.4492 +  if (mScrollbarActivity) {
  1.4493 +    mScrollbarActivity->ActivityStarted();
  1.4494 +  }
  1.4495 +}
  1.4496 +
  1.4497 +void
  1.4498 +nsTreeBodyFrame::ScrollbarActivityStopped() const
  1.4499 +{
  1.4500 +  if (mScrollbarActivity) {
  1.4501 +    mScrollbarActivity->ActivityStopped();
  1.4502 +  }
  1.4503 +}
  1.4504 +
  1.4505 +void
  1.4506 +nsTreeBodyFrame::DetachImageListeners()
  1.4507 +{
  1.4508 +  mCreatedListeners.Clear();
  1.4509 +}
  1.4510 +
  1.4511 +void
  1.4512 +nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener)
  1.4513 +{
  1.4514 +  if (aListener) {
  1.4515 +    mCreatedListeners.RemoveEntry(aListener);
  1.4516 +  }
  1.4517 +}
  1.4518 +
  1.4519 +#ifdef ACCESSIBILITY
  1.4520 +void
  1.4521 +nsTreeBodyFrame::FireRowCountChangedEvent(int32_t aIndex, int32_t aCount)
  1.4522 +{
  1.4523 +  nsCOMPtr<nsIContent> content(GetBaseElement());
  1.4524 +  if (!content)
  1.4525 +    return;
  1.4526 +
  1.4527 +  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc());
  1.4528 +  if (!domDoc)
  1.4529 +    return;
  1.4530 +
  1.4531 +  nsCOMPtr<nsIDOMEvent> event;
  1.4532 +  domDoc->CreateEvent(NS_LITERAL_STRING("customevent"),
  1.4533 +                      getter_AddRefs(event));
  1.4534 +
  1.4535 +  nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event));
  1.4536 +  if (!treeEvent)
  1.4537 +    return;
  1.4538 +
  1.4539 +  nsCOMPtr<nsIWritablePropertyBag2> propBag(
  1.4540 +    do_CreateInstance("@mozilla.org/hash-property-bag;1"));
  1.4541 +  if (!propBag)
  1.4542 +    return;
  1.4543 +
  1.4544 +  // Set 'index' data - the row index rows are changed from.
  1.4545 +  propBag->SetPropertyAsInt32(NS_LITERAL_STRING("index"), aIndex);
  1.4546 +
  1.4547 +  // Set 'count' data - the number of changed rows.
  1.4548 +  propBag->SetPropertyAsInt32(NS_LITERAL_STRING("count"), aCount);
  1.4549 +
  1.4550 +  nsCOMPtr<nsIWritableVariant> detailVariant(
  1.4551 +    do_CreateInstance("@mozilla.org/variant;1"));
  1.4552 +  if (!detailVariant)
  1.4553 +    return;
  1.4554 +
  1.4555 +  detailVariant->SetAsISupports(propBag);
  1.4556 +  treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeRowCountChanged"),
  1.4557 +                             true, false, detailVariant);
  1.4558 +
  1.4559 +  event->SetTrusted(true);
  1.4560 +
  1.4561 +  nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
  1.4562 +    new AsyncEventDispatcher(content, event);
  1.4563 +  asyncDispatcher->PostDOMEvent();
  1.4564 +}
  1.4565 +
  1.4566 +void
  1.4567 +nsTreeBodyFrame::FireInvalidateEvent(int32_t aStartRowIdx, int32_t aEndRowIdx,
  1.4568 +                                     nsITreeColumn *aStartCol,
  1.4569 +                                     nsITreeColumn *aEndCol)
  1.4570 +{
  1.4571 +  nsCOMPtr<nsIContent> content(GetBaseElement());
  1.4572 +  if (!content)
  1.4573 +    return;
  1.4574 +
  1.4575 +  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc());
  1.4576 +  if (!domDoc)
  1.4577 +    return;
  1.4578 +
  1.4579 +  nsCOMPtr<nsIDOMEvent> event;
  1.4580 +  domDoc->CreateEvent(NS_LITERAL_STRING("customevent"),
  1.4581 +                      getter_AddRefs(event));
  1.4582 +
  1.4583 +  nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event));
  1.4584 +  if (!treeEvent)
  1.4585 +    return;
  1.4586 +
  1.4587 +  nsCOMPtr<nsIWritablePropertyBag2> propBag(
  1.4588 +    do_CreateInstance("@mozilla.org/hash-property-bag;1"));
  1.4589 +  if (!propBag)
  1.4590 +    return;
  1.4591 +
  1.4592 +  if (aStartRowIdx != -1 && aEndRowIdx != -1) {
  1.4593 +    // Set 'startrow' data - the start index of invalidated rows.
  1.4594 +    propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startrow"),
  1.4595 +                                aStartRowIdx);
  1.4596 +
  1.4597 +    // Set 'endrow' data - the end index of invalidated rows.
  1.4598 +    propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endrow"),
  1.4599 +                                aEndRowIdx);
  1.4600 +  }
  1.4601 +
  1.4602 +  if (aStartCol && aEndCol) {
  1.4603 +    // Set 'startcolumn' data - the start index of invalidated rows.
  1.4604 +    int32_t startColIdx = 0;
  1.4605 +    nsresult rv = aStartCol->GetIndex(&startColIdx);
  1.4606 +    if (NS_FAILED(rv))
  1.4607 +      return;
  1.4608 +
  1.4609 +    propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"),
  1.4610 +                                startColIdx);
  1.4611 +
  1.4612 +    // Set 'endcolumn' data - the start index of invalidated rows.
  1.4613 +    int32_t endColIdx = 0;
  1.4614 +    rv = aEndCol->GetIndex(&endColIdx);
  1.4615 +    if (NS_FAILED(rv))
  1.4616 +      return;
  1.4617 +
  1.4618 +    propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"),
  1.4619 +                                endColIdx);
  1.4620 +  }
  1.4621 +
  1.4622 +  nsCOMPtr<nsIWritableVariant> detailVariant(
  1.4623 +    do_CreateInstance("@mozilla.org/variant;1"));
  1.4624 +  if (!detailVariant)
  1.4625 +    return;
  1.4626 +
  1.4627 +  detailVariant->SetAsISupports(propBag);
  1.4628 +  treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeInvalidated"),
  1.4629 +                             true, false, detailVariant);
  1.4630 +
  1.4631 +  event->SetTrusted(true);
  1.4632 +
  1.4633 +  nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
  1.4634 +    new AsyncEventDispatcher(content, event);
  1.4635 +  asyncDispatcher->PostDOMEvent();
  1.4636 +}
  1.4637 +#endif
  1.4638 +
  1.4639 +class nsOverflowChecker : public nsRunnable
  1.4640 +{
  1.4641 +public:
  1.4642 +  nsOverflowChecker(nsTreeBodyFrame* aFrame) : mFrame(aFrame) {}
  1.4643 +  NS_IMETHOD Run() MOZ_OVERRIDE
  1.4644 +  {
  1.4645 +    if (mFrame.IsAlive()) {
  1.4646 +      nsTreeBodyFrame* tree = static_cast<nsTreeBodyFrame*>(mFrame.GetFrame());
  1.4647 +      nsTreeBodyFrame::ScrollParts parts = tree->GetScrollParts();
  1.4648 +      tree->CheckOverflow(parts);
  1.4649 +    }
  1.4650 +    return NS_OK;
  1.4651 +  }
  1.4652 +private:
  1.4653 +  nsWeakFrame mFrame;
  1.4654 +};
  1.4655 +
  1.4656 +bool
  1.4657 +nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation)
  1.4658 +{
  1.4659 +  ScrollParts parts = GetScrollParts();
  1.4660 +  nsWeakFrame weakFrame(this);
  1.4661 +  nsWeakFrame weakColumnsFrame(parts.mColumnsFrame);
  1.4662 +  UpdateScrollbars(parts);
  1.4663 +  NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
  1.4664 +  if (aNeedsFullInvalidation) {
  1.4665 +    Invalidate();
  1.4666 +  }
  1.4667 +  InvalidateScrollbars(parts, weakColumnsFrame);
  1.4668 +  NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
  1.4669 +
  1.4670 +  // Overflow checking dispatches synchronous events, which can cause infinite
  1.4671 +  // recursion during reflow. Do the first overflow check synchronously, but
  1.4672 +  // force any nested checks to round-trip through the event loop. See bug
  1.4673 +  // 905909.
  1.4674 +  nsRefPtr<nsOverflowChecker> checker = new nsOverflowChecker(this);
  1.4675 +  if (!mCheckingOverflow) {
  1.4676 +    nsContentUtils::AddScriptRunner(checker);
  1.4677 +  } else {
  1.4678 +    NS_DispatchToCurrentThread(checker);
  1.4679 +  }
  1.4680 +  return weakFrame.IsAlive();
  1.4681 +}
  1.4682 +
  1.4683 +nsresult
  1.4684 +nsTreeBodyFrame::OnImageIsAnimated(imgIRequest* aRequest)
  1.4685 +{
  1.4686 +  nsLayoutUtils::RegisterImageRequest(PresContext(),
  1.4687 +                                      aRequest, nullptr);
  1.4688 +
  1.4689 +  return NS_OK;
  1.4690 +}

mercurial