Files
Mozilla/mozilla/layout/xml/content/src/nsXMLElement.cpp
dbaron%fas.harvard.edu b4de65c19e Clean up easily fixed unnecessary includes and exports of implementation header files in layout. b=63766 r=jst@netscape.com sr=buster@netscape.com
git-svn-id: svn://10.0.0.236/trunk@84233 18797224-902f-48f8-a5cc-f745e15eee43
2000-12-30 19:22:22 +00:00

691 lines
20 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 "nsXMLElement.h"
#include "nsIDocument.h"
#include "nsIAtom.h"
#include "nsIEventListenerManager.h"
#include "nsIDOMScriptObjectFactory.h"
#include "nsIWebShell.h"
#include "nsIEventStateManager.h"
#include "nsIDOMEvent.h"
#include "nsINameSpace.h"
#include "nsINameSpaceManager.h"
#include "nsINodeInfo.h"
#include "nsIURL.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsXPIDLString.h"
#include "nsIDocShell.h"
#include "nsIScriptSecurityManager.h"
#include "nsIRefreshURI.h"
#include "nsStyleConsts.h"
#include "nsIPresShell.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsIDOMViewCSS.h"
#include "nsIXBLService.h"
#include "nsIXBLBinding.h"
#include "nsIBindingManager.h"
#include "nsIScriptGlobalObject.h"
nsresult
NS_NewXMLElement(nsIXMLContent** aInstancePtrResult, nsINodeInfo *aNodeInfo)
{
NS_ENSURE_ARG_POINTER(aInstancePtrResult);
nsXMLElement* it = new nsXMLElement();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = it->Init(aNodeInfo);
if (NS_FAILED(rv)) {
delete it;
return rv;
}
*aInstancePtrResult = NS_STATIC_CAST(nsIXMLContent *, it);
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
static nsIAtom* kSimpleAtom; // XXX these should get moved to nsXMLAtoms
static nsIAtom* kHrefAtom;
static nsIAtom* kShowAtom;
static nsIAtom* kTypeAtom;
static nsIAtom* kBaseAtom;
static nsIAtom* kActuateAtom;
static nsIAtom* kOnLoadAtom;
static nsIAtom* kEmbedAtom;
static PRUint32 kElementCount;
nsXMLElement::nsXMLElement() : mNameSpace(nsnull)
{
mIsLink = PR_FALSE;
if (0 == kElementCount++) {
kSimpleAtom = NS_NewAtom("simple");
kHrefAtom = NS_NewAtom("href");
kShowAtom = NS_NewAtom("show");
kTypeAtom = NS_NewAtom("type");
kBaseAtom = NS_NewAtom("base");
kActuateAtom = NS_NewAtom("actuate");
kOnLoadAtom = NS_NewAtom("onLoad");
kEmbedAtom = NS_NewAtom("embed");
}
}
nsXMLElement::~nsXMLElement()
{
if (0 == --kElementCount) {
NS_RELEASE(kSimpleAtom);
NS_RELEASE(kHrefAtom);
NS_RELEASE(kShowAtom);
NS_RELEASE(kTypeAtom);
NS_RELEASE(kBaseAtom);
NS_RELEASE(kActuateAtom);
NS_RELEASE(kOnLoadAtom);
NS_RELEASE(kEmbedAtom);
}
NS_IF_RELEASE(mNameSpace);
}
NS_IMETHODIMP
nsXMLElement::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
NS_ENSURE_ARG_POINTER(aInstancePtr);
*aInstancePtr = nsnull;
nsresult rv = nsGenericContainerElement::QueryInterface(aIID, aInstancePtr);
if (NS_SUCCEEDED(rv))
return rv;
nsISupports *inst = nsnull;
if (aIID.Equals(NS_GET_IID(nsIDOMNode))) {
inst = NS_STATIC_CAST(nsIDOMNode *, this);
} else if (aIID.Equals(NS_GET_IID(nsIDOMElement))) {
inst = NS_STATIC_CAST(nsIDOMElement *, this);
} else if (aIID.Equals(NS_GET_IID(nsIXMLContent))) {
inst = NS_STATIC_CAST(nsIXMLContent *, this);
} else {
return NS_NOINTERFACE;
}
NS_ADDREF(inst);
*aInstancePtr = inst;
return NS_OK;
}
NS_IMPL_ADDREF_INHERITED(nsXMLElement, nsGenericElement)
NS_IMPL_RELEASE_INHERITED(nsXMLElement, nsGenericElement)
static inline nsresult MakeURI(const char *aSpec, nsIURI *aBase, nsIURI **aURI)
{
nsresult rv;
static NS_DEFINE_CID(ioServCID,NS_IOSERVICE_CID);
NS_WITH_SERVICE(nsIIOService,service,ioServCID,&rv);
if (NS_FAILED(rv))
return rv;
return service->NewURI(aSpec,aBase,aURI);
}
nsresult
nsXMLElement::GetXMLBaseURI(nsIURI **aURI)
{
NS_ABORT_IF_FALSE(aURI,"null ptr");
if (!aURI)
return NS_ERROR_NULL_POINTER;
*aURI = nsnull;
nsresult rv;
nsAutoString base;
nsCOMPtr<nsIContent> content = do_QueryInterface(NS_STATIC_CAST(nsIXMLContent*,this),&rv);
while (NS_SUCCEEDED(rv) && content) {
nsAutoString value;
rv = content->GetAttribute(kNameSpaceID_XML,kBaseAtom,value);
PRInt32 value_len;
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
PRInt32 colon = value.FindChar(':',PR_FALSE);
PRInt32 slash = value.FindChar('/',PR_FALSE);
if (colon > 0 && !( slash >= 0 && slash < colon)) {
// Yay, we have absolute path!
// The complex looking if above is to make sure that we do not erroneously
// think a value of "./this:that" would have a scheme of "./that"
// XXX URL escape?
nsCAutoString str; str.AssignWithConversion(value);
rv = MakeURI(str,nsnull,aURI);
if (NS_FAILED(rv))
break;
if (!base.IsEmpty()) {
// XXX URL escape?
str.AssignWithConversion(base.GetUnicode());
nsXPIDLCString resolvedStr;
rv = (*aURI)->Resolve(str, getter_Copies(resolvedStr));
if (NS_FAILED(rv)) break;
rv = (*aURI)->SetSpec(resolvedStr);
}
break;
} else if ((value_len = value.Length()) > 0) {
if (base.Length() > 0) {
if (base[0] == '/') {
// Do nothing, we are waiting for a scheme starting value
} else {
// We do not want to add double / delimiters (although the user is free to do so)
if (value[value_len - 1] != '/')
value.AppendWithConversion('/');
base.Insert(value, 0);
}
} else {
if (value[value_len - 1] != '/')
value.AppendWithConversion('/'); // Add delimiter/make sure we treat this as dir
base = value;
}
}
} // if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
nsCOMPtr<nsIContent> parent;
rv = content->GetParent(*getter_AddRefs(parent));
content = parent;
} // while
if (NS_SUCCEEDED(rv)) {
if (!*aURI && mDocument) {
nsCOMPtr<nsIURI> docBase = dont_AddRef(mDocument->GetDocumentURL());
if (base.IsEmpty()) {
*aURI = docBase.get();
NS_IF_ADDREF(*aURI); // nsCOMPtr releases this once
} else {
// XXX URL escape?
nsCAutoString str; str.AssignWithConversion(base);
rv = MakeURI(str,docBase,aURI);
}
}
} else {
NS_IF_RELEASE(*aURI);
}
return rv;
}
NS_IMETHODIMP
nsXMLElement::SetAttribute(nsINodeInfo *aNodeInfo,
const nsAReadableString& aValue,
PRBool aNotify)
{
NS_ENSURE_ARG_POINTER(aNodeInfo);
if (aNodeInfo->Equals(kTypeAtom, kNameSpaceID_XLink)) {
const PRUnichar* simpleStr;
kSimpleAtom->GetUnicode(&simpleStr);
if (aValue.Equals(simpleStr)) {
// NOTE: This really is a link according to the XLink spec,
// we do not need to check other attributes. If there
// is no href attribute, then this link is simply
// untraversible [XLink 3.2].
// XXX If a parent of this element is already a simple link, then this
// must not create a link of its own, this is just a normal element
// inside the parent simple XLink element [XLink 3.2].
mIsLink = PR_TRUE;
} else {
mIsLink = PR_FALSE;
}
// We will check for actuate="onLoad" in MaybeTriggerAutoLink
}
return nsGenericContainerElement::SetAttribute(aNodeInfo, aValue, aNotify);
}
static nsresult WebShellToPresContext(nsIWebShell *aShell,
nsIPresContext **aPresContext)
{
*aPresContext = nsnull;
nsresult rv;
nsCOMPtr<nsIDocShell> ds = do_QueryInterface(aShell,&rv);
if (NS_FAILED(rv))
return rv;
return ds->GetPresContext(aPresContext);
}
static nsresult CheckLoadURI(nsIURI *aBaseURI, const nsAReadableString& aURI,
nsIURI **aAbsURI)
{
// XXX URL escape?
nsCAutoString str; str.Assign(NS_ConvertUCS2toUTF8(aURI));
*aAbsURI = nsnull;
nsresult rv;
rv = MakeURI(str,aBaseURI,aAbsURI);
if (NS_SUCCEEDED(rv)) {
NS_WITH_SERVICE(nsIScriptSecurityManager,
securityManager,
NS_SCRIPTSECURITYMANAGER_CONTRACTID,
&rv);
if (NS_SUCCEEDED(rv)) {
rv= securityManager->CheckLoadURI(aBaseURI,
*aAbsURI,
nsIScriptSecurityManager::DISALLOW_FROM_MAIL);
}
}
if (NS_FAILED(rv)) {
NS_IF_RELEASE(*aAbsURI);
}
return rv;
}
static inline nsresult SpecialAutoLoadReturn(nsresult aRv, nsLinkVerb aVerb)
{
if (NS_SUCCEEDED(aRv)) {
switch(aVerb) {
case eLinkVerb_Embed:
aRv = NS_XML_AUTOLINK_EMBED;
break;
case eLinkVerb_New:
aRv = NS_XML_AUTOLINK_NEW;
break;
case eLinkVerb_Replace:
aRv = NS_XML_AUTOLINK_REPLACE;
break;
default:
aRv = NS_XML_AUTOLINK_UNDEFINED;
break;
}
}
return aRv;
}
NS_IMETHODIMP
nsXMLElement::MaybeTriggerAutoLink(nsIWebShell *aShell)
{
NS_ABORT_IF_FALSE(aShell,"null ptr");
if (!aShell)
return NS_ERROR_NULL_POINTER;
nsresult rv = NS_OK;
if (mIsLink) {
do {
// actuate="onLoad" ?
nsAutoString value;
rv = nsGenericContainerElement::GetAttribute(kNameSpaceID_XLink,
kActuateAtom,
value);
if (rv == NS_CONTENT_ATTR_HAS_VALUE &&
value.EqualsAtom(kOnLoadAtom,PR_FALSE)) {
// show= ?
nsLinkVerb verb = eLinkVerb_Undefined;
rv = nsGenericContainerElement::GetAttribute(kNameSpaceID_XLink,
kShowAtom, value);
if (NS_FAILED(rv))
break;
// XXX Should probably do this using atoms
if (value.EqualsWithConversion("new")) {
verb = eLinkVerb_New;
} else if (value.EqualsWithConversion("replace")) {
// We want to actually stop processing the current document now.
// We do this by returning the correct value so that the one
// that called us knows to stop processing.
verb = eLinkVerb_Replace;
} else if (value.EqualsWithConversion("embed")) {
// XXX TODO
break;
}
// base
nsCOMPtr<nsIURI> base;
rv = GetXMLBaseURI(getter_AddRefs(base));
if (NS_FAILED(rv))
break;
// href= ?
rv = nsGenericContainerElement::GetAttribute(kNameSpaceID_XLink,
kHrefAtom,
value);
if (rv == NS_CONTENT_ATTR_HAS_VALUE && !value.IsEmpty()) {
nsCOMPtr<nsIURI> uri;
rv = CheckLoadURI(base,value,getter_AddRefs(uri));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIPresContext> pc;
rv = WebShellToPresContext(aShell,getter_AddRefs(pc));
if (NS_SUCCEEDED(rv)) {
rv = TriggerLink(pc, verb, base, value, nsAutoString(), PR_TRUE);
return SpecialAutoLoadReturn(rv,verb);
}
}
} // href
}
} while (0);
}
return rv;
}
NS_IMETHODIMP
nsXMLElement::HandleDOMEvent(nsIPresContext* aPresContext,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent,
PRUint32 aFlags,
nsEventStatus* aEventStatus)
{
NS_ENSURE_ARG_POINTER(aEventStatus);
NS_ENSURE_ARG(aPresContext);
// Try script event handlers first
nsresult ret = nsGenericContainerElement::HandleDOMEvent(aPresContext,
aEvent,
aDOMEvent,
aFlags,
aEventStatus);
if (mIsLink && (NS_OK == ret) && (nsEventStatus_eIgnore == *aEventStatus) &&
!(aFlags & NS_EVENT_FLAG_CAPTURE)) {
switch (aEvent->message) {
case NS_MOUSE_LEFT_BUTTON_DOWN:
{
nsIEventStateManager *stateManager;
if (NS_OK == aPresContext->GetEventStateManager(&stateManager)) {
stateManager->SetContentState(this, NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS);
NS_RELEASE(stateManager);
}
*aEventStatus = nsEventStatus_eConsumeDoDefault;
}
break;
case NS_MOUSE_LEFT_CLICK:
{
if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
nsAutoString show, href, target;
nsIURI* baseURL = nsnull;
nsLinkVerb verb = eLinkVerb_Undefined;
nsGenericContainerElement::GetAttribute(kNameSpaceID_XLink,
kHrefAtom,
href);
if (href.IsEmpty()) {
*aEventStatus = nsEventStatus_eConsumeDoDefault;
break;
}
nsGenericContainerElement::GetAttribute(kNameSpaceID_XLink,
kShowAtom,
show);
// XXX Should probably do this using atoms
if (show.EqualsWithConversion("new")) {
verb = eLinkVerb_New;
} else if (show.EqualsWithConversion("replace")) {
verb = eLinkVerb_Replace;
} else if (show.EqualsWithConversion("embed")) {
verb = eLinkVerb_Embed;
}
GetXMLBaseURI(&baseURL);
ret = TriggerLink(aPresContext, verb, baseURL, href, target,
PR_TRUE);
NS_IF_RELEASE(baseURL);
*aEventStatus = nsEventStatus_eConsumeDoDefault;
}
}
break;
case NS_MOUSE_RIGHT_BUTTON_DOWN:
// XXX Bring up a contextual menu provided by the application
break;
case NS_MOUSE_ENTER_SYNTH:
{
nsAutoString href, target;
nsIURI* baseURL = nsnull;
nsGenericContainerElement::GetAttribute(kNameSpaceID_XLink, kHrefAtom,
href);
if (href.IsEmpty()) {
*aEventStatus = nsEventStatus_eConsumeDoDefault;
break;
}
GetXMLBaseURI(&baseURL);
ret = TriggerLink(aPresContext, eLinkVerb_Replace, baseURL, href,
target, PR_FALSE);
NS_IF_RELEASE(baseURL);
*aEventStatus = nsEventStatus_eConsumeDoDefault;
}
break;
// XXX this doesn't seem to do anything yet
case NS_MOUSE_EXIT_SYNTH:
{
nsAutoString empty;
ret = TriggerLink(aPresContext, eLinkVerb_Replace, nsnull, empty,
empty, PR_FALSE);
*aEventStatus = nsEventStatus_eConsumeDoDefault;
}
break;
default:
break;
}
}
return ret;
}
NS_IMETHODIMP
nsXMLElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
nsXMLElement* it = new nsXMLElement();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCOMPtr<nsISupports> kungFuDeathGrip(NS_STATIC_CAST(nsIContent *, this));
nsresult rv = it->Init(mNodeInfo);
if (NS_FAILED(rv)) {
return rv;
}
CopyInnerTo(this, it, aDeep);
it->mNameSpace = mNameSpace;
NS_IF_ADDREF(it->mNameSpace);
return it->QueryInterface(NS_GET_IID(nsIDOMNode), (void**) aReturn);
}
NS_IMETHODIMP
nsXMLElement::GetScriptObject(nsIScriptContext* aContext, void** aScriptObject)
{
nsresult res = NS_OK;
// XXX Yuck! Reaching into the generic content object isn't good.
nsDOMSlots *slots = GetDOMSlots();
if (!slots) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!slots->mScriptObject) {
nsIDOMScriptObjectFactory *factory;
res = nsGenericElement::GetScriptObjectFactory(&factory);
if (NS_FAILED(res)) {
return res;
}
nsAutoString tag;
mNodeInfo->GetQualifiedName(tag);
res = factory->NewScriptXMLElement(tag, aContext,
NS_STATIC_CAST(nsIContent *, this),
mParent, (void**)&slots->mScriptObject);
NS_RELEASE(factory);
if (mDocument && slots->mScriptObject) {
aContext->AddNamedReference((void *)&slots->mScriptObject,
slots->mScriptObject,
"nsXMLElement::mScriptObject");
// See if we have a frame.
nsCOMPtr<nsIPresShell> shell = getter_AddRefs(mDocument->GetShellAt(0));
if (shell) {
nsIFrame* frame;
shell->GetPrimaryFrameFor(this, &frame);
if (!frame) {
// We must ensure that the XBL Binding is installed before we hand
// back this object.
nsCOMPtr<nsIBindingManager> bindingManager;
mDocument->GetBindingManager(getter_AddRefs(bindingManager));
nsCOMPtr<nsIXBLBinding> binding;
bindingManager->GetBinding(this, getter_AddRefs(binding));
if (!binding) {
nsCOMPtr<nsIScriptGlobalObject> global;
mDocument->GetScriptGlobalObject(getter_AddRefs(global));
nsCOMPtr<nsIDOMViewCSS> viewCSS(do_QueryInterface(global));
if (viewCSS) {
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
nsAutoString empty;
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this)));
viewCSS->GetComputedStyle(elt, empty, getter_AddRefs(cssDecl));
if (cssDecl) {
nsAutoString behavior;
behavior.AssignWithConversion("-moz-binding");
nsAutoString value;
cssDecl->GetPropertyValue(behavior, value);
if (!value.IsEmpty()) {
// We have a binding that must be installed.
nsresult rv;
PRBool dummy;
NS_WITH_SERVICE(nsIXBLService, xblService,
"@mozilla.org/xbl;1", &rv);
xblService->LoadBindings(this, value, PR_FALSE,
getter_AddRefs(binding), &dummy);
if (binding) {
binding->ExecuteAttachedHandler();
}
}
}
}
}
}
}
}
}
*aScriptObject = slots->mScriptObject;
return res;
}
NS_IMETHODIMP
nsXMLElement::SetContainingNameSpace(nsINameSpace* aNameSpace)
{
NS_IF_RELEASE(mNameSpace);
mNameSpace = aNameSpace;
NS_IF_ADDREF(mNameSpace);
return NS_OK;
}
NS_IMETHODIMP
nsXMLElement::GetContainingNameSpace(nsINameSpace*& aNameSpace) const
{
aNameSpace = mNameSpace;
NS_IF_ADDREF(aNameSpace);
return NS_OK;
}
// nsIStyledContent implementation
NS_IMETHODIMP
nsXMLElement::GetID(nsIAtom*& aResult) const
{
nsresult rv;
nsCOMPtr<nsIAtom> atom;
rv = mNodeInfo->GetIDAttributeAtom(getter_AddRefs(atom));
aResult = nsnull;
if (NS_SUCCEEDED(rv) && atom) {
nsAutoString value;
rv = nsGenericContainerElement::GetAttribute(kNameSpaceID_Unknown, atom,
value);
if (NS_SUCCEEDED(rv))
aResult = NS_NewAtom(value);
}
return rv;
}
NS_IMETHODIMP
nsXMLElement::SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const
{
if (!aResult) {
return NS_ERROR_NULL_POINTER;
}
#ifdef DEBUG
*aResult = sizeof(*this);
#else
*aResult = 0;
#endif
return NS_OK;
}