michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "nsXBLContentSink.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsBindingManager.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsIURI.h" michael@0: #include "nsTextFragment.h" michael@0: #ifdef MOZ_XUL michael@0: #include "nsXULElement.h" michael@0: #endif michael@0: #include "nsXBLProtoImplProperty.h" michael@0: #include "nsXBLProtoImplMethod.h" michael@0: #include "nsXBLProtoImplField.h" michael@0: #include "nsXBLPrototypeBinding.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsNodeInfoManager.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "mozilla/dom/Element.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: nsresult michael@0: NS_NewXBLContentSink(nsIXMLContentSink** aResult, michael@0: nsIDocument* aDoc, michael@0: nsIURI* aURI, michael@0: nsISupports* aContainer) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: michael@0: nsXBLContentSink* it = new nsXBLContentSink(); michael@0: NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsCOMPtr kungFuDeathGrip = it; michael@0: nsresult rv = it->Init(aDoc, aURI, aContainer); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return CallQueryInterface(it, aResult); michael@0: } michael@0: michael@0: nsXBLContentSink::nsXBLContentSink() michael@0: : mState(eXBL_InDocument), michael@0: mSecondaryState(eXBL_None), michael@0: mDocInfo(nullptr), michael@0: mIsChromeOrResource(false), michael@0: mFoundFirstBinding(false), michael@0: mBinding(nullptr), michael@0: mHandler(nullptr), michael@0: mImplementation(nullptr), michael@0: mImplMember(nullptr), michael@0: mImplField(nullptr), michael@0: mProperty(nullptr), michael@0: mMethod(nullptr), michael@0: mField(nullptr) michael@0: { michael@0: mPrettyPrintXML = false; michael@0: } michael@0: michael@0: nsXBLContentSink::~nsXBLContentSink() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsXBLContentSink::Init(nsIDocument* aDoc, michael@0: nsIURI* aURI, michael@0: nsISupports* aContainer) michael@0: { michael@0: nsresult rv; michael@0: rv = nsXMLContentSink::Init(aDoc, aURI, aContainer, nullptr); michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::MaybeStartLayout(bool aIgnorePendingSheets) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: nsresult michael@0: nsXBLContentSink::FlushText(bool aReleaseTextNode) michael@0: { michael@0: if (mTextLength != 0) { michael@0: const nsASingleFragmentString& text = Substring(mText, mText+mTextLength); michael@0: if (mState == eXBL_InHandlers) { michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: // Get the text and add it to the event handler. michael@0: if (mSecondaryState == eXBL_InHandler) michael@0: mHandler->AppendHandlerText(text); michael@0: mTextLength = 0; michael@0: return NS_OK; michael@0: } michael@0: else if (mState == eXBL_InImplementation) { michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: if (mSecondaryState == eXBL_InConstructor || michael@0: mSecondaryState == eXBL_InDestructor) { michael@0: // Construct a method for the constructor/destructor. michael@0: nsXBLProtoImplMethod* method; michael@0: if (mSecondaryState == eXBL_InConstructor) michael@0: method = mBinding->GetConstructor(); michael@0: else michael@0: method = mBinding->GetDestructor(); michael@0: michael@0: // Get the text and add it to the constructor/destructor. michael@0: method->AppendBodyText(text); michael@0: } michael@0: else if (mSecondaryState == eXBL_InGetter || michael@0: mSecondaryState == eXBL_InSetter) { michael@0: // Get the text and add it to the getter/setter michael@0: if (mSecondaryState == eXBL_InGetter) michael@0: mProperty->AppendGetterText(text); michael@0: else michael@0: mProperty->AppendSetterText(text); michael@0: } michael@0: else if (mSecondaryState == eXBL_InBody) { michael@0: // Get the text and add it to the method michael@0: if (mMethod) michael@0: mMethod->AppendBodyText(text); michael@0: } michael@0: else if (mSecondaryState == eXBL_InField) { michael@0: // Get the text and add it to the method michael@0: if (mField) michael@0: mField->AppendFieldText(text); michael@0: } michael@0: mTextLength = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIContent* content = GetCurrentContent(); michael@0: if (content && michael@0: (content->NodeInfo()->NamespaceEquals(kNameSpaceID_XBL) || michael@0: (content->NodeInfo()->NamespaceEquals(kNameSpaceID_XUL) && michael@0: content->Tag() != nsGkAtoms::label && michael@0: content->Tag() != nsGkAtoms::description))) { michael@0: michael@0: bool isWS = true; michael@0: if (mTextLength > 0) { michael@0: const char16_t* cp = mText; michael@0: const char16_t* end = mText + mTextLength; michael@0: while (cp < end) { michael@0: char16_t ch = *cp++; michael@0: if (!dom::IsSpaceCharacter(ch)) { michael@0: isWS = false; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (isWS && mTextLength > 0) { michael@0: mTextLength = 0; michael@0: // Make sure to drop the textnode, if any michael@0: return nsXMLContentSink::FlushText(aReleaseTextNode); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return nsXMLContentSink::FlushText(aReleaseTextNode); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXBLContentSink::ReportError(const char16_t* aErrorText, michael@0: const char16_t* aSourceText, michael@0: nsIScriptError *aError, michael@0: bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); michael@0: michael@0: // XXX FIXME This function overrides and calls on michael@0: // nsXMLContentSink::ReportError, and probably should die. See bug 347826. michael@0: michael@0: // XXX We should make sure the binding has no effect, but that it also michael@0: // gets destroyed properly without leaking. Perhaps we should even michael@0: // ensure that the content that was bound is displayed with no michael@0: // binding. michael@0: michael@0: #ifdef DEBUG michael@0: // Report the error to stderr. michael@0: fprintf(stderr, michael@0: "\n%s\n%s\n\n", michael@0: NS_LossyConvertUTF16toASCII(aErrorText).get(), michael@0: NS_LossyConvertUTF16toASCII(aSourceText).get()); michael@0: #endif michael@0: michael@0: // Most of what this does won't be too useful, but whatever... michael@0: // nsXMLContentSink::ReportError will handle the console logging. michael@0: return nsXMLContentSink::ReportError(aErrorText, michael@0: aSourceText, michael@0: aError, michael@0: _retval); michael@0: } michael@0: michael@0: nsresult michael@0: nsXBLContentSink::ReportUnexpectedElement(nsIAtom* aElementName, michael@0: uint32_t aLineNumber) michael@0: { michael@0: // XXX we should really somehow stop the parse and drop the binding michael@0: // instead of just letting the XML sink build the content model like michael@0: // we do... michael@0: mState = eXBL_Error; michael@0: nsAutoString elementName; michael@0: aElementName->ToString(elementName); michael@0: michael@0: const char16_t* params[] = { elementName.get() }; michael@0: michael@0: return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, michael@0: NS_LITERAL_CSTRING("XBL Content Sink"), michael@0: mDocument, michael@0: nsContentUtils::eXBL_PROPERTIES, michael@0: "UnexpectedElement", michael@0: params, ArrayLength(params), michael@0: nullptr, michael@0: EmptyString() /* source line */, michael@0: aLineNumber); michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::AddMember(nsXBLProtoImplMember* aMember) michael@0: { michael@0: // Add this member to our chain. michael@0: if (mImplMember) michael@0: mImplMember->SetNext(aMember); // Already have a chain. Just append to the end. michael@0: else michael@0: mImplementation->SetMemberList(aMember); // We're the first member in the chain. michael@0: michael@0: mImplMember = aMember; // Adjust our pointer to point to the new last member in the chain. michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::AddField(nsXBLProtoImplField* aField) michael@0: { michael@0: // Add this field to our chain. michael@0: if (mImplField) michael@0: mImplField->SetNext(aField); // Already have a chain. Just append to the end. michael@0: else michael@0: mImplementation->SetFieldList(aField); // We're the first member in the chain. michael@0: michael@0: mImplField = aField; // Adjust our pointer to point to the new last field in the chain. michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXBLContentSink::HandleStartElement(const char16_t *aName, michael@0: const char16_t **aAtts, michael@0: uint32_t aAttsCount, michael@0: int32_t aIndex, michael@0: uint32_t aLineNumber) michael@0: { michael@0: nsresult rv = nsXMLContentSink::HandleStartElement(aName,aAtts,aAttsCount,aIndex,aLineNumber); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (mState == eXBL_InBinding && !mBinding) { michael@0: rv = ConstructBinding(aLineNumber); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // mBinding may still be null, if the binding had no id. If so, michael@0: // we'll deal with that later in the sink. michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXBLContentSink::HandleEndElement(const char16_t *aName) michael@0: { michael@0: FlushText(); michael@0: michael@0: if (mState != eXBL_InDocument) { michael@0: int32_t nameSpaceID; michael@0: nsCOMPtr prefix, localName; michael@0: nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (nameSpaceID == kNameSpaceID_XBL) { michael@0: if (mState == eXBL_Error) { michael@0: // Check whether we've opened this tag before; we may not have if michael@0: // it was a real XBL tag before the error occurred. michael@0: if (!GetCurrentContent()->NodeInfo()->Equals(localName, michael@0: nameSpaceID)) { michael@0: // OK, this tag was never opened as far as the XML sink is michael@0: // concerned. Just drop the HandleEndElement michael@0: return NS_OK; michael@0: } michael@0: } michael@0: else if (mState == eXBL_InHandlers) { michael@0: if (localName == nsGkAtoms::handlers) { michael@0: mState = eXBL_InBinding; michael@0: mHandler = nullptr; michael@0: } michael@0: else if (localName == nsGkAtoms::handler) michael@0: mSecondaryState = eXBL_None; michael@0: return NS_OK; michael@0: } michael@0: else if (mState == eXBL_InResources) { michael@0: if (localName == nsGkAtoms::resources) michael@0: mState = eXBL_InBinding; michael@0: return NS_OK; michael@0: } michael@0: else if (mState == eXBL_InImplementation) { michael@0: if (localName == nsGkAtoms::implementation) michael@0: mState = eXBL_InBinding; michael@0: else if (localName == nsGkAtoms::property) { michael@0: mSecondaryState = eXBL_None; michael@0: mProperty = nullptr; michael@0: } michael@0: else if (localName == nsGkAtoms::method) { michael@0: mSecondaryState = eXBL_None; michael@0: mMethod = nullptr; michael@0: } michael@0: else if (localName == nsGkAtoms::field) { michael@0: mSecondaryState = eXBL_None; michael@0: mField = nullptr; michael@0: } michael@0: else if (localName == nsGkAtoms::constructor || michael@0: localName == nsGkAtoms::destructor) michael@0: mSecondaryState = eXBL_None; michael@0: else if (localName == nsGkAtoms::getter || michael@0: localName == nsGkAtoms::setter) michael@0: mSecondaryState = eXBL_InProperty; michael@0: else if (localName == nsGkAtoms::parameter || michael@0: localName == nsGkAtoms::body) michael@0: mSecondaryState = eXBL_InMethod; michael@0: return NS_OK; michael@0: } michael@0: else if (mState == eXBL_InBindings && michael@0: localName == nsGkAtoms::bindings) { michael@0: mState = eXBL_InDocument; michael@0: } michael@0: michael@0: nsresult rv = nsXMLContentSink::HandleEndElement(aName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (mState == eXBL_InBinding && localName == nsGkAtoms::binding) { michael@0: mState = eXBL_InBindings; michael@0: if (mBinding) { // See comment in HandleStartElement() michael@0: mBinding->Initialize(); michael@0: mBinding = nullptr; // Clear our current binding ref. michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return nsXMLContentSink::HandleEndElement(aName); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXBLContentSink::HandleCDataSection(const char16_t *aData, michael@0: uint32_t aLength) michael@0: { michael@0: if (mState == eXBL_InHandlers || mState == eXBL_InImplementation) michael@0: return AddText(aData, aLength); michael@0: return nsXMLContentSink::HandleCDataSection(aData, aLength); michael@0: } michael@0: michael@0: #define ENSURE_XBL_STATE(_cond) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (!(_cond)) { ReportUnexpectedElement(aTagName, aLineNumber); return true; } \ michael@0: PR_END_MACRO michael@0: michael@0: bool michael@0: nsXBLContentSink::OnOpenContainer(const char16_t **aAtts, michael@0: uint32_t aAttsCount, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aTagName, michael@0: uint32_t aLineNumber) michael@0: { michael@0: if (mState == eXBL_Error) { michael@0: return true; michael@0: } michael@0: michael@0: if (aNameSpaceID != kNameSpaceID_XBL) { michael@0: // Construct non-XBL nodes michael@0: return true; michael@0: } michael@0: michael@0: bool ret = true; michael@0: if (aTagName == nsGkAtoms::bindings) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InDocument); michael@0: michael@0: NS_ASSERTION(mDocument, "Must have a document!"); michael@0: nsRefPtr info = new nsXBLDocumentInfo(mDocument); michael@0: michael@0: // We keep a weak ref. We're creating a cycle between doc/binding manager/doc info. michael@0: mDocInfo = info; michael@0: michael@0: if (!mDocInfo) { michael@0: mState = eXBL_Error; michael@0: return true; michael@0: } michael@0: michael@0: mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo); michael@0: michael@0: nsIURI *uri = mDocument->GetDocumentURI(); michael@0: michael@0: bool isChrome = false; michael@0: bool isRes = false; michael@0: michael@0: uri->SchemeIs("chrome", &isChrome); michael@0: uri->SchemeIs("resource", &isRes); michael@0: mIsChromeOrResource = isChrome || isRes; michael@0: michael@0: mState = eXBL_InBindings; michael@0: } michael@0: else if (aTagName == nsGkAtoms::binding) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InBindings); michael@0: mState = eXBL_InBinding; michael@0: } michael@0: else if (aTagName == nsGkAtoms::handlers) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding); michael@0: mState = eXBL_InHandlers; michael@0: ret = false; michael@0: } michael@0: else if (aTagName == nsGkAtoms::handler) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InHandlers); michael@0: mSecondaryState = eXBL_InHandler; michael@0: ConstructHandler(aAtts, aLineNumber); michael@0: ret = false; michael@0: } michael@0: else if (aTagName == nsGkAtoms::resources) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding); michael@0: mState = eXBL_InResources; michael@0: // Note that this mState will cause us to return false, so no need michael@0: // to set ret to false. michael@0: } michael@0: else if (aTagName == nsGkAtoms::stylesheet || aTagName == nsGkAtoms::image) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InResources); michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: ConstructResource(aAtts, aTagName); michael@0: } michael@0: else if (aTagName == nsGkAtoms::implementation) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding); michael@0: mState = eXBL_InImplementation; michael@0: ConstructImplementation(aAtts); michael@0: // Note that this mState will cause us to return false, so no need michael@0: // to set ret to false. michael@0: } michael@0: else if (aTagName == nsGkAtoms::constructor) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InImplementation && michael@0: mSecondaryState == eXBL_None); michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: michael@0: mSecondaryState = eXBL_InConstructor; michael@0: nsAutoString name; michael@0: if (!mCurrentBindingID.IsEmpty()) { michael@0: name.Assign(mCurrentBindingID); michael@0: name.AppendLiteral("_XBL_Constructor"); michael@0: } else { michael@0: name.AppendLiteral("XBL_Constructor"); michael@0: } michael@0: nsXBLProtoImplAnonymousMethod* newMethod = michael@0: new nsXBLProtoImplAnonymousMethod(name.get()); michael@0: if (newMethod) { michael@0: newMethod->SetLineNumber(aLineNumber); michael@0: mBinding->SetConstructor(newMethod); michael@0: AddMember(newMethod); michael@0: } michael@0: } michael@0: else if (aTagName == nsGkAtoms::destructor) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InImplementation && michael@0: mSecondaryState == eXBL_None); michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: mSecondaryState = eXBL_InDestructor; michael@0: nsAutoString name; michael@0: if (!mCurrentBindingID.IsEmpty()) { michael@0: name.Assign(mCurrentBindingID); michael@0: name.AppendLiteral("_XBL_Destructor"); michael@0: } else { michael@0: name.AppendLiteral("XBL_Destructor"); michael@0: } michael@0: nsXBLProtoImplAnonymousMethod* newMethod = michael@0: new nsXBLProtoImplAnonymousMethod(name.get()); michael@0: if (newMethod) { michael@0: newMethod->SetLineNumber(aLineNumber); michael@0: mBinding->SetDestructor(newMethod); michael@0: AddMember(newMethod); michael@0: } michael@0: } michael@0: else if (aTagName == nsGkAtoms::field) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InImplementation && michael@0: mSecondaryState == eXBL_None); michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: mSecondaryState = eXBL_InField; michael@0: ConstructField(aAtts, aLineNumber); michael@0: } michael@0: else if (aTagName == nsGkAtoms::property) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InImplementation && michael@0: mSecondaryState == eXBL_None); michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: mSecondaryState = eXBL_InProperty; michael@0: ConstructProperty(aAtts, aLineNumber); michael@0: } michael@0: else if (aTagName == nsGkAtoms::getter) { michael@0: ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty); michael@0: NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); michael@0: mProperty->SetGetterLineNumber(aLineNumber); michael@0: mSecondaryState = eXBL_InGetter; michael@0: } michael@0: else if (aTagName == nsGkAtoms::setter) { michael@0: ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty); michael@0: NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); michael@0: mProperty->SetSetterLineNumber(aLineNumber); michael@0: mSecondaryState = eXBL_InSetter; michael@0: } michael@0: else if (aTagName == nsGkAtoms::method) { michael@0: ENSURE_XBL_STATE(mState == eXBL_InImplementation && michael@0: mSecondaryState == eXBL_None); michael@0: NS_ASSERTION(mBinding, "Must have binding here"); michael@0: mSecondaryState = eXBL_InMethod; michael@0: ConstructMethod(aAtts); michael@0: } michael@0: else if (aTagName == nsGkAtoms::parameter) { michael@0: ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod); michael@0: NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); michael@0: ConstructParameter(aAtts); michael@0: } michael@0: else if (aTagName == nsGkAtoms::body) { michael@0: ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod); michael@0: NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); michael@0: // stash away the line number michael@0: mMethod->SetLineNumber(aLineNumber); michael@0: mSecondaryState = eXBL_InBody; michael@0: } michael@0: michael@0: return ret && mState != eXBL_InResources && mState != eXBL_InImplementation; michael@0: } michael@0: michael@0: #undef ENSURE_XBL_STATE michael@0: michael@0: nsresult michael@0: nsXBLContentSink::ConstructBinding(uint32_t aLineNumber) michael@0: { michael@0: nsCOMPtr binding = GetCurrentContent(); michael@0: binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mCurrentBindingID); michael@0: NS_ConvertUTF16toUTF8 cid(mCurrentBindingID); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Don't create a binding with no id. nsXBLPrototypeBinding::Read also michael@0: // performs this check. michael@0: if (!cid.IsEmpty()) { michael@0: mBinding = new nsXBLPrototypeBinding(); michael@0: if (!mBinding) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding); michael@0: if (NS_SUCCEEDED(rv) && michael@0: NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) { michael@0: if (!mFoundFirstBinding) { michael@0: mFoundFirstBinding = true; michael@0: mDocInfo->SetFirstPrototypeBinding(mBinding); michael@0: } michael@0: binding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::id, false); michael@0: } else { michael@0: delete mBinding; michael@0: mBinding = nullptr; michael@0: } michael@0: } else { michael@0: nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, michael@0: NS_LITERAL_CSTRING("XBL Content Sink"), nullptr, michael@0: nsContentUtils::eXBL_PROPERTIES, michael@0: "MissingIdAttr", nullptr, 0, michael@0: mDocumentURI, michael@0: EmptyString(), michael@0: aLineNumber); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static bool michael@0: FindValue(const char16_t **aAtts, nsIAtom *aAtom, const char16_t **aResult) michael@0: { michael@0: nsCOMPtr prefix, localName; michael@0: for (; *aAtts; aAtts += 2) { michael@0: int32_t nameSpaceID; michael@0: nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: // Is this attribute one of the ones we care about? michael@0: if (nameSpaceID == kNameSpaceID_None && localName == aAtom) { michael@0: *aResult = aAtts[1]; michael@0: michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::ConstructHandler(const char16_t **aAtts, uint32_t aLineNumber) michael@0: { michael@0: const char16_t* event = nullptr; michael@0: const char16_t* modifiers = nullptr; michael@0: const char16_t* button = nullptr; michael@0: const char16_t* clickcount = nullptr; michael@0: const char16_t* keycode = nullptr; michael@0: const char16_t* charcode = nullptr; michael@0: const char16_t* phase = nullptr; michael@0: const char16_t* command = nullptr; michael@0: const char16_t* action = nullptr; michael@0: const char16_t* group = nullptr; michael@0: const char16_t* preventdefault = nullptr; michael@0: const char16_t* allowuntrusted = nullptr; michael@0: michael@0: nsCOMPtr prefix, localName; michael@0: for (; *aAtts; aAtts += 2) { michael@0: int32_t nameSpaceID; michael@0: nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (nameSpaceID != kNameSpaceID_None) { michael@0: continue; michael@0: } michael@0: michael@0: // Is this attribute one of the ones we care about? michael@0: if (localName == nsGkAtoms::event) michael@0: event = aAtts[1]; michael@0: else if (localName == nsGkAtoms::modifiers) michael@0: modifiers = aAtts[1]; michael@0: else if (localName == nsGkAtoms::button) michael@0: button = aAtts[1]; michael@0: else if (localName == nsGkAtoms::clickcount) michael@0: clickcount = aAtts[1]; michael@0: else if (localName == nsGkAtoms::keycode) michael@0: keycode = aAtts[1]; michael@0: else if (localName == nsGkAtoms::key || localName == nsGkAtoms::charcode) michael@0: charcode = aAtts[1]; michael@0: else if (localName == nsGkAtoms::phase) michael@0: phase = aAtts[1]; michael@0: else if (localName == nsGkAtoms::command) michael@0: command = aAtts[1]; michael@0: else if (localName == nsGkAtoms::action) michael@0: action = aAtts[1]; michael@0: else if (localName == nsGkAtoms::group) michael@0: group = aAtts[1]; michael@0: else if (localName == nsGkAtoms::preventdefault) michael@0: preventdefault = aAtts[1]; michael@0: else if (localName == nsGkAtoms::allowuntrusted) michael@0: allowuntrusted = aAtts[1]; michael@0: } michael@0: michael@0: if (command && !mIsChromeOrResource) { michael@0: // Make sure the XBL doc is chrome or resource if we have a command michael@0: // shorthand syntax. michael@0: mState = eXBL_Error; michael@0: nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, michael@0: NS_LITERAL_CSTRING("XBL Content Sink"), michael@0: mDocument, michael@0: nsContentUtils::eXBL_PROPERTIES, michael@0: "CommandNotInChrome", nullptr, 0, michael@0: nullptr, michael@0: EmptyString() /* source line */, michael@0: aLineNumber); michael@0: return; // Don't even make this handler. michael@0: } michael@0: michael@0: // All of our pointers are now filled in. Construct our handler with all of michael@0: // these parameters. michael@0: nsXBLPrototypeHandler* newHandler; michael@0: newHandler = new nsXBLPrototypeHandler(event, phase, action, command, michael@0: keycode, charcode, modifiers, button, michael@0: clickcount, group, preventdefault, michael@0: allowuntrusted, mBinding, aLineNumber); michael@0: michael@0: if (newHandler) { michael@0: // Add this handler to our chain of handlers. michael@0: if (mHandler) { michael@0: // Already have a chain. Just append to the end. michael@0: mHandler->SetNextHandler(newHandler); michael@0: } michael@0: else { michael@0: // We're the first handler in the chain. michael@0: mBinding->SetPrototypeHandlers(newHandler); michael@0: } michael@0: // Adjust our mHandler pointer to point to the new last handler in the michael@0: // chain. michael@0: mHandler = newHandler; michael@0: } else { michael@0: mState = eXBL_Error; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::ConstructResource(const char16_t **aAtts, michael@0: nsIAtom* aResourceType) michael@0: { michael@0: if (!mBinding) michael@0: return; michael@0: michael@0: const char16_t* src = nullptr; michael@0: if (FindValue(aAtts, nsGkAtoms::src, &src)) { michael@0: mBinding->AddResource(aResourceType, nsDependentString(src)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::ConstructImplementation(const char16_t **aAtts) michael@0: { michael@0: mImplementation = nullptr; michael@0: mImplMember = nullptr; michael@0: mImplField = nullptr; michael@0: michael@0: if (!mBinding) michael@0: return; michael@0: michael@0: const char16_t* name = nullptr; michael@0: michael@0: nsCOMPtr prefix, localName; michael@0: for (; *aAtts; aAtts += 2) { michael@0: int32_t nameSpaceID; michael@0: nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (nameSpaceID != kNameSpaceID_None) { michael@0: continue; michael@0: } michael@0: michael@0: // Is this attribute one of the ones we care about? michael@0: if (localName == nsGkAtoms::name) { michael@0: name = aAtts[1]; michael@0: } michael@0: else if (localName == nsGkAtoms::implements) { michael@0: // Only allow implementation of interfaces via XBL if the principal of michael@0: // our XBL document is the system principal. michael@0: if (nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) { michael@0: mBinding->ConstructInterfaceTable(nsDependentString(aAtts[1])); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_NewXBLProtoImpl(mBinding, name, &mImplementation); michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::ConstructField(const char16_t **aAtts, uint32_t aLineNumber) michael@0: { michael@0: const char16_t* name = nullptr; michael@0: const char16_t* readonly = nullptr; michael@0: michael@0: nsCOMPtr prefix, localName; michael@0: for (; *aAtts; aAtts += 2) { michael@0: int32_t nameSpaceID; michael@0: nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (nameSpaceID != kNameSpaceID_None) { michael@0: continue; michael@0: } michael@0: michael@0: // Is this attribute one of the ones we care about? michael@0: if (localName == nsGkAtoms::name) { michael@0: name = aAtts[1]; michael@0: } michael@0: else if (localName == nsGkAtoms::readonly) { michael@0: readonly = aAtts[1]; michael@0: } michael@0: } michael@0: michael@0: if (name) { michael@0: // All of our pointers are now filled in. Construct our field with all of michael@0: // these parameters. michael@0: mField = new nsXBLProtoImplField(name, readonly); michael@0: if (mField) { michael@0: mField->SetLineNumber(aLineNumber); michael@0: AddField(mField); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::ConstructProperty(const char16_t **aAtts, uint32_t aLineNumber) michael@0: { michael@0: const char16_t* name = nullptr; michael@0: const char16_t* readonly = nullptr; michael@0: const char16_t* onget = nullptr; michael@0: const char16_t* onset = nullptr; michael@0: bool exposeToUntrustedContent = false; michael@0: michael@0: nsCOMPtr prefix, localName; michael@0: for (; *aAtts; aAtts += 2) { michael@0: int32_t nameSpaceID; michael@0: nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (nameSpaceID != kNameSpaceID_None) { michael@0: continue; michael@0: } michael@0: michael@0: // Is this attribute one of the ones we care about? michael@0: if (localName == nsGkAtoms::name) { michael@0: name = aAtts[1]; michael@0: } michael@0: else if (localName == nsGkAtoms::readonly) { michael@0: readonly = aAtts[1]; michael@0: } michael@0: else if (localName == nsGkAtoms::onget) { michael@0: onget = aAtts[1]; michael@0: } michael@0: else if (localName == nsGkAtoms::onset) { michael@0: onset = aAtts[1]; michael@0: } michael@0: else if (localName == nsGkAtoms::exposeToUntrustedContent && michael@0: nsDependentString(aAtts[1]).EqualsLiteral("true")) michael@0: { michael@0: exposeToUntrustedContent = true; michael@0: } michael@0: } michael@0: michael@0: if (name) { michael@0: // All of our pointers are now filled in. Construct our property with all of michael@0: // these parameters. michael@0: mProperty = new nsXBLProtoImplProperty(name, onget, onset, readonly, aLineNumber); michael@0: if (exposeToUntrustedContent) { michael@0: mProperty->SetExposeToUntrustedContent(true); michael@0: } michael@0: AddMember(mProperty); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::ConstructMethod(const char16_t **aAtts) michael@0: { michael@0: mMethod = nullptr; michael@0: michael@0: const char16_t* name = nullptr; michael@0: const char16_t* expose = nullptr; michael@0: if (FindValue(aAtts, nsGkAtoms::name, &name)) { michael@0: mMethod = new nsXBLProtoImplMethod(name); michael@0: if (FindValue(aAtts, nsGkAtoms::exposeToUntrustedContent, &expose) && michael@0: nsDependentString(expose).EqualsLiteral("true")) michael@0: { michael@0: mMethod->SetExposeToUntrustedContent(true); michael@0: } michael@0: } michael@0: michael@0: if (mMethod) { michael@0: AddMember(mMethod); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXBLContentSink::ConstructParameter(const char16_t **aAtts) michael@0: { michael@0: if (!mMethod) michael@0: return; michael@0: michael@0: const char16_t* name = nullptr; michael@0: if (FindValue(aAtts, nsGkAtoms::name, &name)) { michael@0: mMethod->AddParameter(nsDependentString(name)); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsXBLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount, michael@0: nsINodeInfo* aNodeInfo, uint32_t aLineNumber, michael@0: nsIContent** aResult, bool* aAppendContent, michael@0: FromParser aFromParser) michael@0: { michael@0: #ifdef MOZ_XUL michael@0: if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) { michael@0: #endif michael@0: return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo, michael@0: aLineNumber, aResult, michael@0: aAppendContent, aFromParser); michael@0: #ifdef MOZ_XUL michael@0: } michael@0: michael@0: // Note that this needs to match the code in nsXBLPrototypeBinding::ReadContentNode. michael@0: michael@0: *aAppendContent = true; michael@0: nsRefPtr prototype = new nsXULPrototypeElement(); michael@0: if (!prototype) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: prototype->mNodeInfo = aNodeInfo; michael@0: michael@0: AddAttributesToXULPrototype(aAtts, aAttsCount, prototype); michael@0: michael@0: Element* result; michael@0: nsresult rv = nsXULElement::Create(prototype, mDocument, false, false, &result); michael@0: *aResult = result; michael@0: return rv; michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsXBLContentSink::AddAttributes(const char16_t** aAtts, michael@0: nsIContent* aContent) michael@0: { michael@0: if (aContent->IsXUL()) michael@0: return NS_OK; // Nothing to do, since the proto already has the attrs. michael@0: michael@0: return nsXMLContentSink::AddAttributes(aAtts, aContent); michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: nsresult michael@0: nsXBLContentSink::AddAttributesToXULPrototype(const char16_t **aAtts, michael@0: uint32_t aAttsCount, michael@0: nsXULPrototypeElement* aElement) michael@0: { michael@0: // Add tag attributes to the element michael@0: nsresult rv; michael@0: michael@0: // Create storage for the attributes michael@0: nsXULPrototypeAttribute* attrs = nullptr; michael@0: if (aAttsCount > 0) { michael@0: attrs = new nsXULPrototypeAttribute[aAttsCount]; michael@0: if (!attrs) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: aElement->mAttributes = attrs; michael@0: aElement->mNumAttributes = aAttsCount; michael@0: michael@0: // Copy the attributes into the prototype michael@0: nsCOMPtr prefix, localName; michael@0: michael@0: uint32_t i; michael@0: for (i = 0; i < aAttsCount; ++i) { michael@0: int32_t nameSpaceID; michael@0: nsContentUtils::SplitExpatName(aAtts[i * 2], getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (nameSpaceID == kNameSpaceID_None) { michael@0: attrs[i].mName.SetTo(localName); michael@0: } michael@0: else { michael@0: nsCOMPtr ni; michael@0: ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, michael@0: nsIDOMNode::ATTRIBUTE_NODE); michael@0: attrs[i].mName.SetTo(ni); michael@0: } michael@0: michael@0: rv = aElement->SetAttrAt(i, nsDependentString(aAtts[i * 2 + 1]), michael@0: mDocumentURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: #endif