/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * (C) Copyright The MITRE Corporation 1999 All rights reserved. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * The program provided "as is" without any warranty express or * implied, including the warranty of non-infringement and the implied * warranties of merchantibility and fitness for a particular purpose. * The Copyright owner will not be liable for any damages suffered by * you as a result of using the Program. In no event will the Copyright * owner be liable for any special, indirect or consequential damages or * lost profits even if the Copyright owner has been advised of the * possibility of their occurrence. * * Please see release.txt distributed with this file for more information. * * Contributor(s): Tom Kneeland * Peter Van der Beken * */ /** * Implementation of the wrapper class to convert the Mozilla nsIDOMDocument * interface into a TransforMIIX Document interface. */ #include "mozilladom.h" #include "nsIDocument.h" #include "nsIDOMAttr.h" #include "nsIDOMComment.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentFragment.h" #include "nsIDOMElement.h" #include "nsIDOMProcessingInstruction.h" #include "nsIDOMElement.h" #include "nsIDOMText.h" // Need to determine if these are well-chosen. #define WRAPPER_INITIAL_SIZE 256 #define ATTR_INITIAL_SIZE 128 struct txWrapperHashEntry : public PLDHashEntryHdr { MozillaObjectWrapper* mWrapper; }; PR_STATIC_CALLBACK(const void *) txWrapperHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry) { txWrapperHashEntry *e = NS_STATIC_CAST(txWrapperHashEntry *, entry); return e->mWrapper->getNSObj(); } PR_STATIC_CALLBACK(void) txWrapperHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) { txWrapperHashEntry *e = NS_STATIC_CAST(txWrapperHashEntry *, entry); // Don't delete the document wrapper, this is only called from its // destructor. if (e->mWrapper != table->data) { delete e->mWrapper; } } PR_STATIC_CALLBACK(PRBool) txWrapperHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry, const void *key) { const txWrapperHashEntry *e = NS_STATIC_CAST(const txWrapperHashEntry *, entry); return e->mWrapper->getNSObj() == key; } struct txAttributeHashEntry : public PLDHashEntryHdr { Attr* mAttribute; }; PR_STATIC_CALLBACK(const void *) txAttributeHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry) { txAttributeHashEntry *e = NS_STATIC_CAST(txAttributeHashEntry *, entry); return e->mAttribute->GetKey(); } PR_STATIC_CALLBACK(PLDHashNumber) txAttributeHashHashKey(PLDHashTable *table, const void *key) { const txAttributeNodeKey* attrKey = NS_STATIC_CAST(const txAttributeNodeKey *, key); return attrKey->GetHash(); } PR_STATIC_CALLBACK(PRBool) txAttributeHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry, const void *key) { const txAttributeHashEntry *e = NS_STATIC_CAST(const txAttributeHashEntry *, entry); const txAttributeNodeKey* key1 = e->mAttribute->GetKey(); const txAttributeNodeKey* key2 = NS_STATIC_CAST(const txAttributeNodeKey *, key); return key1->Equals(*key2); } PR_STATIC_CALLBACK(void) txAttributeHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) { txAttributeHashEntry *e = NS_STATIC_CAST(txAttributeHashEntry *, entry); delete e->mAttribute; } /** * Construct a wrapper with the specified Mozilla object. The caller is * responsible for deleting the wrapper object! * * @param aDocument the nsIDOMDocument you want to wrap */ Document::Document(nsIDOMDocument* aDocument) : Node(aDocument, this) { #ifdef DEBUG mInHashTableDeletion = PR_FALSE; #endif static PLDHashTableOps wrapper_hash_table_ops = { PL_DHashAllocTable, PL_DHashFreeTable, txWrapperHashGetKey, PL_DHashVoidPtrKeyStub, txWrapperHashMatchEntry, PL_DHashMoveEntryStub, txWrapperHashClearEntry, PL_DHashFinalizeStub, }; PRBool success = PL_DHashTableInit(&mWrapperHashTable, &wrapper_hash_table_ops, this, sizeof(txWrapperHashEntry), WRAPPER_INITIAL_SIZE); if (success) { txWrapperHashEntry* entry = NS_STATIC_CAST(txWrapperHashEntry *, PL_DHashTableOperate(&mWrapperHashTable, aDocument, PL_DHASH_ADD)); NS_ASSERTION(entry, "Out-of-memory creating an entry."); if (entry && !entry->mWrapper) { entry->mWrapper = this; } } else { mWrapperHashTable.ops = nsnull; } static PLDHashTableOps attribute_hash_table_ops = { PL_DHashAllocTable, PL_DHashFreeTable, txAttributeHashGetKey, txAttributeHashHashKey, txAttributeHashMatchEntry, PL_DHashMoveEntryStub, txAttributeHashClearEntry, PL_DHashFinalizeStub, }; success = PL_DHashTableInit(&mAttributeNodes, &attribute_hash_table_ops, nsnull, sizeof(txAttributeHashEntry), ATTR_INITIAL_SIZE); if (!success) { mAttributeNodes.ops = nsnull; } } /** * Destructor */ Document::~Document() { #ifdef DEBUG mInHashTableDeletion = PR_TRUE; #endif if (mAttributeNodes.ops) { PL_DHashTableFinish(&mAttributeNodes); } if (mWrapperHashTable.ops) { PL_DHashTableFinish(&mWrapperHashTable); } } /** * This macro can be used to declare a createWrapper implementation * for the supplied wrapper class. */ #define IMPL_CREATE_WRAPPER(_txClass) \ IMPL_CREATE_WRAPPER2(_txClass, create##_txClass) /** * This macro can be used to declare a createWrapper implementation * for the supplied wrapper class. The function parameter defines * the function name for the implementation function. */ #define IMPL_CREATE_WRAPPER2(_txClass, _function) \ _txClass* Document::_function(nsIDOM##_txClass* aNsObject) \ { \ NS_ASSERTION(aNsObject, \ "Need a Mozilla object to create a wrapper."); \ if (!mWrapperHashTable.ops) { \ return new _txClass(aNsObject, this); \ } \ \ txWrapperHashEntry* entry = \ NS_STATIC_CAST(txWrapperHashEntry *, \ PL_DHashTableOperate(&mWrapperHashTable, \ aNsObject, \ PL_DHASH_ADD)); \ \ NS_ASSERTION(entry, "Out-of-memory creating an entry."); \ if (!entry) { \ return nsnull; \ } \ if (!entry->mWrapper) { \ entry->mWrapper = new _txClass(aNsObject, this); \ NS_ASSERTION(entry->mWrapper, \ "Out-of-memory creating a wrapper."); \ if (!entry->mWrapper) { \ PL_DHashTableRawRemove(&mWrapperHashTable, entry); \ return nsnull; \ } \ } \ return NS_STATIC_CAST(_txClass*, entry->mWrapper); \ } /** * Call nsIDOMDocument::GetDocumentElement to get the document's * DocumentElement. * * @return the document element */ Element* Document::getDocumentElement() { NSI_FROM_TX(Document); nsCOMPtr element; nsDocument->GetDocumentElement(getter_AddRefs(element)); if (!element) { return nsnull; } return createElement(element); } /** * Call nsIDOMDocument::CreateDocumentFragment to create a DocumentFragment. * * @return the DocumentFragment */ Node* Document::createDocumentFragment() { NSI_FROM_TX(Document); nsCOMPtr fragment; nsDocument->CreateDocumentFragment(getter_AddRefs(fragment)); if (!fragment) { return nsnull; } return createNode(fragment); } /** * Call nsIDOMDocument::GetElementById to get Element with ID. * * @param aID the name of ID referencing the element * * @return the Element */ Element* Document::getElementById(const String aID) { NSI_FROM_TX(Document); nsCOMPtr element; nsDocument->GetElementById(aID, getter_AddRefs(element)); if (!element) { return nsnull; } return createElement(element); } /** * Create a wrapper for a nsIDOMElement, reuses an existing wrapper if possible. * * @param aElement the nsIDOMElement you want to wrap * * @return the Element */ IMPL_CREATE_WRAPPER(Element) /** * Call nsIDOMDocument::CreateElementNS to create an Element. * * @param aNamespaceURI the URI of the namespace for the element * @param aTagName the name of the element you want to create * * @return the Element */ Element* Document::createElementNS(const String& aNamespaceURI, const String& aTagName) { NSI_FROM_TX(Document); nsCOMPtr element; nsDocument->CreateElementNS(aNamespaceURI, aTagName, getter_AddRefs(element)); if (!element) { return nsnull; } return createElement(element); } /** * Create a wrapper for a nsIDOMAttr, reuses an existing wrapper if possible. * * @param aAttr the nsIDOMAttr you want to wrap * * @return the Attribute */ Attr* Document::createAttribute(nsIDOMAttr* aAttr) { NS_ASSERTION(aAttr, "Need a Mozilla object to create a wrapper."); if (!aAttr) { return nsnull; } nsCOMPtr owner; aAttr->GetOwnerElement(getter_AddRefs(owner)); nsCOMPtr parent = do_QueryInterface(owner); nsAutoString nameString; aAttr->GetLocalName(nameString); nsCOMPtr localName = do_GetAtom(nameString); nsAutoString ns; aAttr->GetNamespaceURI(ns); PRInt32 namespaceID = kNameSpaceID_None; if (!ns.IsEmpty()) { NS_ASSERTION(gTxNameSpaceManager, "No namespace manager"); if (gTxNameSpaceManager) { gTxNameSpaceManager->GetNameSpaceID(ns, namespaceID); } } if (!mAttributeNodes.ops) { return nsnull; } txAttributeNodeKey hashKey(parent, localName, namespaceID); txAttributeHashEntry* entry = NS_STATIC_CAST(txAttributeHashEntry *, PL_DHashTableOperate(&mAttributeNodes, &hashKey, PL_DHASH_ADD)); NS_ASSERTION(entry, "Out-of-memory creating an entry."); if (!entry) { return nsnull; } if (!entry->mAttribute) { entry->mAttribute = new Attr(aAttr, this); NS_ASSERTION(entry->mAttribute, "Out-of-memory creating an attribute wrapper."); if (!entry->mAttribute) { PL_DHashTableRawRemove(&mAttributeNodes, entry); return nsnull; } } return entry->mAttribute; } /** * Call nsIDOMDocument::CreateTextNode to create a Text node. * * @param aData the data of the text node you want to create * * @return the Text node */ Node* Document::createTextNode(const String& aData) { NSI_FROM_TX(Document); nsCOMPtr text; nsDocument->CreateTextNode(aData, getter_AddRefs(text)); nsCOMPtr node = do_QueryInterface(text); if (!node) { return nsnull; } return createNode(node); } /** * Call nsIDOMDocument::CreateComment to create a Comment node. * * @param aData the data of the comment node you want to create * * @return the Comment node */ Node* Document::createComment(const String& aData) { NSI_FROM_TX(Document); nsCOMPtr comment; nsDocument->CreateComment(aData, getter_AddRefs(comment)); nsCOMPtr node = do_QueryInterface(comment); if (!node) { return nsnull; } return createNode(node); } /** * Call nsIDOMDocument::CreateProcessingInstruction to create a * ProcessingInstruction. * * @param aTarget the target of the ProcessingInstruction you want to create * @param aData the data of the ProcessingInstruction you want to create * * @return the ProcessingInstruction node */ ProcessingInstruction* Document::createProcessingInstruction( const String& aTarget, const String& aData) { NSI_FROM_TX(Document); nsCOMPtr pi; nsDocument->CreateProcessingInstruction(aTarget, aData, getter_AddRefs(pi)); if (!pi) { return nsnull; } return createProcessingInstruction(pi); } /** * Create a wrapper for a nsIDOMProcessingInstruction, reuses an existing * wrapper if possible. * * @param aPi the nsIDOMProcessingInstruction you want to wrap * * @return the ProcessingInstruction node */ IMPL_CREATE_WRAPPER(ProcessingInstruction) /** * Create a wrapper for a nsIDOMNode, reuses an existing wrapper if possible. * * @param aNode the nsIDOMNode you want to wrap * * @return the Node */ IMPL_CREATE_WRAPPER(Node) /** * Create a wrapper for a nsIDOMNamedNodeMap, reuses an existing wrapper if * possible. * * @param aMap the nsIDOMNamedNodeMap you want to wrap * * @return the NamedNodeMap */ IMPL_CREATE_WRAPPER(NamedNodeMap) /** * Create a wrapper for a nsIDOMNode, reuses an existing wrapper if possible. * This function creates the right kind of wrapper depending on the type of * the node. * * @param aNode the nsIDOMNode you want to wrap * * @return the Node */ Node* Document::createWrapper(nsIDOMNode* aNode) { NS_ASSERTION(aNode, "Need a Mozilla object to create a wrapper."); PRUint16 nodeType = 0; aNode->GetNodeType(&nodeType); // Look up wrapper in the hash, except for attribute nodes since // they're in a separate attribute hash. Let them be handled in // createAttribute by falling through to the switch below. if (nodeType != nsIDOMNode::ATTRIBUTE_NODE && mWrapperHashTable.ops) { txWrapperHashEntry* entry = NS_STATIC_CAST(txWrapperHashEntry *, PL_DHashTableOperate(&mWrapperHashTable, aNode, PL_DHASH_LOOKUP)); if (entry->mWrapper) { return NS_STATIC_CAST(Node*, entry->mWrapper); } } switch (nodeType) { case nsIDOMNode::ELEMENT_NODE: { nsIDOMElement* element; CallQueryInterface(aNode, &element); Element* txElement = createElement(element); NS_RELEASE(element); return txElement; } case nsIDOMNode::ATTRIBUTE_NODE: { nsIDOMAttr* attr; CallQueryInterface(aNode, &attr); Attr* txAttr = createAttribute(attr); NS_RELEASE(attr); return txAttr; } case nsIDOMNode::CDATA_SECTION_NODE: case nsIDOMNode::COMMENT_NODE: case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: case nsIDOMNode::DOCUMENT_TYPE_NODE: case nsIDOMNode::ENTITY_REFERENCE_NODE: case nsIDOMNode::ENTITY_NODE: case nsIDOMNode::NOTATION_NODE: case nsIDOMNode::TEXT_NODE: { return createNode(aNode); } case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: { nsIDOMProcessingInstruction* pi; CallQueryInterface(aNode, &pi); ProcessingInstruction* txPi = createProcessingInstruction(pi); NS_RELEASE(pi); return txPi; } case nsIDOMNode::DOCUMENT_NODE: { if (aNode == mMozObject) { return this; } NS_ASSERTION(0, "We don't support creating new documents."); return nsnull; } default: { NS_WARNING("Don't know nodetype. This could be a failure."); return createNode(aNode); } } } PRInt32 Document::namespaceURIToID(const String& aNamespaceURI) { PRInt32 namesspaceID = kNameSpaceID_Unknown; if (gTxNameSpaceManager) { gTxNameSpaceManager->RegisterNameSpace(aNamespaceURI, namesspaceID); } return namesspaceID; } void Document::namespaceIDToURI(PRInt32 aNamespaceID, String& aNamespaceURI) { if (gTxNameSpaceManager) { gTxNameSpaceManager->GetNameSpaceURI(aNamespaceID, aNamespaceURI); } } #ifdef DEBUG PRBool MozillaObjectWrapper::inHashTableDeletion() { return mOwnerDocument->mInHashTableDeletion; } #endif