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