/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** 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): * Chris Waterson * David Hyatt * Brendan Eich * * 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 ***** */ /* An implementation for a Gecko-style content sink that knows how to build a content model from XUL. For more information on XUL, see http://www.mozilla.org/xpfe TO DO ----- */ #include "nsCOMPtr.h" #include "nsForwardReference.h" #include "nsICSSLoader.h" #include "nsICSSParser.h" #include "nsICSSStyleSheet.h" #include "nsIContentSink.h" #include "nsIDOMDocument.h" #include "nsIDOMEventListener.h" #include "nsIDOMHTMLFormElement.h" #include "nsIDOMXULDocument.h" #include "nsIDocument.h" #include "nsIDocumentLoader.h" #include "nsIFormControl.h" #include "nsHTMLStyleSheet.h" #include "nsINameSpaceManager.h" #include "nsINodeInfo.h" #include "nsIParser.h" #include "nsIPresShell.h" #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsIServiceManager.h" #include "nsITextContent.h" #include "nsIURL.h" #include "nsIViewManager.h" #include "nsIXULContentSink.h" #include "nsIXULDocument.h" #include "nsIXULPrototypeDocument.h" #include "nsIXULPrototypeCache.h" #include "nsIScriptSecurityManager.h" #include "nsLayoutCID.h" #include "nsNetUtil.h" #include "nsRDFCID.h" #include "nsParserUtils.h" #include "nsIMIMEHeaderParam.h" #include "nsVoidArray.h" #include "nsWeakPtr.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsXULElement.h" #include "prlog.h" #include "prmem.h" #include "jsapi.h" // for JSVERSION_*, JS_VersionToString, etc. #include "nsCRT.h" #include "nsIFastLoadService.h" // XXXbe temporary #include "nsIObjectInputStream.h" // XXXbe temporary #include "nsXULDocument.h" // XXXbe temporary #include "nsIExpatSink.h" #include "nsUnicharUtils.h" #include "nsXULAtoms.h" #include "nsHTMLAtoms.h" #include "nsNodeInfoManager.h" #include "nsContentUtils.h" #include "nsAttrName.h" #include "nsXMLContentSink.h" #include "nsLayoutAtoms.h" #ifdef PR_LOGGING static PRLogModuleInfo* gLog; #endif static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); //---------------------------------------------------------------------- class XULContentSinkImpl : public nsIXULContentSink, public nsIExpatSink { public: XULContentSinkImpl(nsresult& aRV); virtual ~XULContentSinkImpl(); // nsISupports NS_DECL_ISUPPORTS NS_DECL_NSIEXPATSINK // nsIContentSink NS_IMETHOD WillBuildModel(void); NS_IMETHOD DidBuildModel(void); NS_IMETHOD WillInterrupt(void); NS_IMETHOD WillResume(void); NS_IMETHOD SetParser(nsIParser* aParser); virtual void FlushPendingNotifications(mozFlushType aType) { } NS_IMETHOD SetDocumentCharset(nsACString& aCharset); virtual nsISupports *GetTarget(); // nsIXULContentSink NS_IMETHOD Init(nsIDocument* aDocument, nsIXULPrototypeDocument* aPrototype); protected: // pseudo-constants static nsrefcnt gRefCnt; static nsIXULPrototypeCache* gXULCache; PRUnichar* mText; PRInt32 mTextLength; PRInt32 mTextSize; PRBool mConstrainSize; nsresult AddAttributes(const PRUnichar** aAttributes, const PRUint32 aAttrLen, nsXULPrototypeElement* aElement); nsresult OpenRoot(const PRUnichar** aAttributes, const PRUint32 aAttrLen, nsINodeInfo *aNodeInfo); nsresult OpenTag(const PRUnichar** aAttributes, const PRUint32 aAttrLen, const PRUint32 aLineNumber, nsINodeInfo *aNodeInfo); nsresult OpenScript(const PRUnichar** aAttributes, const PRUint32 aLineNumber); static PRBool IsDataInBuffer(PRUnichar* aBuffer, PRInt32 aLength); // Text management nsresult FlushText(PRBool aCreateTextNode = PR_TRUE); nsresult AddText(const PRUnichar* aText, PRInt32 aLength); nsRefPtr mNodeInfoManager; nsresult NormalizeAttributeString(const PRUnichar *aExpatName, nsAttrName &aName); nsresult CreateElement(nsINodeInfo *aNodeInfo, nsXULPrototypeElement** aResult); // Style sheets nsresult ProcessStyleLink(nsIContent* aElement, const nsString& aHref, PRBool aAlternate, const nsString& aTitle, const nsString& aType, const nsString& aMedia); public: enum State { eInProlog, eInDocumentElement, eInScript, eInEpilog }; protected: State mState; // content stack management class ContextStack { protected: struct Entry { nsXULPrototypeNode* mNode; // a LOT of nodes have children; preallocate for 8 nsAutoVoidArray mChildren; State mState; Entry* mNext; }; Entry* mTop; PRInt32 mDepth; public: ContextStack(); ~ContextStack(); PRInt32 Depth() { return mDepth; } nsresult Push(nsXULPrototypeNode* aNode, State aState); nsresult Pop(State* aState); nsresult GetTopNode(nsXULPrototypeNode** aNode); nsresult GetTopChildren(nsVoidArray** aChildren); }; friend class ContextStack; ContextStack mContextStack; nsWeakPtr mDocument; // [OWNER] nsCOMPtr mDocumentURL; // [OWNER] nsCOMPtr mPrototype; // [OWNER] nsIParser* mParser; // [OWNER] We use regular pointer b/c of funky exports on nsIParser nsString mPreferredStyle; nsCOMPtr mCSSLoader; // [OWNER] nsCOMPtr mCSSParser; // [OWNER] nsCOMPtr mSecMan; }; nsrefcnt XULContentSinkImpl::gRefCnt; nsIXULPrototypeCache* XULContentSinkImpl::gXULCache; //---------------------------------------------------------------------- XULContentSinkImpl::ContextStack::ContextStack() : mTop(nsnull), mDepth(0) { } XULContentSinkImpl::ContextStack::~ContextStack() { while (mTop) { Entry* doomed = mTop; mTop = mTop->mNext; delete doomed; } } nsresult XULContentSinkImpl::ContextStack::Push(nsXULPrototypeNode* aNode, State aState) { Entry* entry = new Entry; if (! entry) return NS_ERROR_OUT_OF_MEMORY; entry->mNode = aNode; entry->mState = aState; entry->mNext = mTop; mTop = entry; ++mDepth; return NS_OK; } nsresult XULContentSinkImpl::ContextStack::Pop(State* aState) { if (mDepth == 0) return NS_ERROR_UNEXPECTED; Entry* entry = mTop; mTop = mTop->mNext; --mDepth; *aState = entry->mState; delete entry; return NS_OK; } nsresult XULContentSinkImpl::ContextStack::GetTopNode(nsXULPrototypeNode** aNode) { if (mDepth == 0) return NS_ERROR_UNEXPECTED; *aNode = mTop->mNode; return NS_OK; } nsresult XULContentSinkImpl::ContextStack::GetTopChildren(nsVoidArray** aChildren) { if (mDepth == 0) return NS_ERROR_UNEXPECTED; *aChildren = &(mTop->mChildren); return NS_OK; } //---------------------------------------------------------------------- XULContentSinkImpl::XULContentSinkImpl(nsresult& rv) : mText(nsnull), mTextLength(0), mTextSize(0), mConstrainSize(PR_TRUE), mState(eInProlog), mParser(nsnull) { if (gRefCnt++ == 0) { rv = CallGetService(kXULPrototypeCacheCID, &gXULCache); } #ifdef PR_LOGGING if (! gLog) gLog = PR_NewLogModule("nsXULContentSink"); #endif rv = NS_OK; } XULContentSinkImpl::~XULContentSinkImpl() { NS_IF_RELEASE(mParser); // XXX should've been released by now, unless error. // Pop all of the elements off of the context stack, and delete // any remaining content elements. The context stack _should_ be // empty, unless something has gone wrong. while (mContextStack.Depth()) { nsresult rv; nsVoidArray* children; rv = mContextStack.GetTopChildren(&children); if (NS_SUCCEEDED(rv)) { for (PRInt32 i = children->Count() - 1; i >= 0; --i) { nsXULPrototypeNode* child = NS_REINTERPRET_CAST(nsXULPrototypeNode*, children->ElementAt(i)); delete child; } } nsXULPrototypeNode* node; rv = mContextStack.GetTopNode(&node); if (NS_SUCCEEDED(rv)) delete node; State state; mContextStack.Pop(&state); } PR_FREEIF(mText); if (--gRefCnt == 0) { NS_IF_RELEASE(gXULCache); } } //---------------------------------------------------------------------- // nsISupports interface NS_IMPL_ISUPPORTS4(XULContentSinkImpl, nsIXULContentSink, nsIXMLContentSink, nsIContentSink, nsIExpatSink) //---------------------------------------------------------------------- // nsIContentSink interface NS_IMETHODIMP XULContentSinkImpl::WillBuildModel(void) { #if FIXME if (! mParentContentSink) { // If we're _not_ an overlay, then notify the document that // the load is beginning. mDocument->BeginLoad(); } #endif return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::DidBuildModel(void) { nsCOMPtr doc = do_QueryReferent(mDocument); if (doc) { doc->EndLoad(); mDocument = nsnull; } // Drop our reference to the parser to get rid of a circular // reference. NS_IF_RELEASE(mParser); return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::WillInterrupt(void) { // XXX Notify the docshell, if necessary return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::WillResume(void) { // XXX Notify the docshell, if necessary return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::SetParser(nsIParser* aParser) { NS_IF_RELEASE(mParser); mParser = aParser; NS_IF_ADDREF(mParser); return NS_OK; } nsresult XULContentSinkImpl::ProcessStyleLink(nsIContent* aElement, const nsString& aHref, PRBool aAlternate, const nsString& aTitle, const nsString& aType, const nsString& aMedia) { static const char kCSSType[] = "text/css"; nsresult rv = NS_OK; if (aAlternate) { // if alternate, does it have title? if (aTitle.IsEmpty()) { // alternates must have title return NS_OK; //return without error, for now } } nsAutoString mimeType; nsAutoString params; nsParserUtils::SplitMimeType(aType, mimeType, params); if ((mimeType.IsEmpty()) || mimeType.EqualsIgnoreCase(kCSSType)) { nsCOMPtr url; rv = NS_NewURI(getter_AddRefs(url), aHref, nsnull, mDocumentURL); if (NS_OK != rv) { return NS_OK; // The URL is bad, move along, don't propagate the error (for now) } // Add the style sheet reference to the prototype mPrototype->AddStyleSheetReference(url); // Nope, we need to load it asynchronously PRBool blockParser = PR_FALSE; if (! aAlternate) { if (!aTitle.IsEmpty()) { // possibly preferred sheet if (mPreferredStyle.IsEmpty()) { mPreferredStyle = aTitle; mCSSLoader->SetPreferredSheet(aTitle); nsCOMPtr defaultStyle = do_GetAtom("default-style"); if (defaultStyle) { mPrototype->SetHeaderData(defaultStyle, aTitle); } } } else { // persistent sheet, block blockParser = PR_TRUE; } } nsCOMPtr doc = do_QueryReferent(mDocument); if (! doc) return NS_ERROR_FAILURE; // doc went away! PRBool doneLoading; rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, ((blockParser) ? mParser : nsnull), doneLoading, nsnull); if (NS_SUCCEEDED(rv) && blockParser && (! doneLoading)) { rv = NS_ERROR_HTMLPARSER_BLOCK; } } return rv; } NS_IMETHODIMP XULContentSinkImpl::SetDocumentCharset(nsACString& aCharset) { nsCOMPtr doc = do_QueryReferent(mDocument); if (doc) { doc->SetDocumentCharacterSet(aCharset); } return NS_OK; } nsISupports * XULContentSinkImpl::GetTarget() { nsCOMPtr doc = do_QueryReferent(mDocument); return doc; } //---------------------------------------------------------------------- // // nsIXULContentSink interface // NS_IMETHODIMP XULContentSinkImpl::Init(nsIDocument* aDocument, nsIXULPrototypeDocument* aPrototype) { NS_PRECONDITION(aDocument != nsnull, "null ptr"); if (! aDocument) return NS_ERROR_NULL_POINTER; nsresult rv; mDocument = do_GetWeakReference(aDocument); mPrototype = aPrototype; rv = mPrototype->GetURI(getter_AddRefs(mDocumentURL)); if (NS_FAILED(rv)) return rv; // XXX this presumes HTTP header info is already set in document // XXX if it isn't we need to set it here... nsCOMPtr defaultStyle = do_GetAtom("default-style"); if (! defaultStyle) return NS_ERROR_OUT_OF_MEMORY; rv = mPrototype->GetHeaderData(defaultStyle, mPreferredStyle); if (NS_FAILED(rv)) return rv; // Get the CSS loader from the document so we can load // stylesheets mCSSLoader = aDocument->CSSLoader(); mNodeInfoManager = aPrototype->GetNodeInfoManager(); if (! mNodeInfoManager) return NS_ERROR_UNEXPECTED; mState = eInProlog; return NS_OK; } //---------------------------------------------------------------------- // // Text buffering // PRBool XULContentSinkImpl::IsDataInBuffer(PRUnichar* buffer, PRInt32 length) { for (PRInt32 i = 0; i < length; ++i) { if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n' || buffer[i] == '\r') continue; return PR_TRUE; } return PR_FALSE; } nsresult XULContentSinkImpl::FlushText(PRBool aCreateTextNode) { nsresult rv; do { // Don't do anything if there's no text to create a node from, or // if they've told us not to create a text node if (! mTextLength) break; if (! aCreateTextNode) break; nsXULPrototypeNode* node; rv = mContextStack.GetTopNode(&node); if (NS_FAILED(rv)) return rv; PRBool stripWhitespace = PR_FALSE; if (node->mType == nsXULPrototypeNode::eType_Element) { nsINodeInfo *nodeInfo = NS_STATIC_CAST(nsXULPrototypeElement*, node)->mNodeInfo; if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL)) stripWhitespace = !nodeInfo->Equals(nsXULAtoms::label) && !nodeInfo->Equals(nsXULAtoms::description); } // Don't bother if there's nothing but whitespace. if (stripWhitespace && ! IsDataInBuffer(mText, mTextLength)) break; // Don't bother if we're not in XUL document body if (mState != eInDocumentElement || mContextStack.Depth() == 0) break; nsXULPrototypeText* text = new nsXULPrototypeText(); if (! text) return NS_ERROR_OUT_OF_MEMORY; text->mValue.Assign(mText, mTextLength); if (stripWhitespace) text->mValue.Trim(" \t\n\r"); // hook it up nsVoidArray* children; rv = mContextStack.GetTopChildren(&children); if (NS_FAILED(rv)) return rv; // transfer ownership of 'text' to the children array children->AppendElement(text); } while (0); // Reset our text buffer mTextLength = 0; return NS_OK; } //---------------------------------------------------------------------- nsresult XULContentSinkImpl::NormalizeAttributeString(const PRUnichar *aExpatName, nsAttrName &aName) { PRInt32 nameSpaceID; nsCOMPtr prefix, localName; nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix), getter_AddRefs(localName), &nameSpaceID); if (nameSpaceID == kNameSpaceID_None) { aName.SetTo(localName); return NS_OK; } nsCOMPtr ni; nsresult rv = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, getter_AddRefs(ni)); NS_ENSURE_SUCCESS(rv, rv); aName.SetTo(ni); return NS_OK; } nsresult XULContentSinkImpl::CreateElement(nsINodeInfo *aNodeInfo, nsXULPrototypeElement** aResult) { nsXULPrototypeElement* element = new nsXULPrototypeElement(); if (! element) return NS_ERROR_OUT_OF_MEMORY; element->mNodeInfo = aNodeInfo; *aResult = element; return NS_OK; } nsresult NS_NewXULContentSink(nsIXULContentSink** aResult) { NS_PRECONDITION(aResult != nsnull, "null ptr"); if (! aResult) return NS_ERROR_NULL_POINTER; nsresult rv; XULContentSinkImpl* sink = new XULContentSinkImpl(rv); if (! sink) return NS_ERROR_OUT_OF_MEMORY; if (NS_FAILED(rv)) { delete sink; return rv; } NS_ADDREF(sink); *aResult = sink; return NS_OK; } /**** BEGIN NEW APIs ****/ NS_IMETHODIMP XULContentSinkImpl::HandleStartElement(const PRUnichar *aName, const PRUnichar **aAtts, PRUint32 aAttsCount, PRInt32 aIndex, PRUint32 aLineNumber) { // XXX Hopefully the parser will flag this before we get here. If // we're in the epilog, there should be no new elements NS_PRECONDITION(mState != eInEpilog, "tag in XUL doc epilog"); NS_PRECONDITION(aIndex >= -1, "Bogus aIndex"); NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount"); // Adjust aAttsCount so it's the actual number of attributes aAttsCount /= 2; if (mState == eInEpilog) return NS_ERROR_UNEXPECTED; if (mState != eInScript) { FlushText(); } PRInt32 nameSpaceID; nsCOMPtr prefix, localName; nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), getter_AddRefs(localName), &nameSpaceID); nsCOMPtr nodeInfo; nsresult rv = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, getter_AddRefs(nodeInfo)); NS_ENSURE_SUCCESS(rv, rv); switch (mState) { case eInProlog: // We're the root document element rv = OpenRoot(aAtts, aAttsCount, nodeInfo); break; case eInDocumentElement: rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo); break; case eInEpilog: case eInScript: PR_LOG(gLog, PR_LOG_WARNING, ("xul: warning: unexpected tags in epilog at line %d", aLineNumber)); rv = NS_ERROR_UNEXPECTED; // XXX break; } // Set the ID attribute atom on the node info object for this node if (aIndex != -1 && NS_SUCCEEDED(rv)) { nsCOMPtr IDAttr = do_GetAtom(aAtts[aIndex]); if (IDAttr) { nodeInfo->SetIDAttributeAtom(IDAttr); } } return rv; } NS_IMETHODIMP XULContentSinkImpl::HandleEndElement(const PRUnichar *aName) { // Never EVER return anything but NS_OK or // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow // the parser's little mind all over the planet. nsresult rv; nsXULPrototypeNode* node; rv = mContextStack.GetTopNode(&node); if (NS_FAILED(rv)) { return NS_OK; } switch (node->mType) { case nsXULPrototypeNode::eType_Element: { // Flush any text _now_, so that we'll get text nodes created // before popping the stack. FlushText(); // Pop the context stack and do prototype hookup. nsVoidArray* children; rv = mContextStack.GetTopChildren(&children); if (NS_FAILED(rv)) return rv; nsXULPrototypeElement* element = NS_REINTERPRET_CAST(nsXULPrototypeElement*, node); PRInt32 count = children->Count(); if (count) { element->mChildren = new nsXULPrototypeNode*[count]; if (! element->mChildren) return NS_ERROR_OUT_OF_MEMORY; for (PRInt32 i = count - 1; i >= 0; --i) element->mChildren[i] = NS_REINTERPRET_CAST(nsXULPrototypeNode*, children->ElementAt(i)); element->mNumChildren = count; } } break; case nsXULPrototypeNode::eType_Script: { nsXULPrototypeScript* script = NS_STATIC_CAST(nsXULPrototypeScript*, node); // If given a src= attribute, we must ignore script tag content. if (! script->mSrcURI && ! script->mJSObject) { nsCOMPtr doc = do_QueryReferent(mDocument); script->mOutOfLine = PR_FALSE; if (doc) script->Compile(mText, mTextLength, mDocumentURL, script->mLineNo, doc, mPrototype); } FlushText(PR_FALSE); } break; default: NS_ERROR("didn't expect that"); break; } rv = mContextStack.Pop(&mState); NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted"); if (NS_FAILED(rv)) return rv; if (mContextStack.Depth() == 0) { // The root element should -always- be an element, because // it'll have been created via XULContentSinkImpl::OpenRoot(). NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element, "root is not an element"); if (node->mType != nsXULPrototypeNode::eType_Element) return NS_ERROR_UNEXPECTED; // Now that we're done parsing, set the prototype document's // root element. This transfers ownership of the prototype // element tree to the prototype document. nsXULPrototypeElement* element = NS_STATIC_CAST(nsXULPrototypeElement*, node); rv = mPrototype->SetRootElement(element); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to set document root"); if (NS_FAILED(rv)) return rv; mState = eInEpilog; } return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::HandleComment(const PRUnichar *aName) { FlushText(); return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::HandleCDataSection(const PRUnichar *aData, PRUint32 aLength) { FlushText(); return AddText(aData, aLength); } NS_IMETHODIMP XULContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset, const nsAString & aName, const nsAString & aSystemId, const nsAString & aPublicId, nsISupports* aCatalogData) { return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::HandleCharacterData(const PRUnichar *aData, PRUint32 aLength) { if (aData && mState != eInProlog && mState != eInEpilog) { return AddText(aData, aLength); } return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::HandleProcessingInstruction(const PRUnichar *aTarget, const PRUnichar *aData) { FlushText(); // XXX For now, we don't add the PI to the content model. // We just check for a style sheet PI const nsDependentString target(aTarget); const nsDependentString data(aData); nsReadingIterator targetStart, targetEnd, tmp; target.BeginReading(targetStart); target.EndReading(targetEnd); tmp = targetStart; nsresult rv; if (FindInReadable(NS_LITERAL_STRING("xul-overlay"), targetStart, targetEnd)) { // Load a XUL overlay. nsAutoString href; nsParserUtils::GetQuotedAttributeValue(data, nsHTMLAtoms::href, href); // If there was no href, we can't do // anything with this PI if (href.IsEmpty()) { return NS_OK; } // Add the overlay to our list of overlays that need to be processed. nsCOMPtr url; rv = NS_NewURI(getter_AddRefs(url), href, nsnull, mDocumentURL); if (NS_FAILED(rv)) { // XXX This is wrong, the error message could be out of memory // or something else equally bad, which we should propagate. // Bad URL should probably be "success with info" value. return NS_OK; // The URL is bad, move along. Don't propagate for now. } return mPrototype->AddOverlayReference(url); } targetStart = tmp; if (!FindInReadable(NS_LITERAL_STRING("xml-stylesheet"), targetStart, targetEnd)) { return NS_OK; } // It's a stylesheet PI... nsAutoString type; nsParserUtils::GetQuotedAttributeValue(data, nsHTMLAtoms::type, type); nsAutoString href, title, media; PRBool isAlternate = PR_FALSE; nsXMLContentSink::ParsePIData(data, href, title, media, isAlternate); // If there was no href, we can't do anything with this PI if (href.IsEmpty()) { return NS_OK; } // XXX need a node here rv = ProcessStyleLink(nsnull , href, isAlternate, title, type, media); if (rv == NS_ERROR_HTMLPARSER_BLOCK && mParser) { mParser->BlockParser(); } return rv; } NS_IMETHODIMP XULContentSinkImpl::HandleXMLDeclaration(const PRUnichar *aVersion, const PRUnichar *aEncoding, PRInt32 aStandalone) { return NS_OK; } NS_IMETHODIMP XULContentSinkImpl::ReportError(const PRUnichar* aErrorText, const PRUnichar* aSourceText) { nsresult rv = NS_OK; // make sure to empty the context stack so that // could become the root element. while (mContextStack.Depth()) { nsVoidArray* children; rv = mContextStack.GetTopChildren(&children); if (NS_SUCCEEDED(rv)) { for (PRInt32 i = children->Count() - 1; i >= 0; --i) { nsXULPrototypeNode* child = NS_REINTERPRET_CAST(nsXULPrototypeNode*, children->ElementAt(i)); delete child; } } State state; mContextStack.Pop(&state); } mState = eInProlog; // Clear any buffered-up text we have. It's enough to set the length to 0. // The buffer itself is allocated when we're created and deleted in our // destructor, so don't mess with it. mTextLength = 0; const PRUnichar* noAtts[] = { 0, 0 }; NS_NAMED_LITERAL_STRING(errorNs, "http://www.mozilla.org/newlayout/xml/parsererror.xml"); nsAutoString parsererror(errorNs); parsererror.Append((PRUnichar)0xFFFF); parsererror.AppendLiteral("parsererror"); rv = HandleStartElement(parsererror.get(), noAtts, 0, -1, 0); NS_ENSURE_SUCCESS(rv,rv); rv = HandleCharacterData(aErrorText, nsCRT::strlen(aErrorText)); NS_ENSURE_SUCCESS(rv,rv); nsAutoString sourcetext(errorNs); sourcetext.Append((PRUnichar)0xFFFF); sourcetext.AppendLiteral("sourcetext"); rv = HandleStartElement(sourcetext.get(), noAtts, 0, -1, 0); NS_ENSURE_SUCCESS(rv,rv); rv = HandleCharacterData(aSourceText, nsCRT::strlen(aSourceText)); NS_ENSURE_SUCCESS(rv,rv); rv = HandleEndElement(sourcetext.get()); NS_ENSURE_SUCCESS(rv,rv); rv = HandleEndElement(parsererror.get()); NS_ENSURE_SUCCESS(rv,rv); return rv; } nsresult XULContentSinkImpl::OpenRoot(const PRUnichar** aAttributes, const PRUint32 aAttrLen, nsINodeInfo *aNodeInfo) { NS_ASSERTION(mState == eInProlog, "how'd we get here?"); if (mState != eInProlog) return NS_ERROR_UNEXPECTED; nsresult rv; if (aNodeInfo->Equals(nsHTMLAtoms::script, kNameSpaceID_XHTML) || aNodeInfo->Equals(nsHTMLAtoms::script, kNameSpaceID_XUL)) { PR_LOG(gLog, PR_LOG_ERROR, ("xul: script tag not allowed as root content element")); return NS_ERROR_UNEXPECTED; } // Create the element nsXULPrototypeElement* element; rv = CreateElement(aNodeInfo, &element); if (NS_FAILED(rv)) { #ifdef PR_LOGGING if (PR_LOG_TEST(gLog, PR_LOG_ERROR)) { nsAutoString anodeC; aNodeInfo->GetName(anodeC); PR_LOG(gLog, PR_LOG_ERROR, ("xul: unable to create element '%s' at line %d", NS_ConvertUCS2toUTF8(anodeC).get(), -1)); // XXX pass in line number } #endif return rv; } // Push the element onto the context stack, so that child // containers will hook up to us as their parent. rv = mContextStack.Push(element, mState); if (NS_FAILED(rv)) { delete element; return rv; } // Add the attributes rv = AddAttributes(aAttributes, aAttrLen, element); if (NS_FAILED(rv)) return rv; mState = eInDocumentElement; return NS_OK; } nsresult XULContentSinkImpl::OpenTag(const PRUnichar** aAttributes, const PRUint32 aAttrLen, const PRUint32 aLineNumber, nsINodeInfo *aNodeInfo) { nsresult rv; // Create the element nsXULPrototypeElement* element; rv = CreateElement(aNodeInfo, &element); if (NS_FAILED(rv)) { #ifdef PR_LOGGING if (PR_LOG_TEST(gLog, PR_LOG_ERROR)) { nsAutoString anodeC; aNodeInfo->GetName(anodeC); PR_LOG(gLog, PR_LOG_ERROR, ("xul: unable to create element '%s' at line %d", NS_ConvertUCS2toUTF8(anodeC).get(), aLineNumber)); } #endif return rv; } // Link this element to its parent. nsVoidArray* children; rv = mContextStack.GetTopChildren(&children); if (NS_FAILED(rv)) { delete element; return rv; } // Add the attributes rv = AddAttributes(aAttributes, aAttrLen, element); if (NS_FAILED(rv)) return rv; children->AppendElement(element); if (aNodeInfo->Equals(nsHTMLAtoms::script, kNameSpaceID_XHTML) || aNodeInfo->Equals(nsHTMLAtoms::script, kNameSpaceID_XUL)) { // Do scripty things now. OpenScript will push the // nsPrototypeScriptElement onto the stack, so we're done after this. return OpenScript(aAttributes, aLineNumber); } // Push the element onto the context stack, so that child // containers will hook up to us as their parent. rv = mContextStack.Push(element, mState); if (NS_FAILED(rv)) return rv; mState = eInDocumentElement; return NS_OK; } nsresult XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes, const PRUint32 aLineNumber) { nsresult rv = NS_OK; PRBool isJavaScript = PR_TRUE; PRBool hasE4XOption = PR_TRUE; const char* jsVersionString = nsnull; // Look for SRC attribute and look for a LANGUAGE attribute nsAutoString src; while (*aAttributes) { const nsDependentString key(aAttributes[0]); if (key.EqualsLiteral("src")) { src.Assign(aAttributes[1]); } else if (key.EqualsLiteral("type")) { nsCOMPtr mimeHdrParser = do_GetService("@mozilla.org/network/mime-hdrparam;1"); NS_ENSURE_TRUE(mimeHdrParser, NS_ERROR_FAILURE); NS_ConvertUTF16toUTF8 typeAndParams(aAttributes[1]); nsAutoString mimeType; rv = mimeHdrParser->GetParameter(typeAndParams, nsnull, EmptyCString(), PR_FALSE, nsnull, mimeType); NS_ENSURE_SUCCESS(rv, rv); // Table ordered from most to least likely JS MIME types. For .xul // files that we host, the likeliest type is application/x-javascript. // See bug 62485, feel free to add