diff options
Diffstat (limited to 'layout/inspector/inDOMView.cpp')
-rw-r--r-- | layout/inspector/inDOMView.cpp | 1283 |
1 files changed, 1283 insertions, 0 deletions
diff --git a/layout/inspector/inDOMView.cpp b/layout/inspector/inDOMView.cpp new file mode 100644 index 000000000..967671ba3 --- /dev/null +++ b/layout/inspector/inDOMView.cpp @@ -0,0 +1,1283 @@ +/* -*- 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 "nsAccessibilityService.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) { + nsAccessibilityService* accService = GetOrCreateAccService(); + 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; +} |