/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// See if the width is even smaller than the ellipsis // If so, clear the text completely. const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
aFontMetrics.SetTextRunRTL(false);
nscoord ellipsisWidth =
nsLayoutUtils::AppUnitWidthOfString(kEllipsis, aFontMetrics, drawTarget);
if (ellipsisWidth > aWidth) {
aText.Truncate(0); return;
} if (ellipsisWidth == aWidth) {
aText.Assign(kEllipsis); return;
}
// We will be drawing an ellipsis, thank you very much. // Subtract out the required width of the ellipsis. // This is the total remaining width we have to play with.
aWidth -= ellipsisWidth;
using mozilla::intl::GraphemeClusterBreakIteratorUtf16; using mozilla::intl::GraphemeClusterBreakReverseIteratorUtf16;
// Now we crop. This is quite basic: it will not be really accurate in the // presence of complex scripts with contextual shaping, etc., as it measures // each grapheme cluster in isolation, not in its proper context. switch (aCropType) { case CropAuto: case CropNone: case CropRight: { const Span text(aText);
GraphemeClusterBreakIteratorUtf16 iter(text);
uint32_t pos = 0;
nscoord totalWidth = 0;
// nextPos is decreasing since we use a reverse iterator. while (Maybe<uint32_t> nextPos = iter.Next()) { const nscoord charWidth = nsLayoutUtils::AppUnitWidthOfString(
text.FromTo(*nextPos, pos), aFontMetrics, drawTarget); if (totalWidth + charWidth > aWidth) { break;
}
staticvoid DoCancelImageCacheEntry(const nsTreeImageCacheEntry& aEntry,
nsPresContext* aPc) { // If our imgIRequest object was registered with the refresh driver // then we need to deregister it.
aEntry.listener->ClearFrame();
nsLayoutUtils::DeregisterImageRequest(aPc, aEntry.request, nullptr);
aEntry.request->UnlockImage();
aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
// Function that cancels all the image requests in our cache. void nsTreeBodyFrame::CancelImageRequests() { auto* pc = PresContext(); for (const nsTreeImageCacheEntry& entry : mImageCache.Values()) {
DoCancelImageCacheEntry(entry, pc);
}
}
// // NS_NewTreeFrame // // Creates a new tree frame //
nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle) { returnnew (aPresShell) nsTreeBodyFrame(aStyle, aPresShell->GetPresContext());
}
mScrollEvent.Revoke(); // Make sure we cancel any posted callbacks. if (mReflowCallbackPosted) {
PresShell()->CancelReflowCallback(this);
mReflowCallbackPosted = false;
}
if (mColumns) {
mColumns->SetTree(nullptr);
}
RefPtr tree = mTree;
if (nsCOMPtr<nsITreeView> view = std::move(mView)) {
nsCOMPtr<nsITreeSelection> sel;
view->GetSelection(getter_AddRefs(sel)); if (sel) {
sel->SetTree(nullptr);
}
view->SetTree(nullptr);
}
// Make this call now because view->SetTree can run js which can undo this // call. if (tree) {
tree->BodyDestroyed(mTopRowIndex);
} if (mTree && mTree != tree) {
mTree->BodyDestroyed(mTopRowIndex);
}
SimpleXULLeafFrame::Destroy(aContext);
}
void nsTreeBodyFrame::EnsureView() { if (mView) { return;
}
if (PresShell()->IsReflowLocked()) { if (!mReflowCallbackPosted) {
mReflowCallbackPosted = true;
PresShell()->PostReflowCallback(this);
} return;
}
AutoWeakFrame weakFrame(this);
RefPtr<XULTreeElement> tree = GetBaseElement(); if (!tree) { return;
}
nsCOMPtr<nsITreeView> treeView = tree->GetView(); if (!treeView || !weakFrame.IsAlive()) { return;
}
int32_t rowIndex = tree->GetCachedTopVisibleRow();
// Set our view.
SetView(treeView);
NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
// Scroll to the given row. // XXX is this optimal if we haven't laid out yet?
ScrollToRow(rowIndex);
NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
}
void nsTreeBodyFrame::ManageReflowCallback() { const nscoord horzWidth = mRect.width; if (!mReflowCallbackPosted) { if (!mLastReflowRect || !mLastReflowRect->IsEqualEdges(mRect) ||
mHorzWidth != horzWidth) {
PresShell()->PostReflowCallback(this);
mReflowCallbackPosted = true;
mOriginalHorzWidth = mHorzWidth;
}
} elseif (mHorzWidth != horzWidth && mOriginalHorzWidth == horzWidth) { // FIXME(emilio): This doesn't seem sound to me, if the rect changes in the // block axis.
PresShell()->CancelReflowCallback(this);
mReflowCallbackPosted = false;
mOriginalHorzWidth = -1;
}
mLastReflowRect = Some(mRect);
mHorzWidth = horzWidth;
}
XULTreeElement* treeContent = GetBaseElement(); if (treeContent && treeContent->AttrValueIs(
kNameSpaceID_None, nsGkAtoms::keepcurrentinview,
nsGkAtoms::_true, eCaseMatters)) { // make sure that the current selected item is still // visible after the tree changes size. if (nsCOMPtr<nsITreeSelection> sel = GetSelection()) {
int32_t currentIndex;
sel->GetCurrentIndex(¤tIndex); if (currentIndex != -1) {
EnsureRowIsVisibleInternal(parts, currentIndex);
}
}
}
if (!FullScrollbarsUpdate(false)) { returnfalse;
}
}
if (aView) { // Give the view a new empty selection object to play with, but only if it // doesn't have one already.
nsCOMPtr<nsITreeSelection> sel;
aView->GetSelection(getter_AddRefs(sel)); if (sel) {
sel->SetTree(treeContent);
} else {
NS_NewTreeSelection(treeContent, getter_AddRefs(sel));
aView->SetSelection(sel);
}
// View, meet the tree.
AutoWeakFrame weakFrame(this);
aView->SetTree(treeContent);
NS_ENSURE_STATE(weakFrame.IsAlive());
aView->GetRowCount(&mRowCount);
if (!PresShell()->IsReflowLocked()) { // The scrollbar will need to be updated.
FullScrollbarsUpdate(false);
} elseif (!mReflowCallbackPosted) {
mReflowCallbackPosted = true;
PresShell()->PostReflowCallback(this);
}
}
// iterate through the visible rows and add the selected ones to the // drag region
int32_t x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
int32_t y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
int32_t top = y;
int32_t end = LastVisibleRow();
int32_t rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); for (int32_t i = mTopRowIndex; i <= end; i++) { bool isSelected;
selection->IsSelected(i, &isSelected); if (isSelected) {
region.OrWith(CSSIntRect(x, y, rect.width, rowHeight));
}
y += rowHeight;
}
// clip to the tree boundary in case one row extends past it
region.AndWith(CSSIntRect(x, top, rect.width, rect.height));
return Some(region);
}
nsresult nsTreeBodyFrame::Invalidate() { if (mUpdateBatchNest) { return NS_OK;
}
InvalidateFrame();
return NS_OK;
}
nsresult nsTreeBodyFrame::InvalidateColumn(nsTreeColumn* aCol) { if (mUpdateBatchNest) { return NS_OK;
}
nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts() {
ScrollParts result;
XULTreeElement* tree = GetBaseElement(); if (nsIFrame* treeFrame = tree ? tree->GetPrimaryFrame() : nullptr) { // The way we do this, searching through the entire frame subtree, is pretty // dumb! We should know where these frames are.
FindScrollParts(treeFrame, &result); if (result.mVScrollbar) {
result.mVScrollbar->SetScrollbarMediatorContent(GetContent());
result.mVScrollbarContent = result.mVScrollbar->GetContent()->AsElement();
}
} return result;
}
// The synchronous event dispatch above can trigger reflow notifications. // Flush those explicitly now, so that we can guard against potential infinite // recursion. See bug 905909. if (!weakFrame.IsAlive()) { return;
}
NS_ASSERTION(!mCheckingOverflow, "mCheckingOverflow should not already be set"); // Don't use AutoRestore since we want to not touch mCheckingOverflow if we // fail the weakFrame.IsAlive() check below
mCheckingOverflow = true;
presShell->FlushPendingNotifications(FlushType::Layout); if (!weakFrame.IsAlive()) { return;
}
mCheckingOverflow = false;
}
// Also set our page increment and decrement.
nscoord pageincrement = mPageLength * rowHeightAsPixels;
nsAutoString pageStr;
pageStr.AppendInt(pageincrement);
aParts.mVScrollbarContent->SetAttr(kNameSpaceID_None,
nsGkAtoms::pageincrement, pageStr, true);
NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
}
if (weakFrame.IsAlive() && mScrollbarActivity) {
mScrollbarActivity->ActivityOccurred();
}
}
// Takes client x/y in pixels, converts them to appunits, and converts into // values relative to this nsTreeBodyFrame frame.
nsPoint nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(int32_t aX,
int32_t aY) {
nsPoint point(nsPresContext::CSSPixelsToAppUnits(aX),
nsPresContext::CSSPixelsToAppUnits(aY));
nsPresContext* presContext = PresContext();
point -= GetOffsetTo(presContext->GetPresShell()->GetRootFrame());
// Adjust by the inner box coords, so that we're in the inner box's // coordinate space.
point -= mInnerBox.TopLeft(); return point;
} // AdjustClientCoordsToBoxCoordSpace
// // GetCoordsForCellItem // // Find the x/y location and width/height (all in PIXELS) of the given object // in the given column. // // XXX IMPORTANT XXX: // Hyatt says in the bug for this, that the following needs to be done: // (1) You need to deal with overflow when computing cell rects. See other // column iteration examples... if you don't deal with this, you'll mistakenly // extend the cell into the scrollbar's rect. // // (2) You are adjusting the cell rect by the *row" border padding. That's // wrong. You need to first adjust a row rect by its border/padding, and then // the cell rect fits inside the adjusted row rect. It also can have // border/padding as well as margins. The vertical direction isn't that // important, but you need to get the horizontal direction right. // // (3) GetImageSize() does not include margins (but it does include // border/padding). You need to make sure to add in the image's margins as well. //
nsresult nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsTreeColumn* aCol, const nsACString& aElement,
int32_t* aX, int32_t* aY,
int32_t* aWidth,
int32_t* aHeight) {
*aX = 0;
*aY = 0;
*aWidth = 0;
*aHeight = 0;
// The Rect for the requested item.
nsRect theRect;
nsPresContext* presContext = PresContext();
nsCOMPtr<nsITreeView> view = GetExistingView();
for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
currCol = currCol->GetNext()) { // The Rect for the current cell.
nscoord colWidth; #ifdef DEBUG
nsresult rv = #endif
currCol->GetWidthInTwips(this, &colWidth);
NS_ASSERTION(NS_SUCCEEDED(rv), "invalid column");
// Check the ID of the current column to see if it matches. If it doesn't // increment the current X value and continue to the next column. if (currCol != aCol) {
currX += cellRect.width; continue;
} // Now obtain the properties for our cell.
PrefillPropertyArray(aRow, currCol);
// We don't want to consider any of the decorations that may be present // on the current row, so we have to deflate the rect by the border and // padding and offset its left and top coordinates appropriately.
AdjustForBorderPadding(rowContext, cellRect);
constexpr auto cell = "cell"_ns; if (currCol->IsCycler() || cell.Equals(aElement)) { // If the current Column is a Cycler, then the Rect is just the cell - the // margins. Similarly, if we're just being asked for the cell rect, // provide it.
// Since we're not looking for the cell, and since the cell isn't a cycler, // we're looking for some subcomponent, and now we need to subtract the // borders and padding of the cell from cellRect so this does not // interfere with our computations.
AdjustForBorderPadding(cellContext, cellRect);
// Now we'll start making our way across the cell, starting at the edge of // the cell and proceeding until we hit the right edge. |cellX| is the // working X value that we will increment as we crawl from left to right.
nscoord cellX = cellRect.x;
nscoord remainWidth = cellRect.width;
if (currCol->IsPrimary()) { // If the current Column is a Primary, then we need to take into account // the indentation and possibly a twisty.
// The amount of indentation is the indentation width (|mIndentation|) by // the level.
int32_t level;
view->GetLevel(aRow, &level); if (!isRTL) {
cellX += mIndentation * level;
}
remainWidth -= mIndentation * level;
// Find the twisty rect by computing its size.
nsRect imageRect;
nsRect twistyRect(cellRect);
ComputedStyle* twistyContext =
GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext,
twistyContext);
if ("twisty"_ns.Equals(aElement)) { // If we're looking for the twisty Rect, just return the size
theRect = twistyRect; break;
}
// Now we need to add in the margins of the twisty element, so that we // can find the offset of the next element in the cell.
nsMargin twistyMargin;
twistyContext->StyleMargin()->GetMargin(twistyMargin);
twistyRect.Inflate(twistyMargin);
// Adjust our working X value with the twisty width (image size, margins, // borders, padding. if (!isRTL) {
cellX += twistyRect.width;
}
}
// Add in the margins of the cell image.
nsMargin imageMargin;
imageContext->StyleMargin()->GetMargin(imageMargin);
imageSize.Inflate(imageMargin);
// Increment cellX by the image width if (!isRTL) {
cellX += imageSize.width;
}
// Cell Text
nsAutoString cellText;
view->GetCellText(aRow, currCol, cellText); // We're going to measure this text so we need to ensure bidi is enabled if // necessary
CheckTextForBidi(cellText);
// Create a scratch rect to represent the text rectangle, with the current // X and Y coords, and a guess at the width and height. The width is the // remaining width we have left to traverse in the cell, which will be the // widest possible value for the text rect, and the row height.
nsRect textRect(cellX, cellRect.y, remainWidth, cellRect.height);
// Measure the width of the text. If the width of the text is greater than // the remaining width available, then we just assume that the text has // been cropped and use the remaining rect as the text Rect. Otherwise, // we add in borders and padding to the text dimension and give that back.
ComputedStyle* textContext =
GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCellText());
RefPtr<nsFontMetrics> fm =
nsLayoutUtils::GetFontMetricsForComputedStyle(textContext, presContext);
nscoord height = fm->MaxHeight();
// Now just mod by our total inner box height and add to our top row index.
int32_t row = (aY / mRowHeight) + mTopRowIndex;
// Check if the coordinates are below our visible space (or within our visible // space but below any row). if (row > mTopRowIndex + mPageLength || row >= mRowCount) { return -1;
}
return row;
}
void nsTreeBodyFrame::CheckTextForBidi(nsAutoString& aText) { // We could check to see whether the prescontext already has bidi enabled, // but usually it won't, so it's probably faster to avoid the call to // GetPresContext() when it's not needed. if (HasRTLChars(aText)) {
PresContext()->SetBidiEnabled();
}
}
nsCOMPtr<nsITreeView> view = GetExistingView(); if (aColumn->Overflow()) {
DebugOnly<nsresult> rv;
nsTreeColumn* nextColumn = aColumn->GetNext(); while (nextColumn && widthIsGreater) { while (nextColumn) {
nscoord width;
rv = nextColumn->GetWidthInTwips(this, &width);
NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
if (width != 0) { break;
}
nextColumn = nextColumn->GetNext();
}
if (nextColumn) {
nsAutoString nextText;
view->GetCellText(aRowIndex, nextColumn, nextText); // We don't measure or draw this text so no need to check it for // bidi-ness
if (nextText.Length() == 0) {
nscoord width;
rv = nextColumn->GetWidthInTwips(this, &width);
NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
// Obtain the properties for our cell.
PrefillPropertyArray(aRowIndex, aColumn);
nsAutoString properties;
nsCOMPtr<nsITreeView> view = GetExistingView();
view->GetCellProperties(aRowIndex, aColumn, properties);
nsTreeUtils::TokenizeProperties(properties, mScratchArray);
// Resolve style for the cell.
ComputedStyle* cellContext =
GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCell());
// Obtain the margins for the cell and then deflate our rect by that // amount. The cell is assumed to be contained within the deflated rect.
nsRect cellRect(aCellRect);
nsMargin cellMargin;
cellContext->StyleMargin()->GetMargin(cellMargin);
cellRect.Deflate(cellMargin);
// Adjust the rect for its border and padding.
AdjustForBorderPadding(cellContext, cellRect);
if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) { // The user clicked within the cell's margins/borders/padding. This // constitutes a click on the cell. return nsCSSAnonBoxes::mozTreeCell();
}
if ((isRTL && aX > currX + remainingWidth) || (!isRTL && aX < currX)) { // The user clicked within the indentation. return nsCSSAnonBoxes::mozTreeCell();
}
// Always leave space for the twisty.
nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height); bool hasTwisty = false; bool isContainer = false;
view->IsContainer(aRowIndex, &isContainer); if (isContainer) { bool isContainerEmpty = false;
view->IsContainerEmpty(aRowIndex, &isContainerEmpty); if (!isContainerEmpty) {
hasTwisty = true;
}
}
// Resolve style for the twisty.
ComputedStyle* twistyContext =
GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
// We will treat a click as hitting the twisty if it happens on the margins, // borders, padding, or content of the twisty object. By allowing a "slop" // into the margin, we make it a little bit easier for a user to hit the // twisty. (We don't want to be too picky here.)
nsMargin twistyMargin;
twistyContext->StyleMargin()->GetMargin(twistyMargin);
twistyRect.Inflate(twistyMargin); if (isRTL) {
twistyRect.x = currX + remainingWidth - twistyRect.width;
}
// Now we test to see if aX is actually within the twistyRect. If it is, // and if the item should have a twisty, then we return "twisty". If it is // within the rect but we shouldn't have a twisty, then we return "cell". if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) { if (hasTwisty) { return nsCSSAnonBoxes::mozTreeTwisty();
} return nsCSSAnonBoxes::mozTreeCell();
}
if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) { // The user clicked on the image. return nsCSSAnonBoxes::mozTreeImage();
}
if (!isRTL) {
currX += iconRect.width;
}
remainingWidth -= iconRect.width;
nsAutoString cellText;
view->GetCellText(aRowIndex, aColumn, cellText); // We're going to measure this text so we need to ensure bidi is enabled if // necessary
CheckTextForBidi(cellText);
// Determine the column hit. for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
currCol = currCol->GetNext()) {
nsRect cellRect;
nsresult rv = currCol->GetRect( this, mInnerBox.y + mRowHeight * (*aRow - mTopRowIndex), mRowHeight,
&cellRect); if (NS_FAILED(rv)) {
MOZ_ASSERT_UNREACHABLE("column has no frame"); continue;
}
if (!OffsetForHorzScroll(cellRect, false)) { continue;
}
if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) { // We know the column hit now.
*aCol = currCol;
if (currCol->IsCycler()) { // Cyclers contain only images. Fill this in immediately and return.
*aChildElt = nsCSSAnonBoxes::mozTreeImage();
} else {
*aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol);
} break;
}
}
}
nsresult nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
gfxContext* aRenderingContext,
nscoord& aDesiredSize,
nscoord& aCurrentSize) {
MOZ_ASSERT(aCol, "aCol must not be null");
MOZ_ASSERT(aRenderingContext, "aRenderingContext must not be null");
// The rect for the current cell.
nscoord colWidth;
nsresult rv = aCol->GetWidthInTwips(this, &colWidth);
NS_ENSURE_SUCCESS(rv, rv);
if (aCol->IsPrimary()) { // If the current Column is a Primary, then we need to take into account // the indentation and possibly a twisty.
// The amount of indentation is the indentation width (|mIndentation|) by // the level.
int32_t level;
view->GetLevel(aRow, &level);
aDesiredSize += mIndentation * level;
// Find the twisty rect by computing its size.
ComputedStyle* twistyContext =
GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeTwisty());
// Add in the margins of the twisty element.
nsMargin twistyMargin;
twistyContext->StyleMargin()->GetMargin(twistyMargin);
twistyRect.Inflate(twistyMargin);
// Account for the width of the cell image.
nsRect imageSize = GetImageSize(aRow, aCol, false, imageContext); // Add in the margins of the cell image.
nsMargin imageMargin;
imageContext->StyleMargin()->GetMargin(imageMargin);
imageSize.Inflate(imageMargin);
aDesiredSize += imageSize.width;
// Get the cell text.
nsAutoString cellText;
view->GetCellText(aRow, aCol, cellText); // We're going to measure this text so we need to ensure bidi is enabled if // necessary
CheckTextForBidi(cellText);
nsresult nsTreeBodyFrame::CreateTimer(const LookAndFeel::IntID aID,
nsTimerCallbackFunc aFunc, int32_t aType,
nsITimer** aTimer, constchar* aName) { // Get the delay from the look and feel service.
int32_t delay = LookAndFeel::GetInt(aID, 0);
nsCOMPtr<nsITimer> timer;
// Create a new timer only if the delay is greater than zero. // Zero value means that this feature is completely disabled. if (delay > 0) {
MOZ_TRY_VAR(timer,
NS_NewTimerWithFuncCallback(aFunc, this, delay, aType, aName,
GetMainThreadSerialEventTarget()));
}
timer.forget(aTimer); return NS_OK;
}
nsresult nsTreeBodyFrame::RowCountChanged(int32_t aIndex, int32_t aCount) { if (aCount == 0 || !mView) { return NS_OK; // Nothing to do.
}
// odd or even if (aRowIndex % 2) {
mScratchArray.AppendElement(nsGkAtoms::odd);
} else {
mScratchArray.AppendElement(nsGkAtoms::even);
}
XULTreeElement* tree = GetBaseElement(); if (tree && tree->HasAttr(nsGkAtoms::editing)) {
mScratchArray.AppendElement(nsGkAtoms::editing);
}
// multiple columns if (mColumns->GetColumnAt(1)) {
mScratchArray.AppendElement(nsGkAtoms::multicol);
}
}
if (aCol) {
mScratchArray.AppendElement(aCol->GetAtom());
if (aCol->IsPrimary()) {
mScratchArray.AppendElement(nsGkAtoms::primary);
}
if (aCol->GetType() == TreeColumn_Binding::TYPE_CHECKBOX) {
mScratchArray.AppendElement(nsGkAtoms::checkbox);
if (aRowIndex != -1) {
nsAutoString value;
mView->GetCellValue(aRowIndex, aCol, value); if (value.EqualsLiteral("true")) {
mScratchArray.AppendElement(nsGkAtoms::checked);
}
}
}
if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ordinal,
u"1"_ns, eIgnoreCase)) {
mScratchArray.AppendElement(nsGkAtoms::firstColumn);
}
// Read special properties from attributes on the column content node if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::insertbefore,
nsGkAtoms::_true, eCaseMatters)) {
mScratchArray.AppendElement(nsGkAtoms::insertbefore);
} if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::insertafter,
nsGkAtoms::_true, eCaseMatters)) {
mScratchArray.AppendElement(nsGkAtoms::insertafter);
}
}
}
void nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, nsTreeColumn* aColumn,
nsRect& aImageRect, nsRect& aTwistyRect,
nsPresContext* aPresContext,
ComputedStyle* aTwistyContext) { // The twisty rect extends all the way to the end of the cell. This is // incorrect. We need to determine the twisty rect's true width. This is // done by examining the ComputedStyle for a width first. If it has one, we // use that. If it doesn't, we use the image's natural width. If the image // hasn't loaded and if no width is specified, then we just bail. If there is // a -moz-appearance involved, adjust the rect by the minimum widget size // provided by the theme implementation.
aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext); if (aImageRect.height > aTwistyRect.height) {
aImageRect.height = aTwistyRect.height;
} if (aImageRect.width > aTwistyRect.width) {
aImageRect.width = aTwistyRect.width;
} else {
aTwistyRect.width = aImageRect.width;
}
}
already_AddRefed<imgIContainer> nsTreeBodyFrame::GetImage(
int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
ComputedStyle* aComputedStyle) {
Document* doc = PresContext()->Document();
nsAutoString imageSrc;
mView->GetImageSrc(aRowIndex, aCol, imageSrc);
RefPtr<imgRequestProxy> styleRequest;
nsCOMPtr<nsIURI> uri; if (aUseContext || imageSrc.IsEmpty()) { // Obtain the URL from the ComputedStyle.
styleRequest =
aComputedStyle->StyleList()->mListStyleImage.GetImageRequest(); if (!styleRequest) { return nullptr;
}
styleRequest->GetURI(getter_AddRefs(uri));
} else {
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), imageSrc,
doc, mContent->GetBaseURI());
} if (!uri) { return nullptr;
} // Look the image up in our cache.
nsTreeImageCacheEntry entry; if (mImageCache.Get(uri, &entry)) {
nsCOMPtr<imgIContainer> result;
entry.request->GetImage(getter_AddRefs(result));
entry.listener->AddCell(aRowIndex, aCol); return result.forget();
}
// Create a new nsTreeImageListener object and pass it our row and column // information. auto listener = MakeRefPtr<nsTreeImageListener>(this);
listener->AddCell(aRowIndex, aCol);
RefPtr<imgRequestProxy> imageRequest; if (styleRequest) {
styleRequest->SyncClone(listener, doc, getter_AddRefs(imageRequest));
} else { auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc); // XXXbz what's the origin principal for this stuff that comes from our // view? I guess we should assume that it's the node's principal...
nsContentUtils::LoadImage(uri, mContent, doc, mContent->NodePrincipal(), 0,
referrerInfo, listener, nsIRequest::LOAD_NORMAL,
u""_ns, getter_AddRefs(imageRequest));
}
listener->UnsuppressInvalidation(); if (!imageRequest) { return nullptr;
}
// NOTE(heycam): If it's an SVG image, and we need to want the image to // able to respond to media query changes, it needs to be added to the // document's ImageTracker. For now, assume we don't need this. // We don't want discarding/decode-on-draw for xul images
imageRequest->StartDecoding(imgIContainer::FLAG_ASYNC_NOTIFY);
imageRequest->LockImage();
// In a case it was already cached.
mImageCache.InsertOrUpdate(uri,
nsTreeImageCacheEntry(imageRequest, listener));
nsCOMPtr<imgIContainer> result;
imageRequest->GetImage(getter_AddRefs(result)); return result.forget();
}
nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
ComputedStyle* aComputedStyle) { // XXX We should respond to visibility rules for collapsed vs. hidden.
// This method returns the width of the twisty INCLUDING borders and padding. // It first checks the ComputedStyle for a width. If none is found, it tries // to use the default image width for the twisty. If no image is found, it // defaults to border+padding.
nsRect r(0, 0, 0, 0);
nsMargin bp(0, 0, 0, 0);
GetBorderPadding(aComputedStyle, bp);
r.Inflate(bp);
// Now r contains our border+padding info. We now need to get our width and // height. bool needWidth = false; bool needHeight = false;
// We have to load image even though we already have a size. // Don't change this, otherwise things start to go awry.
nsCOMPtr<imgIContainer> image =
GetImage(aRowIndex, aCol, aUseContext, aComputedStyle);
// GetImageDestSize returns the destination size of the image. // The width and height do not include borders and padding. // The width and height have not been adjusted to fit in the row height // or cell width. // The width and height reflect the destination size specified in CSS, // or the image region specified in CSS, or the natural size of the // image. // If only the destination width has been specified in CSS, the height is // calculated to maintain the aspect ratio of the image. // If only the destination height has been specified in CSS, the width is // calculated to maintain the aspect ratio of the image.
nsSize nsTreeBodyFrame::GetImageDestSize(ComputedStyle* aComputedStyle,
imgIContainer* image) {
nsSize size(0, 0);
// We need to get the width and height. bool needWidth = false; bool needHeight = false;
// Get the style position to see if the CSS has specified the // destination width/height. const nsStylePosition* myPosition = aComputedStyle->StylePosition();
if (myPosition->GetWidth().ConvertsToLength()) { // CSS has specified the destination width.
size.width = myPosition->GetWidth().ToLength();
} else { // We'll need to get the width of the image/region.
needWidth = true;
}
if (myPosition->GetHeight().ConvertsToLength()) { // CSS has specified the destination height.
size.height = myPosition->GetHeight().ToLength();
} else { // We'll need to get the height of the image/region.
needHeight = true;
}
if (needWidth || needHeight) { // We need to get the size of the image/region.
nsSize imageSize(0, 0); if (image) {
nscoord width;
image->GetWidth(&width);
imageSize.width = nsPresContext::CSSPixelsToAppUnits(width);
nscoord height;
image->GetHeight(&height);
imageSize.height = nsPresContext::CSSPixelsToAppUnits(height);
}
if (needWidth) { if (!needHeight && imageSize.height != 0) { // The CSS specified the destination height, but not the destination // width. We need to calculate the width so that we maintain the // image's aspect ratio.
size.width = imageSize.width * size.height / imageSize.height;
} else {
size.width = imageSize.width;
}
}
if (needHeight) { if (!needWidth && imageSize.width != 0) { // The CSS specified the destination width, but not the destination // height. We need to calculate the height so that we maintain the // image's aspect ratio.
size.height = imageSize.height * size.width / imageSize.width;
} else {
size.height = imageSize.height;
}
}
}
return size;
}
// GetImageSourceRect returns the source rectangle of the image to be // displayed. // The width and height reflect the image region specified in CSS, or // the natural size of the image. // The width and height do not include borders and padding. // The width and height do not reflect the destination size specified // in CSS.
nsRect nsTreeBodyFrame::GetImageSourceRect(ComputedStyle* aComputedStyle,
imgIContainer* image) { if (!image) { return nsRect();
}
nsRect r; // Use the actual image size.
nscoord coord; if (NS_SUCCEEDED(image->GetWidth(&coord))) {
r.width = nsPresContext::CSSPixelsToAppUnits(coord);
} if (NS_SUCCEEDED(image->GetHeight(&coord))) {
r.height = nsPresContext::CSSPixelsToAppUnits(coord);
} return r;
}
int32_t nsTreeBodyFrame::GetRowHeight() { // Look up the correct height. It is equal to the specified height // + the specified margins.
mScratchArray.Clear();
ComputedStyle* rowContext =
GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeRow()); if (rowContext) { const nsStylePosition* myPosition = rowContext->StylePosition();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.