tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsTreeColumns.cpp (13642B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsTreeColumns.h"
      8 
      9 #include "mozilla/CSSOrderAwareFrameIterator.h"
     10 #include "mozilla/ComputedStyle.h"
     11 #include "mozilla/dom/Element.h"
     12 #include "mozilla/dom/TreeColumnBinding.h"
     13 #include "mozilla/dom/TreeColumnsBinding.h"
     14 #include "mozilla/dom/XULTreeElement.h"
     15 #include "nsContentUtils.h"
     16 #include "nsGkAtoms.h"
     17 #include "nsNameSpaceManager.h"
     18 #include "nsTreeBodyFrame.h"
     19 #include "nsTreeUtils.h"
     20 
     21 using namespace mozilla;
     22 using namespace mozilla::dom;
     23 
     24 // Column class that caches all the info about our column.
     25 nsTreeColumn::nsTreeColumn(nsTreeColumns* aColumns, dom::Element* aElement)
     26    : mContent(aElement), mColumns(aColumns), mIndex(0), mPrevious(nullptr) {
     27  NS_ASSERTION(aElement && aElement->NodeInfo()->Equals(nsGkAtoms::treecol,
     28                                                        kNameSpaceID_XUL),
     29               "nsTreeColumn's content must be a <xul:treecol>");
     30 
     31  Invalidate(IgnoreErrors());
     32 }
     33 
     34 nsTreeColumn::~nsTreeColumn() {
     35  if (mNext) {
     36    mNext->SetPrevious(nullptr);
     37  }
     38 }
     39 
     40 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsTreeColumn)
     41 
     42 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTreeColumn)
     43  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     44  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
     45  if (tmp->mNext) {
     46    tmp->mNext->SetPrevious(nullptr);
     47    NS_IMPL_CYCLE_COLLECTION_UNLINK(mNext)
     48  }
     49 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTreeColumn)
     51  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
     52  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNext)
     53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     54 
     55 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumn)
     56 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumn)
     57 
     58 // QueryInterface implementation for nsTreeColumn
     59 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumn)
     60  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     61  NS_INTERFACE_MAP_ENTRY(nsISupports)
     62  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsTreeColumn)
     63 NS_INTERFACE_MAP_END
     64 
     65 nsIFrame* nsTreeColumn::GetFrame() { return mContent->GetPrimaryFrame(); }
     66 
     67 bool nsTreeColumn::IsLastVisible(nsTreeBodyFrame* aBodyFrame) {
     68  NS_ASSERTION(GetFrame(), "should have checked for this already");
     69 
     70  // cyclers are fixed width, don't adjust them
     71  if (IsCycler()) {
     72    return false;
     73  }
     74 
     75  // we're certainly not the last visible if we're not visible
     76  if (GetFrame()->GetRect().width == 0) {
     77    return false;
     78  }
     79 
     80  // try to find a visible successor
     81  for (nsTreeColumn* next = GetNext(); next; next = next->GetNext()) {
     82    nsIFrame* frame = next->GetFrame();
     83    if (frame && frame->GetRect().width > 0) {
     84      return false;
     85    }
     86  }
     87  return true;
     88 }
     89 
     90 nsresult nsTreeColumn::GetRect(nsTreeBodyFrame* aBodyFrame, nscoord aY,
     91                               nscoord aHeight, nsRect* aResult) {
     92  nsIFrame* frame = GetFrame();
     93  if (!frame) {
     94    *aResult = nsRect();
     95    return NS_ERROR_FAILURE;
     96  }
     97 
     98  *aResult = frame->GetRect();
     99  if (frame->StyleVisibility()->IsCollapse()) {
    100    aResult->SizeTo(nsSize());
    101  }
    102  aResult->y = aY;
    103  aResult->height = aHeight;
    104  return NS_OK;
    105 }
    106 
    107 nsresult nsTreeColumn::GetXInTwips(nsTreeBodyFrame* aBodyFrame,
    108                                   nscoord* aResult) {
    109  nsIFrame* frame = GetFrame();
    110  if (!frame) {
    111    *aResult = 0;
    112    return NS_ERROR_FAILURE;
    113  }
    114  *aResult = frame->GetRect().x;
    115  return NS_OK;
    116 }
    117 
    118 nsresult nsTreeColumn::GetWidthInTwips(nsTreeBodyFrame* aBodyFrame,
    119                                       nscoord* aResult) {
    120  nsIFrame* frame = GetFrame();
    121  if (!frame) {
    122    *aResult = 0;
    123    return NS_ERROR_FAILURE;
    124  }
    125  *aResult = frame->GetRect().width;
    126  return NS_OK;
    127 }
    128 
    129 void nsTreeColumn::GetId(nsAString& aId) const { aId = GetId(); }
    130 
    131 void nsTreeColumn::Invalidate(ErrorResult& aRv) {
    132  nsIFrame* frame = GetFrame();
    133  if (NS_WARN_IF(!frame)) {
    134    aRv.Throw(NS_ERROR_FAILURE);
    135    return;
    136  }
    137 
    138  // Fetch the Id.
    139  mContent->GetAttr(nsGkAtoms::id, mId);
    140 
    141  // If we have an Id, cache the Id as an atom.
    142  if (!mId.IsEmpty()) {
    143    mAtom = NS_Atomize(mId);
    144  }
    145 
    146  // Cache our index.
    147  nsTreeUtils::GetColumnIndex(mContent, &mIndex);
    148 
    149  const nsStyleVisibility* vis = frame->StyleVisibility();
    150 
    151  // Cache our text alignment policy.
    152  const nsStyleText* textStyle = frame->StyleText();
    153 
    154  mTextAlignment = textStyle->mTextAlign;
    155  // START or END alignment sometimes means RIGHT
    156  if ((mTextAlignment == StyleTextAlign::Start &&
    157       vis->mDirection == StyleDirection::Rtl) ||
    158      (mTextAlignment == StyleTextAlign::End &&
    159       vis->mDirection == StyleDirection::Ltr)) {
    160    mTextAlignment = StyleTextAlign::Right;
    161  } else if (mTextAlignment == StyleTextAlign::Start ||
    162             mTextAlignment == StyleTextAlign::End) {
    163    mTextAlignment = StyleTextAlign::Left;
    164  }
    165 
    166  // Figure out if we're the primary column (that has to have indentation
    167  // and twisties drawn.
    168  mIsPrimary = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
    169                                     nsGkAtoms::_true, eCaseMatters);
    170 
    171  // Figure out if we're a cycling column (one that doesn't cause a selection
    172  // to happen).
    173  mIsCycler = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::cycler,
    174                                    nsGkAtoms::_true, eCaseMatters);
    175 
    176  mIsEditable = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
    177                                      nsGkAtoms::_true, eCaseMatters);
    178 
    179  mOverflow = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::overflow,
    180                                    nsGkAtoms::_true, eCaseMatters);
    181 
    182  // Figure out our column type. Default type is text.
    183  mType = TreeColumn_Binding::TYPE_TEXT;
    184  static Element::AttrValuesArray typestrings[] = {nsGkAtoms::checkbox,
    185                                                   nullptr};
    186  switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
    187                                    typestrings, eCaseMatters)) {
    188    case 0:
    189      mType = TreeColumn_Binding::TYPE_CHECKBOX;
    190      break;
    191  }
    192 
    193  // Fetch the crop style.
    194  mCropStyle = 0;
    195  static Element::AttrValuesArray cropstrings[] = {
    196      nsGkAtoms::center, nsGkAtoms::left, nsGkAtoms::start, nullptr};
    197  switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::crop,
    198                                    cropstrings, eCaseMatters)) {
    199    case 0:
    200      mCropStyle = 1;
    201      break;
    202    case 1:
    203    case 2:
    204      mCropStyle = 2;
    205      break;
    206  }
    207 }
    208 
    209 nsIContent* nsTreeColumn::GetParentObject() const { return mContent; }
    210 
    211 /* virtual */
    212 JSObject* nsTreeColumn::WrapObject(JSContext* aCx,
    213                                   JS::Handle<JSObject*> aGivenProto) {
    214  return dom::TreeColumn_Binding::Wrap(aCx, this, aGivenProto);
    215 }
    216 
    217 Element* nsTreeColumn::Element() { return mContent; }
    218 
    219 int32_t nsTreeColumn::GetX(mozilla::ErrorResult& aRv) {
    220  nsIFrame* frame = GetFrame();
    221  if (NS_WARN_IF(!frame)) {
    222    aRv.Throw(NS_ERROR_FAILURE);
    223    return 0;
    224  }
    225 
    226  return nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().x);
    227 }
    228 
    229 int32_t nsTreeColumn::GetWidth(mozilla::ErrorResult& aRv) {
    230  nsIFrame* frame = GetFrame();
    231  if (NS_WARN_IF(!frame)) {
    232    aRv.Throw(NS_ERROR_FAILURE);
    233    return 0;
    234  }
    235 
    236  return nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().width);
    237 }
    238 
    239 already_AddRefed<nsTreeColumn> nsTreeColumn::GetPreviousColumn() {
    240  return do_AddRef(mPrevious);
    241 }
    242 
    243 nsTreeColumns::nsTreeColumns(nsTreeBodyFrame* aTree) : mTree(aTree) {}
    244 
    245 nsTreeColumns::~nsTreeColumns() { nsTreeColumns::InvalidateColumns(); }
    246 
    247 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsTreeColumns)
    248 
    249 // QueryInterface implementation for nsTreeColumns
    250 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumns)
    251  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    252  NS_INTERFACE_MAP_ENTRY(nsISupports)
    253 NS_INTERFACE_MAP_END
    254 
    255 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumns)
    256 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumns)
    257 
    258 nsIContent* nsTreeColumns::GetParentObject() const {
    259  return mTree ? mTree->GetBaseElement() : nullptr;
    260 }
    261 
    262 /* virtual */
    263 JSObject* nsTreeColumns::WrapObject(JSContext* aCx,
    264                                    JS::Handle<JSObject*> aGivenProto) {
    265  return dom::TreeColumns_Binding::Wrap(aCx, this, aGivenProto);
    266 }
    267 
    268 XULTreeElement* nsTreeColumns::GetTree() const {
    269  if (!mTree) {
    270    return nullptr;
    271  }
    272 
    273  return XULTreeElement::FromNodeOrNull(mTree->GetBaseElement());
    274 }
    275 
    276 uint32_t nsTreeColumns::Count() {
    277  EnsureColumns();
    278  uint32_t count = 0;
    279  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    280       currCol = currCol->GetNext()) {
    281    ++count;
    282  }
    283  return count;
    284 }
    285 
    286 nsTreeColumn* nsTreeColumns::GetLastColumn() {
    287  EnsureColumns();
    288  nsTreeColumn* currCol = mFirstColumn;
    289  while (currCol) {
    290    nsTreeColumn* next = currCol->GetNext();
    291    if (!next) {
    292      return currCol;
    293    }
    294    currCol = next;
    295  }
    296  return nullptr;
    297 }
    298 
    299 nsTreeColumn* nsTreeColumns::GetSortedColumn() {
    300  EnsureColumns();
    301  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    302       currCol = currCol->GetNext()) {
    303    if (nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
    304                                        nsGkAtoms::sortDirection)) {
    305      return currCol;
    306    }
    307  }
    308  return nullptr;
    309 }
    310 
    311 nsTreeColumn* nsTreeColumns::GetKeyColumn() {
    312  EnsureColumns();
    313 
    314  nsTreeColumn* first = nullptr;
    315  nsTreeColumn* primary = nullptr;
    316  nsTreeColumn* sorted = nullptr;
    317 
    318  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    319       currCol = currCol->GetNext()) {
    320    // Skip hidden columns.
    321    if (currCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
    322                                       nsGkAtoms::_true, eCaseMatters)) {
    323      continue;
    324    }
    325 
    326    // Skip non-text column
    327    if (currCol->GetType() != TreeColumn_Binding::TYPE_TEXT) {
    328      continue;
    329    }
    330 
    331    if (!first) {
    332      first = currCol;
    333    }
    334 
    335    if (nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
    336                                        nsGkAtoms::sortDirection)) {
    337      // Use sorted column as the key.
    338      sorted = currCol;
    339      break;
    340    }
    341 
    342    if (currCol->IsPrimary()) {
    343      if (!primary) {
    344        primary = currCol;
    345      }
    346    }
    347  }
    348 
    349  if (sorted) {
    350    return sorted;
    351  }
    352  if (primary) {
    353    return primary;
    354  }
    355  return first;
    356 }
    357 
    358 nsTreeColumn* nsTreeColumns::GetColumnFor(dom::Element* aElement) {
    359  EnsureColumns();
    360  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    361       currCol = currCol->GetNext()) {
    362    if (currCol->mContent == aElement) {
    363      return currCol;
    364    }
    365  }
    366  return nullptr;
    367 }
    368 
    369 nsTreeColumn* nsTreeColumns::NamedGetter(const nsAString& aId, bool& aFound) {
    370  EnsureColumns();
    371  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    372       currCol = currCol->GetNext()) {
    373    if (currCol->GetId().Equals(aId)) {
    374      aFound = true;
    375      return currCol;
    376    }
    377  }
    378  aFound = false;
    379  return nullptr;
    380 }
    381 
    382 nsTreeColumn* nsTreeColumns::GetNamedColumn(const nsAString& aId) {
    383  bool dummy;
    384  return NamedGetter(aId, dummy);
    385 }
    386 
    387 void nsTreeColumns::GetSupportedNames(nsTArray<nsString>& aNames) {
    388  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    389       currCol = currCol->GetNext()) {
    390    aNames.AppendElement(currCol->GetId());
    391  }
    392 }
    393 
    394 nsTreeColumn* nsTreeColumns::IndexedGetter(uint32_t aIndex, bool& aFound) {
    395  EnsureColumns();
    396  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    397       currCol = currCol->GetNext()) {
    398    if (currCol->GetIndex() == static_cast<int32_t>(aIndex)) {
    399      aFound = true;
    400      return currCol;
    401    }
    402  }
    403  aFound = false;
    404  return nullptr;
    405 }
    406 
    407 nsTreeColumn* nsTreeColumns::GetColumnAt(uint32_t aIndex) {
    408  bool dummy;
    409  return IndexedGetter(aIndex, dummy);
    410 }
    411 
    412 void nsTreeColumns::InvalidateColumns() {
    413  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    414       currCol = currCol->GetNext()) {
    415    currCol->SetColumns(nullptr);
    416  }
    417  mFirstColumn = nullptr;
    418 }
    419 
    420 nsTreeColumn* nsTreeColumns::GetPrimaryColumn() {
    421  EnsureColumns();
    422  for (nsTreeColumn* currCol = mFirstColumn; currCol;
    423       currCol = currCol->GetNext()) {
    424    if (currCol->IsPrimary()) {
    425      return currCol;
    426    }
    427  }
    428  return nullptr;
    429 }
    430 
    431 void nsTreeColumns::EnsureColumns() {
    432  if (mTree && !mFirstColumn) {
    433    nsIContent* treeContent = mTree->GetBaseElement();
    434    if (!treeContent) {
    435      return;
    436    }
    437 
    438    nsIContent* colsContent =
    439        nsTreeUtils::GetDescendantChild(treeContent, nsGkAtoms::treecols);
    440    if (!colsContent) {
    441      return;
    442    }
    443 
    444    nsIContent* colContent =
    445        nsTreeUtils::GetDescendantChild(colsContent, nsGkAtoms::treecol);
    446    if (!colContent) {
    447      return;
    448    }
    449 
    450    nsIFrame* colFrame = colContent->GetPrimaryFrame();
    451    if (!colFrame) {
    452      return;
    453    }
    454 
    455    colFrame = colFrame->GetParent();
    456    if (!colFrame || !colFrame->GetContent()) {
    457      return;
    458    }
    459 
    460    nsTreeColumn* currCol = nullptr;
    461 
    462    // Enumerate the columns in visible order
    463    CSSOrderAwareFrameIterator iter(
    464        colFrame, FrameChildListID::Principal,
    465        CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
    466    for (; !iter.AtEnd(); iter.Next()) {
    467      nsIFrame* colFrame = iter.get();
    468      nsIContent* colContent = colFrame->GetContent();
    469      if (!colContent->IsXULElement(nsGkAtoms::treecol)) {
    470        continue;
    471      }
    472      // Create a new column structure.
    473      nsTreeColumn* col = new nsTreeColumn(this, colContent->AsElement());
    474 
    475      if (currCol) {
    476        currCol->SetNext(col);
    477        col->SetPrevious(currCol);
    478      } else {
    479        mFirstColumn = col;
    480      }
    481      currCol = col;
    482    }
    483  }
    484 }