gecko-dev/content/base/src/nsGenericDOMDataNode.cpp

1467 lines
36 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
1998-09-06 00:16:36 +00:00
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (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/NPL/
1998-09-06 00:16:36 +00:00
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
1998-09-06 00:16:36 +00:00
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
1998-09-06 04:16:22 +00:00
#include "nsGenericDOMDataNode.h"
#include "nsGenericElement.h"
#include "nsIDocument.h"
1998-09-06 00:16:36 +00:00
#include "nsIEventListenerManager.h"
#include "nsIDOMRange.h"
#include "nsIDOMDocument.h"
#include "nsRange.h"
#include "nsTextContentChangeData.h"
#include "nsISelection.h"
#include "nsISelectionPrivate.h"
#include "nsReadableUtils.h"
2000-11-27 07:55:20 +00:00
#include "nsMutationEvent.h"
#include "nsINameSpaceManager.h"
1998-09-06 00:16:36 +00:00
#include "nsIPrivateDOMEvent.h"
#include "nsIDOMEvent.h"
#include "nsIDOMText.h"
#include "nsCOMPtr.h"
1998-09-06 00:16:36 +00:00
#include "pldhash.h"
#include "prprf.h"
static PLDHashTable *gEventListenerHash = nsnull;
static PLDHashTable *gRangeListsHash = nsnull;
class EventListenerManagerMapEntry : public PLDHashEntryHdr
{
public:
void *mKey; // must be first, to look like PLDHashEntryStub
nsIEventListenerManager *mListenerManager;
};
class RangeListMapEntry : public PLDHashEntryHdr
{
public:
void *mKey; // must be first, to look like PLDHashEntryStub
nsVoidArray *mRangeList;
};
PR_STATIC_CALLBACK(PLDHashOperator)
EventListenerHashTeardownEnumFunc(PLDHashTable *table, PLDHashEntryHdr *hdr,
PRUint32 number, void *arg)
{
EventListenerManagerMapEntry *entry =
NS_STATIC_CAST(EventListenerManagerMapEntry *, hdr);
NS_RELEASE(entry->mListenerManager);
return PL_DHASH_NEXT;
}
PR_STATIC_CALLBACK(PLDHashOperator)
RangeListsHashTeardownEnumFunc(PLDHashTable *table, PLDHashEntryHdr *hdr,
PRUint32 number, void *arg)
{
RangeListMapEntry *entry = NS_STATIC_CAST(RangeListMapEntry *, hdr);
delete entry->mRangeList;
return PL_DHASH_NEXT;
}
// static
void
nsGenericDOMDataNode::Shutdown()
{
if (gEventListenerHash) {
PL_DHashTableEnumerate(gEventListenerHash,
EventListenerHashTeardownEnumFunc, nsnull);
PL_DHashTableDestroy(gEventListenerHash);
gEventListenerHash = nsnull;
}
if (gRangeListsHash) {
PL_DHashTableEnumerate(gRangeListsHash, RangeListsHashTeardownEnumFunc,
nsnull);
PL_DHashTableDestroy(gRangeListsHash);
gRangeListsHash = nsnull;
}
}
1998-09-06 00:16:36 +00:00
//----------------------------------------------------------------------
1998-09-06 04:16:22 +00:00
nsGenericDOMDataNode::nsGenericDOMDataNode()
: mText(), mDocument(nsnull), mParentPtrBits(0)
1998-09-06 00:16:36 +00:00
{
NS_INIT_ISUPPORTS();
1998-09-06 00:16:36 +00:00
}
1998-09-06 04:16:22 +00:00
nsGenericDOMDataNode::~nsGenericDOMDataNode()
1998-09-06 00:16:36 +00:00
{
if (HasEventListenerManager() && gEventListenerHash) {
EventListenerManagerMapEntry *entry =
NS_STATIC_CAST(EventListenerManagerMapEntry*,
PL_DHashTableOperate(gEventListenerHash, this,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
entry->mListenerManager->SetListenerTarget(nsnull);
NS_RELEASE(entry->mListenerManager);
PL_DHashTableRawRemove(gEventListenerHash, entry);
}
}
if (HasRangeList() && gRangeListsHash) {
RangeListMapEntry *entry =
NS_STATIC_CAST(RangeListMapEntry*,
PL_DHashTableOperate(gRangeListsHash, this,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
NS_ASSERTION(entry->mRangeList, "No range list in entry!");
delete entry->mRangeList;
PL_DHashTableRawRemove(gRangeListsHash, entry);
}
}
1998-09-06 00:16:36 +00:00
}
NS_IMPL_ADDREF(nsGenericDOMDataNode)
NS_IMPL_RELEASE(nsGenericDOMDataNode)
NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
if (aIID.Equals(NS_GET_IID(nsIDOMEventReceiver)) ||
aIID.Equals(NS_GET_IID(nsIDOMEventTarget))) {
foundInterface = NS_STATIC_CAST(nsIDOMEventReceiver *,
nsDOMEventRTTearoff::Create(this));
NS_ENSURE_TRUE(foundInterface, NS_ERROR_OUT_OF_MEMORY);
} else
NS_INTERFACE_MAP_ENTRY(nsIContent)
// No nsITextContent since all subclasses might not want that.
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, nsNode3Tearoff(this))
NS_INTERFACE_MAP_END
1998-09-06 00:16:36 +00:00
nsresult
nsGenericDOMDataNode::GetNodeValue(nsAWritableString& aNodeValue)
1998-09-06 00:16:36 +00:00
{
return GetData(aNodeValue);
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::SetNodeValue(const nsAReadableString& aNodeValue)
1998-09-06 00:16:36 +00:00
{
return SetData(aNodeValue);
1998-09-06 00:16:36 +00:00
}
nsresult
1998-09-06 04:16:22 +00:00
nsGenericDOMDataNode::GetParentNode(nsIDOMNode** aParentNode)
1998-09-06 00:16:36 +00:00
{
nsresult res = NS_OK;
nsIContent *parent_weak = GetParentWeak();
if (parent_weak) {
res = CallQueryInterface(parent_weak, aParentNode);
} else if (mDocument) {
// If we don't have a parent, but we're in the document, we must
// be the root node of the document. The DOM says that the root
// is the document.
res = CallQueryInterface(mDocument, aParentNode);
} else {
*aParentNode = nsnull;
1998-09-06 00:16:36 +00:00
}
NS_ASSERTION(NS_OK == res, "Must be a DOM Node");
return res;
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::GetPreviousSibling(nsIDOMNode** aPrevSibling)
1998-09-06 00:16:36 +00:00
{
nsCOMPtr<nsIContent> sibling;
nsresult rv = NS_OK;
nsIContent *parent_weak = GetParentWeak();
if (parent_weak) {
1998-09-06 00:16:36 +00:00
PRInt32 pos;
parent_weak->IndexOf(this, pos);
if (pos > -1 ) {
parent_weak->ChildAt(--pos, *getter_AddRefs(sibling));
1998-09-06 00:16:36 +00:00
}
} else if (mDocument) {
// Nodes that are just below the document (their parent is the
// document) need to go to the document to find their next sibling.
PRInt32 pos;
mDocument->IndexOf(this, pos);
if (pos > -1 ) {
mDocument->ChildAt(--pos, *getter_AddRefs(sibling));
}
}
if (sibling) {
rv = CallQueryInterface(sibling, aPrevSibling);
NS_ASSERTION(rv == NS_OK, "Must be a DOM Node");
} else {
*aPrevSibling = nsnull;
}
return rv;
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::GetNextSibling(nsIDOMNode** aNextSibling)
1998-09-06 00:16:36 +00:00
{
nsCOMPtr<nsIContent> sibling;
nsresult rv = NS_OK;
nsIContent *parent_weak = GetParentWeak();
if (parent_weak) {
1998-09-06 00:16:36 +00:00
PRInt32 pos;
parent_weak->IndexOf(this, pos);
1998-09-06 00:16:36 +00:00
if (pos > -1 ) {
parent_weak->ChildAt(++pos, *getter_AddRefs(sibling));
1998-09-06 00:16:36 +00:00
}
}
else if (mDocument) {
// Nodes that are just below the document (their parent is the
// document) need to go to the document to find their next sibling.
PRInt32 pos;
mDocument->IndexOf(this, pos);
if (pos > -1 ) {
mDocument->ChildAt(++pos, *getter_AddRefs(sibling));
}
}
if (sibling) {
rv = CallQueryInterface(sibling, aNextSibling);
NS_ASSERTION(rv == NS_OK, "Must be a DOM Node");
} else {
*aNextSibling = nsnull;
}
return rv;
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::GetChildNodes(nsIDOMNodeList** aChildNodes)
{
// XXX Since we believe this won't be done very often, we won't
// burn another slot in the data node and just create a new
// (empty) childNodes list every time we're asked.
nsChildContentList* list = new nsChildContentList(nsnull);
if (!list) {
return NS_ERROR_OUT_OF_MEMORY;
}
return CallQueryInterface(list, aChildNodes);
}
nsresult
nsGenericDOMDataNode::GetOwnerDocument(nsIDOMDocument** aOwnerDocument)
{
// XXX Actually the owner document is the document in whose context
// the node has been created. We should be able to get at it
// whether or not we are attached to the document.
if (mDocument) {
return CallQueryInterface(mDocument, aOwnerDocument);
}
*aOwnerDocument = nsnull;
return NS_OK;
}
nsresult
nsGenericDOMDataNode::GetNamespaceURI(nsAWritableString& aNamespaceURI)
{
SetDOMStringToNull(aNamespaceURI);
return NS_OK;
}
nsresult
nsGenericDOMDataNode::GetPrefix(nsAWritableString& aPrefix)
{
SetDOMStringToNull(aPrefix);
return NS_OK;
}
nsresult
nsGenericDOMDataNode::SetPrefix(const nsAReadableString& aPrefix)
{
return NS_ERROR_DOM_NAMESPACE_ERR;
}
nsresult
nsGenericDOMDataNode::GetLocalName(nsAWritableString& aLocalName)
{
SetDOMStringToNull(aLocalName);
return NS_OK;
}
nsresult
nsGenericDOMDataNode::Normalize()
{
return NS_OK;
}
nsresult
nsGenericDOMDataNode::IsSupported(const nsAReadableString& aFeature,
const nsAReadableString& aVersion,
PRBool* aReturn)
{
return nsGenericElement::InternalIsSupported(aFeature, aVersion, aReturn);
}
nsresult
nsGenericDOMDataNode::GetBaseURI(nsAWritableString& aURI)
{
aURI.Truncate();
nsresult rv = NS_OK;
// DOM Data Node inherits the base from its parent element/document
nsCOMPtr<nsIDOM3Node> node;
nsIContent *parent_weak = GetParentWeak();
if (parent_weak) {
node = do_QueryInterface(parent_weak);
} else if (mDocument) {
node = do_QueryInterface(mDocument);
}
if (node) {
rv = node->GetBaseURI(aURI);
}
return rv;
}
nsresult
nsGenericDOMDataNode::LookupNamespacePrefix(const nsAReadableString& aNamespaceURI,
nsAWritableString& aPrefix)
{
aPrefix.Truncate();
nsIContent *parent_weak = GetParentWeak();
// DOM Data Node passes the query on to its parent
nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(parent_weak));
if (node) {
return node->LookupNamespacePrefix(aNamespaceURI, aPrefix);
}
return NS_OK;
}
nsresult
nsGenericDOMDataNode::LookupNamespaceURI(const nsAReadableString& aNamespacePrefix,
nsAWritableString& aNamespaceURI)
{
aNamespaceURI.Truncate();
nsIContent *parent_weak = GetParentWeak();
// DOM Data Node passes the query on to its parent
nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(parent_weak));
if (node) {
return node->LookupNamespaceURI(aNamespacePrefix, aNamespaceURI);
}
return NS_OK;
}
1998-09-06 00:16:36 +00:00
//----------------------------------------------------------------------
// Implementation of nsIDOMCharacterData
1998-09-06 00:16:36 +00:00
nsresult
nsGenericDOMDataNode::GetData(nsAWritableString& aData)
1998-09-06 00:16:36 +00:00
{
if (mText.Is2b()) {
aData.Assign(mText.Get2b(), mText.GetLength());
} else {
// Must use Substring() since nsDependentCString() requires null
// terminated strings.
const char *data = mText.Get1b();
CopyASCIItoUCS2(Substring(data, data + mText.GetLength()), aData);
}
1998-09-06 00:16:36 +00:00
return NS_OK;
}
nsresult
nsGenericDOMDataNode::SetData(const nsAReadableString& aData)
1998-09-06 00:16:36 +00:00
{
// inform any enclosed ranges of change
// we can lie and say we are deleting all the text, since in a total
// text replacement we should just collapse all the ranges.
if (HasRangeList()) {
nsRange::TextOwnerChanged(this, 0, mText.GetLength(), 0);
}
nsCOMPtr<nsITextContent> textContent = do_QueryInterface(this);
return SetText(aData, PR_TRUE);
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::GetLength(PRUint32* aLength)
1998-09-06 00:16:36 +00:00
{
*aLength = mText.GetLength();
1998-09-06 00:16:36 +00:00
return NS_OK;
}
nsresult
nsGenericDOMDataNode::SubstringData(PRUint32 aStart, PRUint32 aCount,
nsAWritableString& aReturn)
1998-09-06 00:16:36 +00:00
{
aReturn.Truncate();
// XXX add <0 checks if types change
PRUint32 textLength = PRUint32( mText.GetLength() );
if (aStart > textLength) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
1998-09-06 00:16:36 +00:00
}
PRUint32 amount = aCount;
if (amount > textLength - aStart) {
amount = textLength - aStart;
}
if (mText.Is2b()) {
aReturn.Assign(mText.Get2b() + aStart, amount);
} else {
// Must use Substring() since nsDependentCString() requires null
// terminated strings.
const char *data = mText.Get1b() + aStart;
CopyASCIItoUCS2(Substring(data, data + amount), aReturn);
}
1998-09-06 00:16:36 +00:00
return NS_OK;
}
//----------------------------------------------------------------------
nsresult
nsGenericDOMDataNode::AppendData(const nsAReadableString& aData)
1998-09-06 00:16:36 +00:00
{
#if 1
nsresult rv = NS_OK;
nsAutoString old_data;
mText.AppendTo(old_data);
rv = SetText(old_data + aData, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
// Trigger a reflow
if (mDocument) {
// This seems bad, why do we need to *allocate* a
// nsTextContentChangeData thingy here? Could it not be on the stack?
nsTextContentChangeData* tccd = nsnull;
rv = NS_NewTextContentChangeData(&tccd);
if (NS_SUCCEEDED(rv)) {
tccd->SetData(nsITextContentChangeData::Append, old_data.Length(),
aData.Length());
rv = mDocument->ContentChanged(this, tccd);
NS_RELEASE(tccd);
} else {
rv = mDocument->ContentChanged(this, nsnull);
}
}
return rv;
#else
return ReplaceData(mText.GetLength(), 0, aData);
#endif
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::InsertData(PRUint32 aOffset,
const nsAReadableString& aData)
1998-09-06 00:16:36 +00:00
{
return ReplaceData(aOffset, 0, aData);
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::DeleteData(PRUint32 aOffset, PRUint32 aCount)
1998-09-06 00:16:36 +00:00
{
nsAutoString empty;
return ReplaceData(aOffset, aCount, empty);
1998-09-06 00:16:36 +00:00
}
nsresult
nsGenericDOMDataNode::ReplaceData(PRUint32 aOffset, PRUint32 aCount,
const nsAReadableString& aData)
1998-09-06 00:16:36 +00:00
{
nsresult result = NS_OK;
1998-09-06 00:16:36 +00:00
// sanitize arguments
PRUint32 textLength = mText.GetLength();
if (aOffset > textLength) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
1998-09-06 00:16:36 +00:00
}
// Allocate new buffer
PRUint32 endOffset = aOffset + aCount;
if (endOffset > textLength) {
aCount = textLength - aOffset;
endOffset = textLength;
1998-09-06 00:16:36 +00:00
}
PRInt32 dataLength = aData.Length();
PRInt32 newLength = textLength - aCount + dataLength;
PRUnichar* to = new PRUnichar[newLength + 1];
if (!to) {
1998-09-06 00:16:36 +00:00
return NS_ERROR_OUT_OF_MEMORY;
}
// inform any enclosed ranges of change
if (HasRangeList()) {
nsRange::TextOwnerChanged(this, aOffset, endOffset, dataLength);
}
1998-09-06 00:16:36 +00:00
// Copy over appropriate data
if (0 != aOffset) {
mText.CopyTo(to, 0, aOffset);
1998-09-06 00:16:36 +00:00
}
if (0 != dataLength) {
CopyUnicodeTo(aData, 0, to+aOffset, dataLength);
1998-09-06 00:16:36 +00:00
}
if (endOffset != textLength) {
mText.CopyTo(to + aOffset + dataLength, endOffset, textLength - endOffset);
1998-09-06 00:16:36 +00:00
}
1999-08-28 05:12:11 +00:00
// Null terminate the new buffer...
to[newLength] = (PRUnichar)0;
result = SetText(to, newLength, PR_TRUE);
delete [] to;
1998-09-06 00:16:36 +00:00
return result;
1998-09-06 00:16:36 +00:00
}
//----------------------------------------------------------------------
NS_IMETHODIMP
nsGenericDOMDataNode::GetListenerManager(nsIEventListenerManager** aResult)
1998-09-06 00:16:36 +00:00
{
nsCOMPtr<nsIEventListenerManager> listener_manager;
LookupListenerManager(getter_AddRefs(listener_manager));
if (listener_manager) {
*aResult = listener_manager;
NS_ADDREF(*aResult);
1998-09-06 00:16:36 +00:00
return NS_OK;
}
if (!gEventListenerHash) {
gEventListenerHash =
PL_NewDHashTable(PL_DHashGetStubOps(), nsnull,
sizeof(EventListenerManagerMapEntry), 16);
if (!gEventListenerHash) {
return NS_ERROR_OUT_OF_MEMORY;
}
1998-09-06 00:16:36 +00:00
}
nsresult rv = NS_NewEventListenerManager(aResult);
NS_ENSURE_SUCCESS(rv, rv);
// Add a mapping to the hash table
EventListenerManagerMapEntry *entry =
NS_STATIC_CAST(EventListenerManagerMapEntry *,
PL_DHashTableOperate(gEventListenerHash, this,
PL_DHASH_ADD));
entry->mKey = this;
entry->mListenerManager = *aResult;
NS_ADDREF(entry->mListenerManager);
entry->mListenerManager->SetListenerTarget(this);
SetHasEventListenerManager(PR_TRUE);
return NS_OK;
1998-09-06 00:16:36 +00:00
}
//----------------------------------------------------------------------
// Implementation of nsIContent
#ifdef DEBUG
1998-09-06 00:16:36 +00:00
void
nsGenericDOMDataNode::ToCString(nsAWritableString& aBuf, PRInt32 aOffset,
1998-09-06 00:16:36 +00:00
PRInt32 aLen) const
{
if (mText.Is2b()) {
const PRUnichar* cp = mText.Get2b() + aOffset;
const PRUnichar* end = cp + aLen;
while (cp < end) {
PRUnichar ch = *cp++;
if (ch == '\r') {
aBuf.Append(NS_LITERAL_STRING("\\r"));
} else if (ch == '\n') {
aBuf.Append(NS_LITERAL_STRING("\\n"));
} else if (ch == '\t') {
aBuf.Append(NS_LITERAL_STRING("\\t"));
} else if ((ch < ' ') || (ch >= 127)) {
char buf[10];
PR_snprintf(buf, sizeof(buf), "\\u%04x", ch);
aBuf.Append(NS_ConvertASCIItoUCS2(buf));
} else {
aBuf.Append(ch);
}
}
} else {
unsigned char* cp = (unsigned char*)mText.Get1b() + aOffset;
const unsigned char* end = cp + aLen;
while (cp < end) {
PRUnichar ch = *cp++;
if (ch == '\r') {
aBuf.Append(NS_LITERAL_STRING("\\r"));
} else if (ch == '\n') {
aBuf.Append(NS_LITERAL_STRING("\\n"));
} else if (ch == '\t') {
aBuf.Append(NS_LITERAL_STRING("\\t"));
} else if ((ch < ' ') || (ch >= 127)) {
char buf[10];
PR_snprintf(buf, sizeof(buf), "\\u%04x", ch);
aBuf.Append(NS_ConvertASCIItoUCS2(buf));
} else {
aBuf.Append(ch);
}
1998-09-06 00:16:36 +00:00
}
}
}
#endif
1998-09-06 00:16:36 +00:00
NS_IMETHODIMP
1998-09-06 04:16:22 +00:00
nsGenericDOMDataNode::GetDocument(nsIDocument*& aResult) const
1998-09-06 00:16:36 +00:00
{
aResult = mDocument;
NS_IF_ADDREF(mDocument);
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetDocument(nsIDocument* aDocument, PRBool aDeep,
PRBool aCompileEventHandlers)
{
1998-09-06 00:16:36 +00:00
mDocument = aDocument;
#ifdef IBMBIDI
if (mDocument && mText.IsBidi()) {
mDocument->SetBidiEnabled(PR_TRUE);
}
#endif
1998-09-06 00:16:36 +00:00
return NS_OK;
}
NS_IMETHODIMP
1998-09-06 04:16:22 +00:00
nsGenericDOMDataNode::GetParent(nsIContent*& aResult) const
1998-09-06 00:16:36 +00:00
{
aResult = GetParentWeak();
NS_IF_ADDREF(aResult);
1998-09-06 00:16:36 +00:00
return NS_OK;;
}
NS_IMETHODIMP
1998-09-06 04:16:22 +00:00
nsGenericDOMDataNode::SetParent(nsIContent* aParent)
1998-09-06 00:16:36 +00:00
{
PtrBits new_bits = NS_REINTERPRET_CAST(PtrBits, aParent);
new_bits |= mParentPtrBits & PARENT_BIT_MASK;
mParentPtrBits = new_bits;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetNameSpaceID(PRInt32& aID) const
{
aID = kNameSpaceID_None;
1998-09-06 00:16:36 +00:00
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::NormalizeAttrString(const nsAReadableString& aStr,
nsINodeInfo*& aNodeInfo)
{
aNodeInfo = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttr,
const nsAReadableString& aValue, PRBool aNotify)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetAttr(nsINodeInfo *aNodeInfo,
const nsAReadableString& aValue, PRBool aNotify)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttr,
PRBool aNotify)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetAttr(PRInt32 aNameSpaceID, nsIAtom *aAttr,
nsAWritableString& aResult) const
{
aResult.Truncate();
return NS_CONTENT_ATTR_NOT_THERE;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetAttr(PRInt32 aNameSpaceID, nsIAtom *aAttr,
nsIAtom*& aPrefix,
nsAWritableString& aResult) const
{
aPrefix = nsnull;
aResult.Truncate();
return NS_CONTENT_ATTR_NOT_THERE;
}
NS_IMETHODIMP_(PRBool)
nsGenericDOMDataNode::HasAttr(PRInt32 aNameSpaceID, nsIAtom *aAttribute) const
{
return PR_FALSE;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetAttrNameAt(PRInt32 aIndex, PRInt32& aNameSpaceID,
nsIAtom*& aName, nsIAtom*& aPrefix) const
{
aNameSpaceID = kNameSpaceID_None;
aName = nsnull;
aPrefix = nsnull;
return NS_ERROR_ILLEGAL_VALUE;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetAttrCount(PRInt32& aResult) const
{
aResult = 0;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::HandleDOMEvent(nsIPresContext* aPresContext,
nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
1998-09-06 00:16:36 +00:00
PRUint32 aFlags,
nsEventStatus* aEventStatus)
1998-09-06 00:16:36 +00:00
{
nsresult ret = NS_OK;
nsIDOMEvent* domEvent = nsnull;
PRBool externalDOMEvent = PR_FALSE;
if (NS_EVENT_FLAG_INIT & aFlags) {
if (!aDOMEvent) {
aDOMEvent = &domEvent;
} else {
externalDOMEvent = PR_TRUE;
}
aEvent->flags = aFlags;
aFlags &= ~(NS_EVENT_FLAG_CANT_BUBBLE | NS_EVENT_FLAG_CANT_CANCEL);
}
nsIContent *parent_weak = GetParentWeak();
//Capturing stage evaluation
if (NS_EVENT_FLAG_BUBBLE != aFlags) {
//Initiate capturing phase. Special case first call to document
if (parent_weak) {
parent_weak->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
NS_EVENT_FLAG_CAPTURE, aEventStatus);
} else if (mDocument) {
ret = mDocument->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
NS_EVENT_FLAG_CAPTURE, aEventStatus);
}
}
nsCOMPtr<nsIEventListenerManager> listener_manager;
LookupListenerManager(getter_AddRefs(listener_manager));
1998-09-06 00:16:36 +00:00
//Local handling stage
if (listener_manager && !(aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) &&
!(NS_EVENT_FLAG_BUBBLE & aFlags &&
NS_EVENT_FLAG_CANT_BUBBLE & aEvent->flags)
&& !(aEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH)) {
aEvent->flags |= aFlags;
listener_manager->HandleEvent(aPresContext, aEvent, aDOMEvent, nsnull,
aFlags, aEventStatus);
aEvent->flags &= ~aFlags;
1998-09-06 00:16:36 +00:00
}
//Bubbling stage
if (NS_EVENT_FLAG_CAPTURE != aFlags && parent_weak) {
ret = parent_weak->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
NS_EVENT_FLAG_BUBBLE, aEventStatus);
1998-09-06 00:16:36 +00:00
}
if (NS_EVENT_FLAG_INIT & aFlags) {
1998-09-06 00:16:36 +00:00
// We're leaving the DOM event loop so if we created a DOM event,
// release here.
if (!externalDOMEvent && *aDOMEvent) {
1998-09-06 00:16:36 +00:00
if (0 != (*aDOMEvent)->Release()) {
// Okay, so someone in the DOM loop (a listener, JS object)
// still has a ref to the DOM Event but the internal data
// hasn't been malloc'd. Force a copy of the data here so the
// DOM Event is still valid.
nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
do_QueryInterface(*aDOMEvent);
if (privateEvent) {
1998-09-06 00:16:36 +00:00
privateEvent->DuplicatePrivateData();
}
}
}
1998-09-06 00:16:36 +00:00
aDOMEvent = nsnull;
}
1998-09-06 00:16:36 +00:00
return ret;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetContentID(PRUint32* aID)
{
*aID = 0;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetContentID(PRUint32 aID)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetNodeInfo(nsINodeInfo*& aResult) const
{
aResult = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::CanContainChildren(PRBool& aResult) const
{
aResult = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::ChildCount(PRInt32& aResult) const
{
aResult = 0;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::ChildAt(PRInt32 aIndex, nsIContent*& aResult) const
{
aResult = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::IndexOf(nsIContent* aPossibleChild,
PRInt32& aResult) const
{
aResult = -1;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::InsertChildAt(nsIContent* aKid, PRInt32 aIndex,
PRBool aNotify, PRBool aDeepSetDocument)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex,
PRBool aNotify, PRBool aDeepSetDocument)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::AppendChildTo(nsIContent* aKid, PRBool aNotify,
PRBool aDeepSetDocument)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::RemoveChildAt(PRInt32 aIndex, PRBool aNotify)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::RangeAdd(nsIDOMRange* aRange)
{
// lazy allocation of range list
if (!gRangeListsHash) {
gRangeListsHash = PL_NewDHashTable(PL_DHashGetStubOps(), nsnull,
sizeof(RangeListMapEntry), 16);
if (!gRangeListsHash) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
nsVoidArray *range_list = LookupRangeList();
if (!range_list) {
range_list = new nsAutoVoidArray();
NS_ENSURE_TRUE(range_list, NS_ERROR_OUT_OF_MEMORY);
// Add a mapping to the hash table
RangeListMapEntry *entry =
NS_STATIC_CAST(RangeListMapEntry *,
PL_DHashTableOperate(gRangeListsHash, this,
PL_DHASH_ADD));
entry->mKey = this;
entry->mRangeList = range_list;
SetHasRangeList(PR_TRUE);
} else {
// Make sure we don't add a range that is already
// in the list!
PRInt32 i = range_list->IndexOf(aRange);
if (i >= 0) {
// Range is already in the list, so there is nothing to do!
return NS_OK;
}
}
// dont need to addref - this call is made by the range object itself
PRBool rv = range_list->AppendElement(aRange);
return rv ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsGenericDOMDataNode::RangeRemove(nsIDOMRange* aRange)
{
RangeListMapEntry *entry = nsnull;
if (HasRangeList()) {
entry = NS_STATIC_CAST(RangeListMapEntry*,
PL_DHashTableOperate(gRangeListsHash, this,
PL_DHASH_LOOKUP));
}
if (entry && PL_DHASH_ENTRY_IS_BUSY(entry)) {
// dont need to release - this call is made by the range object
// itself
PRBool rv = entry->mRangeList->RemoveElement(aRange);
1998-12-18 02:51:34 +00:00
if (rv) {
if (entry->mRangeList->Count() == 0) {
delete entry->mRangeList;
PL_DHashTableRawRemove(gRangeListsHash, entry);
SetHasRangeList(PR_FALSE);
1998-12-18 02:51:34 +00:00
}
1998-12-18 02:51:34 +00:00
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}
1998-12-18 02:51:34 +00:00
NS_IMETHODIMP
1998-12-18 02:51:34 +00:00
nsGenericDOMDataNode::GetRangeList(nsVoidArray*& aResult) const
{
aResult = LookupRangeList();
1998-12-18 02:51:34 +00:00
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetFocus(nsIPresContext* aPresContext)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::RemoveFocus(nsIPresContext* aPresContext)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetBindingParent(nsIContent** aContent)
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetBindingParent(nsIContent* aParent)
{
return NS_OK;
}
NS_IMETHODIMP_(PRBool)
nsGenericDOMDataNode::IsContentOfType(PRUint32 aFlags)
{
return PR_FALSE;
}
#ifdef DEBUG
NS_IMETHODIMP
nsGenericDOMDataNode::List(FILE* out, PRInt32 aIndent) const
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::DumpContent(FILE* out, PRInt32 aIndent,
PRBool aDumpAll) const
{
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const
1999-10-05 23:47:46 +00:00
{
if (!aResult) {
return NS_ERROR_NULL_POINTER;
}
PRUint32 sum = sizeof(*this);
sum += mText.GetLength() * (mText.Is2b() ? sizeof(PRUnichar) : sizeof(char));
1999-10-05 23:47:46 +00:00
*aResult = sum;
return NS_OK;
}
#endif
//----------------------------------------------------------------------
// Implementation of the nsIDOMText interface
nsresult
nsGenericDOMDataNode::SplitText(PRUint32 aOffset, nsIDOMText** aReturn)
{
nsresult rv = NS_OK;
nsAutoString cutText;
PRUint32 length;
GetLength(&length);
if (aOffset > length) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
rv = SubstringData(aOffset, length - aOffset, cutText);
if (NS_FAILED(rv)) {
return rv;
}
rv = DeleteData(aOffset, length - aOffset);
if (NS_FAILED(rv)) {
return rv;
}
/*
* Use CloneContent() for creating the new node so that the new node is of
* same class as this node!
*/
nsCOMPtr<nsITextContent> newContent;
rv = CloneContent(PR_FALSE, getter_AddRefs(newContent));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(newContent, &rv);
if (NS_FAILED(rv)) {
return rv;
}
rv = newNode->SetNodeValue(cutText);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIContent> parentNode;
GetParent(*getter_AddRefs(parentNode));
if (parentNode) {
PRInt32 index;
rv = parentNode->IndexOf(this, index);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIContent> content(do_QueryInterface(newNode));
rv = parentNode->InsertChildAt(content, index+1, PR_TRUE, PR_FALSE);
}
}
return newNode->QueryInterface(NS_GET_IID(nsIDOMText), (void**)aReturn);
}
//----------------------------------------------------------------------
// Implementation of the nsITextContent interface
NS_IMETHODIMP
nsGenericDOMDataNode::GetText(const nsTextFragment** aFragmentsResult)
{
*aFragmentsResult = &mText;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::GetTextLength(PRInt32* aLengthResult)
{
if (!aLengthResult) {
return NS_ERROR_NULL_POINTER;
}
*aLengthResult = mText.GetLength();
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::CopyText(nsAWritableString& aResult)
{
if (mText.Is2b()) {
aResult.Assign(mText.Get2b(), mText.GetLength());
} else {
// Must use Substring() since nsDependentCString() requires null
// terminated strings.
const char *data = mText.Get1b();
CopyASCIItoUCS2(Substring(data, data + mText.GetLength()), aResult);
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetText(const PRUnichar* aBuffer,
PRInt32 aLength,
PRBool aNotify)
{
NS_PRECONDITION((aLength >= 0) && aBuffer, "bad args");
if (aLength < 0) {
return NS_ERROR_ILLEGAL_VALUE;
}
if (!aBuffer) {
return NS_ERROR_NULL_POINTER;
}
if (aNotify && mDocument) {
mDocument->BeginUpdate();
}
mText.SetTo(aBuffer, aLength);
#ifdef IBMBIDI
SetBidiStatus();
#endif // IBMBIDI
if (mDocument && nsGenericElement::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED)) {
nsCOMPtr<nsIDOMEventTarget> node(do_QueryInterface(this));
2000-11-27 07:55:20 +00:00
nsMutationEvent mutation;
mutation.eventStructType = NS_MUTATION_EVENT;
mutation.message = NS_MUTATION_CHARACTERDATAMODIFIED;
mutation.mTarget = node;
// XXX Handle the setting of prevValue!
nsAutoString newVal(aBuffer);
if (!newVal.IsEmpty())
mutation.mNewAttrValue = getter_AddRefs(NS_NewAtom(newVal));
nsEventStatus status = nsEventStatus_eIgnore;
2001-04-04 04:30:03 +00:00
HandleDOMEvent(nsnull, &mutation, nsnull,
NS_EVENT_FLAG_INIT, &status);
2000-11-27 07:55:20 +00:00
}
// Trigger a reflow
if (aNotify && mDocument) {
mDocument->ContentChanged(this, nsnull);
mDocument->EndUpdate();
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetText(const char* aBuffer, PRInt32 aLength,
PRBool aNotify)
{
NS_PRECONDITION((aLength >= 0) && aBuffer, "bad args");
if (aLength < 0) {
return NS_ERROR_ILLEGAL_VALUE;
}
if (!aBuffer) {
return NS_ERROR_NULL_POINTER;
}
if (aNotify && mDocument) {
mDocument->BeginUpdate();
}
mText.SetTo(aBuffer, aLength);
if (mDocument && nsGenericElement::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED)) {
nsCOMPtr<nsIDOMEventTarget> node(do_QueryInterface(this));
2000-11-27 07:55:20 +00:00
nsMutationEvent mutation;
mutation.eventStructType = NS_MUTATION_EVENT;
mutation.message = NS_MUTATION_CHARACTERDATAMODIFIED;
mutation.mTarget = node;
// XXX Handle the setting of prevValue!
nsAutoString newVal; newVal.AssignWithConversion(aBuffer);
if (!newVal.IsEmpty())
mutation.mNewAttrValue = getter_AddRefs(NS_NewAtom(newVal));
nsEventStatus status = nsEventStatus_eIgnore;
2001-04-04 04:30:03 +00:00
HandleDOMEvent(nsnull, &mutation, nsnull,
NS_EVENT_FLAG_INIT, &status);
2000-11-27 07:55:20 +00:00
}
// Trigger a reflow
if (aNotify && mDocument) {
mDocument->ContentChanged(this, nsnull);
mDocument->EndUpdate();
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::SetText(const nsAReadableString& aStr,
PRBool aNotify)
{
if (aNotify && mDocument) {
mDocument->BeginUpdate();
}
mText = aStr;
#ifdef IBMBIDI
SetBidiStatus();
#endif // IBMBIDI
if (mDocument && nsGenericElement::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED)) {
nsCOMPtr<nsIDOMEventTarget> node(do_QueryInterface(this));
2001-04-04 04:30:03 +00:00
nsMutationEvent mutation;
mutation.eventStructType = NS_MUTATION_EVENT;
mutation.message = NS_MUTATION_CHARACTERDATAMODIFIED;
mutation.mTarget = node;
// XXX Handle the setting of prevValue!
nsAutoString newVal(aStr);
if (!newVal.IsEmpty())
mutation.mNewAttrValue = getter_AddRefs(NS_NewAtom(newVal));
nsEventStatus status = nsEventStatus_eIgnore;
HandleDOMEvent(nsnull, &mutation, nsnull,
NS_EVENT_FLAG_INIT, &status);
}
// Trigger a reflow
if (aNotify && mDocument) {
mDocument->ContentChanged(this, nsnull);
mDocument->EndUpdate();
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::IsOnlyWhitespace(PRBool* aResult)
{
nsTextFragment& frag = mText;
if (frag.Is2b()) {
const PRUnichar* cp = frag.Get2b();
const PRUnichar* end = cp + frag.GetLength();
while (cp < end) {
PRUnichar ch = *cp++;
if (!XP_IS_SPACE(ch)) {
*aResult = PR_FALSE;
return NS_OK;
}
}
} else {
const char* cp = frag.Get1b();
const char* end = cp + frag.GetLength();
while (cp < end) {
PRUnichar ch = PRUnichar(*(unsigned char*)cp);
++cp;
if (!XP_IS_SPACE(ch)) {
*aResult = PR_FALSE;
return NS_OK;
}
}
}
*aResult = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsGenericDOMDataNode::CloneContent(PRBool aCloneText, nsITextContent** aClone)
{
NS_ERROR("Override me!");
*aClone = nsnull;
return NS_ERROR_NOT_IMPLEMENTED;
}
void
nsGenericDOMDataNode::LookupListenerManager(nsIEventListenerManager **aListenerManager) const
{
*aListenerManager = nsnull;
if (!HasEventListenerManager()) {
return;
}
EventListenerManagerMapEntry *entry =
NS_STATIC_CAST(EventListenerManagerMapEntry*,
PL_DHashTableOperate(gEventListenerHash, this,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
*aListenerManager = entry->mListenerManager;
NS_ADDREF(*aListenerManager);
}
}
nsVoidArray *
nsGenericDOMDataNode::LookupRangeList() const
{
if (!HasRangeList()) {
return nsnull;
}
RangeListMapEntry *entry =
NS_STATIC_CAST(RangeListMapEntry*,
PL_DHashTableOperate(gRangeListsHash, this,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
return entry->mRangeList;
}
return nsnull;
}
#ifdef IBMBIDI
void nsGenericDOMDataNode::SetBidiStatus()
{
if (mDocument) {
PRBool isBidiDocument = PR_FALSE;
mDocument->GetBidiEnabled(&isBidiDocument);
if (isBidiDocument) {
// OK, we already know it's Bidi, so we won't test again
return;
}
}
mText.SetBidiFlag();
if (mDocument && mText.IsBidi()) {
mDocument->SetBidiEnabled(PR_TRUE);
}
}
#endif // IBMBIDI