michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsTreeBoxObject.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIDOMXULElement.h" michael@0: #include "nsIXULTemplateBuilder.h" michael@0: #include "nsTreeContentView.h" michael@0: #include "nsITreeSelection.h" michael@0: #include "ChildIterator.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsTreeBodyFrame.h" michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(nsTreeBoxObject, mView) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsTreeBoxObject, nsBoxObject) michael@0: NS_IMPL_RELEASE_INHERITED(nsTreeBoxObject, nsBoxObject) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsTreeBoxObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsITreeBoxObject) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsBoxObject) michael@0: michael@0: void michael@0: nsTreeBoxObject::Clear() michael@0: { michael@0: ClearCachedValues(); michael@0: michael@0: // Drop the view's ref to us. michael@0: if (mView) { michael@0: nsCOMPtr sel; michael@0: mView->GetSelection(getter_AddRefs(sel)); michael@0: if (sel) michael@0: sel->SetTree(nullptr); michael@0: mView->SetTree(nullptr); // Break the circular ref between the view and us. michael@0: } michael@0: mView = nullptr; michael@0: michael@0: nsBoxObject::Clear(); michael@0: } michael@0: michael@0: michael@0: nsTreeBoxObject::nsTreeBoxObject() michael@0: : mTreeBody(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsTreeBoxObject::~nsTreeBoxObject() michael@0: { michael@0: /* destructor code */ michael@0: } michael@0: michael@0: static nsIContent* FindBodyElement(nsIContent* aParent) michael@0: { michael@0: mozilla::dom::FlattenedChildIterator iter(aParent); michael@0: for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) { michael@0: nsINodeInfo *ni = content->NodeInfo(); michael@0: if (ni->Equals(nsGkAtoms::treechildren, kNameSpaceID_XUL)) { michael@0: return content; michael@0: } else if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { michael@0: // There are nesting tree elements. Only the innermost should michael@0: // find the treechilren. michael@0: return nullptr; michael@0: } else if (content->IsElement() && michael@0: !ni->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) { michael@0: nsIContent* result = FindBodyElement(content); michael@0: if (result) michael@0: return result; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsTreeBodyFrame* michael@0: nsTreeBoxObject::GetTreeBody(bool aFlushLayout) michael@0: { michael@0: // Make sure our frames are up to date, and layout as needed. We michael@0: // have to do this before checking for our cached mTreeBody, since michael@0: // it might go away on style flush, and in any case if aFlushLayout michael@0: // is true we need to make sure to flush no matter what. michael@0: // XXXbz except that flushing style when we were not asked to flush michael@0: // layout here breaks things. See bug 585123. michael@0: nsIFrame* frame; michael@0: if (aFlushLayout) { michael@0: frame = GetFrame(aFlushLayout); michael@0: if (!frame) michael@0: return nullptr; michael@0: } michael@0: michael@0: if (mTreeBody) { michael@0: // Have one cached already. michael@0: return mTreeBody; michael@0: } michael@0: michael@0: if (!aFlushLayout) { michael@0: frame = GetFrame(aFlushLayout); michael@0: if (!frame) michael@0: return nullptr; michael@0: } michael@0: michael@0: // Iterate over our content model children looking for the body. michael@0: nsCOMPtr content = FindBodyElement(frame->GetContent()); michael@0: if (!content) michael@0: return nullptr; michael@0: michael@0: frame = content->GetPrimaryFrame(); michael@0: if (!frame) michael@0: return nullptr; michael@0: michael@0: // Make sure that the treebodyframe has a pointer to |this|. michael@0: nsTreeBodyFrame *treeBody = do_QueryFrame(frame); michael@0: NS_ENSURE_TRUE(treeBody && treeBody->GetTreeBoxObject() == this, nullptr); michael@0: michael@0: mTreeBody = treeBody; michael@0: return mTreeBody; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetView(nsITreeView * *aView) michael@0: { michael@0: if (!mTreeBody) { michael@0: if (!GetTreeBody()) { michael@0: // Don't return an uninitialised view michael@0: *aView = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mView) michael@0: // Our new frame needs to initialise itself michael@0: return mTreeBody->GetView(aView); michael@0: } michael@0: if (!mView) { michael@0: nsCOMPtr xulele = do_QueryInterface(mContent); michael@0: if (xulele) { michael@0: // See if there is a XUL tree builder associated with the element michael@0: nsCOMPtr builder; michael@0: xulele->GetBuilder(getter_AddRefs(builder)); michael@0: mView = do_QueryInterface(builder); michael@0: michael@0: if (!mView) { michael@0: // No tree builder, create a tree content view. michael@0: nsresult rv = NS_NewTreeContentView(getter_AddRefs(mView)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Initialise the frame and view michael@0: mTreeBody->SetView(mView); michael@0: } michael@0: } michael@0: NS_IF_ADDREF(*aView = mView); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool michael@0: CanTrustView(nsISupports* aValue) michael@0: { michael@0: // Untrusted content is only allowed to specify known-good views michael@0: if (nsContentUtils::IsCallerChrome()) michael@0: return true; michael@0: nsCOMPtr nativeTreeView = do_QueryInterface(aValue); michael@0: if (!nativeTreeView || NS_FAILED(nativeTreeView->EnsureNative())) { michael@0: // XXX ERRMSG need a good error here for developers michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::SetView(nsITreeView * aView) michael@0: { michael@0: if (!CanTrustView(aView)) michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: michael@0: mView = aView; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: body->SetView(aView); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetFocused(bool* aFocused) michael@0: { michael@0: *aFocused = false; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetFocused(aFocused); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::SetFocused(bool aFocused) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->SetFocused(aFocused); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetTreeBody(nsIDOMElement** aElement) michael@0: { michael@0: *aElement = nullptr; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetTreeBody(aElement); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetColumns(nsITreeColumns** aColumns) michael@0: { michael@0: *aColumns = nullptr; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: *aColumns = body->Columns().take(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetRowHeight(int32_t* aRowHeight) michael@0: { michael@0: *aRowHeight = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetRowHeight(aRowHeight); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetRowWidth(int32_t *aRowWidth) michael@0: { michael@0: *aRowWidth = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetRowWidth(aRowWidth); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetFirstVisibleRow(int32_t *aFirstVisibleRow) michael@0: { michael@0: *aFirstVisibleRow = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: *aFirstVisibleRow = body->FirstVisibleRow(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetLastVisibleRow(int32_t *aLastVisibleRow) michael@0: { michael@0: *aLastVisibleRow = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: *aLastVisibleRow = body->LastVisibleRow(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetHorizontalPosition(int32_t *aHorizontalPosition) michael@0: { michael@0: *aHorizontalPosition = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetHorizontalPosition(aHorizontalPosition); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetPageLength(int32_t *aPageLength) michael@0: { michael@0: *aPageLength = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: *aPageLength = body->PageLength(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetSelectionRegion(nsIScriptableRegion **aRegion) michael@0: { michael@0: *aRegion = nullptr; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetSelectionRegion(aRegion); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::EnsureRowIsVisible(int32_t aRow) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->EnsureRowIsVisible(aRow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->EnsureCellIsVisible(aRow, aCol); michael@0: return NS_OK; michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::ScrollToRow(int32_t aRow) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(true); michael@0: if (body) michael@0: return body->ScrollToRow(aRow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::ScrollByLines(int32_t aNumLines) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->ScrollByLines(aNumLines); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::ScrollByPages(int32_t aNumPages) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->ScrollByPages(aNumPages); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::ScrollToCell(int32_t aRow, nsITreeColumn* aCol) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->ScrollToCell(aRow, aCol); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::ScrollToColumn(nsITreeColumn* aCol) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->ScrollToColumn(aCol); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::ScrollToHorizontalPosition(int32_t aHorizontalPosition) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->ScrollToHorizontalPosition(aHorizontalPosition); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::Invalidate() michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->Invalidate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::InvalidateColumn(nsITreeColumn* aCol) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->InvalidateColumn(aCol); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::InvalidateRow(int32_t aIndex) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->InvalidateRow(aIndex); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::InvalidateCell(int32_t aRow, nsITreeColumn* aCol) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->InvalidateCell(aRow, aCol); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::InvalidateRange(int32_t aStart, int32_t aEnd) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->InvalidateRange(aStart, aEnd); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->InvalidateColumnRange(aStart, aEnd, aCol); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetRowAt(int32_t x, int32_t y, int32_t *aRow) michael@0: { michael@0: *aRow = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetRowAt(x, y, aRow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::GetCellAt(int32_t aX, int32_t aY, int32_t *aRow, nsITreeColumn** aCol, michael@0: nsACString& aChildElt) michael@0: { michael@0: *aRow = 0; michael@0: *aCol = nullptr; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetCellAt(aX, aY, aRow, aCol, aChildElt); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsACString& aElement, michael@0: int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight) michael@0: { michael@0: *aX = *aY = *aWidth = *aHeight = 0; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->GetCoordsForCellItem(aRow, aCol, aElement, aX, aY, aWidth, aHeight); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTreeBoxObject::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *aIsCropped) michael@0: { michael@0: *aIsCropped = false; michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->IsCellCropped(aRow, aCol, aIsCropped); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::RowCountChanged(int32_t aIndex, int32_t aDelta) michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->RowCountChanged(aIndex, aDelta); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::BeginUpdateBatch() michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->BeginUpdateBatch(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::EndUpdateBatch() michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->EndUpdateBatch(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsTreeBoxObject::ClearStyleAndImageCaches() michael@0: { michael@0: nsTreeBodyFrame* body = GetTreeBody(); michael@0: if (body) michael@0: return body->ClearStyleAndImageCaches(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsTreeBoxObject::ClearCachedValues() michael@0: { michael@0: mTreeBody = nullptr; michael@0: } michael@0: michael@0: // Creation Routine /////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult michael@0: NS_NewTreeBoxObject(nsIBoxObject** aResult) michael@0: { michael@0: *aResult = new nsTreeBoxObject; michael@0: if (!*aResult) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: