/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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 the Mozilla XTF project.
 *
 * The Initial Developer of the Original Code is
 * Alex Fritze.
 * Portions created by the Initial Developer are Copyright (C) 2004
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Alex Fritze <alex@croczilla.com> (original author)
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the 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 "nsXTFElementWrapper.h"
#include "nsIXTFElement.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsXTFInterfaceAggregator.h"
#include "nsIClassInfo.h"
#include "nsPIDOMWindow.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIDocument.h"
#include "nsHTMLAtoms.h" // XXX only needed for nsHTMLAtoms::id
#include "nsIEventListenerManager.h"
#include "nsIDOMEvent.h"
#include "nsGUIEvent.h"
#include "nsContentUtils.h"
#include "nsIXTFService.h"
#include "nsDOMAttributeMap.h"

nsXTFElementWrapper::nsXTFElementWrapper(nsINodeInfo* aNodeInfo)
    : nsXTFElementWrapperBase(aNodeInfo),
      mNotificationMask(0),
      mIntrinsicState(0)
{
}

nsresult
nsXTFElementWrapper::Init()
{
  PRBool innerHandlesAttribs = PR_FALSE;
  GetXTFElement()->GetIsAttributeHandler(&innerHandlesAttribs);
  if (innerHandlesAttribs)
    mAttributeHandler = do_QueryInterface(GetXTFElement());
  return NS_OK;
}

//----------------------------------------------------------------------
// nsISupports implementation

NS_IMPL_ADDREF_INHERITED(nsXTFElementWrapper,nsXTFElementWrapperBase)
NS_IMPL_RELEASE_INHERITED(nsXTFElementWrapper,nsXTFElementWrapperBase)

NS_IMETHODIMP
nsXTFElementWrapper::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
  nsresult rv;
  
  if(aIID.Equals(NS_GET_IID(nsIClassInfo))) {
    *aInstancePtr = NS_STATIC_CAST(nsIClassInfo*, this);
    NS_ADDREF_THIS();
    return NS_OK;
  }
  else if (aIID.Equals(NS_GET_IID(nsIXTFElementWrapperPrivate))) {
    *aInstancePtr = NS_STATIC_CAST(nsIXTFElementWrapperPrivate*, this);
    NS_ADDREF_THIS();
    return NS_OK;
  }
  else if(aIID.Equals(NS_GET_IID(nsIXTFElementWrapper))) {
    *aInstancePtr = NS_STATIC_CAST(nsIXTFElementWrapper*, this);
    NS_ADDREF_THIS();
    return NS_OK;
  }
  else if (NS_SUCCEEDED(rv = nsXTFElementWrapperBase::QueryInterface(aIID, aInstancePtr))) {
    return rv;
  }
  else {
    // try to get get the interface from our wrapped element:
    nsCOMPtr<nsISupports> inner;
    QueryInterfaceInner(aIID, getter_AddRefs(inner));

    if (inner) {
      rv = NS_NewXTFInterfaceAggregator(aIID, inner,
                                        NS_STATIC_CAST(nsIContent*, this),
                                        aInstancePtr);

      return rv;
    }
  }

  return NS_ERROR_NO_INTERFACE;
}

//----------------------------------------------------------------------
// nsIContent methods:

nsresult
nsXTFElementWrapper::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                nsIContent* aBindingParent,
                                PRBool aCompileEventHandlers)
{
  // XXXbz making up random order for the notifications... Perhaps
  // this api should more closely match BindToTree/UnbindFromTree?
  nsCOMPtr<nsIDOMElement> domParent;
  if (aParent != GetParent()) {
    domParent = do_QueryInterface(aParent);
  }

  nsCOMPtr<nsIDOMDocument> domDocument;
  if (aDocument &&
      (mNotificationMask & (nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT |
                            nsIXTFElement::NOTIFY_DOCUMENT_CHANGED))) {
    domDocument = do_QueryInterface(aDocument);
  }

  if (domDocument &&
      (mNotificationMask & (nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT))) {
    GetXTFElement()->WillChangeDocument(domDocument);
  }

  if (domParent &&
      (mNotificationMask & (nsIXTFElement::NOTIFY_WILL_CHANGE_PARENT))) {
    GetXTFElement()->WillChangeParent(domParent);
  }

  nsresult rv = nsXTFElementWrapperBase::BindToTree(aDocument, aParent,
                                                    aBindingParent,
                                                    aCompileEventHandlers);

  NS_ENSURE_SUCCESS(rv, rv);

  if (domDocument &&
      (mNotificationMask & (nsIXTFElement::NOTIFY_DOCUMENT_CHANGED))) {
    GetXTFElement()->DocumentChanged(domDocument);
  }

  if (domParent &&
      (mNotificationMask & (nsIXTFElement::NOTIFY_PARENT_CHANGED))) {
    GetXTFElement()->ParentChanged(domParent);
  }

  return rv;  
}

void
nsXTFElementWrapper::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
  // XXXbz making up random order for the notifications... Perhaps
  // this api should more closely match BindToTree/UnbindFromTree?
  PRBool inDoc = IsInDoc();
  if (inDoc &&
      (mNotificationMask & nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT)) {
    GetXTFElement()->WillChangeDocument(nsnull);
  }

  PRBool parentChanged = aNullParent && GetParent();

  if (parentChanged &&
      (mNotificationMask & nsIXTFElement::NOTIFY_WILL_CHANGE_PARENT)) {
    GetXTFElement()->WillChangeParent(nsnull);
  }

  nsXTFElementWrapperBase::UnbindFromTree(aDeep, aNullParent);

  if (parentChanged &&
      (mNotificationMask & nsIXTFElement::NOTIFY_PARENT_CHANGED)) {
    GetXTFElement()->ParentChanged(nsnull);
  }

  if (inDoc &&
      (mNotificationMask & nsIXTFElement::NOTIFY_DOCUMENT_CHANGED)) {
    GetXTFElement()->DocumentChanged(nsnull);
  }
}

nsresult
nsXTFElementWrapper::InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
                                   PRBool aNotify)
{
  nsresult rv;

  nsCOMPtr<nsIDOMNode> domKid;
  if (mNotificationMask & (nsIXTFElement::NOTIFY_WILL_INSERT_CHILD |
                           nsIXTFElement::NOTIFY_CHILD_INSERTED))
    domKid = do_QueryInterface(aKid);
  
  if (mNotificationMask & nsIXTFElement::NOTIFY_WILL_INSERT_CHILD)
    GetXTFElement()->WillInsertChild(domKid, aIndex);
  rv = nsXTFElementWrapperBase::InsertChildAt(aKid, aIndex, aNotify);
  if (mNotificationMask & nsIXTFElement::NOTIFY_CHILD_INSERTED)
    GetXTFElement()->ChildInserted(domKid, aIndex);
  
  return rv;
}

nsresult
nsXTFElementWrapper::AppendChildTo(nsIContent* aKid, PRBool aNotify)
{
  nsresult rv;

  nsCOMPtr<nsIDOMNode> domKid;
  if (mNotificationMask & (nsIXTFElement::NOTIFY_WILL_APPEND_CHILD |
                           nsIXTFElement::NOTIFY_CHILD_APPENDED))
    domKid = do_QueryInterface(aKid);
  
  if (mNotificationMask & nsIXTFElement::NOTIFY_WILL_APPEND_CHILD)
    GetXTFElement()->WillAppendChild(domKid);
  rv = nsXTFElementWrapperBase::AppendChildTo(aKid, aNotify);
  if (mNotificationMask & nsIXTFElement::NOTIFY_CHILD_APPENDED)
    GetXTFElement()->ChildAppended(domKid);
  return rv;
}

nsresult
nsXTFElementWrapper::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
{
  nsresult rv;
  if (mNotificationMask & nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD)
    GetXTFElement()->WillRemoveChild(aIndex);
  rv = nsXTFElementWrapperBase::RemoveChildAt(aIndex, aNotify);
  if (mNotificationMask & nsIXTFElement::NOTIFY_CHILD_REMOVED)
    GetXTFElement()->ChildRemoved(aIndex);
  return rv;
}

nsIAtom *
nsXTFElementWrapper::GetIDAttributeName() const
{
  // XXX:
  return nsHTMLAtoms::id;
}

nsresult
nsXTFElementWrapper::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                             nsIAtom* aPrefix, const nsAString& aValue,
                             PRBool aNotify)
{
  nsresult rv;
  
  if (mNotificationMask & nsIXTFElement::NOTIFY_WILL_SET_ATTRIBUTE)
    GetXTFElement()->WillSetAttribute(aName, aValue);

  if (aNameSpaceID==kNameSpaceID_None && HandledByInner(aName)) {
    // XXX we don't do namespaced attributes yet
    if (aNameSpaceID != kNameSpaceID_None) {
      NS_WARNING("setattr: xtf elements don't do namespaced attribs yet!");
      return NS_ERROR_FAILURE;
    }  
    rv = mAttributeHandler->SetAttribute(aName, aValue);
    // XXX mutation events?
  }
  else { // let wrapper handle it
    rv = nsXTFElementWrapperBase::SetAttr(aNameSpaceID, aName, aPrefix, aValue, aNotify);
  }
  
  if (mNotificationMask & nsIXTFElement::NOTIFY_ATTRIBUTE_SET)
    GetXTFElement()->AttributeSet(aName, aValue);
  
  return rv;
}

nsresult
nsXTFElementWrapper::GetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, 
                             nsAString& aResult) const
{
  if (aNameSpaceID==kNameSpaceID_None && HandledByInner(aName)) {
    // XXX we don't do namespaced attributes yet
    if (aNameSpaceID != kNameSpaceID_None) {
      NS_WARNING("getattr: xtf elements don't do namespaced attribs yet!");
      return NS_CONTENT_ATTR_NOT_THERE;
    }
    nsresult rv = mAttributeHandler->GetAttribute(aName, aResult);
    if (NS_FAILED(rv)) return rv;
    if (aResult.IsVoid()) return NS_CONTENT_ATTR_NOT_THERE;
    if (aResult.IsEmpty()) return NS_CONTENT_ATTR_NO_VALUE;
    
    return NS_CONTENT_ATTR_HAS_VALUE;
  }
  else { // try wrapper
    return nsXTFElementWrapperBase::GetAttr(aNameSpaceID, aName, aResult);
  }
}

PRBool
nsXTFElementWrapper::HasAttr(PRInt32 aNameSpaceID, nsIAtom* aName) const
{
  if (aNameSpaceID==kNameSpaceID_None && HandledByInner(aName)) {
    PRBool rval = PR_FALSE;
    mAttributeHandler->HasAttribute(aName, &rval);
    return rval;
  }
  else { // try wrapper
    return nsXTFElementWrapperBase::HasAttr(aNameSpaceID, aName);
  }
}


nsresult
nsXTFElementWrapper::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttr, 
                               PRBool aNotify)
{
  nsresult rv;

  if (mNotificationMask & nsIXTFElement::NOTIFY_WILL_REMOVE_ATTRIBUTE)
    GetXTFElement()->WillRemoveAttribute(aAttr);
  
  if (aNameSpaceID==kNameSpaceID_None && HandledByInner(aAttr)) {
    // XXX we don't do namespaced attributes yet
    if (aNameSpaceID != kNameSpaceID_None) {
      NS_WARNING("setattr: xtf elements don't do namespaced attribs yet!");
      return NS_ERROR_FAILURE;
    }  
    nsDOMSlots *slots = GetExistingDOMSlots();
    if (slots && slots->mAttributeMap) {
      slots->mAttributeMap->DropAttribute(aNameSpaceID, aAttr);
    }
    rv = mAttributeHandler->RemoveAttribute(aAttr);

    // XXX if the RemoveAttribute() call fails, we might end up having removed
    // the attribute from the attribute map even though the attribute is still
    // on the element
    // https://bugzilla.mozilla.org/show_bug.cgi?id=296205

    // XXX mutation events?
  }
  else { // try wrapper
    rv = nsXTFElementWrapperBase::UnsetAttr(aNameSpaceID, aAttr, aNotify);
  }

  if (mNotificationMask & nsIXTFElement::NOTIFY_ATTRIBUTE_REMOVED)
    GetXTFElement()->AttributeRemoved(aAttr);

  return rv;
}

nsresult
nsXTFElementWrapper::GetAttrNameAt(PRUint32 aIndex, PRInt32* aNameSpaceID,
                                   nsIAtom** aName, nsIAtom** aPrefix) const
{
  PRUint32 innerCount=0;
  if (mAttributeHandler) {
    mAttributeHandler->GetAttributeCount(&innerCount);
  }
  
  if (aIndex < innerCount) {
    *aNameSpaceID = kNameSpaceID_None;
    *aPrefix = nsnull;
    return mAttributeHandler->GetAttributeNameAt(aIndex, aName);
  }
  else { // wrapper handles attrib
    return nsXTFElementWrapperBase::GetAttrNameAt(aIndex - innerCount, aNameSpaceID,
                                                  aName, aPrefix);
  }
}

PRUint32
nsXTFElementWrapper::GetAttrCount() const
{
  PRUint32 innerCount = 0;
  if (mAttributeHandler) {
    mAttributeHandler->GetAttributeCount(&innerCount);
  }
  // add wrapper attribs
  return innerCount + nsXTFElementWrapperBase::GetAttrCount();
}

void
nsXTFElementWrapper::BeginAddingChildren()
{
  if (mNotificationMask & nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN)
    GetXTFElement()->BeginAddingChildren();
}

void
nsXTFElementWrapper::DoneAddingChildren()
{
  if (mNotificationMask & nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN)
    GetXTFElement()->DoneAddingChildren();
}

already_AddRefed<nsINodeInfo>
nsXTFElementWrapper::GetExistingAttrNameFromQName(const nsAString& aStr) const
{
  nsINodeInfo* nodeInfo = nsXTFElementWrapperBase::GetExistingAttrNameFromQName(aStr).get();

  // Maybe this attribute is handled by our inner element:
  if (!nodeInfo) {
    nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aStr);
    if (HandledByInner(nameAtom)) 
      mNodeInfo->NodeInfoManager()->GetNodeInfo(nameAtom, nsnull, kNameSpaceID_None, &nodeInfo);
  }
  
  return nodeInfo;
}

PRInt32
nsXTFElementWrapper::IntrinsicState() const
{
  return mIntrinsicState;
}

//----------------------------------------------------------------------
// nsIDOMNode methods:

NS_IMETHODIMP
nsXTFElementWrapper::CloneNode(PRBool aDeep, nsIDOMNode **aResult)
{
  *aResult = nsnull;
  nsCOMPtr<nsIContent> it;
  nsContentUtils::GetXTFServiceWeakRef()->CreateElement(getter_AddRefs(it),
                                                        mNodeInfo);
  if (!it)
    return NS_ERROR_OUT_OF_MEMORY;

  nsCOMPtr<nsIDOMNode> kungFuDeathGrip(do_QueryInterface(it));
  nsXTFElementWrapper* wrapper =
    NS_STATIC_CAST(nsXTFElementWrapper*, NS_STATIC_CAST(nsIContent*, it.get()));
  nsresult rv = CopyInnerTo(wrapper, aDeep);

  if (NS_SUCCEEDED(rv)) {
    if (mAttributeHandler) {
      PRUint32 innerCount = 0;
      mAttributeHandler->GetAttributeCount(&innerCount);
      for (PRUint32 i = 0; i < innerCount; ++i) {
        nsCOMPtr<nsIAtom> attrName;
        mAttributeHandler->GetAttributeNameAt(i, getter_AddRefs(attrName));
        if (attrName) {
          nsAutoString value;
          if (NS_SUCCEEDED(mAttributeHandler->GetAttribute(attrName, value)))
            it->SetAttr(kNameSpaceID_None, attrName, value, PR_TRUE);
        }
      }
    }
    kungFuDeathGrip.swap(*aResult);
  }

  wrapper->CloneState(this);
  return rv;
}

//----------------------------------------------------------------------
// nsIDOMElement methods:

NS_IMETHODIMP
nsXTFElementWrapper::GetAttribute(const nsAString& aName, nsAString& aReturn)
{
  const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
  if (name) {
    GetAttr(name->NamespaceID(), name->LocalName(), aReturn);
    return NS_OK;
  }

  // Maybe this attribute is handled by our inner element:
  if (mAttributeHandler) {
    nsresult rv = nsContentUtils::CheckQName(aName, PR_FALSE);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
    if (HandledByInner(nameAtom)) {
      GetAttr(kNameSpaceID_None, nameAtom, aReturn);
      return NS_OK;
    }
  }
  
  SetDOMStringToNull(aReturn);
  return NS_OK;
}

NS_IMETHODIMP
nsXTFElementWrapper::RemoveAttribute(const nsAString& aName)
{
  const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);

  if (name) {
    nsAttrName tmp(*name);
    return UnsetAttr(name->NamespaceID(), name->LocalName(), PR_TRUE);
  }

  // Maybe this attribute is handled by our inner element:
  if (mAttributeHandler) {
    nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
    return UnsetAttr(kNameSpaceID_None, nameAtom, PR_TRUE);
  }

  return NS_OK;
}

NS_IMETHODIMP
nsXTFElementWrapper::HasAttribute(const nsAString& aName, PRBool* aReturn)
{
  const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
  if (name) {
    *aReturn = PR_TRUE;
    return NS_OK;
  }
  
  // Maybe this attribute is handled by our inner element:
  if (mAttributeHandler) {
    nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
    *aReturn = HasAttr(kNameSpaceID_None, nameAtom);
    return NS_OK;
  }

  *aReturn = PR_FALSE;
  return NS_OK;
}


//----------------------------------------------------------------------
// nsIClassInfo implementation

/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */
NS_IMETHODIMP 
nsXTFElementWrapper::GetInterfaces(PRUint32 *count, nsIID * **array)
{
  return GetXTFElement()->GetScriptingInterfaces(count, array);
}

/* nsISupports getHelperForLanguage (in PRUint32 language); */
NS_IMETHODIMP 
nsXTFElementWrapper::GetHelperForLanguage(PRUint32 language,
                                          nsISupports** aHelper)
{
  *aHelper = nsnull;
  nsCOMPtr<nsIClassInfo> ci = 
    do_QueryInterface(nsContentUtils::GetClassInfoInstance(eDOMClassInfo_Element_id));
  return
    ci ? ci->GetHelperForLanguage(language, aHelper) : NS_ERROR_NOT_AVAILABLE;
}

/* readonly attribute string contractID; */
NS_IMETHODIMP 
nsXTFElementWrapper::GetContractID(char * *aContractID)
{
  *aContractID = nsnull;
  return NS_OK;
}

/* readonly attribute string classDescription; */
NS_IMETHODIMP 
nsXTFElementWrapper::GetClassDescription(char * *aClassDescription)
{
  *aClassDescription = nsnull;
  return NS_OK;
}

/* readonly attribute nsCIDPtr classID; */
NS_IMETHODIMP 
nsXTFElementWrapper::GetClassID(nsCID * *aClassID)
{
  *aClassID = nsnull;
  return NS_OK;
}

/* readonly attribute PRUint32 implementationLanguage; */
NS_IMETHODIMP 
nsXTFElementWrapper::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
{
  *aImplementationLanguage = nsIProgrammingLanguage::UNKNOWN;
  return NS_OK;
}

/* readonly attribute PRUint32 flags; */
NS_IMETHODIMP 
nsXTFElementWrapper::GetFlags(PRUint32 *aFlags)
{
  *aFlags = nsIClassInfo::DOM_OBJECT;
  return NS_OK;
}

/* [notxpcom] readonly attribute nsCID classIDNoAlloc; */
NS_IMETHODIMP 
nsXTFElementWrapper::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
{
  return NS_ERROR_NOT_AVAILABLE;
}

//----------------------------------------------------------------------
// nsIXTFElementWrapper implementation:

/* readonly attribute nsIDOMElement elementNode; */
NS_IMETHODIMP
nsXTFElementWrapper::GetElementNode(nsIDOMElement * *aElementNode)
{
  *aElementNode = (nsIDOMElement*)this;
  NS_ADDREF(*aElementNode);
  return NS_OK;
}

/* readonly attribute nsIDOMElement documentFrameElement; */
NS_IMETHODIMP
nsXTFElementWrapper::GetDocumentFrameElement(nsIDOMElement * *aDocumentFrameElement)
{
  *aDocumentFrameElement = nsnull;
  
  nsIDocument *doc = GetCurrentDoc();
  if (!doc) {
    NS_WARNING("no document");
    return NS_OK;
  }
  nsCOMPtr<nsISupports> container = doc->GetContainer();
  if (!container) {
    NS_ERROR("no docshell");
    return NS_ERROR_FAILURE;
  }
  nsCOMPtr<nsPIDOMWindow> pidomwin = do_GetInterface(container);
  if (!pidomwin) {
    NS_ERROR("no nsPIDOMWindow interface on docshell");
    return NS_ERROR_FAILURE;
  }
  *aDocumentFrameElement = pidomwin->GetFrameElementInternal();
  NS_IF_ADDREF(*aDocumentFrameElement);
  return NS_OK;
}

/* attribute unsigned long notificationMask; */
NS_IMETHODIMP
nsXTFElementWrapper::GetNotificationMask(PRUint32 *aNotificationMask)
{
  *aNotificationMask = mNotificationMask;
  return NS_OK;
}
NS_IMETHODIMP
nsXTFElementWrapper::SetNotificationMask(PRUint32 aNotificationMask)
{
  mNotificationMask = aNotificationMask;
  return NS_OK;
}

//----------------------------------------------------------------------
// implementation helpers:
PRBool
nsXTFElementWrapper::QueryInterfaceInner(REFNSIID aIID, void** result)
{
  // We must ensure that the inner element has a distinct xpconnect
  // identity, so we mustn't aggregate nsIXPConnectWrappedJS:
  if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) return PR_FALSE;

  GetXTFElement()->QueryInterface(aIID, result);
  return (*result!=nsnull);
}

PRBool
nsXTFElementWrapper::HandledByInner(nsIAtom *attr) const
{
  PRBool retval = PR_FALSE;
  if (mAttributeHandler)
    mAttributeHandler->HandlesAttribute(attr, &retval);
  return retval;
}

nsresult
nsXTFElementWrapper::HandleDOMEvent(nsPresContext* aPresContext,
                                    nsEvent* aEvent,
                                    nsIDOMEvent** aDOMEvent,
                                    PRUint32 aFlags,
                                    nsEventStatus* aEventStatus)
{
  nsresult rv = nsXTFElementWrapperBase::HandleDOMEvent(aPresContext, aEvent,
                                                        aDOMEvent, aFlags,
                                                        aEventStatus);

  if (NS_FAILED(rv) ||
      (nsEventStatus_eIgnore != *aEventStatus) ||
      !(mNotificationMask & nsIXTFElement::NOTIFY_HANDLE_DEFAULT) ||
      (aFlags & (NS_EVENT_FLAG_SYSTEM_EVENT | NS_EVENT_FLAG_CAPTURE)))
    return rv;

  nsIDOMEvent* domEvent = nsnull;
  if (!aDOMEvent)
    aDOMEvent = &domEvent;

  if (!*aDOMEvent) {
    // We haven't made a DOMEvent yet.  Force making one now.
    nsCOMPtr<nsIEventListenerManager> listenerManager;
    if (NS_FAILED(rv = GetListenerManager(getter_AddRefs(listenerManager))))
      return rv;

    nsAutoString empty;
    if (NS_FAILED(rv = listenerManager->CreateEvent(aPresContext, aEvent,
                                                    empty, aDOMEvent)))
      return rv;
  }
  if (!*aDOMEvent)
    return NS_ERROR_FAILURE;
  
  PRBool defaultHandled = PR_FALSE;
  nsIXTFElement * xtfElement = GetXTFElement();
  if (xtfElement)
    rv = xtfElement->HandleDefault(*aDOMEvent, &defaultHandled);
  if (defaultHandled)
    *aEventStatus = nsEventStatus_eConsumeNoDefault;
  return rv;
}

NS_IMETHODIMP
nsXTFElementWrapper::SetIntrinsicState(PRInt32 aNewState)
{
  nsIDocument *doc = GetCurrentDoc();
  PRInt32 bits = mIntrinsicState ^ aNewState;
  
  if (!doc || !bits)
    return NS_OK;

  mIntrinsicState = aNewState;
  mozAutoDocUpdate(doc, UPDATE_CONTENT_STATE, PR_TRUE);
  doc->ContentStatesChanged(this, nsnull, bits);

  return NS_OK;
}

// nsXTFStyleableElementWrapper

nsXTFStyledElementWrapper::nsXTFStyledElementWrapper(nsINodeInfo* aNodeInfo)
: nsXTFElementWrapper(aNodeInfo)
{
}

nsIAtom *
nsXTFStyledElementWrapper::GetClassAttributeName() const
{
  return mClassAttributeName;
}

const nsAttrValue*
nsXTFStyledElementWrapper::GetClasses() const
{
  const nsAttrValue* val = nsnull;
  nsIAtom* clazzAttr = GetClassAttributeName();
  if (clazzAttr) {
    val = mAttrsAndChildren.GetAttr(clazzAttr);
    // This is possibly the first time we need any classes.
    if (val && val->Type() == nsAttrValue::eString) {
      nsAutoString value;
      val->ToString(value);
      nsAttrValue newValue;
      newValue.ParseAtomArray(value);
      NS_CONST_CAST(nsAttrAndChildArray*, &mAttrsAndChildren)->
        SetAndTakeAttr(clazzAttr, newValue);
    }
  }
  return val;
}

PRBool
nsXTFStyledElementWrapper::HasClass(nsIAtom* aClass, PRBool /*aCaseSensitive*/) const
{
  const nsAttrValue* val = GetClasses();
  if (val) {
    if (val->Type() == nsAttrValue::eAtom) {
      return aClass == val->GetAtomValue();
    }

    if (val->Type() == nsAttrValue::eAtomArray) {
      return val->GetAtomArrayValue()->IndexOf(aClass) >= 0;
    }
  }
  return PR_FALSE;
}

nsresult
nsXTFStyledElementWrapper::SetClassAttributeName(nsIAtom* aName)
{
  // The class attribute name can be set only once
  if (mClassAttributeName || !aName)
    return NS_ERROR_FAILURE;
  
  mClassAttributeName = aName;
  return NS_OK;
}
