590 lines
18 KiB
C++
590 lines
18 KiB
C++
/* -*- 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 <peter.vanderbeken@pandora.be>
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* 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<nsIDOMElement> 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<nsIDOMDocumentFragment> 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<nsIDOMElement> 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<nsIDOMElement> 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<nsIDOMElement> owner;
|
|
aAttr->GetOwnerElement(getter_AddRefs(owner));
|
|
nsCOMPtr<nsIContent> parent = do_QueryInterface(owner);
|
|
|
|
nsAutoString nameString;
|
|
aAttr->GetLocalName(nameString);
|
|
nsCOMPtr<nsIAtom> 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<nsIDOMText> text;
|
|
nsDocument->CreateTextNode(aData, getter_AddRefs(text));
|
|
nsCOMPtr<nsIDOMNode> 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<nsIDOMComment> comment;
|
|
nsDocument->CreateComment(aData, getter_AddRefs(comment));
|
|
nsCOMPtr<nsIDOMNode> 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<nsIDOMProcessingInstruction> 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
|