Files
tubestation/layout/inspector/inDOMView.cpp
Nathan Froyd b89e7ed3e4 Bug 1278925 - micro-optimize nsCOMArray appending with already_AddRefed AppendElement; r=erahm
In all of the places touched by this patch, the smart pointer we're
appending is about to become unused, so simply .forget()'ing its
reference into the appropriate nsCOMArray works just fine.
2016-06-20 18:38:10 -04:00

1285 lines
30 KiB
C++

/* -*- 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 "inDOMView.h"
#include "inIDOMUtils.h"
#include "inLayoutUtils.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeFilter.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMAttr.h"
#include "nsIDOMMozNamedAttrMap.h"
#include "nsIDOMMutationEvent.h"
#include "nsBindingManager.h"
#include "nsNameSpaceManager.h"
#include "nsIDocument.h"
#include "nsIServiceManager.h"
#include "nsITreeColumns.h"
#include "nsITreeBoxObject.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Services.h"
#ifdef ACCESSIBILITY
#include "nsIAccessibilityService.h"
#endif
using namespace mozilla;
////////////////////////////////////////////////////////////////////////
// inDOMViewNode
class inDOMViewNode
{
public:
inDOMViewNode() {}
explicit inDOMViewNode(nsIDOMNode* aNode);
~inDOMViewNode();
nsCOMPtr<nsIDOMNode> node;
inDOMViewNode* parent;
inDOMViewNode* next;
inDOMViewNode* previous;
int32_t level;
bool isOpen;
bool isContainer;
bool hasAnonymous;
bool hasSubDocument;
};
inDOMViewNode::inDOMViewNode(nsIDOMNode* aNode) :
node(aNode),
parent(nullptr),
next(nullptr),
previous(nullptr),
level(0),
isOpen(false),
isContainer(false),
hasAnonymous(false),
hasSubDocument(false)
{
}
inDOMViewNode::~inDOMViewNode()
{
}
////////////////////////////////////////////////////////////////////////
inDOMView::inDOMView() :
mShowAnonymous(false),
mShowSubDocuments(false),
mShowWhitespaceNodes(true),
mShowAccessibleNodes(false),
mWhatToShow(nsIDOMNodeFilter::SHOW_ALL)
{
}
inDOMView::~inDOMView()
{
SetRootNode(nullptr);
}
////////////////////////////////////////////////////////////////////////
// nsISupports
NS_IMPL_ISUPPORTS(inDOMView,
inIDOMView,
nsITreeView,
nsIMutationObserver)
////////////////////////////////////////////////////////////////////////
// inIDOMView
NS_IMETHODIMP
inDOMView::GetRootNode(nsIDOMNode** aNode)
{
*aNode = mRootNode;
NS_IF_ADDREF(*aNode);
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetRootNode(nsIDOMNode* aNode)
{
if (mTree)
mTree->BeginUpdateBatch();
if (mRootDocument) {
// remove previous document observer
nsCOMPtr<nsINode> doc(do_QueryInterface(mRootDocument));
if (doc)
doc->RemoveMutationObserver(this);
}
RemoveAllNodes();
mRootNode = aNode;
if (aNode) {
// If we are able to show element nodes, then start with the root node
// as the first node in the buffer
if (mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT) {
// allocate new node array
AppendNode(CreateNode(aNode, nullptr));
} else {
// place only the children of the root node in the buffer
ExpandNode(-1);
}
// store an owning reference to document so that it isn't
// destroyed before we are
mRootDocument = do_QueryInterface(aNode);
if (!mRootDocument) {
aNode->GetOwnerDocument(getter_AddRefs(mRootDocument));
}
// add document observer
nsCOMPtr<nsINode> doc(do_QueryInterface(mRootDocument));
if (doc)
doc->AddMutationObserver(this);
} else {
mRootDocument = nullptr;
}
if (mTree)
mTree->EndUpdateBatch();
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetNodeFromRowIndex(int32_t rowIndex, nsIDOMNode **_retval)
{
inDOMViewNode* viewNode = nullptr;
RowToNode(rowIndex, &viewNode);
if (!viewNode) return NS_ERROR_FAILURE;
*_retval = viewNode->node;
NS_IF_ADDREF(*_retval);
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetRowIndexFromNode(nsIDOMNode *node, int32_t *_retval)
{
NodeToRow(node, _retval);
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetShowAnonymousContent(bool *aShowAnonymousContent)
{
*aShowAnonymousContent = mShowAnonymous;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetShowAnonymousContent(bool aShowAnonymousContent)
{
mShowAnonymous = aShowAnonymousContent;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetShowSubDocuments(bool *aShowSubDocuments)
{
*aShowSubDocuments = mShowSubDocuments;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetShowSubDocuments(bool aShowSubDocuments)
{
mShowSubDocuments = aShowSubDocuments;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetShowWhitespaceNodes(bool *aShowWhitespaceNodes)
{
*aShowWhitespaceNodes = mShowWhitespaceNodes;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetShowWhitespaceNodes(bool aShowWhitespaceNodes)
{
mShowWhitespaceNodes = aShowWhitespaceNodes;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetShowAccessibleNodes(bool *aShowAccessibleNodes)
{
*aShowAccessibleNodes = mShowAccessibleNodes;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetShowAccessibleNodes(bool aShowAccessibleNodes)
{
mShowAccessibleNodes = aShowAccessibleNodes;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetWhatToShow(uint32_t *aWhatToShow)
{
*aWhatToShow = mWhatToShow;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetWhatToShow(uint32_t aWhatToShow)
{
mWhatToShow = aWhatToShow;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::Rebuild()
{
nsCOMPtr<nsIDOMNode> root;
GetRootNode(getter_AddRefs(root));
SetRootNode(root);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// nsITreeView
NS_IMETHODIMP
inDOMView::GetRowCount(int32_t *aRowCount)
{
*aRowCount = GetRowCount();
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetRowProperties(int32_t index, nsAString& aProps)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetCellProperties(int32_t row, nsITreeColumn* col,
nsAString& aProps)
{
inDOMViewNode* node = nullptr;
RowToNode(row, &node);
if (!node) return NS_ERROR_FAILURE;
nsCOMPtr<nsIContent> content = do_QueryInterface(node->node);
if (content && content->IsInAnonymousSubtree()) {
aProps.AppendLiteral("anonymous ");
}
uint16_t nodeType;
node->node->GetNodeType(&nodeType);
switch (nodeType) {
case nsIDOMNode::ELEMENT_NODE:
aProps.AppendLiteral("ELEMENT_NODE");
break;
case nsIDOMNode::ATTRIBUTE_NODE:
aProps.AppendLiteral("ATTRIBUTE_NODE");
break;
case nsIDOMNode::TEXT_NODE:
aProps.AppendLiteral("TEXT_NODE");
break;
case nsIDOMNode::CDATA_SECTION_NODE:
aProps.AppendLiteral("CDATA_SECTION_NODE");
break;
case nsIDOMNode::ENTITY_REFERENCE_NODE:
aProps.AppendLiteral("ENTITY_REFERENCE_NODE");
break;
case nsIDOMNode::ENTITY_NODE:
aProps.AppendLiteral("ENTITY_NODE");
break;
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
aProps.AppendLiteral("PROCESSING_INSTRUCTION_NODE");
break;
case nsIDOMNode::COMMENT_NODE:
aProps.AppendLiteral("COMMENT_NODE");
break;
case nsIDOMNode::DOCUMENT_NODE:
aProps.AppendLiteral("DOCUMENT_NODE");
break;
case nsIDOMNode::DOCUMENT_TYPE_NODE:
aProps.AppendLiteral("DOCUMENT_TYPE_NODE");
break;
case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
aProps.AppendLiteral("DOCUMENT_FRAGMENT_NODE");
break;
case nsIDOMNode::NOTATION_NODE:
aProps.AppendLiteral("NOTATION_NODE");
break;
}
#ifdef ACCESSIBILITY
if (mShowAccessibleNodes) {
nsCOMPtr<nsIAccessibilityService> accService =
services::GetAccessibilityService();
NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
if (accService->HasAccessible(node->node))
aProps.AppendLiteral(" ACCESSIBLE_NODE");
}
#endif
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetColumnProperties(nsITreeColumn* col, nsAString& aProps)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetImageSrc(int32_t row, nsITreeColumn* col, nsAString& _retval)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetProgressMode(int32_t row, nsITreeColumn* col, int32_t* _retval)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetCellValue(int32_t row, nsITreeColumn* col, nsAString& _retval)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetCellText(int32_t row, nsITreeColumn* col, nsAString& _retval)
{
inDOMViewNode* node = nullptr;
RowToNode(row, &node);
if (!node) return NS_ERROR_FAILURE;
nsIDOMNode* domNode = node->node;
nsAutoString colID;
col->GetId(colID);
if (colID.EqualsLiteral("colNodeName"))
domNode->GetNodeName(_retval);
else if (colID.EqualsLiteral("colLocalName"))
domNode->GetLocalName(_retval);
else if (colID.EqualsLiteral("colPrefix"))
domNode->GetPrefix(_retval);
else if (colID.EqualsLiteral("colNamespaceURI"))
domNode->GetNamespaceURI(_retval);
else if (colID.EqualsLiteral("colNodeType")) {
uint16_t nodeType;
domNode->GetNodeType(&nodeType);
nsAutoString temp;
temp.AppendInt(int32_t(nodeType));
_retval = temp;
} else if (colID.EqualsLiteral("colNodeValue"))
domNode->GetNodeValue(_retval);
else {
if (StringBeginsWith(colID, NS_LITERAL_STRING("col@"))) {
nsCOMPtr<nsIDOMElement> el = do_QueryInterface(node->node);
if (el) {
nsAutoString attr;
colID.Right(attr, colID.Length()-4); // have to use this because Substring is crashing on me!
el->GetAttribute(attr, _retval);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
inDOMView::IsContainer(int32_t index, bool *_retval)
{
inDOMViewNode* node = nullptr;
RowToNode(index, &node);
if (!node) return NS_ERROR_FAILURE;
*_retval = node->isContainer;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::IsContainerOpen(int32_t index, bool *_retval)
{
inDOMViewNode* node = nullptr;
RowToNode(index, &node);
if (!node) return NS_ERROR_FAILURE;
*_retval = node->isOpen;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::IsContainerEmpty(int32_t index, bool *_retval)
{
inDOMViewNode* node = nullptr;
RowToNode(index, &node);
if (!node) return NS_ERROR_FAILURE;
*_retval = node->isContainer ? false : true;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetLevel(int32_t index, int32_t *_retval)
{
inDOMViewNode* node = nullptr;
RowToNode(index, &node);
if (!node) return NS_ERROR_FAILURE;
*_retval = node->level;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetParentIndex(int32_t rowIndex, int32_t *_retval)
{
inDOMViewNode* node = nullptr;
RowToNode(rowIndex, &node);
if (!node) return NS_ERROR_FAILURE;
// GetParentIndex returns -1 if there is no parent
*_retval = -1;
inDOMViewNode* checkNode = nullptr;
int32_t i = rowIndex - 1;
do {
nsresult rv = RowToNode(i, &checkNode);
if (NS_FAILED(rv)) {
// No parent. Just break out.
break;
}
if (checkNode == node->parent) {
*_retval = i;
return NS_OK;
}
--i;
} while (checkNode);
return NS_OK;
}
NS_IMETHODIMP
inDOMView::HasNextSibling(int32_t rowIndex, int32_t afterIndex, bool *_retval)
{
inDOMViewNode* node = nullptr;
RowToNode(rowIndex, &node);
if (!node) return NS_ERROR_FAILURE;
*_retval = node->next != nullptr;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::ToggleOpenState(int32_t index)
{
inDOMViewNode* node = nullptr;
RowToNode(index, &node);
if (!node) return NS_ERROR_FAILURE;
int32_t oldCount = GetRowCount();
if (node->isOpen)
CollapseNode(index);
else
ExpandNode(index);
// Update the twisty.
mTree->InvalidateRow(index);
mTree->RowCountChanged(index+1, GetRowCount() - oldCount);
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetTree(nsITreeBoxObject *tree)
{
mTree = tree;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::GetSelection(nsITreeSelection * *aSelection)
{
*aSelection = mSelection;
NS_IF_ADDREF(*aSelection);
return NS_OK;
}
NS_IMETHODIMP inDOMView::SetSelection(nsITreeSelection * aSelection)
{
mSelection = aSelection;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SelectionChanged()
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetCellValue(int32_t row, nsITreeColumn* col, const nsAString& value)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::SetCellText(int32_t row, nsITreeColumn* col, const nsAString& value)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::CycleHeader(nsITreeColumn* col)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::CycleCell(int32_t row, nsITreeColumn* col)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::IsEditable(int32_t row, nsITreeColumn* col, bool *_retval)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::IsSelectable(int32_t row, nsITreeColumn* col, bool *_retval)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::IsSeparator(int32_t index, bool *_retval)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::IsSorted(bool *_retval)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::CanDrop(int32_t index, int32_t orientation,
nsIDOMDataTransfer* aDataTransfer, bool *_retval)
{
*_retval = false;
return NS_OK;
}
NS_IMETHODIMP
inDOMView::Drop(int32_t row, int32_t orientation, nsIDOMDataTransfer* aDataTransfer)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::PerformAction(const char16_t *action)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::PerformActionOnRow(const char16_t *action, int32_t row)
{
return NS_OK;
}
NS_IMETHODIMP
inDOMView::PerformActionOnCell(const char16_t* action, int32_t row, nsITreeColumn* col)
{
return NS_OK;
}
///////////////////////////////////////////////////////////////////////
// nsIMutationObserver
void
inDOMView::NodeWillBeDestroyed(const nsINode* aNode)
{
NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
}
void
inDOMView::AttributeChanged(nsIDocument* aDocument, dom::Element* aElement,
int32_t aNameSpaceID, nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue)
{
if (!mTree) {
return;
}
if (!(mWhatToShow & nsIDOMNodeFilter::SHOW_ATTRIBUTE)) {
return;
}
nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
// get the dom attribute node, if there is any
nsCOMPtr<nsIDOMElement> el(do_QueryInterface(aElement));
nsCOMPtr<nsIDOMAttr> domAttr;
nsDependentAtomString attrStr(aAttribute);
if (aNameSpaceID) {
nsNameSpaceManager* nsm = nsNameSpaceManager::GetInstance();
if (!nsm) {
// we can't find out which attribute we want :(
return;
}
nsString attrNS;
nsresult rv = nsm->GetNameSpaceURI(aNameSpaceID, attrNS);
if (NS_FAILED(rv)) {
return;
}
(void)el->GetAttributeNodeNS(attrNS, attrStr, getter_AddRefs(domAttr));
} else {
(void)el->GetAttributeNode(attrStr, getter_AddRefs(domAttr));
}
if (aModType == nsIDOMMutationEvent::MODIFICATION) {
// No fancy stuff here, just invalidate the changed row
if (!domAttr) {
return;
}
int32_t row = 0;
NodeToRow(domAttr, &row);
mTree->InvalidateRange(row, row);
} else if (aModType == nsIDOMMutationEvent::ADDITION) {
if (!domAttr) {
return;
}
// get the number of attributes on this content node
nsCOMPtr<nsIDOMMozNamedAttrMap> attrs;
el->GetAttributes(getter_AddRefs(attrs));
uint32_t attrCount;
attrs->GetLength(&attrCount);
inDOMViewNode* contentNode = nullptr;
int32_t contentRow;
int32_t attrRow;
if (mRootNode == el &&
!(mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT)) {
// if this view has a root node but is not displaying it,
// it is ok to act as if the changed attribute is on the root.
attrRow = attrCount - 1;
} else {
if (NS_FAILED(NodeToRow(el, &contentRow))) {
return;
}
RowToNode(contentRow, &contentNode);
if (!contentNode->isOpen) {
return;
}
attrRow = contentRow + attrCount;
}
inDOMViewNode* newNode = CreateNode(domAttr, contentNode);
inDOMViewNode* insertNode = nullptr;
RowToNode(attrRow, &insertNode);
if (insertNode) {
if (contentNode &&
insertNode->level <= contentNode->level) {
RowToNode(attrRow-1, &insertNode);
InsertLinkAfter(newNode, insertNode);
} else
InsertLinkBefore(newNode, insertNode);
}
InsertNode(newNode, attrRow);
mTree->RowCountChanged(attrRow, 1);
} else if (aModType == nsIDOMMutationEvent::REMOVAL) {
// At this point, the attribute is already gone from the DOM, but is still represented
// in our mRows array. Search through the content node's children for the corresponding
// node and remove it.
// get the row of the content node
inDOMViewNode* contentNode = nullptr;
int32_t contentRow;
int32_t baseLevel;
if (NS_SUCCEEDED(NodeToRow(el, &contentRow))) {
RowToNode(contentRow, &contentNode);
baseLevel = contentNode->level;
} else {
if (mRootNode == el) {
contentRow = -1;
baseLevel = -1;
} else
return;
}
// search for the attribute node that was removed
inDOMViewNode* checkNode = nullptr;
int32_t row = 0;
for (row = contentRow+1; row < GetRowCount(); ++row) {
checkNode = GetNodeAt(row);
if (checkNode->level == baseLevel+1) {
domAttr = do_QueryInterface(checkNode->node);
if (domAttr) {
nsAutoString attrName;
domAttr->GetNodeName(attrName);
if (attrName.Equals(attrStr)) {
// we have found the row for the attribute that was removed
RemoveLink(checkNode);
RemoveNode(row);
mTree->RowCountChanged(row, -1);
break;
}
}
}
if (checkNode->level <= baseLevel)
break;
}
}
}
void
inDOMView::ContentAppended(nsIDocument *aDocument,
nsIContent* aContainer,
nsIContent* aFirstNewContent,
int32_t /* unused */)
{
if (!mTree) {
return;
}
for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
// Our ContentInserted impl doesn't use the index
ContentInserted(aDocument, aContainer, cur, 0);
}
}
void
inDOMView::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aChild, int32_t /* unused */)
{
if (!mTree)
return;
nsresult rv;
nsCOMPtr<nsIDOMNode> childDOMNode(do_QueryInterface(aChild));
nsCOMPtr<nsIDOMNode> parent;
if (!mDOMUtils) {
mDOMUtils = services::GetInDOMUtils();
if (!mDOMUtils) {
return;
}
}
mDOMUtils->GetParentForNode(childDOMNode, mShowAnonymous,
getter_AddRefs(parent));
// find the inDOMViewNode for the parent of the inserted content
int32_t parentRow = 0;
if (NS_FAILED(rv = NodeToRow(parent, &parentRow)))
return;
inDOMViewNode* parentNode = nullptr;
if (NS_FAILED(rv = RowToNode(parentRow, &parentNode)))
return;
nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
if (!parentNode->isOpen) {
// Parent is not open, so don't bother creating tree rows for the
// kids. But do indicate that it's now a container, if needed.
if (!parentNode->isContainer) {
parentNode->isContainer = true;
mTree->InvalidateRow(parentRow);
}
return;
}
// get the previous sibling of the inserted content
nsCOMPtr<nsIDOMNode> previous;
GetRealPreviousSibling(childDOMNode, parent, getter_AddRefs(previous));
inDOMViewNode* previousNode = nullptr;
int32_t row = 0;
if (previous) {
// find the inDOMViewNode for the previous sibling of the inserted content
int32_t previousRow = 0;
if (NS_FAILED(rv = NodeToRow(previous, &previousRow)))
return;
if (NS_FAILED(rv = RowToNode(previousRow, &previousNode)))
return;
// get the last descendant of the previous row, which is the row
// after which to insert this new row
GetLastDescendantOf(previousNode, previousRow, &row);
++row;
} else {
// there is no previous sibling, so the new row will be inserted after the parent
row = parentRow+1;
}
inDOMViewNode* newNode = CreateNode(childDOMNode, parentNode);
if (previous) {
InsertLinkAfter(newNode, previousNode);
} else {
int32_t firstChildRow;
if (NS_SUCCEEDED(GetFirstDescendantOf(parentNode, parentRow, &firstChildRow))) {
inDOMViewNode* firstChild;
RowToNode(firstChildRow, &firstChild);
InsertLinkBefore(newNode, firstChild);
}
}
// insert new node
InsertNode(newNode, row);
mTree->RowCountChanged(row, 1);
}
void
inDOMView::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aChild, int32_t aIndexInContainer,
nsIContent* aPreviousSibling)
{
if (!mTree)
return;
nsresult rv;
// find the inDOMViewNode for the old child
nsCOMPtr<nsIDOMNode> oldDOMNode(do_QueryInterface(aChild));
int32_t row = 0;
if (NS_FAILED(rv = NodeToRow(oldDOMNode, &row)))
return;
inDOMViewNode* oldNode;
if (NS_FAILED(rv = RowToNode(row, &oldNode)))
return;
nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
// The parent may no longer be a container. Note that we don't want
// to access oldNode after calling RemoveNode, so do this now.
inDOMViewNode* parentNode = oldNode->parent;
bool isOnlyChild = oldNode->previous == nullptr && oldNode->next == nullptr;
// Keep track of how many rows we are removing. It's at least one,
// but if we're open it's more.
int32_t oldCount = GetRowCount();
if (oldNode->isOpen)
CollapseNode(row);
RemoveLink(oldNode);
RemoveNode(row);
if (isOnlyChild) {
// Fix up the parent
parentNode->isContainer = false;
parentNode->isOpen = false;
mTree->InvalidateRow(NodeToRow(parentNode));
}
mTree->RowCountChanged(row, GetRowCount() - oldCount);
}
///////////////////////////////////////////////////////////////////////
// inDOMView
//////// NODE MANAGEMENT
inDOMViewNode*
inDOMView::GetNodeAt(int32_t aRow)
{
return mNodes.ElementAt(aRow);
}
int32_t
inDOMView::GetRowCount()
{
return mNodes.Length();
}
int32_t
inDOMView::NodeToRow(inDOMViewNode* aNode)
{
return mNodes.IndexOf(aNode);
}
inDOMViewNode*
inDOMView::CreateNode(nsIDOMNode* aNode, inDOMViewNode* aParent)
{
inDOMViewNode* viewNode = new inDOMViewNode(aNode);
viewNode->level = aParent ? aParent->level+1 : 0;
viewNode->parent = aParent;
nsCOMArray<nsIDOMNode> grandKids;
GetChildNodesFor(aNode, grandKids);
viewNode->isContainer = (grandKids.Count() > 0);
return viewNode;
}
bool
inDOMView::RowOutOfBounds(int32_t aRow, int32_t aCount)
{
return aRow < 0 || aRow >= GetRowCount() || aCount+aRow > GetRowCount();
}
void
inDOMView::AppendNode(inDOMViewNode* aNode)
{
mNodes.AppendElement(aNode);
}
void
inDOMView::InsertNode(inDOMViewNode* aNode, int32_t aRow)
{
if (RowOutOfBounds(aRow, 1))
AppendNode(aNode);
else
mNodes.InsertElementAt(aRow, aNode);
}
void
inDOMView::RemoveNode(int32_t aRow)
{
if (RowOutOfBounds(aRow, 1))
return;
delete GetNodeAt(aRow);
mNodes.RemoveElementAt(aRow);
}
void
inDOMView::ReplaceNode(inDOMViewNode* aNode, int32_t aRow)
{
if (RowOutOfBounds(aRow, 1))
return;
delete GetNodeAt(aRow);
mNodes.ElementAt(aRow) = aNode;
}
void
inDOMView::InsertNodes(nsTArray<inDOMViewNode*>& aNodes, int32_t aRow)
{
if (aRow < 0 || aRow > GetRowCount())
return;
mNodes.InsertElementsAt(aRow, aNodes);
}
void
inDOMView::RemoveNodes(int32_t aRow, int32_t aCount)
{
if (aRow < 0)
return;
int32_t rowCount = GetRowCount();
for (int32_t i = aRow; i < aRow+aCount && i < rowCount; ++i) {
delete GetNodeAt(i);
}
mNodes.RemoveElementsAt(aRow, aCount);
}
void
inDOMView::RemoveAllNodes()
{
int32_t rowCount = GetRowCount();
for (int32_t i = 0; i < rowCount; ++i) {
delete GetNodeAt(i);
}
mNodes.Clear();
}
void
inDOMView::ExpandNode(int32_t aRow)
{
inDOMViewNode* node = nullptr;
RowToNode(aRow, &node);
nsCOMArray<nsIDOMNode> kids;
GetChildNodesFor(node ? node->node : mRootNode,
kids);
int32_t kidCount = kids.Count();
nsTArray<inDOMViewNode*> list(kidCount);
inDOMViewNode* newNode = nullptr;
inDOMViewNode* prevNode = nullptr;
for (int32_t i = 0; i < kidCount; ++i) {
newNode = CreateNode(kids[i], node);
list.AppendElement(newNode);
if (prevNode)
prevNode->next = newNode;
newNode->previous = prevNode;
prevNode = newNode;
}
InsertNodes(list, aRow+1);
if (node)
node->isOpen = true;
}
void
inDOMView::CollapseNode(int32_t aRow)
{
inDOMViewNode* node = nullptr;
nsresult rv = RowToNode(aRow, &node);
if (NS_FAILED(rv)) {
return;
}
int32_t row = 0;
GetLastDescendantOf(node, aRow, &row);
RemoveNodes(aRow+1, row-aRow);
node->isOpen = false;
}
//////// NODE AND ROW CONVERSION
nsresult
inDOMView::RowToNode(int32_t aRow, inDOMViewNode** aNode)
{
if (aRow < 0 || aRow >= GetRowCount())
return NS_ERROR_FAILURE;
*aNode = GetNodeAt(aRow);
return NS_OK;
}
nsresult
inDOMView::NodeToRow(nsIDOMNode* aNode, int32_t* aRow)
{
int32_t rowCount = GetRowCount();
for (int32_t i = 0; i < rowCount; ++i) {
if (GetNodeAt(i)->node == aNode) {
*aRow = i;
return NS_OK;
}
}
*aRow = -1;
return NS_ERROR_FAILURE;
}
//////// NODE HIERARCHY MUTATION
void
inDOMView::InsertLinkAfter(inDOMViewNode* aNode, inDOMViewNode* aInsertAfter)
{
if (aInsertAfter->next)
aInsertAfter->next->previous = aNode;
aNode->next = aInsertAfter->next;
aInsertAfter->next = aNode;
aNode->previous = aInsertAfter;
}
void
inDOMView::InsertLinkBefore(inDOMViewNode* aNode, inDOMViewNode* aInsertBefore)
{
if (aInsertBefore->previous)
aInsertBefore->previous->next = aNode;
aNode->previous = aInsertBefore->previous;
aInsertBefore->previous = aNode;
aNode->next = aInsertBefore;
}
void
inDOMView::RemoveLink(inDOMViewNode* aNode)
{
if (aNode->previous)
aNode->previous->next = aNode->next;
if (aNode->next)
aNode->next->previous = aNode->previous;
}
void
inDOMView::ReplaceLink(inDOMViewNode* aNewNode, inDOMViewNode* aOldNode)
{
if (aOldNode->previous)
aOldNode->previous->next = aNewNode;
if (aOldNode->next)
aOldNode->next->previous = aNewNode;
aNewNode->next = aOldNode->next;
aNewNode->previous = aOldNode->previous;
}
//////// NODE HIERARCHY UTILITIES
nsresult
inDOMView::GetFirstDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult)
{
// get the first node that is a descendant of the previous sibling
int32_t row = 0;
inDOMViewNode* node;
for (row = aRow+1; row < GetRowCount(); ++row) {
node = GetNodeAt(row);
if (node->parent == aNode) {
*aResult = row;
return NS_OK;
}
if (node->level <= aNode->level)
break;
}
return NS_ERROR_FAILURE;
}
nsresult
inDOMView::GetLastDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult)
{
// get the last node that is a descendant of the previous sibling
int32_t row = 0;
for (row = aRow+1; row < GetRowCount(); ++row) {
if (GetNodeAt(row)->level <= aNode->level)
break;
}
*aResult = row-1;
return NS_OK;
}
//////// DOM UTILITIES
nsresult
inDOMView::GetChildNodesFor(nsIDOMNode* aNode, nsCOMArray<nsIDOMNode>& aResult)
{
NS_ENSURE_ARG(aNode);
// attribute nodes
if (mWhatToShow & nsIDOMNodeFilter::SHOW_ATTRIBUTE) {
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
if (element) {
nsCOMPtr<nsIDOMMozNamedAttrMap> attrs;
element->GetAttributes(getter_AddRefs(attrs));
if (attrs) {
AppendAttrsToArray(attrs, aResult);
}
}
}
if (mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT) {
nsCOMPtr<nsIDOMNodeList> kids;
if (!mDOMUtils) {
mDOMUtils = services::GetInDOMUtils();
if (!mDOMUtils) {
return NS_ERROR_FAILURE;
}
}
mDOMUtils->GetChildrenForNode(aNode, mShowAnonymous,
getter_AddRefs(kids));
if (kids) {
AppendKidsToArray(kids, aResult);
}
}
if (mShowSubDocuments) {
nsCOMPtr<nsIDOMNode> domdoc =
do_QueryInterface(inLayoutUtils::GetSubDocumentFor(aNode));
if (domdoc) {
aResult.AppendObject(domdoc);
}
}
return NS_OK;
}
nsresult
inDOMView::GetRealPreviousSibling(nsIDOMNode* aNode, nsIDOMNode* aRealParent, nsIDOMNode** aSibling)
{
// XXXjrh: This won't work for some cases during some situations where XBL insertion points
// are involved. Fix me!
aNode->GetPreviousSibling(aSibling);
return NS_OK;
}
nsresult
inDOMView::AppendKidsToArray(nsIDOMNodeList* aKids,
nsCOMArray<nsIDOMNode>& aArray)
{
uint32_t l = 0;
aKids->GetLength(&l);
nsCOMPtr<nsIDOMNode> kid;
uint16_t nodeType = 0;
// Try and get DOM Utils in case we don't have one yet.
if (!mShowWhitespaceNodes && !mDOMUtils) {
mDOMUtils = services::GetInDOMUtils();
}
for (uint32_t i = 0; i < l; ++i) {
aKids->Item(i, getter_AddRefs(kid));
kid->GetNodeType(&nodeType);
NS_ASSERTION(nodeType && nodeType <= nsIDOMNode::NOTATION_NODE,
"Unknown node type. "
"Were new types added to the spec?");
// As of DOM Level 2 Core and Traversal, each NodeFilter constant
// is defined as the lower nth bit in the NodeFilter bitmask,
// where n is the numeric constant of the nodeType it represents.
// If this invariant ever changes, we will need to update the
// following line.
uint32_t filterForNodeType = 1 << (nodeType - 1);
if (mWhatToShow & filterForNodeType) {
if ((nodeType == nsIDOMNode::TEXT_NODE ||
nodeType == nsIDOMNode::COMMENT_NODE) &&
!mShowWhitespaceNodes && mDOMUtils) {
nsCOMPtr<nsIDOMCharacterData> data = do_QueryInterface(kid);
NS_ASSERTION(data, "Does not implement nsIDOMCharacterData!");
bool ignore;
mDOMUtils->IsIgnorableWhitespace(data, &ignore);
if (ignore) {
continue;
}
}
aArray.AppendElement(kid.forget());
}
}
return NS_OK;
}
nsresult
inDOMView::AppendAttrsToArray(nsIDOMMozNamedAttrMap* aAttributes,
nsCOMArray<nsIDOMNode>& aArray)
{
uint32_t l = 0;
aAttributes->GetLength(&l);
nsCOMPtr<nsIDOMAttr> attribute;
for (uint32_t i = 0; i < l; ++i) {
aAttributes->Item(i, getter_AddRefs(attribute));
aArray.AppendElement(attribute.forget());
}
return NS_OK;
}