mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 18:31:29 +00:00
1284 lines
30 KiB
C++
1284 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)
|
|
{
|
|
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.AppendObject(kid);
|
|
}
|
|
}
|
|
|
|
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.AppendObject(attribute);
|
|
}
|
|
return NS_OK;
|
|
}
|