gecko-dev/content/xbl/src/nsXBLContentSink.cpp

959 lines
30 KiB
C++
Raw Normal View History

/* -*- 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 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):
* David Hyatt <hyatt@netscape.com> (Original Author)
*
* 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 "nsXBLContentSink.h"
#include "nsIDocument.h"
#include "nsBindingManager.h"
#include "nsIDOMNode.h"
#include "nsIParser.h"
#include "nsGkAtoms.h"
#include "nsINameSpaceManager.h"
#include "nsHTMLTokens.h"
#include "nsIURI.h"
#include "nsTextFragment.h"
#ifdef MOZ_XUL
#include "nsXULElement.h"
#endif
#include "nsXBLProtoImplProperty.h"
#include "nsXBLProtoImplMethod.h"
#include "nsXBLProtoImplField.h"
#include "nsXBLPrototypeBinding.h"
#include "nsContentUtils.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsNodeInfoManager.h"
2004-06-25 14:35:40 +00:00
#include "nsINodeInfo.h"
#include "nsIPrincipal.h"
nsresult
NS_NewXBLContentSink(nsIXMLContentSink** aResult,
nsIDocument* aDoc,
nsIURI* aURI,
nsISupports* aContainer)
{
NS_ENSURE_ARG_POINTER(aResult);
nsXBLContentSink* it;
NS_NEWXPCOM(it, nsXBLContentSink);
NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
nsresult rv = it->Init(aDoc, aURI, aContainer);
NS_ENSURE_SUCCESS(rv, rv);
return CallQueryInterface(it, aResult);
}
nsXBLContentSink::nsXBLContentSink()
: mState(eXBL_InDocument),
mSecondaryState(eXBL_None),
mDocInfo(nsnull),
mFoundFirstBinding(PR_FALSE),
mIsChromeOrResource(PR_FALSE),
mBinding(nsnull),
mHandler(nsnull),
mImplementation(nsnull),
mImplMember(nsnull),
mProperty(nsnull),
mMethod(nsnull),
mField(nsnull)
{
mPrettyPrintXML = PR_FALSE;
}
nsXBLContentSink::~nsXBLContentSink()
{
}
nsresult
nsXBLContentSink::Init(nsIDocument* aDoc,
nsIURI* aURI,
nsISupports* aContainer)
{
nsresult rv;
rv = nsXMLContentSink::Init(aDoc, aURI, aContainer, nsnull);
return rv;
}
void
nsXBLContentSink::MaybeStartLayout()
{
return;
}
nsresult
nsXBLContentSink::FlushText(PRBool aCreateTextNode,
PRBool* aDidFlush)
{
if (mTextLength == 0) {
if (aDidFlush)
*aDidFlush = PR_FALSE;
return NS_OK;
}
const nsASingleFragmentString& text = Substring(mText, mText+mTextLength);
if (mState == eXBL_InHandlers) {
NS_ASSERTION(mBinding, "Must have binding here");
// Get the text and add it to the event handler.
if (mSecondaryState == eXBL_InHandler)
mHandler->AppendHandlerText(text);
mTextLength = 0;
if (aDidFlush)
*aDidFlush = PR_TRUE;
return NS_OK;
}
else if (mState == eXBL_InImplementation) {
NS_ASSERTION(mBinding, "Must have binding here");
if (mSecondaryState == eXBL_InConstructor ||
mSecondaryState == eXBL_InDestructor) {
// Construct a method for the constructor/destructor.
nsXBLProtoImplMethod* method;
if (mSecondaryState == eXBL_InConstructor)
method = mBinding->GetConstructor();
else
method = mBinding->GetDestructor();
// Get the text and add it to the constructor/destructor.
method->AppendBodyText(text);
}
else if (mSecondaryState == eXBL_InGetter ||
mSecondaryState == eXBL_InSetter) {
// Get the text and add it to the getter/setter
if (mSecondaryState == eXBL_InGetter)
mProperty->AppendGetterText(text);
else
mProperty->AppendSetterText(text);
}
else if (mSecondaryState == eXBL_InBody) {
// Get the text and add it to the method
if (mMethod)
mMethod->AppendBodyText(text);
}
else if (mSecondaryState == eXBL_InField) {
// Get the text and add it to the method
mField->AppendFieldText(text);
}
mTextLength = 0;
if (aDidFlush)
*aDidFlush = PR_TRUE;
return NS_OK;
}
nsIContent* content = GetCurrentContent();
if (content &&
(content->NodeInfo()->NamespaceEquals(kNameSpaceID_XBL) ||
(content->NodeInfo()->NamespaceEquals(kNameSpaceID_XUL) &&
content->Tag() != nsGkAtoms::label &&
content->Tag() != nsGkAtoms::description))) {
PRBool isWS = PR_TRUE;
if (mTextLength > 0) {
const PRUnichar* cp = mText;
const PRUnichar* end = mText + mTextLength;
while (cp < end) {
PRUnichar ch = *cp++;
if (!XP_IS_SPACE(ch)) {
isWS = PR_FALSE;
break;
}
}
}
if (isWS && mTextLength > 0) {
mTextLength = 0;
if (aDidFlush)
*aDidFlush = PR_TRUE;
return NS_OK;
}
}
return nsXMLContentSink::FlushText(aCreateTextNode, aDidFlush);
}
NS_IMETHODIMP
nsXBLContentSink::ReportError(const PRUnichar* aErrorText,
const PRUnichar* aSourceText,
nsIScriptError *aError,
PRBool *_retval)
{
NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
// XXX FIXME This function overrides and calls on
// nsXMLContentSink::ReportError, and probably should die. See bug 347826.
// XXX We should make sure the binding has no effect, but that it also
// gets destroyed properly without leaking. Perhaps we should even
// ensure that the content that was bound is displayed with no
// binding.
#ifdef DEBUG
// Report the error to stderr.
fprintf(stderr,
"\n%s\n%s\n\n",
NS_LossyConvertUTF16toASCII(aErrorText).get(),
NS_LossyConvertUTF16toASCII(aSourceText).get());
#endif
// Most of what this does won't be too useful, but whatever...
// nsXMLContentSink::ReportError will handle the console logging.
return nsXMLContentSink::ReportError(aErrorText,
aSourceText,
aError,
_retval);
}
nsresult
nsXBLContentSink::ReportUnexpectedElement(nsIAtom* aElementName,
PRUint32 aLineNumber)
{
// XXX we should really somehow stop the parse and drop the binding
// instead of just letting the XML sink build the content model like
// we do...
mState = eXBL_Error;
nsAutoString elementName;
aElementName->ToString(elementName);
const PRUnichar* params[] = { elementName.get() };
return nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
"UnexpectedElement",
params, NS_ARRAY_LENGTH(params),
mDocumentURI,
EmptyString() /* source line */,
aLineNumber, 0 /* column number */,
nsIScriptError::errorFlag,
"XBL Content Sink");
}
void
nsXBLContentSink::AddMember(nsXBLProtoImplMember* aMember)
{
// Add this member to our chain.
if (mImplMember)
mImplMember->SetNext(aMember); // Already have a chain. Just append to the end.
else
mImplementation->SetMemberList(aMember); // We're the first member in the chain.
mImplMember = aMember; // Adjust our pointer to point to the new last member in the chain.
}
NS_IMETHODIMP
nsXBLContentSink::HandleStartElement(const PRUnichar *aName,
const PRUnichar **aAtts,
PRUint32 aAttsCount,
PRInt32 aIndex,
PRUint32 aLineNumber)
{
nsresult rv = nsXMLContentSink::HandleStartElement(aName,aAtts,aAttsCount,aIndex,aLineNumber);
if (NS_FAILED(rv))
return rv;
if (mState == eXBL_InBinding && !mBinding) {
rv = ConstructBinding();
if (NS_FAILED(rv))
return rv;
// mBinding may still be null, if the binding had no id. If so,
// we'll deal with that later in the sink.
}
return rv;
}
NS_IMETHODIMP
nsXBLContentSink::HandleEndElement(const PRUnichar *aName)
{
FlushText();
if (mState != eXBL_InDocument) {
PRInt32 nameSpaceID;
nsCOMPtr<nsIAtom> prefix, localName;
nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
getter_AddRefs(localName), &nameSpaceID);
if (nameSpaceID == kNameSpaceID_XBL) {
if (mState == eXBL_Error) {
// Check whether we've opened this tag before; we may not have if
// it was a real XBL tag before the error occured.
if (!GetCurrentContent()->NodeInfo()->Equals(localName,
nameSpaceID)) {
// OK, this tag was never opened as far as the XML sink is
// concerned. Just drop the HandleEndElement
return NS_OK;
}
}
else if (mState == eXBL_InHandlers) {
if (localName == nsGkAtoms::handlers) {
mState = eXBL_InBinding;
mHandler = nsnull;
}
else if (localName == nsGkAtoms::handler)
mSecondaryState = eXBL_None;
return NS_OK;
}
else if (mState == eXBL_InResources) {
if (localName == nsGkAtoms::resources)
mState = eXBL_InBinding;
return NS_OK;
}
else if (mState == eXBL_InImplementation) {
if (localName == nsGkAtoms::implementation)
mState = eXBL_InBinding;
else if (localName == nsGkAtoms::property) {
mSecondaryState = eXBL_None;
mProperty = nsnull;
}
else if (localName == nsGkAtoms::method) {
mSecondaryState = eXBL_None;
mMethod = nsnull;
}
else if (localName == nsGkAtoms::field) {
mSecondaryState = eXBL_None;
mField = nsnull;
}
else if (localName == nsGkAtoms::constructor ||
localName == nsGkAtoms::destructor)
mSecondaryState = eXBL_None;
else if (localName == nsGkAtoms::getter ||
localName == nsGkAtoms::setter)
mSecondaryState = eXBL_InProperty;
else if (localName == nsGkAtoms::parameter ||
localName == nsGkAtoms::body)
mSecondaryState = eXBL_InMethod;
return NS_OK;
}
else if (mState == eXBL_InBindings &&
localName == nsGkAtoms::bindings) {
mState = eXBL_InDocument;
}
nsresult rv = nsXMLContentSink::HandleEndElement(aName);
if (NS_FAILED(rv))
return rv;
if (mState == eXBL_InBinding && localName == nsGkAtoms::binding) {
mState = eXBL_InBindings;
if (mBinding) { // See comment in HandleStartElement()
mBinding->Initialize();
mBinding = nsnull; // Clear our current binding ref.
}
}
return NS_OK;
}
}
return nsXMLContentSink::HandleEndElement(aName);
}
NS_IMETHODIMP
nsXBLContentSink::HandleCDataSection(const PRUnichar *aData,
PRUint32 aLength)
{
if (mState == eXBL_InHandlers || mState == eXBL_InImplementation)
return AddText(aData, aLength);
return nsXMLContentSink::HandleCDataSection(aData, aLength);
}
#define ENSURE_XBL_STATE(_cond) \
PR_BEGIN_MACRO \
if (!(_cond)) { ReportUnexpectedElement(aTagName, aLineNumber); return PR_TRUE; } \
PR_END_MACRO
PRBool
nsXBLContentSink::OnOpenContainer(const PRUnichar **aAtts,
PRUint32 aAttsCount,
PRInt32 aNameSpaceID,
nsIAtom* aTagName,
PRUint32 aLineNumber)
{
if (mState == eXBL_Error) {
return PR_TRUE;
}
if (aNameSpaceID != kNameSpaceID_XBL) {
// Construct non-XBL nodes
return PR_TRUE;
}
PRBool ret = PR_TRUE;
if (aTagName == nsGkAtoms::bindings) {
ENSURE_XBL_STATE(mState == eXBL_InDocument);
NS_NewXBLDocumentInfo(mDocument, &mDocInfo);
if (!mDocInfo) {
mState = eXBL_Error;
return PR_TRUE;
}
2001-11-05 06:45:04 +00:00
mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
2001-11-05 06:45:04 +00:00
nsIURI *uri = mDocument->GetDocumentURI();
PRBool isChrome = PR_FALSE;
PRBool isRes = PR_FALSE;
uri->SchemeIs("chrome", &isChrome);
uri->SchemeIs("resource", &isRes);
mIsChromeOrResource = isChrome || isRes;
nsIXBLDocumentInfo* info = mDocInfo;
NS_RELEASE(info); // We keep a weak ref. We've created a cycle between doc/binding manager/doc info.
mState = eXBL_InBindings;
}
else if (aTagName == nsGkAtoms::binding) {
ENSURE_XBL_STATE(mState == eXBL_InBindings);
mState = eXBL_InBinding;
}
else if (aTagName == nsGkAtoms::handlers) {
ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
mState = eXBL_InHandlers;
ret = PR_FALSE;
}
else if (aTagName == nsGkAtoms::handler) {
ENSURE_XBL_STATE(mState == eXBL_InHandlers);
mSecondaryState = eXBL_InHandler;
ConstructHandler(aAtts, aLineNumber);
ret = PR_FALSE;
}
else if (aTagName == nsGkAtoms::resources) {
ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
mState = eXBL_InResources;
// Note that this mState will cause us to return false, so no need
// to set ret to false.
}
else if (aTagName == nsGkAtoms::stylesheet || aTagName == nsGkAtoms::image) {
ENSURE_XBL_STATE(mState == eXBL_InResources);
NS_ASSERTION(mBinding, "Must have binding here");
ConstructResource(aAtts, aTagName);
}
else if (aTagName == nsGkAtoms::implementation) {
ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
mState = eXBL_InImplementation;
ConstructImplementation(aAtts);
// Note that this mState will cause us to return false, so no need
// to set ret to false.
}
else if (aTagName == nsGkAtoms::constructor) {
ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
mSecondaryState == eXBL_None);
NS_ASSERTION(mBinding, "Must have binding here");
mSecondaryState = eXBL_InConstructor;
nsXBLProtoImplAnonymousMethod* newMethod =
new nsXBLProtoImplAnonymousMethod();
if (newMethod) {
newMethod->SetLineNumber(aLineNumber);
mBinding->SetConstructor(newMethod);
AddMember(newMethod);
}
}
else if (aTagName == nsGkAtoms::destructor) {
ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
mSecondaryState == eXBL_None);
NS_ASSERTION(mBinding, "Must have binding here");
mSecondaryState = eXBL_InDestructor;
nsXBLProtoImplAnonymousMethod* newMethod =
new nsXBLProtoImplAnonymousMethod();
if (newMethod) {
newMethod->SetLineNumber(aLineNumber);
mBinding->SetDestructor(newMethod);
AddMember(newMethod);
}
}
else if (aTagName == nsGkAtoms::field) {
ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
mSecondaryState == eXBL_None);
NS_ASSERTION(mBinding, "Must have binding here");
mSecondaryState = eXBL_InField;
ConstructField(aAtts, aLineNumber);
}
else if (aTagName == nsGkAtoms::property) {
ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
mSecondaryState == eXBL_None);
NS_ASSERTION(mBinding, "Must have binding here");
mSecondaryState = eXBL_InProperty;
ConstructProperty(aAtts);
}
else if (aTagName == nsGkAtoms::getter) {
ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
mProperty->SetGetterLineNumber(aLineNumber);
mSecondaryState = eXBL_InGetter;
}
else if (aTagName == nsGkAtoms::setter) {
ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
mProperty->SetSetterLineNumber(aLineNumber);
mSecondaryState = eXBL_InSetter;
}
else if (aTagName == nsGkAtoms::method) {
ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
mSecondaryState == eXBL_None);
NS_ASSERTION(mBinding, "Must have binding here");
mSecondaryState = eXBL_InMethod;
ConstructMethod(aAtts);
}
else if (aTagName == nsGkAtoms::parameter) {
ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
ConstructParameter(aAtts);
}
else if (aTagName == nsGkAtoms::body) {
ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
// stash away the line number
mMethod->SetLineNumber(aLineNumber);
mSecondaryState = eXBL_InBody;
}
return ret && mState != eXBL_InResources && mState != eXBL_InImplementation;
}
#undef ENSURE_XBL_STATE
nsresult
nsXBLContentSink::ConstructBinding()
{
nsCOMPtr<nsIContent> binding = GetCurrentContent();
nsAutoString id;
binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
NS_ConvertUTF16toUTF8 cid(id);
nsresult rv = NS_OK;
if (!cid.IsEmpty()) {
mBinding = new nsXBLPrototypeBinding();
if (!mBinding)
return NS_ERROR_OUT_OF_MEMORY;
rv = mBinding->Init(cid, mDocInfo, binding);
if (NS_SUCCEEDED(rv)) {
if (!mFoundFirstBinding) {
mFoundFirstBinding = PR_TRUE;
mDocInfo->SetFirstPrototypeBinding(mBinding);
}
mDocInfo->SetPrototypeBinding(cid, mBinding);
binding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::id, PR_FALSE);
} else {
delete mBinding;
mBinding = nsnull;
}
}
return rv;
}
static PRBool
FindValue(const PRUnichar **aAtts, nsIAtom *aAtom, const PRUnichar **aResult)
{
nsCOMPtr<nsIAtom> prefix, localName;
for (; *aAtts; aAtts += 2) {
PRInt32 nameSpaceID;
nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
getter_AddRefs(localName), &nameSpaceID);
// Is this attribute one of the ones we care about?
if (nameSpaceID == kNameSpaceID_None && localName == aAtom) {
*aResult = aAtts[1];
return PR_TRUE;
}
}
return PR_FALSE;
}
void
nsXBLContentSink::ConstructHandler(const PRUnichar **aAtts, PRUint32 aLineNumber)
{
const PRUnichar* event = nsnull;
const PRUnichar* modifiers = nsnull;
const PRUnichar* button = nsnull;
const PRUnichar* clickcount = nsnull;
const PRUnichar* keycode = nsnull;
const PRUnichar* charcode = nsnull;
const PRUnichar* phase = nsnull;
const PRUnichar* command = nsnull;
const PRUnichar* action = nsnull;
const PRUnichar* group = nsnull;
const PRUnichar* preventdefault = nsnull;
const PRUnichar* allowuntrusted = nsnull;
nsCOMPtr<nsIAtom> prefix, localName;
for (; *aAtts; aAtts += 2) {
PRInt32 nameSpaceID;
nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
getter_AddRefs(localName), &nameSpaceID);
if (nameSpaceID != kNameSpaceID_None) {
continue;
}
// Is this attribute one of the ones we care about?
if (localName == nsGkAtoms::event)
event = aAtts[1];
else if (localName == nsGkAtoms::modifiers)
modifiers = aAtts[1];
else if (localName == nsGkAtoms::button)
button = aAtts[1];
else if (localName == nsGkAtoms::clickcount)
clickcount = aAtts[1];
else if (localName == nsGkAtoms::keycode)
keycode = aAtts[1];
else if (localName == nsGkAtoms::key || localName == nsGkAtoms::charcode)
charcode = aAtts[1];
else if (localName == nsGkAtoms::phase)
phase = aAtts[1];
else if (localName == nsGkAtoms::command)
command = aAtts[1];
else if (localName == nsGkAtoms::action)
action = aAtts[1];
else if (localName == nsGkAtoms::group)
group = aAtts[1];
else if (localName == nsGkAtoms::preventdefault)
preventdefault = aAtts[1];
else if (localName == nsGkAtoms::allowuntrusted)
allowuntrusted = aAtts[1];
}
if (command && !mIsChromeOrResource) {
// Make sure the XBL doc is chrome or resource if we have a command
// shorthand syntax.
mState = eXBL_Error;
nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
"CommandNotInChrome", nsnull, 0,
mDocumentURI,
EmptyString() /* source line */,
aLineNumber, 0 /* column number */,
nsIScriptError::errorFlag,
"XBL Content Sink");
return; // Don't even make this handler.
}
// All of our pointers are now filled in. Construct our handler with all of
// these parameters.
nsXBLPrototypeHandler* newHandler;
newHandler = new nsXBLPrototypeHandler(event, phase, action, command,
keycode, charcode, modifiers, button,
clickcount, group, preventdefault,
allowuntrusted, mBinding);
if (newHandler) {
newHandler->SetLineNumber(aLineNumber);
// Add this handler to our chain of handlers.
if (mHandler) {
// Already have a chain. Just append to the end.
mHandler->SetNextHandler(newHandler);
}
else {
// We're the first handler in the chain.
mBinding->SetPrototypeHandlers(newHandler);
}
// Adjust our mHandler pointer to point to the new last handler in the
// chain.
mHandler = newHandler;
} else {
mState = eXBL_Error;
}
}
void
nsXBLContentSink::ConstructResource(const PRUnichar **aAtts,
nsIAtom* aResourceType)
{
if (!mBinding)
return;
const PRUnichar* src = nsnull;
if (FindValue(aAtts, nsGkAtoms::src, &src)) {
mBinding->AddResource(aResourceType, nsDependentString(src));
}
}
void
nsXBLContentSink::ConstructImplementation(const PRUnichar **aAtts)
{
mImplementation = nsnull;
mImplMember = nsnull;
if (!mBinding)
return;
const PRUnichar* name = nsnull;
nsCOMPtr<nsIAtom> prefix, localName;
for (; *aAtts; aAtts += 2) {
PRInt32 nameSpaceID;
nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
getter_AddRefs(localName), &nameSpaceID);
if (nameSpaceID != kNameSpaceID_None) {
continue;
}
// Is this attribute one of the ones we care about?
if (localName == nsGkAtoms::name) {
name = aAtts[1];
}
else if (localName == nsGkAtoms::implements) {
// Only allow implementation of interfaces via XBL if the principal of
// our XBL document has UniversalXPConnect privileges. No principal
// means no privs!
// XXX this api is so badly tied to JS it's not even funny. We don't
// have a concept of enabling capabilities on a per-principal basis,
// but only on a per-principal-and-JS-stackframe basis! So for now
// this is basically equivalent to testing that we have the system
// principal, since there is no JS stackframe in sight here...
PRBool hasUniversalXPConnect;
nsresult rv = mDocument->NodePrincipal()->
IsCapabilityEnabled("UniversalXPConnect", nsnull,
&hasUniversalXPConnect);
if (NS_SUCCEEDED(rv) && hasUniversalXPConnect) {
mBinding->ConstructInterfaceTable(nsDependentString(aAtts[1]));
}
}
}
NS_NewXBLProtoImpl(mBinding, name, &mImplementation);
}
void
nsXBLContentSink::ConstructField(const PRUnichar **aAtts, PRUint32 aLineNumber)
{
const PRUnichar* name = nsnull;
const PRUnichar* readonly = nsnull;
nsCOMPtr<nsIAtom> prefix, localName;
for (; *aAtts; aAtts += 2) {
PRInt32 nameSpaceID;
nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
getter_AddRefs(localName), &nameSpaceID);
if (nameSpaceID != kNameSpaceID_None) {
continue;
}
// Is this attribute one of the ones we care about?
if (localName == nsGkAtoms::name) {
name = aAtts[1];
}
else if (localName == nsGkAtoms::readonly) {
readonly = aAtts[1];
}
}
if (name) {
// All of our pointers are now filled in. Construct our field with all of
// these parameters.
mField = new nsXBLProtoImplField(name, readonly);
if (mField) {
mField->SetLineNumber(aLineNumber);
AddMember(mField);
}
}
}
void
nsXBLContentSink::ConstructProperty(const PRUnichar **aAtts)
{
const PRUnichar* name = nsnull;
const PRUnichar* readonly = nsnull;
const PRUnichar* onget = nsnull;
const PRUnichar* onset = nsnull;
nsCOMPtr<nsIAtom> prefix, localName;
for (; *aAtts; aAtts += 2) {
PRInt32 nameSpaceID;
nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
getter_AddRefs(localName), &nameSpaceID);
if (nameSpaceID != kNameSpaceID_None) {
continue;
}
// Is this attribute one of the ones we care about?
if (localName == nsGkAtoms::name) {
name = aAtts[1];
}
else if (localName == nsGkAtoms::readonly) {
readonly = aAtts[1];
}
else if (localName == nsGkAtoms::onget) {
onget = aAtts[1];
}
else if (localName == nsGkAtoms::onset) {
onset = aAtts[1];
}
}
if (name) {
// All of our pointers are now filled in. Construct our property with all of
// these parameters.
mProperty = new nsXBLProtoImplProperty(name, onget, onset, readonly);
if (mProperty) {
AddMember(mProperty);
}
}
}
void
nsXBLContentSink::ConstructMethod(const PRUnichar **aAtts)
{
mMethod = nsnull;
const PRUnichar* name = nsnull;
if (FindValue(aAtts, nsGkAtoms::name, &name)) {
mMethod = new nsXBLProtoImplMethod(name);
}
if (mMethod) {
AddMember(mMethod);
}
}
void
nsXBLContentSink::ConstructParameter(const PRUnichar **aAtts)
{
if (!mMethod)
return;
const PRUnichar* name = nsnull;
if (FindValue(aAtts, nsGkAtoms::name, &name)) {
mMethod->AddParameter(nsDependentString(name));
}
}
nsresult
nsXBLContentSink::CreateElement(const PRUnichar** aAtts, PRUint32 aAttsCount,
nsINodeInfo* aNodeInfo, PRUint32 aLineNumber,
nsIContent** aResult, PRBool* aAppendContent)
{
#ifdef MOZ_XUL
if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
#endif
return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
aLineNumber, aResult,
aAppendContent);
#ifdef MOZ_XUL
}
*aAppendContent = PR_TRUE;
nsXULPrototypeElement* prototype = new nsXULPrototypeElement();
if (!prototype)
return NS_ERROR_OUT_OF_MEMORY;
prototype->mNodeInfo = aNodeInfo;
// XXX - we need to do exactly what the XUL content-sink does (eg,
// look for 'type', 'version' etc attributes)
prototype->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
AddAttributesToXULPrototype(aAtts, aAttsCount, prototype);
nsresult rv = nsXULElement::Create(prototype, mDocument, PR_FALSE, aResult);
// XUL prototype elements start with a refcnt of 1 to represent
// ownership by the XUL prototype document. In our case we have no
// prototype document, so release that reference. The Create call
// above took a reference.
prototype->Release();
return rv;
#endif
}
nsresult
nsXBLContentSink::AddAttributes(const PRUnichar** aAtts,
nsIContent* aContent)
{
if (aContent->IsNodeOfType(nsINode::eXUL))
return NS_OK; // Nothing to do, since the proto already has the attrs.
return nsXMLContentSink::AddAttributes(aAtts, aContent);
}
#ifdef MOZ_XUL
nsresult
nsXBLContentSink::AddAttributesToXULPrototype(const PRUnichar **aAtts,
PRUint32 aAttsCount,
nsXULPrototypeElement* aElement)
{
// Add tag attributes to the element
nsresult rv;
// Create storage for the attributes
nsXULPrototypeAttribute* attrs = nsnull;
if (aAttsCount > 0) {
attrs = new nsXULPrototypeAttribute[aAttsCount];
if (!attrs)
return NS_ERROR_OUT_OF_MEMORY;
}
aElement->mAttributes = attrs;
aElement->mNumAttributes = aAttsCount;
// Copy the attributes into the prototype
nsCOMPtr<nsIAtom> prefix, localName;
PRUint32 i;
for (i = 0; i < aAttsCount; ++i) {
PRInt32 nameSpaceID;
nsContentUtils::SplitExpatName(aAtts[i * 2], getter_AddRefs(prefix),
getter_AddRefs(localName), &nameSpaceID);
if (nameSpaceID == kNameSpaceID_None) {
attrs[i].mName.SetTo(localName);
}
else {
nsCOMPtr<nsINodeInfo> ni;
mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
getter_AddRefs(ni));
attrs[i].mName.SetTo(ni);
}
rv = aElement->SetAttrAt(i, nsDependentString(aAtts[i * 2 + 1]),
mDocumentURI);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
#endif