/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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/. */ #include "nsIFileView.h" #include "nsITreeView.h" #include "mozilla/ModuleUtils.h" #include "nsITreeSelection.h" #include "nsITreeColumns.h" #include "nsITreeBoxObject.h" #include "nsIFile.h" #include "nsString.h" #include "nsReadableUtils.h" #include "nsCRT.h" #include "nsPrintfCString.h" #include "nsIDateTimeFormat.h" #include "nsDateTimeFormatCID.h" #include "nsQuickSort.h" #include "nsIAtom.h" #include "nsIAutoCompleteResult.h" #include "nsIAutoCompleteSearch.h" #include "nsISimpleEnumerator.h" #include "nsAutoPtr.h" #include "nsIMutableArray.h" #include "nsTArray.h" #include "mozilla/Attributes.h" #include "nsWildCard.h" class nsIDOMDataTransfer; #define NS_FILECOMPLETE_CID { 0xcb60980e, 0x18a5, 0x4a77, \ { 0x91, 0x10, 0x81, 0x46, 0x61, 0x4c, 0xa7, 0xf0 } } #define NS_FILECOMPLETE_CONTRACTID "@mozilla.org/autocomplete/search;1?name=file" class nsFileResult MOZ_FINAL : public nsIAutoCompleteResult { public: // aSearchString is the text typed into the autocomplete widget // aSearchParam is the picker's currently displayed directory nsFileResult(const nsAString& aSearchString, const nsAString& aSearchParam); NS_DECL_ISUPPORTS NS_DECL_NSIAUTOCOMPLETERESULT nsTArray mValues; nsAutoString mSearchString; uint16_t mSearchResult; }; NS_IMPL_ISUPPORTS(nsFileResult, nsIAutoCompleteResult) nsFileResult::nsFileResult(const nsAString& aSearchString, const nsAString& aSearchParam): mSearchString(aSearchString) { if (aSearchString.IsEmpty()) mSearchResult = RESULT_IGNORED; else { int32_t slashPos = mSearchString.RFindChar('/'); mSearchResult = RESULT_FAILURE; nsCOMPtr directory; nsDependentSubstring parent(Substring(mSearchString, 0, slashPos + 1)); if (!parent.IsEmpty() && parent.First() == '/') NS_NewLocalFile(parent, true, getter_AddRefs(directory)); if (!directory) { if (NS_FAILED(NS_NewLocalFile(aSearchParam, true, getter_AddRefs(directory)))) return; if (slashPos > 0) directory->AppendRelativePath(Substring(mSearchString, 0, slashPos)); } nsCOMPtr dirEntries; if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(dirEntries)))) return; mSearchResult = RESULT_NOMATCH; bool hasMore = false; nsDependentSubstring prefix(Substring(mSearchString, slashPos + 1)); while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr nextItem; dirEntries->GetNext(getter_AddRefs(nextItem)); nsCOMPtr nextFile(do_QueryInterface(nextItem)); nsAutoString fileName; nextFile->GetLeafName(fileName); if (StringBeginsWith(fileName, prefix)) { fileName.Insert(parent, 0); if (mSearchResult == RESULT_NOMATCH && fileName.Equals(mSearchString)) mSearchResult = RESULT_IGNORED; else mSearchResult = RESULT_SUCCESS; bool isDirectory = false; nextFile->IsDirectory(&isDirectory); if (isDirectory) fileName.Append('/'); mValues.AppendElement(fileName); } } mValues.Sort(); } } NS_IMETHODIMP nsFileResult::GetSearchString(nsAString & aSearchString) { aSearchString.Assign(mSearchString); return NS_OK; } NS_IMETHODIMP nsFileResult::GetSearchResult(uint16_t *aSearchResult) { NS_ENSURE_ARG_POINTER(aSearchResult); *aSearchResult = mSearchResult; return NS_OK; } NS_IMETHODIMP nsFileResult::GetDefaultIndex(int32_t *aDefaultIndex) { NS_ENSURE_ARG_POINTER(aDefaultIndex); *aDefaultIndex = -1; return NS_OK; } NS_IMETHODIMP nsFileResult::GetErrorDescription(nsAString & aErrorDescription) { aErrorDescription.Truncate(); return NS_OK; } NS_IMETHODIMP nsFileResult::GetMatchCount(uint32_t *aMatchCount) { NS_ENSURE_ARG_POINTER(aMatchCount); *aMatchCount = mValues.Length(); return NS_OK; } NS_IMETHODIMP nsFileResult::GetTypeAheadResult(bool *aTypeAheadResult) { NS_ENSURE_ARG_POINTER(aTypeAheadResult); *aTypeAheadResult = false; return NS_OK; } NS_IMETHODIMP nsFileResult::GetValueAt(int32_t index, nsAString & aValue) { aValue = mValues[index]; if (aValue.Last() == '/') aValue.Truncate(aValue.Length() - 1); return NS_OK; } NS_IMETHODIMP nsFileResult::GetLabelAt(int32_t index, nsAString & aValue) { return GetValueAt(index, aValue); } NS_IMETHODIMP nsFileResult::GetCommentAt(int32_t index, nsAString & aComment) { aComment.Truncate(); return NS_OK; } NS_IMETHODIMP nsFileResult::GetStyleAt(int32_t index, nsAString & aStyle) { if (mValues[index].Last() == '/') aStyle.AssignLiteral("directory"); else aStyle.AssignLiteral("file"); return NS_OK; } NS_IMETHODIMP nsFileResult::GetImageAt(int32_t index, nsAString & aImage) { aImage.Truncate(); return NS_OK; } NS_IMETHODIMP nsFileResult::GetFinalCompleteValueAt(int32_t index, nsAString & aValue) { return GetValueAt(index, aValue); } NS_IMETHODIMP nsFileResult::RemoveValueAt(int32_t rowIndex, bool removeFromDb) { return NS_OK; } class nsFileComplete MOZ_FINAL : public nsIAutoCompleteSearch { public: NS_DECL_ISUPPORTS NS_DECL_NSIAUTOCOMPLETESEARCH }; NS_IMPL_ISUPPORTS(nsFileComplete, nsIAutoCompleteSearch) NS_IMETHODIMP nsFileComplete::StartSearch(const nsAString& aSearchString, const nsAString& aSearchParam, nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener) { NS_ENSURE_ARG_POINTER(aListener); nsRefPtr result = new nsFileResult(aSearchString, aSearchParam); NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); return aListener->OnSearchResult(this, result); } NS_IMETHODIMP nsFileComplete::StopSearch() { return NS_OK; } #define NS_FILEVIEW_CID { 0xa5570462, 0x1dd1, 0x11b2, \ { 0x9d, 0x19, 0xdf, 0x30, 0xa2, 0x7f, 0xbd, 0xc4 } } class nsFileView : public nsIFileView, public nsITreeView { public: nsFileView(); nsresult Init(); NS_DECL_ISUPPORTS NS_DECL_NSIFILEVIEW NS_DECL_NSITREEVIEW protected: virtual ~nsFileView(); void FilterFiles(); void ReverseArray(nsTArray >& aArray); void SortArray(nsTArray >& aArray); void SortInternal(); nsTArray > mFileList; nsTArray > mDirList; nsTArray > mFilteredFiles; nsCOMPtr mDirectoryPath; nsCOMPtr mTree; nsCOMPtr mSelection; nsCOMPtr mDateFormatter; int16_t mSortType; int32_t mTotalRows; nsTArray mCurrentFilters; bool mShowHiddenFiles; bool mDirectoryFilter; bool mReverseSort; }; // Factory constructor NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileComplete) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFileView, Init) NS_DEFINE_NAMED_CID(NS_FILECOMPLETE_CID); NS_DEFINE_NAMED_CID(NS_FILEVIEW_CID); static const mozilla::Module::CIDEntry kFileViewCIDs[] = { { &kNS_FILECOMPLETE_CID, false, nullptr, nsFileCompleteConstructor }, { &kNS_FILEVIEW_CID, false, nullptr, nsFileViewConstructor }, { nullptr } }; static const mozilla::Module::ContractIDEntry kFileViewContracts[] = { { NS_FILECOMPLETE_CONTRACTID, &kNS_FILECOMPLETE_CID }, { NS_FILEVIEW_CONTRACTID, &kNS_FILEVIEW_CID }, { nullptr } }; static const mozilla::Module kFileViewModule = { mozilla::Module::kVersion, kFileViewCIDs, kFileViewContracts }; NSMODULE_DEFN(nsFileViewModule) = &kFileViewModule; nsFileView::nsFileView() : mSortType(-1), mTotalRows(0), mShowHiddenFiles(false), mDirectoryFilter(false), mReverseSort(false) { } nsFileView::~nsFileView() { uint32_t count = mCurrentFilters.Length(); for (uint32_t i = 0; i < count; ++i) NS_Free(mCurrentFilters[i]); } nsresult nsFileView::Init() { mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID); if (!mDateFormatter) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } // nsISupports implementation NS_IMPL_ISUPPORTS(nsFileView, nsITreeView, nsIFileView) // nsIFileView implementation NS_IMETHODIMP nsFileView::SetShowHiddenFiles(bool aShowHidden) { if (aShowHidden != mShowHiddenFiles) { mShowHiddenFiles = aShowHidden; // This could be better optimized, but since the hidden // file functionality is not currently used, this will be fine. SetDirectory(mDirectoryPath); } return NS_OK; } NS_IMETHODIMP nsFileView::GetShowHiddenFiles(bool* aShowHidden) { *aShowHidden = mShowHiddenFiles; return NS_OK; } NS_IMETHODIMP nsFileView::SetShowOnlyDirectories(bool aOnlyDirs) { if (aOnlyDirs == mDirectoryFilter) return NS_OK; mDirectoryFilter = aOnlyDirs; uint32_t dirCount = mDirList.Length(); if (mDirectoryFilter) { int32_t rowDiff = mTotalRows - dirCount; mFilteredFiles.Clear(); mTotalRows = dirCount; if (mTree) mTree->RowCountChanged(mTotalRows, -rowDiff); } else { // Run the filter again to get the file list back FilterFiles(); SortArray(mFilteredFiles); if (mReverseSort) ReverseArray(mFilteredFiles); if (mTree) mTree->RowCountChanged(dirCount, mTotalRows - dirCount); } return NS_OK; } NS_IMETHODIMP nsFileView::GetShowOnlyDirectories(bool* aOnlyDirs) { *aOnlyDirs = mDirectoryFilter; return NS_OK; } NS_IMETHODIMP nsFileView::GetSortType(int16_t* aSortType) { *aSortType = mSortType; return NS_OK; } NS_IMETHODIMP nsFileView::GetReverseSort(bool* aReverseSort) { *aReverseSort = mReverseSort; return NS_OK; } NS_IMETHODIMP nsFileView::Sort(int16_t aSortType, bool aReverseSort) { if (aSortType == mSortType) { if (aReverseSort == mReverseSort) return NS_OK; mReverseSort = aReverseSort; ReverseArray(mDirList); ReverseArray(mFilteredFiles); } else { mSortType = aSortType; mReverseSort = aReverseSort; SortInternal(); } if (mTree) mTree->Invalidate(); return NS_OK; } NS_IMETHODIMP nsFileView::SetDirectory(nsIFile* aDirectory) { NS_ENSURE_ARG_POINTER(aDirectory); nsCOMPtr dirEntries; aDirectory->GetDirectoryEntries(getter_AddRefs(dirEntries)); if (!dirEntries) { // Couldn't read in the directory, this can happen if the user does not // have permission to list it. return NS_ERROR_FAILURE; } mDirectoryPath = aDirectory; mFileList.Clear(); mDirList.Clear(); bool hasMore = false; while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr nextItem; dirEntries->GetNext(getter_AddRefs(nextItem)); nsCOMPtr theFile = do_QueryInterface(nextItem); bool isDirectory = false; if (theFile) { theFile->IsDirectory(&isDirectory); if (isDirectory) { bool isHidden; theFile->IsHidden(&isHidden); if (mShowHiddenFiles || !isHidden) { mDirList.AppendElement(theFile); } } else { mFileList.AppendElement(theFile); } } } if (mTree) { mTree->BeginUpdateBatch(); mTree->RowCountChanged(0, -mTotalRows); } FilterFiles(); SortInternal(); if (mTree) { mTree->EndUpdateBatch(); mTree->ScrollToRow(0); } return NS_OK; } NS_IMETHODIMP nsFileView::SetFilter(const nsAString& aFilterString) { uint32_t filterCount = mCurrentFilters.Length(); for (uint32_t i = 0; i < filterCount; ++i) NS_Free(mCurrentFilters[i]); mCurrentFilters.Clear(); nsAString::const_iterator start, iter, end; aFilterString.BeginReading(iter); aFilterString.EndReading(end); while (true) { // skip over delimiters while (iter != end && (*iter == ';' || *iter == ' ')) ++iter; if (iter == end) break; start = iter; // start of a filter // we know this is neither ';' nor ' ', skip to next char ++iter; // find next delimiter or end of string while (iter != end && (*iter != ';' && *iter != ' ')) ++iter; char16_t* filter = ToNewUnicode(Substring(start, iter)); if (!filter) return NS_ERROR_OUT_OF_MEMORY; if (!mCurrentFilters.AppendElement(filter)) { NS_Free(filter); return NS_ERROR_OUT_OF_MEMORY; } if (iter == end) break; ++iter; // we know this is either ';' or ' ', skip to next char } if (mTree) { mTree->BeginUpdateBatch(); uint32_t count = mDirList.Length(); mTree->RowCountChanged(count, count - mTotalRows); } mFilteredFiles.Clear(); FilterFiles(); SortArray(mFilteredFiles); if (mReverseSort) ReverseArray(mFilteredFiles); if (mTree) mTree->EndUpdateBatch(); return NS_OK; } NS_IMETHODIMP nsFileView::GetSelectedFiles(nsIArray** aFiles) { *aFiles = nullptr; if (!mSelection) return NS_OK; int32_t numRanges; mSelection->GetRangeCount(&numRanges); uint32_t dirCount = mDirList.Length(); nsCOMPtr fileArray = do_CreateInstance(NS_ARRAY_CONTRACTID); NS_ENSURE_STATE(fileArray); for (int32_t range = 0; range < numRanges; ++range) { int32_t rangeBegin, rangeEnd; mSelection->GetRangeAt(range, &rangeBegin, &rangeEnd); for (int32_t itemIndex = rangeBegin; itemIndex <= rangeEnd; ++itemIndex) { nsIFile* curFile = nullptr; if (itemIndex < (int32_t) dirCount) curFile = mDirList[itemIndex]; else { if (itemIndex < mTotalRows) curFile = mFilteredFiles[itemIndex - dirCount]; } if (curFile) fileArray->AppendElement(curFile, false); } } NS_ADDREF(*aFiles = fileArray); return NS_OK; } // nsITreeView implementation NS_IMETHODIMP nsFileView::GetRowCount(int32_t* aRowCount) { *aRowCount = mTotalRows; return NS_OK; } NS_IMETHODIMP nsFileView::GetSelection(nsITreeSelection** aSelection) { *aSelection = mSelection; NS_IF_ADDREF(*aSelection); return NS_OK; } NS_IMETHODIMP nsFileView::SetSelection(nsITreeSelection* aSelection) { mSelection = aSelection; return NS_OK; } NS_IMETHODIMP nsFileView::GetRowProperties(int32_t aIndex, nsAString& aProps) { return NS_OK; } NS_IMETHODIMP nsFileView::GetCellProperties(int32_t aRow, nsITreeColumn* aCol, nsAString& aProps) { uint32_t dirCount = mDirList.Length(); if (aRow < (int32_t) dirCount) aProps.AppendLiteral("directory"); else if (aRow < mTotalRows) aProps.AppendLiteral("file"); return NS_OK; } NS_IMETHODIMP nsFileView::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps) { return NS_OK; } NS_IMETHODIMP nsFileView::IsContainer(int32_t aIndex, bool* aIsContainer) { *aIsContainer = false; return NS_OK; } NS_IMETHODIMP nsFileView::IsContainerOpen(int32_t aIndex, bool* aIsOpen) { *aIsOpen = false; return NS_OK; } NS_IMETHODIMP nsFileView::IsContainerEmpty(int32_t aIndex, bool* aIsEmpty) { *aIsEmpty = false; return NS_OK; } NS_IMETHODIMP nsFileView::IsSeparator(int32_t aIndex, bool* aIsSeparator) { *aIsSeparator = false; return NS_OK; } NS_IMETHODIMP nsFileView::IsSorted(bool* aIsSorted) { *aIsSorted = (mSortType >= 0); return NS_OK; } NS_IMETHODIMP nsFileView::CanDrop(int32_t aIndex, int32_t aOrientation, nsIDOMDataTransfer* dataTransfer, bool* aCanDrop) { *aCanDrop = false; return NS_OK; } NS_IMETHODIMP nsFileView::Drop(int32_t aRow, int32_t aOrientation, nsIDOMDataTransfer* dataTransfer) { return NS_OK; } NS_IMETHODIMP nsFileView::GetParentIndex(int32_t aRowIndex, int32_t* aParentIndex) { *aParentIndex = -1; return NS_OK; } NS_IMETHODIMP nsFileView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* aHasSibling) { *aHasSibling = (aAfterIndex < (mTotalRows - 1)); return NS_OK; } NS_IMETHODIMP nsFileView::GetLevel(int32_t aIndex, int32_t* aLevel) { *aLevel = 0; return NS_OK; } NS_IMETHODIMP nsFileView::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& aImageSrc) { return NS_OK; } NS_IMETHODIMP nsFileView::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* aProgressMode) { return NS_OK; } NS_IMETHODIMP nsFileView::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& aCellValue) { return NS_OK; } NS_IMETHODIMP nsFileView::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& aCellText) { uint32_t dirCount = mDirList.Length(); bool isDirectory; nsIFile* curFile = nullptr; if (aRow < (int32_t) dirCount) { isDirectory = true; curFile = mDirList[aRow]; } else if (aRow < mTotalRows) { isDirectory = false; curFile = mFilteredFiles[aRow - dirCount]; } else { // invalid row aCellText.SetCapacity(0); return NS_OK; } const char16_t* colID; aCol->GetIdConst(&colID); if (NS_LITERAL_STRING("FilenameColumn").Equals(colID)) { curFile->GetLeafName(aCellText); } else if (NS_LITERAL_STRING("LastModifiedColumn").Equals(colID)) { PRTime lastModTime; curFile->GetLastModifiedTime(&lastModTime); // XXX FormatPRTime could take an nsAString& nsAutoString temp; mDateFormatter->FormatPRTime(nullptr, kDateFormatShort, kTimeFormatSeconds, lastModTime * 1000, temp); aCellText = temp; } else { // file size if (isDirectory) aCellText.SetCapacity(0); else { int64_t fileSize; curFile->GetFileSize(&fileSize); CopyUTF8toUTF16(nsPrintfCString("%lld", fileSize), aCellText); } } return NS_OK; } NS_IMETHODIMP nsFileView::SetTree(nsITreeBoxObject* aTree) { mTree = aTree; return NS_OK; } NS_IMETHODIMP nsFileView::ToggleOpenState(int32_t aIndex) { return NS_OK; } NS_IMETHODIMP nsFileView::CycleHeader(nsITreeColumn* aCol) { return NS_OK; } NS_IMETHODIMP nsFileView::SelectionChanged() { return NS_OK; } NS_IMETHODIMP nsFileView::CycleCell(int32_t aRow, nsITreeColumn* aCol) { return NS_OK; } NS_IMETHODIMP nsFileView::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* aIsEditable) { *aIsEditable = false; return NS_OK; } NS_IMETHODIMP nsFileView::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* aIsSelectable) { *aIsSelectable = false; return NS_OK; } NS_IMETHODIMP nsFileView::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue) { return NS_OK; } NS_IMETHODIMP nsFileView::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue) { return NS_OK; } NS_IMETHODIMP nsFileView::PerformAction(const char16_t* aAction) { return NS_OK; } NS_IMETHODIMP nsFileView::PerformActionOnRow(const char16_t* aAction, int32_t aRow) { return NS_OK; } NS_IMETHODIMP nsFileView::PerformActionOnCell(const char16_t* aAction, int32_t aRow, nsITreeColumn* aCol) { return NS_OK; } // Private methods void nsFileView::FilterFiles() { uint32_t count = mDirList.Length(); mTotalRows = count; count = mFileList.Length(); mFilteredFiles.Clear(); uint32_t filterCount = mCurrentFilters.Length(); for (uint32_t i = 0; i < count; ++i) { nsIFile* file = mFileList[i]; bool isHidden = false; if (!mShowHiddenFiles) file->IsHidden(&isHidden); nsAutoString ucsLeafName; if(NS_FAILED(file->GetLeafName(ucsLeafName))) { // need to check return value for GetLeafName() continue; } if (!isHidden) { for (uint32_t j = 0; j < filterCount; ++j) { bool matched = false; if (!nsCRT::strcmp(mCurrentFilters.ElementAt(j), MOZ_UTF16("..apps"))) { file->IsExecutable(&matched); } else matched = (NS_WildCardMatch(ucsLeafName.get(), mCurrentFilters.ElementAt(j), true) == MATCH); if (matched) { mFilteredFiles.AppendElement(file); ++mTotalRows; break; } } } } } void nsFileView::ReverseArray(nsTArray >& aArray) { uint32_t count = aArray.Length(); for (uint32_t i = 0; i < count/2; ++i) { // If we get references to the COMPtrs in the array, and then .swap() them // we avoid AdRef() / Release() calls. nsCOMPtr& element = aArray[i]; nsCOMPtr& element2 = aArray[count - i - 1]; element.swap(element2); } } static int SortNameCallback(const void* aElement1, const void* aElement2, void* aContext) { nsIFile* file1 = *static_cast(aElement1); nsIFile* file2 = *static_cast(aElement2); nsAutoString leafName1, leafName2; file1->GetLeafName(leafName1); file2->GetLeafName(leafName2); return Compare(leafName1, leafName2); } static int SortSizeCallback(const void* aElement1, const void* aElement2, void* aContext) { nsIFile* file1 = *static_cast(aElement1); nsIFile* file2 = *static_cast(aElement2); int64_t size1, size2; file1->GetFileSize(&size1); file2->GetFileSize(&size2); if (size1 == size2) return 0; return size1 < size2 ? -1 : 1; } static int SortDateCallback(const void* aElement1, const void* aElement2, void* aContext) { nsIFile* file1 = *static_cast(aElement1); nsIFile* file2 = *static_cast(aElement2); PRTime time1, time2; file1->GetLastModifiedTime(&time1); file2->GetLastModifiedTime(&time2); if (time1 == time2) return 0; return time1 < time2 ? -1 : 1; } void nsFileView::SortArray(nsTArray >& aArray) { // We assume the array to be in filesystem order, which // for our purposes, is completely unordered. int (*compareFunc)(const void*, const void*, void*); switch (mSortType) { case sortName: compareFunc = SortNameCallback; break; case sortSize: compareFunc = SortSizeCallback; break; case sortDate: compareFunc = SortDateCallback; break; default: return; } uint32_t count = aArray.Length(); nsIFile** array = new nsIFile*[count]; for (uint32_t i = 0; i < count; ++i) { array[i] = aArray[i]; } NS_QuickSort(array, count, sizeof(nsIFile*), compareFunc, nullptr); for (uint32_t i = 0; i < count; ++i) { // Use swap() to avoid refcounting. aArray[i].swap(array[i]); } delete[] array; } void nsFileView::SortInternal() { SortArray(mDirList); SortArray(mFilteredFiles); if (mReverseSort) { ReverseArray(mDirList); ReverseArray(mFilteredFiles); } }