/* -*- 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; }