4394 lines
127 KiB
C++
4394 lines
127 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=2 sw=2 et tw=78: */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla 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/MPL/
|
|
*
|
|
* 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.
|
|
*
|
|
* 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 of 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 MPL, 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 MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
#include "nsGenericElement.h"
|
|
|
|
#include "nsDOMAttribute.h"
|
|
#include "nsDOMAttributeMap.h"
|
|
#include "nsIAtom.h"
|
|
#include "nsINodeInfo.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDocumentEncoder.h"
|
|
#include "nsIDOMNodeList.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMRange.h"
|
|
#include "nsIDOMText.h"
|
|
#include "nsIDOMEventReceiver.h"
|
|
#include "nsITextContent.h"
|
|
#include "nsIContentIterator.h"
|
|
#include "nsRange.h"
|
|
#include "nsIEventListenerManager.h"
|
|
#include "nsILinkHandler.h"
|
|
#include "nsIScriptGlobalObject.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsIURL.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsIFrame.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsIView.h"
|
|
#include "nsIViewManager.h"
|
|
#include "nsString.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsIEventStateManager.h"
|
|
#include "nsIDOMEvent.h"
|
|
#include "nsIPrivateDOMEvent.h"
|
|
#include "nsDOMCID.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIDOMCSSStyleDeclaration.h"
|
|
#include "nsDOMCSSDeclaration.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsContentList.h"
|
|
#include "nsDOMError.h"
|
|
#include "nsDOMString.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsIDOMMutationEvent.h"
|
|
#include "nsMutationEvent.h"
|
|
|
|
#include "nsIBindingManager.h"
|
|
#include "nsXBLBinding.h"
|
|
#include "nsIDOMCSSStyleDeclaration.h"
|
|
#include "nsIDOMViewCSS.h"
|
|
#include "nsIXBLService.h"
|
|
#include "nsPIDOMWindow.h"
|
|
#include "nsIBoxObject.h"
|
|
#include "nsPIBoxObject.h"
|
|
#include "nsIDOMNSDocument.h"
|
|
|
|
#include "nsLayoutAtoms.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIJSContextStack.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIDOMEventListener.h"
|
|
|
|
#include "nsIWebNavigation.h"
|
|
#include "nsIBaseWindow.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
#include "nsIDOMXPathEvaluator.h"
|
|
#include "nsNodeInfoManager.h"
|
|
#include "nsICategoryManager.h"
|
|
#include "nsIDOMNSFeatureFactory.h"
|
|
#include "nsIDOMDocumentType.h"
|
|
|
|
|
|
/**
|
|
* Most of the implementation of the nsIContent InsertChildAt method. Shared
|
|
* by nsDocument for insertions via DOM methods. When called from
|
|
* nsDocument, aParent will be null.
|
|
*
|
|
* @param aKid The child to insert.
|
|
* @param aIndex The index to insert at.
|
|
* @param aNotify Whether to notify.
|
|
* @param aParent The parent to use for the new child.
|
|
* @param aDocument The document to use for the new child.
|
|
* Must be non-null if aParent is null and must match
|
|
* aParent->GetCurrentDoc() if aParent is not null.
|
|
* @param aChildArray The child array to work with
|
|
*/
|
|
static nsresult
|
|
doInsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify,
|
|
nsIContent* aParent, nsIDocument* aDocument,
|
|
nsAttrAndChildArray& aChildArray);
|
|
|
|
/**
|
|
* Most of the implementation of the nsIContent RemoveChildAt method. Shared
|
|
* by nsDocument for removals via DOM methods, sometimes. When called from
|
|
* nsDocument, aParent will be null.
|
|
*
|
|
* @param aIndex The index to remove at.
|
|
* @param aNotify Whether to notify.
|
|
* @param aKid The kid at aIndex. Must not be null.
|
|
* @param aParent The parent we're removing from.
|
|
* @param aDocument The document we're removing from.
|
|
* Must be non-null if aParent is null and must match
|
|
* aParent->GetCurrentDoc() if aParent is not null.
|
|
* @param aChildArray The child array to work with
|
|
*/
|
|
extern nsresult
|
|
doRemoveChildAt(PRUint32 aIndex, PRBool aNotify, nsIContent* aKid,
|
|
nsIContent* aParent, nsIDocument* aDocument,
|
|
nsAttrAndChildArray& aChildArray);
|
|
|
|
#ifdef MOZ_SVG
|
|
PRBool NS_SVG_TestFeature(const nsAString &fstr);
|
|
#endif /* MOZ_SVG */
|
|
|
|
#ifdef DEBUG_waterson
|
|
|
|
/**
|
|
* List a content tree to stdout. Meant to be called from gdb.
|
|
*/
|
|
void
|
|
DebugListContentTree(nsIContent* aElement)
|
|
{
|
|
aElement->List(stdout, 0);
|
|
printf("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
PLDHashTable nsGenericElement::sRangeListsHash;
|
|
PLDHashTable nsGenericElement::sEventListenerManagersHash;
|
|
PRInt32 nsIContent::sTabFocusModel = eTabFocus_any;
|
|
PRBool nsIContent::sTabFocusModelAppliesToXUL = PR_FALSE;
|
|
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
|
|
//----------------------------------------------------------------------
|
|
|
|
nsChildContentList::nsChildContentList(nsIContent *aContent)
|
|
{
|
|
// This reference is not reference-counted. The content
|
|
// object tells us when its about to go away.
|
|
mContent = aContent;
|
|
}
|
|
|
|
nsChildContentList::~nsChildContentList()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsChildContentList::GetLength(PRUint32* aLength)
|
|
{
|
|
if (mContent) {
|
|
*aLength = mContent->GetChildCount();
|
|
} else {
|
|
*aLength = 0;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsChildContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
|
|
{
|
|
if (mContent) {
|
|
nsIContent *content = mContent->GetChildAt(aIndex);
|
|
if (content) {
|
|
return CallQueryInterface(content, aReturn);
|
|
}
|
|
}
|
|
|
|
*aReturn = nsnull;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsChildContentList::DropReference()
|
|
{
|
|
mContent = nsnull;
|
|
}
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsNode3Tearoff)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOM3Node)
|
|
NS_INTERFACE_MAP_END_AGGREGATED(mContent)
|
|
|
|
NS_IMPL_ADDREF(nsNode3Tearoff)
|
|
NS_IMPL_RELEASE(nsNode3Tearoff)
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::GetBaseURI(nsAString& aURI)
|
|
{
|
|
nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
|
|
nsCAutoString spec;
|
|
|
|
if (baseURI) {
|
|
baseURI->GetSpec(spec);
|
|
}
|
|
|
|
CopyUTF8toUTF16(spec, aURI);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::GetTextContent(nsAString &aTextContent)
|
|
{
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
|
|
NS_ASSERTION(node, "We have an nsIContent which doesn't support nsIDOMNode");
|
|
|
|
PRUint16 nodeType;
|
|
node->GetNodeType(&nodeType);
|
|
if (nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE ||
|
|
nodeType == nsIDOMNode::NOTATION_NODE) {
|
|
SetDOMStringToNull(aTextContent);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
if (nodeType == nsIDOMNode::TEXT_NODE ||
|
|
nodeType == nsIDOMNode::CDATA_SECTION_NODE ||
|
|
nodeType == nsIDOMNode::COMMENT_NODE ||
|
|
nodeType == nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
|
|
return node->GetNodeValue(aTextContent);
|
|
}
|
|
|
|
return GetTextContent(mContent, aTextContent);
|
|
}
|
|
|
|
// static
|
|
nsresult
|
|
nsNode3Tearoff::GetTextContent(nsIContent *aContent,
|
|
nsAString &aTextContent)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aContent);
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
NS_NewContentIterator(getter_AddRefs(iter));
|
|
iter->Init(aContent);
|
|
|
|
nsString tempString;
|
|
aTextContent.Truncate();
|
|
while (!iter->IsDone()) {
|
|
nsIContent *content = iter->GetCurrentNode();
|
|
if (content->IsContentOfType(nsIContent::eTEXT)) {
|
|
nsCOMPtr<nsITextContent> textContent(do_QueryInterface(iter->GetCurrentNode()));
|
|
if (textContent)
|
|
textContent->AppendTextTo(aTextContent);
|
|
}
|
|
iter->Next();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::SetTextContent(const nsAString &aTextContent)
|
|
{
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
|
|
NS_ASSERTION(node, "We have an nsIContent which doesn't support nsIDOMNode");
|
|
|
|
PRUint16 nodeType;
|
|
node->GetNodeType(&nodeType);
|
|
if (nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE ||
|
|
nodeType == nsIDOMNode::NOTATION_NODE) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (nodeType == nsIDOMNode::TEXT_NODE ||
|
|
nodeType == nsIDOMNode::CDATA_SECTION_NODE ||
|
|
nodeType == nsIDOMNode::COMMENT_NODE ||
|
|
nodeType == nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
|
|
return node->SetNodeValue(aTextContent);
|
|
}
|
|
|
|
return SetTextContent(mContent, aTextContent);
|
|
}
|
|
|
|
// static
|
|
nsresult
|
|
nsNode3Tearoff::SetTextContent(nsIContent* aContent,
|
|
const nsAString &aTextContent)
|
|
{
|
|
PRUint32 childCount = aContent->GetChildCount();
|
|
|
|
// i is unsigned, so i >= is always true
|
|
for (PRUint32 i = childCount; i-- != 0; ) {
|
|
aContent->RemoveChildAt(i, PR_TRUE);
|
|
}
|
|
|
|
if (!aTextContent.IsEmpty()) {
|
|
nsCOMPtr<nsITextContent> textContent;
|
|
nsresult rv = NS_NewTextNode(getter_AddRefs(textContent),
|
|
aContent->GetNodeInfo()->NodeInfoManager());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
textContent->SetText(aTextContent, PR_TRUE);
|
|
|
|
aContent->AppendChildTo(textContent, PR_TRUE);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::CompareDocumentPosition(nsIDOMNode* aOther,
|
|
PRUint16* aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aOther);
|
|
NS_PRECONDITION(aReturn, "Must have an out parameter");
|
|
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
|
|
if (node == aOther) {
|
|
// If the two nodes being compared are the same node,
|
|
// then no flags are set on the return.
|
|
*aReturn = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
PRUint16 mask = 0;
|
|
|
|
// If the other node is an attribute, document, or document fragment,
|
|
// we can find the position easier by comparing this node relative to
|
|
// the other node, and then reversing positions.
|
|
PRUint16 otherType = 0;
|
|
aOther->GetNodeType(&otherType);
|
|
if (otherType == nsIDOMNode::ATTRIBUTE_NODE ||
|
|
otherType == nsIDOMNode::DOCUMENT_NODE ||
|
|
otherType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
|
PRUint16 otherMask = 0;
|
|
nsCOMPtr<nsIDOM3Node> other(do_QueryInterface(aOther));
|
|
other->CompareDocumentPosition(node, &otherMask);
|
|
|
|
*aReturn = nsContentUtils::ReverseDocumentPosition(otherMask);
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
PRUint16 nodeType = 0;
|
|
node->GetNodeType(&nodeType);
|
|
|
|
if (nodeType == nsIDOMNode::ENTITY_NODE ||
|
|
nodeType == nsIDOMNode::NOTATION_NODE) {
|
|
NS_NOTYETIMPLEMENTED("Entities and Notations are not fully supported yet");
|
|
}
|
|
else {
|
|
NS_ASSERTION((nodeType == nsIDOMNode::ELEMENT_NODE ||
|
|
nodeType == nsIDOMNode::TEXT_NODE ||
|
|
nodeType == nsIDOMNode::CDATA_SECTION_NODE ||
|
|
nodeType == nsIDOMNode::ENTITY_REFERENCE_NODE ||
|
|
nodeType == nsIDOMNode::PROCESSING_INSTRUCTION_NODE ||
|
|
nodeType == nsIDOMNode::COMMENT_NODE),
|
|
"Invalid node type!");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
mask |= nsContentUtils::ComparePositionWithAncestors(node, aOther);
|
|
|
|
*aReturn = mask;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::IsSameNode(nsIDOMNode* aOther,
|
|
PRBool* aReturn)
|
|
{
|
|
PRBool sameNode = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIContent> other(do_QueryInterface(aOther));
|
|
if (mContent == other) {
|
|
sameNode = PR_TRUE;
|
|
}
|
|
|
|
*aReturn = sameNode;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::IsEqualNode(nsIDOMNode* aOther, PRBool* aReturn)
|
|
{
|
|
NS_NOTYETIMPLEMENTED("nsNode3Tearoff::IsEqualNode()");
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::GetFeature(const nsAString& aFeature,
|
|
const nsAString& aVersion,
|
|
nsISupports** aReturn)
|
|
{
|
|
return nsGenericElement::InternalGetFeature(this, aFeature, aVersion, aReturn);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::SetUserData(const nsAString& aKey,
|
|
nsIVariant* aData,
|
|
nsIDOMUserDataHandler* aHandler,
|
|
nsIVariant** aReturn)
|
|
{
|
|
NS_NOTYETIMPLEMENTED("nsNode3Tearoff::SetUserData()");
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::GetUserData(const nsAString& aKey,
|
|
nsIVariant** aReturn)
|
|
{
|
|
NS_NOTYETIMPLEMENTED("nsNode3Tearoff::GetUserData()");
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::LookupPrefix(const nsAString& aNamespaceURI,
|
|
nsAString& aPrefix)
|
|
{
|
|
SetDOMStringToNull(aPrefix);
|
|
|
|
// XXX Waiting for DOM spec to list error codes.
|
|
|
|
// Get the namespace id for the URI
|
|
PRInt32 namespaceId;
|
|
nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI,
|
|
&namespaceId);
|
|
if (namespaceId == kNameSpaceID_Unknown) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIAtom> name, prefix;
|
|
PRInt32 namespace_id;
|
|
nsAutoString ns;
|
|
|
|
// Trace up the content parent chain looking for the namespace
|
|
// declaration that defines the aNamespaceURI namespace. Once found,
|
|
// return the prefix (i.e. the attribute localName).
|
|
for (nsIContent* content = mContent; content;
|
|
content = content->GetParent()) {
|
|
PRUint32 attrCount = content->GetAttrCount();
|
|
|
|
for (PRUint32 i = 0; i < attrCount; ++i) {
|
|
content->GetAttrNameAt(i, &namespace_id, getter_AddRefs(name),
|
|
getter_AddRefs(prefix));
|
|
|
|
if (namespace_id == kNameSpaceID_XMLNS) {
|
|
nsresult rv = content->GetAttr(namespace_id, name, ns);
|
|
|
|
if (rv == NS_CONTENT_ATTR_HAS_VALUE && ns.Equals(aNamespaceURI)) {
|
|
name->ToString(aPrefix);
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::LookupNamespaceURI(const nsAString& aNamespacePrefix,
|
|
nsAString& aNamespaceURI)
|
|
{
|
|
if (NS_FAILED(nsContentUtils::LookupNamespaceURI(mContent,
|
|
aNamespacePrefix,
|
|
aNamespaceURI))) {
|
|
SetDOMStringToNull(aNamespaceURI);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNode3Tearoff::IsDefaultNamespace(const nsAString& aNamespaceURI,
|
|
PRBool* aReturn)
|
|
{
|
|
nsAutoString defaultNamespace;
|
|
LookupNamespaceURI(EmptyString(), defaultNamespace);
|
|
*aReturn = aNamespaceURI.Equals(defaultNamespace);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsDOMEventRTTearoff *
|
|
nsDOMEventRTTearoff::mCachedEventTearoff[NS_EVENT_TEAROFF_CACHE_SIZE];
|
|
|
|
PRUint32 nsDOMEventRTTearoff::mCachedEventTearoffCount = 0;
|
|
|
|
|
|
nsDOMEventRTTearoff::nsDOMEventRTTearoff(nsIContent *aContent)
|
|
: mContent(aContent)
|
|
{
|
|
}
|
|
|
|
nsDOMEventRTTearoff::~nsDOMEventRTTearoff()
|
|
{
|
|
}
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsDOMEventRTTearoff)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
|
|
NS_INTERFACE_MAP_END_AGGREGATED(mContent)
|
|
|
|
NS_IMPL_ADDREF(nsDOMEventRTTearoff)
|
|
NS_IMPL_RELEASE_WITH_DESTROY(nsDOMEventRTTearoff, LastRelease())
|
|
|
|
nsDOMEventRTTearoff *
|
|
nsDOMEventRTTearoff::Create(nsIContent *aContent)
|
|
{
|
|
if (mCachedEventTearoffCount) {
|
|
// We have cached unused instances of this class, return a cached
|
|
// instance in stead of always creating a new one.
|
|
nsDOMEventRTTearoff *tearoff =
|
|
mCachedEventTearoff[--mCachedEventTearoffCount];
|
|
|
|
// Set the back pointer to the content object
|
|
tearoff->mContent = aContent;
|
|
|
|
return tearoff;
|
|
}
|
|
|
|
// The cache is empty, this means we haveto create a new instance.
|
|
return new nsDOMEventRTTearoff(aContent);
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsDOMEventRTTearoff::Shutdown()
|
|
{
|
|
// Clear our cache.
|
|
while (mCachedEventTearoffCount) {
|
|
delete mCachedEventTearoff[--mCachedEventTearoffCount];
|
|
}
|
|
}
|
|
|
|
void
|
|
nsDOMEventRTTearoff::LastRelease()
|
|
{
|
|
if (mCachedEventTearoffCount < NS_EVENT_TEAROFF_CACHE_SIZE) {
|
|
// There's still space in the cache for one more instance, put
|
|
// this instance in the cache in stead of deleting it.
|
|
mCachedEventTearoff[mCachedEventTearoffCount++] = this;
|
|
|
|
// Don't set mContent to null directly since setting mContent to null
|
|
// could result in code that grabs a tearoff from the cache and we don't
|
|
// want to get reused while still being torn down.
|
|
// See bug 330526.
|
|
nsCOMPtr<nsIContent> kungFuDeathGrip;
|
|
kungFuDeathGrip.swap(mContent);
|
|
|
|
// The refcount balancing and destructor re-entrancy protection
|
|
// code in Release() sets mRefCnt to 1 so we have to set it to 0
|
|
// here to prevent leaks
|
|
mRefCnt = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
delete this;
|
|
}
|
|
|
|
nsresult
|
|
nsDOMEventRTTearoff::GetEventReceiver(nsIDOMEventReceiver **aReceiver)
|
|
{
|
|
nsCOMPtr<nsIEventListenerManager> listener_manager;
|
|
nsresult rv = mContent->GetListenerManager(getter_AddRefs(listener_manager));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return CallQueryInterface(listener_manager, aReceiver);
|
|
}
|
|
|
|
nsresult
|
|
nsDOMEventRTTearoff::GetDOM3EventTarget(nsIDOM3EventTarget **aTarget)
|
|
{
|
|
nsCOMPtr<nsIEventListenerManager> listener_manager;
|
|
nsresult rv = mContent->GetListenerManager(getter_AddRefs(listener_manager));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return CallQueryInterface(listener_manager, aTarget);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::AddEventListenerByIID(nsIDOMEventListener *aListener,
|
|
const nsIID& aIID)
|
|
{
|
|
nsCOMPtr<nsIDOMEventReceiver> event_receiver;
|
|
nsresult rv = GetEventReceiver(getter_AddRefs(event_receiver));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return event_receiver->AddEventListenerByIID(aListener, aIID);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::RemoveEventListenerByIID(nsIDOMEventListener *aListener,
|
|
const nsIID& aIID)
|
|
{
|
|
nsCOMPtr<nsIDOMEventReceiver> event_receiver;
|
|
nsresult rv = GetEventReceiver(getter_AddRefs(event_receiver));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return event_receiver->RemoveEventListenerByIID(aListener, aIID);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::GetListenerManager(nsIEventListenerManager** aResult)
|
|
{
|
|
return mContent->GetListenerManager(aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::HandleEvent(nsIDOMEvent *aEvent)
|
|
{
|
|
nsCOMPtr<nsIDOMEventReceiver> event_receiver;
|
|
nsresult rv = GetEventReceiver(getter_AddRefs(event_receiver));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return event_receiver->HandleEvent(aEvent);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::GetSystemEventGroup(nsIDOMEventGroup **aGroup)
|
|
{
|
|
nsCOMPtr<nsIEventListenerManager> manager;
|
|
GetListenerManager(getter_AddRefs(manager));
|
|
|
|
if (!manager) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return manager->GetSystemEventGroupLM(aGroup);
|
|
}
|
|
|
|
// nsIDOMEventTarget
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::AddEventListener(const nsAString& aType,
|
|
nsIDOMEventListener *aListener,
|
|
PRBool useCapture)
|
|
{
|
|
return
|
|
AddEventListener(aType, aListener, useCapture,
|
|
!nsContentUtils::IsChromeDoc(mContent->GetOwnerDoc()));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::RemoveEventListener(const nsAString& type,
|
|
nsIDOMEventListener *listener,
|
|
PRBool useCapture)
|
|
{
|
|
nsCOMPtr<nsIDOMEventReceiver> event_receiver;
|
|
nsresult rv = GetEventReceiver(getter_AddRefs(event_receiver));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return event_receiver->RemoveEventListener(type, listener, useCapture);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::DispatchEvent(nsIDOMEvent *evt, PRBool* _retval)
|
|
{
|
|
nsCOMPtr<nsIDOMEventReceiver> event_receiver;
|
|
nsresult rv = GetEventReceiver(getter_AddRefs(event_receiver));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return event_receiver->DispatchEvent(evt, _retval);
|
|
}
|
|
|
|
// nsIDOM3EventTarget
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::AddGroupedEventListener(const nsAString& aType,
|
|
nsIDOMEventListener *aListener,
|
|
PRBool aUseCapture,
|
|
nsIDOMEventGroup *aEvtGrp)
|
|
{
|
|
nsCOMPtr<nsIDOM3EventTarget> event_target;
|
|
nsresult rv = GetDOM3EventTarget(getter_AddRefs(event_target));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return event_target->AddGroupedEventListener(aType, aListener, aUseCapture,
|
|
aEvtGrp);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::RemoveGroupedEventListener(const nsAString& aType,
|
|
nsIDOMEventListener *aListener,
|
|
PRBool aUseCapture,
|
|
nsIDOMEventGroup *aEvtGrp)
|
|
{
|
|
nsCOMPtr<nsIDOM3EventTarget> event_target;
|
|
nsresult rv = GetDOM3EventTarget(getter_AddRefs(event_target));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return event_target->RemoveGroupedEventListener(aType, aListener,
|
|
aUseCapture, aEvtGrp);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::CanTrigger(const nsAString & type, PRBool *_retval)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::IsRegisteredHere(const nsAString & type, PRBool *_retval)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// nsIDOMNSEventTarget
|
|
NS_IMETHODIMP
|
|
nsDOMEventRTTearoff::AddEventListener(const nsAString& aType,
|
|
nsIDOMEventListener *aListener,
|
|
PRBool aUseCapture,
|
|
PRBool aWantsUntrusted)
|
|
{
|
|
nsCOMPtr<nsIEventListenerManager> listener_manager;
|
|
nsresult rv = mContent->GetListenerManager(getter_AddRefs(listener_manager));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
|
|
|
|
if (aWantsUntrusted) {
|
|
flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
|
|
}
|
|
|
|
return listener_manager->AddEventListenerByType(aListener, aType, flags,
|
|
nsnull);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
PRUint32 nsMutationGuard::sMutationCount = 0;
|
|
|
|
nsDOMSlots::nsDOMSlots(PtrBits aFlags)
|
|
: mFlags(aFlags & ~GENERIC_ELEMENT_CONTENT_ID_MASK),
|
|
mBindingParent(nsnull),
|
|
mContentID(aFlags >> GENERIC_ELEMENT_CONTENT_ID_BITS_OFFSET)
|
|
{
|
|
}
|
|
|
|
nsDOMSlots::~nsDOMSlots()
|
|
{
|
|
if (mChildNodes) {
|
|
mChildNodes->DropReference();
|
|
}
|
|
|
|
if (mStyle) {
|
|
mStyle->DropReference();
|
|
}
|
|
|
|
if (mAttributeMap) {
|
|
mAttributeMap->DropReference();
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
nsDOMSlots::IsEmpty()
|
|
{
|
|
return (!mChildNodes && !mStyle && !mAttributeMap && !mBindingParent &&
|
|
mContentID < GENERIC_ELEMENT_CONTENT_ID_MAX_VALUE);
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(void)
|
|
NopClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
|
|
{
|
|
// Do nothing
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsGenericElement::Shutdown()
|
|
{
|
|
nsDOMEventRTTearoff::Shutdown();
|
|
|
|
if (sRangeListsHash.ops) {
|
|
NS_ASSERTION(sRangeListsHash.entryCount == 0,
|
|
"nsGenericElement's range hash not empty at shutdown!");
|
|
|
|
// We're already being shut down and if there are entries left in
|
|
// this hash at this point it means we leaked nsGenericElements or
|
|
// nsGenericDOMDataNodes. Since we're already partly through the
|
|
// shutdown process it's too late to release what's held on to by
|
|
// this hash (since the teardown code relies on some things being
|
|
// around that aren't around any more) so we rather leak what's
|
|
// already leaked in stead of crashing trying to release what
|
|
// should've been released much earlier on.
|
|
|
|
// Copy the ops out of the hash table
|
|
PLDHashTableOps hash_table_ops = *sRangeListsHash.ops;
|
|
|
|
// Set the clearEntry hook to be a nop
|
|
hash_table_ops.clearEntry = NopClearEntry;
|
|
|
|
// Set the ops in the hash table to be the new ops
|
|
sRangeListsHash.ops = &hash_table_ops;
|
|
|
|
PL_DHashTableFinish(&sRangeListsHash);
|
|
|
|
sRangeListsHash.ops = nsnull;
|
|
}
|
|
|
|
if (sEventListenerManagersHash.ops) {
|
|
NS_ASSERTION(sEventListenerManagersHash.entryCount == 0,
|
|
"nsGenericElement's event listener manager hash not empty "
|
|
"at shutdown!");
|
|
|
|
// See comment above.
|
|
|
|
// However, we have to handle this table differently. If it still
|
|
// has entries, we want to leak it too, so that we can keep it alive
|
|
// in case any elements are destroyed. Because if they are, we need
|
|
// their event listener managers to be destroyed too, or otherwise
|
|
// it could leave dangling references in DOMClassInfo's preserved
|
|
// wrapper table.
|
|
|
|
if (sEventListenerManagersHash.entryCount == 0) {
|
|
PL_DHashTableFinish(&sEventListenerManagersHash);
|
|
sEventListenerManagersHash.ops = nsnull;
|
|
}
|
|
}
|
|
}
|
|
|
|
nsGenericElement::nsGenericElement(nsINodeInfo *aNodeInfo)
|
|
: mNodeInfo(aNodeInfo),
|
|
mFlagsOrSlots(GENERIC_ELEMENT_DOESNT_HAVE_DOMSLOTS)
|
|
{
|
|
NS_ASSERTION(mNodeInfo, "No nsINodeInfo passed to nsGenericElement, "
|
|
"PREPARE TO CRASH!!!");
|
|
}
|
|
|
|
nsGenericElement::~nsGenericElement()
|
|
{
|
|
NS_PRECONDITION(!IsInDoc(),
|
|
"Please remove this from the document properly");
|
|
|
|
// pop any enclosed ranges out
|
|
// nsRange::OwnerGone(mContent); not used for now
|
|
|
|
if (HasRangeList()) {
|
|
#ifdef DEBUG
|
|
{
|
|
RangeListMapEntry *entry =
|
|
NS_STATIC_CAST(RangeListMapEntry *,
|
|
PL_DHashTableOperate(&sRangeListsHash, this,
|
|
PL_DHASH_LOOKUP));
|
|
|
|
if (PL_DHASH_ENTRY_IS_FREE(entry)) {
|
|
NS_ERROR("Huh, our bit says we have a range list, but there's nothing "
|
|
"in the hash!?!!");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
PL_DHashTableOperate(&sRangeListsHash, this, PL_DHASH_REMOVE);
|
|
}
|
|
|
|
if (HasEventListenerManager()) {
|
|
EventListenerManagerMapEntry *entry =
|
|
NS_STATIC_CAST(EventListenerManagerMapEntry *,
|
|
PL_DHashTableOperate(&sEventListenerManagersHash, this,
|
|
PL_DHASH_LOOKUP));
|
|
NS_ASSERTION(!PL_DHASH_ENTRY_IS_FREE(entry),
|
|
"Huh, our bit says we have a listener manager list, "
|
|
"but there's nothing in the hash!?!!");
|
|
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
|
|
nsCOMPtr<nsIEventListenerManager> listenerManager;
|
|
listenerManager.swap(entry->mListenerManager);
|
|
// Remove the entry and *then* do operations that could cause further
|
|
// modification of sEventListenerManagersHash. See bug 334177.
|
|
PL_DHashTableRawRemove(&sEventListenerManagersHash, entry);
|
|
if (listenerManager) {
|
|
listenerManager->Disconnect();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (HasDOMSlots()) {
|
|
nsDOMSlots *slots = GetDOMSlots();
|
|
|
|
delete slots;
|
|
}
|
|
|
|
// No calling GetFlags() beyond this point...
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(PRBool)
|
|
RangeListHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
|
|
const void *key)
|
|
{
|
|
// Initialize the entry with placement new
|
|
new (entry) RangeListMapEntry(key);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(void)
|
|
RangeListHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
|
|
{
|
|
RangeListMapEntry *r = NS_STATIC_CAST(RangeListMapEntry *, entry);
|
|
|
|
// Let the RangeListMapEntry clean itself up...
|
|
r->~RangeListMapEntry();
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(PRBool)
|
|
EventListenerManagerHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
|
|
const void *key)
|
|
{
|
|
// Initialize the entry with placement new
|
|
new (entry) EventListenerManagerMapEntry(key);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(void)
|
|
EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
|
|
{
|
|
EventListenerManagerMapEntry *lm =
|
|
NS_STATIC_CAST(EventListenerManagerMapEntry *, entry);
|
|
|
|
// Let the EventListenerManagerMapEntry clean itself up...
|
|
lm->~EventListenerManagerMapEntry();
|
|
}
|
|
|
|
|
|
// static
|
|
nsresult
|
|
nsGenericElement::InitHashes()
|
|
{
|
|
NS_ASSERTION(sizeof(PtrBits) == sizeof(void *),
|
|
"Eeek! You'll need to adjust the size of PtrBits to the size "
|
|
"of a pointer on your platform.");
|
|
|
|
if (!sRangeListsHash.ops) {
|
|
static PLDHashTableOps hash_table_ops =
|
|
{
|
|
PL_DHashAllocTable,
|
|
PL_DHashFreeTable,
|
|
PL_DHashGetKeyStub,
|
|
PL_DHashVoidPtrKeyStub,
|
|
PL_DHashMatchEntryStub,
|
|
PL_DHashMoveEntryStub,
|
|
RangeListHashClearEntry,
|
|
PL_DHashFinalizeStub,
|
|
RangeListHashInitEntry
|
|
};
|
|
|
|
if (!PL_DHashTableInit(&sRangeListsHash, &hash_table_ops, nsnull,
|
|
sizeof(RangeListMapEntry), 16)) {
|
|
sRangeListsHash.ops = nsnull;
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (!sEventListenerManagersHash.ops) {
|
|
static PLDHashTableOps hash_table_ops =
|
|
{
|
|
PL_DHashAllocTable,
|
|
PL_DHashFreeTable,
|
|
PL_DHashGetKeyStub,
|
|
PL_DHashVoidPtrKeyStub,
|
|
PL_DHashMatchEntryStub,
|
|
PL_DHashMoveEntryStub,
|
|
EventListenerManagerHashClearEntry,
|
|
PL_DHashFinalizeStub,
|
|
EventListenerManagerHashInitEntry
|
|
};
|
|
|
|
if (!PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
|
|
nsnull, sizeof(EventListenerManagerMapEntry), 16)) {
|
|
sEventListenerManagersHash.ops = nsnull;
|
|
|
|
PL_DHashTableFinish(&sRangeListsHash);
|
|
sRangeListsHash.ops = nsnull;
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* During the Mark phase of the GC, we need to mark all of the preserved
|
|
* wrappers that are reachable via DOM APIs. Since reachability for DOM
|
|
* nodes is symmetric, if one DOM node is reachable from another via DOM
|
|
* APIs, then they are in the same strongly connected component.
|
|
* (Strongly connected components are never reachable from each other
|
|
* via DOM APIs.) We can refer to each strongly connected component by
|
|
* walking up to the top of the parent chain. This function finds that
|
|
* root node for any DOM node.
|
|
*/
|
|
nsIDOMGCParticipant*
|
|
nsGenericElement::GetSCCIndex()
|
|
{
|
|
// This is an optimized way of walking nsIDOMNode::GetParentNode to
|
|
// the top of the tree.
|
|
nsCOMPtr<nsIDOMGCParticipant> result = do_QueryInterface(GetCurrentDoc());
|
|
if (!result) {
|
|
nsIContent *top = this;
|
|
while (top->GetParent())
|
|
top = top->GetParent();
|
|
result = do_QueryInterface(top);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
nsGenericElement::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
|
{
|
|
NS_ASSERTION(GetCurrentDoc() == nsnull,
|
|
"shouldn't be an SCC index if we're in a doc");
|
|
|
|
// This node is the root of a subtree that's been removed from the
|
|
// document (since AppendReachableList is only called on SCC index
|
|
// nodes). The document is reachable from it (through
|
|
// .ownerDocument), but it's not reachable from the document.
|
|
nsCOMPtr<nsIDOMGCParticipant> participant = do_QueryInterface(GetOwnerDoc());
|
|
aArray.AppendObject(participant);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetNodeName(nsAString& aNodeName)
|
|
{
|
|
mNodeInfo->GetQualifiedName(aNodeName);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetLocalName(nsAString& aLocalName)
|
|
{
|
|
mNodeInfo->GetLocalName(aLocalName);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetNodeValue(nsAString& aNodeValue)
|
|
{
|
|
SetDOMStringToNull(aNodeValue);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::SetNodeValue(const nsAString& aNodeValue)
|
|
{
|
|
// The DOM spec says that when nodeValue is defined to be null "setting it
|
|
// has no effect", so we don't throw an exception.
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetNodeType(PRUint16* aNodeType)
|
|
{
|
|
*aNodeType = (PRUint16)nsIDOMNode::ELEMENT_NODE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetParentNode(nsIDOMNode** aParentNode)
|
|
{
|
|
nsIContent *parent = GetParent();
|
|
if (parent) {
|
|
return CallQueryInterface(parent, aParentNode);
|
|
}
|
|
|
|
nsIDocument* doc = GetCurrentDoc();
|
|
if (doc) {
|
|
// 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.
|
|
|
|
return CallQueryInterface(doc, aParentNode);
|
|
}
|
|
|
|
*aParentNode = nsnull;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetPreviousSibling(nsIDOMNode** aPrevSibling)
|
|
{
|
|
*aPrevSibling = nsnull;
|
|
|
|
nsIContent *sibling = nsnull;
|
|
nsresult rv = NS_OK;
|
|
|
|
nsIContent *parent = GetParent();
|
|
if (parent) {
|
|
PRInt32 pos = parent->IndexOf(this);
|
|
if (pos > 0 ) {
|
|
sibling = parent->GetChildAt(pos - 1);
|
|
}
|
|
} else {
|
|
nsIDocument* document = GetCurrentDoc();
|
|
if (document) {
|
|
// 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 = document->IndexOf(this);
|
|
if (pos > 0 ) {
|
|
sibling = document->GetChildAt(pos - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sibling) {
|
|
rv = CallQueryInterface(sibling, aPrevSibling);
|
|
NS_ASSERTION(*aPrevSibling, "Must be a DOM Node");
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetNextSibling(nsIDOMNode** aNextSibling)
|
|
{
|
|
*aNextSibling = nsnull;
|
|
|
|
nsIContent *sibling = nsnull;
|
|
nsresult rv = NS_OK;
|
|
|
|
nsIContent *parent = GetParent();
|
|
if (parent) {
|
|
PRInt32 pos = parent->IndexOf(this);
|
|
if (pos > -1 ) {
|
|
sibling = parent->GetChildAt(pos + 1);
|
|
}
|
|
} else {
|
|
nsIDocument* document = GetCurrentDoc();
|
|
if (document) {
|
|
// 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 = document->IndexOf(this);
|
|
if (pos > -1 ) {
|
|
sibling = document->GetChildAt(pos + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sibling) {
|
|
rv = CallQueryInterface(sibling, aNextSibling);
|
|
NS_ASSERTION(*aNextSibling, "Must be a DOM Node");
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetOwnerDocument(nsIDOMDocument** aOwnerDocument)
|
|
{
|
|
nsIDocument *doc = GetOwnerDoc();
|
|
if (doc) {
|
|
return CallQueryInterface(doc, aOwnerDocument);
|
|
}
|
|
|
|
*aOwnerDocument = nsnull;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetNamespaceURI(nsAString& aNamespaceURI)
|
|
{
|
|
return mNodeInfo->GetNamespaceURI(aNamespaceURI);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetPrefix(nsAString& aPrefix)
|
|
{
|
|
mNodeInfo->GetPrefix(aPrefix);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::SetPrefix(const nsAString& aPrefix)
|
|
{
|
|
// XXX: Validate the prefix string!
|
|
|
|
nsCOMPtr<nsIAtom> prefix;
|
|
|
|
if (!aPrefix.IsEmpty()) {
|
|
prefix = do_GetAtom(aPrefix);
|
|
NS_ENSURE_TRUE(prefix, NS_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
|
|
nsCOMPtr<nsINodeInfo> newNodeInfo;
|
|
nsresult rv = nsContentUtils::PrefixChanged(mNodeInfo, prefix,
|
|
getter_AddRefs(newNodeInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mNodeInfo = newNodeInfo;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
extern PRBool gCheckedForXPathDOM;
|
|
extern PRBool gHaveXPathDOM;
|
|
|
|
nsresult
|
|
nsGenericElement::InternalIsSupported(nsISupports* aObject,
|
|
const nsAString& aFeature,
|
|
const nsAString& aVersion,
|
|
PRBool* aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
*aReturn = PR_FALSE;
|
|
|
|
// Convert the incoming UTF16 strings to raw char*'s to save us some
|
|
// code when doing all those string compares.
|
|
NS_ConvertUTF16toUTF8 feature(aFeature);
|
|
NS_ConvertUTF16toUTF8 version(aVersion);
|
|
|
|
const char *f = feature.get();
|
|
const char *v = version.get();
|
|
|
|
if (PL_strcasecmp(f, "XML") == 0 ||
|
|
PL_strcasecmp(f, "HTML") == 0) {
|
|
if (aVersion.IsEmpty() ||
|
|
PL_strcmp(v, "1.0") == 0 ||
|
|
PL_strcmp(v, "2.0") == 0) {
|
|
*aReturn = PR_TRUE;
|
|
}
|
|
} else if (PL_strcasecmp(f, "Views") == 0 ||
|
|
PL_strcasecmp(f, "StyleSheets") == 0 ||
|
|
PL_strcasecmp(f, "Core") == 0 ||
|
|
PL_strcasecmp(f, "CSS") == 0 ||
|
|
PL_strcasecmp(f, "CSS2") == 0 ||
|
|
PL_strcasecmp(f, "Events") == 0 ||
|
|
PL_strcasecmp(f, "UIEvents") == 0 ||
|
|
PL_strcasecmp(f, "MouseEvents") == 0 ||
|
|
// Non-standard!
|
|
PL_strcasecmp(f, "MouseScrollEvents") == 0 ||
|
|
PL_strcasecmp(f, "HTMLEvents") == 0 ||
|
|
PL_strcasecmp(f, "Range") == 0 ||
|
|
PL_strcasecmp(f, "XHTML") == 0) {
|
|
if (aVersion.IsEmpty() ||
|
|
PL_strcmp(v, "2.0") == 0) {
|
|
*aReturn = PR_TRUE;
|
|
}
|
|
} else if ((!gCheckedForXPathDOM || gHaveXPathDOM) &&
|
|
PL_strcasecmp(f, "XPath") == 0 &&
|
|
(aVersion.IsEmpty() ||
|
|
PL_strcmp(v, "3.0") == 0)) {
|
|
if (!gCheckedForXPathDOM) {
|
|
nsCOMPtr<nsIDOMXPathEvaluator> evaluator =
|
|
do_CreateInstance(NS_XPATH_EVALUATOR_CONTRACTID);
|
|
gHaveXPathDOM = (evaluator != nsnull);
|
|
gCheckedForXPathDOM = PR_TRUE;
|
|
}
|
|
|
|
*aReturn = gHaveXPathDOM;
|
|
}
|
|
#ifdef MOZ_SVG
|
|
else if (PL_strcasecmp(f, "SVGEvents") == 0 ||
|
|
PL_strcasecmp(f, "SVGZoomEvents") == 0 ||
|
|
NS_SVG_TestFeature(aFeature)) {
|
|
if (aVersion.IsEmpty() ||
|
|
PL_strcmp(v, "1.0") == 0 ||
|
|
PL_strcmp(v, "1.1") == 0) {
|
|
*aReturn = PR_TRUE;
|
|
}
|
|
}
|
|
#endif /* MOZ_SVG */
|
|
else {
|
|
nsCOMPtr<nsIDOMNSFeatureFactory> factory =
|
|
GetDOMFeatureFactory(aFeature, aVersion);
|
|
|
|
if (factory) {
|
|
factory->HasFeature(aObject, aFeature, aVersion, aReturn);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::InternalGetFeature(nsISupports* aObject,
|
|
const nsAString& aFeature,
|
|
const nsAString& aVersion,
|
|
nsISupports** aReturn)
|
|
{
|
|
*aReturn = nsnull;
|
|
nsCOMPtr<nsIDOMNSFeatureFactory> factory =
|
|
GetDOMFeatureFactory(aFeature, aVersion);
|
|
|
|
if (factory) {
|
|
factory->GetFeature(aObject, aFeature, aVersion, aReturn);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
already_AddRefed<nsIDOMNSFeatureFactory>
|
|
nsGenericElement::GetDOMFeatureFactory(const nsAString& aFeature,
|
|
const nsAString& aVersion)
|
|
{
|
|
nsIDOMNSFeatureFactory *factory = nsnull;
|
|
nsCOMPtr<nsICategoryManager> categoryManager =
|
|
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
|
|
if (categoryManager) {
|
|
nsCAutoString featureCategory(NS_DOMNS_FEATURE_PREFIX);
|
|
AppendUTF16toUTF8(aFeature, featureCategory);
|
|
nsXPIDLCString contractID;
|
|
nsresult rv = categoryManager->GetCategoryEntry(featureCategory.get(),
|
|
NS_ConvertUTF16toUTF8(aVersion).get(),
|
|
getter_Copies(contractID));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
CallGetService(contractID.get(), &factory); // addrefs
|
|
}
|
|
}
|
|
return factory;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::IsSupported(const nsAString& aFeature,
|
|
const nsAString& aVersion,
|
|
PRBool* aReturn)
|
|
{
|
|
return InternalIsSupported(this, aFeature, aVersion, aReturn);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::HasAttributes(PRBool* aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
|
|
*aReturn = GetAttrCount() > 0;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetAttributes(nsIDOMNamedNodeMap** aAttributes)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aAttributes);
|
|
nsDOMSlots *slots = GetDOMSlots();
|
|
|
|
if (!slots) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (!slots->mAttributeMap) {
|
|
slots->mAttributeMap = new nsDOMAttributeMap(this);
|
|
if (!slots->mAttributeMap) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
if (!slots->mAttributeMap->Init()) {
|
|
slots->mAttributeMap = nsnull;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(*aAttributes = slots->mAttributeMap);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetChildNodes(nsIDOMNodeList** aChildNodes)
|
|
{
|
|
nsDOMSlots *slots = GetDOMSlots();
|
|
|
|
if (!slots) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (!slots->mChildNodes) {
|
|
slots->mChildNodes = new nsChildContentList(this);
|
|
if (!slots->mChildNodes) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(*aChildNodes = slots->mChildNodes);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::HasChildNodes(PRBool* aReturn)
|
|
{
|
|
*aReturn = mAttrsAndChildren.ChildCount() > 0;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetFirstChild(nsIDOMNode** aNode)
|
|
{
|
|
nsIContent *child = mAttrsAndChildren.GetSafeChildAt(0);
|
|
if (child) {
|
|
return CallQueryInterface(child, aNode);
|
|
}
|
|
|
|
*aNode = nsnull;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetLastChild(nsIDOMNode** aNode)
|
|
{
|
|
PRUint32 count = mAttrsAndChildren.ChildCount();
|
|
|
|
if (count > 0) {
|
|
return CallQueryInterface(mAttrsAndChildren.ChildAt(count - 1), aNode);
|
|
}
|
|
|
|
*aNode = nsnull;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::GetTagName(nsAString& aTagName)
|
|
{
|
|
mNodeInfo->GetQualifiedName(aTagName);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetAttribute(const nsAString& aName,
|
|
nsAString& aReturn)
|
|
{
|
|
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
|
|
|
|
if (!name) {
|
|
if (mNodeInfo->NamespaceID() == kNameSpaceID_XUL) {
|
|
// XXX should be SetDOMStringToNull(aReturn);
|
|
// See bug 232598
|
|
aReturn.Truncate();
|
|
}
|
|
else {
|
|
SetDOMStringToNull(aReturn);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
GetAttr(name->NamespaceID(), name->LocalName(), aReturn);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::SetAttribute(const nsAString& aName,
|
|
const nsAString& aValue)
|
|
{
|
|
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
|
|
|
|
if (!name) {
|
|
nsresult rv = nsContentUtils::CheckQName(aName, PR_FALSE);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
|
|
NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
return SetAttr(kNameSpaceID_None, nameAtom, aValue, PR_TRUE);
|
|
}
|
|
|
|
return SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
|
|
aValue, PR_TRUE);
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::RemoveAttribute(const nsAString& aName)
|
|
{
|
|
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
|
|
|
|
if (!name) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Hold a strong reference here so that the atom or nodeinfo doesn't go
|
|
// away during UnsetAttr. If it did UnsetAttr would be left with a
|
|
// dangling pointer as argument without knowing it.
|
|
nsAttrName tmp(*name);
|
|
|
|
return UnsetAttr(name->NamespaceID(), name->LocalName(), PR_TRUE);
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetAttributeNode(const nsAString& aName,
|
|
nsIDOMAttr** aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
*aReturn = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNamedNodeMap> map;
|
|
nsresult rv = GetAttributes(getter_AddRefs(map));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
rv = map->GetNamedItem(aName, getter_AddRefs(node));
|
|
|
|
if (NS_SUCCEEDED(rv) && node) {
|
|
rv = CallQueryInterface(node, aReturn);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::SetAttributeNode(nsIDOMAttr* aAttribute,
|
|
nsIDOMAttr** aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
NS_ENSURE_ARG_POINTER(aAttribute);
|
|
|
|
*aReturn = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNamedNodeMap> map;
|
|
nsresult rv = GetAttributes(getter_AddRefs(map));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIDOMNode> returnNode;
|
|
rv = map->SetNamedItem(aAttribute, getter_AddRefs(returnNode));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (returnNode) {
|
|
rv = CallQueryInterface(returnNode, aReturn);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::RemoveAttributeNode(nsIDOMAttr* aAttribute,
|
|
nsIDOMAttr** aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
NS_ENSURE_ARG_POINTER(aAttribute);
|
|
|
|
*aReturn = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNamedNodeMap> map;
|
|
nsresult rv = GetAttributes(getter_AddRefs(map));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString name;
|
|
|
|
rv = aAttribute->GetName(name);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
rv = map->RemoveNamedItem(name, getter_AddRefs(node));
|
|
|
|
if (NS_SUCCEEDED(rv) && node) {
|
|
rv = CallQueryInterface(node, aReturn);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetElementsByTagName(const nsAString& aTagname,
|
|
nsIDOMNodeList** aReturn)
|
|
{
|
|
nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aTagname);
|
|
NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
nsContentList *list = NS_GetContentList(GetCurrentDoc(), nameAtom,
|
|
kNameSpaceID_Unknown, this).get();
|
|
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
// transfer ref to aReturn
|
|
*aReturn = list;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetAttributeNS(const nsAString& aNamespaceURI,
|
|
const nsAString& aLocalName,
|
|
nsAString& aReturn)
|
|
{
|
|
PRInt32 nsid;
|
|
nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI, &nsid);
|
|
|
|
if (nsid == kNameSpaceID_Unknown) {
|
|
// Unknown namespace means no attr...
|
|
|
|
aReturn.Truncate();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
|
|
GetAttr(nsid, name, aReturn);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::SetAttributeNS(const nsAString& aNamespaceURI,
|
|
const nsAString& aQualifiedName,
|
|
const nsAString& aValue)
|
|
{
|
|
nsCOMPtr<nsINodeInfo> ni;
|
|
nsresult rv =
|
|
nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
|
|
mNodeInfo->NodeInfoManager(),
|
|
getter_AddRefs(ni));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
|
|
aValue, PR_TRUE);
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::RemoveAttributeNS(const nsAString& aNamespaceURI,
|
|
const nsAString& aLocalName)
|
|
{
|
|
nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
|
|
PRInt32 nsid;
|
|
|
|
nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI, &nsid);
|
|
|
|
if (nsid == kNameSpaceID_Unknown) {
|
|
// Unknown namespace means no attr...
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoString tmp;
|
|
UnsetAttr(nsid, name, PR_TRUE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetAttributeNodeNS(const nsAString& aNamespaceURI,
|
|
const nsAString& aLocalName,
|
|
nsIDOMAttr** aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
|
|
*aReturn = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNamedNodeMap> map;
|
|
nsresult rv = GetAttributes(getter_AddRefs(map));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
rv = map->GetNamedItemNS(aNamespaceURI, aLocalName, getter_AddRefs(node));
|
|
|
|
if (NS_SUCCEEDED(rv) && node) {
|
|
rv = CallQueryInterface(node, aReturn);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::SetAttributeNodeNS(nsIDOMAttr* aNewAttr,
|
|
nsIDOMAttr** aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
NS_ENSURE_ARG_POINTER(aNewAttr);
|
|
|
|
*aReturn = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNamedNodeMap> map;
|
|
nsresult rv = GetAttributes(getter_AddRefs(map));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIDOMNode> returnNode;
|
|
rv = map->SetNamedItemNS(aNewAttr, getter_AddRefs(returnNode));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (returnNode) {
|
|
rv = CallQueryInterface(returnNode, aReturn);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
|
|
const nsAString& aLocalName,
|
|
nsIDOMNodeList** aReturn)
|
|
{
|
|
PRInt32 nameSpaceId = kNameSpaceID_Unknown;
|
|
|
|
nsContentList *list = nsnull;
|
|
|
|
nsIDocument* document = GetCurrentDoc();
|
|
if (!aNamespaceURI.EqualsLiteral("*")) {
|
|
nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI,
|
|
&nameSpaceId);
|
|
|
|
if (nameSpaceId == kNameSpaceID_Unknown) {
|
|
// Unknown namespace means no matches, we create an empty list...
|
|
list = NS_GetContentList(document, nsnull,
|
|
kNameSpaceID_None, nsnull).get();
|
|
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
}
|
|
|
|
if (!list) {
|
|
nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aLocalName);
|
|
NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
list = NS_GetContentList(document, nameAtom, nameSpaceId, this).get();
|
|
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
|
|
// transfer ref to aReturn
|
|
*aReturn = list;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::HasAttribute(const nsAString& aName, PRBool* aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
|
|
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
|
|
*aReturn = (name != nsnull);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::HasAttributeNS(const nsAString& aNamespaceURI,
|
|
const nsAString& aLocalName,
|
|
PRBool* aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
|
|
PRInt32 nsid;
|
|
nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI, &nsid);
|
|
|
|
if (nsid == kNameSpaceID_Unknown) {
|
|
// Unknown namespace means no attr...
|
|
|
|
*aReturn = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
|
|
*aReturn = HasAttr(nsid, name);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::JoinTextNodes(nsIContent* aFirst,
|
|
nsIContent* aSecond)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIDOMText> firstText(do_QueryInterface(aFirst, &rv));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIDOMText> secondText(do_QueryInterface(aSecond, &rv));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsAutoString str;
|
|
|
|
rv = secondText->GetData(str);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = firstText->AppendData(str);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::Normalize()
|
|
{
|
|
nsresult result = NS_OK;
|
|
PRUint32 index, count = GetChildCount();
|
|
|
|
for (index = 0; (index < count) && (NS_OK == result); index++) {
|
|
nsIContent *child = GetChildAt(index);
|
|
|
|
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(child);
|
|
if (node) {
|
|
PRUint16 nodeType;
|
|
node->GetNodeType(&nodeType);
|
|
|
|
switch (nodeType) {
|
|
case nsIDOMNode::TEXT_NODE:
|
|
|
|
if (index+1 < count) {
|
|
// Get the sibling. If it's also a text node, then
|
|
// remove it from the tree and join the two text
|
|
// nodes.
|
|
nsIContent *sibling = GetChildAt(index + 1);
|
|
|
|
nsCOMPtr<nsIDOMNode> siblingNode = do_QueryInterface(sibling);
|
|
|
|
if (siblingNode) {
|
|
PRUint16 siblingNodeType;
|
|
siblingNode->GetNodeType(&siblingNodeType);
|
|
|
|
if (siblingNodeType == nsIDOMNode::TEXT_NODE) {
|
|
result = RemoveChildAt(index+1, PR_TRUE);
|
|
if (NS_FAILED(result)) {
|
|
return result;
|
|
}
|
|
|
|
result = JoinTextNodes(child, sibling);
|
|
if (NS_FAILED(result)) {
|
|
return result;
|
|
}
|
|
count--;
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case nsIDOMNode::ELEMENT_NODE:
|
|
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(child);
|
|
|
|
if (element) {
|
|
result = element->Normalize();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|
nsIContent* aBindingParent,
|
|
PRBool aCompileEventHandlers)
|
|
{
|
|
NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!");
|
|
// XXXbz XUL elements are confused about their current doc when they're
|
|
// cloned, so we don't assert if aParent is a XUL element and aDocument is
|
|
// null, even if aParent->GetCurrentDoc() is non-null
|
|
// NS_PRECONDITION(!aParent || aDocument == aParent->GetCurrentDoc(),
|
|
// "aDocument must be current doc of aParent");
|
|
NS_PRECONDITION(!aParent ||
|
|
(aParent->IsContentOfType(eXUL) && aDocument == nsnull) ||
|
|
aDocument == aParent->GetCurrentDoc(),
|
|
"aDocument must be current doc of aParent");
|
|
NS_PRECONDITION(!GetCurrentDoc(), "Already have a document. Unbind first!");
|
|
// Note that as we recurse into the kids, they'll have a non-null parent. So
|
|
// only assert if our parent is _changing_ while we have a parent.
|
|
NS_PRECONDITION(!GetParent() || aParent == GetParent(),
|
|
"Already have a parent. Unbind first!");
|
|
NS_PRECONDITION(!GetBindingParent() ||
|
|
aBindingParent == GetBindingParent() ||
|
|
(!aBindingParent && aParent &&
|
|
aParent->GetBindingParent() == GetBindingParent()),
|
|
"Already have a binding parent. Unbind first!");
|
|
|
|
if (!aBindingParent && aParent) {
|
|
aBindingParent = aParent->GetBindingParent();
|
|
}
|
|
|
|
// First set the binding parent
|
|
if (aBindingParent) {
|
|
nsDOMSlots *slots = GetDOMSlots();
|
|
|
|
if (!slots) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
|
|
}
|
|
|
|
// Now set the parent; make sure to preserve the bits we have stashed there
|
|
// Note that checking whether aParent == GetParent() is probably not worth it
|
|
// here.
|
|
PtrBits new_bits = NS_REINTERPRET_CAST(PtrBits, aParent);
|
|
new_bits |= mParentPtrBits & nsIContent::kParentBitMask;
|
|
mParentPtrBits = new_bits;
|
|
|
|
nsresult rv;
|
|
|
|
nsIDocument *oldOwnerDocument = GetOwnerDoc();
|
|
nsIDocument *newOwnerDocument;
|
|
nsNodeInfoManager* nodeInfoManager;
|
|
|
|
// XXXbz sXBL/XBL2 issue!
|
|
|
|
// Finally, set the document
|
|
if (aDocument) {
|
|
// Notify XBL- & nsIAnonymousContentCreator-generated
|
|
// anonymous content that the document is changing.
|
|
// XXXbz ordering issues here? Probably not, since ChangeDocumentFor is
|
|
// just pretty broken anyway.... Need to get it working.
|
|
// XXXbz XBL doesn't handle this (asserts), and we don't really want
|
|
// to be doing this during parsing anyway... sort this out.
|
|
// aDocument->BindingManager()->ChangeDocumentFor(this, nsnull,
|
|
// aDocument);
|
|
|
|
// Being added to a document.
|
|
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
|
|
|
|
newOwnerDocument = aDocument;
|
|
nodeInfoManager = newOwnerDocument->NodeInfoManager();
|
|
} else {
|
|
newOwnerDocument = aParent->GetOwnerDoc();
|
|
nodeInfoManager = aParent->GetNodeInfo()->NodeInfoManager();
|
|
}
|
|
|
|
// Handle a change in our owner document.
|
|
|
|
if (oldOwnerDocument && oldOwnerDocument != newOwnerDocument) {
|
|
// Remove all properties.
|
|
oldOwnerDocument->PropertyTable()->DeleteAllPropertiesFor(this);
|
|
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(this);
|
|
if (domElement) {
|
|
nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(oldOwnerDocument);
|
|
if (nsDoc) {
|
|
nsDoc->SetBoxObjectFor(domElement, nsnull);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mNodeInfo->NodeInfoManager() != nodeInfoManager) {
|
|
nsCOMPtr<nsINodeInfo> newNodeInfo;
|
|
rv = nodeInfoManager->GetNodeInfo(mNodeInfo->NameAtom(),
|
|
mNodeInfo->GetPrefixAtom(),
|
|
mNodeInfo->NamespaceID(),
|
|
getter_AddRefs(newNodeInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
|
|
mNodeInfo.swap(newNodeInfo);
|
|
}
|
|
|
|
// Now recurse into our kids
|
|
PRUint32 i;
|
|
for (i = 0; i < GetChildCount(); ++i) {
|
|
// The child can remove itself from the parent in BindToTree.
|
|
nsCOMPtr<nsIContent> child = mAttrsAndChildren.ChildAt(i);
|
|
rv = child->BindToTree(aDocument, this, aBindingParent,
|
|
aCompileEventHandlers);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// XXXbz script execution during binding can trigger some of these
|
|
// postcondition asserts.... But we do want that, since things will
|
|
// generally be quite broken when that happens.
|
|
NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");
|
|
NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
|
|
NS_POSTCONDITION(aBindingParent == GetBindingParent(),
|
|
"Bound to wrong binding parent");
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsGenericElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
|
|
{
|
|
NS_PRECONDITION(aDeep || (!GetCurrentDoc() && !GetBindingParent()),
|
|
"Shallow unbind won't clear document and binding parent on "
|
|
"kids!");
|
|
// Make sure to unbind this node before doing the kids
|
|
nsIDocument *document = GetCurrentDoc();
|
|
if (document) {
|
|
// Notify XBL- & nsIAnonymousContentCreator-generated
|
|
// anonymous content that the document is changing.
|
|
document->BindingManager()->ChangeDocumentFor(this, document, nsnull);
|
|
|
|
if (HasAttr(kNameSpaceID_XLink, nsHTMLAtoms::href)) {
|
|
document->ForgetLink(this);
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(this);
|
|
|
|
if (domElement) {
|
|
nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(document);
|
|
nsDoc->SetBoxObjectFor(domElement, nsnull);
|
|
}
|
|
}
|
|
|
|
// Unset things in the reverse order from how we set them in BindToTree
|
|
mParentPtrBits &= ~PARENT_BIT_INDOCUMENT;
|
|
|
|
if (aNullParent) {
|
|
// Just mask it out
|
|
mParentPtrBits &= nsIContent::kParentBitMask;
|
|
}
|
|
|
|
nsDOMSlots *slots = GetExistingDOMSlots();
|
|
if (slots) {
|
|
slots->mBindingParent = nsnull;
|
|
}
|
|
|
|
if (aDeep) {
|
|
// Do the kids
|
|
PRUint32 i, n = GetChildCount();
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
// Note that we pass PR_FALSE for aNullParent here, since we don't want
|
|
// the kids to forget us. We _do_ want them to forget their binding
|
|
// parent, though, since this only walks non-anonymous kids.
|
|
mAttrsAndChildren.ChildAt(i)->UnbindFromTree(PR_TRUE, PR_FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
nsGenericElement::IsNativeAnonymous() const
|
|
{
|
|
return !!(GetFlags() & GENERIC_ELEMENT_IS_ANONYMOUS);
|
|
}
|
|
|
|
void
|
|
nsGenericElement::SetNativeAnonymous(PRBool aAnonymous)
|
|
{
|
|
if (aAnonymous) {
|
|
SetFlags(GENERIC_ELEMENT_IS_ANONYMOUS);
|
|
} else {
|
|
UnsetFlags(GENERIC_ELEMENT_IS_ANONYMOUS);
|
|
}
|
|
}
|
|
|
|
PRInt32
|
|
nsGenericElement::GetNameSpaceID() const
|
|
{
|
|
return mNodeInfo->NamespaceID();
|
|
}
|
|
|
|
nsIAtom *
|
|
nsGenericElement::Tag() const
|
|
{
|
|
return mNodeInfo->NameAtom();
|
|
}
|
|
|
|
nsINodeInfo *
|
|
nsGenericElement::GetNodeInfo() const
|
|
{
|
|
return mNodeInfo;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::HandleDOMEvent(nsPresContext* aPresContext,
|
|
nsEvent* aEvent,
|
|
nsIDOMEvent** aDOMEvent,
|
|
PRUint32 aFlags,
|
|
nsEventStatus* aEventStatus)
|
|
{
|
|
// Make sure to tell the event that dispatch has started.
|
|
NS_MARK_EVENT_DISPATCH_STARTED(aEvent);
|
|
|
|
nsresult ret = NS_OK;
|
|
PRBool retarget = PR_FALSE;
|
|
PRBool externalDOMEvent = PR_FALSE;
|
|
nsCOMPtr<nsIDOMEventTarget> oldTarget;
|
|
|
|
nsIDOMEvent* domEvent = nsnull;
|
|
if (NS_EVENT_FLAG_INIT & aFlags) {
|
|
if (aDOMEvent) {
|
|
if (*aDOMEvent) {
|
|
externalDOMEvent = PR_TRUE;
|
|
}
|
|
} else {
|
|
aDOMEvent = &domEvent;
|
|
}
|
|
aEvent->flags |= aFlags;
|
|
aFlags &= ~(NS_EVENT_FLAG_CANT_BUBBLE | NS_EVENT_FLAG_CANT_CANCEL);
|
|
aFlags |= NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE;
|
|
}
|
|
|
|
// Find out whether we're anonymous.
|
|
if (IsNativeAnonymous()) {
|
|
retarget = PR_TRUE;
|
|
} else {
|
|
nsIContent* parent = GetParent();
|
|
if (parent) {
|
|
if (*aDOMEvent) {
|
|
(*aDOMEvent)->GetTarget(getter_AddRefs(oldTarget));
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(oldTarget));
|
|
if (content && content->GetBindingParent() == parent)
|
|
retarget = PR_TRUE;
|
|
} else if (GetBindingParent() == parent) {
|
|
retarget = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for an anonymous parent
|
|
nsCOMPtr<nsIContent> parent;
|
|
nsIDocument* ownerDoc = GetOwnerDoc();
|
|
if (ownerDoc) {
|
|
ownerDoc->BindingManager()->GetInsertionParent(this,
|
|
getter_AddRefs(parent));
|
|
}
|
|
if (!parent) {
|
|
// if we didn't find an anonymous parent, use the explicit one,
|
|
// whether it's null or not...
|
|
parent = GetParent();
|
|
}
|
|
|
|
if (retarget || (parent.get() != GetParent())) {
|
|
if (!*aDOMEvent) {
|
|
// We haven't made a DOMEvent yet. Force making one now.
|
|
nsCOMPtr<nsIEventListenerManager> listenerManager;
|
|
if (NS_FAILED(ret = GetListenerManager(getter_AddRefs(listenerManager)))) {
|
|
return ret;
|
|
}
|
|
nsAutoString empty;
|
|
if (NS_FAILED(ret = listenerManager->CreateEvent(aPresContext, aEvent,
|
|
empty, aDOMEvent)))
|
|
return ret;
|
|
}
|
|
|
|
if (!*aDOMEvent) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
|
|
if (!privateEvent) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
(*aDOMEvent)->GetTarget(getter_AddRefs(oldTarget));
|
|
|
|
PRBool hasOriginal;
|
|
privateEvent->HasOriginalTarget(&hasOriginal);
|
|
|
|
if (!hasOriginal)
|
|
privateEvent->SetOriginalTarget(oldTarget);
|
|
|
|
if (retarget) {
|
|
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetParent());
|
|
privateEvent->SetTarget(target);
|
|
}
|
|
}
|
|
|
|
//Capturing stage evaluation
|
|
if (NS_EVENT_FLAG_CAPTURE & aFlags &&
|
|
aEvent->message != NS_PAGE_LOAD &&
|
|
aEvent->message != NS_SCRIPT_LOAD &&
|
|
aEvent->message != NS_IMAGE_LOAD &&
|
|
aEvent->message != NS_IMAGE_ERROR &&
|
|
aEvent->message != NS_SCROLL_EVENT &&
|
|
!(aEvent->eventStructType == NS_MUTATION_EVENT &&
|
|
IsAnonymousForEvents())) {
|
|
//Initiate capturing phase. Special case first call to document
|
|
if (parent) {
|
|
parent->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
|
|
aFlags & NS_EVENT_CAPTURE_MASK,
|
|
aEventStatus);
|
|
} else {
|
|
nsIDocument* document = GetCurrentDoc();
|
|
if (document) {
|
|
ret = document->HandleDOMEvent(aPresContext, aEvent,
|
|
aDOMEvent,
|
|
aFlags & NS_EVENT_CAPTURE_MASK,
|
|
aEventStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (retarget) {
|
|
// The event originated beneath us, and we performed a retargeting.
|
|
// We need to restore the original target of the event.
|
|
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
|
|
if (privateEvent)
|
|
privateEvent->SetTarget(oldTarget);
|
|
}
|
|
|
|
// Weak pointer, which is fine since the hash table owns the
|
|
// listener manager
|
|
nsIEventListenerManager *lm = nsnull;
|
|
|
|
if (HasEventListenerManager()) {
|
|
EventListenerManagerMapEntry *entry =
|
|
NS_STATIC_CAST(EventListenerManagerMapEntry *,
|
|
PL_DHashTableOperate(&sEventListenerManagersHash, this,
|
|
PL_DHASH_LOOKUP));
|
|
|
|
if (PL_DHASH_ENTRY_IS_FREE(entry)) {
|
|
NS_ERROR("Huh, our bit says we have an event listener manager, but "
|
|
"there's nothing in the hash!?!!");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
lm = entry->mListenerManager;
|
|
}
|
|
|
|
//Local handling stage
|
|
if (lm &&
|
|
!(NS_EVENT_FLAG_CANT_BUBBLE & aEvent->flags &&
|
|
NS_EVENT_FLAG_BUBBLE & aFlags && !(NS_EVENT_FLAG_INIT & aFlags)) &&
|
|
!(aEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH)) {
|
|
aEvent->flags |= aFlags;
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> curTarg =
|
|
do_QueryInterface(NS_STATIC_CAST(nsIXMLContent *, this));
|
|
|
|
lm->HandleEvent(aPresContext, aEvent, aDOMEvent, curTarg, aFlags,
|
|
aEventStatus);
|
|
|
|
aEvent->flags &= ~aFlags;
|
|
}
|
|
|
|
if (retarget) {
|
|
// The event originated beneath us, and we need to perform a
|
|
// retargeting.
|
|
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
|
|
if (privateEvent) {
|
|
nsCOMPtr<nsIDOMEventTarget> parentTarget(do_QueryInterface(GetParent()));
|
|
privateEvent->SetTarget(parentTarget);
|
|
}
|
|
}
|
|
|
|
//Bubbling stage
|
|
if (NS_EVENT_FLAG_BUBBLE & aFlags && IsInDoc() &&
|
|
aEvent->message != NS_PAGE_LOAD && aEvent->message != NS_SCRIPT_LOAD &&
|
|
aEvent->message != NS_IMAGE_ERROR && aEvent->message != NS_IMAGE_LOAD &&
|
|
// scroll events fired at elements don't bubble (although scroll events
|
|
// fired at documents do, to the window)
|
|
aEvent->message != NS_SCROLL_EVENT &&
|
|
!(aEvent->eventStructType == NS_MUTATION_EVENT &&
|
|
IsAnonymousForEvents())) {
|
|
if (parent) {
|
|
// If there's a parent we pass the event to the parent...
|
|
|
|
ret = parent->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
|
|
aFlags & NS_EVENT_BUBBLE_MASK,
|
|
aEventStatus);
|
|
} else {
|
|
// If there's no parent but there is a document (i.e. this is
|
|
// the root node) we pass the event to the document...
|
|
nsIDocument* document = GetCurrentDoc();
|
|
if (document) {
|
|
ret = document->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
|
|
aFlags & NS_EVENT_BUBBLE_MASK,
|
|
aEventStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (retarget) {
|
|
// The event originated beneath us, and we performed a
|
|
// retargeting. We need to restore the original target of the
|
|
// event.
|
|
|
|
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
|
|
if (privateEvent)
|
|
privateEvent->SetTarget(oldTarget);
|
|
}
|
|
|
|
if (NS_EVENT_FLAG_INIT & aFlags) {
|
|
// We're leaving the DOM event loop so if we created a DOM event,
|
|
// release here. If externalDOMEvent is set the event was passed
|
|
// in and we don't own it
|
|
|
|
if (*aDOMEvent && !externalDOMEvent) {
|
|
nsrefcnt rc;
|
|
NS_RELEASE2(*aDOMEvent, rc);
|
|
if (0 != rc) {
|
|
// 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) {
|
|
privateEvent->DuplicatePrivateData();
|
|
}
|
|
}
|
|
|
|
aDOMEvent = nsnull;
|
|
}
|
|
|
|
// Now that we're done with this event, remove the flag that says
|
|
// we're in the process of dispatching this event.
|
|
NS_MARK_EVENT_DISPATCH_DONE(aEvent);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
PRUint32
|
|
nsGenericElement::ContentID() const
|
|
{
|
|
nsDOMSlots *slots = GetExistingDOMSlots();
|
|
|
|
if (slots) {
|
|
return slots->mContentID;
|
|
}
|
|
|
|
PtrBits flags = GetFlags();
|
|
|
|
return flags >> GENERIC_ELEMENT_CONTENT_ID_BITS_OFFSET;
|
|
}
|
|
|
|
void
|
|
nsGenericElement::SetContentID(PRUint32 aID)
|
|
{
|
|
// This should be in the constructor!!!
|
|
|
|
if (HasDOMSlots() || aID > GENERIC_ELEMENT_CONTENT_ID_MAX_VALUE) {
|
|
nsDOMSlots *slots = GetDOMSlots();
|
|
|
|
if (slots) {
|
|
slots->mContentID = aID;
|
|
}
|
|
} else {
|
|
UnsetFlags(GENERIC_ELEMENT_CONTENT_ID_MASK);
|
|
SetFlags(aID << GENERIC_ELEMENT_CONTENT_ID_BITS_OFFSET);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::MaybeTriggerAutoLink(nsIDocShell *aShell)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIAtom*
|
|
nsGenericElement::GetID() const
|
|
{
|
|
nsIAtom* IDName = GetIDAttributeName();
|
|
if (IDName) {
|
|
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(IDName);
|
|
if (attrVal){
|
|
if (attrVal->Type() == nsAttrValue::eAtom) {
|
|
return attrVal->GetAtomValue();
|
|
}
|
|
if(attrVal->IsEmptyString()){
|
|
return nsnull;
|
|
}
|
|
// Check if the ID has been stored as a string.
|
|
// This would occur if the ID attribute name changed after
|
|
// the ID was parsed.
|
|
if (attrVal->Type() == nsAttrValue::eString) {
|
|
nsAutoString idVal(attrVal->GetStringValue());
|
|
|
|
// Create an atom from the value and set it into the attribute list.
|
|
NS_CONST_CAST(nsAttrValue*, attrVal)->ParseAtom(idVal);
|
|
return attrVal->GetAtomValue();
|
|
}
|
|
}
|
|
}
|
|
return nsnull;
|
|
}
|
|
|
|
const nsAttrValue*
|
|
nsGenericElement::GetClasses() const
|
|
{
|
|
return nsnull;
|
|
}
|
|
|
|
NS_IMETHODIMP_(PRBool)
|
|
nsGenericElement::HasClass(nsIAtom* aClass, PRBool aCaseSensitive) const
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
nsICSSStyleRule*
|
|
nsGenericElement::GetInlineStyleRule()
|
|
{
|
|
return nsnull;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::SetInlineStyleRule(nsICSSStyleRule* aStyleRule,
|
|
PRBool aNotify)
|
|
{
|
|
NS_NOTYETIMPLEMENTED("nsGenericElement::SetInlineStyleRule");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP_(PRBool)
|
|
nsGenericElement::IsAttributeMapped(const nsIAtom* aAttribute) const
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
nsChangeHint
|
|
nsGenericElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
|
|
PRInt32 aModType) const
|
|
{
|
|
return nsChangeHint(0);
|
|
}
|
|
|
|
nsIAtom *
|
|
nsGenericElement::GetIDAttributeName() const
|
|
{
|
|
return mNodeInfo->GetIDAttributeAtom();
|
|
}
|
|
|
|
nsIAtom *
|
|
nsGenericElement::GetClassAttributeName() const
|
|
{
|
|
return nsnull;
|
|
}
|
|
|
|
PRBool
|
|
nsGenericElement::FindAttributeDependence(const nsIAtom* aAttribute,
|
|
const MappedAttributeEntry* const aMaps[],
|
|
PRUint32 aMapCount)
|
|
{
|
|
for (PRUint32 mapindex = 0; mapindex < aMapCount; ++mapindex) {
|
|
for (const MappedAttributeEntry* map = aMaps[mapindex];
|
|
map->attribute; ++map) {
|
|
if (aAttribute == *map->attribute) {
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
already_AddRefed<nsINodeInfo>
|
|
nsGenericElement::GetExistingAttrNameFromQName(const nsAString& aStr) const
|
|
{
|
|
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aStr);
|
|
if (!name) {
|
|
return nsnull;
|
|
}
|
|
|
|
nsINodeInfo* nodeInfo;
|
|
if (name->IsAtom()) {
|
|
mNodeInfo->NodeInfoManager()->GetNodeInfo(name->Atom(), nsnull,
|
|
kNameSpaceID_None, &nodeInfo);
|
|
}
|
|
else {
|
|
NS_ADDREF(nodeInfo = name->NodeInfo());
|
|
}
|
|
|
|
return nodeInfo;
|
|
}
|
|
|
|
already_AddRefed<nsIURI>
|
|
nsGenericElement::GetBaseURI() const
|
|
{
|
|
nsIDocument* doc = GetOwnerDoc();
|
|
if (!doc) {
|
|
// We won't be able to do security checks, etc. So don't go any
|
|
// further. That said, this really shouldn't happen...
|
|
NS_ERROR("Element without owner document");
|
|
return nsnull;
|
|
}
|
|
|
|
// Our base URL depends on whether we have an xml:base attribute, as
|
|
// well as on whether any of our ancestors do.
|
|
nsCOMPtr<nsIURI> parentBase;
|
|
|
|
nsIContent *parent = GetParent();
|
|
if (parent) {
|
|
parentBase = parent->GetBaseURI();
|
|
} else {
|
|
// No parent, so just use the document (we must be the root or not in the
|
|
// tree).
|
|
parentBase = doc->GetBaseURI();
|
|
}
|
|
|
|
// Now check for an xml:base attr
|
|
nsAutoString value;
|
|
nsresult rv = GetAttr(kNameSpaceID_XML, nsHTMLAtoms::base, value);
|
|
if (rv != NS_CONTENT_ATTR_HAS_VALUE) {
|
|
// No xml:base, so we just use the parent's base URL
|
|
nsIURI *base = parentBase;
|
|
NS_IF_ADDREF(base);
|
|
|
|
return base;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> ourBase;
|
|
rv = NS_NewURI(getter_AddRefs(ourBase), value,
|
|
doc->GetDocumentCharacterSet().get(), parentBase);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// do a security check, almost the same as nsDocument::SetBaseURL()
|
|
rv = nsContentUtils::GetSecurityManager()->
|
|
CheckLoadURIWithPrincipal(doc->GetPrincipal(), ourBase,
|
|
nsIScriptSecurityManager::STANDARD);
|
|
}
|
|
|
|
nsIURI *base;
|
|
if (NS_FAILED(rv)) {
|
|
base = parentBase;
|
|
} else {
|
|
base = ourBase;
|
|
}
|
|
|
|
NS_IF_ADDREF(base);
|
|
|
|
return base;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::RangeAdd(nsIDOMRange* aRange)
|
|
{
|
|
if (!sRangeListsHash.ops) {
|
|
// We've already been shut down, don't bother adding a range...
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RangeListMapEntry *entry =
|
|
NS_STATIC_CAST(RangeListMapEntry *,
|
|
PL_DHashTableOperate(&sRangeListsHash, this, PL_DHASH_ADD));
|
|
|
|
if (!entry) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// lazy allocation of range list
|
|
if (!entry->mRangeList) {
|
|
NS_ASSERTION(!(GetFlags() & GENERIC_ELEMENT_HAS_RANGELIST),
|
|
"Huh, nsGenericElement flags don't reflect reality!!!");
|
|
|
|
entry->mRangeList = new nsAutoVoidArray();
|
|
|
|
if (!entry->mRangeList) {
|
|
PL_DHashTableRawRemove(&sRangeListsHash, entry);
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
SetFlags(GENERIC_ELEMENT_HAS_RANGELIST);
|
|
}
|
|
|
|
// Make sure we don't add a range that is already in the list!
|
|
PRInt32 i = entry->mRangeList->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 = entry->mRangeList->AppendElement(aRange);
|
|
if (!rv) {
|
|
if (entry->mRangeList->Count() == 0) {
|
|
// Fresh entry, remove it from the hash...
|
|
|
|
PL_DHashTableRawRemove(&sRangeListsHash, entry);
|
|
}
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
void
|
|
nsGenericElement::RangeRemove(nsIDOMRange* aRange)
|
|
{
|
|
if (!HasRangeList()) {
|
|
return;
|
|
}
|
|
|
|
RangeListMapEntry *entry =
|
|
NS_STATIC_CAST(RangeListMapEntry *,
|
|
PL_DHashTableOperate(&sRangeListsHash, this,
|
|
PL_DHASH_LOOKUP));
|
|
|
|
if (PL_DHASH_ENTRY_IS_FREE(entry)) {
|
|
NS_ERROR("Huh, our bit says we have a range list, but there's nothing "
|
|
"in the hash!?!!");
|
|
|
|
return;
|
|
}
|
|
|
|
if (!entry->mRangeList) {
|
|
return;
|
|
}
|
|
|
|
// dont need to release - this call is made by the range object itself
|
|
entry->mRangeList->RemoveElement(aRange);
|
|
|
|
if (entry->mRangeList->Count() == 0) {
|
|
PL_DHashTableRawRemove(&sRangeListsHash, entry);
|
|
|
|
UnsetFlags(GENERIC_ELEMENT_HAS_RANGELIST);
|
|
}
|
|
}
|
|
|
|
const nsVoidArray *
|
|
nsGenericElement::GetRangeList() const
|
|
{
|
|
if (!HasRangeList()) {
|
|
return nsnull;
|
|
}
|
|
|
|
RangeListMapEntry *entry =
|
|
NS_STATIC_CAST(RangeListMapEntry *,
|
|
PL_DHashTableOperate(&sRangeListsHash, this,
|
|
PL_DHASH_LOOKUP));
|
|
|
|
if (PL_DHASH_ENTRY_IS_FREE(entry)) {
|
|
NS_ERROR("Huh, our bit says we have a range list, but there's nothing "
|
|
"in the hash!?!!");
|
|
|
|
return nsnull;
|
|
}
|
|
|
|
return entry->mRangeList;
|
|
}
|
|
|
|
void
|
|
nsGenericElement::SetFocus(nsPresContext* aPresContext)
|
|
{
|
|
// Traditionally focusable elements can take focus as long as they don't set
|
|
// the disabled attribute
|
|
|
|
nsIFrame* frame = nsnull;
|
|
nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
|
|
presShell->GetPrimaryFrameFor(this, &frame);
|
|
if (frame && frame->IsFocusable()) {
|
|
aPresContext->EventStateManager()->SetContentState(this,
|
|
NS_EVENT_STATE_FOCUS);
|
|
// Setting content state can cause the frame to be destroyed because of
|
|
// style changes so we need to lookup the frame again (bug 330367).
|
|
presShell->GetPrimaryFrameFor(this, &frame);
|
|
if (frame) {
|
|
presShell->ScrollFrameIntoView(frame, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
PRBool
|
|
nsGenericElement::ShouldFocus(nsIContent *aContent)
|
|
{
|
|
// Default to false, since if the document is not attached to a window,
|
|
// we should not focus any of its content.
|
|
PRBool visible = PR_FALSE;
|
|
|
|
// Figure out if we're focusing an element in an inactive (hidden)
|
|
// tab (whose docshell is not visible), if so, drop this focus
|
|
// request on the floor
|
|
|
|
nsIDocument *document = aContent->GetDocument();
|
|
|
|
if (document) {
|
|
nsIScriptGlobalObject *sgo = document->GetScriptGlobalObject();
|
|
|
|
if (sgo) {
|
|
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(sgo));
|
|
nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(webNav));
|
|
|
|
if (baseWin) {
|
|
baseWin->GetVisibility(&visible);
|
|
}
|
|
}
|
|
}
|
|
|
|
return visible;
|
|
}
|
|
|
|
nsIContent*
|
|
nsGenericElement::GetBindingParent() const
|
|
{
|
|
nsDOMSlots *slots = GetExistingDOMSlots();
|
|
|
|
if (slots) {
|
|
return slots->mBindingParent;
|
|
}
|
|
return nsnull;
|
|
}
|
|
|
|
PRBool
|
|
nsGenericElement::IsContentOfType(PRUint32 aFlags) const
|
|
{
|
|
return !(aFlags & ~eELEMENT);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsGenericElement::GetListenerManager(nsIEventListenerManager **aResult)
|
|
{
|
|
*aResult = nsnull;
|
|
|
|
if (!sEventListenerManagersHash.ops) {
|
|
// We're already shut down, don't bother creating a event listener
|
|
// manager.
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
EventListenerManagerMapEntry *entry =
|
|
NS_STATIC_CAST(EventListenerManagerMapEntry *,
|
|
PL_DHashTableOperate(&sEventListenerManagersHash, this,
|
|
PL_DHASH_ADD));
|
|
|
|
if (!entry) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (!entry->mListenerManager) {
|
|
nsresult rv =
|
|
NS_NewEventListenerManager(getter_AddRefs(entry->mListenerManager));
|
|
|
|
if (NS_FAILED(rv)) {
|
|
PL_DHashTableRawRemove(&sEventListenerManagersHash, entry);
|
|
|
|
return rv;
|
|
}
|
|
|
|
entry->mListenerManager->SetListenerTarget(NS_STATIC_CAST(nsIXMLContent *,
|
|
this));
|
|
|
|
SetFlags(GENERIC_ELEMENT_HAS_LISTENERMANAGER);
|
|
}
|
|
|
|
*aResult = entry->mListenerManager;
|
|
NS_ADDREF(*aResult);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// virtual
|
|
void
|
|
nsGenericElement::SetMayHaveFrame(PRBool aMayHaveFrame)
|
|
{
|
|
if (aMayHaveFrame) {
|
|
SetFlags(GENERIC_ELEMENT_MAY_HAVE_FRAME);
|
|
} else {
|
|
UnsetFlags(GENERIC_ELEMENT_MAY_HAVE_FRAME);
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
PRBool
|
|
nsGenericElement::MayHaveFrame() const
|
|
{
|
|
return !!(GetFlags() & GENERIC_ELEMENT_MAY_HAVE_FRAME);
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::InsertChildAt(nsIContent* aKid,
|
|
PRUint32 aIndex,
|
|
PRBool aNotify)
|
|
{
|
|
NS_PRECONDITION(aKid, "null ptr");
|
|
|
|
return doInsertChildAt(aKid, aIndex, aNotify, this, GetCurrentDoc(),
|
|
mAttrsAndChildren);
|
|
}
|
|
|
|
|
|
static nsresult
|
|
doInsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify,
|
|
nsIContent* aParent, nsIDocument* aDocument,
|
|
nsAttrAndChildArray& aChildArray)
|
|
{
|
|
NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!");
|
|
NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument,
|
|
"Incorrect aDocument");
|
|
|
|
nsMutationGuard::DidMutate();
|
|
|
|
// Do this before checking the child-count since this could cause mutations
|
|
mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL, aNotify);
|
|
|
|
PRUint32 childCount = aChildArray.ChildCount();
|
|
NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE);
|
|
|
|
PRBool isAppend = (aIndex == childCount);
|
|
|
|
// Note that SetRootContent already deals with binding, so if we plan to call
|
|
// it we shouldn't bind ourselves.
|
|
// XXXbz this doesn't put aKid in the right spot, really... We really need a
|
|
// better api for handling kids on documents.
|
|
if (!aParent && aKid->IsContentOfType(nsIContent::eELEMENT)) {
|
|
nsresult rv = aDocument->SetRootContent(aKid);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
} else {
|
|
nsresult rv = aChildArray.InsertChildAt(aKid, aIndex);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = aKid->BindToTree(aDocument, aParent, nsnull, PR_TRUE);
|
|
if (NS_FAILED(rv)) {
|
|
aChildArray.RemoveChildAt(aIndex);
|
|
aKid->UnbindFromTree();
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
if (aParent && !aParent->IsContentOfType(nsIContent::eXUL)) {
|
|
nsRange::OwnerChildInserted(aParent, aIndex);
|
|
}
|
|
|
|
// The kid may have removed its parent from the document, so recheck that
|
|
// that's still in the document before proceeding. Also, the kid may have
|
|
// just removed itself, in which case we don't really want to fire
|
|
// ContentAppended or a mutation event.
|
|
// XXXbz What if the kid just moved us in the document? Scripts suck. We
|
|
// really need to stop running them while we're in the middle of modifying
|
|
// the DOM....
|
|
if (aNotify && aDocument && aKid->GetCurrentDoc() == aDocument &&
|
|
(!aParent || aKid->GetParent() == aParent)) {
|
|
// Note that we always want to call ContentInserted when things are added
|
|
// as kids to documents
|
|
if (aParent && isAppend) {
|
|
aDocument->ContentAppended(aParent, aIndex);
|
|
} else {
|
|
aDocument->ContentInserted(aParent, aKid, aIndex);
|
|
}
|
|
|
|
// XXXbz how come we're not firing mutation listeners for adding to
|
|
// documents?
|
|
if (aParent &&
|
|
nsGenericElement::HasMutationListeners(aParent,
|
|
NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
|
|
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED, aKid);
|
|
mutation.mRelatedNode = do_QueryInterface(aParent);
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
aKid->HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT, &status);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::AppendChildTo(nsIContent* aKid, PRBool aNotify)
|
|
{
|
|
NS_PRECONDITION(aKid && this != aKid, "null ptr");
|
|
nsIDocument *document = GetCurrentDoc();
|
|
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
|
|
|
|
nsresult rv = mAttrsAndChildren.AppendChild(aKid);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = aKid->BindToTree(document, this, nsnull, PR_TRUE);
|
|
if (NS_FAILED(rv)) {
|
|
mAttrsAndChildren.RemoveChildAt(GetChildCount() - 1);
|
|
aKid->UnbindFromTree();
|
|
return rv;
|
|
}
|
|
// ranges don't need adjustment since new child is at end of list
|
|
|
|
// The kid may have removed us from the document, so recheck that we're still
|
|
// in the document before proceeding. Also, the kid may have just removed
|
|
// itself, in which case we don't really want to fire ContentAppended or a
|
|
// mutation event.
|
|
// XXXbz What if the kid just moved us in the document? Scripts suck. We
|
|
// really need to stop running them while we're in the middle of modifying
|
|
// the DOM....
|
|
if (aNotify && document && document == GetCurrentDoc() &&
|
|
aKid->GetParent() == this) {
|
|
document->ContentAppended(this, GetChildCount() - 1);
|
|
|
|
if (HasMutationListeners(this, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
|
|
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED, aKid);
|
|
mutation.mRelatedNode = do_QueryInterface(this);
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
aKid->HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT,
|
|
&status);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
|
|
{
|
|
nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex);
|
|
NS_ASSERTION(oldKid == GetChildAt(aIndex), "Unexpected child in RemoveChildAt");
|
|
|
|
if (oldKid) {
|
|
return doRemoveChildAt(aIndex, aNotify, oldKid, this, GetCurrentDoc(),
|
|
mAttrsAndChildren);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Note: A lot of methods like IndexOf(), GetChildCount(), etc, need to do more
|
|
// than just look at the child array for some subclasses, so make sure to call
|
|
// them instead of just messing with aChildArray
|
|
struct nsContentOrDocument {
|
|
nsContentOrDocument(nsIContent* aContent, nsIDocument* aDocument) :
|
|
mContent(aContent), mDocument(aDocument)
|
|
{}
|
|
|
|
PRInt32 IndexOf(nsIContent* aPossibleChild)
|
|
{
|
|
return mContent ? mContent->IndexOf(aPossibleChild) :
|
|
mDocument->IndexOf(aPossibleChild);
|
|
}
|
|
|
|
PRUint32 GetChildCount()
|
|
{
|
|
return mContent ? mContent->GetChildCount() :
|
|
mDocument->GetChildCount();
|
|
}
|
|
|
|
nsIContent* GetChildAt(PRUint32 aIndex)
|
|
{
|
|
return mContent ? mContent->GetChildAt(aIndex) :
|
|
mDocument->GetChildAt(aIndex);
|
|
}
|
|
|
|
nsIDocument* GetOwnerDoc()
|
|
{
|
|
return mContent ? mContent->GetOwnerDoc() : mDocument;
|
|
}
|
|
|
|
nsresult InsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify,
|
|
nsAttrAndChildArray& aChildArray)
|
|
{
|
|
// XXXbz can't quite use doInsertChildAt because InsertChildAt has this
|
|
// random subclass notification it now does... and because subclasses
|
|
// might be intercepting InsertChildAt and doing stuff.
|
|
return mContent ? mContent->InsertChildAt(aKid, aIndex, aNotify) :
|
|
doInsertChildAt(aKid, aIndex, aNotify, mContent, mDocument, aChildArray);
|
|
}
|
|
|
|
nsresult RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
|
|
{
|
|
// XXXbz can't quite use doRemoveChildAt because RemoveChildAt has this
|
|
// random subclass notification it now does... and because subclasses
|
|
// might be intercepting RemoveChildAt and doing stuff.
|
|
if (mContent) {
|
|
return mContent->RemoveChildAt(aIndex, aNotify);
|
|
}
|
|
|
|
nsCOMPtr<nsIDocument_MOZILLA_1_8_0_BRANCH> doc =
|
|
do_QueryInterface(mDocument);
|
|
|
|
return doc->RemoveChildAt(aIndex, aNotify);
|
|
}
|
|
|
|
PRBool Equals(nsContentOrDocument& aOther)
|
|
{
|
|
return mContent ? mContent == aOther.mContent :
|
|
mDocument == aOther.mDocument;
|
|
}
|
|
|
|
nsIContent* mContent;
|
|
nsIDocument* mDocument;
|
|
};
|
|
|
|
|
|
nsresult
|
|
doRemoveChildAt(PRUint32 aIndex, PRBool aNotify, nsIContent* aKid,
|
|
nsIContent* aParent, nsIDocument* aDocument,
|
|
nsAttrAndChildArray& aChildArray)
|
|
{
|
|
NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!");
|
|
NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument,
|
|
"Incorrect aDocument");
|
|
|
|
nsMutationGuard::DidMutate();
|
|
|
|
nsContentOrDocument container(aParent, aDocument);
|
|
|
|
NS_PRECONDITION(aKid && aKid->GetParent() == aParent &&
|
|
aKid == container.GetChildAt(aIndex) &&
|
|
container.IndexOf(aKid) == (PRInt32)aIndex, "Bogus aKid");
|
|
|
|
mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL, aNotify);
|
|
|
|
if (aNotify) {
|
|
nsMutationGuard guard;
|
|
|
|
if (aParent &&
|
|
nsGenericElement::HasMutationListeners(aParent,
|
|
NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
|
|
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED, aKid);
|
|
mutation.mRelatedNode = do_QueryInterface(aParent);
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
aKid->HandleDOMEvent(nsnull, &mutation, nsnull,
|
|
NS_EVENT_FLAG_INIT, &status);
|
|
}
|
|
|
|
// Someone may have removed the kid or any of its siblings while that event
|
|
// was processing.
|
|
if (guard.Mutated(0)) {
|
|
aIndex = container.IndexOf(aKid);
|
|
if (aIndex == (PRUint32)(-1)) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aParent && !aParent->IsContentOfType(nsIContent::eXUL)) {
|
|
nsRange::OwnerChildRemoved(aParent, aIndex, aKid);
|
|
}
|
|
|
|
// Note that SetRootContent already deals with unbinding, so if we plan to
|
|
// call it we shouldn't unbind ourselves. It also deals with removing the
|
|
// node from the child array, but not with notifying, unfortunately. So we
|
|
// have to call ContentRemoved ourselves after setting the root content.
|
|
if (!aParent && aKid->IsContentOfType(nsIContent::eELEMENT)) {
|
|
aDocument->SetRootContent(nsnull);
|
|
if (aNotify) {
|
|
aDocument->ContentRemoved(aParent, aKid, aIndex);
|
|
}
|
|
} else {
|
|
aChildArray.RemoveChildAt(aIndex);
|
|
|
|
if (aNotify && aDocument) {
|
|
aDocument->ContentRemoved(aParent, aKid, aIndex);
|
|
}
|
|
|
|
aKid->UnbindFromTree();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Generic DOMNode implementations
|
|
|
|
/*
|
|
* This helper function checks if aChild is the same as aNode or if
|
|
* aChild is one of aNode's ancestors. -- jst@citec.fi
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::InsertBefore(nsIDOMNode *aNewChild, nsIDOMNode *aRefChild,
|
|
nsIDOMNode **aReturn)
|
|
{
|
|
return doReplaceOrInsertBefore(PR_FALSE, aNewChild, aRefChild, this,
|
|
GetCurrentDoc(), mAttrsAndChildren, aReturn);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::ReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild,
|
|
nsIDOMNode** aReturn)
|
|
{
|
|
return doReplaceOrInsertBefore(PR_TRUE, aNewChild, aOldChild, this,
|
|
GetCurrentDoc(), mAttrsAndChildren, aReturn);
|
|
}
|
|
|
|
// When replacing, aRefContent is the content being replaced; when
|
|
// inserting it's the content before which we're inserting. In the
|
|
// latter case it may be null.
|
|
static
|
|
PRBool IsAllowedAsChild(nsIContent* aNewChild, PRUint16 aNewNodeType,
|
|
nsIContent* aParent, nsIDocument* aDocument,
|
|
PRBool aIsReplace, nsIContent* aRefContent)
|
|
{
|
|
NS_PRECONDITION(aNewChild, "Must have new child");
|
|
NS_PRECONDITION(!aIsReplace || aRefContent,
|
|
"Must have ref content for replace");
|
|
#ifdef DEBUG
|
|
PRUint16 debugNodeType = 0;
|
|
nsCOMPtr<nsIDOMNode> debugNode(do_QueryInterface(aNewChild));
|
|
nsresult debugRv = debugNode->GetNodeType(&debugNodeType);
|
|
|
|
NS_PRECONDITION(NS_SUCCEEDED(debugRv) && debugNodeType == aNewNodeType,
|
|
"Bogus node type passed");
|
|
#endif
|
|
|
|
if (aParent && nsContentUtils::ContentIsDescendantOf(aParent, aNewChild)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// The allowed child nodes differ for documents and elements
|
|
switch (aNewNodeType) {
|
|
case nsIDOMNode::COMMENT_NODE :
|
|
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE :
|
|
// OK in both cases
|
|
return PR_TRUE;
|
|
case nsIDOMNode::TEXT_NODE :
|
|
case nsIDOMNode::CDATA_SECTION_NODE :
|
|
case nsIDOMNode::ENTITY_REFERENCE_NODE :
|
|
// Only allowed under elements
|
|
return aParent != nsnull;
|
|
case nsIDOMNode::ELEMENT_NODE :
|
|
{
|
|
if (aParent) {
|
|
// Always ok to have elements under other elements
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsIContent* rootContent = aDocument->GetRootContent();
|
|
if (rootContent) {
|
|
// Already have a documentElement, so this is only OK if we're
|
|
// replacing it.
|
|
return aIsReplace && rootContent == aRefContent;
|
|
}
|
|
|
|
// We don't have a documentElement yet. Our one remaining constraint is
|
|
// that the documentElement must come after the doctype.
|
|
if (!aRefContent) {
|
|
// Appending is just fine.
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Now grovel for a doctype
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDocument);
|
|
NS_ASSERTION(doc, "Shouldn't happen");
|
|
nsCOMPtr<nsIDOMDocumentType> docType;
|
|
doc->GetDoctype(getter_AddRefs(docType));
|
|
nsCOMPtr<nsIContent> docTypeContent = do_QueryInterface(docType);
|
|
|
|
if (!docTypeContent) {
|
|
// It's all good.
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRInt32 doctypeIndex = aDocument->IndexOf(docTypeContent);
|
|
PRInt32 insertIndex = aDocument->IndexOf(aRefContent);
|
|
|
|
// Now we're OK in the following two cases only:
|
|
// 1) We're replacing something that's not before the doctype
|
|
// 2) We're inserting before something that comes after the doctype
|
|
return aIsReplace ? (insertIndex >= doctypeIndex) :
|
|
insertIndex > doctypeIndex;
|
|
}
|
|
case nsIDOMNode::DOCUMENT_TYPE_NODE :
|
|
{
|
|
if (aParent) {
|
|
// no doctypes allowed under elements
|
|
return PR_FALSE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDocument);
|
|
NS_ASSERTION(doc, "Shouldn't happen");
|
|
nsCOMPtr<nsIDOMDocumentType> docType;
|
|
doc->GetDoctype(getter_AddRefs(docType));
|
|
nsCOMPtr<nsIContent> docTypeContent = do_QueryInterface(docType);
|
|
if (docTypeContent) {
|
|
// Already have a doctype, so this is only OK if we're replacing it
|
|
return aIsReplace && docTypeContent == aRefContent;
|
|
}
|
|
|
|
// We don't have a doctype yet. Our one remaining constraint is
|
|
// that the doctype must come before the documentElement.
|
|
nsIContent* rootContent = aDocument->GetRootContent();
|
|
if (!rootContent) {
|
|
// It's all good
|
|
return PR_TRUE;
|
|
}
|
|
|
|
if (!aRefContent) {
|
|
// Trying to append a doctype, but have a documentElement
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRInt32 rootIndex = aDocument->IndexOf(rootContent);
|
|
PRInt32 insertIndex = aDocument->IndexOf(aRefContent);
|
|
|
|
// Now we're OK if and only if insertIndex <= rootIndex. Indeed, either
|
|
// we end up replacing aRefContent or we end up before it. Either one is
|
|
// ok as long as aRefContent is not after rootContent.
|
|
return insertIndex <= rootIndex;
|
|
}
|
|
case nsIDOMNode::DOCUMENT_FRAGMENT_NODE :
|
|
{
|
|
// Note that for now we only allow nodes inside document fragments if
|
|
// they're allowed inside elements. If we ever change this to allow
|
|
// doctype nodes in document fragments, we'll need to update this code
|
|
if (aParent) {
|
|
// All good here
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool sawElement = PR_FALSE;
|
|
PRUint32 count = aNewChild->GetChildCount();
|
|
for (PRUint32 index = 0; index < count; ++index) {
|
|
nsIContent* childContent = aNewChild->GetChildAt(index);
|
|
NS_ASSERTION(childContent, "Something went wrong");
|
|
if (childContent->IsContentOfType(nsIContent::eELEMENT)) {
|
|
if (sawElement) {
|
|
// Can't put two elements into a document
|
|
return PR_FALSE;
|
|
}
|
|
sawElement = PR_TRUE;
|
|
}
|
|
// If we can put this content at the the right place, we might be ok;
|
|
// if not, we bail out.
|
|
nsCOMPtr<nsIDOMNode> childNode(do_QueryInterface(childContent));
|
|
PRUint16 type;
|
|
childNode->GetNodeType(&type);
|
|
if (!IsAllowedAsChild(childContent, type, aParent, aDocument,
|
|
aIsReplace, aRefContent)) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
// Everything in the fragment checked out ok, so we can stick it in here
|
|
return PR_TRUE;
|
|
}
|
|
default:
|
|
/*
|
|
* aNewChild is of invalid type.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
class nsFragmentObserver : public nsStubDocumentObserver {
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
nsFragmentObserver(PRUint32 aOldChildCount, nsIContent* aParent,
|
|
nsIDocument* aDocument) :
|
|
mOldChildCount(aOldChildCount),
|
|
mChildrenBound(0),
|
|
mParent(aParent),
|
|
mDocument(aDocument)
|
|
{
|
|
NS_ASSERTION(mParent, "Must have parent!");
|
|
}
|
|
|
|
virtual void BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType) {
|
|
// Make sure to notify on whatever content has been appended thus far
|
|
Notify();
|
|
}
|
|
|
|
virtual void DocumentWillBeDestroyed(nsIDocument *aDocument) {
|
|
Disconnect();
|
|
}
|
|
|
|
void Connect() {
|
|
if (mDocument) {
|
|
mDocument->AddObserver(this);
|
|
}
|
|
}
|
|
|
|
void Disconnect() {
|
|
if (mDocument) {
|
|
mDocument->RemoveObserver(this);
|
|
}
|
|
mDocument = nsnull;
|
|
}
|
|
|
|
void Finish() {
|
|
// Notify on any remaining content
|
|
Notify();
|
|
Disconnect();
|
|
}
|
|
|
|
void Notify() {
|
|
if (mDocument && mDocument == mParent->GetCurrentDoc()) {
|
|
if (mChildrenBound > 0) {
|
|
// Some stuff got bound since we notified last time. Notify on it.
|
|
PRUint32 boundCount = mOldChildCount + mChildrenBound;
|
|
PRUint32 notifySlot = mOldChildCount;
|
|
// Make sure to update mChildrenBound and mOldChildCount so that if
|
|
// ContentAppended calls BeginUpdate for some reason (eg XBL) so we
|
|
// reenter Notify() we won't double-notify.
|
|
mChildrenBound = 0;
|
|
mOldChildCount = boundCount;
|
|
PRUint32 i;
|
|
nsIContent *child;
|
|
if (boundCount == mParent->GetChildCount()) {
|
|
// All the kids have been bound already. Just append
|
|
mDocument->ContentAppended(mParent, notifySlot);
|
|
} else {
|
|
// Just notify on the already-bound kids
|
|
for (i = notifySlot; i < boundCount; ++i) {
|
|
child = mParent->GetChildAt(i);
|
|
// Could have no child if script has rearranged the DOM or
|
|
// something...
|
|
if (child) {
|
|
mDocument->ContentInserted(mParent, child, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
const PRUint32 eventBits = NS_EVENT_BITS_MUTATION_NODEINSERTED;
|
|
if (nsGenericElement::HasMutationListeners(mParent, eventBits)) {
|
|
// Walk over the child nodes and collect those we will fire
|
|
// notifications for
|
|
nsCOMArray<nsIContent> kidsToNotify;
|
|
for (i = notifySlot; i < boundCount; ++i) {
|
|
kidsToNotify.AppendObject(mParent->GetChildAt(i));
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(mParent);
|
|
|
|
PRUint32 length = kidsToNotify.Count();
|
|
for (i = 0; i < length; ++i) {
|
|
child = kidsToNotify[i];
|
|
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED, child);
|
|
mutation.mRelatedNode = parentNode;
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
child->HandleDOMEvent(nsnull, &mutation, nsnull,
|
|
NS_EVENT_FLAG_INIT, &status);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChildBound() {
|
|
++mChildrenBound;
|
|
}
|
|
|
|
private:
|
|
PRUint32 mOldChildCount;
|
|
PRUint32 mChildrenBound; // Number of children bound since we last notified
|
|
nsCOMPtr<nsIContent> mParent;
|
|
nsIDocument* mDocument;
|
|
};
|
|
|
|
|
|
NS_IMPL_ISUPPORTS1(nsFragmentObserver, nsIDocumentObserver)
|
|
|
|
/* static */
|
|
nsresult
|
|
nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace,
|
|
nsIDOMNode* aNewChild,
|
|
nsIDOMNode* aRefChild,
|
|
nsIContent* aParent,
|
|
nsIDocument* aDocument,
|
|
nsAttrAndChildArray& aChildArray,
|
|
nsIDOMNode** aReturn)
|
|
{
|
|
NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!");
|
|
NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument,
|
|
"Incorrect aDocument");
|
|
|
|
*aReturn = nsnull;
|
|
|
|
if (!aNewChild || (aReplace && !aRefChild)) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
// Keep a strong reference to the node that we'll return to ensure it
|
|
// doesn't go away.
|
|
nsCOMPtr<nsIDOMNode> returnVal = aReplace ? aRefChild : aNewChild;
|
|
|
|
nsCOMPtr<nsIContent> refContent;
|
|
nsresult res = NS_OK;
|
|
PRInt32 insPos;
|
|
|
|
nsContentOrDocument container(aParent, aDocument);
|
|
|
|
// Figure out which index to insert at
|
|
if (aRefChild) {
|
|
refContent = do_QueryInterface(aRefChild);
|
|
insPos = container.IndexOf(refContent);
|
|
if (insPos < 0) {
|
|
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
|
}
|
|
|
|
if (aRefChild == aNewChild) {
|
|
NS_ADDREF(*aReturn = aNewChild);
|
|
|
|
return NS_OK;
|
|
}
|
|
} else {
|
|
insPos = container.GetChildCount();
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> newContent = do_QueryInterface(aNewChild);
|
|
if (!newContent) {
|
|
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
|
}
|
|
|
|
PRUint16 nodeType = 0;
|
|
res = aNewChild->GetNodeType(&nodeType);
|
|
NS_ENSURE_SUCCESS(res, res);
|
|
|
|
// Make sure that the inserted node is allowed as a child of its new parent.
|
|
if (!IsAllowedAsChild(newContent, nodeType, aParent, aDocument, aReplace,
|
|
refContent)) {
|
|
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
|
}
|
|
|
|
nsIDocument* old_doc = newContent->GetOwnerDoc();
|
|
|
|
// XXXbz The document code and content code have two totally different
|
|
// security checks here. Why? Because I'm afraid to change such things this
|
|
// close to 1.8. But which should we do here, really? Or both? For example
|
|
// what should a caller with UniversalBrowserRead/Write/whatever be able to
|
|
// do, exactly? Do we need to be more careful with documents because random
|
|
// callers _can_ get access to them? That might be....
|
|
if (old_doc && old_doc != container.GetOwnerDoc()) {
|
|
if (aParent) {
|
|
if (!nsContentUtils::CanCallerAccess(aNewChild)) {
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
|
}
|
|
} else {
|
|
nsCOMPtr<nsIDOMNode> doc(do_QueryInterface(aDocument));
|
|
if (NS_FAILED(nsContentUtils::CheckSameOrigin(doc, aNewChild))) {
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We want an update batch when we expect several mutations to be performed,
|
|
// which is when we're replacing a node, or when we're inserting a fragment.
|
|
mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL,
|
|
aReplace || nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE);
|
|
|
|
// If we're replacing
|
|
if (aReplace) {
|
|
// Getting (and addrefing) the following child here is sort of wasteful
|
|
// in the common case, but really, it's not that expensive. Get over it.
|
|
refContent = container.GetChildAt(insPos + 1);
|
|
|
|
nsMutationGuard guard;
|
|
|
|
res = container.RemoveChildAt(insPos, PR_TRUE);
|
|
NS_ENSURE_SUCCESS(res, res);
|
|
|
|
if (guard.Mutated(1)) {
|
|
insPos = refContent ? container.IndexOf(refContent) :
|
|
container.GetChildCount();
|
|
if (insPos < 0) {
|
|
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
|
}
|
|
|
|
// Passing PR_FALSE for aIsReplace since we now have removed the node
|
|
// to be replaced.
|
|
if (!IsAllowedAsChild(newContent, nodeType, aParent, aDocument,
|
|
PR_FALSE, refContent)) {
|
|
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if we're inserting a document fragment. If we are, we need
|
|
* to remove the children of the document fragment and add them
|
|
* individually (i.e. we don't add the actual document fragment).
|
|
*/
|
|
if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
|
PRUint32 count = newContent->GetChildCount();
|
|
PRBool do_notify = refContent || !aParent;
|
|
|
|
// Copy the children into a separate array to avoid having to deal with
|
|
// mutations to the fragment while we're inserting.
|
|
nsCOMArray<nsIContent> fragChildren;
|
|
PRUint32 i;
|
|
for (i = 0; i < count; i++) {
|
|
nsIContent* child = newContent->GetChildAt(i);
|
|
NS_ASSERTION(child->GetCurrentDoc() == nsnull,
|
|
"How did we get a child with a current doc?");
|
|
if (!fragChildren.AppendObject(child)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
// Remove the children from the fragment and flag for possible mutations.
|
|
PRBool mutated = PR_FALSE;
|
|
for (i = count; i > 0;) {
|
|
// We don't need to update i if someone mutates the DOM. The only thing
|
|
// that'd happen is that the resulting child list might be unexpected,
|
|
// but we should never crash since RemoveChildAt is out-of-bounds safe.
|
|
nsMutationGuard guard;
|
|
newContent->RemoveChildAt(--i, PR_TRUE);
|
|
mutated = mutated || guard.Mutated(1);
|
|
}
|
|
|
|
nsRefPtr<nsFragmentObserver> fragmentObs;
|
|
if (count && !do_notify) {
|
|
fragmentObs = new nsFragmentObserver(container.GetChildCount(), aParent, aDocument);
|
|
NS_ENSURE_TRUE(fragmentObs, NS_ERROR_OUT_OF_MEMORY);
|
|
fragmentObs->Connect();
|
|
}
|
|
|
|
// If do_notify is true, then we don't have to handle the notifications
|
|
// ourselves... Also, if count is 0 there will be no updates. So we only
|
|
// want an update batch to happen if count is nonzero and do_notify is not
|
|
// true.
|
|
mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL,
|
|
count && !do_notify);
|
|
|
|
// Iterate through the fragment's children, and insert them in the new
|
|
// parent
|
|
for (i = 0; i < count; ++i) {
|
|
// Get the n:th child from the array.
|
|
nsIContent* childContent = fragChildren[i];
|
|
|
|
// If we've had any unexpeted mutations so far we need to recheck that
|
|
// the child can still be inserted.
|
|
if (mutated) {
|
|
// We really only need to update insPos if we *just* got an unexpected
|
|
// mutation as opposed to 3 insertions ago. But this is an edgecase so
|
|
// no need to over optimize.
|
|
insPos = refContent ? container.IndexOf(refContent) :
|
|
container.GetChildCount();
|
|
if (insPos < 0) {
|
|
// Someone seriously messed up the childlist. We have no idea
|
|
// where to insert the remaining children, so just bail.
|
|
res = NS_ERROR_DOM_NOT_FOUND_ERR;
|
|
break;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> tmpNode = do_QueryInterface(childContent);
|
|
PRUint16 tmpType = 0;
|
|
tmpNode->GetNodeType(&tmpType);
|
|
|
|
if (childContent->GetParent() || childContent->IsInDoc() ||
|
|
!IsAllowedAsChild(childContent, tmpType, aParent, aDocument, PR_FALSE,
|
|
refContent)) {
|
|
res = NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
nsMutationGuard guard;
|
|
|
|
// XXXbz how come no reparenting here? That seems odd...
|
|
// Insert the child.
|
|
res = container.InsertChildAt(childContent, insPos, do_notify,
|
|
aChildArray);
|
|
|
|
if (NS_FAILED(res)) {
|
|
break;
|
|
}
|
|
|
|
if (fragmentObs) {
|
|
fragmentObs->ChildBound();
|
|
}
|
|
|
|
// Check to see if any evil mutation events mucked around with the
|
|
// child list.
|
|
mutated = mutated || guard.Mutated(1);
|
|
|
|
++insPos;
|
|
}
|
|
|
|
if (NS_FAILED(res)) {
|
|
if (fragmentObs) {
|
|
fragmentObs->Disconnect();
|
|
}
|
|
|
|
// We could try to put the nodes back into the fragment here if we
|
|
// really cared.
|
|
|
|
return res;
|
|
}
|
|
|
|
if (fragmentObs) {
|
|
NS_ASSERTION(count && !do_notify, "Unexpected state");
|
|
fragmentObs->Finish();
|
|
}
|
|
|
|
}
|
|
else {
|
|
PRBool newContentIsXUL = newContent->IsContentOfType(eXUL);
|
|
|
|
// Remove the element from the old parent if one exists
|
|
nsIDocument* oldParentDoc = nsnull;
|
|
nsIContent* oldParentCont = newContent->GetParent();
|
|
if (!oldParentCont) {
|
|
oldParentDoc = newContent->GetCurrentDoc();
|
|
|
|
// See bug 53901. Crappy XUL sometimes lies about being in the document
|
|
if (oldParentDoc && newContentIsXUL && oldParentDoc->IndexOf(newContent) < 0) {
|
|
oldParentDoc = nsnull;
|
|
}
|
|
}
|
|
if (oldParentCont || oldParentDoc) {
|
|
nsContentOrDocument oldParent(oldParentCont, oldParentDoc);
|
|
PRInt32 removeIndex = oldParent.IndexOf(newContent);
|
|
|
|
if (removeIndex < 0) {
|
|
// newContent is anonymous. We can't deal with this, so just bail
|
|
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
|
}
|
|
|
|
NS_ASSERTION(!(oldParent.Equals(container) && removeIndex == insPos),
|
|
"invalid removeIndex");
|
|
|
|
nsMutationGuard guard;
|
|
|
|
res = oldParent.RemoveChildAt(removeIndex, PR_TRUE);
|
|
NS_ENSURE_SUCCESS(res, res);
|
|
|
|
// Adjust insert index if the node we ripped out was a sibling
|
|
// of the node we're inserting before
|
|
if (oldParent.Equals(container) && removeIndex < insPos) {
|
|
--insPos;
|
|
}
|
|
|
|
if (guard.Mutated(1)) {
|
|
insPos = refContent ? container.IndexOf(refContent) :
|
|
container.GetChildCount();
|
|
if (insPos < 0) {
|
|
// Someone seriously messed up the childlist. We have no idea
|
|
// where to insert the new child, so just bail.
|
|
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
|
}
|
|
|
|
if (newContent->GetParent() || newContent->IsInDoc() ||
|
|
!IsAllowedAsChild(newContent, nodeType, aParent, aDocument, PR_FALSE,
|
|
refContent)) {
|
|
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!newContentIsXUL) {
|
|
nsContentUtils::ReparentContentWrapper(newContent, aParent,
|
|
container.GetOwnerDoc(),
|
|
old_doc);
|
|
}
|
|
|
|
res = container.InsertChildAt(newContent, insPos, PR_TRUE, aChildArray);
|
|
NS_ENSURE_SUCCESS(res, res);
|
|
}
|
|
|
|
returnVal.swap(*aReturn);
|
|
|
|
return res;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsGenericElement::RemoveChild(nsIDOMNode *aOldChild, nsIDOMNode **aReturn)
|
|
{
|
|
*aReturn = nsnull;
|
|
|
|
if (!aOldChild) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
nsresult res;
|
|
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(aOldChild, &res));
|
|
|
|
if (NS_FAILED(res)) {
|
|
/*
|
|
* If we're asked to remove something that doesn't support nsIContent
|
|
* it can not be one of our children, i.e. we return NOT_FOUND_ERR.
|
|
*/
|
|
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
|
}
|
|
|
|
PRInt32 pos = IndexOf(content);
|
|
|
|
if (pos < 0) {
|
|
/*
|
|
* aOldChild isn't one of our children.
|
|
*/
|
|
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
|
}
|
|
|
|
res = RemoveChildAt(pos, PR_TRUE);
|
|
|
|
*aReturn = aOldChild;
|
|
NS_ADDREF(aOldChild);
|
|
|
|
return res;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// nsISupports implementation
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsGenericElement)
|
|
NS_INTERFACE_MAP_ENTRY(nsIContent)
|
|
NS_INTERFACE_MAP_ENTRY(nsIStyledContent)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
|
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
|
|
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventReceiver,
|
|
nsDOMEventRTTearoff::Create(this))
|
|
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventTarget,
|
|
nsDOMEventRTTearoff::Create(this))
|
|
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3EventTarget,
|
|
nsDOMEventRTTearoff::Create(this))
|
|
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMNSEventTarget,
|
|
nsDOMEventRTTearoff::Create(this))
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_ADDREF(nsGenericElement)
|
|
NS_IMPL_RELEASE(nsGenericElement)
|
|
|
|
nsresult
|
|
nsGenericElement::PostQueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
{
|
|
nsIDocument *document = GetOwnerDoc();
|
|
if (document) {
|
|
return document->BindingManager()->GetBindingImplementation(this, aIID,
|
|
aInstancePtr);
|
|
}
|
|
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
nsresult
|
|
nsGenericElement::LeaveLink(nsPresContext* aPresContext)
|
|
{
|
|
nsILinkHandler *handler = aPresContext->GetLinkHandler();
|
|
if (!handler) {
|
|
return NS_OK;
|
|
}
|
|
|
|
return handler->OnLeaveLink();
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::TriggerLink(nsPresContext* aPresContext,
|
|
nsLinkVerb aVerb,
|
|
nsIURI* aLinkURI,
|
|
const nsAFlatString& aTargetSpec,
|
|
PRBool aClick,
|
|
PRBool aIsUserTriggered)
|
|
{
|
|
NS_PRECONDITION(aLinkURI, "No link URI");
|
|
nsresult rv = NS_OK;
|
|
|
|
nsIDocument* doc = GetOwnerDoc();
|
|
nsIURI* originURI = nsnull;
|
|
if (doc) {
|
|
originURI = doc->GetDocumentURI();
|
|
}
|
|
NS_ENSURE_TRUE(originURI, NS_ERROR_FAILURE);
|
|
|
|
nsILinkHandler *handler = aPresContext->GetLinkHandler();
|
|
if (!handler) return NS_OK;
|
|
|
|
if (aClick) {
|
|
nsresult proceed = NS_OK;
|
|
// Check that this page is allowed to load this URI.
|
|
nsCOMPtr<nsIScriptSecurityManager> securityManager =
|
|
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
PRUint32 flag = aIsUserTriggered ?
|
|
(PRUint32) nsIScriptSecurityManager::STANDARD :
|
|
(PRUint32) nsIScriptSecurityManager::DISALLOW_FROM_MAIL;
|
|
proceed =
|
|
securityManager->CheckLoadURI(originURI, aLinkURI, flag);
|
|
}
|
|
|
|
// Only pass off the click event if the script security manager
|
|
// says it's ok.
|
|
if (NS_SUCCEEDED(proceed))
|
|
handler->OnLinkClick(this, aVerb, aLinkURI, aTargetSpec.get());
|
|
} else {
|
|
handler->OnOverLink(this, aLinkURI, aTargetSpec.get());
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::AddScriptEventListener(nsIAtom* aAttribute,
|
|
const nsAString& aValue)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsISupports *target = NS_STATIC_CAST(nsIContent *, this);
|
|
PRBool defer = PR_TRUE;
|
|
|
|
nsCOMPtr<nsIEventListenerManager> manager;
|
|
|
|
// Attributes on the body and frameset tags get set on the global object
|
|
if (mNodeInfo->Equals(nsHTMLAtoms::body) ||
|
|
mNodeInfo->Equals(nsHTMLAtoms::frameset)) {
|
|
nsIScriptGlobalObject *sgo;
|
|
|
|
// If we have a document, and it has a script global, add the
|
|
// event listener on the global. If not, proceed as normal.
|
|
// XXXbz should we instead use GetCurrentDoc() here, override
|
|
// BindToTree for those classes and munge event listeners there?
|
|
nsIDocument *document = GetOwnerDoc();
|
|
nsCOMPtr<nsPIDOMWindow> win;
|
|
if (document && (sgo = document->GetScriptGlobalObject()) &&
|
|
(win = do_QueryInterface(sgo)) && win->IsInnerWindow()) {
|
|
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(sgo));
|
|
NS_ENSURE_TRUE(receiver, NS_ERROR_FAILURE);
|
|
|
|
receiver->GetListenerManager(getter_AddRefs(manager));
|
|
|
|
target = sgo;
|
|
defer = PR_FALSE;
|
|
}
|
|
} else {
|
|
GetListenerManager(getter_AddRefs(manager));
|
|
}
|
|
|
|
if (manager) {
|
|
nsIDocument *ownerDoc = GetOwnerDoc();
|
|
|
|
rv =
|
|
manager->AddScriptEventListener(target, aAttribute, aValue, defer,
|
|
!nsContentUtils::IsChromeDoc(ownerDoc));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
const nsAttrName*
|
|
nsGenericElement::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const
|
|
{
|
|
return mAttrsAndChildren.GetExistingAttrNameFromQName(
|
|
NS_ConvertUTF16toUTF8(aStr));
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::CopyInnerTo(nsGenericElement* aDst, PRBool aDeep)
|
|
{
|
|
nsresult rv;
|
|
PRUint32 i, count = mAttrsAndChildren.AttrCount();
|
|
for (i = 0; i < count; ++i) {
|
|
const nsAttrName* name = mAttrsAndChildren.GetSafeAttrNameAt(i);
|
|
const nsAttrValue* value = mAttrsAndChildren.AttrAt(i);
|
|
nsAutoString valStr;
|
|
value->ToString(valStr);
|
|
rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
|
|
name->GetPrefix(), valStr, PR_FALSE);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (!aDeep) {
|
|
return NS_OK;
|
|
}
|
|
|
|
count = mAttrsAndChildren.ChildCount();
|
|
for (i = 0; i < count; ++i) {
|
|
nsCOMPtr<nsIDOMNode> node =
|
|
do_QueryInterface(mAttrsAndChildren.ChildAt(i));
|
|
NS_ASSERTION(node, "child doesn't implement nsIDOMNode");
|
|
|
|
nsCOMPtr<nsIDOMNode> newNode;
|
|
rv = node->CloneNode(PR_TRUE, getter_AddRefs(newNode));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIContent> newContent = do_QueryInterface(newNode);
|
|
rv = aDst->AppendChildTo(newContent, PR_FALSE);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static PRBool
|
|
NodeHasMutationListeners(nsISupports* aNode)
|
|
{
|
|
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(aNode));
|
|
if (rec) {
|
|
nsCOMPtr<nsIEventListenerManager> manager;
|
|
rec->GetListenerManager(getter_AddRefs(manager));
|
|
if (manager) {
|
|
PRBool hasMutationListeners = PR_FALSE;
|
|
manager->HasMutationListeners(&hasMutationListeners);
|
|
if (hasMutationListeners)
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Static helper method
|
|
|
|
PRBool
|
|
nsGenericElement::HasMutationListeners(nsIContent* aContent, PRUint32 aType)
|
|
{
|
|
nsIDocument* doc = aContent->GetDocument();
|
|
if (!doc)
|
|
return PR_FALSE;
|
|
|
|
nsIScriptGlobalObject *global = doc->GetScriptGlobalObject();
|
|
if (!global)
|
|
return PR_FALSE;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(global));
|
|
if (!window)
|
|
return PR_FALSE;
|
|
|
|
if (!window->HasMutationListeners(aType))
|
|
return PR_FALSE;
|
|
|
|
// We know a mutation listener is registered, but it might not
|
|
// be in our chain. Check quickly to see.
|
|
|
|
for (nsIContent* curr = aContent; curr; curr = curr->GetParent())
|
|
if (NodeHasMutationListeners(curr))
|
|
return PR_TRUE;
|
|
|
|
return NodeHasMutationListeners(doc) || NodeHasMutationListeners(window);
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::SetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|
nsIAtom* aPrefix, const nsAString& aValue,
|
|
PRBool aNotify)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aName);
|
|
NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
|
|
"Don't call SetAttr with unknown namespace");
|
|
|
|
if (kNameSpaceID_XLink == aNamespaceID && nsHTMLAtoms::href == aName) {
|
|
// XLink URI(s) might be changing. Drop the link from the map. If it
|
|
// is still style relevant it will be re-added by
|
|
// nsStyleUtil::IsSimpleXlink. Make sure to keep the style system
|
|
// consistent so this remains true! In particular if the style system
|
|
// were to get smarter and not restyling an XLink element if the href
|
|
// doesn't change in a "significant" way, we'd need to do the same
|
|
// significance check here.
|
|
nsIDocument* doc = GetCurrentDoc();
|
|
if (doc) {
|
|
doc->ForgetLink(this);
|
|
}
|
|
}
|
|
|
|
PRBool modification = PR_FALSE;
|
|
nsAutoString oldValue;
|
|
|
|
PRInt32 index = mAttrsAndChildren.IndexOfAttr(aName, aNamespaceID);
|
|
if (index >= 0) {
|
|
modification = PR_TRUE;
|
|
|
|
// Get old value and see if it's the same as new one
|
|
const nsAttrName* attrName = mAttrsAndChildren.GetSafeAttrNameAt(index);
|
|
const nsAttrValue* val = mAttrsAndChildren.AttrAt(index);
|
|
NS_ASSERTION(attrName && val, "attribute is supposed to be there");
|
|
val->ToString(oldValue);
|
|
if (oldValue.Equals(aValue) &&
|
|
aPrefix == attrName->GetPrefix()) {
|
|
// Nothing to do
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// Begin the update _before_ changing the attr value
|
|
nsIDocument *document = GetCurrentDoc();
|
|
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
|
|
|
|
if (aNotify && document) {
|
|
document->AttributeWillChange(this, aNamespaceID, aName);
|
|
}
|
|
|
|
nsresult rv;
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
if (aName == GetIDAttributeName() && !aValue.IsEmpty()) {
|
|
// Store id as atom. id="" means that the element has no id, not that it has
|
|
// an emptystring as the id.
|
|
nsAttrValue attrValue;
|
|
attrValue.ParseAtom(aValue);
|
|
rv = mAttrsAndChildren.SetAndTakeAttr(aName, attrValue);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
else {
|
|
rv = mAttrsAndChildren.SetAttr(aName, aValue);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
else {
|
|
nsCOMPtr<nsINodeInfo> ni;
|
|
rv = mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix,
|
|
aNamespaceID,
|
|
getter_AddRefs(ni));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAttrValue attrVal(aValue);
|
|
rv = mAttrsAndChildren.SetAndTakeAttr(ni, attrVal);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (document) {
|
|
nsRefPtr<nsXBLBinding> binding = document->BindingManager()->GetBinding(this);
|
|
if (binding)
|
|
binding->AttributeChanged(aName, aNamespaceID, PR_FALSE, aNotify);
|
|
|
|
if (aNotify) {
|
|
PRInt32 modHint = modification ? PRInt32(nsIDOMMutationEvent::MODIFICATION)
|
|
: PRInt32(nsIDOMMutationEvent::ADDITION);
|
|
document->AttributeChanged(this, aNamespaceID, aName, modHint);
|
|
|
|
if (HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED)) {
|
|
nsCOMPtr<nsIDOMEventTarget> node =
|
|
do_QueryInterface(NS_STATIC_CAST(nsIContent *, this));
|
|
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED, node);
|
|
|
|
nsAutoString attrName;
|
|
aName->ToString(attrName);
|
|
nsCOMPtr<nsIDOMAttr> attrNode;
|
|
GetAttributeNode(attrName, getter_AddRefs(attrNode));
|
|
mutation.mRelatedNode = attrNode;
|
|
|
|
mutation.mAttrName = aName;
|
|
if (!oldValue.IsEmpty()) {
|
|
mutation.mPrevAttrValue = do_GetAtom(oldValue);
|
|
}
|
|
|
|
if (!aValue.IsEmpty()) {
|
|
mutation.mNewAttrValue = do_GetAtom(aValue);
|
|
}
|
|
|
|
if (modification) {
|
|
mutation.mAttrChange = nsIDOMMutationEvent::MODIFICATION;
|
|
} else {
|
|
mutation.mAttrChange = nsIDOMMutationEvent::ADDITION;
|
|
}
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
HandleDOMEvent(nsnull, &mutation, nsnull,
|
|
NS_EVENT_FLAG_INIT, &status);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aNamespaceID == kNameSpaceID_XMLEvents && aName == nsHTMLAtoms::_event &&
|
|
mNodeInfo->GetDocument()) {
|
|
mNodeInfo->GetDocument()->AddXMLEventsContent(this);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
|
nsAString& aResult) const
|
|
{
|
|
NS_ASSERTION(nsnull != aName, "must have attribute name");
|
|
NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown,
|
|
"must have a real namespace ID!");
|
|
|
|
const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID);
|
|
if (!val) {
|
|
// Since we are returning a success code we'd better do
|
|
// something about the out parameters (someone may have
|
|
// given us a non-empty string).
|
|
aResult.Truncate();
|
|
|
|
return NS_CONTENT_ATTR_NOT_THERE;
|
|
}
|
|
|
|
val->ToString(aResult);
|
|
|
|
return aResult.IsEmpty() ? NS_CONTENT_ATTR_NO_VALUE :
|
|
NS_CONTENT_ATTR_HAS_VALUE;
|
|
}
|
|
|
|
PRBool
|
|
nsGenericElement::HasAttr(PRInt32 aNameSpaceID, nsIAtom* aName) const
|
|
{
|
|
NS_ASSERTION(nsnull != aName, "must have attribute name");
|
|
NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown,
|
|
"must have a real namespace ID!");
|
|
|
|
return mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID) >= 0;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
|
PRBool aNotify)
|
|
{
|
|
NS_ASSERTION(nsnull != aName, "must have attribute name");
|
|
|
|
PRInt32 index = mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID);
|
|
if (index < 0) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIDocument *document = GetCurrentDoc();
|
|
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
|
|
|
|
// we do not need to look for mutation listeners if
|
|
// aNotify is false, because there is no use of that knowledge then
|
|
// (mutation events won't be fired).
|
|
PRBool hasMutationListeners = aNotify && document &&
|
|
HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED);
|
|
// Grab the attr node if needed before we remove it from the attr map
|
|
nsCOMPtr<nsIDOMAttr> attrNode;
|
|
|
|
if (document) {
|
|
if (kNameSpaceID_XLink == aNameSpaceID && nsHTMLAtoms::href == aName) {
|
|
// XLink URI might be changing.
|
|
document->ForgetLink(this);
|
|
}
|
|
|
|
if (aNotify) {
|
|
document->AttributeWillChange(this, aNameSpaceID, aName);
|
|
|
|
if (hasMutationListeners) {
|
|
nsAutoString attrName;
|
|
aName->ToString(attrName);
|
|
GetAttributeNode(attrName, getter_AddRefs(attrNode));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear binding to nsIDOMNamedNodeMap
|
|
nsDOMSlots *slots = GetExistingDOMSlots();
|
|
if (slots && slots->mAttributeMap) {
|
|
slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
|
|
}
|
|
|
|
nsAttrValue oldValue;
|
|
nsresult rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (document) {
|
|
nsRefPtr<nsXBLBinding> binding = document->BindingManager()->GetBinding(this);
|
|
if (binding)
|
|
binding->AttributeChanged(aName, aNameSpaceID, PR_TRUE, aNotify);
|
|
|
|
if (aNotify) {
|
|
document->AttributeChanged(this, aNameSpaceID, aName,
|
|
nsIDOMMutationEvent::REMOVAL);
|
|
|
|
if (hasMutationListeners) {
|
|
nsCOMPtr<nsIDOMEventTarget> node =
|
|
do_QueryInterface(NS_STATIC_CAST(nsIContent *, this));
|
|
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED, node);
|
|
|
|
mutation.mRelatedNode = attrNode;
|
|
mutation.mAttrName = aName;
|
|
|
|
nsAutoString value;
|
|
oldValue.ToString(value);
|
|
if (!value.IsEmpty())
|
|
mutation.mPrevAttrValue = do_GetAtom(value);
|
|
mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
HandleDOMEvent(nsnull, &mutation, nsnull,
|
|
NS_EVENT_FLAG_INIT, &status);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::GetAttrNameAt(PRUint32 aIndex, PRInt32* aNameSpaceID,
|
|
nsIAtom** aName, nsIAtom** aPrefix) const
|
|
{
|
|
const nsAttrName* name = mAttrsAndChildren.GetSafeAttrNameAt(aIndex);
|
|
if (name) {
|
|
*aNameSpaceID = name->NamespaceID();
|
|
NS_ADDREF(*aName = name->LocalName());
|
|
NS_IF_ADDREF(*aPrefix = name->GetPrefix());
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
*aNameSpaceID = kNameSpaceID_None;
|
|
*aName = nsnull;
|
|
*aPrefix = nsnull;
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
|
|
PRUint32
|
|
nsGenericElement::GetAttrCount() const
|
|
{
|
|
return mAttrsAndChildren.AttrCount();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
nsGenericElement::List(FILE* out, PRInt32 aIndent) const
|
|
{
|
|
NS_PRECONDITION(IsInDoc(), "bad content");
|
|
|
|
PRInt32 index;
|
|
for (index = aIndent; --index >= 0; ) fputs(" ", out);
|
|
|
|
nsAutoString buf;
|
|
mNodeInfo->GetQualifiedName(buf);
|
|
fputs(NS_LossyConvertUCS2toASCII(buf).get(), out);
|
|
|
|
fprintf(out, "@%p", (void *)this);
|
|
|
|
PRUint32 attrcount = mAttrsAndChildren.AttrCount();
|
|
for (index = 0; index < attrcount; index++) {
|
|
nsAutoString buffer;
|
|
|
|
// name
|
|
mAttrsAndChildren.GetSafeAttrNameAt(index)->GetQualifiedName(buffer);
|
|
|
|
// value
|
|
buffer.AppendLiteral("=");
|
|
nsAutoString value;
|
|
mAttrsAndChildren.AttrAt(index)->ToString(value);
|
|
buffer.Append(value);
|
|
|
|
fputs(" ", out);
|
|
fputs(NS_LossyConvertUCS2toASCII(buffer).get(), out);
|
|
}
|
|
|
|
fprintf(out, " refcount=%d<", mRefCnt.get());
|
|
|
|
fputs("\n", out);
|
|
PRUint32 kids = GetChildCount();
|
|
|
|
for (index = 0; index < kids; index++) {
|
|
nsIContent *kid = GetChildAt(index);
|
|
kid->List(out, aIndent + 1);
|
|
}
|
|
for (index = aIndent; --index >= 0; ) fputs(" ", out);
|
|
|
|
fputs(">\n", out);
|
|
|
|
nsIDocument *document = GetOwnerDoc();
|
|
if (document) {
|
|
nsIBindingManager* bindingManager = document->BindingManager();
|
|
nsCOMPtr<nsIDOMNodeList> anonymousChildren;
|
|
bindingManager->GetAnonymousNodesFor(NS_CONST_CAST(nsGenericElement*, this),
|
|
getter_AddRefs(anonymousChildren));
|
|
|
|
if (anonymousChildren) {
|
|
PRUint32 length;
|
|
anonymousChildren->GetLength(&length);
|
|
if (length) {
|
|
for (index = aIndent; --index >= 0; ) fputs(" ", out);
|
|
fputs("anonymous-children<\n", out);
|
|
|
|
for (PRUint32 i = 0; i < length; ++i) {
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
anonymousChildren->Item(i, getter_AddRefs(node));
|
|
nsCOMPtr<nsIContent> child = do_QueryInterface(node);
|
|
child->List(out, aIndent + 1);
|
|
}
|
|
|
|
for (index = aIndent; --index >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
}
|
|
}
|
|
|
|
PRBool hasContentList;
|
|
bindingManager->HasContentListFor(NS_CONST_CAST(nsGenericElement*, this),
|
|
&hasContentList);
|
|
|
|
if (hasContentList) {
|
|
nsCOMPtr<nsIDOMNodeList> contentList;
|
|
bindingManager->GetContentListFor(NS_CONST_CAST(nsGenericElement*, this),
|
|
getter_AddRefs(contentList));
|
|
|
|
NS_ASSERTION(contentList != nsnull, "oops, binding manager lied");
|
|
|
|
PRUint32 length;
|
|
contentList->GetLength(&length);
|
|
if (length) {
|
|
for (index = aIndent; --index >= 0; ) fputs(" ", out);
|
|
fputs("content-list<\n", out);
|
|
|
|
for (PRUint32 i = 0; i < length; ++i) {
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
contentList->Item(i, getter_AddRefs(node));
|
|
nsCOMPtr<nsIContent> child = do_QueryInterface(node);
|
|
child->List(out, aIndent + 1);
|
|
}
|
|
|
|
for (index = aIndent; --index >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsGenericElement::DumpContent(FILE* out, PRInt32 aIndent,
|
|
PRBool aDumpAll) const
|
|
{
|
|
}
|
|
#endif
|
|
|
|
PRUint32
|
|
nsGenericElement::GetChildCount() const
|
|
{
|
|
return mAttrsAndChildren.ChildCount();
|
|
}
|
|
|
|
nsIContent *
|
|
nsGenericElement::GetChildAt(PRUint32 aIndex) const
|
|
{
|
|
return mAttrsAndChildren.GetSafeChildAt(aIndex);
|
|
}
|
|
|
|
PRInt32
|
|
nsGenericElement::IndexOf(nsIContent* aPossibleChild) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aPossibleChild, "null ptr");
|
|
|
|
return mAttrsAndChildren.IndexOfChild(aPossibleChild);
|
|
}
|
|
|
|
void
|
|
nsGenericElement::GetContentsAsText(nsAString& aText)
|
|
{
|
|
aText.Truncate();
|
|
PRInt32 children = GetChildCount();
|
|
|
|
nsCOMPtr<nsITextContent> tc;
|
|
|
|
PRInt32 i;
|
|
for (i = 0; i < children; ++i) {
|
|
nsIContent *child = GetChildAt(i);
|
|
|
|
if (child->IsContentOfType(eTEXT)) {
|
|
tc = do_QueryInterface(child);
|
|
|
|
tc->AppendTextTo(aText);
|
|
}
|
|
}
|
|
}
|
|
|
|
void*
|
|
nsGenericElement::GetProperty(nsIAtom *aPropertyName, nsresult *aStatus) const
|
|
{
|
|
nsIDocument *doc = GetOwnerDoc();
|
|
if (!doc)
|
|
return nsnull;
|
|
|
|
return doc->PropertyTable()->GetProperty(this, aPropertyName, aStatus);
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::SetProperty(nsIAtom *aPropertyName,
|
|
void *aValue,
|
|
NSPropertyDtorFunc aDtor)
|
|
{
|
|
nsIDocument *doc = GetOwnerDoc();
|
|
if (!doc)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsresult rv = doc->PropertyTable()->SetProperty(this, aPropertyName,
|
|
aValue, aDtor, nsnull);
|
|
if (NS_SUCCEEDED(rv))
|
|
SetFlags(GENERIC_ELEMENT_HAS_PROPERTIES);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsGenericElement::DeleteProperty(nsIAtom *aPropertyName)
|
|
{
|
|
nsIDocument *doc = GetOwnerDoc();
|
|
if (!doc)
|
|
return nsnull;
|
|
|
|
return doc->PropertyTable()->DeleteProperty(this, aPropertyName);
|
|
}
|
|
|
|
void*
|
|
nsGenericElement::UnsetProperty(nsIAtom *aPropertyName, nsresult *aStatus)
|
|
{
|
|
nsIDocument *doc = GetOwnerDoc();
|
|
if (!doc)
|
|
return nsnull;
|
|
|
|
return doc->PropertyTable()->UnsetProperty(this, aPropertyName, aStatus);
|
|
}
|