Contextual information added to HTML copy and intelligence added to HTML paste in the editor (fixes bugs 47014, 50568 and 46554, and partly (at least) fixes bug 53188). Code written by vidur, jfrancis, jst, akkana. Tested by jfrancis, akkana, vidur, jst, kin. Reviwed (and super reviewed) by waterson, vidur, kin, jfrancis, jst git-svn-id: svn://10.0.0.236/trunk@80681 18797224-902f-48f8-a5cc-f745e15eee43
571 lines
15 KiB
C++
571 lines
15 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* 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 Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
*/
|
|
#include "nsIAttributeContent.h"
|
|
#include "nsGenericElement.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIEventListenerManager.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMRange.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMDocumentFragment.h"
|
|
#include "nsRange.h"
|
|
|
|
#include "nsISelection.h"
|
|
#include "nsIEnumerator.h"
|
|
|
|
|
|
#include "nsCRT.h"
|
|
#include "nsIEventStateManager.h"
|
|
#include "nsIPrivateDOMEvent.h"
|
|
#include "nsISizeOfHandler.h"
|
|
#include "nsDOMEvent.h"
|
|
#include "nsIDOMText.h"
|
|
#include "nsIDOMScriptObjectFactory.h"
|
|
#include "prprf.h"
|
|
#include "nsCOMPtr.h"
|
|
|
|
|
|
#include "nsIContent.h"
|
|
#include "nsTextFragment.h"
|
|
#include "nsVoidArray.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsITextContent.h"
|
|
|
|
class nsIDOMAttr;
|
|
class nsIDOMEventListener;
|
|
class nsIDOMNodeList;
|
|
class nsIEventListenerManager;
|
|
class nsIFrame;
|
|
class nsIStyleContext;
|
|
class nsIStyleRule;
|
|
class nsISupportsArray;
|
|
class nsIDOMText;
|
|
|
|
|
|
// XXX share all id's in this dir
|
|
|
|
NS_DEFINE_IID(kIDOMCharacterDataIID, NS_IDOMCHARACTERDATA_IID);
|
|
|
|
static NS_DEFINE_IID(kIPrivateDOMEventIID, NS_IPRIVATEDOMEVENT_IID);
|
|
static NS_DEFINE_IID(kIEnumeratorIID, NS_IENUMERATOR_IID);
|
|
static NS_DEFINE_IID(kIDOMDocumentIID, NS_IDOMDOCUMENT_IID);
|
|
static NS_DEFINE_IID(kIDOMTextIID, NS_IDOMTEXT_IID);
|
|
static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);
|
|
|
|
|
|
class nsAttributeContent : public nsITextContent, public nsIAttributeContent {
|
|
public:
|
|
friend nsresult NS_NewAttributeContent(nsAttributeContent** aNewFrame);
|
|
|
|
nsAttributeContent();
|
|
virtual ~nsAttributeContent();
|
|
|
|
NS_IMETHOD Init(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttrName);
|
|
|
|
// nsISupports
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// Implementation for nsIContent
|
|
NS_IMETHOD GetDocument(nsIDocument*& aResult) const;
|
|
NS_IMETHOD SetDocument(nsIDocument* aDocument, PRBool aDeep, PRBool aCompileEventHandlers);
|
|
NS_IMETHOD GetParent(nsIContent*& aResult) const;
|
|
NS_IMETHOD SetParent(nsIContent* aParent);
|
|
|
|
NS_IMETHOD IsSynthetic(PRBool& aResult) {
|
|
aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetNameSpaceID(PRInt32& aID) const {
|
|
aID = kNameSpaceID_None;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetTag(nsIAtom*& aResult) const {
|
|
aResult = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetNodeInfo(nsINodeInfo*& aResult) const {
|
|
aResult = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHOD NormalizeAttributeString(const nsAReadableString& aStr,
|
|
nsINodeInfo*& aNodeInfo) {
|
|
aNodeInfo = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD SetFocus(nsIPresContext* aPresContext) { return NS_OK; }
|
|
NS_IMETHOD RemoveFocus(nsIPresContext* aPresContext) { return NS_OK; }
|
|
|
|
NS_IMETHOD GetBindingParent(nsIContent** aContent) {
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD SetBindingParent(nsIContent* aParent) {
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD SetAttribute(PRInt32 aNameSpaceID, nsIAtom* aAttribute, const nsAReadableString& aValue,
|
|
PRBool aNotify) { return NS_OK; }
|
|
NS_IMETHOD SetAttribute(nsINodeInfo *aNodeInfo, const nsAReadableString& aValue,
|
|
PRBool aNotify) { return NS_OK; }
|
|
NS_IMETHOD UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRBool aNotify) { return NS_OK; }
|
|
NS_IMETHOD GetAttribute(PRInt32 aNameSpaceID, nsIAtom *aAttribute, nsAWritableString& aResult) const {return NS_CONTENT_ATTR_NOT_THERE; }
|
|
NS_IMETHOD GetAttribute(PRInt32 aNameSpaceID, nsIAtom *aAttribute, nsIAtom*& aPrefix, nsAWritableString& aResult) const {return NS_CONTENT_ATTR_NOT_THERE; }
|
|
NS_IMETHOD GetAttributeNameAt(PRInt32 aIndex, PRInt32& aNameSpaceID, nsIAtom*& aName, nsIAtom*& aPrefix) const {
|
|
aName = nsnull;
|
|
aPrefix = nsnull;
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
|
|
NS_IMETHOD GetAttributeCount(PRInt32& aResult) const { aResult = 0; return NS_OK; }
|
|
|
|
NS_IMETHOD List(FILE* out, PRInt32 aIndent) const { return NS_OK; }
|
|
NS_IMETHOD DumpContent(FILE* out, PRInt32 aIndent,PRBool aDumpAll) const { return NS_OK; }
|
|
NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const;
|
|
NS_IMETHOD HandleDOMEvent(nsIPresContext* aPresContext,
|
|
nsEvent* aEvent,
|
|
nsIDOMEvent** aDOMEvent,
|
|
PRUint32 aFlags,
|
|
nsEventStatus* aEventStatus);
|
|
|
|
NS_IMETHOD GetContentID(PRUint32* aID) {
|
|
*aID = 0;
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHOD SetContentID(PRUint32 aID) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHOD RangeAdd(nsIDOMRange& aRange);
|
|
NS_IMETHOD RangeRemove(nsIDOMRange& aRange);
|
|
NS_IMETHOD GetRangeList(nsVoidArray*& aResult) const;
|
|
|
|
// Implementation for nsIContent
|
|
NS_IMETHOD CanContainChildren(PRBool& aResult) const { aResult = PR_FALSE; return NS_OK; }
|
|
|
|
NS_IMETHOD ChildCount(PRInt32& aResult) const { aResult = 0; return NS_OK; }
|
|
NS_IMETHOD ChildAt(PRInt32 aIndex, nsIContent*& aResult) const { aResult = nsnull; return NS_OK; }
|
|
NS_IMETHOD IndexOf(nsIContent* aPossibleChild, PRInt32& aResult) const { aResult = -1; return NS_OK; }
|
|
NS_IMETHOD InsertChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify) { return NS_OK; }
|
|
NS_IMETHOD ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify) { return NS_OK; }
|
|
NS_IMETHOD AppendChildTo(nsIContent* aKid, PRBool aNotify) { return NS_OK; }
|
|
NS_IMETHOD RemoveChildAt(PRInt32 aIndex, PRBool aNotify) { return NS_OK; }
|
|
NS_IMETHOD SplitText(PRUint32 aOffset, nsIDOMText** aReturn){ return NS_OK; }
|
|
|
|
///////////////////
|
|
// Implementation for nsITextContent
|
|
NS_IMETHOD GetText(const nsTextFragment** aFragmentsResult);
|
|
NS_IMETHOD GetTextLength(PRInt32* aLengthResult);
|
|
NS_IMETHOD CopyText(nsAWritableString& aResult);
|
|
NS_IMETHOD SetText(const PRUnichar* aBuffer,
|
|
PRInt32 aLength,
|
|
PRBool aNotify);
|
|
NS_IMETHOD SetText(const nsAReadableString& aStr,
|
|
PRBool aNotify);
|
|
NS_IMETHOD SetText(const char* aBuffer,
|
|
PRInt32 aLength,
|
|
PRBool aNotify);
|
|
NS_IMETHOD IsOnlyWhitespace(PRBool* aResult);
|
|
NS_IMETHOD CloneContent(PRBool aCloneText, nsITextContent** aClone);
|
|
|
|
//----------------------------------------
|
|
|
|
void ValidateTextFragment();
|
|
|
|
void ToCString(nsAWritableString& aBuf, PRInt32 aOffset, PRInt32 aLen) const;
|
|
|
|
// Up pointer to the real content object that we are
|
|
// supporting. Sometimes there is work that we just can't do
|
|
// ourselves, so this is needed to ask the real object to do the
|
|
// work.
|
|
nsIContent* mContent;
|
|
nsIDocument* mDocument;
|
|
nsIContent* mParent;
|
|
|
|
nsTextFragment mText;
|
|
PRInt32 mNameSpaceID;
|
|
nsIAtom* mAttrName;
|
|
|
|
};
|
|
|
|
|
|
NS_IMPL_ADDREF(nsAttributeContent)
|
|
|
|
NS_IMPL_RELEASE(nsAttributeContent)
|
|
|
|
|
|
nsresult
|
|
NS_NewAttributeContent(nsIContent** aContent)
|
|
{
|
|
NS_PRECONDITION(aContent, "null OUT ptr");
|
|
if (nsnull == aContent) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsAttributeContent* it = new nsAttributeContent;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return NS_SUCCEEDED(it->QueryInterface(NS_GET_IID(nsIContent), (void **)aContent)) ?
|
|
NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsAttributeContent::nsAttributeContent()
|
|
: mText()
|
|
{
|
|
NS_INIT_REFCNT();
|
|
mDocument = nsnull;
|
|
mParent = nsnull;
|
|
mContent = nsnull;
|
|
mAttrName = nsnull;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
nsAttributeContent::~nsAttributeContent()
|
|
{
|
|
NS_IF_RELEASE(mAttrName);
|
|
//NS_IF_RELEASE(mDocument);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsAttributeContent::Init(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttrName)
|
|
{
|
|
NS_ASSERTION((nsnull == mContent) && (nsnull != aContent), "null ptr");
|
|
mContent = aContent;
|
|
|
|
NS_IF_RELEASE(mAttrName);
|
|
mNameSpaceID = aNameSpaceID;
|
|
mAttrName = aAttrName;
|
|
NS_ADDREF(mAttrName);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @param aIID The name of the class implementing the method
|
|
* @param _classiiddef The name of the #define symbol that defines the IID
|
|
* for the class (e.g. NS_ISUPPORTS_IID)
|
|
*
|
|
*/
|
|
nsresult nsAttributeContent::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsIContent))) {
|
|
*aInstancePtr = (void*) ((nsIContent*)this);
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsITextContent))) {
|
|
*aInstancePtr = (void*) ((nsITextContent*)this);
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsIAttributeContent))) {
|
|
*aInstancePtr = (void*) ((nsIAttributeContent*)this);
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsISupports))) {
|
|
*aInstancePtr = (void*) ((nsISupports*)(nsIContent*)this);
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Implementation of nsIContent
|
|
|
|
|
|
void
|
|
nsAttributeContent::ToCString(nsAWritableString& aBuf, PRInt32 aOffset,
|
|
PRInt32 aLen) const
|
|
{
|
|
}
|
|
|
|
nsresult
|
|
nsAttributeContent::GetDocument(nsIDocument*& aResult) const
|
|
{
|
|
aResult = mDocument;
|
|
NS_IF_ADDREF(mDocument);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsAttributeContent::SetDocument(nsIDocument* aDocument, PRBool aDeep, PRBool aCompileEventHandlers)
|
|
{
|
|
mDocument = aDocument;
|
|
//NS_IF_ADDREF(mDocument);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsAttributeContent::GetParent(nsIContent*& aResult) const
|
|
{
|
|
NS_IF_ADDREF(mParent);
|
|
aResult = mParent;
|
|
return NS_OK;;
|
|
}
|
|
|
|
nsresult
|
|
nsAttributeContent::SetParent(nsIContent* aParent)
|
|
{
|
|
mParent = aParent;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsAttributeContent::HandleDOMEvent(nsIPresContext* aPresContext,
|
|
nsEvent* aEvent,
|
|
nsIDOMEvent** aDOMEvent,
|
|
PRUint32 aFlags,
|
|
nsEventStatus* aEventStatus)
|
|
{
|
|
nsresult ret = NS_OK;
|
|
return ret;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsAttributeContent::RangeAdd(nsIDOMRange& aRange)
|
|
{
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsAttributeContent::RangeRemove(nsIDOMRange& aRange)
|
|
{
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsAttributeContent::GetRangeList(nsVoidArray*& aResult) const
|
|
{
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Implementation of the nsITextContent interface
|
|
|
|
void
|
|
nsAttributeContent::ValidateTextFragment()
|
|
{
|
|
if (nsnull != mContent) {
|
|
nsAutoString result;
|
|
mContent->GetAttribute(mNameSpaceID, mAttrName, result);
|
|
|
|
PRUnichar * text = result.ToNewUnicode();
|
|
mText.SetTo(text, result.Length());
|
|
nsCRT::free(text);
|
|
}
|
|
else {
|
|
mText.SetTo("", 0);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsAttributeContent::GetText(const nsTextFragment** aFragmentsResult)
|
|
{
|
|
ValidateTextFragment();
|
|
if (nsnull != mContent) {
|
|
*aFragmentsResult = &mText;
|
|
return NS_OK;
|
|
}
|
|
// XXX is this a good idea, or should we just return an empty
|
|
// fragment with no data in it?
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult
|
|
nsAttributeContent::GetTextLength(PRInt32* aLengthResult)
|
|
{
|
|
if (!aLengthResult) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
ValidateTextFragment();
|
|
*aLengthResult = mText.GetLength();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsAttributeContent::CopyText(nsAWritableString& aResult)
|
|
{
|
|
ValidateTextFragment();
|
|
if (mText.Is2b()) {
|
|
aResult.Assign(mText.Get2b(), mText.GetLength());
|
|
}
|
|
else {
|
|
aResult.Assign(NS_ConvertASCIItoUCS2(mText.Get1b(), mText.GetLength()).get(), mText.GetLength());
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
// XXX shouldn't these update mContent's attribute?
|
|
nsresult
|
|
nsAttributeContent::SetText(const PRUnichar* aBuffer, PRInt32 aLength,
|
|
PRBool aNotify)
|
|
{
|
|
NS_PRECONDITION((aLength >= 0) && (nsnull != aBuffer), "bad args");
|
|
if (aLength < 0) {
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
if (nsnull == aBuffer) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
mText.SetTo(aBuffer, aLength);
|
|
|
|
// Trigger a reflow
|
|
if (aNotify && (nsnull != mDocument)) {
|
|
mDocument->ContentChanged(mContent, nsnull);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsAttributeContent::SetText(const nsAReadableString& aStr,
|
|
PRBool aNotify)
|
|
{
|
|
mText = aStr;
|
|
|
|
// Trigger a reflow
|
|
if (aNotify && (nsnull != mDocument)) {
|
|
mDocument->ContentChanged(mContent, nsnull);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
// XXX shouldn't these update mContent's attribute?
|
|
nsresult
|
|
nsAttributeContent::SetText(const char* aBuffer,
|
|
PRInt32 aLength,
|
|
PRBool aNotify)
|
|
{
|
|
NS_PRECONDITION((aLength >= 0) && (nsnull != aBuffer), "bad args");
|
|
if (aLength < 0) {
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
if (nsnull == aBuffer) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
mText.SetTo(aBuffer, aLength);
|
|
|
|
// Trigger a reflow
|
|
if (aNotify && (nsnull != mDocument)) {
|
|
mDocument->ContentChanged(mContent, nsnull);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsAttributeContent::IsOnlyWhitespace(PRBool* aResult)
|
|
{
|
|
ValidateTextFragment();
|
|
|
|
nsTextFragment& frag = mText;
|
|
if (frag.Is2b()) {
|
|
const PRUnichar* cp = frag.Get2b();
|
|
const PRUnichar* end = cp + frag.GetLength();
|
|
while (cp < end) {
|
|
PRUnichar ch = *cp++;
|
|
if (!XP_IS_SPACE(ch)) {
|
|
*aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
const char* cp = frag.Get1b();
|
|
const char* end = cp + frag.GetLength();
|
|
while (cp < end) {
|
|
PRUnichar ch = PRUnichar(*(unsigned char*)cp);
|
|
cp++;
|
|
if (!XP_IS_SPACE(ch)) {
|
|
*aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
*aResult = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsAttributeContent::CloneContent(PRBool aCloneText, nsITextContent** aReturn)
|
|
{
|
|
nsresult result = NS_OK;
|
|
nsAttributeContent* it;
|
|
NS_NEWXPCOM(it, nsAttributeContent);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
result = it->QueryInterface(kITextContentIID, (void**) aReturn);
|
|
if (NS_FAILED(result)) {
|
|
return result;
|
|
}
|
|
result = it->Init(mContent, mNameSpaceID, mAttrName);
|
|
if (NS_FAILED(result) || !aCloneText) {
|
|
return result;
|
|
}
|
|
it->mText = mText;
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsAttributeContent::SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const
|
|
{
|
|
if (!aResult) return NS_ERROR_NULL_POINTER;
|
|
*aResult = sizeof(*this);
|
|
return NS_OK;
|
|
}
|