mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-04 11:26:09 +00:00
938 lines
30 KiB
C++
938 lines
30 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
|
|
#include "nsXBLContentSink.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsBindingManager.h"
|
|
#include "nsIDOMNode.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsNameSpaceManager.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"
|
|
#include "nsIPrincipal.h"
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
nsresult
|
|
NS_NewXBLContentSink(nsIXMLContentSink** aResult,
|
|
nsIDocument* aDoc,
|
|
nsIURI* aURI,
|
|
nsISupports* aContainer)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
nsRefPtr<nsXBLContentSink> it = new nsXBLContentSink();
|
|
nsresult rv = it->Init(aDoc, aURI, aContainer);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
it.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsXBLContentSink::nsXBLContentSink()
|
|
: mState(eXBL_InDocument),
|
|
mSecondaryState(eXBL_None),
|
|
mDocInfo(nullptr),
|
|
mIsChromeOrResource(false),
|
|
mFoundFirstBinding(false),
|
|
mBinding(nullptr),
|
|
mHandler(nullptr),
|
|
mImplementation(nullptr),
|
|
mImplMember(nullptr),
|
|
mImplField(nullptr),
|
|
mProperty(nullptr),
|
|
mMethod(nullptr),
|
|
mField(nullptr)
|
|
{
|
|
mPrettyPrintXML = false;
|
|
}
|
|
|
|
nsXBLContentSink::~nsXBLContentSink()
|
|
{
|
|
}
|
|
|
|
nsresult
|
|
nsXBLContentSink::Init(nsIDocument* aDoc,
|
|
nsIURI* aURI,
|
|
nsISupports* aContainer)
|
|
{
|
|
nsresult rv;
|
|
rv = nsXMLContentSink::Init(aDoc, aURI, aContainer, nullptr);
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::MaybeStartLayout(bool aIgnorePendingSheets)
|
|
{
|
|
return;
|
|
}
|
|
|
|
nsresult
|
|
nsXBLContentSink::FlushText(bool aReleaseTextNode)
|
|
{
|
|
if (mTextLength != 0) {
|
|
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;
|
|
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
|
|
if (mField)
|
|
mField->AppendFieldText(text);
|
|
}
|
|
mTextLength = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIContent* content = GetCurrentContent();
|
|
if (content &&
|
|
(content->NodeInfo()->NamespaceEquals(kNameSpaceID_XBL) ||
|
|
(content->IsXULElement() &&
|
|
!content->IsAnyOfXULElements(nsGkAtoms::label,
|
|
nsGkAtoms::description)))) {
|
|
|
|
bool isWS = true;
|
|
if (mTextLength > 0) {
|
|
const char16_t* cp = mText;
|
|
const char16_t* end = mText + mTextLength;
|
|
while (cp < end) {
|
|
char16_t ch = *cp++;
|
|
if (!dom::IsSpaceCharacter(ch)) {
|
|
isWS = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isWS && mTextLength > 0) {
|
|
mTextLength = 0;
|
|
// Make sure to drop the textnode, if any
|
|
return nsXMLContentSink::FlushText(aReleaseTextNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nsXMLContentSink::FlushText(aReleaseTextNode);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXBLContentSink::ReportError(const char16_t* aErrorText,
|
|
const char16_t* aSourceText,
|
|
nsIScriptError *aError,
|
|
bool *_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,
|
|
uint32_t 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 char16_t* params[] = { elementName.get() };
|
|
|
|
return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
|
|
NS_LITERAL_CSTRING("XBL Content Sink"),
|
|
mDocument,
|
|
nsContentUtils::eXBL_PROPERTIES,
|
|
"UnexpectedElement",
|
|
params, ArrayLength(params),
|
|
nullptr,
|
|
EmptyString() /* source line */,
|
|
aLineNumber);
|
|
}
|
|
|
|
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.
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::AddField(nsXBLProtoImplField* aField)
|
|
{
|
|
// Add this field to our chain.
|
|
if (mImplField)
|
|
mImplField->SetNext(aField); // Already have a chain. Just append to the end.
|
|
else
|
|
mImplementation->SetFieldList(aField); // We're the first member in the chain.
|
|
|
|
mImplField = aField; // Adjust our pointer to point to the new last field in the chain.
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXBLContentSink::HandleStartElement(const char16_t *aName,
|
|
const char16_t **aAtts,
|
|
uint32_t aAttsCount,
|
|
uint32_t aLineNumber)
|
|
{
|
|
nsresult rv = nsXMLContentSink::HandleStartElement(aName, aAtts, aAttsCount,
|
|
aLineNumber);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (mState == eXBL_InBinding && !mBinding) {
|
|
rv = ConstructBinding(aLineNumber);
|
|
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 char16_t *aName)
|
|
{
|
|
FlushText();
|
|
|
|
if (mState != eXBL_InDocument) {
|
|
int32_t 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 occurred.
|
|
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 = nullptr;
|
|
}
|
|
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 = nullptr;
|
|
}
|
|
else if (localName == nsGkAtoms::method) {
|
|
mSecondaryState = eXBL_None;
|
|
mMethod = nullptr;
|
|
}
|
|
else if (localName == nsGkAtoms::field) {
|
|
mSecondaryState = eXBL_None;
|
|
mField = nullptr;
|
|
}
|
|
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 = nullptr; // Clear our current binding ref.
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return nsXMLContentSink::HandleEndElement(aName);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXBLContentSink::HandleCDataSection(const char16_t *aData,
|
|
uint32_t 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 true; } \
|
|
PR_END_MACRO
|
|
|
|
bool
|
|
nsXBLContentSink::OnOpenContainer(const char16_t **aAtts,
|
|
uint32_t aAttsCount,
|
|
int32_t aNameSpaceID,
|
|
nsIAtom* aTagName,
|
|
uint32_t aLineNumber)
|
|
{
|
|
if (mState == eXBL_Error) {
|
|
return true;
|
|
}
|
|
|
|
if (aNameSpaceID != kNameSpaceID_XBL) {
|
|
// Construct non-XBL nodes
|
|
return true;
|
|
}
|
|
|
|
bool ret = true;
|
|
if (aTagName == nsGkAtoms::bindings) {
|
|
ENSURE_XBL_STATE(mState == eXBL_InDocument);
|
|
|
|
NS_ASSERTION(mDocument, "Must have a document!");
|
|
nsRefPtr<nsXBLDocumentInfo> info = new nsXBLDocumentInfo(mDocument);
|
|
|
|
// We keep a weak ref. We're creating a cycle between doc/binding manager/doc info.
|
|
mDocInfo = info;
|
|
|
|
if (!mDocInfo) {
|
|
mState = eXBL_Error;
|
|
return true;
|
|
}
|
|
|
|
mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
|
|
|
|
nsIURI *uri = mDocument->GetDocumentURI();
|
|
|
|
bool isChrome = false;
|
|
bool isRes = false;
|
|
|
|
uri->SchemeIs("chrome", &isChrome);
|
|
uri->SchemeIs("resource", &isRes);
|
|
mIsChromeOrResource = isChrome || isRes;
|
|
|
|
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 = false;
|
|
}
|
|
else if (aTagName == nsGkAtoms::handler) {
|
|
ENSURE_XBL_STATE(mState == eXBL_InHandlers);
|
|
mSecondaryState = eXBL_InHandler;
|
|
ConstructHandler(aAtts, aLineNumber);
|
|
ret = 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;
|
|
nsAutoString name;
|
|
if (!mCurrentBindingID.IsEmpty()) {
|
|
name.Assign(mCurrentBindingID);
|
|
name.AppendLiteral("_XBL_Constructor");
|
|
} else {
|
|
name.AppendLiteral("XBL_Constructor");
|
|
}
|
|
nsXBLProtoImplAnonymousMethod* newMethod =
|
|
new nsXBLProtoImplAnonymousMethod(name.get());
|
|
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;
|
|
nsAutoString name;
|
|
if (!mCurrentBindingID.IsEmpty()) {
|
|
name.Assign(mCurrentBindingID);
|
|
name.AppendLiteral("_XBL_Destructor");
|
|
} else {
|
|
name.AppendLiteral("XBL_Destructor");
|
|
}
|
|
nsXBLProtoImplAnonymousMethod* newMethod =
|
|
new nsXBLProtoImplAnonymousMethod(name.get());
|
|
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, aLineNumber);
|
|
}
|
|
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(uint32_t aLineNumber)
|
|
{
|
|
nsCOMPtr<nsIContent> binding = GetCurrentContent();
|
|
binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mCurrentBindingID);
|
|
NS_ConvertUTF16toUTF8 cid(mCurrentBindingID);
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
// Don't create a binding with no id. nsXBLPrototypeBinding::Read also
|
|
// performs this check.
|
|
if (!cid.IsEmpty()) {
|
|
mBinding = new nsXBLPrototypeBinding();
|
|
|
|
rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding);
|
|
if (NS_SUCCEEDED(rv) &&
|
|
NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) {
|
|
if (!mFoundFirstBinding) {
|
|
mFoundFirstBinding = true;
|
|
mDocInfo->SetFirstPrototypeBinding(mBinding);
|
|
}
|
|
binding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::id, false);
|
|
} else {
|
|
delete mBinding;
|
|
mBinding = nullptr;
|
|
}
|
|
} else {
|
|
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
|
|
NS_LITERAL_CSTRING("XBL Content Sink"), nullptr,
|
|
nsContentUtils::eXBL_PROPERTIES,
|
|
"MissingIdAttr", nullptr, 0,
|
|
mDocumentURI,
|
|
EmptyString(),
|
|
aLineNumber);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static bool
|
|
FindValue(const char16_t **aAtts, nsIAtom *aAtom, const char16_t **aResult)
|
|
{
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
for (; *aAtts; aAtts += 2) {
|
|
int32_t 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 true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::ConstructHandler(const char16_t **aAtts, uint32_t aLineNumber)
|
|
{
|
|
const char16_t* event = nullptr;
|
|
const char16_t* modifiers = nullptr;
|
|
const char16_t* button = nullptr;
|
|
const char16_t* clickcount = nullptr;
|
|
const char16_t* keycode = nullptr;
|
|
const char16_t* charcode = nullptr;
|
|
const char16_t* phase = nullptr;
|
|
const char16_t* command = nullptr;
|
|
const char16_t* action = nullptr;
|
|
const char16_t* group = nullptr;
|
|
const char16_t* preventdefault = nullptr;
|
|
const char16_t* allowuntrusted = nullptr;
|
|
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
for (; *aAtts; aAtts += 2) {
|
|
int32_t 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(nsIScriptError::errorFlag,
|
|
NS_LITERAL_CSTRING("XBL Content Sink"),
|
|
mDocument,
|
|
nsContentUtils::eXBL_PROPERTIES,
|
|
"CommandNotInChrome", nullptr, 0,
|
|
nullptr,
|
|
EmptyString() /* source line */,
|
|
aLineNumber);
|
|
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, 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;
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::ConstructResource(const char16_t **aAtts,
|
|
nsIAtom* aResourceType)
|
|
{
|
|
if (!mBinding)
|
|
return;
|
|
|
|
const char16_t* src = nullptr;
|
|
if (FindValue(aAtts, nsGkAtoms::src, &src)) {
|
|
mBinding->AddResource(aResourceType, nsDependentString(src));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::ConstructImplementation(const char16_t **aAtts)
|
|
{
|
|
mImplementation = nullptr;
|
|
mImplMember = nullptr;
|
|
mImplField = nullptr;
|
|
|
|
if (!mBinding)
|
|
return;
|
|
|
|
const char16_t* name = nullptr;
|
|
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
for (; *aAtts; aAtts += 2) {
|
|
int32_t 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 is the system principal.
|
|
if (nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) {
|
|
mBinding->ConstructInterfaceTable(nsDependentString(aAtts[1]));
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_NewXBLProtoImpl(mBinding, name, &mImplementation);
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::ConstructField(const char16_t **aAtts, uint32_t aLineNumber)
|
|
{
|
|
const char16_t* name = nullptr;
|
|
const char16_t* readonly = nullptr;
|
|
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
for (; *aAtts; aAtts += 2) {
|
|
int32_t 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);
|
|
mField->SetLineNumber(aLineNumber);
|
|
AddField(mField);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::ConstructProperty(const char16_t **aAtts, uint32_t aLineNumber)
|
|
{
|
|
const char16_t* name = nullptr;
|
|
const char16_t* readonly = nullptr;
|
|
const char16_t* onget = nullptr;
|
|
const char16_t* onset = nullptr;
|
|
bool exposeToUntrustedContent = false;
|
|
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
for (; *aAtts; aAtts += 2) {
|
|
int32_t 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];
|
|
}
|
|
else if (localName == nsGkAtoms::exposeToUntrustedContent &&
|
|
nsDependentString(aAtts[1]).EqualsLiteral("true"))
|
|
{
|
|
exposeToUntrustedContent = true;
|
|
}
|
|
}
|
|
|
|
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, aLineNumber);
|
|
if (exposeToUntrustedContent) {
|
|
mProperty->SetExposeToUntrustedContent(true);
|
|
}
|
|
AddMember(mProperty);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::ConstructMethod(const char16_t **aAtts)
|
|
{
|
|
mMethod = nullptr;
|
|
|
|
const char16_t* name = nullptr;
|
|
const char16_t* expose = nullptr;
|
|
if (FindValue(aAtts, nsGkAtoms::name, &name)) {
|
|
mMethod = new nsXBLProtoImplMethod(name);
|
|
if (FindValue(aAtts, nsGkAtoms::exposeToUntrustedContent, &expose) &&
|
|
nsDependentString(expose).EqualsLiteral("true"))
|
|
{
|
|
mMethod->SetExposeToUntrustedContent(true);
|
|
}
|
|
}
|
|
|
|
if (mMethod) {
|
|
AddMember(mMethod);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXBLContentSink::ConstructParameter(const char16_t **aAtts)
|
|
{
|
|
if (!mMethod)
|
|
return;
|
|
|
|
const char16_t* name = nullptr;
|
|
if (FindValue(aAtts, nsGkAtoms::name, &name)) {
|
|
mMethod->AddParameter(nsDependentString(name));
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsXBLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
|
|
mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
|
|
nsIContent** aResult, bool* aAppendContent,
|
|
FromParser aFromParser)
|
|
{
|
|
#ifdef MOZ_XUL
|
|
if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
|
|
#endif
|
|
return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
|
|
aLineNumber, aResult,
|
|
aAppendContent, aFromParser);
|
|
#ifdef MOZ_XUL
|
|
}
|
|
|
|
// Note that this needs to match the code in nsXBLPrototypeBinding::ReadContentNode.
|
|
|
|
*aAppendContent = true;
|
|
nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
|
|
|
|
prototype->mNodeInfo = aNodeInfo;
|
|
|
|
AddAttributesToXULPrototype(aAtts, aAttsCount, prototype);
|
|
|
|
Element* result;
|
|
nsresult rv = nsXULElement::Create(prototype, mDocument, false, false, &result);
|
|
*aResult = result;
|
|
return rv;
|
|
#endif
|
|
}
|
|
|
|
nsresult
|
|
nsXBLContentSink::AddAttributes(const char16_t** aAtts,
|
|
nsIContent* aContent)
|
|
{
|
|
if (aContent->IsXULElement())
|
|
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 char16_t **aAtts,
|
|
uint32_t aAttsCount,
|
|
nsXULPrototypeElement* aElement)
|
|
{
|
|
// Add tag attributes to the element
|
|
nsresult rv;
|
|
|
|
// Create storage for the attributes
|
|
nsXULPrototypeAttribute* attrs = nullptr;
|
|
if (aAttsCount > 0) {
|
|
attrs = new nsXULPrototypeAttribute[aAttsCount];
|
|
}
|
|
|
|
aElement->mAttributes = attrs;
|
|
aElement->mNumAttributes = aAttsCount;
|
|
|
|
// Copy the attributes into the prototype
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
|
|
uint32_t i;
|
|
for (i = 0; i < aAttsCount; ++i) {
|
|
int32_t nameSpaceID;
|
|
nsContentUtils::SplitExpatName(aAtts[i * 2], getter_AddRefs(prefix),
|
|
getter_AddRefs(localName), &nameSpaceID);
|
|
|
|
if (nameSpaceID == kNameSpaceID_None) {
|
|
attrs[i].mName.SetTo(localName);
|
|
}
|
|
else {
|
|
nsRefPtr<NodeInfo> ni;
|
|
ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
|
|
nsIDOMNode::ATTRIBUTE_NODE);
|
|
attrs[i].mName.SetTo(ni);
|
|
}
|
|
|
|
rv = aElement->SetAttrAt(i, nsDependentString(aAtts[i * 2 + 1]),
|
|
mDocumentURI);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
#endif
|