import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,695 @@
/* -*- 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 "mozilla/dom/TreeBoxObject.h"
#include "nsCOMPtr.h"
#include "nsIDOMXULElement.h"
#include "nsIScriptableRegion.h"
#include "nsIXULTemplateBuilder.h"
#include "nsTreeContentView.h"
#include "nsITreeSelection.h"
#include "ChildIterator.h"
#include "nsContentUtils.h"
#include "nsError.h"
#include "nsTreeBodyFrame.h"
#include "mozilla/dom/TreeBoxObjectBinding.h"
#include "nsITreeColumns.h"
#include "mozilla/dom/DOMRect.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ToJSValue.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(TreeBoxObject, BoxObject,
mView)
NS_IMPL_ADDREF_INHERITED(TreeBoxObject, BoxObject)
NS_IMPL_RELEASE_INHERITED(TreeBoxObject, BoxObject)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TreeBoxObject)
NS_INTERFACE_MAP_ENTRY(nsITreeBoxObject)
NS_INTERFACE_MAP_END_INHERITING(BoxObject)
void
TreeBoxObject::Clear()
{
ClearCachedValues();
// Drop the view's ref to us.
if (mView) {
nsCOMPtr<nsITreeSelection> sel;
mView->GetSelection(getter_AddRefs(sel));
if (sel)
sel->SetTree(nullptr);
mView->SetTree(nullptr); // Break the circular ref between the view and us.
}
mView = nullptr;
BoxObject::Clear();
}
TreeBoxObject::TreeBoxObject()
: mTreeBody(nullptr)
{
}
TreeBoxObject::~TreeBoxObject()
{
}
static nsIContent* FindBodyElement(nsIContent* aParent)
{
mozilla::dom::FlattenedChildIterator iter(aParent);
for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
mozilla::dom::NodeInfo *ni = content->NodeInfo();
if (ni->Equals(nsGkAtoms::treechildren, kNameSpaceID_XUL)) {
return content;
} else if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) {
// There are nesting tree elements. Only the innermost should
// find the treechilren.
return nullptr;
} else if (content->IsElement() &&
!ni->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) {
nsIContent* result = FindBodyElement(content);
if (result)
return result;
}
}
return nullptr;
}
nsTreeBodyFrame*
TreeBoxObject::GetTreeBodyFrame(bool aFlushLayout)
{
// Make sure our frames are up to date, and layout as needed. We
// have to do this before checking for our cached mTreeBody, since
// it might go away on style flush, and in any case if aFlushLayout
// is true we need to make sure to flush no matter what.
// XXXbz except that flushing style when we were not asked to flush
// layout here breaks things. See bug 585123.
nsIFrame* frame = nullptr;
if (aFlushLayout) {
frame = GetFrame(aFlushLayout);
if (!frame)
return nullptr;
}
if (mTreeBody) {
// Have one cached already.
return mTreeBody;
}
if (!aFlushLayout) {
frame = GetFrame(aFlushLayout);
if (!frame)
return nullptr;
}
// Iterate over our content model children looking for the body.
nsCOMPtr<nsIContent> content = FindBodyElement(frame->GetContent());
if (!content)
return nullptr;
frame = content->GetPrimaryFrame();
if (!frame)
return nullptr;
// Make sure that the treebodyframe has a pointer to |this|.
nsTreeBodyFrame *treeBody = do_QueryFrame(frame);
NS_ENSURE_TRUE(treeBody && treeBody->GetTreeBoxObject() == this, nullptr);
mTreeBody = treeBody;
return mTreeBody;
}
NS_IMETHODIMP
TreeBoxObject::GetView(nsITreeView * *aView)
{
if (!mTreeBody) {
if (!GetTreeBodyFrame()) {
// Don't return an uninitialised view
*aView = nullptr;
return NS_OK;
}
if (mView)
// Our new frame needs to initialise itself
return mTreeBody->GetView(aView);
}
if (!mView) {
nsCOMPtr<nsIDOMXULElement> xulele = do_QueryInterface(mContent);
if (xulele) {
// See if there is a XUL tree builder associated with the element
nsCOMPtr<nsIXULTemplateBuilder> builder;
xulele->GetBuilder(getter_AddRefs(builder));
mView = do_QueryInterface(builder);
if (!mView) {
// No tree builder, create a tree content view.
nsresult rv = NS_NewTreeContentView(getter_AddRefs(mView));
NS_ENSURE_SUCCESS(rv, rv);
}
// Initialise the frame and view
mTreeBody->SetView(mView);
}
}
NS_IF_ADDREF(*aView = mView);
return NS_OK;
}
already_AddRefed<nsITreeView>
TreeBoxObject::GetView() {
nsCOMPtr<nsITreeView> view;
GetView(getter_AddRefs(view));
return view.forget();
}
static bool
CanTrustView(nsISupports* aValue)
{
// Untrusted content is only allowed to specify known-good views
if (nsContentUtils::IsCallerChrome())
return true;
nsCOMPtr<nsINativeTreeView> nativeTreeView = do_QueryInterface(aValue);
if (!nativeTreeView || NS_FAILED(nativeTreeView->EnsureNative())) {
// XXX ERRMSG need a good error here for developers
return false;
}
return true;
}
NS_IMETHODIMP TreeBoxObject::SetView(nsITreeView * aView)
{
if (!CanTrustView(aView))
return NS_ERROR_DOM_SECURITY_ERR;
mView = aView;
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
body->SetView(aView);
return NS_OK;
}
void TreeBoxObject::SetView(nsITreeView* aView, ErrorResult& aRv)
{
aRv = SetView(aView);
}
bool TreeBoxObject::Focused()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->GetFocused();
return false;
}
NS_IMETHODIMP TreeBoxObject::GetFocused(bool* aFocused)
{
*aFocused = Focused();
return NS_OK;
}
NS_IMETHODIMP TreeBoxObject::SetFocused(bool aFocused)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->SetFocused(aFocused);
return NS_OK;
}
NS_IMETHODIMP TreeBoxObject::GetTreeBody(nsIDOMElement** aElement)
{
*aElement = nullptr;
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->GetTreeBody(aElement);
return NS_OK;
}
already_AddRefed<Element>
TreeBoxObject::GetTreeBody()
{
nsCOMPtr<nsIDOMElement> el;
GetTreeBody(getter_AddRefs(el));
nsCOMPtr<Element> ret(do_QueryInterface(el));
return ret.forget();
}
already_AddRefed<nsTreeColumns>
TreeBoxObject::GetColumns()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->Columns();
return nullptr;
}
NS_IMETHODIMP TreeBoxObject::GetColumns(nsITreeColumns** aColumns)
{
*aColumns = GetColumns().take();
return NS_OK;
}
int32_t TreeBoxObject::RowHeight()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->RowHeight();
return 0;
}
int32_t TreeBoxObject::RowWidth()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->RowWidth();
return 0;
}
NS_IMETHODIMP TreeBoxObject::GetRowHeight(int32_t* aRowHeight)
{
*aRowHeight = RowHeight();
return NS_OK;
}
NS_IMETHODIMP TreeBoxObject::GetRowWidth(int32_t *aRowWidth)
{
*aRowWidth = RowWidth();
return NS_OK;
}
int32_t TreeBoxObject::GetFirstVisibleRow()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->FirstVisibleRow();
return 0;
}
NS_IMETHODIMP TreeBoxObject::GetFirstVisibleRow(int32_t *aFirstVisibleRow)
{
*aFirstVisibleRow = GetFirstVisibleRow();
return NS_OK;
}
int32_t TreeBoxObject::GetLastVisibleRow()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->LastVisibleRow();
return 0;
}
NS_IMETHODIMP TreeBoxObject::GetLastVisibleRow(int32_t *aLastVisibleRow)
{
*aLastVisibleRow = GetLastVisibleRow();
return NS_OK;
}
int32_t TreeBoxObject::HorizontalPosition()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->GetHorizontalPosition();
return 0;
}
NS_IMETHODIMP TreeBoxObject::GetHorizontalPosition(int32_t *aHorizontalPosition)
{
*aHorizontalPosition = HorizontalPosition();
return NS_OK;
}
int32_t TreeBoxObject::GetPageLength()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->PageLength();
return 0;
}
NS_IMETHODIMP TreeBoxObject::GetPageLength(int32_t *aPageLength)
{
*aPageLength = GetPageLength();
return NS_OK;
}
NS_IMETHODIMP TreeBoxObject::GetSelectionRegion(nsIScriptableRegion **aRegion)
{
*aRegion = nullptr;
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->GetSelectionRegion(aRegion);
return NS_OK;
}
already_AddRefed<nsIScriptableRegion>
TreeBoxObject::SelectionRegion()
{
nsCOMPtr<nsIScriptableRegion> region;
GetSelectionRegion(getter_AddRefs(region));
return region.forget();
}
NS_IMETHODIMP
TreeBoxObject::EnsureRowIsVisible(int32_t aRow)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->EnsureRowIsVisible(aRow);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->EnsureCellIsVisible(aRow, aCol);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::ScrollToRow(int32_t aRow)
{
nsTreeBodyFrame* body = GetTreeBodyFrame(true);
if (body)
return body->ScrollToRow(aRow);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::ScrollByLines(int32_t aNumLines)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->ScrollByLines(aNumLines);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::ScrollByPages(int32_t aNumPages)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->ScrollByPages(aNumPages);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::ScrollToCell(int32_t aRow, nsITreeColumn* aCol)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->ScrollToCell(aRow, aCol);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::ScrollToColumn(nsITreeColumn* aCol)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->ScrollToColumn(aCol);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::ScrollToHorizontalPosition(int32_t aHorizontalPosition)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->ScrollToHorizontalPosition(aHorizontalPosition);
return NS_OK;
}
NS_IMETHODIMP TreeBoxObject::Invalidate()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->Invalidate();
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::InvalidateColumn(nsITreeColumn* aCol)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->InvalidateColumn(aCol);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::InvalidateRow(int32_t aIndex)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->InvalidateRow(aIndex);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::InvalidateCell(int32_t aRow, nsITreeColumn* aCol)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->InvalidateCell(aRow, aCol);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::InvalidateRange(int32_t aStart, int32_t aEnd)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->InvalidateRange(aStart, aEnd);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->InvalidateColumnRange(aStart, aEnd, aCol);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::GetRowAt(int32_t x, int32_t y, int32_t *aRow)
{
*aRow = 0;
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->GetRowAt(x, y, aRow);
return NS_OK;
}
int32_t
TreeBoxObject::GetRowAt(int32_t x, int32_t y)
{
int32_t row;
GetRowAt(x, y, &row);
return row;
}
NS_IMETHODIMP
TreeBoxObject::GetCellAt(int32_t aX, int32_t aY, int32_t *aRow,
nsITreeColumn** aCol, nsAString& aChildElt)
{
*aRow = 0;
*aCol = nullptr;
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body) {
nsAutoCString element;
nsresult retval = body->GetCellAt(aX, aY, aRow, aCol, element);
CopyUTF8toUTF16(element, aChildElt);
return retval;
}
return NS_OK;
}
void
TreeBoxObject::GetCellAt(int32_t x, int32_t y, TreeCellInfo& aRetVal, ErrorResult& aRv)
{
nsCOMPtr<nsITreeColumn> col;
GetCellAt(x, y, &aRetVal.mRow, getter_AddRefs(col), aRetVal.mChildElt);
aRetVal.mCol = col.forget().downcast<nsTreeColumn>();
}
void
TreeBoxObject::GetCellAt(JSContext* cx,
int32_t x, int32_t y,
JS::Handle<JSObject*> rowOut,
JS::Handle<JSObject*> colOut,
JS::Handle<JSObject*> childEltOut,
ErrorResult& aRv)
{
int32_t row;
nsITreeColumn* col;
nsAutoString childElt;
GetCellAt(x, y, &row, &col, childElt);
JS::Rooted<JS::Value> v(cx);
if (!ToJSValue(cx, row, &v) ||
!JS_SetProperty(cx, rowOut, "value", v)) {
aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
return;
}
if (!dom::WrapObject(cx, col, &v) ||
!JS_SetProperty(cx, colOut, "value", v)) {
aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
return;
}
if (!ToJSValue(cx, childElt, &v) ||
!JS_SetProperty(cx, childEltOut, "value", v)) {
aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
return;
}
}
NS_IMETHODIMP
TreeBoxObject::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsAString& aElement,
int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight)
{
*aX = *aY = *aWidth = *aHeight = 0;
nsTreeBodyFrame* body = GetTreeBodyFrame();
NS_ConvertUTF16toUTF8 element(aElement);
if (body)
return body->GetCoordsForCellItem(aRow, aCol, element, aX, aY, aWidth, aHeight);
return NS_OK;
}
already_AddRefed<DOMRect>
TreeBoxObject::GetCoordsForCellItem(int32_t row, nsTreeColumn& col, const nsAString& element, ErrorResult& aRv)
{
int32_t x, y, w, h;
GetCoordsForCellItem(row, &col, element, &x, &y, &w, &h);
RefPtr<DOMRect> rect = new DOMRect(mContent, x, y, w, h);
return rect.forget();
}
void
TreeBoxObject::GetCoordsForCellItem(JSContext* cx,
int32_t row,
nsTreeColumn& col,
const nsAString& element,
JS::Handle<JSObject*> xOut,
JS::Handle<JSObject*> yOut,
JS::Handle<JSObject*> widthOut,
JS::Handle<JSObject*> heightOut,
ErrorResult& aRv)
{
int32_t x, y, w, h;
GetCoordsForCellItem(row, &col, element, &x, &y, &w, &h);
JS::Rooted<JS::Value> v(cx, JS::Int32Value(x));
if (!JS_SetProperty(cx, xOut, "value", v)) {
aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
return;
}
v.setInt32(y);
if (!JS_SetProperty(cx, yOut, "value", v)) {
aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
return;
}
v.setInt32(w);
if (!JS_SetProperty(cx, widthOut, "value", v)) {
aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
return;
}
v.setInt32(h);
if (!JS_SetProperty(cx, heightOut, "value", v)) {
aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
return;
}
}
NS_IMETHODIMP
TreeBoxObject::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *aIsCropped)
{
*aIsCropped = false;
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->IsCellCropped(aRow, aCol, aIsCropped);
return NS_OK;
}
bool
TreeBoxObject::IsCellCropped(int32_t row, nsITreeColumn* col, ErrorResult& aRv)
{
bool ret;
aRv = IsCellCropped(row, col, &ret);
return ret;
}
NS_IMETHODIMP
TreeBoxObject::RowCountChanged(int32_t aIndex, int32_t aDelta)
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->RowCountChanged(aIndex, aDelta);
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::BeginUpdateBatch()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->BeginUpdateBatch();
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::EndUpdateBatch()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->EndUpdateBatch();
return NS_OK;
}
NS_IMETHODIMP
TreeBoxObject::ClearStyleAndImageCaches()
{
nsTreeBodyFrame* body = GetTreeBodyFrame();
if (body)
return body->ClearStyleAndImageCaches();
return NS_OK;
}
void
TreeBoxObject::ClearCachedValues()
{
mTreeBody = nullptr;
}
JSObject*
TreeBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return TreeBoxObjectBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla
// Creation Routine ///////////////////////////////////////////////////////////////////////
using namespace mozilla::dom;
nsresult
NS_NewTreeBoxObject(nsIBoxObject** aResult)
{
NS_ADDREF(*aResult = new TreeBoxObject());
return NS_OK;
}

View file

@ -0,0 +1,128 @@
/* -*- 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/. */
#ifndef mozilla_dom_TreeBoxObject_h
#define mozilla_dom_TreeBoxObject_h
#include "mozilla/dom/BoxObject.h"
#include "nsITreeView.h"
#include "nsITreeBoxObject.h"
class nsTreeBodyFrame;
class nsTreeColumn;
class nsTreeColumns;
namespace mozilla {
namespace dom {
struct TreeCellInfo;
class DOMRect;
class TreeBoxObject final : public BoxObject,
public nsITreeBoxObject
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TreeBoxObject, BoxObject)
NS_DECL_NSITREEBOXOBJECT
TreeBoxObject();
nsTreeBodyFrame* GetTreeBodyFrame(bool aFlushLayout = false);
nsTreeBodyFrame* GetCachedTreeBodyFrame() { return mTreeBody; }
//NS_PIBOXOBJECT interfaces
virtual void Clear() override;
virtual void ClearCachedValues() override;
// WebIDL
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
already_AddRefed<nsTreeColumns> GetColumns();
already_AddRefed<nsITreeView> GetView();
void SetView(nsITreeView* arg, ErrorResult& aRv);
bool Focused();
already_AddRefed<Element> GetTreeBody();
int32_t RowHeight();
int32_t RowWidth();
int32_t HorizontalPosition();
already_AddRefed<nsIScriptableRegion> SelectionRegion();
int32_t GetFirstVisibleRow();
int32_t GetLastVisibleRow();
int32_t GetPageLength();
int32_t GetRowAt(int32_t x, int32_t y);
void GetCellAt(int32_t x, int32_t y, TreeCellInfo& aRetVal, ErrorResult& aRv);
already_AddRefed<DOMRect> GetCoordsForCellItem(int32_t row,
nsTreeColumn& col,
const nsAString& element,
ErrorResult& aRv);
bool IsCellCropped(int32_t row, nsITreeColumn* col, ErrorResult& aRv);
// Deprecated APIs from old IDL
void GetCellAt(JSContext* cx,
int32_t x, int32_t y,
JS::Handle<JSObject*> rowOut,
JS::Handle<JSObject*> colOut,
JS::Handle<JSObject*> childEltOut,
ErrorResult& aRv);
void GetCoordsForCellItem(JSContext* cx,
int32_t row,
nsTreeColumn& col,
const nsAString& element,
JS::Handle<JSObject*> xOut,
JS::Handle<JSObject*> yOut,
JS::Handle<JSObject*> widthOut,
JS::Handle<JSObject*> heightOut,
ErrorResult& aRv);
// Same signature (except for nsresult return type) as the XPIDL impls
// void Invalidate();
// void BeginUpdateBatch();
// void EndUpdateBatch();
// void ClearStyleAndImageCaches();
// void SetFocused(bool arg);
// void EnsureRowIsVisible(int32_t index);
// void EnsureCellIsVisible(int32_t row, nsITreeColumn* col);
// void ScrollToRow(int32_t index);
// void ScrollByLines(int32_t numLines);
// void ScrollByPages(int32_t numPages);
// void ScrollToCell(int32_t row, nsITreeColumn* col);
// void ScrollToColumn(nsITreeColumn* col);
// void ScrollToHorizontalPosition(int32_t horizontalPosition);
// void InvalidateColumn(nsITreeColumn* col);
// void InvalidateRow(int32_t index);
// void InvalidateCell(int32_t row, nsITreeColumn* col);
// void InvalidateRange(int32_t startIndex, int32_t endIndex);
// void InvalidateColumnRange(int32_t startIndex, int32_t endIndex, nsITreeColumn* col);
// void RowCountChanged(int32_t index, int32_t count);
protected:
nsTreeBodyFrame* mTreeBody;
nsCOMPtr<nsITreeView> mView;
private:
~TreeBoxObject();
};
} // namespace dom
} // namespace mozilla
#endif

View file

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="var tree = document.getElementById('tree'), treeitem = document.getElementById('treeitem'); tree.parentNode.insertBefore(treeitem, tree);">
<tree flex="1" id="tree">
<treecols>
<treecol id="name" label="Name" primary="true" flex="1"/>
</treecols>
<treechildren>
<treeitem id="treeitem">
<treerow>
<treecell label="Click the button below to crash"/>
</treerow>
</treeitem>
</treechildren>
</tree>
</window>

View file

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="reftest-wait" onload="setTimeout(boom, 30)">
<script>
function boom()
{
document.documentElement.appendChild(document.getElementById("TC"));
document.documentElement.appendChild(document.getElementById("TI"));
document.documentElement.removeAttribute("class");
}
</script>
<tree flex="1">
<treecols>
<treecol label="Name"/>
</treecols>
<treechildren id="TC">
<treeitem id="TI">
<treerow>
<treecell label="First treecell"/>
</treerow>
</treeitem>
</treechildren>
</tree>
</window>

View file

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="reftest-wait" onload="setTimeout(boom, 30)">
<script>
function boom()
{
document.documentElement.appendChild(document.getElementById('TC'));
document.getElementById('TI').hidden = false;
document.documentElement.removeAttribute("class");
}
</script>
<tree flex="1">
<treecols>
<treecol label="Name" flex="1"/>
</treecols>
<treechildren id="TC">
<treeitem>
<treerow>
<treecell label="First treecell"/>
</treerow>
</treeitem>
</treechildren>
</tree>
<treeitem id="TI" hidden="true"/>
</window>

View file

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="boom1();"
class="reftest-wait">
<script>
var tree;
function boom1()
{
tree = document.getElementById("tree");
tree.style.position = "fixed";
setTimeout(boom2, 30);
}
function boom2()
{
tree.style.overflow = "visible";
document.documentElement.removeAttribute("class");
}
</script>
<tree rows="6" id="tree" style="display: list-item; overflow: auto; visibility: collapse;">
<treecols>
<treecol id="firstname" label="First Name" primary="true" flex="3"/>
<treecol id="lastname" label="Last Name" flex="7"/>
</treecols>
<treechildren>
<treeitem container="true" open="true">
<treerow>
<treecell label="Foo"/>
</treerow>
</treeitem>
</treechildren>
</tree>
</window>

View file

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.documentElement.style.content = '\'a\'';">
<html:style>
* { position: fixed; }
</html:style>
<tree rows="6">
<treecols>
<treecol id="firstname" label="First Name" primary="true"/>
</treecols>
<treechildren>
<treeitem>
<treerow>
<treecell/>
</treerow>
</treeitem>
</treechildren>
</tree>
</window>

View file

@ -0,0 +1,15 @@
<html>
<head>
<title>Testcase bug - Crash [@ nsINodeInfo::Equals] with underflow event, tree stuff and removing window</title>
</head>
<body>
<iframe src="data:application/vnd.mozilla.xul+xml;charset=utf-8,%3Cwindow%20xmlns%3D%22http%3A//www.mozilla.org/keymaster/gatekeeper/there.is.only.xul%22%3E%0A%3Ctree%20style%3D%22overflow%3A%20auto%3B%20display%3A%20-moz-inline-box%3B%22%3E%0A%3Ctreeitem%20style%3D%22overflow%3A%20scroll%3B%20display%3A%20table-cell%3B%22%3E%0A%3Ctreechildren%20style%3D%22%20display%3A%20table-row%3B%22%3E%0A%3Ctreeitem%20id%3D%22a%22%20style%3D%22display%3A%20table-cell%3B%22%3E%0A%3C/treeitem%3E%0A%3C/treechildren%3E%0A%3C/treeitem%3E%0A%0A%3C/tree%3E%0A%0A%3Cscript%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%0Afunction%20doe%28%29%20%7B%0Adocument.getElementById%28%27a%27%29.parentNode.removeChild%28document.getElementById%28%27a%27%29%29%3B%0A%7D%0AsetTimeout%28doe%2C%20100%29%3B%0Adocument.addEventListener%28%27underflow%27%2C%20function%28e%29%20%7Bwindow.frameElement.parentNode.removeChild%28window.frameElement%29%20%7D%2C%20true%29%3B%0Awindow.addEventListener%28%27underflow%27%2C%20function%28e%29%20%7Bwindow.frameElement.parentNode.removeChild%28window.frameElement%29%20%7D%2C%20true%29%3B%0A%3C/script%3E%0A%3C/window%3E" id="content"></iframe>
<script>
function doe() {
window.location.reload();
}
setTimeout(doe, 500);
</script>
</body>
</html>

View file

@ -0,0 +1,9 @@
<html class="reftest-wait">
<head>
<script>
setTimeout('document.documentElement.className = ""', 500);
</script>
<body>
<iframe src="382444-1-inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,41 @@
<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
<head>
<script>
var ccc;
function boom()
{
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var hbox = document.createElementNS(XUL_NS, 'hbox');
var tree = document.createElementNS(XUL_NS, 'tree');
var treecol = document.createElementNS(XUL_NS, 'treecol');
ccc = document.getElementById("ccc");
ccc.style.position = "fixed";
hbox.appendChild(treecol);
tree.appendChild(hbox);
ccc.appendChild(tree);
setTimeout(boom2, 200);
}
function boom2()
{
ccc.style.position = "";
document.documentElement.removeAttribute("class");
}
</script>
</head>
<body onload="boom();">
<div id="ccc">
</div>
</body>
</html>

View file

@ -0,0 +1,20 @@
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" class="reftest-wait">
<tree id="a" style="position: fixed;">
<box style=" display: -moz-box; position: fixed;">
<treecol style=" display: -moz-box;"/>
</box>
<box style="position: fixed;">
<treechildren style="display: -moz-box; position: absolute;"/>
</box>
</tree>
<script xmlns="http://www.w3.org/1999/xhtml">
function removestyles(){
document.getElementById('a').removeAttribute('style');
document.documentElement.removeAttribute("class");
}
setTimeout(removestyles, 100);
</script>
</window>

View file

@ -0,0 +1,3 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<treechildren style="display: block" />
</window>

View file

@ -0,0 +1,44 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="reftest-wait" onload="setTimeout(boom, 30)">
<script>
function boom()
{
var tree = document.getElementById("thetree");
var selection = tree.view.selection;
selection.select(0);
tree.parentNode.removeChild(tree);
// This is expected to throw an error (it used to crash).
try {
selection.rangedSelect(1, 1, false);
}
catch (ex) {}
document.documentElement.removeAttribute("class");
}
</script>
<tree flex="1" id="thetree">
<treecols>
<treecol label="Name"/>
</treecols>
<treechildren id="TC">
<treeitem id="TI1">
<treerow>
<treecell label="First treecell"/>
</treerow>
</treeitem>
<treeitem id="TI2">
<treerow>
<treecell label="Second treecell"/>
</treerow>
</treeitem>
</treechildren>
</tree>
</window>

View file

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="reftest-wait" onload="setTimeout(boom, 30);">
<script>
function boom()
{
var tree = document.getElementById("thetree");
var selection = tree.view.selection;
var treecolumn0 = tree.columns[0];
var treecolumn1 = tree.columns[1];
selection.select(0);
selection.currentColumn = treecolumn0;
tree.parentNode.removeChild(tree);
// This is expected to throw an error (it used to crash).
try {
selection.currentColumn = treecolumn1;
}
catch (ex) {}
document.documentElement.removeAttribute("class");
}
</script>
<tree flex="1" id="thetree" seltype="cell">
<treecols>
<treecol label="Name"/>
<treecol label="Test"/>
</treecols>
<treechildren id="TC">
<treeitem id="TI1">
<treerow>
<treecell label="First treecell"/>
<treecell label="Second treecell"/>
</treerow>
</treeitem>
<treeitem id="TI2">
<treerow>
<treecell label="Third treecell"/>
<treecell label="Fourth treecell"/>
</treerow>
</treeitem>
</treechildren>
</tree>
</window>

View file

@ -0,0 +1,10 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
</head>
<body>
<xul:treechildren style="display: inline;" />
</body>
</html>

View file

@ -0,0 +1,9 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<body style="float: right;" onload="document.body.style.cssFloat = '';">
<xul:tree><xul:hbox><xul:treecol /></xul:hbox></xul:tree>
</body>
</html>

View file

@ -0,0 +1,25 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="boom();">
<script type="text/javascript">
function boom()
{
var tree = document.getElementById("tree");
var tc = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treechildren");
document.addEventListener("DOMAttrModified", m, false);
tree.appendChild(tc);
function m()
{
document.removeEventListener("DOMAttrModified", m, false);
tree.removeChild(tc);
}
}
</script>
<tree id="tree" />
</window>

View file

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="boom();">
<script type="text/javascript">
function boom()
{
var option = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
document.getElementById("tc").appendChild(option);
}
</script>
<tree><treechildren id="tc"><hbox/></treechildren></tree>
</window>

View file

@ -0,0 +1,8 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<tree id="a" style="content: 't';" rows="-2">
<menuitem id="b" onoverflow="event.currentTarget.parentNode.removeAttribute('style')">
<treechildren style="display: block;" onoverflow="event.target.parentNode.removeChild(event.target)" />
<treechildren style="display: block;" ordinal="0.5"/>
</menuitem>
</tree>
</window>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<tree flex="1">
<treecols>
<treecol label="test" flex="1" type="progressmeter" />
</treecols>
<treechildren>
<treeitem>
<treerow>
<treecell value="50" mode="normal" />
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell mode="undetermined" />
</treerow>
</treeitem>
</treechildren>
</tree>
</window>

View file

@ -0,0 +1,19 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<script type="text/javascript">
function boom()
{
var o = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
var q = document.getElementById("q");
q.appendChild(o);
}
</script>
</head>
<body onload="boom();">
<xul:tree><xul:treechildren id="q"><div/></xul:treechildren></xul:tree>
</body>
</html>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<box id="b">
<box onDOMAttrModified="event.target.parentNode.removeChild(event.target)" id="d"/>
<tree/>
</box>
<tree>
<box id="b" observes="d"/>
<treechildren observes="b"/>
</tree>
</overlay>

View file

@ -0,0 +1,3 @@
<?xul-overlay href="509602-1-overlay.xul"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>

View file

@ -0,0 +1,72 @@
<?xml version="1.0"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="setInterval(run, 25)">
<tree flex="1" rows="2">
<treecols>
<treecol id="sender" label="Sender" flex="1"/>
<treecol id="subject" label="Subject" flex="2"/>
</treecols>
<treechildren>
<treeitem>
<treerow>
<treecell label="joe@somewhere.com"/>
<treecell label="Top secret plans"/>
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell label="mel@whereever.com"/>
<treecell label="Let's do lunch"/>
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell label="mel@whereever.com"/>
<treecell label="Let's do lunch"/>
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell label="mel@whereever.com"/>
<treecell label="Let's do lunch"/>
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell label="mel@whereever.com"/>
<treecell label="Let's do lunch"/>
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell label="mel@whereever.com"/>
<treecell label="Let's do lunch"/>
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell label="mel@whereever.com"/>
<treecell label="Let's do lunch"/>
</treerow>
</treeitem>
<treeitem>
<treerow>
<treecell label="mel@whereever.com"/>
<treecell label="Let's do lunch"/>
</treerow>
</treeitem>
</treechildren>
</tree>
<script type="text/javascript"><![CDATA[
function run() {
var tree = document.getElementsByTagName("tree")[0];
var sel = tree.view.selection;
sel.rangedSelect(0, 0, true);
sel.rangedSelect(1000, 1001, true);
sel.adjustSelection(1, 0x7fffffff);
}
]]></script>
</window>

View file

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html class="reftest-wait"><head>
<meta charset="utf-8">
<title>Testcase for bug 585815</title>
<script>
function done()
{
document.documentElement.removeAttribute("class");
}
</script>
</head>
<body onload="setTimeout(done,1000)">
<iframe src="585815-iframe.xul"></iframe>
</body>
</html>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html class="reftest-wait">
<script>
var onPaintFunctions =
[
function() { document.documentElement.style.MozAppearance = "treeheadersortarrow"; },
function() { document.documentElement.style.position = "fixed"; },
function() { document.documentElement.removeAttribute("class"); }
];
var i = 0;
function advance()
{
var f = onPaintFunctions[i++];
if (f)
f();
}
function start()
{
window.addEventListener("MozAfterPaint", advance, true);
advance();
}
window.addEventListener("load", start, false);
</script>
</html>

View file

@ -0,0 +1,54 @@
<?xml version="1.0"?>
<!--
Program received signal SIGSEGV, Segmentation fault.
0xb6457185 in nsIContent::SetAttr (this=0x0, aNameSpaceID=0, aName=0xb0cb064c, aValue=..., aNotify=1) at ../../dist/include/nsIContent.h:285
285 return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
(gdb) p this
$6 = (nsIContent * const) 0x0
(gdb) bt 3
#0 0xb6457185 in nsIContent::SetAttr (this=0x0, aNameSpaceID=0, aName=0xb0cb064c, aValue=..., aNotify=1) at ../../dist/include/nsIContent.h:285
#1 0xb6b72072 in nsTreeColumns::RestoreNaturalOrder (this=0xaaf83cc0) at layout/xul/base/src/tree/src/nsTreeColumns.cpp:605
#2 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
(More stack frames follow...)
(gdb) frame 1
#1 0xb6b72072 in nsTreeColumns::RestoreNaturalOrder (this=0xaaf83cc0) at layout/xul/base/src/tree/src/nsTreeColumns.cpp:605
605 child->SetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, ordinal, PR_TRUE);
(gdb) list
600 PRUint32 numChildren = colsContent->GetChildCount();
601 for (PRUint32 i = 0; i < numChildren; ++i) {
602 nsIContent *child = colsContent->GetChildAt(i);
603 nsAutoString ordinal;
604 ordinal.AppendInt(i);
605 child->SetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, ordinal, PR_TRUE);
606 }
(gdb) p child
$7 = (nsIContent *) 0x0
First loop iteration: |child->SetAttr()| dispatches "DOMAttrModified" event.
Event listener removes next column. Second loop iteration: |colsContent->GetChildAt(i)|
returns null. Then we have |null->SetAttr()|.
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="run();">
<tree id="tree">
<treecols>
<treecol id="col1"/>
<treecol id="col2"/>
</treecols>
<treechildren/>
</tree>
<script type="text/javascript"><![CDATA[
function listener() {
var col2 = document.getElementById("col2");
col2.parentNode.removeChild(col2);
}
function run() {
var col1 = document.getElementById("col1");
col1.addEventListener("DOMAttrModified", listener, true);
var tree = document.getElementById("tree");
tree.columns.restoreNaturalOrder();
}
]]></script>
</window>

View file

@ -0,0 +1,52 @@
<?xml version="1.0"?>
<!--
Program received signal SIGSEGV, Segmentation fault.
0xb6b720a6 in nsTreeColumns::RestoreNaturalOrder (this=0xa947a580) at layout/xul/base/src/tree/src/nsTreeColumns.cpp:610
610 mTree->Invalidate();
(gdb) bt 3
#0 0xb6b720a6 in nsTreeColumns::RestoreNaturalOrder (this=0xa947a580) at layout/xul/base/src/tree/src/nsTreeColumns.cpp:610
#1 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
#2 0xb6171901 in XPCWrappedNative::CallMethod (ccx=..., mode=XPCWrappedNative::CALL_METHOD)
at js/src/xpconnect/src/xpcwrappednative.cpp:2722
(More stack frames follow...)
(gdb) list
605 child->SetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, ordinal, PR_TRUE);
606 }
607
608 nsTreeColumns::InvalidateColumns();
609
610 mTree->Invalidate();
611
612 return NS_OK;
613 }
614
(gdb) p mTree
$9 = (nsITreeBoxObject *) 0x0
|child->SetAttr()| dispatches "DOMAttrModified" event. Event listener removes
whole tree, |mTree| is being set to null. Then we have |null->Invalidate()|.
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="run();">
<tree id="tree">
<treecols>
<treecol id="col"/>
</treecols>
<treechildren/>
</tree>
<script type="text/javascript"><![CDATA[
var tree = null;
function listener() {
tree.parentNode.removeChild(tree);
}
function run() {
col = document.getElementById("col");
col.addEventListener("DOMAttrModified", listener, true);
tree = document.getElementById("tree");
tree.columns.restoreNaturalOrder();
}
]]></script>
</window>

View file

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<!--
###!!! ASSERTION: You can't dereference a NULL nsCOMPtr with operator->().: 'mRawPtr != 0', file ../../../../dist/include/nsCOMPtr.h, line 796
Program received signal SIGSEGV, Segmentation fault.
0xb6b7463a in nsTreeContentView::SetTree (this=0xb0ba2510, aTree=0xaaecece0) at layout/xul/base/src/tree/src/nsTreeContentView.cpp:571
571 boxObject->GetElement(getter_AddRefs(element));
(gdb) bt 3
#0 0xb6b7463a in nsTreeContentView::SetTree (this=0xb0ba2510, aTree=0xaaecece0) at layout/xul/base/src/tree/src/nsTreeContentView.cpp:571
#1 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
#2 0xb6171901 in XPCWrappedNative::CallMethod (ccx=..., mode=XPCWrappedNative::CALL_METHOD)
at js/src/xpconnect/src/xpcwrappednative.cpp:2722
(More stack frames follow...)
(gdb) list 566
561 nsTreeContentView::SetTree(nsITreeBoxObject* aTree)
562 {
563 ClearRows();
564
565 mBoxObject = aTree;
566
567 if (aTree && !mRoot) {
568 // Get our root element
569 nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mBoxObject);
570 nsCOMPtr<nsIDOMElement> element;
571 boxObject->GetElement(getter_AddRefs(element));
(gdb) p boxObject
$16 = {mRawPtr = 0x0}
|aTree| does not implement |nsIBoxObject|, so |do_QueryInterface(mBoxObject)|
returns null. Then we have |null->GetElement()|.
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('tree').view.setTree({});">
<tree id="tree">
<treechildren/>
</tree>
</window>

View file

@ -0,0 +1,24 @@
load 307298-1.xul
load 309732-1.xul
load 309732-2.xul
load 366583-1.xul
load 380217-1.xul
load 382444-1.html
load 391178-1.xhtml
load 391178-2.xul
load 393665-1.xul
load 399227-1.xul
load 399227-2.xul
load 399692-1.xhtml
load 399715-1.xhtml
load 409807-1.xul
load 414170-1.xul
load 430394-1.xul
load 454186-1.xul
load 479931-1.xhtml
load 509602-1.xul
load 585815.html
load 601427.html
load 730441-1.xul
load 730441-2.xul
load 730441-3.xul

53
layout/xul/tree/moz.build Normal file
View file

@ -0,0 +1,53 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
with Files('**'):
BUG_COMPONENT = ('Core', 'XP Toolkit/Widgets: XUL')
XPIDL_SOURCES += [
'nsITreeBoxObject.idl',
'nsITreeColumns.idl',
'nsITreeContentView.idl',
'nsITreeSelection.idl',
'nsITreeView.idl',
]
XPIDL_MODULE = 'layout_xul_tree'
EXPORTS += [
'nsTreeColFrame.h',
'nsTreeColumns.h',
'nsTreeUtils.h',
]
EXPORTS.mozilla.dom += [
'TreeBoxObject.h'
]
UNIFIED_SOURCES += [
'nsTreeBodyFrame.cpp',
'nsTreeColFrame.cpp',
'nsTreeColumns.cpp',
'nsTreeContentView.cpp',
'nsTreeImageListener.cpp',
'nsTreeSelection.cpp',
'nsTreeStyleCache.cpp',
'nsTreeUtils.cpp',
'TreeBoxObject.cpp',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'..',
'../../base',
'../../forms',
'../../generic',
'../../style',
'/dom/base',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -0,0 +1,189 @@
/* -*- 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 "nsISupports.idl"
interface nsIDOMElement;
interface nsITreeView;
interface nsITreeSelection;
interface nsITreeColumn;
interface nsITreeColumns;
interface nsIScriptableRegion;
[scriptable, uuid(f3da0c5e-51f5-45f0-b2cd-6be3ab6847ae)]
interface nsITreeBoxObject : nsISupports
{
/**
* Obtain the columns.
*/
readonly attribute nsITreeColumns columns;
/**
* The view that backs the tree and that supplies it with its data.
* It is dynamically settable, either using a view attribute on the
* tree tag or by setting this attribute to a new value.
*/
attribute nsITreeView view;
/**
* Whether or not we are currently focused.
*/
attribute boolean focused;
/**
* Obtain the treebody content node
*/
readonly attribute nsIDOMElement treeBody;
/**
* Obtain the height of a row.
*/
readonly attribute long rowHeight;
/**
* Obtain the width of a row.
*/
readonly attribute long rowWidth;
/**
* Get the pixel position of the horizontal scrollbar.
*/
readonly attribute long horizontalPosition;
/**
* Return the region for the visible parts of the selection, in device pixels.
*/
readonly attribute nsIScriptableRegion selectionRegion;
/**
* Get the index of the first visible row.
*/
long getFirstVisibleRow();
/**
* Get the index of the last visible row.
*/
long getLastVisibleRow();
/**
* Gets the number of possible visible rows.
*/
long getPageLength();
/**
* Ensures that a row at a given index is visible.
*/
void ensureRowIsVisible(in long index);
/**
* Ensures that a given cell in the tree is visible.
*/
void ensureCellIsVisible(in long row, in nsITreeColumn col);
/**
* Scrolls such that the row at index is at the top of the visible view.
*/
void scrollToRow(in long index);
/**
* Scroll the tree up or down by numLines lines. Positive
* values move down in the tree. Prevents scrolling off the
* end of the tree.
*/
void scrollByLines(in long numLines);
/**
* Scroll the tree up or down by numPages pages. A page
* is considered to be the amount displayed by the tree.
* Positive values move down in the tree. Prevents scrolling
* off the end of the tree.
*/
void scrollByPages(in long numPages);
/**
* Scrolls such that a given cell is visible (if possible)
* at the top left corner of the visible view.
*/
void scrollToCell(in long row, in nsITreeColumn col);
/**
* Scrolls horizontally so that the specified column is
* at the left of the view (if possible).
*/
void scrollToColumn(in nsITreeColumn col);
/**
* Scroll to a specific horizontal pixel position.
*/
void scrollToHorizontalPosition(in long horizontalPosition);
/**
* Invalidation methods for fine-grained painting control.
*/
void invalidate();
void invalidateColumn(in nsITreeColumn col);
void invalidateRow(in long index);
void invalidateCell(in long row, in nsITreeColumn col);
void invalidateRange(in long startIndex, in long endIndex);
void invalidateColumnRange(in long startIndex, in long endIndex,
in nsITreeColumn col);
/**
* A hit test that can tell you what row the mouse is over.
* returns -1 for invalid mouse coordinates.
*
* The coordinate system is the client coordinate system for the
* document this boxObject lives in, and the units are CSS pixels.
*/
long getRowAt(in long x, in long y);
/**
* A hit test that can tell you what cell the mouse is over. Row is the row index
* hit, returns -1 for invalid mouse coordinates. ColID is the column hit.
* ChildElt is the pseudoelement hit: this can have values of
* "cell", "twisty", "image", and "text".
*
* The coordinate system is the client coordinate system for the
* document this boxObject lives in, and the units are CSS pixels.
*/
void getCellAt(in long x, in long y, out long row, out nsITreeColumn col, out AString childElt);
/**
* Find the coordinates of an element within a specific cell.
*/
void getCoordsForCellItem(in long row, in nsITreeColumn col, in AString element,
out long x, out long y, out long width, out long height);
/**
* Determine if the text of a cell is being cropped or not.
*/
boolean isCellCropped(in long row, in nsITreeColumn col);
/**
* The view is responsible for calling these notification methods when
* rows are added or removed. Index is the position at which the new
* rows were added or at which rows were removed. For
* non-contiguous additions/removals, this method should be called multiple times.
*/
void rowCountChanged(in long index, in long count);
/**
* Notify the tree that the view is about to perform a batch
* update, that is, add, remove or invalidate several rows at once.
* This must be followed by calling endUpdateBatch(), otherwise the tree
* will get out of sync.
*/
void beginUpdateBatch();
/**
* Notify the tree that the view has completed a batch update.
*/
void endUpdateBatch();
/**
* Called on a theme switch to flush out the tree's style and image caches.
*/
void clearStyleAndImageCaches();
};

View file

@ -0,0 +1,96 @@
/* 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 "nsISupports.idl"
interface nsITreeColumns;
interface nsIDOMElement;
interface nsIAtom;
[scriptable, uuid(ae835ecf-6b32-4660-9b43-8a270df56e02)]
interface nsITreeColumn : nsISupports
{
readonly attribute nsIDOMElement element;
readonly attribute nsITreeColumns columns;
readonly attribute long x;
readonly attribute long width;
readonly attribute AString id;
[noscript] void getIdConst([shared] out wstring idConst);
[noscript] readonly attribute nsIAtom atom;
readonly attribute long index;
readonly attribute boolean primary;
readonly attribute boolean cycler;
readonly attribute boolean editable;
readonly attribute boolean selectable;
const short TYPE_TEXT = 1;
const short TYPE_CHECKBOX = 2;
const short TYPE_PROGRESSMETER = 3;
const short TYPE_PASSWORD = 4;
readonly attribute short type;
nsITreeColumn getNext();
nsITreeColumn getPrevious();
void invalidate();
};
interface nsITreeBoxObject;
[scriptable, uuid(f8a8d6b4-6788-438d-9009-7142798767ab)]
interface nsITreeColumns : nsISupports
{
/**
* The tree widget for these columns.
*/
readonly attribute nsITreeBoxObject tree;
/**
* The number of columns.
*/
readonly attribute long count;
/**
* An alias for count (for the benefit of scripts which treat this as an
* array).
*/
readonly attribute long length;
/**
* Get the first/last column.
*/
nsITreeColumn getFirstColumn();
nsITreeColumn getLastColumn();
/**
* Attribute based column getters.
*/
nsITreeColumn getPrimaryColumn();
nsITreeColumn getSortedColumn();
nsITreeColumn getKeyColumn();
/**
* Get the column for the given element.
*/
nsITreeColumn getColumnFor(in nsIDOMElement element);
/**
* Parametric column getters.
*/
nsITreeColumn getNamedColumn(in AString id);
nsITreeColumn getColumnAt(in long index);
/**
* This method is called whenever a treecol is added or removed and
* the column cache needs to be rebuilt.
*/
void invalidateColumns();
void restoreNaturalOrder();
};

View file

@ -0,0 +1,22 @@
/* -*- 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 "nsISupports.idl"
interface nsIDOMElement;
[scriptable, uuid(5ef62896-0c0a-41f1-bb3c-44a60f5dfdab)]
interface nsITreeContentView : nsISupports
{
/**
* Retrieve the content item associated with the specified index.
*/
nsIDOMElement getItemAtIndex(in long index);
/**
* Retrieve the index associated with the specified content item.
*/
long getIndexOfItem(in nsIDOMElement item);
};

View file

@ -0,0 +1,130 @@
/* -*- 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/. */
interface nsITreeBoxObject;
interface nsITreeColumn;
#include "nsISupports.idl"
[scriptable, uuid(ab6fe746-300b-4ab4-abb9-1c0e3977874c)]
interface nsITreeSelection : nsISupports
{
/**
* The tree widget for this selection.
*/
attribute nsITreeBoxObject tree;
/**
* This attribute is a boolean indicating single selection.
*/
readonly attribute boolean single;
/**
* The number of rows currently selected in this tree.
*/
readonly attribute long count;
/**
* Indicates whether or not the row at the specified index is
* part of the selection.
*/
boolean isSelected(in long index);
/**
* Deselect all rows and select the row at the specified index.
*/
void select(in long index);
/**
* Perform a timed select.
*/
void timedSelect(in long index, in long delay);
/**
* Toggle the selection state of the row at the specified index.
*/
void toggleSelect(in long index);
/**
* Select the range specified by the indices. If augment is true,
* then we add the range to the selection without clearing out anything
* else. If augment is false, everything is cleared except for the specified range.
*/
void rangedSelect(in long startIndex, in long endIndex, in boolean augment);
/**
* Clears the range.
*/
void clearRange(in long startIndex, in long endIndex);
/**
* Clears the selection.
*/
void clearSelection();
/**
* Inverts the selection.
*/
void invertSelection();
/**
* Selects all rows.
*/
void selectAll();
/**
* Iterate the selection using these methods.
*/
long getRangeCount();
void getRangeAt(in long i, out long min, out long max);
/**
* Can be used to invalidate the selection.
*/
void invalidateSelection();
/**
* Called when the row count changes to adjust selection indices.
*/
void adjustSelection(in long index, in long count);
/**
* This attribute is a boolean indicating whether or not the
* "select" event should fire when the selection is changed using
* one of our methods. A view can use this to temporarily suppress
* the selection while manipulating all of the indices, e.g., on
* a sort.
* Note: setting this attribute to false will fire a select event.
*/
attribute boolean selectEventsSuppressed;
/**
* The current item (the one that gets a focus rect in addition to being
* selected).
*/
attribute long currentIndex;
/**
* The current column.
*/
attribute nsITreeColumn currentColumn;
/**
* The selection "pivot". This is the first item the user selected as
* part of a ranged select.
*/
readonly attribute long shiftSelectPivot;
};
/**
* The following interface is not scriptable and MUST NEVER BE MADE scriptable.
* Native treeselections implement it, and we use this to check whether a
* treeselection is native (and therefore suitable for use by untrusted content).
*/
[uuid(1bd59678-5cb3-4316-b246-31a91b19aabe)]
interface nsINativeTreeSelection : nsITreeSelection
{
[noscript] void ensureNative();
};

View file

@ -0,0 +1,215 @@
/* -*- 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 "nsISupports.idl"
interface nsITreeBoxObject;
interface nsITreeSelection;
interface nsITreeColumn;
interface nsIDOMDataTransfer;
[scriptable, uuid(091116f0-0bdc-4b32-b9c8-c8d5a37cb088)]
interface nsITreeView : nsISupports
{
/**
* The total number of rows in the tree (including the offscreen rows).
*/
readonly attribute long rowCount;
/**
* The selection for this view.
*/
attribute nsITreeSelection selection;
/**
* A whitespace delimited list of properties. For each property X the view
* gives back will cause the pseudoclasses ::-moz-tree-cell(x),
* ::-moz-tree-row(x), ::-moz-tree-twisty(x), ::-moz-tree-image(x),
* ::-moz-tree-cell-text(x). to be matched on the pseudoelement
* ::moz-tree-row.
*/
AString getRowProperties(in long index);
/**
* A whitespace delimited list of properties for a given cell. Each
* property, x, that the view gives back will cause the pseudoclasses
* ::-moz-tree-cell(x), ::-moz-tree-row(x), ::-moz-tree-twisty(x),
* ::-moz-tree-image(x), ::-moz-tree-cell-text(x). to be matched on the
* cell.
*/
AString getCellProperties(in long row, in nsITreeColumn col);
/**
* Called to get properties to paint a column background. For shading the sort
* column, etc.
*/
AString getColumnProperties(in nsITreeColumn col);
/**
* Methods that can be used to test whether or not a twisty should be drawn,
* and if so, whether an open or closed twisty should be used.
*/
boolean isContainer(in long index);
boolean isContainerOpen(in long index);
boolean isContainerEmpty(in long index);
/**
* isSeparator is used to determine if the row at index is a separator.
* A value of true will result in the tree drawing a horizontal separator.
* The tree uses the ::moz-tree-separator pseudoclass to draw the separator.
*/
boolean isSeparator(in long index);
/**
* Specifies if there is currently a sort on any column. Used mostly by dragdrop
* to affect drop feedback.
*/
boolean isSorted();
const short DROP_BEFORE = -1;
const short DROP_ON = 0;
const short DROP_AFTER = 1;
/**
* Methods used by the drag feedback code to determine if a drag is allowable at
* the current location. To get the behavior where drops are only allowed on
* items, such as the mailNews folder pane, always return false when
* the orientation is not DROP_ON.
*/
boolean canDrop(in long index, in long orientation, in nsIDOMDataTransfer dataTransfer);
/**
* Called when the user drops something on this view. The |orientation| param
* specifies before/on/after the given |row|.
*/
void drop(in long row, in long orientation, in nsIDOMDataTransfer dataTransfer);
/**
* Methods used by the tree to draw thread lines in the tree.
* getParentIndex is used to obtain the index of a parent row.
* If there is no parent row, getParentIndex returns -1.
*/
long getParentIndex(in long rowIndex);
/**
* hasNextSibling is used to determine if the row at rowIndex has a nextSibling
* that occurs *after* the index specified by afterIndex. Code that is forced
* to march down the view looking at levels can optimize the march by starting
* at afterIndex+1.
*/
boolean hasNextSibling(in long rowIndex, in long afterIndex);
/**
* The level is an integer value that represents
* the level of indentation. It is multiplied by the width specified in the
* :moz-tree-indentation pseudoelement to compute the exact indendation.
*/
long getLevel(in long index);
/**
* The image path for a given cell. For defining an icon for a cell.
* If the empty string is returned, the :moz-tree-image pseudoelement
* will be used.
*/
AString getImageSrc(in long row, in nsITreeColumn col);
/**
* The progress mode for a given cell. This method is only called for
* columns of type |progressmeter|.
*/
const short PROGRESS_NORMAL = 1;
const short PROGRESS_UNDETERMINED = 2;
const short PROGRESS_NONE = 3;
long getProgressMode(in long row, in nsITreeColumn col);
/**
* The value for a given cell. This method is only called for columns
* of type other than |text|.
*/
AString getCellValue(in long row, in nsITreeColumn col);
/**
* The text for a given cell. If a column consists only of an image, then
* the empty string is returned.
*/
AString getCellText(in long row, in nsITreeColumn col);
/**
* Called during initialization to link the view to the front end box object.
*/
void setTree(in nsITreeBoxObject tree);
/**
* Called on the view when an item is opened or closed.
*/
void toggleOpenState(in long index);
/**
* Called on the view when a header is clicked.
*/
void cycleHeader(in nsITreeColumn col);
/**
* Should be called from a XUL onselect handler whenever the selection changes.
*/
void selectionChanged();
/**
* Called on the view when a cell in a non-selectable cycling column (e.g., unread/flag/etc.) is clicked.
*/
void cycleCell(in long row, in nsITreeColumn col);
/**
* isEditable is called to ask the view if the cell contents are editable.
* A value of true will result in the tree popping up a text field when
* the user tries to inline edit the cell.
*/
boolean isEditable(in long row, in nsITreeColumn col);
/**
* isSelectable is called to ask the view if the cell is selectable.
* This method is only called if the selection style is |cell| or |text|.
* XXXvarga shouldn't this be called isCellSelectable?
*/
boolean isSelectable(in long row, in nsITreeColumn col);
/**
* setCellValue is called when the value of the cell has been set by the user.
* This method is only called for columns of type other than |text|.
*/
void setCellValue(in long row, in nsITreeColumn col, in AString value);
/**
* setCellText is called when the contents of the cell have been edited by the user.
*/
void setCellText(in long row, in nsITreeColumn col, in AString value);
/**
* A command API that can be used to invoke commands on the selection. The tree
* will automatically invoke this method when certain keys are pressed. For example,
* when the DEL key is pressed, performAction will be called with the "delete" string.
*/
void performAction(in wstring action);
/**
* A command API that can be used to invoke commands on a specific row.
*/
void performActionOnRow(in wstring action, in long row);
/**
* A command API that can be used to invoke commands on a specific cell.
*/
void performActionOnCell(in wstring action, in long row, in nsITreeColumn col);
};
/**
* The following interface is not scriptable and MUST NEVER BE MADE scriptable.
* Native treeviews implement it, and we use this to check whether a treeview
* is native (and therefore suitable for use by untrusted content).
*/
[uuid(46c90265-6553-41ae-8d39-7022e7d09145)]
interface nsINativeTreeView : nsITreeView
{
[noscript] void ensureNative();
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,651 @@
/* -*- 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/. */
#ifndef nsTreeBodyFrame_h
#define nsTreeBodyFrame_h
#include "mozilla/Attributes.h"
#include "nsLeafBoxFrame.h"
#include "nsITreeView.h"
#include "nsICSSPseudoComparator.h"
#include "nsIScrollbarMediator.h"
#include "nsITimer.h"
#include "nsIReflowCallback.h"
#include "nsTArray.h"
#include "nsTreeStyleCache.h"
#include "nsTreeColumns.h"
#include "nsDataHashtable.h"
#include "imgIRequest.h"
#include "imgINotificationObserver.h"
#include "nsScrollbarFrame.h"
#include "nsThreadUtils.h"
#include "mozilla/LookAndFeel.h"
class nsFontMetrics;
class nsOverflowChecker;
class nsTreeImageListener;
namespace mozilla {
namespace layout {
class ScrollbarActivity;
} // namespace layout
} // namespace mozilla
// An entry in the tree's image cache
struct nsTreeImageCacheEntry
{
nsTreeImageCacheEntry() {}
nsTreeImageCacheEntry(imgIRequest *aRequest, imgINotificationObserver *aListener)
: request(aRequest), listener(aListener) {}
nsCOMPtr<imgIRequest> request;
nsCOMPtr<imgINotificationObserver> listener;
};
// The actual frame that paints the cells and rows.
class nsTreeBodyFrame final
: public nsLeafBoxFrame
, public nsICSSPseudoComparator
, public nsIScrollbarMediator
, public nsIReflowCallback
{
typedef mozilla::layout::ScrollbarActivity ScrollbarActivity;
typedef mozilla::image::DrawResult DrawResult;
public:
explicit nsTreeBodyFrame(nsStyleContext* aContext);
~nsTreeBodyFrame();
NS_DECL_QUERYFRAME_TARGET(nsTreeBodyFrame)
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
// Callback handler methods for refresh driver based animations.
// Calls to these functions are forwarded from nsTreeImageListener. These
// mirror how nsImageFrame works.
nsresult OnImageIsAnimated(imgIRequest* aRequest);
// non-virtual signatures like nsITreeBodyFrame
already_AddRefed<nsTreeColumns> Columns() const
{
RefPtr<nsTreeColumns> cols = mColumns;
return cols.forget();
}
already_AddRefed<nsITreeView> GetExistingView() const
{
nsCOMPtr<nsITreeView> view = mView;
return view.forget();
}
nsresult GetView(nsITreeView **aView);
nsresult SetView(nsITreeView *aView);
bool GetFocused() const { return mFocused; }
nsresult SetFocused(bool aFocused);
nsresult GetTreeBody(nsIDOMElement **aElement);
int32_t RowHeight() const;
int32_t RowWidth();
int32_t GetHorizontalPosition() const;
nsresult GetSelectionRegion(nsIScriptableRegion **aRegion);
int32_t FirstVisibleRow() const { return mTopRowIndex; }
int32_t LastVisibleRow() const { return mTopRowIndex + mPageLength; }
int32_t PageLength() const { return mPageLength; }
nsresult EnsureRowIsVisible(int32_t aRow);
nsresult EnsureCellIsVisible(int32_t aRow, nsITreeColumn *aCol);
nsresult ScrollToRow(int32_t aRow);
nsresult ScrollByLines(int32_t aNumLines);
nsresult ScrollByPages(int32_t aNumPages);
nsresult ScrollToCell(int32_t aRow, nsITreeColumn *aCol);
nsresult ScrollToColumn(nsITreeColumn *aCol);
nsresult ScrollToHorizontalPosition(int32_t aValue);
nsresult Invalidate();
nsresult InvalidateColumn(nsITreeColumn *aCol);
nsresult InvalidateRow(int32_t aRow);
nsresult InvalidateCell(int32_t aRow, nsITreeColumn *aCol);
nsresult InvalidateRange(int32_t aStart, int32_t aEnd);
nsresult InvalidateColumnRange(int32_t aStart, int32_t aEnd,
nsITreeColumn *aCol);
nsresult GetRowAt(int32_t aX, int32_t aY, int32_t *aValue);
nsresult GetCellAt(int32_t aX, int32_t aY, int32_t *aRow,
nsITreeColumn **aCol, nsACString &aChildElt);
nsresult GetCoordsForCellItem(int32_t aRow, nsITreeColumn *aCol,
const nsACString &aElt,
int32_t *aX, int32_t *aY,
int32_t *aWidth, int32_t *aHeight);
nsresult IsCellCropped(int32_t aRow, nsITreeColumn *aCol, bool *aResult);
nsresult RowCountChanged(int32_t aIndex, int32_t aCount);
nsresult BeginUpdateBatch();
nsresult EndUpdateBatch();
nsresult ClearStyleAndImageCaches();
void CancelImageRequests();
void ManageReflowCallback(const nsRect& aRect, nscoord aHorzWidth);
virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override;
virtual void SetXULBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect,
bool aRemoveOverflowArea = false) override;
// nsIReflowCallback
virtual bool ReflowFinished() override;
virtual void ReflowCallbackCanceled() override;
// nsICSSPseudoComparator
virtual bool PseudoMatches(nsCSSSelector* aSelector) override;
// nsIScrollbarMediator
virtual void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
nsIScrollbarMediator::ScrollSnapMode aSnap
= nsIScrollbarMediator::DISABLE_SNAP) override;
virtual void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
nsIScrollbarMediator::ScrollSnapMode aSnap
= nsIScrollbarMediator::DISABLE_SNAP) override;
virtual void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
nsIScrollbarMediator::ScrollSnapMode aSnap
= nsIScrollbarMediator::DISABLE_SNAP) override;
virtual void RepeatButtonScroll(nsScrollbarFrame* aScrollbar) override;
virtual void ThumbMoved(nsScrollbarFrame* aScrollbar,
nscoord aOldPos,
nscoord aNewPos) override;
virtual void ScrollbarReleased(nsScrollbarFrame* aScrollbar) override {}
virtual void VisibilityChanged(bool aVisible) override { Invalidate(); }
virtual nsIFrame* GetScrollbarBox(bool aVertical) override {
ScrollParts parts = GetScrollParts();
return aVertical ? parts.mVScrollbar : parts.mHScrollbar;
}
virtual void ScrollbarActivityStarted() const override;
virtual void ScrollbarActivityStopped() const override;
virtual bool IsScrollbarOnRight() const override {
return (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR);
}
virtual bool ShouldSuppressScrollbarRepaints() const override {
return false;
}
// Overridden from nsIFrame to cache our pres context.
virtual void Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
virtual nsresult GetCursor(const nsPoint& aPoint,
nsIFrame::Cursor& aCursor) override;
virtual nsresult HandleEvent(nsPresContext* aPresContext,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) override;
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
friend nsIFrame* NS_NewTreeBodyFrame(nsIPresShell* aPresShell);
friend class nsTreeColumn;
struct ScrollParts {
nsScrollbarFrame* mVScrollbar;
nsCOMPtr<nsIContent> mVScrollbarContent;
nsScrollbarFrame* mHScrollbar;
nsCOMPtr<nsIContent> mHScrollbarContent;
nsIFrame* mColumnsFrame;
nsIScrollableFrame* mColumnsScrollFrame;
};
DrawResult PaintTreeBody(nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect, nsPoint aPt);
nsITreeBoxObject* GetTreeBoxObject() const { return mTreeBoxObject; }
// Get the base element, <tree> or <select>
nsIContent* GetBaseElement();
bool GetVerticalOverflow() const { return mVerticalOverflow; }
bool GetHorizontalOverflow() const {return mHorizontalOverflow; }
protected:
friend class nsOverflowChecker;
// This method paints a specific column background of the tree.
DrawResult PaintColumn(nsTreeColumn* aColumn,
const nsRect& aColumnRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
// This method paints a single row in the tree.
DrawResult PaintRow(int32_t aRowIndex,
const nsRect& aRowRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsPoint aPt);
// This method paints a single separator in the tree.
DrawResult PaintSeparator(int32_t aRowIndex,
const nsRect& aSeparatorRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
// This method paints a specific cell in a given row of the tree.
DrawResult PaintCell(int32_t aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aCellRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nscoord& aCurrX,
nsPoint aPt);
// This method paints the twisty inside a cell in the primary column of an tree.
DrawResult PaintTwisty(int32_t aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aTwistyRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nscoord& aRemainingWidth,
nscoord& aCurrX);
// This method paints the image inside the cell of an tree.
DrawResult PaintImage(int32_t aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aImageRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nscoord& aRemainingWidth,
nscoord& aCurrX);
// This method paints the text string inside a particular cell of the tree.
DrawResult PaintText(int32_t aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aTextRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nscoord& aCurrX);
// This method paints the checkbox inside a particular cell of the tree.
DrawResult PaintCheckbox(int32_t aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aCheckboxRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
// This method paints the progress meter inside a particular cell of the tree.
DrawResult PaintProgressMeter(int32_t aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aProgressMeterRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
// This method paints a drop feedback of the tree.
DrawResult PaintDropFeedback(const nsRect& aDropFeedbackRect,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsPoint aPt);
// This method is called with a specific style context and rect to
// paint the background rect as if it were a full-blown frame.
DrawResult PaintBackgroundLayer(nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aRect,
const nsRect& aDirtyRect);
// An internal hit test. aX and aY are expected to be in twips in the
// coordinate system of this frame.
int32_t GetRowAt(nscoord aX, nscoord aY);
// Check for bidi characters in the text, and if there are any, ensure
// that the prescontext is in bidi mode.
void CheckTextForBidi(nsAutoString& aText);
void AdjustForCellText(nsAutoString& aText,
int32_t aRowIndex,
nsTreeColumn* aColumn,
nsRenderingContext& aRenderingContext,
nsFontMetrics& aFontMetrics,
nsRect& aTextRect);
// A helper used when hit testing.
nsIAtom* GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect,
int32_t aRowIndex, nsTreeColumn* aColumn);
// An internal hit test. aX and aY are expected to be in twips in the
// coordinate system of this frame.
void GetCellAt(nscoord aX, nscoord aY, int32_t* aRow, nsTreeColumn** aCol,
nsIAtom** aChildElt);
// Retrieve the area for the twisty for a cell.
nsITheme* GetTwistyRect(int32_t aRowIndex,
nsTreeColumn* aColumn,
nsRect& aImageRect,
nsRect& aTwistyRect,
nsPresContext* aPresContext,
nsStyleContext* aTwistyContext);
// Fetch an image from the image cache.
nsresult GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
nsStyleContext* aStyleContext, bool& aAllowImageRegions, imgIContainer** aResult);
// Returns the size of a given image. This size *includes* border and
// padding. It does not include margins.
nsRect GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext, nsStyleContext* aStyleContext);
// Returns the destination size of the image, not including borders and padding.
nsSize GetImageDestSize(nsStyleContext* aStyleContext, bool useImageRegion, imgIContainer* image);
// Returns the source rectangle of the image to be displayed.
nsRect GetImageSourceRect(nsStyleContext* aStyleContext, bool useImageRegion, imgIContainer* image);
// Returns the height of rows in the tree.
int32_t GetRowHeight();
// Returns our indentation width.
int32_t GetIndentation();
// Calculates our width/height once border and padding have been removed.
void CalcInnerBox();
// Calculate the total width of our scrollable portion
nscoord CalcHorzWidth(const ScrollParts& aParts);
// Looks up a style context in the style cache. On a cache miss we resolve
// the pseudo-styles passed in and place them into the cache.
nsStyleContext* GetPseudoStyleContext(nsIAtom* aPseudoElement);
// Retrieves the scrollbars and scrollview relevant to this treebody. We
// traverse the frame tree under our base element, in frame order, looking
// for the first relevant vertical scrollbar, horizontal scrollbar, and
// scrollable frame (with associated content and scrollable view). These
// are all volatile and should not be retained.
ScrollParts GetScrollParts();
// Update the curpos of the scrollbar.
void UpdateScrollbars(const ScrollParts& aParts);
// Update the maxpos of the scrollbar.
void InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame);
// Check overflow and generate events.
void CheckOverflow(const ScrollParts& aParts);
// Calls UpdateScrollbars, Invalidate aNeedsFullInvalidation if true,
// InvalidateScrollbars and finally CheckOverflow.
// returns true if the frame is still alive after the method call.
bool FullScrollbarsUpdate(bool aNeedsFullInvalidation);
// Use to auto-fill some of the common properties without the view having to do it.
// Examples include container, open, selected, and focus.
void PrefillPropertyArray(int32_t aRowIndex, nsTreeColumn* aCol);
// Our internal scroll method, used by all the public scroll methods.
nsresult ScrollInternal(const ScrollParts& aParts, int32_t aRow);
nsresult ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow);
nsresult ScrollToColumnInternal(const ScrollParts& aParts, nsITreeColumn* aCol);
nsresult ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition);
nsresult EnsureRowIsVisibleInternal(const ScrollParts& aParts, int32_t aRow);
// Convert client pixels into appunits in our coordinate space.
nsPoint AdjustClientCoordsToBoxCoordSpace(int32_t aX, int32_t aY);
// Cache the box object
void EnsureBoxObject();
void EnsureView();
nsresult GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
nsRenderingContext* aRenderingContext,
nscoord& aDesiredSize, nscoord& aCurrentSize);
nscoord CalcMaxRowWidth();
// Translate the given rect horizontally from tree coordinates into the
// coordinate system of our nsTreeBodyFrame. If clip is true, then clip the
// rect to its intersection with mInnerBox in the horizontal direction.
// Return whether the result has a nonempty intersection with mInnerBox
// after projecting both onto the horizontal coordinate axis.
bool OffsetForHorzScroll(nsRect& rect, bool clip);
bool CanAutoScroll(int32_t aRowIndex);
// Calc the row and above/below/on status given where the mouse currently is hovering.
// Also calc if we're in the region in which we want to auto-scroll the tree.
// A positive value of |aScrollLines| means scroll down, a negative value
// means scroll up, a zero value means that we aren't in drag scroll region.
void ComputeDropPosition(mozilla::WidgetGUIEvent* aEvent,
int32_t* aRow,
int16_t* aOrient,
int16_t* aScrollLines);
// Mark ourselves dirty if we're a select widget
void MarkDirtyIfSelect();
void InvalidateDropFeedback(int32_t aRow, int16_t aOrientation) {
InvalidateRow(aRow);
if (aOrientation != nsITreeView::DROP_ON)
InvalidateRow(aRow + aOrientation);
}
public:
static
already_AddRefed<nsTreeColumn> GetColumnImpl(nsITreeColumn* aUnknownCol) {
if (!aUnknownCol)
return nullptr;
nsCOMPtr<nsTreeColumn> col = do_QueryInterface(aUnknownCol);
return col.forget();
}
/**
* Remove an nsITreeImageListener from being tracked by this frame. Only tree
* image listeners that are created by this frame are tracked.
*
* @param aListener A pointer to an nsTreeImageListener to no longer
* track.
*/
void RemoveTreeImageListener(nsTreeImageListener* aListener);
protected:
// Create a new timer. This method is used to delay various actions like
// opening/closing folders or tree scrolling.
// aID is type of the action, aFunc is the function to be called when
// the timer fires and aType is type of timer - one shot or repeating.
nsresult CreateTimer(const mozilla::LookAndFeel::IntID aID,
nsTimerCallbackFunc aFunc, int32_t aType,
nsITimer** aTimer);
static void OpenCallback(nsITimer *aTimer, void *aClosure);
static void CloseCallback(nsITimer *aTimer, void *aClosure);
static void LazyScrollCallback(nsITimer *aTimer, void *aClosure);
static void ScrollCallback(nsITimer *aTimer, void *aClosure);
class ScrollEvent : public mozilla::Runnable {
public:
NS_DECL_NSIRUNNABLE
explicit ScrollEvent(nsTreeBodyFrame *aInner) : mInner(aInner) {}
void Revoke() { mInner = nullptr; }
private:
nsTreeBodyFrame* mInner;
};
void PostScrollEvent();
void FireScrollEvent();
/**
* Clear the pointer to this frame for all nsTreeImageListeners that were
* created by this frame.
*/
void DetachImageListeners();
#ifdef ACCESSIBILITY
/**
* Fires 'treeRowCountChanged' event asynchronously. The event supports
* nsIDOMCustomEvent interface that is used to expose the following
* information structures.
*
* @param aIndex the row index rows are added/removed from
* @param aCount the number of added/removed rows (the sign points to
* an operation, plus - addition, minus - removing)
*/
void FireRowCountChangedEvent(int32_t aIndex, int32_t aCount);
/**
* Fires 'treeInvalidated' event asynchronously. The event supports
* nsIDOMCustomEvent interface that is used to expose the information
* structures described by method arguments.
*
* @param aStartRow the start index of invalidated rows, -1 means that
* columns have been invalidated only
* @param aEndRow the end index of invalidated rows, -1 means that columns
* have been invalidated only
* @param aStartCol the start invalidated column, nullptr means that only rows
* have been invalidated
* @param aEndCol the end invalidated column, nullptr means that rows have
* been invalidated only
*/
void FireInvalidateEvent(int32_t aStartRow, int32_t aEndRow,
nsITreeColumn *aStartCol, nsITreeColumn *aEndCol);
#endif
protected: // Data Members
class Slots {
public:
Slots() {
}
~Slots() {
if (mTimer)
mTimer->Cancel();
}
friend class nsTreeBodyFrame;
protected:
// If the drop is actually allowed here or not.
bool mDropAllowed;
// True while dragging over the tree.
bool mIsDragging;
// The row the mouse is hovering over during a drop.
int32_t mDropRow;
// Where we want to draw feedback (above/on this row/below) if allowed.
int16_t mDropOrient;
// Number of lines to be scrolled.
int16_t mScrollLines;
// The drag action that was received for this slot
uint32_t mDragAction;
// Timer for opening/closing spring loaded folders or scrolling the tree.
nsCOMPtr<nsITimer> mTimer;
// An array used to keep track of all spring loaded folders.
nsTArray<int32_t> mArray;
};
Slots* mSlots;
nsRevocableEventPtr<ScrollEvent> mScrollEvent;
RefPtr<ScrollbarActivity> mScrollbarActivity;
// The cached box object parent.
nsCOMPtr<nsITreeBoxObject> mTreeBoxObject;
// Cached column information.
RefPtr<nsTreeColumns> mColumns;
// The current view for this tree widget. We get all of our row and cell data
// from the view.
nsCOMPtr<nsITreeView> mView;
// A cache of all the style contexts we have seen for rows and cells of the tree. This is a mapping from
// a list of atoms to a corresponding style context. This cache stores every combination that
// occurs in the tree, so for n distinct properties, this cache could have 2 to the n entries
// (the power set of all row properties).
nsTreeStyleCache mStyleCache;
// A hashtable that maps from URLs to image request/listener pairs. The URL
// is provided by the view or by the style context. The style context
// represents a resolved :-moz-tree-cell-image (or twisty) pseudo-element.
// It maps directly to an imgIRequest.
nsDataHashtable<nsStringHashKey, nsTreeImageCacheEntry> mImageCache;
// A scratch array used when looking up cached style contexts.
AtomArray mScratchArray;
// The index of the first visible row and the # of rows visible onscreen.
// The tree only examines onscreen rows, starting from
// this index and going up to index+pageLength.
int32_t mTopRowIndex;
int32_t mPageLength;
// The horizontal scroll position
nscoord mHorzPosition;
// The original desired horizontal width before changing it and posting a
// reflow callback. In some cases, the desired horizontal width can first be
// different from the current desired horizontal width, only to return to
// the same value later during the same reflow. In this case, we can cancel
// the posted reflow callback and prevent an unnecessary reflow.
nscoord mOriginalHorzWidth;
// Our desired horizontal width (the width for which we actually have tree
// columns).
nscoord mHorzWidth;
// The amount by which to adjust the width of the last cell.
// This depends on whether or not the columnpicker and scrollbars are present.
nscoord mAdjustWidth;
// Cached heights and indent info.
nsRect mInnerBox; // 4-byte aligned
int32_t mRowHeight;
int32_t mIndentation;
nscoord mStringWidth;
int32_t mUpdateBatchNest;
// Cached row count.
int32_t mRowCount;
// The row the mouse is hovering over.
int32_t mMouseOverRow;
// Whether or not we're currently focused.
bool mFocused;
// Do we have a fixed number of onscreen rows?
bool mHasFixedRowCount;
bool mVerticalOverflow;
bool mHorizontalOverflow;
bool mReflowCallbackPosted;
// Set while we flush layout to take account of effects of
// overflow/underflow event handlers
bool mCheckingOverflow;
// Hash table to keep track of which listeners we created and thus
// have pointers to us.
nsTHashtable<nsPtrHashKey<nsTreeImageListener> > mCreatedListeners;
}; // class nsTreeBodyFrame
#endif

View file

@ -0,0 +1,201 @@
/* -*- 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 "nsCOMPtr.h"
#include "nsTreeColFrame.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
#include "nsStyleContext.h"
#include "nsNameSpaceManager.h"
#include "nsIBoxObject.h"
#include "mozilla/dom/TreeBoxObject.h"
#include "nsIDOMElement.h"
#include "nsITreeColumns.h"
#include "nsIDOMXULTreeElement.h"
#include "nsDisplayList.h"
#include "nsTreeBodyFrame.h"
//
// NS_NewTreeColFrame
//
// Creates a new col frame
//
nsIFrame*
NS_NewTreeColFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsTreeColFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsTreeColFrame)
// Destructor
nsTreeColFrame::~nsTreeColFrame()
{
}
void
nsTreeColFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
InvalidateColumns();
}
void
nsTreeColFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
InvalidateColumns(false);
nsBoxFrame::DestroyFrom(aDestructRoot);
}
class nsDisplayXULTreeColSplitterTarget : public nsDisplayItem {
public:
nsDisplayXULTreeColSplitterTarget(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame) :
nsDisplayItem(aBuilder, aFrame) {
MOZ_COUNT_CTOR(nsDisplayXULTreeColSplitterTarget);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayXULTreeColSplitterTarget() {
MOZ_COUNT_DTOR(nsDisplayXULTreeColSplitterTarget);
}
#endif
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState,
nsTArray<nsIFrame*> *aOutFrames) override;
NS_DISPLAY_DECL_NAME("XULTreeColSplitterTarget", TYPE_XUL_TREE_COL_SPLITTER_TARGET)
};
void
nsDisplayXULTreeColSplitterTarget::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
{
nsRect rect = aRect - ToReferenceFrame();
// If we are in either in the first 4 pixels or the last 4 pixels, we're going to
// do something really strange. Check for an adjacent splitter.
bool left = false;
bool right = false;
if (mFrame->GetSize().width - nsPresContext::CSSPixelsToAppUnits(4) <= rect.XMost()) {
right = true;
} else if (nsPresContext::CSSPixelsToAppUnits(4) > rect.x) {
left = true;
}
// Swap left and right for RTL trees in order to find the correct splitter
if (mFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
bool tmp = left;
left = right;
right = tmp;
}
if (left || right) {
// We are a header. Look for the correct splitter.
nsIFrame* child;
if (left)
child = mFrame->GetPrevSibling();
else
child = mFrame->GetNextSibling();
if (child && child->GetContent()->NodeInfo()->Equals(nsGkAtoms::splitter,
kNameSpaceID_XUL)) {
aOutFrames->AppendElement(child);
}
}
}
void
nsTreeColFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
if (!aBuilder->IsForEventDelivery()) {
nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists);
return;
}
nsDisplayListCollection set;
nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, set);
WrapListsInRedirector(aBuilder, set, aLists);
aLists.Content()->AppendNewToTop(new (aBuilder)
nsDisplayXULTreeColSplitterTarget(aBuilder, this));
}
nsresult
nsTreeColFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
if (aAttribute == nsGkAtoms::ordinal || aAttribute == nsGkAtoms::primary) {
InvalidateColumns();
}
return rv;
}
void
nsTreeColFrame::SetXULBounds(nsBoxLayoutState& aBoxLayoutState,
const nsRect& aRect,
bool aRemoveOverflowArea)
{
nscoord oldWidth = mRect.width;
nsBoxFrame::SetXULBounds(aBoxLayoutState, aRect, aRemoveOverflowArea);
if (mRect.width != oldWidth) {
nsITreeBoxObject* treeBoxObject = GetTreeBoxObject();
if (treeBoxObject) {
treeBoxObject->Invalidate();
}
}
}
nsITreeBoxObject*
nsTreeColFrame::GetTreeBoxObject()
{
nsITreeBoxObject* result = nullptr;
nsIContent* parent = mContent->GetParent();
if (parent) {
nsIContent* grandParent = parent->GetParent();
nsCOMPtr<nsIDOMXULElement> treeElement = do_QueryInterface(grandParent);
if (treeElement) {
nsCOMPtr<nsIBoxObject> boxObject;
treeElement->GetBoxObject(getter_AddRefs(boxObject));
nsCOMPtr<nsITreeBoxObject> treeBoxObject = do_QueryInterface(boxObject);
result = treeBoxObject.get();
}
}
return result;
}
void
nsTreeColFrame::InvalidateColumns(bool aCanWalkFrameTree)
{
nsITreeBoxObject* treeBoxObject = GetTreeBoxObject();
if (treeBoxObject) {
nsCOMPtr<nsITreeColumns> columns;
if (aCanWalkFrameTree) {
treeBoxObject->GetColumns(getter_AddRefs(columns));
} else {
nsTreeBodyFrame* body = static_cast<mozilla::dom::TreeBoxObject*>
(treeBoxObject)->GetCachedTreeBodyFrame();
if (body) {
columns = body->Columns();
}
}
if (columns)
columns->InvalidateColumns();
}
}

View file

@ -0,0 +1,55 @@
/* -*- 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 "mozilla/Attributes.h"
#include "nsBoxFrame.h"
class nsITreeBoxObject;
nsIFrame* NS_NewTreeColFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext);
class nsTreeColFrame : public nsBoxFrame
{
public:
NS_DECL_FRAMEARENA_HELPERS
explicit nsTreeColFrame(nsStyleContext* aContext):
nsBoxFrame(aContext) {}
virtual void Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
virtual void BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType) override;
virtual void SetXULBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect,
bool aRemoveOverflowArea = false) override;
friend nsIFrame* NS_NewTreeColFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext);
protected:
virtual ~nsTreeColFrame();
/**
* @return the tree box object of the tree this column belongs to, or nullptr.
*/
nsITreeBoxObject* GetTreeBoxObject();
/**
* Helper method that gets the nsITreeColumns object this column belongs to
* and calls InvalidateColumns() on it.
*/
void InvalidateColumns(bool aCanWalkFrameTree = true);
};

View file

@ -0,0 +1,765 @@
/* -*- 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 "nsNameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsIDOMElement.h"
#include "nsIBoxObject.h"
#include "nsTreeColumns.h"
#include "nsTreeUtils.h"
#include "nsStyleContext.h"
#include "nsDOMClassInfoID.h"
#include "nsContentUtils.h"
#include "nsTreeBodyFrame.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TreeBoxObject.h"
#include "mozilla/dom/TreeColumnBinding.h"
#include "mozilla/dom/TreeColumnsBinding.h"
using namespace mozilla;
// Column class that caches all the info about our column.
nsTreeColumn::nsTreeColumn(nsTreeColumns* aColumns, nsIContent* aContent)
: mContent(aContent),
mColumns(aColumns),
mPrevious(nullptr)
{
NS_ASSERTION(aContent &&
aContent->NodeInfo()->Equals(nsGkAtoms::treecol,
kNameSpaceID_XUL),
"nsTreeColumn's content must be a <xul:treecol>");
Invalidate();
}
nsTreeColumn::~nsTreeColumn()
{
if (mNext) {
mNext->SetPrevious(nullptr);
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsTreeColumn)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTreeColumn)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
if (tmp->mNext) {
tmp->mNext->SetPrevious(nullptr);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNext)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTreeColumn)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsTreeColumn)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumn)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumn)
// QueryInterface implementation for nsTreeColumn
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumn)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsITreeColumn)
NS_INTERFACE_MAP_ENTRY(nsISupports)
if (aIID.Equals(NS_GET_IID(nsTreeColumn))) {
AddRef();
*aInstancePtr = this;
return NS_OK;
}
else
NS_INTERFACE_MAP_END
nsIFrame*
nsTreeColumn::GetFrame()
{
NS_ENSURE_TRUE(mContent, nullptr);
return mContent->GetPrimaryFrame();
}
bool
nsTreeColumn::IsLastVisible(nsTreeBodyFrame* aBodyFrame)
{
NS_ASSERTION(GetFrame(), "should have checked for this already");
// cyclers are fixed width, don't adjust them
if (IsCycler())
return false;
// we're certainly not the last visible if we're not visible
if (GetFrame()->GetRect().width == 0)
return false;
// try to find a visible successor
for (nsTreeColumn *next = GetNext(); next; next = next->GetNext()) {
nsIFrame* frame = next->GetFrame();
if (frame && frame->GetRect().width > 0)
return false;
}
return true;
}
nsresult
nsTreeColumn::GetRect(nsTreeBodyFrame* aBodyFrame, nscoord aY, nscoord aHeight, nsRect* aResult)
{
nsIFrame* frame = GetFrame();
if (!frame) {
*aResult = nsRect();
return NS_ERROR_FAILURE;
}
bool isRTL = aBodyFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
*aResult = frame->GetRect();
aResult->y = aY;
aResult->height = aHeight;
if (isRTL)
aResult->x += aBodyFrame->mAdjustWidth;
else if (IsLastVisible(aBodyFrame))
aResult->width += aBodyFrame->mAdjustWidth;
return NS_OK;
}
nsresult
nsTreeColumn::GetXInTwips(nsTreeBodyFrame* aBodyFrame, nscoord* aResult)
{
nsIFrame* frame = GetFrame();
if (!frame) {
*aResult = 0;
return NS_ERROR_FAILURE;
}
*aResult = frame->GetRect().x;
return NS_OK;
}
nsresult
nsTreeColumn::GetWidthInTwips(nsTreeBodyFrame* aBodyFrame, nscoord* aResult)
{
nsIFrame* frame = GetFrame();
if (!frame) {
*aResult = 0;
return NS_ERROR_FAILURE;
}
*aResult = frame->GetRect().width;
if (IsLastVisible(aBodyFrame))
*aResult += aBodyFrame->mAdjustWidth;
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetElement(nsIDOMElement** aElement)
{
if (mContent) {
return CallQueryInterface(mContent, aElement);
}
*aElement = nullptr;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsTreeColumn::GetColumns(nsITreeColumns** aColumns)
{
NS_IF_ADDREF(*aColumns = mColumns);
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetX(int32_t* aX)
{
nsIFrame* frame = GetFrame();
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
*aX = nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().x);
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetWidth(int32_t* aWidth)
{
nsIFrame* frame = GetFrame();
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
*aWidth = nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().width);
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetId(nsAString& aId)
{
aId = GetId();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetIdConst(const char16_t** aIdConst)
{
*aIdConst = mId.get();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetAtom(nsIAtom** aAtom)
{
NS_IF_ADDREF(*aAtom = GetAtom());
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetIndex(int32_t* aIndex)
{
*aIndex = GetIndex();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetPrimary(bool* aPrimary)
{
*aPrimary = IsPrimary();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetCycler(bool* aCycler)
{
*aCycler = IsCycler();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetEditable(bool* aEditable)
{
*aEditable = IsEditable();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetSelectable(bool* aSelectable)
{
*aSelectable = IsSelectable();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetType(int16_t* aType)
{
*aType = GetType();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetNext(nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetNext());
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::GetPrevious(nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetPrevious());
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumn::Invalidate()
{
nsIFrame* frame = GetFrame();
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
// Fetch the Id.
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mId);
// If we have an Id, cache the Id as an atom.
if (!mId.IsEmpty()) {
mAtom = NS_Atomize(mId);
}
// Cache our index.
nsTreeUtils::GetColumnIndex(mContent, &mIndex);
const nsStyleVisibility* vis = frame->StyleVisibility();
// Cache our text alignment policy.
const nsStyleText* textStyle = frame->StyleText();
mTextAlignment = textStyle->mTextAlign;
// START or END alignment sometimes means RIGHT
if ((mTextAlignment == NS_STYLE_TEXT_ALIGN_START &&
vis->mDirection == NS_STYLE_DIRECTION_RTL) ||
(mTextAlignment == NS_STYLE_TEXT_ALIGN_END &&
vis->mDirection == NS_STYLE_DIRECTION_LTR)) {
mTextAlignment = NS_STYLE_TEXT_ALIGN_RIGHT;
} else if (mTextAlignment == NS_STYLE_TEXT_ALIGN_START ||
mTextAlignment == NS_STYLE_TEXT_ALIGN_END) {
mTextAlignment = NS_STYLE_TEXT_ALIGN_LEFT;
}
// Figure out if we're the primary column (that has to have indentation
// and twisties drawn.
mIsPrimary = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
nsGkAtoms::_true, eCaseMatters);
// Figure out if we're a cycling column (one that doesn't cause a selection
// to happen).
mIsCycler = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::cycler,
nsGkAtoms::_true, eCaseMatters);
mIsEditable = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
nsGkAtoms::_true, eCaseMatters);
mIsSelectable = !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::selectable,
nsGkAtoms::_false, eCaseMatters);
mOverflow = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::overflow,
nsGkAtoms::_true, eCaseMatters);
// Figure out our column type. Default type is text.
mType = nsITreeColumn::TYPE_TEXT;
static nsIContent::AttrValuesArray typestrings[] =
{&nsGkAtoms::checkbox, &nsGkAtoms::progressmeter, &nsGkAtoms::password,
nullptr};
switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
typestrings, eCaseMatters)) {
case 0: mType = nsITreeColumn::TYPE_CHECKBOX; break;
case 1: mType = nsITreeColumn::TYPE_PROGRESSMETER; break;
case 2: mType = nsITreeColumn::TYPE_PASSWORD; break;
}
// Fetch the crop style.
mCropStyle = 0;
static nsIContent::AttrValuesArray cropstrings[] =
{&nsGkAtoms::center, &nsGkAtoms::left, &nsGkAtoms::start, nullptr};
switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::crop,
cropstrings, eCaseMatters)) {
case 0:
mCropStyle = 1;
break;
case 1:
case 2:
mCropStyle = 2;
break;
}
return NS_OK;
}
nsIContent*
nsTreeColumn::GetParentObject() const
{
return mContent;
}
/* virtual */ JSObject*
nsTreeColumn::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return dom::TreeColumnBinding::Wrap(aCx, this, aGivenProto);
}
mozilla::dom::Element*
nsTreeColumn::GetElement(mozilla::ErrorResult& aRv)
{
nsCOMPtr<nsIDOMElement> element;
aRv = GetElement(getter_AddRefs(element));
if (aRv.Failed()) {
return nullptr;
}
nsCOMPtr<nsINode> node = do_QueryInterface(element);
return node->AsElement();
}
int32_t
nsTreeColumn::GetX(mozilla::ErrorResult& aRv)
{
int32_t x;
aRv = GetX(&x);
return x;
}
int32_t
nsTreeColumn::GetWidth(mozilla::ErrorResult& aRv)
{
int32_t width;
aRv = GetWidth(&width);
return width;
}
void
nsTreeColumn::Invalidate(mozilla::ErrorResult& aRv)
{
aRv = Invalidate();
}
nsTreeColumns::nsTreeColumns(nsTreeBodyFrame* aTree)
: mTree(aTree)
{
}
nsTreeColumns::~nsTreeColumns()
{
nsTreeColumns::InvalidateColumns();
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsTreeColumns)
// QueryInterface implementation for nsTreeColumns
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumns)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsITreeColumns)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumns)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumns)
nsIContent*
nsTreeColumns::GetParentObject() const
{
return mTree ? mTree->GetBaseElement() : nullptr;
}
/* virtual */ JSObject*
nsTreeColumns::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return dom::TreeColumnsBinding::Wrap(aCx, this, aGivenProto);
}
dom::TreeBoxObject*
nsTreeColumns::GetTree() const
{
return mTree ? static_cast<mozilla::dom::TreeBoxObject*>(mTree->GetTreeBoxObject()) : nullptr;
}
NS_IMETHODIMP
nsTreeColumns::GetTree(nsITreeBoxObject** _retval)
{
NS_IF_ADDREF(*_retval = GetTree());
return NS_OK;
}
uint32_t
nsTreeColumns::Count()
{
EnsureColumns();
uint32_t count = 0;
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
++count;
}
return count;
}
NS_IMETHODIMP
nsTreeColumns::GetCount(int32_t* _retval)
{
*_retval = Count();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumns::GetLength(int32_t* _retval)
{
*_retval = Length();
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumns::GetFirstColumn(nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetFirstColumn());
return NS_OK;
}
nsTreeColumn*
nsTreeColumns::GetLastColumn()
{
EnsureColumns();
nsTreeColumn* currCol = mFirstColumn;
while (currCol) {
nsTreeColumn* next = currCol->GetNext();
if (!next) {
return currCol;
}
currCol = next;
}
return nullptr;
}
NS_IMETHODIMP
nsTreeColumns::GetLastColumn(nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetLastColumn());
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumns::GetPrimaryColumn(nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetPrimaryColumn());
return NS_OK;
}
nsTreeColumn*
nsTreeColumns::GetSortedColumn()
{
EnsureColumns();
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
if (currCol->mContent &&
nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
nsGkAtoms::sortDirection)) {
return currCol;
}
}
return nullptr;
}
NS_IMETHODIMP
nsTreeColumns::GetSortedColumn(nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetSortedColumn());
return NS_OK;
}
nsTreeColumn*
nsTreeColumns::GetKeyColumn()
{
EnsureColumns();
nsTreeColumn* first = nullptr;
nsTreeColumn* primary = nullptr;
nsTreeColumn* sorted = nullptr;
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
// Skip hidden columns.
if (!currCol->mContent ||
currCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
nsGkAtoms::_true, eCaseMatters))
continue;
// Skip non-text column
if (currCol->GetType() != nsITreeColumn::TYPE_TEXT)
continue;
if (!first)
first = currCol;
if (nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
nsGkAtoms::sortDirection)) {
// Use sorted column as the key.
sorted = currCol;
break;
}
if (currCol->IsPrimary())
if (!primary)
primary = currCol;
}
if (sorted)
return sorted;
if (primary)
return primary;
return first;
}
NS_IMETHODIMP
nsTreeColumns::GetKeyColumn(nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetKeyColumn());
return NS_OK;
}
nsTreeColumn*
nsTreeColumns::GetColumnFor(dom::Element* aElement)
{
EnsureColumns();
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
if (currCol->mContent == aElement) {
return currCol;
}
}
return nullptr;
}
NS_IMETHODIMP
nsTreeColumns::GetColumnFor(nsIDOMElement* aElement, nsITreeColumn** _retval)
{
nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
NS_ADDREF(*_retval = GetColumnFor(element));
return NS_OK;
}
nsTreeColumn*
nsTreeColumns::NamedGetter(const nsAString& aId, bool& aFound)
{
EnsureColumns();
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
if (currCol->GetId().Equals(aId)) {
aFound = true;
return currCol;
}
}
aFound = false;
return nullptr;
}
nsTreeColumn*
nsTreeColumns::GetNamedColumn(const nsAString& aId)
{
bool dummy;
return NamedGetter(aId, dummy);
}
NS_IMETHODIMP
nsTreeColumns::GetNamedColumn(const nsAString& aId, nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetNamedColumn(aId));
return NS_OK;
}
void
nsTreeColumns::GetSupportedNames(nsTArray<nsString>& aNames)
{
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
aNames.AppendElement(currCol->GetId());
}
}
nsTreeColumn*
nsTreeColumns::IndexedGetter(uint32_t aIndex, bool& aFound)
{
EnsureColumns();
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
if (currCol->GetIndex() == static_cast<int32_t>(aIndex)) {
aFound = true;
return currCol;
}
}
aFound = false;
return nullptr;
}
nsTreeColumn*
nsTreeColumns::GetColumnAt(uint32_t aIndex)
{
bool dummy;
return IndexedGetter(aIndex, dummy);
}
NS_IMETHODIMP
nsTreeColumns::GetColumnAt(int32_t aIndex, nsITreeColumn** _retval)
{
NS_IF_ADDREF(*_retval = GetColumnAt(static_cast<uint32_t>(aIndex)));
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumns::InvalidateColumns()
{
for (nsTreeColumn* currCol = mFirstColumn; currCol;
currCol = currCol->GetNext()) {
currCol->SetColumns(nullptr);
}
mFirstColumn = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsTreeColumns::RestoreNaturalOrder()
{
if (!mTree)
return NS_OK;
nsIContent* content = mTree->GetBaseElement();
// Strong ref, since we'll be setting attributes
nsCOMPtr<nsIContent> colsContent =
nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treecols);
if (!colsContent)
return NS_OK;
for (uint32_t i = 0; i < colsContent->GetChildCount(); ++i) {
nsCOMPtr<nsIContent> child = colsContent->GetChildAt(i);
nsAutoString ordinal;
ordinal.AppendInt(i);
child->SetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, ordinal, true);
}
nsTreeColumns::InvalidateColumns();
if (mTree) {
mTree->Invalidate();
}
return NS_OK;
}
nsTreeColumn*
nsTreeColumns::GetPrimaryColumn()
{
EnsureColumns();
for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) {
if (currCol->IsPrimary()) {
return currCol;
}
}
return nullptr;
}
void
nsTreeColumns::EnsureColumns()
{
if (mTree && !mFirstColumn) {
nsIContent* treeContent = mTree->GetBaseElement();
nsIContent* colsContent =
nsTreeUtils::GetDescendantChild(treeContent, nsGkAtoms::treecols);
if (!colsContent)
return;
nsIContent* colContent =
nsTreeUtils::GetDescendantChild(colsContent, nsGkAtoms::treecol);
if (!colContent)
return;
nsIFrame* colFrame = colContent->GetPrimaryFrame();
if (!colFrame)
return;
colFrame = colFrame->GetParent();
if (!colFrame)
return;
colFrame = colFrame->PrincipalChildList().FirstChild();
if (!colFrame)
return;
// Now that we have the first visible column,
// we can enumerate the columns in visible order
nsTreeColumn* currCol = nullptr;
while (colFrame) {
nsIContent* colContent = colFrame->GetContent();
if (colContent->NodeInfo()->Equals(nsGkAtoms::treecol,
kNameSpaceID_XUL)) {
// Create a new column structure.
nsTreeColumn* col = new nsTreeColumn(this, colContent);
if (!col)
return;
if (currCol) {
currCol->SetNext(col);
col->SetPrevious(currCol);
}
else {
mFirstColumn = col;
}
currCol = col;
}
colFrame = colFrame->GetNextSibling();
}
}
}

View file

@ -0,0 +1,215 @@
/* -*- 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/. */
#ifndef nsTreeColumns_h__
#define nsTreeColumns_h__
#include "nsITreeColumns.h"
#include "nsITreeBoxObject.h"
#include "mozilla/Attributes.h"
#include "nsCoord.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsString.h"
class nsTreeBodyFrame;
class nsTreeColumns;
class nsIFrame;
class nsIContent;
struct nsRect;
namespace mozilla {
class ErrorResult;
namespace dom {
class Element;
class TreeBoxObject;
} // namespace dom
} // namespace mozilla
#define NS_TREECOLUMN_IMPL_CID \
{ /* 02cd1963-4b5d-4a6c-9223-814d3ade93a3 */ \
0x02cd1963, \
0x4b5d, \
0x4a6c, \
{0x92, 0x23, 0x81, 0x4d, 0x3a, 0xde, 0x93, 0xa3} \
}
// This class is our column info. We use it to iterate our columns and to obtain
// information about each column.
class nsTreeColumn final : public nsITreeColumn
, public nsWrapperCache
{
public:
nsTreeColumn(nsTreeColumns* aColumns, nsIContent* aContent);
NS_DECLARE_STATIC_IID_ACCESSOR(NS_TREECOLUMN_IMPL_CID)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsTreeColumn)
NS_DECL_NSITREECOLUMN
// WebIDL
nsIContent* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
mozilla::dom::Element* GetElement(mozilla::ErrorResult& aRv);
nsTreeColumns* GetColumns() const { return mColumns; }
int32_t GetX(mozilla::ErrorResult& aRv);
int32_t GetWidth(mozilla::ErrorResult& aRv);
// GetId is fine
int32_t Index() const { return mIndex; }
bool Primary() const { return mIsPrimary; }
bool Cycler() const { return mIsCycler; }
bool Editable() const { return mIsEditable; }
bool Selectable() const { return mIsSelectable; }
int16_t Type() const { return mType; }
nsTreeColumn* GetNext() const { return mNext; }
nsTreeColumn* GetPrevious() const { return mPrevious; }
void Invalidate(mozilla::ErrorResult& aRv);
friend class nsTreeBodyFrame;
friend class nsTreeColumns;
protected:
~nsTreeColumn();
nsIFrame* GetFrame();
nsIFrame* GetFrame(nsTreeBodyFrame* aBodyFrame);
// Don't call this if GetWidthInTwips or GetRect fails
bool IsLastVisible(nsTreeBodyFrame* aBodyFrame);
/**
* Returns a rect with x and width taken from the frame's rect and specified
* y and height. May fail in case there's no frame for the column.
*/
nsresult GetRect(nsTreeBodyFrame* aBodyFrame, nscoord aY, nscoord aHeight,
nsRect* aResult);
nsresult GetXInTwips(nsTreeBodyFrame* aBodyFrame, nscoord* aResult);
nsresult GetWidthInTwips(nsTreeBodyFrame* aBodyFrame, nscoord* aResult);
void SetColumns(nsTreeColumns* aColumns) { mColumns = aColumns; }
const nsAString& GetId() { return mId; }
nsIAtom* GetAtom() { return mAtom; }
int32_t GetIndex() { return mIndex; }
bool IsPrimary() { return mIsPrimary; }
bool IsCycler() { return mIsCycler; }
bool IsEditable() { return mIsEditable; }
bool IsSelectable() { return mIsSelectable; }
bool Overflow() { return mOverflow; }
int16_t GetType() { return mType; }
int8_t GetCropStyle() { return mCropStyle; }
int32_t GetTextAlignment() { return mTextAlignment; }
void SetNext(nsTreeColumn* aNext) {
NS_ASSERTION(!mNext, "already have a next sibling");
mNext = aNext;
}
void SetPrevious(nsTreeColumn* aPrevious) { mPrevious = aPrevious; }
private:
/**
* Non-null nsIContent for the associated <treecol> element.
*/
nsCOMPtr<nsIContent> mContent;
nsTreeColumns* mColumns;
nsString mId;
nsCOMPtr<nsIAtom> mAtom;
int32_t mIndex;
bool mIsPrimary;
bool mIsCycler;
bool mIsEditable;
bool mIsSelectable;
bool mOverflow;
int16_t mType;
int8_t mCropStyle;
int8_t mTextAlignment;
RefPtr<nsTreeColumn> mNext;
nsTreeColumn* mPrevious;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsTreeColumn, NS_TREECOLUMN_IMPL_CID)
class nsTreeColumns final : public nsITreeColumns
, public nsWrapperCache
{
private:
~nsTreeColumns();
public:
explicit nsTreeColumns(nsTreeBodyFrame* aTree);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsTreeColumns)
NS_DECL_NSITREECOLUMNS
nsIContent* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// WebIDL
mozilla::dom::TreeBoxObject* GetTree() const;
uint32_t Count();
uint32_t Length()
{
return Count();
}
nsTreeColumn* GetFirstColumn() { EnsureColumns(); return mFirstColumn; }
nsTreeColumn* GetLastColumn();
nsTreeColumn* GetPrimaryColumn();
nsTreeColumn* GetSortedColumn();
nsTreeColumn* GetKeyColumn();
nsTreeColumn* GetColumnFor(mozilla::dom::Element* aElement);
nsTreeColumn* IndexedGetter(uint32_t aIndex, bool& aFound);
nsTreeColumn* GetColumnAt(uint32_t aIndex);
nsTreeColumn* NamedGetter(const nsAString& aId, bool& aFound);
nsTreeColumn* GetNamedColumn(const nsAString& aId);
void GetSupportedNames(nsTArray<nsString>& aNames);
// Uses XPCOM InvalidateColumns().
// Uses XPCOM RestoreNaturalOrder().
friend class nsTreeBodyFrame;
protected:
void SetTree(nsTreeBodyFrame* aTree) { mTree = aTree; }
// Builds our cache of column info.
void EnsureColumns();
private:
nsTreeBodyFrame* mTree;
/**
* The first column in the list of columns. All of the columns are supposed
* to be "alive", i.e. have a frame. This is achieved by clearing the columns
* list each time an nsTreeColFrame is destroyed.
*
* XXX this means that new nsTreeColumn objects are unnecessarily created
* for untouched columns.
*/
RefPtr<nsTreeColumn> mFirstColumn;
};
#endif // nsTreeColumns_h__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#ifndef nsTreeContentView_h__
#define nsTreeContentView_h__
#include "nsCycleCollectionParticipant.h"
#include "nsTArray.h"
#include "nsStubDocumentObserver.h"
#include "nsITreeBoxObject.h"
#include "nsITreeColumns.h"
#include "nsITreeView.h"
#include "nsITreeContentView.h"
#include "nsITreeSelection.h"
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
class nsIDocument;
class Row;
nsresult NS_NewTreeContentView(nsITreeView** aResult);
class nsTreeContentView final : public nsINativeTreeView,
public nsITreeContentView,
public nsStubDocumentObserver
{
public:
nsTreeContentView(void);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTreeContentView,
nsINativeTreeView)
NS_DECL_NSITREEVIEW
// nsINativeTreeView: Untrusted code can use us
NS_IMETHOD EnsureNative() override { return NS_OK; }
NS_DECL_NSITREECONTENTVIEW
// nsIDocumentObserver
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
static bool CanTrustTreeSelection(nsISupports* aValue);
protected:
~nsTreeContentView(void);
// Recursive methods which deal with serializing of nested content.
void Serialize(nsIContent* aContent, int32_t aParentIndex, int32_t* aIndex,
nsTArray<mozilla::UniquePtr<Row>>& aRows);
void SerializeItem(nsIContent* aContent, int32_t aParentIndex,
int32_t* aIndex, nsTArray<mozilla::UniquePtr<Row>>& aRows);
void SerializeSeparator(nsIContent* aContent, int32_t aParentIndex,
int32_t* aIndex, nsTArray<mozilla::UniquePtr<Row>>& aRows);
void GetIndexInSubtree(nsIContent* aContainer, nsIContent* aContent, int32_t* aResult);
// Helper methods which we use to manage our plain array of rows.
int32_t EnsureSubtree(int32_t aIndex);
int32_t RemoveSubtree(int32_t aIndex);
int32_t InsertRow(int32_t aParentIndex, int32_t aIndex, nsIContent* aContent);
void InsertRowFor(nsIContent* aParent, nsIContent* aChild);
int32_t RemoveRow(int32_t aIndex);
void ClearRows();
void OpenContainer(int32_t aIndex);
void CloseContainer(int32_t aIndex);
int32_t FindContent(nsIContent* aContent);
void UpdateSubtreeSizes(int32_t aIndex, int32_t aCount);
void UpdateParentIndexes(int32_t aIndex, int32_t aSkip, int32_t aCount);
// Content helpers.
nsIContent* GetCell(nsIContent* aContainer, nsITreeColumn* aCol);
private:
nsCOMPtr<nsITreeBoxObject> mBoxObject;
nsCOMPtr<nsITreeSelection> mSelection;
nsCOMPtr<nsIContent> mRoot;
nsCOMPtr<nsIContent> mBody;
nsIDocument* mDocument; // WEAK
nsTArray<mozilla::UniquePtr<Row>> mRows;
};
#endif // nsTreeContentView_h__

View file

@ -0,0 +1,115 @@
/* -*- 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 "nsTreeImageListener.h"
#include "nsITreeBoxObject.h"
#include "imgIRequest.h"
#include "imgIContainer.h"
#include "nsIContent.h"
NS_IMPL_ISUPPORTS(nsTreeImageListener, imgINotificationObserver)
nsTreeImageListener::nsTreeImageListener(nsTreeBodyFrame* aTreeFrame)
: mTreeFrame(aTreeFrame),
mInvalidationSuppressed(true),
mInvalidationArea(nullptr)
{
}
nsTreeImageListener::~nsTreeImageListener()
{
delete mInvalidationArea;
}
NS_IMETHODIMP
nsTreeImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
{
if (aType == imgINotificationObserver::IS_ANIMATED) {
return mTreeFrame ? mTreeFrame->OnImageIsAnimated(aRequest) : NS_OK;
}
if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
// Ensure the animation (if any) is started. Note: There is no
// corresponding call to Decrement for this. This Increment will be
// 'cleaned up' by the Request when it is destroyed, but only then.
aRequest->IncrementAnimationConsumers();
}
if (aType == imgINotificationObserver::FRAME_UPDATE) {
Invalidate();
}
return NS_OK;
}
void
nsTreeImageListener::AddCell(int32_t aIndex, nsITreeColumn* aCol)
{
if (!mInvalidationArea) {
mInvalidationArea = new InvalidationArea(aCol);
mInvalidationArea->AddRow(aIndex);
}
else {
InvalidationArea* currArea;
for (currArea = mInvalidationArea; currArea; currArea = currArea->GetNext()) {
if (currArea->GetCol() == aCol) {
currArea->AddRow(aIndex);
break;
}
}
if (!currArea) {
currArea = new InvalidationArea(aCol);
currArea->SetNext(mInvalidationArea);
mInvalidationArea = currArea;
mInvalidationArea->AddRow(aIndex);
}
}
}
void
nsTreeImageListener::Invalidate()
{
if (!mInvalidationSuppressed) {
for (InvalidationArea* currArea = mInvalidationArea; currArea;
currArea = currArea->GetNext()) {
// Loop from min to max, invalidating each cell that was listening for this image.
for (int32_t i = currArea->GetMin(); i <= currArea->GetMax(); ++i) {
if (mTreeFrame) {
nsITreeBoxObject* tree = mTreeFrame->GetTreeBoxObject();
if (tree) {
tree->InvalidateCell(i, currArea->GetCol());
}
}
}
}
}
}
nsTreeImageListener::InvalidationArea::InvalidationArea(nsITreeColumn* aCol)
: mCol(aCol),
mMin(-1), // min should start out "undefined"
mMax(0),
mNext(nullptr)
{
}
void
nsTreeImageListener::InvalidationArea::AddRow(int32_t aIndex)
{
if (mMin == -1)
mMin = mMax = aIndex;
else if (aIndex < mMin)
mMin = aIndex;
else if (aIndex > mMax)
mMax = aIndex;
}
NS_IMETHODIMP
nsTreeImageListener::ClearFrame()
{
mTreeFrame = nullptr;
return NS_OK;
}

View file

@ -0,0 +1,66 @@
/* -*- 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/. */
#ifndef nsTreeImageListener_h__
#define nsTreeImageListener_h__
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsITreeColumns.h"
#include "nsTreeBodyFrame.h"
#include "mozilla/Attributes.h"
// This class handles image load observation.
class nsTreeImageListener final : public imgINotificationObserver
{
public:
explicit nsTreeImageListener(nsTreeBodyFrame *aTreeFrame);
NS_DECL_ISUPPORTS
NS_DECL_IMGINOTIFICATIONOBSERVER
NS_IMETHOD ClearFrame();
friend class nsTreeBodyFrame;
protected:
~nsTreeImageListener();
void UnsuppressInvalidation() { mInvalidationSuppressed = false; }
void Invalidate();
void AddCell(int32_t aIndex, nsITreeColumn* aCol);
private:
nsTreeBodyFrame* mTreeFrame;
// A guard that prevents us from recursive painting.
bool mInvalidationSuppressed;
class InvalidationArea {
public:
explicit InvalidationArea(nsITreeColumn* aCol);
~InvalidationArea() { delete mNext; }
friend class nsTreeImageListener;
protected:
void AddRow(int32_t aIndex);
nsITreeColumn* GetCol() { return mCol.get(); }
int32_t GetMin() { return mMin; }
int32_t GetMax() { return mMax; }
InvalidationArea* GetNext() { return mNext; }
void SetNext(InvalidationArea* aNext) { mNext = aNext; }
private:
nsCOMPtr<nsITreeColumn> mCol;
int32_t mMin;
int32_t mMax;
InvalidationArea* mNext;
};
InvalidationArea* mInvalidationArea;
};
#endif // nsTreeImageListener_h__

View file

@ -0,0 +1,866 @@
/* -*- 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 "mozilla/AsyncEventDispatcher.h"
#include "nsCOMPtr.h"
#include "nsTreeSelection.h"
#include "nsIBoxObject.h"
#include "nsITreeBoxObject.h"
#include "nsITreeView.h"
#include "nsString.h"
#include "nsIDOMElement.h"
#include "nsDOMClassInfoID.h"
#include "nsIContent.h"
#include "nsNameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsComponentManagerUtils.h"
using namespace mozilla;
// A helper class for managing our ranges of selection.
struct nsTreeRange
{
nsTreeSelection* mSelection;
nsTreeRange* mPrev;
nsTreeRange* mNext;
int32_t mMin;
int32_t mMax;
nsTreeRange(nsTreeSelection* aSel, int32_t aSingleVal)
:mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aSingleVal), mMax(aSingleVal) {}
nsTreeRange(nsTreeSelection* aSel, int32_t aMin, int32_t aMax)
:mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aMin), mMax(aMax) {}
~nsTreeRange() { delete mNext; }
void Connect(nsTreeRange* aPrev = nullptr, nsTreeRange* aNext = nullptr) {
if (aPrev)
aPrev->mNext = this;
else
mSelection->mFirstRange = this;
if (aNext)
aNext->mPrev = this;
mPrev = aPrev;
mNext = aNext;
}
nsresult RemoveRange(int32_t aStart, int32_t aEnd) {
// This should so be a loop... sigh...
// We start past the range to remove, so no more to remove
if (aEnd < mMin)
return NS_OK;
// We are the last range to be affected
if (aEnd < mMax) {
if (aStart <= mMin) {
// Just chop the start of the range off
mMin = aEnd + 1;
} else {
// We need to split the range
nsTreeRange* range = new nsTreeRange(mSelection, aEnd + 1, mMax);
if (!range)
return NS_ERROR_OUT_OF_MEMORY;
mMax = aStart - 1;
range->Connect(this, mNext);
}
return NS_OK;
}
nsTreeRange* next = mNext;
if (aStart <= mMin) {
// The remove includes us, remove ourselves from the list
if (mPrev)
mPrev->mNext = next;
else
mSelection->mFirstRange = next;
if (next)
next->mPrev = mPrev;
mPrev = mNext = nullptr;
delete this;
} else if (aStart <= mMax) {
// Just chop the end of the range off
mMax = aStart - 1;
}
return next ? next->RemoveRange(aStart, aEnd) : NS_OK;
}
nsresult Remove(int32_t aIndex) {
if (aIndex >= mMin && aIndex <= mMax) {
// We have found the range that contains us.
if (mMin == mMax) {
// Delete the whole range.
if (mPrev)
mPrev->mNext = mNext;
if (mNext)
mNext->mPrev = mPrev;
nsTreeRange* first = mSelection->mFirstRange;
if (first == this)
mSelection->mFirstRange = mNext;
mNext = mPrev = nullptr;
delete this;
}
else if (aIndex == mMin)
mMin++;
else if (aIndex == mMax)
mMax--;
else {
// We have to break this range.
nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex + 1, mMax);
if (!newRange)
return NS_ERROR_OUT_OF_MEMORY;
newRange->Connect(this, mNext);
mMax = aIndex - 1;
}
}
else if (mNext)
return mNext->Remove(aIndex);
return NS_OK;
}
nsresult Add(int32_t aIndex) {
if (aIndex < mMin) {
// We have found a spot to insert.
if (aIndex + 1 == mMin)
mMin = aIndex;
else if (mPrev && mPrev->mMax+1 == aIndex)
mPrev->mMax = aIndex;
else {
// We have to create a new range.
nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
if (!newRange)
return NS_ERROR_OUT_OF_MEMORY;
newRange->Connect(mPrev, this);
}
}
else if (mNext)
mNext->Add(aIndex);
else {
// Insert on to the end.
if (mMax+1 == aIndex)
mMax = aIndex;
else {
// We have to create a new range.
nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
if (!newRange)
return NS_ERROR_OUT_OF_MEMORY;
newRange->Connect(this, nullptr);
}
}
return NS_OK;
}
bool Contains(int32_t aIndex) {
if (aIndex >= mMin && aIndex <= mMax)
return true;
if (mNext)
return mNext->Contains(aIndex);
return false;
}
int32_t Count() {
int32_t total = mMax - mMin + 1;
if (mNext)
total += mNext->Count();
return total;
}
static void CollectRanges(nsTreeRange* aRange, nsTArray<int32_t>& aRanges)
{
nsTreeRange* cur = aRange;
while (cur) {
aRanges.AppendElement(cur->mMin);
aRanges.AppendElement(cur->mMax);
cur = cur->mNext;
}
}
static void InvalidateRanges(nsITreeBoxObject* aTree,
nsTArray<int32_t>& aRanges)
{
if (aTree) {
nsCOMPtr<nsITreeBoxObject> tree = aTree;
for (uint32_t i = 0; i < aRanges.Length(); i += 2) {
aTree->InvalidateRange(aRanges[i], aRanges[i + 1]);
}
}
}
void Invalidate() {
nsTArray<int32_t> ranges;
CollectRanges(this, ranges);
InvalidateRanges(mSelection->mTree, ranges);
}
void RemoveAllBut(int32_t aIndex) {
if (aIndex >= mMin && aIndex <= mMax) {
// Invalidate everything in this list.
nsTArray<int32_t> ranges;
CollectRanges(mSelection->mFirstRange, ranges);
mMin = aIndex;
mMax = aIndex;
nsTreeRange* first = mSelection->mFirstRange;
if (mPrev)
mPrev->mNext = mNext;
if (mNext)
mNext->mPrev = mPrev;
mNext = mPrev = nullptr;
if (first != this) {
delete mSelection->mFirstRange;
mSelection->mFirstRange = this;
}
InvalidateRanges(mSelection->mTree, ranges);
}
else if (mNext)
mNext->RemoveAllBut(aIndex);
}
void Insert(nsTreeRange* aRange) {
if (mMin >= aRange->mMax)
aRange->Connect(mPrev, this);
else if (mNext)
mNext->Insert(aRange);
else
aRange->Connect(this, nullptr);
}
};
nsTreeSelection::nsTreeSelection(nsITreeBoxObject* aTree)
: mTree(aTree),
mSuppressed(false),
mCurrentIndex(-1),
mShiftSelectPivot(-1),
mFirstRange(nullptr)
{
}
nsTreeSelection::~nsTreeSelection()
{
delete mFirstRange;
if (mSelectTimer)
mSelectTimer->Cancel();
}
NS_IMPL_CYCLE_COLLECTION(nsTreeSelection, mTree, mCurrentColumn)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeSelection)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeSelection)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeSelection)
NS_INTERFACE_MAP_ENTRY(nsITreeSelection)
NS_INTERFACE_MAP_ENTRY(nsINativeTreeSelection)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeSelection)
NS_INTERFACE_MAP_END
NS_IMETHODIMP nsTreeSelection::GetTree(nsITreeBoxObject * *aTree)
{
NS_IF_ADDREF(*aTree = mTree);
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::SetTree(nsITreeBoxObject * aTree)
{
if (mSelectTimer) {
mSelectTimer->Cancel();
mSelectTimer = nullptr;
}
// Make sure aTree really implements nsITreeBoxObject and nsIBoxObject!
nsCOMPtr<nsIBoxObject> bo = do_QueryInterface(aTree);
mTree = do_QueryInterface(bo);
NS_ENSURE_STATE(mTree == aTree);
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::GetSingle(bool* aSingle)
{
if (!mTree)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
nsCOMPtr<nsIDOMElement> element;
boxObject->GetElement(getter_AddRefs(element));
nsCOMPtr<nsIContent> content = do_QueryInterface(element);
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::single, &nsGkAtoms::cell, &nsGkAtoms::text, nullptr};
*aSingle = content->FindAttrValueIn(kNameSpaceID_None,
nsGkAtoms::seltype,
strings, eCaseMatters) >= 0;
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::IsSelected(int32_t aIndex, bool* aResult)
{
if (mFirstRange)
*aResult = mFirstRange->Contains(aIndex);
else
*aResult = false;
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::TimedSelect(int32_t aIndex, int32_t aMsec)
{
bool suppressSelect = mSuppressed;
if (aMsec != -1)
mSuppressed = true;
nsresult rv = Select(aIndex);
if (NS_FAILED(rv))
return rv;
if (aMsec != -1) {
mSuppressed = suppressSelect;
if (!mSuppressed) {
if (mSelectTimer)
mSelectTimer->Cancel();
mSelectTimer = do_CreateInstance("@mozilla.org/timer;1");
mSelectTimer->InitWithFuncCallback(SelectCallback, this, aMsec,
nsITimer::TYPE_ONE_SHOT);
}
}
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::Select(int32_t aIndex)
{
mShiftSelectPivot = -1;
nsresult rv = SetCurrentIndex(aIndex);
if (NS_FAILED(rv))
return rv;
if (mFirstRange) {
bool alreadySelected = mFirstRange->Contains(aIndex);
if (alreadySelected) {
int32_t count = mFirstRange->Count();
if (count > 1) {
// We need to deselect everything but our item.
mFirstRange->RemoveAllBut(aIndex);
FireOnSelectHandler();
}
return NS_OK;
}
else {
// Clear out our selection.
mFirstRange->Invalidate();
delete mFirstRange;
}
}
// Create our new selection.
mFirstRange = new nsTreeRange(this, aIndex);
if (!mFirstRange)
return NS_ERROR_OUT_OF_MEMORY;
mFirstRange->Invalidate();
// Fire the select event
FireOnSelectHandler();
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::ToggleSelect(int32_t aIndex)
{
// There are six cases that can occur on a ToggleSelect with our
// range code.
// (1) A new range should be made for a selection.
// (2) A single range is removed from the selection.
// (3) The item is added to an existing range.
// (4) The item is removed from an existing range.
// (5) The addition of the item causes two ranges to be merged.
// (6) The removal of the item causes two ranges to be split.
mShiftSelectPivot = -1;
nsresult rv = SetCurrentIndex(aIndex);
if (NS_FAILED(rv))
return rv;
if (!mFirstRange)
Select(aIndex);
else {
if (!mFirstRange->Contains(aIndex)) {
bool single;
rv = GetSingle(&single);
if (NS_SUCCEEDED(rv) && !single)
rv = mFirstRange->Add(aIndex);
}
else
rv = mFirstRange->Remove(aIndex);
if (NS_SUCCEEDED(rv)) {
if (mTree)
mTree->InvalidateRow(aIndex);
FireOnSelectHandler();
}
}
return rv;
}
NS_IMETHODIMP nsTreeSelection::RangedSelect(int32_t aStartIndex, int32_t aEndIndex, bool aAugment)
{
bool single;
nsresult rv = GetSingle(&single);
if (NS_FAILED(rv))
return rv;
if ((mFirstRange || (aStartIndex != aEndIndex)) && single)
return NS_OK;
if (!aAugment) {
// Clear our selection.
if (mFirstRange) {
mFirstRange->Invalidate();
delete mFirstRange;
mFirstRange = nullptr;
}
}
if (aStartIndex == -1) {
if (mShiftSelectPivot != -1)
aStartIndex = mShiftSelectPivot;
else if (mCurrentIndex != -1)
aStartIndex = mCurrentIndex;
else
aStartIndex = aEndIndex;
}
mShiftSelectPivot = aStartIndex;
rv = SetCurrentIndex(aEndIndex);
if (NS_FAILED(rv))
return rv;
int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
if (aAugment && mFirstRange) {
// We need to remove all the items within our selected range from the selection,
// and then we insert our new range into the list.
nsresult rv = mFirstRange->RemoveRange(start, end);
if (NS_FAILED(rv))
return rv;
}
nsTreeRange* range = new nsTreeRange(this, start, end);
if (!range)
return NS_ERROR_OUT_OF_MEMORY;
range->Invalidate();
if (aAugment && mFirstRange)
mFirstRange->Insert(range);
else
mFirstRange = range;
FireOnSelectHandler();
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::ClearRange(int32_t aStartIndex, int32_t aEndIndex)
{
nsresult rv = SetCurrentIndex(aEndIndex);
if (NS_FAILED(rv))
return rv;
if (mFirstRange) {
int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
mFirstRange->RemoveRange(start, end);
if (mTree)
mTree->InvalidateRange(start, end);
}
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::ClearSelection()
{
if (mFirstRange) {
mFirstRange->Invalidate();
delete mFirstRange;
mFirstRange = nullptr;
}
mShiftSelectPivot = -1;
FireOnSelectHandler();
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::InvertSelection()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsTreeSelection::SelectAll()
{
if (!mTree)
return NS_OK;
nsCOMPtr<nsITreeView> view;
mTree->GetView(getter_AddRefs(view));
if (!view)
return NS_OK;
int32_t rowCount;
view->GetRowCount(&rowCount);
bool single;
nsresult rv = GetSingle(&single);
if (NS_FAILED(rv))
return rv;
if (rowCount == 0 || (rowCount > 1 && single))
return NS_OK;
mShiftSelectPivot = -1;
// Invalidate not necessary when clearing selection, since
// we're going to invalidate the world on the SelectAll.
delete mFirstRange;
mFirstRange = new nsTreeRange(this, 0, rowCount-1);
mFirstRange->Invalidate();
FireOnSelectHandler();
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::GetRangeCount(int32_t* aResult)
{
int32_t count = 0;
nsTreeRange* curr = mFirstRange;
while (curr) {
count++;
curr = curr->mNext;
}
*aResult = count;
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::GetRangeAt(int32_t aIndex, int32_t* aMin, int32_t* aMax)
{
*aMin = *aMax = -1;
int32_t i = -1;
nsTreeRange* curr = mFirstRange;
while (curr) {
i++;
if (i == aIndex) {
*aMin = curr->mMin;
*aMax = curr->mMax;
break;
}
curr = curr->mNext;
}
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::GetCount(int32_t *count)
{
if (mFirstRange)
*count = mFirstRange->Count();
else // No range available, so there's no selected row.
*count = 0;
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::GetSelectEventsSuppressed(bool *aSelectEventsSuppressed)
{
*aSelectEventsSuppressed = mSuppressed;
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::SetSelectEventsSuppressed(bool aSelectEventsSuppressed)
{
mSuppressed = aSelectEventsSuppressed;
if (!mSuppressed)
FireOnSelectHandler();
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::GetCurrentIndex(int32_t *aCurrentIndex)
{
*aCurrentIndex = mCurrentIndex;
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(int32_t aIndex)
{
if (!mTree) {
return NS_ERROR_UNEXPECTED;
}
if (mCurrentIndex == aIndex) {
return NS_OK;
}
if (mCurrentIndex != -1 && mTree)
mTree->InvalidateRow(mCurrentIndex);
mCurrentIndex = aIndex;
if (!mTree)
return NS_OK;
if (aIndex != -1)
mTree->InvalidateRow(aIndex);
// Fire DOMMenuItemActive or DOMMenuItemInactive event for tree.
nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
NS_ASSERTION(boxObject, "no box object!");
if (!boxObject)
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMElement> treeElt;
boxObject->GetElement(getter_AddRefs(treeElt));
nsCOMPtr<nsINode> treeDOMNode(do_QueryInterface(treeElt));
NS_ENSURE_STATE(treeDOMNode);
NS_NAMED_LITERAL_STRING(DOMMenuItemActive, "DOMMenuItemActive");
NS_NAMED_LITERAL_STRING(DOMMenuItemInactive, "DOMMenuItemInactive");
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(treeDOMNode,
(aIndex != -1 ? DOMMenuItemActive :
DOMMenuItemInactive),
true, false);
return asyncDispatcher->PostDOMEvent();
}
NS_IMETHODIMP nsTreeSelection::GetCurrentColumn(nsITreeColumn** aCurrentColumn)
{
NS_IF_ADDREF(*aCurrentColumn = mCurrentColumn);
return NS_OK;
}
NS_IMETHODIMP nsTreeSelection::SetCurrentColumn(nsITreeColumn* aCurrentColumn)
{
if (!mTree) {
return NS_ERROR_UNEXPECTED;
}
if (mCurrentColumn == aCurrentColumn) {
return NS_OK;
}
if (mCurrentColumn) {
if (mFirstRange)
mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
if (mCurrentIndex != -1)
mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
}
mCurrentColumn = aCurrentColumn;
if (mCurrentColumn) {
if (mFirstRange)
mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
if (mCurrentIndex != -1)
mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
}
return NS_OK;
}
#define ADD_NEW_RANGE(macro_range, macro_selection, macro_start, macro_end) \
{ \
int32_t start = macro_start; \
int32_t end = macro_end; \
if (start > end) { \
end = start; \
} \
nsTreeRange* macro_new_range = new nsTreeRange(macro_selection, start, end); \
if (macro_range) \
macro_range->Insert(macro_new_range); \
else \
macro_range = macro_new_range; \
}
NS_IMETHODIMP
nsTreeSelection::AdjustSelection(int32_t aIndex, int32_t aCount)
{
NS_ASSERTION(aCount != 0, "adjusting by zero");
if (!aCount) return NS_OK;
// adjust mShiftSelectPivot, if necessary
if ((mShiftSelectPivot != 1) && (aIndex <= mShiftSelectPivot)) {
// if we are deleting and the delete includes the shift select pivot, reset it
if (aCount < 0 && (mShiftSelectPivot <= (aIndex -aCount -1))) {
mShiftSelectPivot = -1;
}
else {
mShiftSelectPivot += aCount;
}
}
// adjust mCurrentIndex, if necessary
if ((mCurrentIndex != -1) && (aIndex <= mCurrentIndex)) {
// if we are deleting and the delete includes the current index, reset it
if (aCount < 0 && (mCurrentIndex <= (aIndex -aCount -1))) {
mCurrentIndex = -1;
}
else {
mCurrentIndex += aCount;
}
}
// no selection, so nothing to do.
if (!mFirstRange) return NS_OK;
bool selChanged = false;
nsTreeRange* oldFirstRange = mFirstRange;
nsTreeRange* curr = mFirstRange;
mFirstRange = nullptr;
while (curr) {
if (aCount > 0) {
// inserting
if (aIndex > curr->mMax) {
// adjustment happens after the range, so no change
ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
}
else if (aIndex <= curr->mMin) {
// adjustment happens before the start of the range, so shift down
ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
selChanged = true;
}
else {
// adjustment happen inside the range.
// break apart the range and create two ranges
ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1);
ADD_NEW_RANGE(mFirstRange, this, aIndex + aCount, curr->mMax + aCount);
selChanged = true;
}
}
else {
// deleting
if (aIndex > curr->mMax) {
// adjustment happens after the range, so no change
ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
}
else {
// remember, aCount is negative
selChanged = true;
int32_t lastIndexOfAdjustment = aIndex - aCount - 1;
if (aIndex <= curr->mMin) {
if (lastIndexOfAdjustment < curr->mMin) {
// adjustment happens before the start of the range, so shift up
ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
}
else if (lastIndexOfAdjustment >= curr->mMax) {
// adjustment contains the range. remove the range by not adding it to the newRange
}
else {
// adjustment starts before the range, and ends in the middle of it, so trim the range
ADD_NEW_RANGE(mFirstRange, this, aIndex, curr->mMax + aCount)
}
}
else if (lastIndexOfAdjustment >= curr->mMax) {
// adjustment starts in the middle of the current range, and contains the end of the range, so trim the range
ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1)
}
else {
// range contains the adjustment, so shorten the range
ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax + aCount)
}
}
}
curr = curr->mNext;
}
delete oldFirstRange;
// Fire the select event
if (selChanged)
FireOnSelectHandler();
return NS_OK;
}
NS_IMETHODIMP
nsTreeSelection::InvalidateSelection()
{
if (mFirstRange)
mFirstRange->Invalidate();
return NS_OK;
}
NS_IMETHODIMP
nsTreeSelection::GetShiftSelectPivot(int32_t* aIndex)
{
*aIndex = mShiftSelectPivot;
return NS_OK;
}
nsresult
nsTreeSelection::FireOnSelectHandler()
{
if (mSuppressed || !mTree)
return NS_OK;
nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
NS_ASSERTION(boxObject, "no box object!");
if (!boxObject)
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMElement> elt;
boxObject->GetElement(getter_AddRefs(elt));
NS_ENSURE_STATE(elt);
nsCOMPtr<nsINode> node(do_QueryInterface(elt));
NS_ENSURE_STATE(node);
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(node, NS_LITERAL_STRING("select"), true, false);
asyncDispatcher->RunDOMEventWhenSafe();
return NS_OK;
}
void
nsTreeSelection::SelectCallback(nsITimer *aTimer, void *aClosure)
{
RefPtr<nsTreeSelection> self = static_cast<nsTreeSelection*>(aClosure);
if (self) {
self->FireOnSelectHandler();
aTimer->Cancel();
self->mSelectTimer = nullptr;
}
}
///////////////////////////////////////////////////////////////////////////////////
nsresult
NS_NewTreeSelection(nsITreeBoxObject* aTree, nsITreeSelection** aResult)
{
*aResult = new nsTreeSelection(aTree);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult);
return NS_OK;
}

View file

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 3; 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/. */
#ifndef nsTreeSelection_h__
#define nsTreeSelection_h__
#include "nsITreeSelection.h"
#include "nsITreeColumns.h"
#include "nsITimer.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
class nsITreeBoxObject;
struct nsTreeRange;
class nsTreeSelection final : public nsINativeTreeSelection
{
public:
explicit nsTreeSelection(nsITreeBoxObject* aTree);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsTreeSelection)
NS_DECL_NSITREESELECTION
// nsINativeTreeSelection: Untrusted code can use us
NS_IMETHOD EnsureNative() override { return NS_OK; }
friend struct nsTreeRange;
protected:
~nsTreeSelection();
nsresult FireOnSelectHandler();
static void SelectCallback(nsITimer *aTimer, void *aClosure);
protected:
// Members
nsCOMPtr<nsITreeBoxObject> mTree; // The tree will hold on to us through the view and let go when it dies.
bool mSuppressed; // Whether or not we should be firing onselect events.
int32_t mCurrentIndex; // The item to draw the rect around. The last one clicked, etc.
nsCOMPtr<nsITreeColumn> mCurrentColumn;
int32_t mShiftSelectPivot; // Used when multiple SHIFT+selects are performed to pivot on.
nsTreeRange* mFirstRange; // Our list of ranges.
nsCOMPtr<nsITimer> mSelectTimer;
};
nsresult
NS_NewTreeSelection(nsITreeBoxObject* aTree, nsITreeSelection** aResult);
#endif

View file

@ -0,0 +1,99 @@
/* -*- 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 "nsTreeStyleCache.h"
#include "nsStyleSet.h"
#include "mozilla/dom/Element.h"
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
nsTreeStyleCache::Transition::Transition(DFAState aState, nsIAtom* aSymbol)
: mState(aState), mInputSymbol(aSymbol)
{
}
bool
nsTreeStyleCache::Transition::operator==(const Transition& aOther) const
{
return aOther.mState == mState && aOther.mInputSymbol == mInputSymbol;
}
uint32_t
nsTreeStyleCache::Transition::Hash() const
{
// Make a 32-bit integer that combines the low-order 16 bits of the state and the input symbol.
uint32_t hb = mState << 16;
uint32_t lb = (NS_PTR_TO_UINT32(mInputSymbol.get()) << 16) >> 16;
return hb+lb;
}
// The style context cache impl
nsStyleContext*
nsTreeStyleCache::GetStyleContext(nsICSSPseudoComparator* aComparator,
nsPresContext* aPresContext,
nsIContent* aContent,
nsStyleContext* aContext,
nsIAtom* aPseudoElement,
const AtomArray & aInputWord)
{
uint32_t count = aInputWord.Length();
// Go ahead and init the transition table.
if (!mTransitionTable) {
// Automatic miss. Build the table
mTransitionTable = new TransitionTable();
}
// The first transition is always made off the supplied pseudo-element.
Transition transition(0, aPseudoElement);
DFAState currState = mTransitionTable->Get(transition);
if (!currState) {
// We had a miss. Make a new state and add it to our hash.
currState = mNextState;
mNextState++;
mTransitionTable->Put(transition, currState);
}
for (uint32_t i = 0; i < count; i++) {
Transition transition(currState, aInputWord[i]);
currState = mTransitionTable->Get(transition);
if (!currState) {
// We had a miss. Make a new state and add it to our hash.
currState = mNextState;
mNextState++;
mTransitionTable->Put(transition, currState);
}
}
// We're in a final state.
// Look up our style context for this state.
nsStyleContext* result = nullptr;
if (mCache) {
result = mCache->GetWeak(currState);
}
if (!result) {
// We missed the cache. Resolve this pseudo-style.
// XXXheycam ServoStyleSets do not support XUL tree styles.
if (aPresContext->StyleSet()->IsServo()) {
MOZ_CRASH("stylo: ServoStyleSets should not support XUL tree styles yet");
}
RefPtr<nsStyleContext> newResult = aPresContext->StyleSet()->AsGecko()->
ResolveXULTreePseudoStyle(aContent->AsElement(), aPseudoElement,
aContext, aComparator);
// Put the style context in our table, transferring the owning reference to the table.
if (!mCache) {
mCache = new StyleContextCache();
}
result = newResult.get();
mCache->Put(currState, newResult.forget());
}
return result;
}

View file

@ -0,0 +1,89 @@
/* -*- 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/. */
#ifndef nsTreeStyleCache_h__
#define nsTreeStyleCache_h__
#include "mozilla/Attributes.h"
#include "nsAutoPtr.h"
#include "nsIAtom.h"
#include "nsCOMArray.h"
#include "nsICSSPseudoComparator.h"
#include "nsRefPtrHashtable.h"
#include "nsStyleContext.h"
typedef nsCOMArray<nsIAtom> AtomArray;
class nsTreeStyleCache
{
public:
nsTreeStyleCache()
: mNextState(0)
{
}
~nsTreeStyleCache()
{
Clear();
}
void Clear()
{
mTransitionTable = nullptr;
mCache = nullptr;
mNextState = 0;
}
nsStyleContext* GetStyleContext(nsICSSPseudoComparator* aComparator,
nsPresContext* aPresContext,
nsIContent* aContent,
nsStyleContext* aContext,
nsIAtom* aPseudoElement,
const AtomArray & aInputWord);
protected:
typedef uint32_t DFAState;
class Transition final
{
public:
Transition(DFAState aState, nsIAtom* aSymbol);
bool operator==(const Transition& aOther) const;
uint32_t Hash() const;
private:
DFAState mState;
nsCOMPtr<nsIAtom> mInputSymbol;
};
typedef nsDataHashtable<nsGenericHashKey<Transition>, DFAState> TransitionTable;
// A transition table for a deterministic finite automaton. The DFA
// takes as its input a single pseudoelement and an ordered set of properties.
// It transitions on an input word that is the concatenation of the pseudoelement supplied
// with the properties in the array.
//
// It transitions from state to state by looking up entries in the transition table (which is
// a mapping from (S,i)->S', where S is the current state, i is the next
// property in the input word, and S' is the state to transition to.
//
// If S' is not found, it is constructed and entered into the hashtable
// under the key (S,i).
//
// Once the entire word has been consumed, the final state is used
// to reference the cache table to locate the style context.
nsAutoPtr<TransitionTable> mTransitionTable;
// The cache of all active style contexts. This is a hash from
// a final state in the DFA, Sf, to the resultant style context.
typedef nsRefPtrHashtable<nsUint32HashKey, nsStyleContext> StyleContextCache;
nsAutoPtr<StyleContextCache> mCache;
// An integer counter that is used when we need to make new states in the
// DFA.
DFAState mNextState;
};
#endif // nsTreeStyleCache_h__

View file

@ -0,0 +1,140 @@
/* -*- 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 "nsReadableUtils.h"
#include "nsTreeUtils.h"
#include "ChildIterator.h"
#include "nsCRT.h"
#include "nsIAtom.h"
#include "nsNameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
using namespace mozilla;
nsresult
nsTreeUtils::TokenizeProperties(const nsAString& aProperties, AtomArray & aPropertiesArray)
{
nsAString::const_iterator end;
aProperties.EndReading(end);
nsAString::const_iterator iter;
aProperties.BeginReading(iter);
do {
// Skip whitespace
while (iter != end && nsCRT::IsAsciiSpace(*iter))
++iter;
// If only whitespace, we're done
if (iter == end)
break;
// Note the first non-whitespace character
nsAString::const_iterator first = iter;
// Advance to the next whitespace character
while (iter != end && ! nsCRT::IsAsciiSpace(*iter))
++iter;
// XXX this would be nonsensical
NS_ASSERTION(iter != first, "eh? something's wrong here");
if (iter == first)
break;
nsCOMPtr<nsIAtom> atom = NS_Atomize(Substring(first, iter));
aPropertiesArray.AppendElement(atom);
} while (iter != end);
return NS_OK;
}
nsIContent*
nsTreeUtils::GetImmediateChild(nsIContent* aContainer, nsIAtom* aTag)
{
dom::FlattenedChildIterator iter(aContainer);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
if (child->IsXULElement(aTag)) {
return child;
}
}
return nullptr;
}
nsIContent*
nsTreeUtils::GetDescendantChild(nsIContent* aContainer, nsIAtom* aTag)
{
dom::FlattenedChildIterator iter(aContainer);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
if (child->IsXULElement(aTag)) {
return child;
}
child = GetDescendantChild(child, aTag);
if (child) {
return child;
}
}
return nullptr;
}
nsresult
nsTreeUtils::UpdateSortIndicators(nsIContent* aColumn, const nsAString& aDirection)
{
aColumn->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, aDirection, true);
aColumn->SetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, NS_LITERAL_STRING("true"), true);
// Unset sort attribute(s) on the other columns
nsCOMPtr<nsIContent> parentContent = aColumn->GetParent();
if (parentContent &&
parentContent->NodeInfo()->Equals(nsGkAtoms::treecols,
kNameSpaceID_XUL)) {
uint32_t i, numChildren = parentContent->GetChildCount();
for (i = 0; i < numChildren; ++i) {
nsCOMPtr<nsIContent> childContent = parentContent->GetChildAt(i);
if (childContent &&
childContent != aColumn &&
childContent->NodeInfo()->Equals(nsGkAtoms::treecol,
kNameSpaceID_XUL)) {
childContent->UnsetAttr(kNameSpaceID_None,
nsGkAtoms::sortDirection, true);
childContent->UnsetAttr(kNameSpaceID_None,
nsGkAtoms::sortActive, true);
}
}
}
return NS_OK;
}
nsresult
nsTreeUtils::GetColumnIndex(nsIContent* aColumn, int32_t* aResult)
{
nsIContent* parentContent = aColumn->GetParent();
if (parentContent &&
parentContent->NodeInfo()->Equals(nsGkAtoms::treecols,
kNameSpaceID_XUL)) {
uint32_t i, numChildren = parentContent->GetChildCount();
int32_t colIndex = 0;
for (i = 0; i < numChildren; ++i) {
nsIContent *childContent = parentContent->GetChildAt(i);
if (childContent &&
childContent->NodeInfo()->Equals(nsGkAtoms::treecol,
kNameSpaceID_XUL)) {
if (childContent == aColumn) {
*aResult = colIndex;
return NS_OK;
}
++colIndex;
}
}
}
*aResult = -1;
return NS_OK;
}

View file

@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 3; 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/. */
#ifndef nsTreeUtils_h__
#define nsTreeUtils_h__
#include "nsError.h"
#include "nsString.h"
#include "nsTreeStyleCache.h"
class nsIAtom;
class nsIContent;
class nsTreeUtils
{
public:
/**
* Parse a whitespace separated list of properties into an array
* of atoms.
*/
static nsresult
TokenizeProperties(const nsAString& aProperties, AtomArray & aPropertiesArray);
static nsIContent*
GetImmediateChild(nsIContent* aContainer, nsIAtom* aTag);
static nsIContent*
GetDescendantChild(nsIContent* aContainer, nsIAtom* aTag);
static nsresult
UpdateSortIndicators(nsIContent* aColumn, const nsAString& aDirection);
static nsresult
GetColumnIndex(nsIContent* aColumn, int32_t* aResult);
};
#endif // nsTreeUtils_h__