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(¤tIndex); 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(¤tIndex); 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 +}